X-Git-Url: https://git.auder.net/?a=blobdiff_plain;f=client%2Fsrc%2Fviews%2FHall.vue;h=9bb913c4809c6782ac9a5f218e511d62338f6b43;hb=6f2f94374f1e73c375edf732d9425e575e81fff7;hp=b9262d12b339246ce91688e0781164e6ed1d0e11;hpb=0234201fb338fc239d6f613c677fa932c7c3697c;p=vchess.git diff --git a/client/src/views/Hall.vue b/client/src/views/Hall.vue index b9262d12..9bb913c4 100644 --- a/client/src/views/Hall.vue +++ b/client/src/views/Hall.vue @@ -73,7 +73,10 @@ main ) fieldset(v-if="st.user.id > 0") label(for="selectPlayers") {{ st.tr["Play with"] }} - select#selectPlayersInList(v-model="newchallenge.to") + select#selectPlayersInList( + v-model="newchallenge.to" + @change="changeChallTarget()" + ) option(value="") option( v-for="p in Object.values(people)" @@ -120,10 +123,10 @@ main @click="challenge(sid)" ) | {{ st.tr["Challenge"] }} - p.anonymous @nonymous ({{ anonymousCount }}) + p.anonymous @nonymous ({{ anonymousCount() }}) #chat Chat( - :newChat="newChat" + ref="chatcomp" @mychat="processChat" :pastChats="[]" ) @@ -131,7 +134,7 @@ main .row .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2 .button-group - button#peopleBtn(onClick="window.doClick('modalPeople')") + button#peopleBtn(@click="openModalPeople()") | {{ st.tr["Who's there?"] }} button(@click="showNewchallengeForm()") | {{ st.tr["New game"] }} @@ -193,7 +196,7 @@ main ) button#loadMoreBtn( v-if="hasMore" - @click="loadMore()" + @click="loadMoreCorr()" ) | {{ st.tr["Load more"] }} @@ -247,7 +250,6 @@ export default { tchallDiag: "", curChallToAccept: {from: {}}, presetChalls: JSON.parse(localStorage.getItem("presetChalls") || "[]"), - newChat: "", conn: null, connexionString: "", // Related to (killing of) self multi-connects: @@ -266,16 +268,6 @@ export default { this.loadNewchallVariant(); } }, - computed: { - anonymousCount: function() { - let count = 0; - Object.values(this.people).forEach(p => { - // Do not cound people who did not send their identity yet: - count += (!p.name && p.id === 0) ? 1 : 0; - }); - return count; - } - }, created: function() { if (this.st.variants.length > 0 && this.newchallenge.vid > 0) this.loadNewchallVariant(); @@ -307,8 +299,8 @@ export default { encodeURIComponent(this.$route.path); this.conn = new WebSocket(this.connexionString); this.conn.onopen = connectAndPoll; - this.conn.onmessage = this.socketMessageListener; - this.conn.onclose = this.socketCloseListener; + this.conn.addEventListener("message", this.socketMessageListener); + this.conn.addEventListener("close", this.socketCloseListener); }, mounted: function() { document.addEventListener('visibilitychange', this.visibilityChange); @@ -329,41 +321,9 @@ export default { this.setDisplay('c', showCtype); this.setDisplay('g', showGtype); // Ask server for current corr games (all but mines) - ajax( - "/observedgames", - "GET", - { - data: { - uid: this.st.user.id, - cursor: this.cursor - }, - success: (response) => { - if ( - response.games.length > 0 && - this.games.length == 0 && - this.gdisplay == "live" - ) { - document - .getElementById("btnGcorr") - .classList.add("somethingnew"); - } - this.games = this.games.concat( - response.games.map(g => { - const vname = this.getVname(g.vid); - return Object.assign( - {}, - g, - { - type: "corr", - vname: vname - } - ); - }) - ); - } - } - ); + this.loadMoreCorr(); // Also ask for corr challenges (open + sent by/to me) + // List them all, because they are not supposed to be that many (TODO?) ajax( "/challenges", "GET", @@ -436,6 +396,18 @@ export default { ["random-" + pc.randomness]: true }; }, + openModalPeople: function() { + window.doClick("modalPeople"); + document.getElementById("inputChat").focus(); + }, + anonymousCount: function() { + let count = 0; + Object.values(this.people).forEach(p => { + // Do not cound people who did not send their identity yet: + count += (!p.name && p.id === 0) ? 1 : 0; + }); + return count; + }, visibilityChange: function() { // TODO: Use document.hidden? https://webplatform.news/issues/2019-03-27 this.send( @@ -484,6 +456,13 @@ export default { if (!!this.curChallToAccept.fen) return { "margin-top": "10px" }; return {}; }, + changeChallTarget: function() { + if (!this.newchallenge.to) { + // Reset potential FEN + diagram + this.newchallenge.fen = ""; + this.newchallenge.diag = ""; + } + }, cadenceFocusIfOpened: function() { if (event.target.checked) document.getElementById("cadence").focus(); @@ -560,10 +539,7 @@ export default { showGame: function(g) { // NOTE: we are an observer, since only games I don't play are shown here // ==> Moves sent by connected remote player(s) if live game - let url = "/game/" + g.id; - if (g.type == "live") - url += "?rid=" + g.rids[Math.floor(Math.random() * g.rids.length)]; - this.$router.push(url); + this.$router.push("/game/" + g.id); }, resetSocialColor: function() { // TODO: this is called twice, once on opening an once on closing @@ -590,6 +566,8 @@ export default { // Since people can be both in Hall and Game, // need to track "askIdentity" requests: let identityAsked = {}; + // TODO: shuffling and random filtering on server, if + // the room is really crowded. data.sockIds.forEach(s => { const page = s.page || "/"; if (s.sid != this.st.user.sid && !identityAsked[s.sid]) { @@ -605,21 +583,26 @@ export default { if (!s.page) // Peer is in Hall this.send("askchallenges", { target: s.sid }); - // Peer is in Game - else this.send("askgame", { target: s.sid, page: page }); + // Peer is in Game: ask only if live game + else if (!page.match(/\/[0-9]+$/)) + this.send("askgame", { target: s.sid, page: page }); }); break; } case "connect": case "gconnect": { const page = data.page || "/"; - // Only ask game / challenges if first connexion: - if (!this.people[data.from]) { - this.people[data.from] = { pages: [{ path: page, focus: true }] }; - if (data.code == "connect") + if (data.code == "connect") { + // Ask challenges only on first connexion: + if (!this.people[data.from]) this.send("askchallenges", { target: data.from }); - else this.send("askgame", { target: data.from, page: page }); - } else { + } + // Ask game only if live: + else if (!page.match(/\/[0-9]+$/)) + this.send("askgame", { target: data.from, page: page }); + if (!this.people[data.from]) + this.people[data.from] = { pages: [{ path: page, focus: true }] }; + else { // Append page if not already in list if (!(this.people[data.from].pages.find(p => p.path == page))) this.people[data.from].pages.push({ path: page, focus: true }); @@ -637,6 +620,10 @@ export default { // the first reload won't have time to connect but will trigger a "close" event anyway. // ==> Next check is required. if (!this.people[data.from]) return; + const page = data.page || "/"; + ArrayFun.remove(this.people[data.from].pages, p => p.path == page); + if (this.people[data.from].pages.length == 0) + this.$delete(this.people, data.from); // Disconnect means no more tmpIds: if (data.code == "disconnect") { // Remove the live challenges sent by this player: @@ -647,22 +634,16 @@ export default { ); } else { // Remove the matching live game if now unreachable - const gid = data.page.match(/[a-zA-Z0-9]+$/)[0]; + const gid = page.match(/[a-zA-Z0-9]+$/)[0]; // Corr games are always reachable: if (!gid.match(/^[0-9]+$/)) { - const gidx = this.games.findIndex(g => g.id == gid); - // NOTE: gidx should always be >= 0 (TODO?) - if (gidx >= 0) { - const game = this.games[gidx]; - ArrayFun.remove(game.rids, rid => rid == data.from); - if (game.rids.length == 0) this.games.splice(gidx, 1); + // Live games are reachable as long as someone is on the game page + if (Object.values(this.people).every(p => + p.pages.every(pg => pg.path != page))) { + ArrayFun.remove(this.games, g => g.id == gid); } } } - const page = data.page || "/"; - ArrayFun.remove(this.people[data.from].pages, p => p.path == page); - if (this.people[data.from].pages.length == 0) - this.$delete(this.people, data.from); break; } case "getfocus": @@ -682,6 +663,8 @@ export default { break; case "killed": // I logged in elsewhere: + this.conn.removeEventListener("message", this.socketMessageListener); + this.conn.removeEventListener("close", this.socketCloseListener); this.conn = null; alert(this.st.tr["New connexion detected: tab now offline"]); break; @@ -772,35 +755,30 @@ export default { } break; } - case "game": { - // Individual request + case "game": // Individual request + case "newgame": { const game = data.data; - // Ignore games where I play (will go in MyGames page) - if (game.players.every(p => - p.sid != this.st.user.sid && p.id != this.st.user.id)) - { - let locGame = this.games.find(g => g.id == game.id); - if (!locGame) { - let newGame = game; - newGame.type = this.classifyObject(game); - newGame.vname = this.getVname(game.vid); - if (!game.score) - // New game from Hall - newGame.score = "*"; - newGame.rids = [game.rid]; - delete newGame["rid"]; - this.games.push(newGame); - if ( - (newGame.type == "live" && this.gdisplay == "corr") || - (newGame.type == "corr" && this.gdisplay == "live") - ) { - document - .getElementById("btnG" + newGame.type) - .classList.add("somethingnew"); - } - } else { - // Append rid (if not already in list) - if (!locGame.rids.includes(game.rid)) locGame.rids.push(game.rid); + // Ignore games where I play (will go in MyGames page), + // and also games that I already received. + if ( + game.players.every(p => + p.sid != this.st.user.sid && p.id != this.st.user.id) && + this.games.findIndex(g => g.id == game.id) == -1 + ) { + let newGame = game; + newGame.type = this.classifyObject(game); + newGame.vname = this.getVname(game.vid); + if (!game.score) + // New game from Hall + newGame.score = "*"; + this.games.push(newGame); + if ( + (newGame.type == "live" && this.gdisplay == "corr") || + (newGame.type == "corr" && this.gdisplay == "live") + ) { + document + .getElementById("btnG" + newGame.type) + .classList.add("somethingnew"); } } break; @@ -829,7 +807,7 @@ export default { break; } case "newchat": - this.newChat = data.data; + this.$refs["chatcomp"].newChat(data.data); if (!document.getElementById("modalPeople").checked) document.getElementById("peopleBtn").classList.add("somethingnew"); break; @@ -841,7 +819,7 @@ export default { this.conn.addEventListener("message", this.socketMessageListener); this.conn.addEventListener("close", this.socketCloseListener); }, - loadMore: function() { + loadMoreCorr: function() { ajax( "/observedgames", "GET", @@ -851,8 +829,18 @@ export default { cursor: this.cursor }, success: (res) => { - if (res.games.length > 0) { - const L = res.games.length; + const L = res.games.length; + if (L > 0) { + if ( + this.cursor == Number.MAX_SAFE_INTEGER && + this.games.length == 0 && + this.gdisplay == "live" + ) { + // First loading: show indicators + document + .getElementById("btnGcorr") + .classList.add("somethingnew"); + } this.cursor = res.games[L - 1].created; let moreGames = res.games.map(g => { const vname = this.getVname(g.vid); @@ -900,10 +888,13 @@ export default { }, loadNewchallVariant: async function(cb) { const vname = this.getVname(this.newchallenge.vid); - const vModule = await import("@/variants/" + vname + ".js"); - this.newchallenge.V = vModule.VariantRules; - this.newchallenge.vname = vname; - if (!!cb) cb(); + await import("@/variants/" + vname + ".js") + .then((vModule) => { + window.V = vModule[vname + "Rules"]; + this.newchallenge.V = window.V; + this.newchallenge.vname = vname; + if (!!cb) cb(); + }); }, trySetNewchallDiag: function() { if (!this.newchallenge.fen) { @@ -922,7 +913,7 @@ export default { position: parsedFen.position, orientation: parsedFen.turn }); - } + } else this.newchallenge.diag = ""; }, newChallFromPreset(pchall) { this.partialResetNewchallenge(); @@ -1015,7 +1006,6 @@ export default { }); // Add new challenge: chall.from = { - // Decompose to avoid revealing email sid: this.st.user.sid, id: this.st.user.id, name: this.st.user.name @@ -1049,7 +1039,7 @@ export default { { data: { chall: chall }, success: (response) => { - finishAddChallenge(response.cid); + finishAddChallenge(response.id); } } ); @@ -1065,7 +1055,6 @@ export default { finishProcessingChallenge: function(c) { if (c.accepted) { c.seat = { - // Again, avoid c.seat = st.user to not reveal email sid: this.st.user.sid, id: this.st.user.id, name: this.st.user.name @@ -1102,22 +1091,24 @@ export default { return; } c.accepted = true; - const vModule = await import("@/variants/" + c.vname + ".js"); - window.V = vModule.VariantRules; - if (!!c.to) { - // c.to == this.st.user.name (connected) - if (!!c.fen) { - const parsedFen = V.ParseFen(c.fen); - c.mycolor = V.GetOppCol(parsedFen.turn); - this.tchallDiag = getDiagram({ - position: parsedFen.position, - orientation: c.mycolor - }); + await import("@/variants/" + c.vname + ".js") + .then((vModule) => { + window.V = vModule[c.vname + "Rules"]; + if (!!c.to) { + // c.to == this.st.user.name (connected) + if (!!c.fen) { + const parsedFen = V.ParseFen(c.fen); + c.mycolor = V.GetOppCol(parsedFen.turn); + this.tchallDiag = getDiagram({ + position: parsedFen.position, + orientation: c.mycolor + }); + } + this.curChallToAccept = c; + document.getElementById("modalAccept").checked = true; } - this.curChallToAccept = c; - document.getElementById("modalAccept").checked = true; - } - else this.finishProcessingChallenge(c); + else this.finishProcessingChallenge(c); + }); } else { // My challenge @@ -1135,7 +1126,8 @@ export default { }, // NOTE: when launching game, the challenge is already being deleted launchGame: function(c) { - let players = + // White player index 0, black player index 1: + const players = !!c.mycolor ? (c.mycolor == "w" ? [c.seat, c.from] : [c.from, c.seat]) : shuffle([c.from, c.seat]); @@ -1144,10 +1136,7 @@ export default { id: getRandString(), fen: c.fen || V.GenRandInitFen(c.randomness), randomness: c.randomness, //for rematch - // White player index 0, black player index 1: - players: c.mycolor - ? (c.mycolor == "w" ? [c.seat, c.from] : [c.from, c.seat]) - : shuffle([c.from, c.seat]), + players: players, vid: c.vid, cadence: c.cadence }; @@ -1156,14 +1145,22 @@ export default { if (!!oppsid) // Opponent is online this.send("startgame", { data: gameInfo, target: oppsid }); + // If new corr game, notify Hall (except opponent and me) + if (c.type == "corr") { + this.send( + "newgame", + { + data: gameInfo, + excluded: [this.st.user.sid, oppsid] + } + ); + } // Notify MyGames page: this.send( "notifynewgame", { data: gameInfo, - targets: gameInfo.players.map(p => { - return { sid: p.sid, id: p.id }; - }) + targets: gameInfo.players } ); // NOTE: no need to send the game to the room, since I'll connect @@ -1179,11 +1176,14 @@ export default { "POST", { // cid is useful to delete the challenge: - data: { gameInfo: gameInfo, cid: c.id }, + data: { + gameInfo: gameInfo, + cid: c.id + }, success: (response) => { - gameInfo.id = response.gameId; + gameInfo.id = response.id; notifyNewgame(); - this.$router.push("/game/" + response.gameId); + this.$router.push("/game/" + response.id); } } ); @@ -1202,7 +1202,6 @@ export default { // Game state (including FEN): will be updated moves: [], clocks: [-1, -1], //-1 = unstarted - initime: [0, 0], //initialized later score: "*" } ); @@ -1328,8 +1327,8 @@ tr > td margin: 5px 0 button#loadMoreBtn - margin-top: 0 - margin-bottom: 0 + display: block + margin: 0 auto td.remove-preset background-color: lightgrey