X-Git-Url: https://git.auder.net/?p=vchess.git;a=blobdiff_plain;f=client%2Fsrc%2Fviews%2FProblems.vue;h=bcfb3a3939cb2989b711dc4565eae6e74f7fbe88;hp=ae918b95a8d303bb24f81c1cdc94b9328a150c36;hb=a9e7935190d8fc112e674add05e86b8d0152e8df;hpb=32f6285ee325a14286562a53baefc647201df2af diff --git a/client/src/views/Problems.vue b/client/src/views/Problems.vue index ae918b95..bcfb3a39 100644 --- a/client/src/views/Problems.vue +++ b/client/src/views/Problems.vue @@ -18,6 +18,7 @@ main ) option( v-for="v in [emptyVar].concat(st.variants)" + v-if="!v.noProblems" :value="v.id" :selected="curproblem.vid==v.id" ) @@ -47,15 +48,15 @@ main .row(v-if="showOne") .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2 #topPage - .button-group(v-if="st.user.id == curproblem.uid") + .button-group(v-if="canIedit(curproblem.uid)") button(@click="editProblem(curproblem)") {{ st.tr["Edit"] }} button(@click="deleteProblem(curproblem)") {{ st.tr["Delete"] }} span.vname {{ curproblem.vname }} span.uname ({{ curproblem.uname }}) button.marginleft(@click="backToList()") {{ st.tr["Back to list"] }} - button.nomargin(@click="gotoPrevNext($event,curproblem,1)") - | {{ st.tr["Previous"] }} - button.nomargin(@click="gotoPrevNext($event,curproblem,-1)") + button.nomargin(@click="gotoPrevNext(curproblem,1)") + | {{ st.tr["Previous_p"] }} + button.nomargin(@click="gotoPrevNext(curproblem,-1)") | {{ st.tr["Next_p"] }} p.oneInstructions.clickable( v-html="parseHtml(curproblem.instruction)" @@ -71,15 +72,17 @@ main #controls button#newProblem(@click="prepareNewProblem()") | {{ st.tr["New problem"] }} - label(for="checkboxMine") {{ st.tr["My problems"] }} - input#checkboxMine( - type="checkbox" - v-model="onlyMines" - ) + div#myProblems(v-if="st.user.id > 0") + label(for="checkboxMine") {{ st.tr["My problems"] }} + input#checkboxMine( + type="checkbox" + v-model="onlyMine" + ) label(for="selectVariant") {{ st.tr["Variant"] }} select#selectVariant(v-model="selectedVar") option( v-for="v in [emptyVar].concat(st.variants)" + v-if="!v.noProblems" :value="v.id" ) | {{ v.name }} @@ -89,16 +92,16 @@ main th {{ st.tr["Instructions"] }} th {{ st.tr["Number"] }} tr( - v-for="p in problems" - v-show="displayProblem(p)" + v-for="p in problems[onlyMine ? 'mine' : 'others']" + v-show="onlyMine || !selectedVar || p.vid == selectedVar" @click="setHrefPid(p)" ) td {{ p.vname }} td {{ firstChars(p.instruction) }} td {{ p.id }} button#loadMoreBtn( - v-if="hasMore" - @click="loadMore()" + v-if="hasMore[onlyMine ? 'mine' : 'others']" + @click="loadMore(onlyMine ? 'mine' : 'others')" ) | {{ st.tr["Load more"] }} BaseGame( @@ -112,6 +115,7 @@ main import { store } from "@/store"; import { ajax } from "@/utils/ajax"; import { checkProblem } from "@/data/problemCheck"; +import params from "@/parameters"; import { getDiagram } from "@/utils/printDiagram"; import { processModalClick } from "@/utils/modalClick"; import { ArrayFun } from "@/utils/array"; @@ -140,12 +144,15 @@ export default { }, loadedVar: 0, //corresponding to loaded V selectedVar: 0, //to filter problems based on variant - problems: [], + problems: { "mine": [], "others": [] }, // timestamp of oldest showed problem: - cursor: Number.MAX_SAFE_INTEGER, + cursor: { + mine: Number.MAX_SAFE_INTEGER, + others: Number.MAX_SAFE_INTEGER + }, // hasMore == TRUE: a priori there could be more problems to load - hasMore: true, - onlyMines: false, + hasMore: { mine: true, others: true }, + onlyMine: false, showOne: false, infoMsg: "", game: { @@ -155,25 +162,9 @@ export default { }; }, created: function() { - ajax( - "/problems", - "GET", - { - data: { cursor: this.cursor }, - success: (res) => { - // The returned list is sorted from most recent to oldest - this.problems = res.problems; - const L = res.problems.length; - if (L > 0) this.cursor = res.problems[L - 1].added; - else this.hasMore = false; - const showOneIfPid = () => { - const pid = this.$route.query["id"]; - if (!!pid) this.showProblem(this.problems.find(p => p.id == pid)); - }; - this.decorate(this.problems, showOneIfPid); - } - } - ); + const pid = this.$route.query["id"]; + if (!!pid) this.showProblem(pid); + else this.loadMore("others", () => { this.loadMore("mine"); }); }, mounted: function() { document.getElementById("newprobDiv") @@ -183,13 +174,20 @@ export default { // st.variants changes only once, at loading from [] to [...] "st.variants": function() { // Set problems vname (either all are set or none) - if (this.problems.length > 0 && this.problems[0].vname == "") - this.problems.forEach(p => this.setVname(p)); + let problems = this.problems["others"].concat(this.problems["mine"]); + if (problems.length > 0 && problems[0].vname == "") + problems.forEach(p => this.setVname(p)); }, $route: function(to) { const pid = to.query["id"]; - if (!!pid) this.showProblem(this.problems.find(p => p.id == pid)); - else this.showOne = false; + if (!!pid) this.showProblem(pid); + else { + if (this.cursor["others"] == Number.MAX_SAFE_INTEGER) + // Back from a single problem view at initial loading: + // problems lists are empty! + this.loadMore("others", () => { this.loadMore("mine"); }); + this.showOne = false; + } } }, methods: { @@ -278,6 +276,7 @@ export default { this.loadVariant(prob.vid, () => { // Set FEN if possible (might not be correct yet) if (V.IsGoodFen(prob.fen)) this.setDiagram(prob); + else prob.diag = ""; }); }, loadVariant: async function(vid, cb) { @@ -296,6 +295,7 @@ export default { // variant could not be ready, or not defined if (prob.vid > 0 && this.loadedVar == prob.vid && V.IsGoodFen(prob.fen)) this.setDiagram(prob); + else prob.diag = ""; }, setDiagram: function(prob) { // Condition: prob.fen is correct and global V is ready @@ -306,41 +306,65 @@ export default { }; prob.diag = getDiagram(args); }, - displayProblem: function(p) { - return ( - (!this.selectedVar || p.vid == this.selectedVar) && - ((this.onlyMines && p.uid == this.st.user.id) || - (!this.onlyMines && p.uid != this.st.user.id)) - ); - }, - showProblem: function(p) { - this.loadVariant(p.vid, () => { - // The FEN is already checked at this stage: - this.game.vname = p.vname; - this.game.mycolor = V.ParseFen(p.fen).turn; //diagram orientation - this.game.fenStart = p.fen; - this.game.fen = p.fen; - this.showOne = true; - // $nextTick to be sure $refs["basegame"] exists - this.$nextTick(() => { - this.$refs["basegame"].re_setVariables(this.game); }); - this.copyProblem(p, this.curproblem); - }); + showProblem: function(p_id) { + const processWhenWeHaveProb = () => { + this.loadVariant(p.vid, () => { + this.onlyMine = (p.uid == this.st.user.id); + // The FEN is already checked at this stage: + this.game.vname = p.vname; + this.game.mycolor = V.ParseFen(p.fen).turn; //diagram orientation + this.game.fenStart = p.fen; + this.game.fen = p.fen; + this.showOne = true; + // $nextTick to be sure $refs["basegame"] exists + this.$nextTick(() => { + this.$refs["basegame"].re_setVariables(this.game); }); + this.curproblem.showSolution = false; //in case of + this.copyProblem(p, this.curproblem); + }); + }; + let p = undefined; + if (typeof p_id == "object") p = p_id; + else { + const problems = this.problems["others"].concat(this.problems["mine"]); + p = problems.find(prob => prob.id == p_id); + } + if (!p) { + // Bad luck: problem not in list. Get from server + ajax( + "/problems", + "GET", + { + data: { id: p_id }, + success: (res) => { + this.decorate([res.problem], () => { + p = res.problem; + const mode = (p.uid == this.st.user.id ? "mine" : "others"); + this.problems[mode].push(p); + processWhenWeHaveProb(); + }); + } + } + ); + } else processWhenWeHaveProb(); }, - gotoPrevNext: function(e, prob, dir) { - const startIdx = this.problems.findIndex(p => p.id == prob.id); - let nextIdx = startIdx + dir; - while ( - nextIdx >= 0 && - nextIdx < this.problems.length && - ((this.onlyMines && this.problems[nextIdx].uid != this.st.user.id) || - (!this.onlyMines && this.problems[nextIdx].uid == this.st.user.id)) - ) - nextIdx += dir; - if (nextIdx >= 0 && nextIdx < this.problems.length) - this.setHrefPid(this.problems[nextIdx]); - else - alert(this.st.tr["No more problems"]); + gotoPrevNext: function(prob, dir) { + const mode = (this.onlyMine ? "mine" : "others"); + const problems = this.problems[mode]; + const startIdx = problems.findIndex(p => p.id == prob.id); + const nextIdx = startIdx + dir; + if (nextIdx >= 0 && nextIdx < problems.length) + this.setHrefPid(problems[nextIdx]); + else if (this.hasMore[mode]) { + this.loadMore( + mode, + (nbProbs) => { + if (nbProbs > 0) this.gotoPrevNext(prob, dir); + else alert(this.st.tr["No more problems"]); + } + ); + } + else alert(this.st.tr["No more problems"]); }, prepareNewProblem: function() { this.resetCurProb(); @@ -361,7 +385,12 @@ export default { data: { prob: this.curproblem }, success: (ret) => { if (edit) { - let editedP = this.problems.find(p => p.id == this.curproblem.id); + let editedP = this.problems["mine"] + .find(p => p.id == this.curproblem.id); + if (!editedP) + // I'm an admin and edit another user' problem + editedP = this.problems["others"] + .find(p => p.id == this.curproblem.id); this.copyProblem(this.curproblem, editedP); this.showProblem(editedP); } @@ -370,7 +399,8 @@ export default { newProblem.id = ret.id; newProblem.uid = this.st.user.id; newProblem.uname = this.st.user.name; - this.problems = [newProblem].concat(this.problems); + this.problems["mine"] = + [newProblem].concat(this.problems["mine"]); } document.getElementById("modalNewprob").checked = false; this.infoMsg = ""; @@ -378,6 +408,9 @@ export default { } ); }, + canIedit: function(puid) { + return params.devs.concat([puid]).includes(this.st.user.id); + }, editProblem: function(prob) { // prob.diag might correspond to some other problem or be empty: this.setDiagram(prob); //V is loaded at this stage @@ -392,26 +425,42 @@ export default { { data: { id: prob.id }, success: () => { - ArrayFun.remove(this.problems, p => p.id == prob.id); + const mode = prob.uid == (this.st.user.id ? "mine" : "others"); + ArrayFun.remove(this.problems[mode], p => p.id == prob.id); this.backToList(); } } ); } }, - loadMore: function() { + loadMore: function(mode, cb) { ajax( "/problems", "GET", { - data: { cursor: this.cursor }, + data: { + uid: this.st.user.id, + mode: mode, + cursor: this.cursor[mode] + }, success: (res) => { const L = res.problems.length; if (L > 0) { + this.cursor[mode] = res.problems[L - 1].added; + // Remove potential duplicates: + const pids = this.problems[mode].map(p => p.id); + ArrayFun.remove(res.problems, p => pids.includes(p.id), "all"); this.decorate(res.problems); - this.problems = this.problems.concat(res.problems); - this.cursor = res.problems[L - 1].added; - } else this.hasMore = false; + this.problems[mode] = + this.problems[mode].concat(res.problems) + // TODO: problems are alrady sorted, would just need to insert + // the current individual problem in list; more generally + // there is probably only one misclassified problem. + // (Unless the user navigated several times by URL to show a + // single problem...) + .sort((p1, p2) => p2.added - p1.added); + } else this.hasMore[mode] = false; + if (!!cb) cb(L); } } ); @@ -454,6 +503,9 @@ p.oneInstructions padding: 2px 5px background-color: lightgreen +#myProblems + display: inline-block + #topPage span.vname font-weight: bold