Almost finished problems section
[vchess.git] / public / javascripts / components / problems.js
CommitLineData
4ecf423b 1Vue.component('my-problems', {
da06a6eb
BA
2 data: function () {
3 return {
ff1d4c3f 4 userId: user.id,
81da2786 5 problems: [], //oldest first
ff1d4c3f
BA
6 myProblems: [], //same, but only mine
7 display: "list", //or "myList"
8 curIdx: -1, //index in (current) problems array
9 showSolution: false,
10 // New problem (to upload), or existing problem to edit:
11 modalProb: {
12 id: 0, //defined if it's an edit
77fa6d1f 13 fen: "",
45109880
BA
14 instructions: "",
15 solution: "",
a9f262f3 16 preview: false,
45109880 17 },
da06a6eb
BA
18 };
19 },
ff1d4c3f
BA
20 // TODO: problem edit, just fill modalProb + adjust AJAX call
21 // problem delete: just AJAX call + confirm
4ecf423b 22 template: `
a5d56686
BA
23 <div class="col-sm-12 col-md-10 col-md-offset-1 col-lg-8 col-lg-offset-2">
24 <div id="problemControls" class="button-group">
ff1d4c3f 25 <button :aria-label='translate("Previous problem(s)")' class="tooltip" @click="showNext('backward')">
a5d56686
BA
26 <i class="material-icons">skip_previous</i>
27 </button>
ff1d4c3f 28 <button :aria-label='translate("Add a problem")' class="tooltip" onClick="doClick('modal-newproblem')">
247356cd 29 {{ translate("New") }}
a5d56686 30 </button>
ff1d4c3f 31 <button :aria-label='translate("Next problem(s)")' class="tooltip" @click="showNext('forward')">
a5d56686
BA
32 <i class="material-icons">skip_next</i>
33 </button>
34 </div>
ff1d4c3f
BA
35 <div id="mainBoard" v-show="curIdx>=0">
36 <div id="instructions-div" class="section-content">
37 <p id="problem-instructions">{{ curProb.instructions }}</p>
38 </div>
39 <my-board :fen="curProb.fen"></my-board>
40 <div id="solution-div" class="section-content">
41 <h3 class="clickable" @click="showSolution = !showSolution">
42 {{ translations["Show solution"] }}
43 </h3>
44 <p id="problem-solution" v-show="showSolution">{{ curProb.solution }}</p>
45 <div class="button-group" v-show="curProb.uid==userId">
46 <button>Edit</button>
47 <button>Delete</button>
48 </div>
49 </div>
50 </div>
51 <button v-if="!!userId" @click="toggleListDisplay()">My problems (only)</button>
52 <my-problem-summary v-show="curIdx<0"
53 v-for="(p,idx) in sortedProblems" @click="setCurIdx(idx)"
54 v-bind:prob="p" v-bind:preview="false" v-bind:key="p.id">
da06a6eb
BA
55 </my-problem-summary>
56 <input type="checkbox" id="modal-newproblem" class="modal">
ff1d4c3f
BA
57 <div role="dialog" aria-labelledby="modalProblemTxt">
58 <div v-show="!modalProb.preview" class="card newproblem-form">
da06a6eb 59 <label for="modal-newproblem" class="modal-close"></label>
ff1d4c3f
BA
60 <h3 id="modalProblemTxt">{{ translate("Add a problem") }}</h3>
61 <form @submit.prevent="previewNewProblem()">
da06a6eb 62 <fieldset>
247356cd 63 <label for="newpbFen">FEN</label>
ff1d4c3f 64 <input id="newpbFen" type="text" v-model="modalProb.fen"
e081ffe3 65 :placeholder='translate("Full FEN description")'/>
da06a6eb
BA
66 </fieldset>
67 <fieldset>
247356cd
BA
68 <p class="emphasis">{{ translate("Safe HTML tags allowed") }}</p>
69 <label for="newpbInstructions">{{ translate("Instructions") }}</label>
ff1d4c3f 70 <textarea id="newpbInstructions" v-model="modalProb.instructions"
d289b043 71 :placeholder='translate("Describe the problem goal")'></textarea>
247356cd 72 <label for="newpbSolution">{{ translate("Solution") }}</label>
ff1d4c3f 73 <textarea id="newpbSolution" v-model="modalProb.solution"
d289b043 74 :placeholder='translate("How to solve the problem?")'></textarea>
247356cd 75 <button class="center-btn">{{ translate("Preview") }}</button>
da06a6eb 76 </fieldset>
da06a6eb
BA
77 </form>
78 </div>
ff1d4c3f 79 <div v-show="modalProb.preview" class="card newproblem-preview">
b5fb8e69 80 <label for="modal-newproblem" class="modal-close"></label>
ff1d4c3f 81 <my-problem-summary v-bind:prob="modalProb" v-bind:preview="true"></my-problem-summary>
a5d56686 82 <div class="button-group">
ff1d4c3f 83 <button @click="modalProb.preview=false">{{ translate("Cancel") }}</button>
247356cd 84 <button @click="sendNewProblem()">{{ translate("Send") }}</button>
b5fb8e69 85 </div>
45109880 86 </div>
da06a6eb 87 </div>
4ecf423b
BA
88 </div>
89 `,
da06a6eb
BA
90 computed: {
91 sortedProblems: function() {
92 // Newest problem first
ff1d4c3f
BA
93 return this.curProblems.sort((a,b) => a.added - b.added);
94 },
95 curProb: function() {
96 switch (this.display)
97 {
98 case "list":
99 return this.problems[this.curIdx];
100 case "myList":
101 return this.myProblems[this.curIdx];
102 }
da06a6eb 103 },
da06a6eb 104 },
298c42e6 105 created: function() {
ff1d4c3f
BA
106 if (location.hash.length > 0)
107 {
108 this.getOneProblem(location.hash.slice(1));
109 this.curIdx = 0; //TODO: a bit more subtle, depending if it's my problem or not (set display)
110 }
111 else
112 {
113 // Fetch most recent problems from server
114 this.fetchProblems("backward"); //TODO: backward in time from the future. Second argument?
115 }
298c42e6 116 },
da06a6eb 117 methods: {
ff1d4c3f
BA
118 setCurIndex: function(idx) {
119 this.curIdx = idx;
120 location.hash = "#" + idx;
121 },
247356cd
BA
122 translate: function(text) {
123 return translations[text];
124 },
ff1d4c3f
BA
125 curProblems: function() {
126 switch (this.display)
127 {
128 case "list":
129 return this.problems;
130 case "myList":
131 return this.myProblems;
132 }
81da2786 133 },
ff1d4c3f
BA
134 // TODO: dans tous les cas si on n'affiche qu'un seul problème,
135 // le curseur ne doit se déplacer que d'une unité.
136 showNext: function(direction) {
137 if (this.curIdx < 0)
138 this.fetchProblems(direction);
139 let curProbs = this.curProblems();
140 if ((this.curIdx > 0 && direction=="backward")
141 || (this.curIdx < curProbs.length-1 && direction=="forward"))
142 {
143 this.setCurIdx(this.curIdx + (direction=="forward" ? 1 : -1));
144 }
145 else //at boundary
146 {
147 const curSize = curProbs.length;
148 this.fetchProblems(direction);
149 if (curProbs.length
150 }
81da2786 151 else
ff1d4c3f
BA
152 this.setCurIndex(--this.curIdx);
153
154
81da2786
BA
155 if (this.curIdx == this.problems.length - 1)
156 this.fetchProblems("forward");
157 else
158 this.curIdx++;
ff1d4c3f
BA
159 location.hash = this.curIdx;
160 },
161 toggleListDisplay: function() {
162 this.display = (this.display == "list" ? "myList" : "list");
81da2786 163 },
ff1d4c3f 164 // TODO: modal "there are no more problems"
da06a6eb 165 fetchProblems: function(direction) {
ff1d4c3f 166 const problems = if ... this.problems ... ou this.myProblems;
7931e479 167 if (this.problems.length == 0)
ff1d4c3f 168 return; //what could we do?! -------> ask problems older than MAX_NUMBER + backward
7931e479
BA
169 // Search for newest date (or oldest)
170 let last_dt = this.problems[0].added;
171 for (let i=0; i<this.problems.length; i++)
172 {
173 if ((direction == "forward" && this.problems[i].added > last_dt) ||
174 (direction == "backward" && this.problems[i].added < last_dt))
175 {
176 last_dt = this.problems[i].added;
177 }
178 }
8d7e2786 179 ajax("/problems/" + variant.name, "GET", { //TODO: use variant._id ?
7931e479
BA
180 direction: direction,
181 last_dt: last_dt,
182 }, response => {
183 if (response.problems.length > 0)
81da2786
BA
184 {
185 this.problems = response.problems
186 .sort((p1,p2) => { return p1.added - p2.added; });
ff1d4c3f 187 this.setCurIndex(response.problems.length - 1);
81da2786 188 }
7931e479 189 });
da06a6eb 190 },
45109880
BA
191 previewNewProblem: function() {
192 if (!V.IsGoodFen(this.newProblem.fen))
e081ffe3 193 return alert(translations["Bad FEN description"]);
6b5517b4
BA
194 if (this.newProblem.instructions.trim().length == 0)
195 return alert(translations["Empty instructions"]);
196 if (this.newProblem.solution.trim().length == 0)
197 return alert(translations["Empty solution"]);
ff1d4c3f 198 this.modalProb.preview = true;
45109880 199 },
ff1d4c3f 200 // TODO: adjust for update too
45109880
BA
201 sendNewProblem: function() {
202 // Send it to the server and close modal
8d7e2786 203 ajax("/problems/" + variant.name, "POST", { //TODO: with variant._id ?
45109880
BA
204 fen: this.newProblem.fen,
205 instructions: this.newProblem.instructions,
206 solution: this.newProblem.solution,
7931e479 207 }, response => {
ff1d4c3f
BA
208 this.modalProb.added = Date.now();
209 this.curProblems().push(JSON.parse(JSON.stringify(this.modalProb)));
7931e479 210 document.getElementById("modal-newproblem").checked = false;
ff1d4c3f 211 this.modalProb.preview = false;
7931e479 212 });
da06a6eb 213 },
ff1d4c3f 214 // TODO: AJAX for problem deletion
da06a6eb 215 },
4ecf423b 216})