X-Git-Url: https://git.auder.net/?a=blobdiff_plain;f=client%2Fsrc%2Fviews%2FGame.vue;h=e5b45ecda2a896f474ea1d7a942c948e97e64788;hb=0d5335de5c94d780e03ac0aa3279b731c69455cc;hp=ddbd3309e530766cc2177c3d44ac2f16a40d0d69;hpb=54ec15ebc0cc874cb40307d0d674964b2f0e11a4;p=vchess.git diff --git a/client/src/views/Game.vue b/client/src/views/Game.vue index ddbd3309..e5b45ecd 100644 --- a/client/src/views/Game.vue +++ b/client/src/views/Game.vue @@ -26,13 +26,10 @@ main span {{ st.tr["Participant(s):"] }} span( v-for="p in Object.values(people)" - v-if="p.focus && !!p.name" + v-if="participateInChat(p)" ) | {{ p.name }} - span.anonymous( - v-if="Object.values(people).some(p => p.focus && !p.name)" - ) - | + @nonymous + span.anonymous(v-if="someAnonymousPresent()") + @nonymous Chat( ref="chatcomp" :players="game.players" @@ -179,6 +176,7 @@ export default { // If newmove got no pingback, send again: opponentGotMove: false, connexionString: "", + socketCloseListener: 0, // Incomplete info games: show move played moveNotation: "", // Intervals from setInterval(): @@ -192,7 +190,10 @@ export default { }, watch: { $route: function(to, from) { - if (from.params["id"] != to.params["id"]) { + if (to.path.length < 6 || to.path.substr(0, 6) != "/game/") + // Page change + this.cleanBeforeDestroy(); + else if (from.params["id"] != to.params["id"]) { // Change everything: this.cleanBeforeDestroy(); let boardDiv = document.querySelector(".game"); @@ -210,7 +211,6 @@ export default { this.atCreation(); }, mounted: function() { - document.addEventListener('visibilitychange', this.visibilityChange); ["chatWrap", "infoDiv"].forEach(eltName => { document.getElementById(eltName) .addEventListener("click", processModalClick); @@ -223,10 +223,19 @@ export default { } }, beforeDestroy: function() { - document.removeEventListener('visibilitychange', this.visibilityChange); this.cleanBeforeDestroy(); }, methods: { + cleanBeforeDestroy: function() { + clearInterval(this.socketCloseListener); + document.removeEventListener('visibilitychange', this.visibilityChange); + if (!!this.askLastate) clearInterval(this.askLastate); + if (!!this.retrySendmove) clearInterval(this.retrySendmove); + if (!!this.clockUpdate) clearInterval(this.clockUpdate); + this.conn.removeEventListener("message", this.socketMessageListener); + this.send("disconnect"); + this.conn = null; + }, visibilityChange: function() { // TODO: Use document.hidden? https://webplatform.news/issues/2019-03-27 this.send( @@ -235,20 +244,34 @@ export default { : "losefocus" ); }, + participateInChat: function(p) { + return Object.keys(p.tmpIds).some(x => p.tmpIds[x].focus) && !!p.name; + }, + someAnonymousPresent: function() { + return ( + Object.values(this.people).some(p => + !p.name && Object.keys(p.tmpIds).some(x => p.tmpIds[x].focus) + ) + ); + }, atCreation: function() { + document.addEventListener('visibilitychange', this.visibilityChange); // 0] (Re)Set variables this.gameRef = this.$route.params["id"]; // next = next corr games IDs to navigate faster (if applicable) this.nextIds = JSON.parse(this.$route.query["next"] || "[]"); // Always add myself to players' list const my = this.st.user; + const tmpId = getRandString(); this.$set( this.people, my.sid, { id: my.id, name: my.name, - focus: true + tmpIds: { + tmpId: { focus: true } + } } ); this.game = { @@ -278,18 +301,24 @@ export default { // 1] Initialize connection this.connexionString = params.socketUrl + - "/?sid=" + - this.st.user.sid + - "&id=" + - this.st.user.id + - "&tmpId=" + - getRandString() + + "/?sid=" + this.st.user.sid + + "&id=" + this.st.user.id + + "&tmpId=" + tmpId + "&page=" + // Discard potential "/?next=[...]" for page indication: encodeURIComponent(this.$route.path.match(/\/game\/[a-zA-Z0-9]+/)[0]); this.conn = new WebSocket(this.connexionString); this.conn.addEventListener("message", this.socketMessageListener); - this.conn.addEventListener("close", this.socketCloseListener); + this.socketCloseListener = setInterval( + () => { + if (this.conn.readyState == 3) { + this.conn.removeEventListener("message", this.socketMessageListener); + this.conn = new WebSocket(this.connexionString); + this.conn.addEventListener("message", this.socketMessageListener); + } + }, + 1000 + ); // Socket init required before loading remote game: const socketInit = callback => { if (this.conn.readyState == 1) @@ -310,15 +339,6 @@ export default { socketInit(() => { this.send("askfullgame"); }); }); }, - cleanBeforeDestroy: function() { - if (!!this.askLastate) - clearInterval(this.askLastate); - if (!!this.retrySendmove) - clearInterval(this.retrySendmove); - if (!!this.clockUpdate) - clearInterval(this.clockUpdate); - this.send("disconnect"); - }, roomInit: function() { if (!this.roomInitialized) { // Notify the room only now that I connected, because @@ -345,14 +365,22 @@ export default { return ( ( !!player.sid && - Object.keys(this.people).some(sid => - sid == player.sid && this.people[sid].focus) + Object.keys(this.people).some(sid => { + return ( + sid == player.sid && + Object.values(this.people[sid].tmpIds).some(v => v.focus) + ); + }) ) || ( !!player.id && - Object.values(this.people).some(p => - p.id == player.id && p.focus) + Object.values(this.people).some(p => { + return ( + p.id == player.id && + Object.values(p.tmpIds).some(v => v.focus) + ); + }) ) ); }, @@ -443,37 +471,56 @@ export default { const data = JSON.parse(msg.data); switch (data.code) { case "pollclients": - // TODO: shuffling and random filtering on server, if - // the room is really crowded. - data.sockIds.forEach(sid => { + // TODO: shuffling and random filtering on server, + // if the room is really crowded. + Object.keys(data.sockIds).forEach(sid => { if (sid != this.st.user.sid) { - this.people[sid] = { focus: true }; this.send("askidentity", { target: sid }); + this.people[sid] = { tmpIds: data.sockIds[sid] }; + } else { + // Complete my tmpIds: + Object.assign(this.people[sid].tmpIds, data.sockIds[sid]); } }); break; case "connect": - if (!this.people[data.from]) { - this.people[data.from] = { focus: true }; + if (!this.people[data.from[0]]) { + // focus depends on the tmpId (e.g. tab) + this.$set( + this.people, + data.from[0], + { + tmpIds: { + [data.from[1]]: { focus: true } + } + } + ); this.newConnect[data.from] = true; //for self multi-connects tests - this.send("askidentity", { target: data.from }); + this.send("askidentity", { target: data.from[0] }); + } else { + this.people[data.from[0]].tmpIds[data.from[1]] = { focus: true }; + this.$forceUpdate(); //TODO: shouldn't be required } break; case "disconnect": - this.$delete(this.people, data.from); + if (!this.people[data.from[0]]) return; + delete this.people[data.from[0]].tmpIds[data.from[1]]; + if (Object.keys(this.people[data.from[0]].tmpIds).length == 0) + this.$delete(this.people, data.from[0]); + else this.$forceUpdate(); //TODO: shouldn't be required break; case "getfocus": { - let player = this.people[data.from]; + let player = this.people[data.from[0]]; if (!!player) { - player.focus = true; + player.tmpIds[data.from[1]].focus = true; this.$forceUpdate(); //TODO: shouldn't be required } break; } case "losefocus": { - let player = this.people[data.from]; + let player = this.people[data.from[0]]; if (!!player) { - player.focus = false; + player.tmpIds[data.from[1]].focus = false; this.$forceUpdate(); //TODO: shouldn't be required } break; @@ -499,7 +546,7 @@ export default { case "identity": { const user = data.data; let player = this.people[user.sid]; - // player.focus is already set + // player.tmpIds is already set player.name = user.name; player.id = user.id; this.$forceUpdate(); //TODO: shouldn't be required @@ -519,6 +566,7 @@ export default { if (!this.killed[this.st.user.sid]) { // Ask potentially missed last state, if opponent and I play if ( + !this.gotLastate && !!this.game.mycolor && this.game.type == "live" && this.game.score == "*" && @@ -587,6 +635,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 if (!this.game || !this.game.moves) this.lastateAsked = true; else this.sendLastate(data.from); break; @@ -650,6 +699,9 @@ export default { this.opponentGotMove = true; // Now his clock starts running on my side: const oppIdx = ['w','b'].indexOf(this.vr.turn); + // NOTE: next line to avoid multi-resetClocks when several tabs + // on same game, resulting in a faster countdown. + if (!!this.clockUpdate) clearInterval(this.clockUpdate); this.re_setClocks(); break; } @@ -711,11 +763,6 @@ export default { break; } }, - socketCloseListener: function() { - this.conn = new WebSocket(this.connexionString); - this.conn.addEventListener("message", this.socketMessageListener); - this.conn.addEventListener("close", this.socketCloseListener); - }, updateCorrGame: function(obj, callback) { ajax( "/games", @@ -763,7 +810,7 @@ export default { this.$refs["basegame"].play(data.lastMove, "received", null, true); this.processMove(data.lastMove); } else { - clearInterval(this.clockUpdate); + if (!!this.clockUpdate) clearInterval(this.clockUpdate); this.re_setClocks(); } if (data.drawSent) this.drawOffer = "received"; @@ -842,7 +889,7 @@ export default { this.send("rnewgame", { data: gameInfo, oppsid: oppsid }); // To main Hall if corr game: if (this.game.type == "corr") - this.send("newgame", { data: gameInfo }); + this.send("newgame", { data: gameInfo, page: "/" }); // Also to MyGames page: this.notifyMyGames("newgame", gameInfo); }; @@ -1090,6 +1137,7 @@ export default { this.game.score != "*" ) { clearInterval(this.clockUpdate); + this.clockUpdate = null; if (this.game.clocks[colorIdx] < 0) this.gameOver( currentTurn == "w" ? "0-1" : "1-0", @@ -1116,6 +1164,7 @@ export default { const origMovescount = this.game.moves.length; // The move is (about to be) played: stop clock clearInterval(this.clockUpdate); + this.clockUpdate = null; if (moveCol == this.game.mycolor && !data.receiveMyMove) { if (this.drawOffer == "received") // I refuse draw