Improvements - untested
authorBenjamin Auder <benjamin.auder@somewhere>
Sat, 29 Feb 2020 12:42:46 +0000 (13:42 +0100)
committerBenjamin Auder <benjamin.auder@somewhere>
Sat, 29 Feb 2020 12:42:46 +0000 (13:42 +0100)
20 files changed:
client/public/sounds/SOURCE
client/public/sounds/move.mp3 [deleted file]
client/public/sounds/newgame.mp3 [deleted file]
client/public/sounds/newgame.wav [new file with mode: 0644]
client/public/sounds/undo.mp3 [deleted file]
client/src/components/BaseGame.vue
client/src/components/Chat.vue
client/src/components/GameList.vue
client/src/components/Settings.vue
client/src/store.js
client/src/translations/en.js
client/src/translations/es.js
client/src/translations/fr.js
client/src/utils/gameStorage.js
client/src/views/Game.vue
client/src/views/Hall.vue
client/src/views/MyGames.vue
server/models/Game.js
server/routes/games.js
server/sockets.js

index dd2e286..1182b94 100644 (file)
@@ -1,2 +1 @@
-https://audiojungle.net/item/chess-pieces-and-board/11657386
-http://freesound.org/search/?q=cancel + sword
+http://soundbible.com/1531-Temple-Bell.html
diff --git a/client/public/sounds/move.mp3 b/client/public/sounds/move.mp3
deleted file mode 100644 (file)
index 8586f77..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#$# git-fat d9100d524f9bca2f601823d2422bd3c21ed1329c                18889
diff --git a/client/public/sounds/newgame.mp3 b/client/public/sounds/newgame.mp3
deleted file mode 100644 (file)
index 09dac63..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#$# git-fat fca619a21ac9647e2cf6888ecc78e84a3ca8011c                14194
diff --git a/client/public/sounds/newgame.wav b/client/public/sounds/newgame.wav
new file mode 100644 (file)
index 0000000..f88939a
--- /dev/null
@@ -0,0 +1 @@
+#$# git-fat 839f8d81e4e6bf135e3828be652483420d4118b8               917140
diff --git a/client/public/sounds/undo.mp3 b/client/public/sounds/undo.mp3
deleted file mode 100644 (file)
index d767314..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#$# git-fat 527d57bf4f0c83bbfdcfc18d2f9503c28b8e3cc5                 8305
index 2a54cbb..9783c71 100644 (file)
@@ -355,8 +355,6 @@ export default {
         })();
       };
       const afterMove = (smove, initurn) => {
-        if (this.st.settings.sound == 2)
-          new Audio("/sounds/move.mp3").play().catch(() => {});
         if (this.vr.turn != initurn) {
           // Turn has changed: move is complete
           if (!smove.fen) {
@@ -440,8 +438,6 @@ export default {
         if (light) this.cursor--;
         else {
           this.positionCursorTo(this.cursor - 1);
-          if (this.st.settings.sound == 2)
-            new Audio("/sounds/undo.mp3").play().catch(() => {});
           this.incheck = this.vr.getCheckSquares(this.vr.turn);
           this.emitFenIfAnalyze();
         }
index f1e1fad..7f80767 100644 (file)
@@ -1,5 +1,7 @@
 <template lang="pug">
 div
+  button(@click="clearHistory()")
+    | {{ st.tr["Clear chat"] }}
   input#inputChat(
     type="text"
     :placeholder="st.tr['Chat here']"
@@ -51,6 +53,10 @@ export default {
       const chat = { msg: chatTxt, name: this.st.user.name || "@nonymous" };
       this.$emit("mychat", chat);
       this.chats.unshift(chat);
+    },
+    clearHistory: function() {
+      this.chats = [];
+      this.$emit("chatcleared");
     }
   }
 };
index 6bf0e9a..39ad9bf 100644 (file)
@@ -54,6 +54,13 @@ export default {
       // Show in order: games where it's my turn, my running games, my games, other games
       let minCreated = Number.MAX_SAFE_INTEGER;
       let maxCreated = 0;
+      const isMyTurn = (g, myColor) => {
+        const rem = g.movesCount % 2;
+        return (
+          (rem == 0 && myColor == "w") ||
+          (rem == 1 && myColor == "b")
+        );
+      };
       let augmentedGames = this.games
         .filter(g => !this.deleted[g.id])
         .map(g => {
@@ -72,11 +79,7 @@ export default {
                 : "b";
             if (g.score == "*") {
               priority++;
-              // I play in this game, so g.fen will be defined
-              // NOTE: this is a fragile way to detect turn,
-              // but since V isn't defined let's do that for now. (TODO:)
-              //if (V.ParseFen(g.fen).turn == myColor)
-              if (g.fen.match(" " + myColor + " ")) priority++;
+              if (isMyTurn(g, myColor)) priority++;
             }
           }
           if (g.created < minCreated) minCreated = g.created;
index a6c3e65..d01f2cd 100644 (file)
@@ -46,11 +46,12 @@ div
             option(value="chesscom") {{ st.tr["green"] }}
             option(value="chesstempo") {{ st.tr["blue"] }}
         fieldset
-          label(for="setSound") {{ st.tr["Play sounds?"] }}
-          select#setSound(v-model="st.settings.sound")
-            option(value="0") {{ st.tr["None"] }}
-            option(value="1") {{ st.tr["New game"] }}
-            option(value="2") {{ st.tr["All"] }}
+          label(for="setSound")
+            | {{ st.tr["Sound on new game?"] }}
+          input#setSound(
+            type="checkbox"
+            v-model="st.settings.sound"
+          )
 </template>
 
 <script>
@@ -82,10 +83,9 @@ export default {
       const propName = event.target.id
         .substr(3)
         .replace(/^\w/, c => c.toLowerCase());
-      let value = ["bcolor", "sound"].includes(propName)
+      const value = propName == "bcolor"
         ? event.target.value
         : event.target.checked;
-      if (propName == "sound") value = parseInt(value);
       store.updateSetting(propName, value);
     }
   }
index 52870e0..b81ec55 100644 (file)
@@ -53,11 +53,16 @@ export const store = {
       this.state.user.notify = res.notify;
     });
     // Settings initialized with values from localStorage
+    const getItemDefaultTrue = (item) => {
+      const value = localStorage.getItem(item);
+      if (!value) return true;
+      return value == "true";
+    };
     this.state.settings = {
       bcolor: localStorage.getItem("bcolor") || "lichess",
-      sound: parseInt(localStorage.getItem("sound")) || 1,
-      hints: localStorage.getItem("hints") == "true",
-      highlight: localStorage.getItem("highlight") == "true"
+      sound: getItemDefaultTrue("sound"),
+      hints: getItemDefaultTrue("hints"),
+      highlight: getItemDefaultTrue("highlight")
     };
     const supportedLangs = ["en", "es", "fr"];
     const navLanguage = navigator.language.substr(0,2);
index 4416b76..ff178cd 100644 (file)
@@ -3,7 +3,6 @@ export const translations = {
   About: "About",
   "Accept draw?": "Accept draw?",
   "Accept challenge?": "Accept challenge?",
-  All: "All",
   Analyse: "Analyse",
   "Analysis mode": "Analysis mode",
   "Any player": "Any player",
@@ -22,6 +21,7 @@ export const translations = {
   Challenge: "Challenge",
   "Challenge declined": "Challenge declined",
   "Chat here": "Chat here",
+  "Clear history": "Clear history",
   "Connection token sent. Check your emails!": "Connection token sent. Check your emails!",
   Contact: "Contact",
   "Correspondance challenges": "Correspondance challenges",
@@ -71,13 +71,11 @@ export const translations = {
   News: "News",
   "No more problems": "No more problems",
   "No subject. Send anyway?": "No subject. Send anyway?",
-  None: "None",
   "Notifications by email": "Notifications by email",
   Number: "Number",
   Observe: "Observe",
   "Offer draw?": "Offer draw?",
   "Opponent action": "Opponent action",
-  "Play sounds?": "Play sounds?",
   "Play with?": "Play with?",
   Players: "Players",
   "Please log in to accept corr challenges": "Please log in to accept corr challenges",
@@ -105,6 +103,7 @@ export const translations = {
   "Show possible moves?": "Show possible moves?",
   "Show solution": "Show solution",
   Solution: "Solution",
+  "Sound alert when game starts?": "Sound alert when game starts?",
   Stop: "Stop",
   "Stop game": "Stop game",
   Subject: "Subject",
index da8d673..eb08331 100644 (file)
@@ -3,7 +3,6 @@ export const translations = {
   About: "Acerca de",
   "Accept draw?": "¿Acceptar tablas?",
   "Accept challenge?": "¿Acceptar el desafío?",
-  All: "Todos",
   Analyse: "Analizar",
   "Analysis mode": "Modo análisis",
   "Any player": "Cualquier jugador",
@@ -22,6 +21,7 @@ export const translations = {
   Challenge: "Desafiar",
   "Challenge declined": "Desafío rechazado",
   "Chat here": "Chat aquí",
+  "Clear history": "Clara historia",
   "Connection token sent. Check your emails!": "Token de conexión enviado. ¡Revisa tus correos!",
   Contact: "Contacto",
   "Correspondance challenges": "Desafíos por correspondencia",
@@ -71,13 +71,11 @@ export const translations = {
   News: "Noticias",
   "No more problems": "No mas problemas",
   "No subject. Send anyway?": "Sin asunto. ¿Enviar sin embargo?",
-  None: "Ninguno",
   "Notifications by email": "Notificaciones por email",
   Number: "Número",
   "Offer draw?": "¿Ofrecer tablas?",
   Observe: "Observar",
   "Opponent action": "Acción del adversario",
-  "Play sounds?": "¿Permitir sonidos?",
   "Play with?": "¿Jugar con?",
   Players: "Jugadores",
   "Please log in to accept corr challenges": "Inicia sesión para aceptar los desafíos por correspondencia",
@@ -105,6 +103,7 @@ export const translations = {
   "Show possible moves?": "¿Mostrar posibles movimientos?",
   "Show solution": "Mostrar la solución",
   Solution: "Solución",
+  "Sound alert when game starts?": "¿Alerta audible cuando comienza una partida?",
   Stop: "Interrupción",
   "Stop game": "Terminar la partida",
   Subject: "Asunto",
index b64ecc2..6e37e60 100644 (file)
@@ -3,7 +3,6 @@ export const translations = {
   About: "À propos",
   "Accept draw?": "Accepter la nulle ?",
   "Accept challenge?": "Accepter le défi ?",
-  All: "Tous",
   Analyse: "Analyser",
   "Analysis mode": "Mode analyse",
   "Any player": "N'importe qui",
@@ -22,6 +21,7 @@ export const translations = {
   Challenge: "Défier",
   "Challenge declined": "Défi refusé",
   "Chat here": "Chattez ici",
+  "Clear history": "Effacer l'historique",
   "Connection token sent. Check your emails!": "Token de connection envoyé. Allez voir vos emails !",
   Contact: "Contact",
   "Correspondance challenges": "Défis par correspondance",
@@ -71,13 +71,11 @@ export const translations = {
   News: "Nouvelles",
   "No more problems": "Plus de problèmes",
   "No subject. Send anyway?": "Pas de sujet. Envoyer quand-même ??",
-  None: "Aucun",
   "Notifications by email": "Notifications par email",
   Number: "Numéro",
   "Offer draw?": "Proposer nulle ?",
   Observe: "Observer",
   "Opponent action": "Action de l'adversaire",
-  "Play sounds?": "Jouer les sons ?",
   "Play with?": "Jouer avec ?",
   Players: "Joueurs",
   "Please log in to accept corr challenges": "Identifiez vous pour accepter des défis par correspondance",
@@ -105,6 +103,7 @@ export const translations = {
   "Show possible moves?": "Montrer les coups possibles ?",
   "Show solution": "Montrer la solution",
   Solution: "Solution",
+  "Sound alert when game starts?": "Alert sonore quand une partie démarre?",
   Stop: "Arrêt",
   "Stop game": "Arrêter la partie",
   Subject: "Sujet",
index ce609af..4eb6bc7 100644 (file)
@@ -96,7 +96,8 @@ export const GameStorage = {
   },
 
   // Retrieve all local games (running, completed, imported...)
-  getAll: function(callback) {
+  // light: do not retrieve moves or players or clocks (TODO: this is the only usage)
+  getAll: function(light, callback) {
     dbOperation((err,db) => {
       let objectStore = db.transaction("games").objectStore("games");
       let games = [];
@@ -104,7 +105,15 @@ export const GameStorage = {
         let cursor = event.target.result;
         // if there is still another cursor to go, keep running this code
         if (cursor) {
-          games.push(cursor.value);
+          let g = cursor.value;
+          if (light) {
+            g.movesCount = g.moves.length;
+            delete g.moves;
+            delete g.clocks;
+            delete g.initime;
+            delete g.players;
+          }
+          games.push(g);
           cursor.continue();
         } else callback(games);
       };
index 2da8d9e..23c4578 100644 (file)
@@ -24,6 +24,7 @@ main
         :pastChats="game.chats"
         :newChat="newChat"
         @mychat="processChat"
+        @chatcleared="clearChat"
       )
   .row
     #aboveBoard.col-sm-12.col-md-9.col-md-offset-3.col-lg-10.col-lg-offset-2
@@ -193,6 +194,29 @@ export default {
         Object.values(this.people).some(p => p.id == player.uid)
       );
     },
+    resetChatColor: function() {
+      // TODO: this is called twice, once on opening an once on closing
+      document.getElementById("chatBtn").classList.remove("somethingnew");
+    },
+    processChat: function(chat) {
+      this.send("newchat", { data: chat });
+      // NOTE: anonymous chats in corr games are not stored on server (TODO?)
+      if (this.game.type == "corr" && this.st.user.id > 0)
+        GameStorage.update(this.gameRef.id, { chat: chat });
+    },
+    clearChat: function() {
+      // Nothing more to do if game is live (chats not recorded)
+      if (this.game.mycolor && this.game.type == "corr") {
+        ajax(
+          "/chats",
+          "DELETE",
+          {gid: this.game.id},
+          () => {
+            this.$set(this.game, "pastChats", []);
+          }
+        );
+      }
+    },
     socketMessageListener: function(msg) {
       if (!this.conn) return;
       const data = JSON.parse(msg.data);
@@ -634,7 +658,10 @@ export default {
           const sendMove = {
             move: filtered_move,
             addTime: addTime,
-            cancelDrawOffer: this.drawOffer == ""
+            cancelDrawOffer: this.drawOffer == "",
+            // Players' SID required for /mygames page
+            // TODO: precompute and add this field to game object?
+            players: this.game.players.map(p => p.sid)
           };
           this.send("newmove", { data: sendMove });
         }
@@ -717,16 +744,6 @@ export default {
       }
       else doProcessMove();
     },
-    resetChatColor: function() {
-      // TODO: this is called twice, once on opening an once on closing
-      document.getElementById("chatBtn").classList.remove("somethingnew");
-    },
-    processChat: function(chat) {
-      this.send("newchat", { data: chat });
-      // NOTE: anonymous chats in corr games are not stored on server (TODO?)
-      if (this.game.type == "corr" && this.st.user.id > 0)
-        GameStorage.update(this.gameRef.id, { chat: chat });
-    },
     gameOver: function(score, scoreMsg) {
       this.game.score = score;
       this.$set(this.game, "scoreMsg", scoreMsg || getScoreMessage(score));
index 9266089..7a7f911 100644 (file)
@@ -751,6 +751,13 @@ export default {
         localStorage.setItem("cadence", chall.cadence);
         localStorage.setItem("vid", chall.vid);
         document.getElementById("modalNewgame").checked = false;
+        // Show the challenge if not on current display
+        if (
+          (ctype == "live" && this.cdisplay == "corr") ||
+          (ctype == "corr" && this.cdisplay == "live")
+        ) {
+          this.setDisplay('c', ctype);
+        }
       };
       if (ctype == "live") {
         // Live challenges have a random ID
@@ -885,8 +892,8 @@ export default {
       GameStorage.add(game, (err) => {
         // If an error occurred, game is not added: abort
         if (!err) {
-          if (this.st.settings.sound >= 1)
-            new Audio("/sounds/newgame.mp3").play().catch(() => {});
+          if (this.st.settings.sound)
+            new Audio("/sounds/newgame.wav").play().catch(() => {});
           this.$router.push("/game/" + gameInfo.id);
         }
       });
index 41cced5..e0914f6 100644 (file)
@@ -34,11 +34,13 @@ export default {
       st: store.state,
       display: "live",
       liveGames: [],
-      corrGames: []
+      corrGames: [],
+      conn: null,
+      connexionString: ""
     };
   },
   created: function() {
-    GameStorage.getAll(localGames => {
+    GameStorage.getAll(true, localGames => {
       localGames.forEach(g => (g.type = this.classifyObject(g)));
       this.liveGames = localGames;
     });
@@ -48,6 +50,18 @@ export default {
         this.corrGames = res.games;
       });
     }
+    // Initialize connection
+    this.connexionString =
+      params.socketUrl +
+      "/?sid=" +
+      this.st.user.sid +
+      "&tmpId=" +
+      getRandString() +
+      "&page=" +
+      encodeURIComponent(this.$route.path);
+    this.conn = new WebSocket(this.connexionString);
+    this.conn.onmessage = this.socketMessageListener;
+    this.conn.onclose = this.socketCloseListener;
   },
   mounted: function() {
     const showType = localStorage.getItem("type-myGames") || "live";
@@ -69,6 +83,23 @@ export default {
     },
     showGame: function(g) {
       this.$router.push("/game/" + g.id);
+    },
+    socketMessageListener: function(msg) {
+      const data = JSON.parse(msg.data);
+      // Only event is newmove, and received only:
+      if (data.code == "newmove") {
+        let games = !!parseInt(data.gid)
+          ? this.corrGames
+          : this.liveGames;
+        // NOTE: new move itself is not received, because it wouldn't be used.
+        let g = games.find(g => g.id == data.gid);
+        this.$set(g, "movesCount", g.movesCount + 1);
+      }
+    },
+    socketCloseListener: function() {
+      this.conn = new WebSocket(this.connexionString);
+      this.conn.addEventListener("message", this.socketMessageListener);
+      this.conn.addEventListener("close", this.socketCloseListener);
     }
   }
 };
index 21ece2f..3fde91f 100644 (file)
@@ -91,11 +91,18 @@ const GameModel =
         db.all(query, (err2,players) => {
           if (light)
           {
-            const game = Object.assign({},
-              gameInfo,
-              {players: players}
-            );
-            cb(null, game);
+            query =
+              "SELECT COUNT(*) AS nbMoves " +
+              "FROM Moves " +
+              "WHERE gid = " + id;
+            db.get(query, (err,ret) => {
+              const game = Object.assign({},
+                gameInfo,
+                {players: players},
+                {movesCount: ret.nbMoves}
+              );
+              cb(null, game);
+            });
           }
           else
           {
@@ -235,7 +242,6 @@ const GameModel =
         query += modifs + " WHERE id = " + id;
         db.run(query);
       }
-      let wrongMoveIndex = false;
       if (obj.move)
       {
         // Security: only update moves if index is right
@@ -263,6 +269,14 @@ const GameModel =
             + id + ",?,'" + obj.chat.name + "'," + Date.now() + ")";
         db.run(query, obj.chat.msg);
       }
+      else if (obj.delchat)
+      {
+        query =
+          "DELETE " +
+          "FROM Chats " +
+          "WHERE gid = " + id;
+        db.run(query, cb);
+      }
     });
   },
 
index 1b788cb..607ab39 100644 (file)
@@ -54,7 +54,7 @@ router.get("/games", access.ajax, (req,res) => {
   }
 });
 
-// New move + fen update + score + chats...
+// FEN update + score(Msg) + draw status / and new move + chats
 router.put("/games", access.logged, access.ajax, (req,res) => {
   const gid = req.body.gid;
   const obj = req.body.newObj;
@@ -83,4 +83,18 @@ router.put("/games", access.logged, access.ajax, (req,res) => {
   }
 });
 
+// TODO: chats deletion here, but could/should be elsewhere.
+// Moves update also could, although logical unit in a game.
+router.delete("/chats", access.logged, access.ajax, (req,res) => {
+  const gid = req.query["gid"];
+  GameModel.getPlayers(gid, (err,players) => {
+    if (players.some(p => p.uid == req.userId))
+    {
+      GameModel.update(gid, {delchat: true}, (err) => {
+        res.json(err || {});
+      });
+    }
+  });
+});
+
 module.exports = router;
index 42a8840..cf5ea1b 100644 (file)
@@ -190,8 +190,30 @@ module.exports = function(wss) {
         case "abort":
         case "drawoffer":
         case "draw":
+        {
           notifyRoom(page, obj.code, {data:obj.data});
+          const mygamesPg = "/mygames";
+          if (obj.code == "newmove" && clients[mygamesPg])
+          {
+            // Relay newmove info to myGames page
+            // NOTE: the move itself is not needed (for now at least)
+            const newmoveForMygames = {
+              gid: page.split("/")[2] //format is "/game/gid"
+            };
+            obj.data.players.forEach(pSid => {
+              if (clients[mygamesPg][pSid])
+              {
+                Object.keys(clients[mygamesPg][pSid]).forEach(x => {
+                  send(
+                    clients[mygamesPg][pSid][x],
+                    {code:"newmove", data:newmoveForMygames}
+                  );
+                });
+              }
+            });
+          }
           break;
+        }
 
         case "result":
           // Special case: notify all, 'transroom': Game --> Hall