X-Git-Url: https://git.auder.net/?a=blobdiff_plain;f=public%2Fjavascripts%2Fcomponents%2Fproblems.js;h=a95529234fbe9ebaa7766d984395661271424e9a;hb=b955c65b942d09d24b5c3bed0d755d4f2f8f71f1;hp=dbd8340c96236d023f1d88b27ef2b38676ae7e9e;hpb=7931e479adf93c87771ded1892a0873af72ae46d;p=vchess.git diff --git a/public/javascripts/components/problems.js b/public/javascripts/components/problems.js index dbd8340c..a9552923 100644 --- a/public/javascripts/components/problems.js +++ b/public/javascripts/components/problems.js @@ -1,97 +1,344 @@ Vue.component('my-problems', { + props: ["probId","settings"], data: function () { return { - problems: problemArray //initial value + userId: user.id, + problems: [], //oldest first + myProblems: [], //same, but only mine + singletons: [], //requested problems (using #num) + display: "others", //or "mine" + curProb: null, //(reference to) current displayed problem (if any) + showSolution: false, + nomoreMessage: "", + mode: "analyze", //for game component + pbNum: 0, //to navigate directly to some problem + // New problem (to upload), or existing problem to edit: + modalProb: { + id: 0, //defined if it's an edit + uid: 0, //...also + fen: "", + instructions: "", + solution: "", + preview: false, + }, }; }, + // NOTE: always modals first, because otherwise "scroll to the end" undesirable effect template: ` -
- - - - - - -
-
- -

Add problem

-
+
+ +
+
+ +

{{ translate("Add a problem") }}

+
- - + +
-

- Allowed HTML tags: - <p>,<br>,<,ul>,<ol>,<li> -

- - + + +
-

- Note: if you made a mistake, please let me know at - contact@vchess.club -

+
+ + + +
+ + +
+
+
+ +
+
+ +

{{ nomoreMessage }}

+
+
+
+ + +
+
+
+

{{ curProb.instructions }}

+
+ + +
+

+ {{ translate("Show solution") }} +

+

{{ curProb.solution }}

