7c81e6782447e4e0dec7263f1f09755cfe215b6e
[vchess.git] / client / src / views / Problems.vue
1 <template lang="pug">
2 main
3 input#modalNewprob.modal(type="checkbox" @change="infoMsg=''")
4 div#newprobDiv(role="dialog" data-checkbox="modalNewprob")
5 .card(@keyup.enter="sendProblem()")
6 label#closeNewprob.modal-close(for="modalNewprob")
7 fieldset
8 label(for="selectVariant") {{ st.tr["Variant"] }}
9 select#selectVariant(
10 v-model="curproblem.vid"
11 @change="changeVariant(curproblem)"
12 )
13 option(
14 v-for="v in [emptyVar].concat(st.variants)"
15 :value="v.id"
16 :selected="curproblem.vid==v.id"
17 )
18 | {{ v.name }}
19 fieldset
20 input#inputFen(
21 type="text"
22 placeholder="FEN"
23 v-model="curproblem.fen"
24 @input="trySetDiagram(curproblem)"
25 )
26 #diagram(v-html="curproblem.diag")
27 fieldset
28 textarea(
29 :placeholder="st.tr['Instructions']"
30 v-model="curproblem.instruction"
31 )
32 p(v-html="parseHtml(curproblem.instruction)")
33 fieldset
34 textarea(
35 :placeholder="st.tr['Solution']"
36 v-model="curproblem.solution"
37 )
38 p(v-html="parseHtml(curproblem.solution)")
39 button(@click="sendProblem()") {{ st.tr["Send"] }}
40 #dialog.text-center {{ st.tr[infoMsg] }}
41 .row(v-if="showOne")
42 .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
43 #topPage
44 span {{ curproblem.vname }}
45 button.marginleft(@click="backToList()") {{ st.tr["Back to list"] }}
46 button.nomargin(
47 v-if="st.user.id == curproblem.uid"
48 @click="editProblem(curproblem)"
49 )
50 | {{ st.tr["Edit"] }}
51 button.nomargin(
52 v-if="st.user.id == curproblem.uid"
53 @click="deleteProblem(curproblem)"
54 )
55 | {{ st.tr["Delete"] }}
56 p.clickable(
57 v-html="curproblem.uname + ' : ' + parseHtml(curproblem.instruction)"
58 @click="curproblem.showSolution=!curproblem.showSolution"
59 )
60 | {{ st.tr["Show solution"] }}
61 p(
62 v-show="curproblem.showSolution"
63 v-html="parseHtml(curproblem.solution)"
64 )
65 .row(v-else)
66 .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
67 #controls
68 button#newProblem(onClick="doClick('modalNewprob')")
69 | {{ st.tr["New problem"] }}
70 label(for="checkboxMine") {{ st.tr["My problems"] }}
71 input#checkboxMine(
72 type="checkbox"
73 v-model="onlyMines"
74 )
75 label(for="selectVariant") {{ st.tr["Variant"] }}
76 select#selectVariant(v-model="selectedVar")
77 option(
78 v-for="v in [emptyVar].concat(st.variants)"
79 :value="v.id"
80 )
81 | {{ v.name }}
82 table
83 tr
84 th {{ st.tr["Variant"] }}
85 th {{ st.tr["Instructions"] }}
86 tr(
87 v-for="p in problems"
88 v-show="displayProblem(p)"
89 @click="setHrefPid(p)"
90 )
91 td {{ p.vname }}
92 td(v-html="p.instruction")
93 BaseGame(v-if="showOne" :game="game" :vr="vr")
94 </template>
95
96 <script>
97 import { store } from "@/store";
98 import { ajax } from "@/utils/ajax";
99 import { checkProblem } from "@/data/problemCheck";
100 import { getDiagram } from "@/utils/printDiagram";
101 import { processModalClick } from "@/utils/modalClick";
102 import { ArrayFun } from "@/utils/array";
103 import BaseGame from "@/components/BaseGame.vue";
104 export default {
105 name: "my-problems",
106 components: {
107 BaseGame,
108 },
109 data: function() {
110 return {
111 st: store.state,
112 emptyVar: {
113 vid: 0,
114 vname: "",
115 },
116 // Problem currently showed, or edited:
117 curproblem: {
118 id: 0, //used in case of edit
119 vid: 0,
120 fen: "",
121 diag: "",
122 instruction: "",
123 solution: "",
124 showSolution: false,
125 },
126 loadedVar: 0, //corresponding to loaded V
127 selectedVar: 0, //to filter problems based on variant
128 problems: [],
129 onlyMines: false,
130 showOne: false,
131 infoMsg: "",
132 vr: null, //"variant rules" object initialized from FEN
133 game: {
134 players:[{name:"Problem"},{name:"Problem"}],
135 mode: "analyze",
136 },
137 };
138 },
139 created: function() {
140 ajax("/problems", "GET", (res) => {
141 this.problems = res.problems;
142 if (this.st.variants.length > 0)
143 this.problems.forEach(p => this.setVname(p));
144 // Retrieve all problems' authors' names
145 let names = {};
146 this.problems.forEach(p => {
147 if (p.uid != this.st.user.id)
148 names[p.uid] = ""; //unknwon for now
149 else { console.log("assign " + this.st.user.name);
150 p.uname = this.st.user.name; console.log(p); console.log(this.problems); }
151 });
152 if (Object.keys(name).length > 0)
153 {
154 ajax("/users",
155 "GET",
156 { ids: Object.keys(names).join(",") },
157 res2 => {
158 res2.users.forEach(u => {names[u.id] = u.name});
159 this.problems.forEach(p => p.uname = names[p.uid]);
160 }
161 );
162 }
163 const pid = this.$route.query["id"];
164 if (!!pid)
165 this.showProblem(this.problems.find(p => p.id == pid));
166 });
167 },
168 mounted: function() {
169 document.getElementById("newprobDiv").addEventListener("click", processModalClick);
170 },
171 watch: {
172 // st.variants changes only once, at loading from [] to [...]
173 "st.variants": function(variantArray) {
174 // Set problems vname (either all are set or none)
175 if (this.problems.length > 0 && this.problems[0].vname == "")
176 this.problems.forEach(p => this.setVname(p));
177 },
178 "$route": function(to, from) { console.log("ddddd");
179 const pid = to.query["id"];
180 if (!!pid)
181 this.showProblem(this.problems.find(p => p.id == pid));
182 else
183 this.showOne = false
184 },
185 },
186 methods: {
187 setVname: function(prob) {
188 prob.vname = this.st.variants.find(v => v.id == prob.vid).name;
189 },
190 copyProblem: function(p1, p2) {
191 for (let key in p1)
192 p2[key] = p1[key];
193 },
194 setHrefPid: function(p) {
195 // Change href => $route changes, watcher notices, call showProblem
196 const curHref = document.location.href;
197 document.location.href = curHref.split("?")[0] + "?id=" + p.id;
198 },
199 backToList: function() {
200 // Change href => $route change, watcher notices, reset showOne to false
201 document.location.href = document.location.href.split("?")[0];
202 },
203 resetCurProb: function() {
204 this.curproblem.id = 0;
205 this.curproblem.uid = 0;
206 this.curproblem.vid = "";
207 this.curproblem.vname = "";
208 this.curproblem.fen = "";
209 this.curproblem.diag = "";
210 this.curproblem.instruction = "";
211 this.curproblem.solution = "";
212 this.curproblem.showSolution = false;
213 },
214 parseHtml: function(txt) {
215 return !txt.match(/<[/a-zA-Z]+>/)
216 ? txt.replace(/\n/g, "<br/>") //no HTML tag
217 : txt;
218 },
219 changeVariant: function(prob) {
220 this.setVname(prob);
221 this.loadVariant(
222 prob.vid,
223 () => {
224 // Set FEN if possible (might not be correct yet)
225 if (V.IsGoodFen(prob.fen))
226 this.setDiagram(prob);
227 }
228 );
229 },
230 loadVariant: async function(vid, cb) {
231 // Condition: vid is a valid variant ID
232 this.loadedVar = 0;
233 const variant = this.st.variants.find(v => v.id == vid);
234 const vModule = await import("@/variants/" + variant.name + ".js");
235 window.V = vModule.VariantRules;
236 this.loadedVar = vid;
237 cb();
238 },
239 trySetDiagram: function(prob) {
240 // Problem edit: FEN could be wrong or incomplete,
241 // variant could not be ready, or not defined
242 if (prob.vid > 0 && this.loadedVar == prob.vid && V.IsGoodFen(prob.fen))
243 this.setDiagram(prob);
244 },
245 setDiagram: function(prob) {
246 // Condition: prob.fen is correct and global V is ready
247 const parsedFen = V.ParseFen(prob.fen);
248 const args = {
249 position: parsedFen.position,
250 orientation: parsedFen.turn,
251 };
252 prob.diag = getDiagram(args);
253 },
254 displayProblem: function(p) {
255 return ((this.selectedVar == 0 || p.vid == this.selectedVar) &&
256 ((this.onlyMines && p.uid == this.st.user.id)
257 || (!this.onlyMines && p.uid != this.st.user.id)));
258 },
259 showProblem: function(p) {
260 this.loadVariant(
261 p.vid,
262 () => {
263 // The FEN is already checked at this stage:
264 this.vr = new V(p.fen);
265 this.game.vname = p.vname;
266 this.game.mycolor = this.vr.turn; //diagram orientation
267 this.game.fen = p.fen;
268 this.$set(this.game, "fenStart", p.fen);
269 this.copyProblem(p, this.curproblem);
270 this.showOne = true;
271 }
272 );
273 },
274 sendProblem: function() {
275 const error = checkProblem(this.curproblem);
276 if (!!error)
277 return alert(error);
278 const edit = this.curproblem.id > 0;
279 this.infoMsg = "Processing... Please wait";
280 ajax(
281 "/problems",
282 edit ? "PUT" : "POST",
283 {prob: this.curproblem},
284 (ret) => {
285 if (edit)
286 {
287 let editedP = this.problems.find(p => p.id == this.curproblem.id);
288 this.copyProblem(this.curproblem, editedP);
289 }
290 else //new problem
291 {
292 let newProblem = Object.assign({}, this.curproblem);
293 newProblem.id = ret.id;
294 newProblem.uid = this.st.user.id;
295 newProblem.uname = this.st.user.name;
296 this.problems = this.problems.concat(newProblem);
297 }
298 this.resetCurProb();
299 this.infoMsg = "";
300 }
301 );
302 },
303 editProblem: function(prob) {
304 if (!prob.diag)
305 this.setDiagram(prob); //possible because V is loaded at this stage
306 this.copyProblem(prob, this.curproblem);
307 doClick('modalNewprob');
308 },
309 deleteProblem: function(prob) {
310 if (confirm(this.st.tr["Are you sure?"]))
311 {
312 ajax("/problems", "DELETE", {id:prob.id}, () => {
313 ArrayFun.remove(this.problems, p => p.id == prob.id);
314 this.backToList();
315 });
316 }
317 },
318 },
319 };
320 </script>
321
322 <style lang="sass" scoped>
323 [type="checkbox"].modal+div .card
324 max-width: 767px
325 max-height: 100%
326 #inputFen
327 width: 100%
328 textarea
329 width: 100%
330 #diagram
331 margin: 0 auto
332 max-width: 400px
333 #controls
334 margin: 0
335 width: 100%
336 text-align: center
337 & > *
338 margin: 0
339 #topPage
340 span
341 font-weight: bold
342 margin: 0 auto
343 & > .nomargin
344 margin: 0
345 & > .marginleft
346 margin: 0 0 0 15px
347
348 </style>