Update README
[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 },
4ecf423b 20 template: `
a5d56686
BA
21 <div class="col-sm-12 col-md-10 col-md-offset-1 col-lg-8 col-lg-offset-2">
22 <div id="problemControls" class="button-group">
ff1d4c3f 23 <button :aria-label='translate("Previous problem(s)")' class="tooltip" @click="showNext('backward')">
a5d56686
BA
24 <i class="material-icons">skip_previous</i>
25 </button>
ff1d4c3f 26 <button :aria-label='translate("Add a problem")' class="tooltip" onClick="doClick('modal-newproblem')">
247356cd 27 {{ translate("New") }}
a5d56686 28 </button>
ff1d4c3f 29 <button :aria-label='translate("Next problem(s)")' class="tooltip" @click="showNext('forward')">
a5d56686
BA
30 <i class="material-icons">skip_next</i>
31 </button>
32 </div>
ff1d4c3f
BA
33 <div id="mainBoard" v-show="curIdx>=0">
34 <div id="instructions-div" class="section-content">
8ef618ef
BA
35 <p id="problem-instructions">
36 {{ curProb.instructions }}
37 </p>
ff1d4c3f
BA
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>
8ef618ef
BA
44 <p id="problem-solution" v-show="showSolution">
45 {{ curProb.solution }}
46 </p>
ff1d4c3f
BA
47 </div>
48 </div>
8ef618ef
BA
49 <button v-if="!!userId" @click="toggleListDisplay()">
50 <span>My problems (only)</span>
51 </button>
ff1d4c3f
BA
52 <my-problem-summary v-show="curIdx<0"
53 v-for="(p,idx) in sortedProblems" @click="setCurIdx(idx)"
8ef618ef 54 v-bind:prob="p" v-bind:userid="userId" v-bind:key="p.id">
da06a6eb 55 </my-problem-summary>
8ef618ef 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">
8ef618ef
BA
59 <label for="modal-newproblem" class="modal-close">
60 </label>
61 <h3 id="modalProblemTxt">
62 {{ translate("Add a problem") }}
63 </h3>
64 <form @submit.prevent="previewProblem()">
da06a6eb 65 <fieldset>
247356cd 66 <label for="newpbFen">FEN</label>
ff1d4c3f 67 <input id="newpbFen" type="text" v-model="modalProb.fen"
e081ffe3 68 :placeholder='translate("Full FEN description")'/>
da06a6eb
BA
69 </fieldset>
70 <fieldset>
8ef618ef
BA
71 <p class="emphasis">
72 {{ translate("Safe HTML tags allowed") }}
73 </p>
74 <label for="newpbInstructions">
75 {{ translate("Instructions") }}
76 </label>
ff1d4c3f 77 <textarea id="newpbInstructions" v-model="modalProb.instructions"
8ef618ef
BA
78 :placeholder='translate("Describe the problem goal")'>
79 </textarea>
80 <label for="newpbSolution">
81 {{ translate("Solution") }}
82 </label>
ff1d4c3f 83 <textarea id="newpbSolution" v-model="modalProb.solution"
8ef618ef
BA
84 :placeholder='translate("How to solve the problem?")'>
85 </textarea>
86 <button class="center-btn">
87 {{ translate("Preview") }}
88 </button>
da06a6eb 89 </fieldset>
da06a6eb
BA
90 </form>
91 </div>
ff1d4c3f 92 <div v-show="modalProb.preview" class="card newproblem-preview">
8ef618ef
BA
93 <label for="modal-newproblem" class="modal-close">
94 </label>
95 <my-problem-summary v-bind:prob="modalProb" v-bind:userid="userId">
96 </my-problem-summary>
a5d56686 97 <div class="button-group">
8ef618ef
BA
98 <button @click="modalProb.preview=false">
99 {{ translate("Cancel") }}
100 </button>
101 <button @click="sendProblem()">
102 {{ translate("Send") }}
103 </button>
b5fb8e69 104 </div>
45109880 105 </div>
da06a6eb 106 </div>
4ecf423b
BA
107 </div>
108 `,
da06a6eb
BA
109 computed: {
110 sortedProblems: function() {
111 // Newest problem first
ff1d4c3f
BA
112 return this.curProblems.sort((a,b) => a.added - b.added);
113 },
114 curProb: function() {
115 switch (this.display)
116 {
117 case "list":
118 return this.problems[this.curIdx];
119 case "myList":
120 return this.myProblems[this.curIdx];
121 }
da06a6eb 122 },
da06a6eb 123 },
298c42e6 124 created: function() {
ff1d4c3f
BA
125 if (location.hash.length > 0)
126 {
8ef618ef 127 this.getOneProblem(location.hash.slice(1)); //callback?
ff1d4c3f
BA
128 this.curIdx = 0; //TODO: a bit more subtle, depending if it's my problem or not (set display)
129 }
130 else
131 {
132 // Fetch most recent problems from server
133 this.fetchProblems("backward"); //TODO: backward in time from the future. Second argument?
134 }
298c42e6 135 },
da06a6eb 136 methods: {
ff1d4c3f
BA
137 setCurIndex: function(idx) {
138 this.curIdx = idx;
139 location.hash = "#" + idx;
140 },
247356cd
BA
141 translate: function(text) {
142 return translations[text];
143 },
ff1d4c3f
BA
144 curProblems: function() {
145 switch (this.display)
146 {
147 case "list":
148 return this.problems;
149 case "myList":
150 return this.myProblems;
151 }
81da2786 152 },
8ef618ef 153 // TODO?: get 50 from server but only show 10 at a time (for example)
ff1d4c3f
BA
154 showNext: function(direction) {
155 if (this.curIdx < 0)
8ef618ef
BA
156 return this.fetchProblems(direction);
157 // Show next problem (older or newer):
ff1d4c3f
BA
158 let curProbs = this.curProblems();
159 if ((this.curIdx > 0 && direction=="backward")
160 || (this.curIdx < curProbs.length-1 && direction=="forward"))
161 {
162 this.setCurIdx(this.curIdx + (direction=="forward" ? 1 : -1));
163 }
164 else //at boundary
165 {
166 const curSize = curProbs.length;
167 this.fetchProblems(direction);
8ef618ef
BA
168 const newSize = curProbs.length;
169 if (curSize == newSize) //no problems found
170 return;
171 switch (direction)
172 {
173 case "forward":
174 this.setCurIdx(this.curIdx+1);
175 break;
176 case "backward":
177 this.setCurIdx(newSize - curSize + this.curIdx-1);
178 break;
179 }
ff1d4c3f 180 }
ff1d4c3f
BA
181 },
182 toggleListDisplay: function() {
183 this.display = (this.display == "list" ? "myList" : "list");
81da2786 184 },
ff1d4c3f 185 // TODO: modal "there are no more problems"
da06a6eb 186 fetchProblems: function(direction) {
ff1d4c3f 187 const problems = if ... this.problems ... ou this.myProblems;
7931e479 188 if (this.problems.length == 0)
ff1d4c3f 189 return; //what could we do?! -------> ask problems older than MAX_NUMBER + backward
7931e479
BA
190 // Search for newest date (or oldest)
191 let last_dt = this.problems[0].added;
192 for (let i=0; i<this.problems.length; i++)
193 {
194 if ((direction == "forward" && this.problems[i].added > last_dt) ||
195 (direction == "backward" && this.problems[i].added < last_dt))
196 {
197 last_dt = this.problems[i].added;
198 }
199 }
8d7e2786 200 ajax("/problems/" + variant.name, "GET", { //TODO: use variant._id ?
7931e479
BA
201 direction: direction,
202 last_dt: last_dt,
203 }, response => {
204 if (response.problems.length > 0)
81da2786
BA
205 {
206 this.problems = response.problems
207 .sort((p1,p2) => { return p1.added - p2.added; });
ff1d4c3f 208 this.setCurIndex(response.problems.length - 1);
81da2786 209 }
7931e479 210 });
da06a6eb 211 },
8ef618ef 212 previewProblem: function() {
45109880 213 if (!V.IsGoodFen(this.newProblem.fen))
e081ffe3 214 return alert(translations["Bad FEN description"]);
6b5517b4
BA
215 if (this.newProblem.instructions.trim().length == 0)
216 return alert(translations["Empty instructions"]);
217 if (this.newProblem.solution.trim().length == 0)
218 return alert(translations["Empty solution"]);
ff1d4c3f 219 this.modalProb.preview = true;
45109880 220 },
8ef618ef 221 sendProblem: function() {
45109880 222 // Send it to the server and close modal
8ef618ef
BA
223 ajax(
224 "/problems/" + variant.name, //TODO: with variant.id ?
225 (this.modalProb.id > 0 ? "PUT" : "POST"),
226 this.modalProb,
227 response => {
228 document.getElementById("modal-newproblem").checked = false;
229 if (this.modalProb.id == 0)
230 {
231 this.modalProb.added = Date.now();
232 this.modalProb.preview = false;
233 this.curProblems().push(JSON.parse(JSON.stringify(this.modalProb)));
234 }
235 else
236 this.modalProb.id = 0;
237 }
238 );
239 },
240 // TODO: catch signal edit or delete ; on edit: modify modalProb and show modal
241 deleteProblem: function(pid) {
242 // TODO: AJAX call
243 // TODO: delete problem in curProblems() list
da06a6eb
BA
244 },
245 },
4ecf423b 246})