X-Git-Url: https://git.auder.net/?a=blobdiff_plain;f=client%2Fsrc%2Fviews%2FHall.vue;h=0e4d574b0539b61f5666691a2b87f6e69e247040;hb=beda3dd096a455ed337eaaeadc400712bf0f5c6d;hp=ab5108e5d159a29e37739f0c3e23c37adfed0f76;hpb=5a187b07e221493931cf089bf85ef6db41d8b775;p=vchess.git diff --git a/client/src/views/Hall.vue b/client/src/views/Hall.vue index ab5108e5..0e4d574b 100644 --- a/client/src/views/Hall.vue +++ b/client/src/views/Hall.vue @@ -15,6 +15,8 @@ main span.variantName {{ curChallToAccept.vname }} span {{ curChallToAccept.cadence }} span {{ st.tr["with"] + " " + curChallToAccept.from.name }} + p.text-center(v-if="!!curChallToAccept.color") + | {{ st.tr["Your color:"] + " " + invColor(curChallToAccept.color) }} .diagram( v-if="!!curChallToAccept.fen" v-html="tchallDiag" @@ -88,7 +90,13 @@ main type="text" v-model="newchallenge.to" ) - fieldset(v-if="st.user.id > 0 && newchallenge.to.length > 0") + fieldset(v-show="st.user.id > 0 && newchallenge.to.length > 0") + label(for="selectColor") {{ st.tr["Color"] }} + select#selectColor(v-model="newchallenge.color") + option(value='') + option(value='w') {{ st.tr["White"] }} + option(value='b') {{ st.tr["Black"] }} + br input#inputFen( placeholder="FEN" @input="trySetNewchallDiag()" @@ -99,7 +107,7 @@ main button(@click="issueNewChallenge()") {{ st.tr["Send challenge"] }} input#modalPeople.modal( type="checkbox" - @click="resetSocialColor()" + @click="toggleSocialColor()" ) div#peopleWrap( role="dialog" @@ -113,14 +121,15 @@ main v-for="sid in Object.keys(people)" v-if="!!people[sid].name" ) - span {{ people[sid].name }} + UserBio.user-bio(:uid="people[sid].id" :uname="people[sid].name") button.player-action( v-if="isGamer(sid)" @click="watchGame(sid)" ) | {{ st.tr["Observe"] }} button.player-action( - v-else-if="isFocusedOnHall(sid)" + v-else-if="st.user.sid != sid" + :class="{focused: isFocusedOnHall(sid)}" @click="challenge(sid)" ) | {{ st.tr["Challenge"] }} @@ -135,7 +144,7 @@ main .row .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2 .button-group - button#peopleBtn(@click="openModalPeople()") + button#peopleBtn(onClick="window.doClick('modalPeople')") | {{ st.tr["Who's there?"] }} button(@click="showNewchallengeForm()") | {{ st.tr["New game"] }} @@ -166,7 +175,7 @@ main button.tabbtn#btnClive(@click="setDisplay('c','live',$event)") | {{ st.tr["Live challenges"] }} button.tabbtn#btnCcorr(@click="setDisplay('c','corr',$event)") - | {{ st.tr["Correspondance challenges"] }} + | {{ st.tr["Correspondence challenges"] }} ChallengeList( v-show="cdisplay=='live'" :challenges="filterChallenges('live')" @@ -182,17 +191,17 @@ main button.tabbtn#btnGlive(@click="setDisplay('g','live',$event)") | {{ st.tr["Live games"] }} button.tabbtn#btnGcorr(@click="setDisplay('g','corr',$event)") - | {{ st.tr["Correspondance games"] }} + | {{ st.tr["Correspondence games"] }} GameList( v-show="gdisplay=='live'" :games="filterGames('live')" - :showBoth="true" + :show-both="true" @show-game="showGame" ) div(v-show="gdisplay=='corr'") GameList( :games="filterGames('corr')" - :showBoth="true" + :show-both="true" @show-game="showGame" ) button#loadMoreBtn( @@ -212,6 +221,7 @@ import params from "@/parameters"; import { getRandString, shuffle, randInt } from "@/utils/alea"; import { getDiagram } from "@/utils/printDiagram"; import Chat from "@/components/Chat.vue"; +import UserBio from "@/components/UserBio.vue"; import GameList from "@/components/GameList.vue"; import ChallengeList from "@/components/ChallengeList.vue"; import { GameStorage } from "@/utils/gameStorage"; @@ -220,6 +230,7 @@ export default { name: "my-hall", components: { Chat, + UserBio, GameList, ChallengeList }, @@ -238,10 +249,13 @@ export default { infoMessage: "", newchallenge: { fen: "", - vid: parseInt(localStorage.getItem("vid")) || 0, + vid: parseInt(localStorage.getItem("vid"), 10) || 0, to: "", //name of challenged player (if any) + color: '', cadence: localStorage.getItem("cadence") || "", - randomness: parseInt(localStorage.getItem("challRandomness")) || 2, + randomness: + // Warning: randomness can be 0, then !!randomness is false + (parseInt(localStorage.getItem("challRandomness"),10)+1 || 3) - 1, // VariantRules object, stored to not interfere with // diagrams of targetted challenges: V: null, @@ -251,14 +265,13 @@ export default { }, focus: true, tchallDiag: "", - curChallToAccept: {from: {}}, + curChallToAccept: { from: {} }, presetChalls: JSON.parse(localStorage.getItem("presetChalls") || "[]"), conn: null, connexionString: "", socketCloseListener: 0, // Related to (killing of) self multi-connects: - newConnect: {}, - killed: {} + newConnect: {} }; }, watch: { @@ -298,6 +311,28 @@ export default { const connectAndPoll = () => { this.send("connect"); this.send("pollclientsandgamers"); + if (!!this.$route.query["challenge"]) { + // Automatic challenge sending, for tournaments + this.loadNewchallVariant( + () => { + this.newchallenge = { + fen: "", + vid: + this.st.variants + .find(v => v.name == this.$route.query["variant"]) + .id, + to: this.$route.query["challenge"], + color: this.$route.query["color"] || '', + cadence: this.$route.query["cadence"], + // Tournament: no randomness (TODO: for now at least) + randomness: 0, + memorize: false + }; + this.issueNewChallenge(); + }, + this.$route.query["variant"] + ); + } }; // Initialize connection this.connexionString = @@ -323,7 +358,13 @@ export default { ); }, mounted: function() { - ["peopleWrap", "infoDiv", "newgameDiv"].forEach(eltName => { + document.getElementById("peopleWrap") + .addEventListener("click", (e) => { + processModalClick(e, () => { + this.toggleSocialColor("close") + }); + }); + ["infoDiv", "newgameDiv"].forEach(eltName => { document.getElementById(eltName) .addEventListener("click", processModalClick); }); @@ -424,10 +465,6 @@ 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 => { @@ -449,9 +486,14 @@ export default { this.focus = false; this.send("losefocus"); }, + invColor: function(c) { + if (c == 'w') return this.st.tr["Black"]; + return this.tr.tr["White"]; + }, partialResetNewchallenge: function() { // Reset potential target and custom FEN: this.newchallenge.to = ""; + this.newchallenge.color = ''; this.newchallenge.fen = ""; this.newchallenge.diag = ""; this.newchallenge.memorize = false; @@ -494,6 +536,7 @@ export default { if (!this.newchallenge.to) { // Reset potential FEN + diagram this.newchallenge.fen = ""; + this.newchallenge.color = ''; this.newchallenge.diag = ""; } }, @@ -517,9 +560,14 @@ export default { filterGames: function(type) { return this.games.filter(g => g.type == type); }, + // o: challenge or game classifyObject: function(o) { - // o: challenge or game - return o.cadence.indexOf("d") === -1 ? "live" : "corr"; + // No imported games here + return ( + o.cadence.indexOf("d") >= 0 + ? "corr" + : (o.cadence.indexOf("/") >= 0 ? "simul" : "live") + ); }, setDisplay: function(letter, type, e) { this[letter + "display"] = type; @@ -567,18 +615,18 @@ export default { } }); const gid = gids[Math.floor(Math.random() * gids.length)]; - const game = this.games.find(g => g.id == gid); - if (!!game) this.showGame(game); - else this.$router.push("/game/" + gid); //game vs. me + window.open("/#/game/" + gid, "_blank"); }, 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 - this.$router.push("/game/" + g.id); + window.open("/#/game/" + g.id, "_blank"); }, - resetSocialColor: function() { - // TODO: this is called twice, once on opening an once on closing - document.getElementById("peopleBtn").classList.remove("somethingnew"); + toggleSocialColor: function(action) { + if (!action && document.getElementById("modalPeople").checked) + document.getElementById("inputChat").focus(); + else + document.getElementById("peopleBtn").classList.remove("somethingnew"); }, processChat: function(chat) { this.send("newchat", { data: chat }); @@ -638,13 +686,13 @@ export default { const page = data.page || "/"; if (data.code == "connect") { // Ask challenges only on first connexion: - if (!this.people[data.from]) + if (!this.people[data.from[0]]) this.send("askchallenges", { target: data.from[0] }); } // Ask game only if live: else if (!page.match(/\/[0-9]+$/)) this.send("askgame", { target: data.from[0], page: page }); - if (!this.people[data.from]) { + if (!this.people[data.from[0]]) { this.$set( this.people, data.from[0], @@ -654,7 +702,8 @@ export default { } } ); - this.newConnect[data.from] = true; //for self multi-connects tests + // For self multi-connects tests: + this.newConnect[data.from[0]] = true; this.send("askidentity", { target: data.from[0], page: page }); } else { this.people[data.from[0]].tmpIds[data.from[1]] = @@ -675,12 +724,19 @@ export default { this.$delete(this.people, data.from[0]); else this.$forceUpdate(); //TODO: shouldn't be required if (data.code == "disconnect") { - // Remove the live challenges sent by this player: - ArrayFun.remove( - this.challenges, - c => c.type == "live" && c.from.sid == data.from[0], - "all" - ); + // Remove the live challenges sent by this player, if + // he isn't connected on another tab: + if ( + !this.people[data.from[0]] || + Object.values(this.people[data.from[0]].tmpIds) + .every(v => v.page != "/") + ) { + ArrayFun.remove( + this.challenges, + c => c.type == "live" && c.from.sid == data.from[0], + "all" + ); + } } else { // Remove the matching live game if now unreachable const gid = data.page.match(/[a-zA-Z0-9]+$/)[0]; @@ -713,13 +769,6 @@ 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; case "askidentity": { // Request for identification const me = { @@ -742,16 +791,16 @@ export default { this.$forceUpdate(); // If I multi-connect, kill current connexion if no mark (I'm older) if (this.newConnect[user.sid]) { + delete this.newConnect[user.sid]; if ( user.id > 0 && user.id == this.st.user.id && - user.sid != this.st.user.sid && - !this.killed[this.st.user.sid] + user.sid != this.st.user.sid ) { - this.send("killme", { sid: this.st.user.sid }); - this.killed[this.st.user.sid] = true; + // I logged in elsewhere: + this.cleanBeforeDestroy(); + alert(this.st.tr["New connexion detected: tab now offline"]); } - delete this.newConnect[user.sid]; } break; } @@ -808,15 +857,23 @@ export default { } break; } + case "entersimul": + // TODO: confirm box accept/refuse. + // If accept, update seat (array in this case) + break; case "game": // Individual request case "newgame": { const game = data.data; // 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 + this.games.findIndex(g => g.id == game.id) == -1 && + game.players.every(p => { + return ( + p.sid != this.st.user.sid && + (p.id == 0 || p.id != this.st.user.id) + ); + }) ) { let newGame = game; newGame.type = this.classifyObject(game); @@ -826,6 +883,7 @@ export default { newGame.score = "*"; this.games.push(newGame); if ( + newGame.score == '*' && (newGame.type == "live" && this.gdisplay == "corr") || (newGame.type == "corr" && this.gdisplay == "live") ) { @@ -848,7 +906,7 @@ export default { this.startNewGame(gameInfo); else { this.infoMessage = - this.st.tr["New correspondance game:"] + " " + + this.st.tr["New correspondence game:"] + " " + "" + "#/game/" + gameInfo.id + ""; document.getElementById("modalInfo").checked = true; @@ -877,7 +935,8 @@ export default { if ( this.cursor == Number.MAX_SAFE_INTEGER && this.games.length == 0 && - this.gdisplay == "live" + this.gdisplay == "live" && + res.games.some(g => g.score == '*') ) { // First loading: show indicators document @@ -927,10 +986,19 @@ export default { .getElementById("btnC" + newChall.type) .classList.add("somethingnew"); } + if (!!chall.to) { + notify( + "New challenge", + // fromValues.name should exist since the player is online, but + // let's consider there is some chance that the challenge arrives + // right after we connected and before receiving the poll result: + { body: "from " + (fromValues.name || "unknown yet...") } + ); + } } }, - loadNewchallVariant: async function(cb) { - const vname = this.getVname(this.newchallenge.vid); + loadNewchallVariant: async function(cb, vname) { + vname = vname || this.getVname(this.newchallenge.vid); await import("@/variants/" + vname + ".js") .then((vModule) => { window.V = vModule[vname + "Rules"]; @@ -953,8 +1021,8 @@ export default { ) { const parsedFen = V.ParseFen(this.newchallenge.fen); this.newchallenge.diag = getDiagram({ - position: parsedFen.position, - orientation: parsedFen.turn + position: parsedFen.position + //,orientation: parsedFen.turn }); } else this.newchallenge.diag = ""; }, @@ -974,7 +1042,9 @@ export default { if (!this.newchallenge.vid) error = this.st.tr["Please select a variant"]; else if (ctype == "corr" && this.st.user.id <= 0) - error = this.st.tr["Please log in to play correspondance games"]; + error = this.st.tr["Please log in to play correspondence games"]; + else if (!this.newchallenge.to && !!this.newchallenge.color) + error = this.st.tr["Color option only for targeted challenge"]; else if (!!this.newchallenge.to) { if (this.newchallenge.to == this.st.user.name) error = this.st.tr["Self-challenge is forbidden"]; @@ -984,8 +1054,14 @@ export default { ) { error = this.newchallenge.to + " " + this.st.tr["is not online"]; } + if ( + !!this.newchallenge.color && + !['w', 'b'].includes(this.newchallenge.color) + ) { + error = this.st.tr["Wrong color"]; + } } - if (error) { + if (!!error) { alert(error); return; } @@ -997,9 +1073,18 @@ export default { } // NOTE: "from" information is not required here let chall = Object.assign({}, this.newchallenge); - // Add only if not already issued (not counting target or FEN): + // Add only if not already issued (not counting FEN): if (this.challenges.some(c => - (c.from.sid == this.st.user.sid || c.from.id == this.st.user.id) && + ( + c.from.sid == this.st.user.sid || + (c.from.id > 0 && c.from.id == this.st.user.id) + ) + && + ( + (!c.to && !chall.to) || + c.to == chall.to + ) + && c.vid == chall.vid && c.cadence == chall.cadence && c.randomness == chall.randomness @@ -1021,7 +1106,10 @@ export default { const c = this.challenges[i]; if ( c.type == ctype && - (c.from.sid == this.st.user.sid || c.from.id == this.st.user.id) + ( + c.from.sid == this.st.user.sid || + (c.from.id > 0 && c.from.id == this.st.user.id) + ) ) { countMyChalls++; if (c.added < oldestAdded) { @@ -1075,7 +1163,7 @@ export default { // Live challenges have a random ID finishAddChallenge(null); } else { - // Correspondance game: send challenge to server + // Correspondence game: send challenge to server ajax( "/challenges", "POST", @@ -1137,25 +1225,33 @@ export default { alert(this.st.tr["Please log in to accept corr challenges"]); return; } - c.accepted = true; - 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 - }); + if (c.type == "simul") { + // Just notify that I wanna enter the simultaneous game(s) + // TODO: dans défi, indiquer si positions aleatoires, si tjours blancs ou noirs + // --> /w30 ou b1h ou juste 30 (random, sans préfixe). + // => message "entersimul" to c.from + return; + } + else { + c.accepted = true; + 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); + this.tchallDiag = getDiagram({ + position: parsedFen.position, + orientation: parsedFen.turn + }); + } + 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 @@ -1166,18 +1262,28 @@ export default { { data: { id: c.id } } ); } + else if (c.type == "simul" && !!c.seat && Array.isArray(c.seat)) + // TODO: if some players entered, start game + this.launchSimultaneous(c); this.send("deletechallenge_s", { data: { cid: c.id } }); } // In all cases, the challenge is consumed: ArrayFun.remove(this.challenges, ch => ch.id == c.id); }, // NOTE: when launching game, the challenge is already being deleted + // TODO: adapt for simultaneous games, or just write a new function (better) + launchSimultaneous: function(c) { + // TODO + }, launchGame: function(c) { // White player index 0, black player index 1: - const players = - !!c.mycolor - ? (c.mycolor == "w" ? [c.seat, c.from] : [c.from, c.seat]) + let players = + !!c.color + ? (c.color == "w" ? [c.from, c.seat] : [c.seat, c.from]) : shuffle([c.from, c.seat]); + players.forEach(p => { + if (!!p["tmpIds"]) delete p["tmpIds"]; + }); // These game informations will be shared let gameInfo = { id: getRandString(), @@ -1257,13 +1363,13 @@ export default { () => { const myIdx = (game.players[0].sid == this.st.user.sid ? 0 : 1); GameStorage.add(game, (err) => { - // If an error occurred, game is not added: a tab already - // added the game. Maybe a focused one, maybe not. - // We know for sure that it emitted the gong start sound. - // ==> Do not play it again. - if (!err && this.st.settings.sound) - new Audio("/sounds/newgame.flac").play().catch(() => {}); + // If an error occurred, game is not added: the focused tab + // already added the game. if (!this.focus) { + if (this.st.settings.sound) + // This will be played several times if several hidden tabs + // on Hall... TODO: fix that (how ?!) + new Audio("/sounds/newgame.flac").play().catch(() => {}); notify( "New live game", { body: "vs " + game.players[1-myIdx].name || "@nonymous" } @@ -1272,7 +1378,7 @@ export default { this.$router.push("/game/" + gameInfo.id); }); }, - this.focus ? 500 + 1000 * Math.random() : 0 + this.focus ? 0 : 500 + 1000 * Math.random() ); } } @@ -1281,7 +1387,7 @@ export default {