From: Benjamin Auder Date: Sun, 9 Feb 2020 12:14:36 +0000 (+0100) Subject: Experimental improved behavior of login/logout/multitabs X-Git-Url: https://git.auder.net/doc/html/%7B%7B%20asset%28%27mixstore/css/%7B%7B?a=commitdiff_plain;h=a3ac374ba213c7044db6cbcfafb81d4b66a0a290;p=vchess.git Experimental improved behavior of login/logout/multitabs --- diff --git a/client/src/components/UpsertUser.vue b/client/src/components/UpsertUser.vue index 2b28f64b..60970b08 100644 --- a/client/src/components/UpsertUser.vue +++ b/client/src/components/UpsertUser.vue @@ -9,13 +9,13 @@ div div(v-show="stage!='Login'") fieldset label(for="username") {{ st.tr["Name"] }} - input#username(type="text" v-model="user.name") + input#username(type="text" v-model="st.user.name") fieldset label(for="useremail") {{ st.tr["Email"] }} - input#useremail(type="email" v-model="user.email") + input#useremail(type="email" v-model="st.user.email") fieldset label(for="notifyNew") {{ st.tr["Notifications by email"] }} - input#notifyNew(type="checkbox" v-model="user.notify") + input#notifyNew(type="checkbox" v-model="st.user.notify") div(v-show="stage=='Login'") fieldset label(for="nameOrEmail") {{ st.tr["Name or Email"] }} @@ -38,7 +38,6 @@ export default { name: 'my-upsert-user', data: function() { return { - user: store.state.user, nameOrEmail: "", //for login logStage: "Login", //or Register infoMsg: "", @@ -50,13 +49,13 @@ export default { nameOrEmail: function(newValue) { if (newValue.indexOf('@') >= 0) { - this.user.email = newValue; - this.user.name = ""; + this.st.user.email = newValue; + this.st.user.name = ""; } else { - this.user.name = newValue; - this.user.email = ""; + this.st.user.name = newValue; + this.st.user.email = ""; } }, }, @@ -76,7 +75,7 @@ export default { return (this.infoMsg.length > 0 ? "block" : "none"); }, stage: function() { - return this.user.id > 0 ? "Update" : this.logStage; + return this.st.user.id > 0 ? "Update" : this.logStage; }, }, methods: { @@ -133,12 +132,12 @@ export default { error = checkNameEmail({[type]: this.nameOrEmail}); } else - error = checkNameEmail(this.user); + error = checkNameEmail(this.st.user); if (!!error) return alert(error); this.infoMsg = "Processing... Please wait"; ajax(this.ajaxUrl(), this.ajaxMethod(), - this.stage == "Login" ? { nameOrEmail: this.nameOrEmail } : this.user, + this.stage == "Login" ? { nameOrEmail: this.nameOrEmail } : this.st.user, res => { this.infoMsg = this.infoMessage(); if (this.stage != "Update") @@ -155,24 +154,8 @@ export default { ); }, doLogout: function() { - let logoutBtn = document.getElementById("logoutBtn"); - logoutBtn.disabled = true; - // NOTE: this local cleaning would logically happen when we're sure - // that token is erased. But in the case a user clear the cookies, - // it would lead to situations where he cannot ("locally") log out. - // At worst, if token deletion fails the user can erase cookie manually. - this.user.id = 0; - this.user.name = ""; - this.user.email = ""; - this.user.notify = false; - localStorage.removeItem("myid"); - localStorage.removeItem("myname"); - ajax("/logout", "GET", () => { - logoutBtn.disabled = false; //for symmetry, but not very useful... - document.getElementById("modalUser").checked = false; - // this.$router.push("/") will fail if logout from Hall, so: - document.location.reload(true); - }); + document.getElementById("modalUser").checked = false; + this.$router.push("/logout"); }, }, }; diff --git a/client/src/router.js b/client/src/router.js index 599cfcb3..5563980b 100644 --- a/client/src/router.js +++ b/client/src/router.js @@ -31,29 +31,12 @@ const router = new Router({ { path: "/authenticate/:token", name: "authenticate", - beforeEnter: (to, from, next) => { - ajax( - "/authenticate", - "GET", - {token: to.params["token"]}, - (res) => { - if (!res.errmsg) //if not already logged in - { - store.state.user.id = res.id; - store.state.user.name = res.name; - store.state.user.email = res.email; - store.state.user.notify = res.notify; - localStorage["myname"] = res.name; - localStorage["myid"] = res.id; - } - // TODO: I don't like these 2 lines, "next('/')" should be enough - window.location = "/"; - next(); - } - ); - }, - component: Hall, - //redirect: "/", //problem: redirection before end of AJAX request + component: loadView("Auth"), + }, + { + path: "/logout", + name: "logout", + component: loadView("Logout"), }, { path: "/mygames", diff --git a/client/src/translations/en.js b/client/src/translations/en.js index ae1101df..ee24720b 100644 --- a/client/src/translations/en.js +++ b/client/src/translations/en.js @@ -6,6 +6,7 @@ export const translations = "All": "All", "Analyze": "Analyze", "Analyze in Dark mode makes no sense!": "Analyze in Dark mode makes no sense!", + "Authentication successful!": "Authentication successful!", "Apply": "Apply", "Available": "Available", "Black": "Black", @@ -45,6 +46,7 @@ export const translations = "Live games": "Live games", "Login": "Login", "Logout": "Logout", + "Logout successful!": "Logout successful!", "Modifications applied!": "Modifications applied!", "Mutual agreement": "Mutual agreement", "My games": "My games", diff --git a/client/src/translations/es.js b/client/src/translations/es.js index 0e9724b8..32d6e294 100644 --- a/client/src/translations/es.js +++ b/client/src/translations/es.js @@ -7,6 +7,7 @@ export const translations = "Analyze": "Analizar", "Analyze in Dark mode makes no sense!": "¡ Analizar en modo Dark no tiene sentido !", "Apply": "Aplicar", + "Authentication successful!": "¡ Autenticación exitosa !", "Available": "Disponible", "Black": "Negras", "Black to move": "Juegan las negras", @@ -45,6 +46,7 @@ export const translations = "Live games": "Partidas en vivo", "Login": "Login", "Logout": "Logout", + "Logout successful!": "¡ Desconexión exitosa !", "Modifications applied!": "¡ Modificaciones aplicadas !", "Mutual agreement": "Acuerdo mutuo", "My games": "Mis partidas", diff --git a/client/src/translations/fr.js b/client/src/translations/fr.js index 15673519..525eb20c 100644 --- a/client/src/translations/fr.js +++ b/client/src/translations/fr.js @@ -7,6 +7,7 @@ export const translations = "Analyze": "Analyser", "Analyze in Dark mode makes no sense!": "Analyser en mode Dark n'a pas de sens !", "Apply": "Appliquer", + "Authentication successful!": "Authentification réussie !", "Available": "Disponible", "Black": "Noirs", "Black to move": "Trait aux noirs", @@ -45,6 +46,7 @@ export const translations = "Live games": "Parties en direct", "Login": "Login", "Logout": "Logout", + "Logout successful!": "Déconnection réussie !", "Modifications applied!": "Modifications effectuées !", "Mutual agreement": "Accord mutuel", "My games": "Mes parties", diff --git a/client/src/views/About.vue b/client/src/views/About.vue index 3b1a2cb9..7f3e687b 100644 --- a/client/src/views/About.vue +++ b/client/src/views/About.vue @@ -20,33 +20,3 @@ export default { }, }; - - diff --git a/client/src/views/Auth.vue b/client/src/views/Auth.vue new file mode 100644 index 00000000..f892fd55 --- /dev/null +++ b/client/src/views/Auth.vue @@ -0,0 +1,50 @@ + + + + + diff --git a/client/src/views/Game.vue b/client/src/views/Game.vue index 0072bd43..e3a9408b 100644 --- a/client/src/views/Game.vue +++ b/client/src/views/Game.vue @@ -165,11 +165,11 @@ export default { switch (data.code) { case "duplicate": + this.st.conn.send(JSON.stringify({code:"duplicate"})); alert(this.st.tr["Warning: multi-tabs not supported"]); break; // 0.2] Receive clients list (just socket IDs) case "pollclients": - { data.sockIds.forEach(sid => { if (!!this.people[sid]) return; @@ -178,9 +178,7 @@ export default { this.st.conn.send(JSON.stringify({code:"askidentity", target:sid})); }); break; - } case "askidentity": - { // Request for identification: reply if I'm not anonymous if (this.st.user.id > 0) { @@ -194,9 +192,7 @@ export default { target:data.from})); } break; - } case "identity": - { this.$set(this.people, data.user.sid, {id: data.user.id, name: data.user.name}); // Ask potentially missed last state, if opponent and I play @@ -207,9 +203,7 @@ export default { this.st.conn.send(JSON.stringify({code:"asklastate", target:data.user.sid})); } break; - } case "asklastate": - { // Sending last state if I played a move or score != "*" if ((this.game.moves.length > 0 && this.vr.turn != this.game.mycolor) || this.game.score != "*" || this.drawOffer == "sent") @@ -234,7 +228,6 @@ export default { })); } break; - } case "askgame": // Send current (live) game if I play in (not an observer), // and not asked by opponent (!) @@ -271,13 +264,11 @@ export default { document.getElementById("chatBtn").style.backgroundColor = "#c5fefe"; break; case "lastate": //got opponent infos about last move - { this.lastate = data.state; if (this.game.rendered) //game is rendered (Board component) this.processLastate(); //else: will be processed when game is ready break; - } case "resign": this.gameOver(data.side=="b" ? "1-0" : "0-1", "Resign"); break; @@ -300,11 +291,9 @@ export default { this.loadGame(data.game, this.roomInit); break; case "connect": - { this.$set(this.people, data.from, {name:"", id:0}); this.st.conn.send(JSON.stringify({code:"askidentity", target:data.from})); break; - } case "disconnect": this.$delete(this.people, data.from); break; diff --git a/client/src/views/Hall.vue b/client/src/views/Hall.vue index bbff9b95..5fcbb31a 100644 --- a/client/src/views/Hall.vue +++ b/client/src/views/Hall.vue @@ -136,20 +136,6 @@ export default { // Always add myself to players' list const my = this.st.user; this.$set(this.people, my.sid, {id:my.id, name:my.name}); - // Retrieve live challenge (not older than 30 minute) if any: - const chall = JSON.parse(localStorage.getItem("challenge") || "false"); - if (!!chall) - { - // NOTE: a challenge survives 3 minutes, for potential connection issues - if ((Date.now() - chall.added)/1000 <= 3*60) - { - chall.added = Date.now(); //update added time, for next disconnect... - this.challenges.push(chall); - localStorage.setItem("challenge", JSON.stringify(chall)); - } - else - localStorage.removeItem("challenge"); - } // Ask server for current corr games (all but mines) ajax( "/games", @@ -321,6 +307,8 @@ export default { switch (data.code) { case "duplicate": + this.st.conn.send(JSON.stringify({code:"duplicate"})); + this.st.conn.send = () => {}; alert(this.st.tr["Warning: multi-tabs not supported"]); break; // 0.2] Receive clients list (just socket IDs) @@ -343,7 +331,6 @@ export default { this.st.conn.send(JSON.stringify({code:"askgames"})); break; case "askidentity": - { // Request for identification: reply if I'm not anonymous if (this.st.user.id > 0) { @@ -357,9 +344,7 @@ export default { target:data.from})); } break; - } case "identity": - { this.$set(this.people, data.user.sid, { id: data.user.id, @@ -367,7 +352,6 @@ export default { gamer: this.people[data.user.sid].gamer, }); break; - } case "askchallenge": { // Send my current live challenge (if any) @@ -404,7 +388,6 @@ export default { break; } case "challenge": - { // Receive challenge from some player (+sid) // NOTE about next condition: see "askchallenge" case. if (!data.chall.to || data.chall.to == this.st.user.name) @@ -417,7 +400,6 @@ export default { this.challenges.push(newChall); } break; - } case "game": { // Receive game from some player (+sid) @@ -442,7 +424,6 @@ export default { break; } case "newgame": - { // New game just started: data contain all information if (this.classifyObject(data.gameInfo) == "live") this.startNewGame(data.gameInfo); @@ -456,24 +437,17 @@ export default { setTimeout(() => { modalBox.checked = false; }, 3000); } break; - } case "newchat": this.newChat = data.chat; break; case "refusechallenge": - { ArrayFun.remove(this.challenges, c => c.id == data.cid); - localStorage.removeItem("challenge"); alert(this.st.tr["Challenge declined"]); break; - } case "deletechallenge": - { // NOTE: the challenge may be already removed ArrayFun.remove(this.challenges, c => c.id == data.cid); - localStorage.removeItem("challenge"); //in case of break; - } case "connect": case "gconnect": this.$set(this.people, data.from, {name:"", id:0, gamer:data.code[0]=='g'}); @@ -576,9 +550,7 @@ export default { name: this.st.user.name, }; this.challenges.push(chall); - if (ctype == "live") - localStorage.setItem("challenge", JSON.stringify(chall)); - // Also remember timeControl + vid for quicker further challenges: + // Remember timeControl + vid for quicker further challenges: localStorage.setItem("timeControl", chall.timeControl); localStorage.setItem("vid", chall.vid); document.getElementById("modalNewgame").checked = false; @@ -639,8 +611,6 @@ export default { {id: c.id} ); } - else //live - localStorage.removeItem("challenge"); this.sendSomethingTo({name:c.to}, "deletechallenge", {cid:c.id}); } // In all cases, the challenge is consumed: diff --git a/client/src/views/Logout.vue b/client/src/views/Logout.vue new file mode 100644 index 00000000..61259d58 --- /dev/null +++ b/client/src/views/Logout.vue @@ -0,0 +1,43 @@ + + + + + diff --git a/server/sockets.js b/server/sockets.js index e7bb556c..df44520d 100644 --- a/server/sockets.js +++ b/server/sockets.js @@ -18,16 +18,6 @@ module.exports = function(wss) { wss.on("connection", (socket, req) => { const query = getJsonFromUrl(req.url); const sid = query["sid"]; - if (!!clients[sid]) - { - // Dummy messages listener: just send "duplicate" event on anything - // ('connect' events for Hall and Game, 'askfullgame' for observers) - return socket.on("message", objtxt => { - if (["connect","askfullgame"].includes(JSON.parse(objtxt).code)) - socket.send(JSON.stringify({code:"duplicate"})); - }); - } - clients[sid] = {sock: socket, page: query["page"]}; const notifyRoom = (page,code,obj={},excluded=[]) => { Object.keys(clients).forEach(k => { if (k in excluded) @@ -39,15 +29,26 @@ module.exports = function(wss) { } }); }; - // Wait for "connect" message to notify connection to the room, - // because if game loading is slow the message listener might - // not be ready too early. - socket.on("message", objtxt => { + const messageListener = (objtxt) => { let obj = JSON.parse(objtxt); if (!!obj.target && !clients[obj.target]) return; //receiver not connected, nothing we can do switch (obj.code) { + case "duplicate": + // Turn off message listening, and send disconnect if needed: + socket.removeListener("message", messageListener); + socket.removeListener("close", closeListener); + if (clients[sid].page != obj.page) + { + notifyRoom(clients[sid].page, "disconnect"); + if (clients[sid].page.indexOf("/game/") >= 0) + notifyRoom("/", "gdisconnect"); + } + break; + // Wait for "connect" message to notify connection to the room, + // because if game loading is slow the message listener might + // not be ready too early. case "connect": { const curPage = clients[sid].page; @@ -197,13 +198,22 @@ module.exports = function(wss) { {code:"draw", message:obj.message})); break; } - }); - socket.on("close", () => { + }; + const closeListener = () => { const page = clients[sid].page; delete clients[sid]; notifyRoom(page, "disconnect"); if (page.indexOf("/game/") >= 0) notifyRoom("/", "gdisconnect"); //notify main hall - }); + }; + if (!!clients[sid]) + { + // Turn off old sock through current client: + clients[sid].sock.send(JSON.stringify({code:"duplicate"})); + } + // Potentially replace current connection: + clients[sid] = {sock: socket, page: query["page"]}; + socket.on("message", messageListener); + socket.on("close", closeListener); }); }