X-Git-Url: https://git.auder.net/?p=vchess.git;a=blobdiff_plain;f=client%2Fsrc%2Fviews%2FHall.vue;h=94a7bbad1d9f59f3288c5d0c775a393d68699384;hp=57557d493e2f0c69a26ab5ad8d2536fba01d99f1;hb=bae751bc6bc548791772c3ff5883a03deeb77264;hpb=2c5d7b20742b802d9c47916915c1114bcfc9a9c3 diff --git a/client/src/views/Hall.vue b/client/src/views/Hall.vue index 57557d49..94a7bbad 100644 --- a/client/src/views/Hall.vue +++ b/client/src/views/Hall.vue @@ -12,9 +12,13 @@ main div#acceptDiv(role="dialog") .card p.text-center - span.variantName {{ curChallToAccept.vname }} + span.variantName + | {{ curChallToAccept.vname }} + | {{ curChallToAccept.options.abridged || '' }} 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" @@ -46,7 +50,24 @@ main :value="v.id" :selected="newchallenge.vid==v.id" ) - | {{ v.name }} + | {{ v.display }} + // Variant-specific options (often at least randomness) + fieldset(v-if="!!newchallenge.V && newchallenge.V.Options") + div(v-for="select of newchallenge.V.Options.select || []") + label(:for="select.variable + '_opt'") {{ st.tr[select.label] }} * + select(:id="select.variable + '_opt'") + option( + v-for="o of select.options" + :value="o.value" + :selected="o.value == select.defaut" + ) + | {{ st.tr[o.label] }} + div(v-for="check of newchallenge.V.Options.check || []") + label(:for="check.variable + '_opt'") {{ st.tr[check.label] }} * + input( + :id="check.variable + '_opt'" + type="checkbox" + :checked="check.defaut") fieldset label(for="cadence") {{ st.tr["Cadence"] }} * div#predefinedCadences @@ -59,12 +80,6 @@ main v-model="newchallenge.cadence" placeholder="5+0, 1h+30s, 5d ..." ) - fieldset - label(for="selectRandomLevel") {{ st.tr["Randomness"] }} * - select#selectRandomLevel(v-model="newchallenge.randomness") - option(value="0") {{ st.tr["Deterministic"] }} - option(value="1") {{ st.tr["Symmetric random"] }} - option(value="2") {{ st.tr["Asymmetric random"] }} fieldset label(for="memorizeChall") {{ st.tr["Memorize"] }} input#memorizeChall( @@ -88,7 +103,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 +120,7 @@ main button(@click="issueNewChallenge()") {{ st.tr["Send challenge"] }} input#modalPeople.modal( type="checkbox" - @click="resetSocialColor()" + @click="toggleSocialColor()" ) div#peopleWrap( role="dialog" @@ -113,14 +134,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 +157,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"] }} @@ -147,16 +169,18 @@ main tr th {{ st.tr["Variant"] }} th {{ st.tr["Cadence"] }} - th {{ st.tr["Random?"] }} + th {{ st.tr["Options"] }} th tbody + // TODO: remove the check !!pc.options tr( v-for="pc in presetChalls" @click="newChallFromPreset(pc)" + v-if="!!pc.options" ) td {{ pc.vname }} td {{ pc.cadence }} - td(:class="getRandomnessClass(pc)") + td(:class="getRandomnessClass(pc)") {{ pc.options.abridged || '' }} td.remove-preset(@click="removePresetChall($event, pc)") img(src="/images/icons/delete.svg") .row @@ -166,7 +190,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 +206,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 +236,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 +245,7 @@ export default { name: "my-hall", components: { Chat, + UserBio, GameList, ChallengeList }, @@ -238,12 +264,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, + options: {}, // VariantRules object, stored to not interfere with - // diagrams of targetted challenges: + // diagrams of targeted challenges: V: null, vname: "", diag: "", //visualizing FEN @@ -251,14 +278,13 @@ export default { }, focus: true, tchallDiag: "", - curChallToAccept: {from: {}}, + curChallToAccept: { from: {}, options: {} }, presetChalls: JSON.parse(localStorage.getItem("presetChalls") || "[]"), conn: null, connexionString: "", socketCloseListener: 0, // Related to (killing of) self multi-connects: - newConnect: {}, - killed: {} + newConnect: {} }; }, watch: { @@ -266,7 +292,7 @@ export default { "st.variants": function() { // Set potential challenges and games variant names: this.challenges.concat(this.games).forEach(o => { - if (!o.vname) o.vname = this.getVname(o.vid); + if (!o.vname) this.setVname(o); }); if (!this.newchallenge.V && this.newchallenge.vid > 0) this.loadNewchallVariant(); @@ -291,13 +317,37 @@ export default { id: my.id, name: my.name, tmpIds: { - tmpId: { page: "/", focus: true } + [tmpId]: { page: "/", focus: true } } } ); const connectAndPoll = () => { this.send("connect"); this.send("pollclientsandgamers"); + if (!!this.$route.query["challenge"]) { + // Automatic challenge sending, for tournaments + this.loadNewchallVariant( + () => { + Object.assign( + 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"], + options: {}, + memorize: false + } + ); + window.doClick("modalNewgame"); + }, + this.$route.query["variant"] + ); + } }; // Initialize connection this.connexionString = @@ -323,7 +373,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); }); @@ -372,16 +428,16 @@ export default { response.challenges.map(c => { const from = { name: names[c.uid], id: c.uid }; //or just name const type = this.classifyObject(c); - const vname = this.getVname(c.vid); + this.setVname(c); return Object.assign( {}, + c, { type: type, - vname: vname, from: from, - to: c.target ? names[c.target] : "" - }, - c + to: c.target ? names[c.target] : "", + options: JSON.parse(c.options) + } ); }) ); @@ -400,7 +456,8 @@ export default { } } ); - } else addChallenges(); + } + else addChallenges(); } } ); @@ -420,13 +477,12 @@ export default { this.conn = null; }, getRandomnessClass: function(pc) { - return { - ["random-" + pc.randomness]: true - }; - }, - openModalPeople: function() { - window.doClick("modalPeople"); - document.getElementById("inputChat").focus(); + const opts = pc.options; + if (opts.randomness === undefined && opts.random === undefined) + return {}; + if (opts.randomness !== undefined) + return { ["random-" + opts.randomness]: true }; + return { ["random-" + (opts.random ? 2 : 0)]: true }; }, anonymousCount: function() { let count = 0; @@ -449,9 +505,14 @@ export default { this.focus = false; this.send("losefocus"); }, + invColor: function(c) { + if (c == 'w') return this.st.tr["Black"]; + return this.st.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; @@ -460,12 +521,22 @@ export default { this.partialResetNewchallenge(); window.doClick("modalNewgame"); }, + sameOptions: function(opt1, opt2) { + const keys1 = Object.keys(opt1), + keys2 = Object.keys(opt2); + if (keys1.length != keys2.length) return false; + for (const key1 of keys1) { + if (!keys2.includes(key1)) return false; + if (opt1[key1] != opt2[key1]) return false; + } + return true; + }, addPresetChall: function(chall) { // Add only if not already existing: if (this.presetChalls.some(c => c.vid == chall.vid && c.cadence == chall.cadence && - c.randomness == chall.randomness + this.sameOptions(c.options, chall.options) )) { return; } @@ -475,7 +546,7 @@ export default { vid: chall.vid, vname: chall.vname, //redundant, but easier cadence: chall.cadence, - randomness: chall.randomness + options: chall.options }); localStorage.setItem("presetChalls", JSON.stringify(this.presetChalls)); }, @@ -494,6 +565,7 @@ export default { if (!this.newchallenge.to) { // Reset potential FEN + diagram this.newchallenge.fen = ""; + this.newchallenge.color = ''; this.newchallenge.diag = ""; } }, @@ -502,14 +574,19 @@ export default { document.getElementById("cadence").focus(); }, send: function(code, obj) { - if (!!this.conn) { + if (!!this.conn && this.conn.readyState == 1) { this.conn.send(JSON.stringify(Object.assign({ code: code }, obj))); } }, - getVname: function(vid) { - const variant = this.st.variants.find(v => v.id == vid); + setVname: function(obj) { + const variant = this.st.variants.find(v => v.id == obj.vid); // this.st.variants might be uninitialized (variant == null) - return variant ? variant.name : ""; + if (!!variant) { + obj.vname = variant.name; + obj.vdisp = variant.display; + } + // NOTE: Next line is used in loadNewchallVariant + return (!variant ? "" : variant.name); }, filterChallenges: function(type) { return this.challenges.filter(c => c.type == type); @@ -517,9 +594,10 @@ 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" : "live"); }, setDisplay: function(letter, type, e) { this[letter + "display"] = type; @@ -567,18 +645,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 +716,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,9 +732,11 @@ 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 { + } + else { this.people[data.from[0]].tmpIds[data.from[1]] = { page: page, focus: true }; this.$forceUpdate(); //TODO: shouldn't be required @@ -675,13 +755,21 @@ 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" - ); - } else { + // 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]; // Corr games are always reachable: @@ -713,13 +801,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 +823,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; } @@ -769,7 +850,7 @@ export default { id: c.id, from: this.st.user.sid, to: c.to, - randomness: c.randomness, + options: JSON.stringify(c.options), fen: c.fen, vid: c.vid, cadence: c.cadence, @@ -814,18 +895,25 @@ export default { // 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); - newGame.vname = this.getVname(game.vid); + this.setVname(game); if (!game.score) // New game from Hall newGame.score = "*"; + // TODO: remove patch on next line (options || "{}") + newGame.options = JSON.parse(newGame.options || "{}"); this.games.push(newGame); if ( + newGame.score == '*' && (newGame.type == "live" && this.gdisplay == "corr") || (newGame.type == "corr" && this.gdisplay == "live") ) { @@ -843,12 +931,15 @@ export default { } case "startgame": { // New game just started, I'm involved - const gameInfo = data.data; - if (this.classifyObject(gameInfo) == "live") + let gameInfo = data.data; + if (this.classifyObject(gameInfo) == "live") { + // TODO: remove patch on next line (+ const gameInfo) + if (!gameInfo.options) gameInfo.options = "{}"; 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 +968,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 @@ -886,18 +978,14 @@ export default { } this.cursor = res.games[L - 1].created; let moreGames = res.games.map(g => { - const vname = this.getVname(g.vid); - return Object.assign( - {}, - g, - { - type: "corr", - vname: vname - } - ); + this.setVname(g); + g.type = "corr"; + g.options = JSON.parse(g.options); + return g; }); this.games = this.games.concat(moreGames); - } else this.hasMore = false; + } + else this.hasMore = false; } } ); @@ -912,12 +1000,13 @@ export default { ) { let newChall = Object.assign({}, chall); newChall.type = this.classifyObject(chall); - newChall.randomness = chall.randomness; + // TODO: remove patch on next line (options || "{}") + newChall.options = JSON.parse(chall.options || "{}"); newChall.added = Date.now(); let fromValues = Object.assign({}, this.people[chall.from]); delete fromValues["pages"]; //irrelevant in this context newChall.from = Object.assign({ sid: chall.from }, fromValues); - newChall.vname = this.getVname(newChall.vid); + this.setVname(newChall); this.challenges.push(newChall); if ( (newChall.type == "live" && this.cdisplay == "corr") || @@ -927,15 +1016,23 @@ export default { .getElementById("btnC" + newChall.type) .classList.add("somethingnew"); } + if (!!chall.to && chall.to == this.st.user.name) { + 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 || "@nonymous") } + ); + } } }, - loadNewchallVariant: async function(cb) { - const vname = this.getVname(this.newchallenge.vid); + loadNewchallVariant: async function(cb, vname) { + vname = vname || this.setVname(this.newchallenge); await import("@/variants/" + vname + ".js") .then((vModule) => { window.V = vModule[vname + "Rules"]; this.newchallenge.V = window.V; - this.newchallenge.vname = vname; if (!!cb) cb(); }); }, @@ -953,16 +1050,18 @@ 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 = ""; + } + else this.newchallenge.diag = ""; }, newChallFromPreset(pchall) { this.partialResetNewchallenge(); this.newchallenge.vid = pchall.vid; this.newchallenge.cadence = pchall.cadence; - this.newchallenge.randomness = pchall.randomness; + this.newchallenge.options = pchall.options; + this.newchallenge.fromPreset = true; this.loadNewchallVariant(this.issueNewChallenge); }, issueNewChallenge: async function() { @@ -974,7 +1073,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,30 +1085,60 @@ 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) { alert(error); return; } window.V = this.newchallenge.V; - error = checkChallenge(this.newchallenge); + let chall = Object.assign({}, this.newchallenge); + if (!this.newchallenge.fromPreset) chall.options = { options: {} }; + if (V.Options && !this.newchallenge.fromPreset) { + // Get/set options variables (if any) / TODO: v-model?! + for (const check of this.newchallenge.V.Options.check || []) { + const elt = document.getElementById(check.variable + "_opt"); + chall.options[check.variable] = elt.checked; + } + for (const select of this.newchallenge.V.Options.select || []) { + const elt = document.getElementById(select.variable + "_opt"); + const tryIntVal = parseInt(elt.value, 10); + chall.options[select.variable] = + (isNaN(tryIntVal) ? elt.value : tryIntVal); + } + } + error = checkChallenge(chall); if (error) { - alert(error); + alert(this.st.tr[error]); return; } + chall.options.abridged = V.AbbreviateOptions(chall.options); + // Add only if not already issued (not counting FEN): // NOTE: "from" information is not required here - let chall = Object.assign({}, this.newchallenge); - // Add only if not already issued (not counting target or 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 + this.sameOptions(c.options, chall.options) )) { alert(this.st.tr["Challenge already exists"]); return; } - if (this.newchallenge.memorize) this.addPresetChall(this.newchallenge); + if (this.newchallenge.memorize) this.addPresetChall(chall); delete chall["V"]; delete chall["diag"]; const finishAddChallenge = cid => { @@ -1021,7 +1152,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) { @@ -1045,7 +1179,12 @@ export default { this.challenges.splice(challToDelIdx, 1); } this.send("newchallenge", { - data: Object.assign({ from: this.st.user.sid }, chall) + data: Object.assign( + // Temporarily add sender infos to display challenge on Discord. + { from: this.st.user.sid, sender: this.st.user.name }, + chall, + { options: JSON.stringify(chall.options) } + ) }); // Add new challenge: chall.from = { @@ -1058,10 +1197,9 @@ export default { chall.type = ctype; chall.vname = this.newchallenge.vname; this.challenges.push(chall); - // Remember cadence + vid for quicker further challenges: + // Remember cadence + vid for quicker further challenges: localStorage.setItem("cadence", chall.cadence); localStorage.setItem("vid", chall.vid); - localStorage.setItem("challRandomness", chall.randomness); document.getElementById("modalNewgame").checked = false; // Show the challenge if not on current display if ( @@ -1074,13 +1212,20 @@ export default { if (ctype == "live") { // Live challenges have a random ID finishAddChallenge(null); - } else { - // Correspondance game: send challenge to server + } + else { + // Correspondence game: send challenge to server ajax( "/challenges", "POST", { - data: { chall: chall }, + data: { + chall: Object.assign( + {}, + chall, + { options: JSON.stringify(chall.options) } + ) + }, success: (response) => { finishAddChallenge(response.id); } @@ -1113,7 +1258,8 @@ export default { else // Corr challenge: just remove the challenge this.send("deletechallenge_s", { data: { cid: c.id } }); - } else { + } + else { const oppsid = this.getOppsid(c); if (!!oppsid) this.send("refusechallenge", { data: c.id, target: oppsid }); @@ -1137,25 +1283,26 @@ 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 - }); + 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 @@ -1174,15 +1321,18 @@ export default { // NOTE: when launching game, the challenge is already being deleted 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(), - fen: c.fen || V.GenRandInitFen(c.randomness), - randomness: c.randomness, //for rematch + fen: c.fen || V.GenRandInitFen(c.options), + options: JSON.stringify(c.options), //for rematch players: players, vid: c.vid, cadence: c.cadence @@ -1214,9 +1364,17 @@ export default { // on game just after, the main Hall will be notified. }; if (c.type == "live") { + // TODO: ask my IP + opp IP, to add to game infos? (potential bans) notifyNewgame(); this.startNewGame(gameInfo); - } else { + // Increment game stats counter in DB + ajax( + "/gamestat", + "POST", + { data: { vid: gameInfo.vid } } + ); + } + else { // corr: game only on server ajax( "/games", @@ -1238,13 +1396,13 @@ export default { }, // NOTE: for live games only (corr games start on the server) startNewGame: function(gameInfo) { + this.setVname(gameInfo); const game = Object.assign( {}, gameInfo, { // (other) Game infos: constant fenStart: gameInfo.fen, - vname: this.getVname(gameInfo.vid), created: Date.now(), // Game state (including FEN): will be updated moves: [], @@ -1257,22 +1415,22 @@ 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" } + { body: "vs " + (game.players[1-myIdx].name || "@nonymous") } ); } this.$router.push("/game/" + gameInfo.id); }); }, - this.focus ? 500 + 1000 * Math.random() : 0 + this.focus ? 0 : 500 + 1000 * Math.random() ); } } @@ -1281,7 +1439,7 @@ export default {