TODO: finish draw offer logic + fix inCheck bug (no highlight)
authorBenjamin Auder <benjamin.auder@somewhere>
Wed, 5 Feb 2020 11:15:05 +0000 (12:15 +0100)
committerBenjamin Auder <benjamin.auder@somewhere>
Wed, 5 Feb 2020 11:15:05 +0000 (12:15 +0100)
client/src/components/Board.vue
client/src/components/Settings.vue
client/src/store.js
client/src/translations/en.js
client/src/utils/gameStorage.js
client/src/views/Game.vue
server/db/create.sql
server/models/Game.js
server/routes/games.js

index 1b7fd6b..6a20d54 100644 (file)
@@ -1,6 +1,7 @@
 <script>
 import { getSquareId, getSquareFromId } from "@/utils/squareId";
 import { ArrayFun } from "@/utils/array";
 <script>
 import { getSquareId, getSquareFromId } from "@/utils/squareId";
 import { ArrayFun } from "@/utils/array";
+import { store } from "@/store";
 
 export default {
   name: 'my-board',
 
 export default {
   name: 'my-board',
@@ -10,13 +11,12 @@ export default {
   props: ["vr","lastMove","analyze","orientation","userColor","vname"],
   data: function () {
     return {
   props: ["vr","lastMove","analyze","orientation","userColor","vname"],
   data: function () {
     return {
-      hints: (!localStorage["hints"] ? true : localStorage["hints"] === "1"),
-      bcolor: localStorage["bcolor"] || "lichess", //lichess, chesscom or chesstempo
       possibleMoves: [], //filled after each valid click/dragstart
       choices: [], //promotion pieces, or checkered captures... (as moves)
       selectedPiece: null, //moving piece (or clicked piece)
       incheck: [],
       start: {}, //pixels coordinates + id of starting square (click or drag)
       possibleMoves: [], //filled after each valid click/dragstart
       choices: [], //promotion pieces, or checkered captures... (as moves)
       selectedPiece: null, //moving piece (or clicked piece)
       incheck: [],
       start: {}, //pixels coordinates + id of starting square (click or drag)
+      settings: store.state.settings,
     };
   },
   render(h) {
     };
   },
   render(h) {
@@ -83,7 +83,7 @@ export default {
     );
     // Create board element (+ reserves if needed by variant or mode)
     const lm = this.lastMove;
     );
     // Create board element (+ reserves if needed by variant or mode)
     const lm = this.lastMove;
-    const showLight = this.hints && this.vname != "Dark";
+    const showLight = this.settings.highlight && this.vname != "Dark";
     const gameDiv = h(
       'div',
       {
     const gameDiv = h(
       'div',
       {
@@ -126,7 +126,7 @@ export default {
                 )
               );
             }
                 )
               );
             }
-            if (this.hints && hintSquares[ci][cj])
+            if (this.settings.hints && hintSquares[ci][cj])
             {
               elems.push(
                 h(
             {
               elems.push(
                 h(
@@ -150,7 +150,7 @@ export default {
                   ['board'+sizeY]: true,
                   'light-square': (i+j)%2==0,
                   'dark-square': (i+j)%2==1,
                   ['board'+sizeY]: true,
                   'light-square': (i+j)%2==0,
                   'dark-square': (i+j)%2==1,
-                  [this.bcolor]: true,
+                  [this.settings.bcolor]: true,
                   'in-shadow': this.vname=="Dark" && !this.analyze
                     && (!this.userColor
                       || !this.vr.enlightened[this.userColor][ci][cj]),
                   'in-shadow': this.vname=="Dark" && !this.analyze
                     && (!this.userColor
                       || !this.vr.enlightened[this.userColor][ci][cj]),
index 003dc15..b9a2b63 100644 (file)
@@ -39,9 +39,12 @@ export default {
     updateSettings: function(event) {
       const propName =
         event.target.id.substr(3).replace(/^\w/, c => c.toLowerCase())
     updateSettings: function(event) {
       const propName =
         event.target.id.substr(3).replace(/^\w/, c => c.toLowerCase())
-      localStorage[propName] = ["bcolor","sound"].includes(propName)
+      let value = (["bcolor","sound"].includes(propName)
         ? event.target.value
         ? event.target.value
-        : event.target.checked;
+        : event.target.checked);
+      if (propName == "sound")
+        value = parseInt(value);
+      store.updateSetting(propName, value);
     },
   },
 };
     },
   },
 };
index 0234807..798251f 100644 (file)
@@ -42,12 +42,10 @@ export const store =
       "&page=" + encodeURIComponent(page));
     // Settings initialized with values from localStorage
     this.state.settings = {
       "&page=" + encodeURIComponent(page));
     // Settings initialized with values from localStorage
     this.state.settings = {
-      bcolor: localStorage["bcolor"] || "lichess",
-      sound: parseInt(localStorage["sound"]) || 2,
-      hints: parseInt(localStorage["hints"]) || 1,
-      coords: !!eval(localStorage["coords"]),
-      highlight: !!eval(localStorage["highlight"]),
-      sqSize: parseInt(localStorage["sqSize"]),
+      bcolor: localStorage.getItem("bcolor") || "lichess",
+      sound: parseInt(localStorage.getItem("sound")) || 1,
+      hints: localStorage.getItem("hints") == "true",
+      highlight: localStorage.getItem("highlight") == "true",
     };
     this.socketCloseListener = () => {
       // Next line may fail at first, but should retry and eventually success (TODO?)
     };
     this.socketCloseListener = () => {
       // Next line may fail at first, but should retry and eventually success (TODO?)
@@ -62,6 +60,10 @@ export const store =
         : "en");
     this.setTranslations();
   },
         : "en");
     this.setTranslations();
   },
+  updateSetting: function(propName, value) {
+    this.state.settings[propName] = value;
+    localStorage.setItem(propName, value);
+  },
   setTranslations: async function() {
     // Import translations from "./translations/$lang.js"
     const tModule = await import("@/translations/" + this.state.lang + ".js");
   setTranslations: async function() {
     // Import translations from "./translations/$lang.js"
     const tModule = await import("@/translations/" + this.state.lang + ".js");
index 0a7f93d..9b4e137 100644 (file)
@@ -24,6 +24,7 @@ export const translations =
   "Database error:": "Database error:",
   "Download PGN": "Download PGN",
   "Draw": "Draw",
   "Database error:": "Database error:",
   "Download PGN": "Download PGN",
   "Draw": "Draw",
+  "Draw offer only in your turn": "Draw offer only in your turn",
   "Email": "Email",
   "Email sent!": "Email sent!",
   "Empty message": "Empty message",
   "Email": "Email",
   "Email sent!": "Email sent!",
   "Empty message": "Empty message",
index 7fadbed..bf5dfba 100644 (file)
@@ -81,8 +81,9 @@ export const GameStorage =
           gid: gameId,
           newObj:
           {
           gid: gameId,
           newObj:
           {
+            // Some fields may be undefined:
             chat: obj.chat,
             chat: obj.chat,
-            move: obj.move, //may be undefined...
+            move: obj.move,
             fen: obj.fen,
             score: obj.score,
             scoreMsg: obj.scoreMsg,
             fen: obj.fen,
             score: obj.score,
             scoreMsg: obj.scoreMsg,
index 6ea6cdd..ec1962e 100644 (file)
@@ -320,6 +320,8 @@ export default {
       }
       else if (this.drawOffer == "") //no effect if drawOffer == "sent"
       {
       }
       else if (this.drawOffer == "") //no effect if drawOffer == "sent"
       {
+        if (this.game.mycolor != this.vr.turn)
+          return alert(this.st.tr["Draw offer only in your turn"]);
         if (!confirm(this.st.tr["Offer draw?"]))
           return;
         this.drawOffer = "sent";
         if (!confirm(this.st.tr["Offer draw?"]))
           return;
         this.drawOffer = "sent";
@@ -327,7 +329,7 @@ export default {
           if (sid != this.st.user.sid)
             this.st.conn.send(JSON.stringify({code:"drawoffer", target:sid}));
         });
           if (sid != this.st.user.sid)
             this.st.conn.send(JSON.stringify({code:"drawoffer", target:sid}));
         });
-        GameStorage.update(this.gameRef.id, {drawOffer: true});
+        GameStorage.update(this.gameRef.id, {drawOffer: this.game.mycolor});
       }
     },
     abortGame: function() {
       }
     },
     abortGame: function() {
@@ -396,8 +398,6 @@ export default {
             }
             if (L >= 1)
               game.initime[L%2] = game.moves[L-1].played;
             }
             if (L >= 1)
               game.initime[L%2] = game.moves[L-1].played;
-            if (game.drawOffer)
-              this.drawOffer = "received";
           }
           // Now that we used idx and played, re-format moves as for live games
           game.moves = game.moves.map( (m) => {
           }
           // Now that we used idx and played, re-format moves as for live games
           game.moves = game.moves.map( (m) => {
@@ -432,6 +432,22 @@ export default {
             }
           }
         }
             }
           }
         }
