Add unambiguous section in the PGN + some fixes + code formatting and fix typos
[vchess.git] / client / src / views / Game.vue
index 6ef161f..fe55f74 100644 (file)
@@ -131,6 +131,7 @@ import Chat from "@/components/Chat.vue";
 import { store } from "@/store";
 import { GameStorage } from "@/utils/gameStorage";
 import { ppt } from "@/utils/datetime";
+import { notify } from "@/utils/notifications";
 import { ajax } from "@/utils/ajax";
 import { extractTime } from "@/utils/timeControl";
 import { getRandString } from "@/utils/alea";
@@ -154,6 +155,7 @@ export default {
       gameRef: "",
       nextIds: [],
       game: {}, //passed to BaseGame
+      focus: !document.hidden, //will not always work... TODO
       // virtualClocks will be initialized from true game.clocks
       virtualClocks: [],
       vr: null, //"variant rules" object initialized from FEN
@@ -229,6 +231,8 @@ export default {
     cleanBeforeDestroy: function() {
       clearInterval(this.socketCloseListener);
       document.removeEventListener('visibilitychange', this.visibilityChange);
+      window.removeEventListener('focus', this.onFocus);
+      window.removeEventListener('blur', this.onBlur);
       if (!!this.askLastate) clearInterval(this.askLastate);
       if (!!this.retrySendmove) clearInterval(this.retrySendmove);
       if (!!this.clockUpdate) clearInterval(this.clockUpdate);
@@ -238,11 +242,25 @@ export default {
     },
     visibilityChange: function() {
       // TODO: Use document.hidden? https://webplatform.news/issues/2019-03-27
-      this.send(
-        document.visibilityState == "visible"
-          ? "getfocus"
-          : "losefocus"
-      );
+      this.focus = (document.visibilityState == "visible");
+      if (!this.focus && !!this.rematchOffer) {
+        this.rematchOffer = "";
+        this.send("rematchoffer", { data: false });
+        // Do not remove rematch offer from (local) storage
+      }
+      this.send(this.focus ? "getfocus" : "losefocus");
+    },
+    onFocus: function() {
+      this.focus = true;
+      this.send("getfocus");
+    },
+    onBlur: function() {
+      this.focus = false;
+      if (!!this.rematchOffer) {
+        this.rematchOffer = "";
+        this.send("rematchoffer", { data: false });
+      }
+      this.send("losefocus");
     },
     participateInChat: function(p) {
       return Object.keys(p.tmpIds).some(x => p.tmpIds[x].focus) && !!p.name;
@@ -256,6 +274,8 @@ export default {
     },
     atCreation: function() {
       document.addEventListener('visibilitychange', this.visibilityChange);
+      window.addEventListener('focus', this.onFocus);
+      window.addEventListener('blur', this.onBlur);
       // 0] (Re)Set variables
       this.gameRef = this.$route.params["id"];
       // next = next corr games IDs to navigate faster (if applicable)
@@ -312,7 +332,8 @@ export default {
       this.socketCloseListener = setInterval(
         () => {
           if (this.conn.readyState == 3) {
-            this.conn.removeEventListener("message", this.socketMessageListener);
+            this.conn.removeEventListener(
+              "message", this.socketMessageListener);
             this.conn = new WebSocket(this.connexionString);
             this.conn.addEventListener("message", this.socketMessageListener);
           }
@@ -334,7 +355,7 @@ export default {
           this.loadVariantThenGame(game, () => socketInit(this.roomInit));
         else
           // Live game stored remotely: need socket to retrieve it
-          // NOTE: the callback "roomInit" will be lost, so we don't provide it.
+          // NOTE: the callback "roomInit" will be lost, so it's not provided.
           // --> It will be given when receiving "fullgame" socket event.
           socketInit(() => { this.send("askfullgame"); });
       });
@@ -646,7 +667,7 @@ export default {
         case "asklastate":
           // Sending informative last state if I played a move or score != "*"
           // If the game or moves aren't loaded yet, delay the sending:
-          // TODO: since socket init after game load, the game is supposedly ready
+          // TODO: socket init after game load, so the game is supposedly ready
           if (!this.game || !this.game.moves) this.lastateAsked = true;
           else this.sendLastate(data.from);
           break;
@@ -680,9 +701,25 @@ export default {
             } else {
               this.gotMoveIdx = movePlus.index;
               const receiveMyMove = (movePlus.color == this.game.mycolor);
-              if (!receiveMyMove && !!this.game.mycolor)
+              const moveColIdx = ["w", "b"].indexOf(movePlus.color);
+              if (!receiveMyMove && !!this.game.mycolor) {
                 // Notify opponent that I got the move:
-                this.send("gotmove", {data: movePlus.index, target: data.from});
+                this.send(
+                  "gotmove",
+                  { data: movePlus.index, target: data.from }
+                );
+                // And myself if I'm elsewhere:
+                if (!this.focus) {
+                  notify(
+                    "New move",
+                    {
+                      body:
+                        (this.game.players[moveColIdx].name || "@nonymous") +
+                        " just played."
+                    }
+                  );
+                }
+              }
               if (movePlus.cancelDrawOffer) {
                 // Opponent refuses draw
                 this.drawOffer = "";
@@ -695,8 +732,8 @@ export default {
                   GameStorage.update(this.gameRef, { drawOffer: "" });
                 }
               }
-              this.$refs["basegame"].play(movePlus.move, "received", null, true);
-              const moveColIdx = ["w", "b"].indexOf(movePlus.color);
+              this.$refs["basegame"].play(
+                movePlus.move, "received", null, true);
               this.game.clocks[moveColIdx] = movePlus.clock;
               this.processMove(
                 movePlus.move,
@@ -950,7 +987,8 @@ export default {
       }
     },
     abortGame: function() {
-      if (!this.game.mycolor || !confirm(this.st.tr["Terminate game?"])) return;
+      if (!this.game.mycolor || !confirm(this.st.tr["Terminate game?"]))
+        return;
       this.gameOver("?", "Stop");
       this.send("abort");
     },
@@ -969,9 +1007,10 @@ export default {
       const myIdx = game.players.findIndex(p => {
         return p.sid == this.st.user.sid || p.id == this.st.user.id;
       });
-      const mycolor = [undefined, "w", "b"][myIdx + 1]; //undefined for observers
-      // Live games before 26/03/2020 don't have chat history. TODO: remove next line
-      if (!game.chats) game.chats = [];
+      // "mycolor" is undefined for observers
+      const mycolor = [undefined, "w", "b"][myIdx + 1];
+      // Live games before 26/03/2020 don't have chat history:
+      if (!game.chats) game.chats = []; //TODO: remove line
       // Sort chat messages from newest to oldest
       game.chats.sort((c1, c2) => c2.added - c1.added);
       if (gtype == "corr") {
@@ -1295,7 +1334,7 @@ export default {
               });
             };
             // The active tab can update storage immediately
-            if (!document.hidden) updateStorage();
+            if (this.focus) updateStorage();
             // Small random delay otherwise
             else setTimeout(updateStorage, 500 + 1000 * Math.random());
           }
@@ -1305,7 +1344,8 @@ export default {
           let sendMove = {
             move: filtered_move,
             index: origMovescount,
-            // color is required to check if this is my move (if several tabs opened)
+            // color is required to check if this is my move
+            // (if several tabs opened)
             color: moveCol,
             cancelDrawOffer: this.drawOffer == ""
           };
@@ -1426,10 +1466,18 @@ export default {
         };
         if (this.game.type == "live") {
           GameStorage.update(this.gameRef, scoreObj);
+          // Notify myself locally if I'm elsewhere:
+          if (!this.focus) {
+            notify(
+              "Game over",
+              { body: score + " : " + scoreMsg }
+            );
+          }
           if (!!callback) callback();
         }
         else this.updateCorrGame(scoreObj, callback);
-        // Notify the score to main Hall. TODO: only one player (currently double send)
+        // Notify the score to main Hall.
+        // TODO: only one player (currently double send)
         this.send("result", { gid: this.game.id, score: score });
         // Also to MyGames page (TODO: doubled as well...)
         this.notifyMyGames(