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