+
+
+
+        // TODO: (and also when receiving / sending a move ?)
+//        if (!!game.drawOffer)
+//        {
+//          if (game.drawOffer == "w")
+//          {
+//            if (myIdx == 0)
+//            {
+//              this.drawOffer = "sent";
+
+
+
+
+
         this.game = Object.assign({},
           game,
           // NOTE: assign mycolor here, since BaseGame could also be VS computer
         this.game = Object.assign({},
           game,
           // NOTE: assign mycolor here, since BaseGame could also be VS computer
index d18a310..21c7728 100644 (file)
@@ -41,7 +41,7 @@ create table Games (
   scoreMsg varchar,
   timeControl varchar,
   created datetime, --used only for DB cleaning
   scoreMsg varchar,
   timeControl varchar,
   created datetime, --used only for DB cleaning
-  drawOffer boolean,
+  drawOffer character,
   foreign key (vid) references Variants(id)
 );
 
   foreign key (vid) references Variants(id)
 );
 
index da59b4a..a3ddc3c 100644 (file)
@@ -11,7 +11,7 @@ const UserModel = require("./User");
  *   score: varchar (result)
  *   scoreMsg: varchar ("Time", "Mutual agreement"...)
  *   created: datetime
  *   score: varchar (result)
  *   scoreMsg: varchar ("Time", "Mutual agreement"...)
  *   created: datetime
- *   drawOffer: boolean
+ *   drawOffer: char ('w','b' or '' for none)
  *
  * Structure table Players:
  *   gid: ref game id
  *
  * Structure table Players:
  *   gid: ref game id
@@ -56,7 +56,7 @@ const GameModel =
         "INSERT INTO Games"
         + " (vid, fenStart, fen, score, timeControl, created, drawOffer)"
         + " VALUES (" + vid + ",'" + fen + "','" + fen + "','*','"
         "INSERT INTO Games"
         + " (vid, fenStart, fen, score, timeControl, created, drawOffer)"
         + " VALUES (" + vid + ",'" + fen + "','" + fen + "','*','"
-        + timeControl + "'," + Date.now() + "," + false + ")";
+        + timeControl + "'," + Date.now() + ",'')";
       db.run(query, function(err) {
         if (!!err)
           return cb(err);
       db.run(query, function(err) {
         if (!!err)
           return cb(err);
@@ -200,7 +200,9 @@ const GameModel =
       let modifs = "";
       if (!!obj.message)
         modifs += "message = message || ' ' || '" + obj.message + "',";
       let modifs = "";
       if (!!obj.message)
         modifs += "message = message || ' ' || '" + obj.message + "',";
-      if ([true,false].includes(obj.drawOffer))
+      // NOTE: if drawOffer is true, we should check that it's player's turn
+      // A bit overcomplicated. Let's trust the client on that for now...
+      if (!!obj.drawOffer)
         modifs += "drawOffer = " + obj.drawOffer + ",";
       if (!!obj.fen)
         modifs += "fen = '" + obj.fen + "',";
         modifs += "drawOffer = " + obj.drawOffer + ",";
       if (!!obj.fen)
         modifs += "fen = '" + obj.fen + "',";
index 24bfc82..c6e25a6 100644 (file)
@@ -76,14 +76,23 @@ router.put("/games", access.logged, access.ajax, (req,res) => {
   GameModel.update(gid, obj, (err) => {
     if (!!err)
       return res.json(err);
   GameModel.update(gid, obj, (err) => {
     if (!!err)
       return res.json(err);
-    // Notify opponent if he enabled notifications:
-    GameModel.getPlayers(gid, (err2,players) => {
-      if (!!err2)
-        return res.json(err);
-      const oppid = (players[0].id == req.userId ? players[1].id : players[0].id);
-      UserModel.tryNotify(oppid,
-        "New move in game: " + params.siteURL + "/game/" + gid);
-    });
+    if (!!obj.move || !!obj.score)
+    {
+      // Notify opponent if he enabled notifications:
+      GameModel.getPlayers(gid, (err2,players) => {
+        if (!err2)
+        {
+          const oppid = (players[0].id == req.userId
+            ? players[1].id
+            : players[0].id);
+          const messagePrefix = (!!obj.move
+            ? "New move in game: "
+            : "Game ended: ");
+          UserModel.tryNotify(oppid,
+            messagePrefix + params.siteURL + "/game/" + gid);
+        }
+      });
+    }
     res.json({});
   });
 });
     res.json({});
   });
 });