+
+ +
+
+ + +
+ + +
`, - computed: { - sortedProblems: function() { - console.log("call"); - // Newest problem first - return this.problems.sort((p1,p2) => { return p2.added - p1.added; }); - }, - mailErrProblem: function() { - return "mailto:contact@vchess.club?subject=[" + variant + " problems] error"; + watch: { + probId: function() { + this.showProblem(this.probId); }, }, + created: function() { + if (!!this.probId) + this.showProblem(this.probId); + else + this.firstFetch(); + }, methods: { - fetchProblems: function(direction) { - return; //TODO: re-activate after server side is implemented (see routes/all.js) - if (this.problems.length == 0) - return; //what could we do?! - // Search for newest date (or oldest) - let last_dt = this.problems[0].added; - for (let i=0; i last_dt) || - (direction == "backward" && this.problems[i].added < last_dt)) + const pIdx = parray.findIndex(p => p.id == pid); + if (pIdx >= 0) { - last_dt = this.problems[i].added; + this.curProb = parray[pIdx]; + break; } } - ajax("/problems/" + variant, "GET", { - direction: direction, - last_dt: last_dt, - }, response => { - if (response.problems.length > 0) - this.problems = response.problems; + if (!this.curProb) + { + // Cannot find problem in current set; get from server, and add to singletons. + ajax( + "/problems/" + variant.id + "/" + pid, //TODO: variant ID should not be required + "GET", + response => { + if (!!response.problem) + { + this.singletons.push(response.problem); + this.curProb = response.problem; + this.display = (response.problem.uid == this.userId ? "mine" : "others"); + } + else + this.noMoreProblems("Sorry, problem " + pid + " does not exist"); + } + ); + } + else + this.display = (this.curProb.uid == this.userId ? "mine" : "others"); + }, + curProblems: function() { + switch (this.display) + { + case "others": + return this.problems; + case "mine": + return this.myProblems; + } + }, + // TODO?: get 50 from server but only show 10 at a time (for example) + showNext: function(direction) { + const nomorePb = + problems => { + if (!problems || problems.length == 0) + this.noMoreProblems("No more problems in this direction"); + }; + if (!this.curProb) + return this.fetchProblems(this.display, direction, nomorePb); + // Show next problem (older or newer): + let curProbs = this.curProblems(); + // Try to find a neighbour problem in the direction, among current set + const neighbor = this.findClosestNeighbor(this.curProb, curProbs, direction); + if (!!neighbor) + { + this.curProb = neighbor; + return; + } + // Boundary case: nothing in current set, need to fetch from server + const curSize = curProbs.length; + this.fetchProblems(this.display, direction, problems => { + if (problems.length > 0) + { + // Ok, found something: + this.curProb = + this.findClosestNeighbor(this.curProb, curProbs, direction); + } + else + nomorePb(); }); }, - showNewproblemModal: function() { + findClosestNeighbor: function(problem, probList, direction) { + let neighbor = undefined; + let smallestDistance = Number.MAX_SAFE_INTEGER; + for (let prob of probList) + { + const delta = Math.abs(prob.id - problem.id); + if (delta < smallestDistance && + ((direction == "backward" && prob.id < problem.id) + || (direction == "forward" && prob.id > problem.id))) + { + neighbor = prob; + smallestDistance = delta; + } + } + return neighbor; + }, + noMoreProblems: function(message) { + this.nomoreMessage = message; + let modalNomore = document.getElementById("modalNomore"); + modalNomore.checked = true; + setTimeout(() => modalNomore.checked = false, 2000); + }, + displayList: function() { + this.curProb = null; + location.hash = "#problems"; + // Fetch problems if first call (if #num, and then lists) + if (!this.listsInitialized) + this.firstFetch(); + }, + toggleListDisplay: function() { + const displays = ["mine","others"]; + const curIndex = displays.findIndex(item => item == this.display); + this.display = displays[1-curIndex]; + }, + fetchProblems: function(type, direction, cb) { + let problems = (type == "others" ? this.problems : this.myProblems); + // "last datetime" set at a value OK for an empty initial array + let last_dt = (direction=="forward" ? 0 : Number.MAX_SAFE_INTEGER); + if (problems.length > 0) + { + // Search for newest date (or oldest) + last_dt = problems[0].added; + for (let i=1; i last_dt) || + (direction == "backward" && problems[i].added < last_dt)) + { + last_dt = problems[i].added; + } + } + } + ajax( + "/problems/" + variant.id, + "GET", + { + type: type, + direction: direction, + last_dt: last_dt, + }, + response => { + if (response.problems.length > 0) + { + Array.prototype.push.apply(problems, response.problems.sort( + (p1,p2) => { return p2.added - p1.added; })); + // If one list is empty but not the other, show the non-empty + const otherArray = + (type == "mine" ? this.problems : this.myProblems); + if (otherArray.length == 0) + this.display = type; + this.$forceUpdate(); //TODO... + } + if (!!cb) + cb(response.problems); + } + ); + }, + previewProblem: function() { + if (!V.IsGoodFen(this.modalProb.fen)) + return alert(translations["Bad FEN description"]); + if (this.modalProb.instructions.trim().length == 0) + return alert(translations["Empty instructions"]); + if (this.modalProb.solution.trim().length == 0) + return alert(translations["Empty solution"]); + Vue.set(this.modalProb, "preview", true); + }, + editProblem: function(prob) { + this.modalProb = prob; + Vue.set(this.modalProb, "preview", false); document.getElementById("modal-newproblem").checked = true; }, - postNewProblem: function() { - const fen = document.getElementById("newpbFen").value; - if (!V.IsGoodFen(fen)) - return alert("Bad FEN string"); - const instructions = document.getElementById("newpbInstructions").value; - const solution = document.getElementById("newpbSolution").value; - ajax("/problems/" + variant, "POST", { - fen: fen, - instructions: instructions, - solution: solution, - }, response => { - document.getElementById("modal-newproblem").checked = false; - }); + deleteProblem: function(pid) { + ajax( + "/problems/" + pid, + "DELETE", + response => { + // Delete problem from the list on client side + let problems = this.curProblems(); + const pIdx = problems.findIndex(p => p.id == pid); + problems.splice(pIdx, 1); + } + ); + }, + sendProblem: function() { + // Send it to the server and close modal + ajax( + "/problems/" + variant.id, + (this.modalProb.id > 0 ? "PUT" : "POST"), + this.modalProb, + response => { + document.getElementById("modal-newproblem").checked = false; + Vue.set(this.modalProb, "preview", false); + if (this.modalProb.id == 0) + { + this.myProblems.unshift({ + added: Date.now(), + id: response.id, + uid: user.id, + fen: this.modalProb.fen, + instructions: this.modalProb.instructions, + solution: this.modalProb.solution, + }); + if (!this.curProb && this.display != "mine") + this.display = "mine"; + } + else + this.modalProb.id = 0; + } + ); }, }, })