-https://audiojungle.net/item/chess-pieces-and-board/11657386
-http://freesound.org/search/?q=cancel + sword
+http://soundbible.com/1531-Temple-Bell.html
+++ /dev/null
-#$# git-fat d9100d524f9bca2f601823d2422bd3c21ed1329c 18889
+++ /dev/null
-#$# git-fat fca619a21ac9647e2cf6888ecc78e84a3ca8011c 14194
--- /dev/null
+#$# git-fat 839f8d81e4e6bf135e3828be652483420d4118b8 917140
+++ /dev/null
-#$# git-fat 527d57bf4f0c83bbfdcfc18d2f9503c28b8e3cc5 8305
})();
};
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) {
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();
}
<template lang="pug">
div
+ button(@click="clearHistory()")
+ | {{ st.tr["Clear chat"] }}
input#inputChat(
type="text"
:placeholder="st.tr['Chat here']"
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");
}
}
};
// 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 => {
: "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;
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>
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);
}
}
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);
About: "About",
"Accept draw?": "Accept draw?",
"Accept challenge?": "Accept challenge?",
- All: "All",
Analyse: "Analyse",
"Analysis mode": "Analysis mode",
"Any player": "Any player",
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",
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",
"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",
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",
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",
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",
"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",
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",
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",
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",
"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",
},
// 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 = [];
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);
};
: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
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);
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 });
}
}
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));
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
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);
}
});
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;
});
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";
},
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);
}
}
};
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
{
query += modifs + " WHERE id = " + id;
db.run(query);
}
- let wrongMoveIndex = false;
if (obj.move)
{
// Security: only update moves if index is right
+ 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);
+ }
});
},
}
});
-// 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;
}
});
+// 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;
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