From db1f1f9adb920605c7a16b060a7737e54636ee08 Mon Sep 17 00:00:00 2001 From: Benjamin Auder <benjamin.auder@somewhere> Date: Sat, 29 Feb 2020 13:42:46 +0100 Subject: [PATCH] Improvements - untested --- client/public/sounds/SOURCE | 3 +-- client/public/sounds/move.mp3 | 1 - client/public/sounds/newgame.mp3 | 1 - client/public/sounds/newgame.wav | 1 + client/public/sounds/undo.mp3 | 1 - client/src/components/BaseGame.vue | 4 --- client/src/components/Chat.vue | 6 +++++ client/src/components/GameList.vue | 13 ++++++---- client/src/components/Settings.vue | 14 +++++------ client/src/store.js | 11 ++++++--- client/src/translations/en.js | 5 ++-- client/src/translations/es.js | 5 ++-- client/src/translations/fr.js | 5 ++-- client/src/utils/gameStorage.js | 13 ++++++++-- client/src/views/Game.vue | 39 +++++++++++++++++++++--------- client/src/views/Hall.vue | 11 +++++++-- client/src/views/MyGames.vue | 35 +++++++++++++++++++++++++-- server/models/Game.js | 26 +++++++++++++++----- server/routes/games.js | 16 +++++++++++- server/sockets.js | 22 +++++++++++++++++ 20 files changed, 175 insertions(+), 57 deletions(-) delete mode 100644 client/public/sounds/move.mp3 delete mode 100644 client/public/sounds/newgame.mp3 create mode 100644 client/public/sounds/newgame.wav delete mode 100644 client/public/sounds/undo.mp3 diff --git a/client/public/sounds/SOURCE b/client/public/sounds/SOURCE index dd2e2865..1182b943 100644 --- a/client/public/sounds/SOURCE +++ b/client/public/sounds/SOURCE @@ -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 index 8586f77b..00000000 --- a/client/public/sounds/move.mp3 +++ /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 index 09dac630..00000000 --- a/client/public/sounds/newgame.mp3 +++ /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 index 00000000..f88939a0 --- /dev/null +++ b/client/public/sounds/newgame.wav @@ -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 index d7673144..00000000 --- a/client/public/sounds/undo.mp3 +++ /dev/null @@ -1 +0,0 @@ -#$# git-fat 527d57bf4f0c83bbfdcfc18d2f9503c28b8e3cc5 8305 diff --git a/client/src/components/BaseGame.vue b/client/src/components/BaseGame.vue index 2a54cbbe..9783c71f 100644 --- a/client/src/components/BaseGame.vue +++ b/client/src/components/BaseGame.vue @@ -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(); } diff --git a/client/src/components/Chat.vue b/client/src/components/Chat.vue index f1e1fad2..7f807676 100644 --- a/client/src/components/Chat.vue +++ b/client/src/components/Chat.vue @@ -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"); } } }; diff --git a/client/src/components/GameList.vue b/client/src/components/GameList.vue index 6bf0e9ac..39ad9bf2 100644 --- a/client/src/components/GameList.vue +++ b/client/src/components/GameList.vue @@ -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; diff --git a/client/src/components/Settings.vue b/client/src/components/Settings.vue index a6c3e658..d01f2cd4 100644 --- a/client/src/components/Settings.vue +++ b/client/src/components/Settings.vue @@ -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); } } diff --git a/client/src/store.js b/client/src/store.js index 52870e0a..b81ec552 100644 --- a/client/src/store.js +++ b/client/src/store.js @@ -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); diff --git a/client/src/translations/en.js b/client/src/translations/en.js index 4416b76a..ff178cd4 100644 --- a/client/src/translations/en.js +++ b/client/src/translations/en.js @@ -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", diff --git a/client/src/translations/es.js b/client/src/translations/es.js index da8d6730..eb08331d 100644 --- a/client/src/translations/es.js +++ b/client/src/translations/es.js @@ -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", diff --git a/client/src/translations/fr.js b/client/src/translations/fr.js index b64ecc26..6e37e605 100644 --- a/client/src/translations/fr.js +++ b/client/src/translations/fr.js @@ -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", diff --git a/client/src/utils/gameStorage.js b/client/src/utils/gameStorage.js index ce609af0..4eb6bc73 100644 --- a/client/src/utils/gameStorage.js +++ b/client/src/utils/gameStorage.js @@ -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); }; diff --git a/client/src/views/Game.vue b/client/src/views/Game.vue index 2da8d9e0..23c45783 100644 --- a/client/src/views/Game.vue +++ b/client/src/views/Game.vue @@ -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)); diff --git a/client/src/views/Hall.vue b/client/src/views/Hall.vue index 92660898..7a7f911b 100644 --- a/client/src/views/Hall.vue +++ b/client/src/views/Hall.vue @@ -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); } }); diff --git a/client/src/views/MyGames.vue b/client/src/views/MyGames.vue index 41cced56..e0914f67 100644 --- a/client/src/views/MyGames.vue +++ b/client/src/views/MyGames.vue @@ -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); } } }; diff --git a/server/models/Game.js b/server/models/Game.js index 21ece2f1..3fde91f8 100644 --- a/server/models/Game.js +++ b/server/models/Game.js @@ -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); + } }); }, diff --git a/server/routes/games.js b/server/routes/games.js index 1b788cb9..607ab39e 100644 --- a/server/routes/games.js +++ b/server/routes/games.js @@ -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; diff --git a/server/sockets.js b/server/sockets.js index 42a8840f..cf5ea1b8 100644 --- a/server/sockets.js +++ b/server/sockets.js @@ -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 -- 2.44.0