From 9ef63965401837983286ac6a95767af3923405fe Mon Sep 17 00:00:00 2001
From: Benjamin Auder <benjamin.auder@somewhere>
Date: Tue, 18 Feb 2020 12:47:52 +0100
Subject: [PATCH] Fixes and enhancements

---
 client/src/components/BaseGame.vue |   6 +-
 client/src/store.js                |  18 +++--
 client/src/views/Game.vue          | 105 ++++++++++++++++++-----------
 3 files changed, 82 insertions(+), 47 deletions(-)

diff --git a/client/src/components/BaseGame.vue b/client/src/components/BaseGame.vue
index f5eb9a67..ef6ef01e 100644
--- a/client/src/components/BaseGame.vue
+++ b/client/src/components/BaseGame.vue
@@ -216,8 +216,8 @@ export default {
         vr_tmp.play(move);
         move.fen = vr_tmp.getFen();
       });
-      if (this.game.fenStart.indexOf(" b ") >= 0 ||
-        (this.moves.length > 0 && this.moves[0].color == "b"))
+      if ((this.moves.length > 0 && this.moves[0].color == "b") ||
+        (this.moves.length == 0 && this.vr_tmp.turn == "b"))
       {
         // 'end' is required for Board component to check lastMove for e.p.
         this.moves.unshift({color: "w", notation: "...", end: {x:-1,y:-1}});
@@ -225,7 +225,7 @@ export default {
       const L = this.moves.length;
       this.cursor = L-1;
       this.lastMove = (L > 0 ? this.moves[L-1]  : null);
-      this.incheck = [];
+      this.incheck = this.vr.getCheckSquares(this.vr.turn);
     },
     analyzePosition: function() {
       const newUrl = "/analyse/" + this.game.vname +
diff --git a/client/src/store.js b/client/src/store.js
index bcee338e..52a5247b 100644
--- a/client/src/store.js
+++ b/client/src/store.js
@@ -14,16 +14,16 @@ export const store =
   socketCloseListener: null,
   initialize() {
     ajax("/variants", "GET", res => { this.state.variants = res.variantArray; });
-    let mysid = localStorage["mysid"];
+    let mysid = localStorage.getItem("mysid");
     if (!mysid)
     {
       mysid = getRandString();
-      localStorage["mysid"] = mysid; //done only once (unless user clear browser data)
+      localStorage.setItem("mysid", mysid); //done only once (unless user clear browser data)
     }
     // Quick user setup using local storage:
     this.state.user = {
-      id: localStorage["myid"] || 0,
-      name: localStorage["myname"] || "", //"" for "anonymous"
+      id: localStorage.getItem("myid") || 0,
+      name: localStorage.getItem("myname") || "", //"" for "anonymous"
       email: "", //unknown yet
       notify: false, //email notifications
       sid: mysid,
@@ -32,7 +32,17 @@ export const store =
     // NOTE: still superficial identity usurpation possible, but difficult.
     ajax("/whoami", "GET", res => {
       this.state.user.id = res.id;
+      const storedId = localStorage.getItem("myid");
+      if (res.id > 0 && !storedId) //user cleared localStorage
+        localStorage.setItem("myid", res.id);
+      else if (res.id == 0 && !!storedId) //user cleared cookie
+        localStorage.removeItem("myid");
       this.state.user.name = res.name;
+      const storedName = localStorage.getItem("myname");
+      if (!!res.name && !storedName) //user cleared localStorage
+        localStorage.setItem("myname", res.name);
+      else if (!res.name && !!storedName) //user cleared cookie
+        localStorage.removeItem("myname");
       this.state.user.email = res.email;
       this.state.user.notify = res.notify;
     });
diff --git a/client/src/views/Game.vue b/client/src/views/Game.vue
index 51fde64d..5255cf43 100644
--- a/client/src/views/Game.vue
+++ b/client/src/views/Game.vue
@@ -85,34 +85,6 @@ export default {
       this.gameRef.rid = to.query["rid"];
       this.loadGame();
     },
-    "game.clocks": function(newState) {
-      if (this.game.moves.length < 2 || this.game.score != "*")
-      {
-        // 1st move not completed yet, or game over: freeze time
-        this.virtualClocks = newState.map(s => ppt(s));
-        return;
-      }
-      const currentTurn = this.vr.turn;
-      const colorIdx = ["w","b"].indexOf(currentTurn);
-      let countdown = newState[colorIdx] -
-        (Date.now() - this.game.initime[colorIdx])/1000;
-      this.virtualClocks = [0,1].map(i => {
-        const removeTime = i == colorIdx
-          ? (Date.now() - this.game.initime[colorIdx])/1000
-          : 0;
-        return ppt(newState[i] - removeTime);
-      });
-      let clockUpdate = setInterval(() => {
-        if (countdown < 0 || this.vr.turn != currentTurn || this.game.score != "*")
-        {
-          clearInterval(clockUpdate);
-          if (countdown < 0)
-            this.gameOver(this.vr.turn=="w" ? "0-1" : "1-0", this.st.tr["Time"]);
-        }
-        else
-          this.$set(this.virtualClocks, colorIdx, ppt(Math.max(0, --countdown)));
-      }, 1000);
-    },
   },
   // NOTE: some redundant code with Hall.vue (mostly related to people array)
   created: function() {
@@ -421,6 +393,10 @@ export default {
         this.vr = new V(game.fen);
         const gtype = (game.cadence.indexOf('d') >= 0 ? "corr" : "live");
         const tc = extractTime(game.cadence);
+        const myIdx = game.players.findIndex(p => {
+          return p.sid == this.st.user.sid || p.uid == this.st.user.id;
+        });
+        const mycolor = [undefined,"w","b"][myIdx+1]; //undefined for observers
         if (!game.chats)
           game.chats = []; //live games don't have chat history
         if (gtype == "corr")
@@ -453,8 +429,7 @@ export default {
             if (L >= 1)
               game.initime[L%2] = game.moves[L-1].played;
           }
-          // Now that we used idx and played, re-format moves as for live games
-          game.moves = game.moves.map( (m) => {
+          const reformattedMoves = game.moves.map( (m) => {
             const s = m.squares;
             return {
               appear: s.appear,
@@ -463,12 +438,34 @@ export default {
               end: s.end,
             };
           });
-          // Also sort chat messages (if any)
+          // Sort chat messages from newest to oldest
           game.chats.sort( (c1,c2) => { return c2.added - c1.added; });
+          if (myIdx >= 0 && game.chats.length > 0)
+          {
+            // TODO: group multi-moves into an array, to deduce color from index
+            // and not need this (also repeated in BaseGame::re_setVariables())
+            let vr_tmp = new V(game.fenStart); //vr is already at end of game
+            for (let i=0; i<reformattedMoves.length; i++)
+            {
+              game.moves[i].color = vr_tmp.turn;
+              vr_tmp.play(reformattedMoves[i]);
+            }
+            // Blue background on chat button if last chat message arrived after my last move.
+            let dtLastMove = 0;
+            for (let midx = game.moves.length-1; midx >= 0; midx--)
+            {
+              if (game.moves[midx].color == mycolor)
+              {
+                dtLastMove = game.moves[midx].played;
+                break;
+              }
+            }
+            if (dtLastMove < game.chats[0].added)
+              document.getElementById("chatBtn").classList.add("somethingnew");
+          }
+          // Now that we used idx and played, re-format moves as for live games
+          game.moves = reformattedMoves;
         }
-        const myIdx = game.players.findIndex(p => {
-          return p.sid == this.st.user.sid || p.uid == this.st.user.id;
-        });
         if (gtype == "live" && game.clocks[0] < 0) //game unstarted
         {
           game.clocks = [tc.mainTime, tc.mainTime];
@@ -512,13 +509,14 @@ export default {
           {
             type: gtype,
             increment: tc.increment,
-            mycolor: [undefined,"w","b"][myIdx+1],
+            mycolor: mycolor,
             // opponent sid not strictly required (or available), but easier
             // at least oppsid or oppid is available anyway:
             oppsid: (myIdx < 0 ? undefined : game.players[1-myIdx].sid),
             oppid: (myIdx < 0 ? undefined : game.players[1-myIdx].uid),
           }
         );
+        this.re_setClocks();
         this.$nextTick(() => {
           this.game.rendered = true;
           // Did lastate arrive before game was rendered?
@@ -557,17 +555,43 @@ export default {
         GameStorage.get(this.gameRef.id, afterRetrieval);
       }
     },
+    re_setClocks: function() {
+      if (this.game.moves.length < 2 || this.game.score != "*")
+      {
+        // 1st move not completed yet, or game over: freeze time
+        this.virtualClocks = this.game.clocks.map(s => ppt(s));
+        return;
+      }
+      const currentTurn = this.vr.turn;
+      const colorIdx = ["w","b"].indexOf(currentTurn);
+      let countdown = this.game.clocks[colorIdx] -
+        (Date.now() - this.game.initime[colorIdx])/1000;
+      this.virtualClocks = [0,1].map(i => {
+        const removeTime = i == colorIdx
+          ? (Date.now() - this.game.initime[colorIdx])/1000
+          : 0;
+        return ppt(this.game.clocks[i] - removeTime);
+      });
+      let clockUpdate = setInterval(() => {
+        if (countdown < 0 || this.vr.turn != currentTurn || this.game.score != "*")
+        {
+          clearInterval(clockUpdate);
+          if (countdown < 0)
+            this.gameOver(this.vr.turn=="w" ? "0-1" : "1-0", this.st.tr["Time"]);
+        }
+        else
+          this.$set(this.virtualClocks, colorIdx, ppt(Math.max(0, --countdown)));
+      }, 1000);
+    },
     // Post-process a move (which was just played in BaseGame)
     processMove: function(move) {
       if (this.game.type == "corr" && move.color == this.game.mycolor)
       {
-
-//TODO: if takeback, then time over occurs.
-
         if (!confirm(this.st.tr["Move played:"] + " " + move.notation + "\n" + this.st.tr["Are you sure?"]))
+        {
           return this.$set(this.game, "moveToUndo", move);
+        }
       }
-      // Update storage (corr or live) if I play in the game
       const colorIdx = ["w","b"].indexOf(move.color);
       const nextIdx = ["w","b"].indexOf(this.vr.turn);
       // https://stackoverflow.com/a/38750895
@@ -609,9 +633,10 @@ export default {
       // Update current game object:
       this.game.moves.push(move);
       this.game.fen = move.fen;
-      this.$set(this.game.clocks, colorIdx, this.game.clocks[colorIdx] + addTime);
+      this.game.clocks[colorIdx] += addTime;
       // move.initime is set only when I receive a "lastate" move from opponent
       this.game.initime[nextIdx] = move.initime || Date.now();
+      this.re_setClocks();
       // If repetition detected, consider that a draw offer was received:
       const fenObj = V.ParseFen(move.fen);
       let repIdx = fenObj.position + "_" + fenObj.turn;
-- 
2.44.0