Save current state (unmerged, broken, not working...)
[vchess.git] / public / javascripts / components / problems.js
CommitLineData
4ecf423b 1Vue.component('my-problems', {
da06a6eb
BA
2 data: function () {
3 return {
81da2786
BA
4 problems: [], //oldest first
5 curIdx: 0, //index in problems array
6 stage: "nothing", //or "preview" after new problem is filled
45109880 7 newProblem: {
77fa6d1f 8 fen: "",
45109880
BA
9 instructions: "",
10 solution: "",
45109880 11 },
da06a6eb
BA
12 };
13 },
4ecf423b 14 template: `
a5d56686
BA
15 <div class="col-sm-12 col-md-10 col-md-offset-1 col-lg-8 col-lg-offset-2">
16 <div id="problemControls" class="button-group">
81da2786
BA
17 <button :aria-label='translate("Load previous problem")' class="tooltip"
18 @click="showPreviousProblem()">
a5d56686
BA
19 <i class="material-icons">skip_previous</i>
20 </button>
247356cd 21 <button :aria-label='translate("Add a problem")' class="tooltip"
a5d56686 22 @click="showNewproblemModal">
247356cd 23 {{ translate("New") }}
a5d56686 24 </button>
81da2786
BA
25 <button :aria-label='translate("Load next problem")' class="tooltip"
26 @click="showNextProblem()">
a5d56686
BA
27 <i class="material-icons">skip_next</i>
28 </button>
29 </div>
81da2786
BA
30
31
32
33 if (this.mode == "problem")
34 {
35 // Show problem instructions
36 elementArray.push(
37 h('div',
38 {
39 attrs: { id: "instructions-div" },
40 "class": {
41 "clearer": true,
42 "section-content": true,
43 },
44 },
45 [
46 h('p',
47 {
48 attrs: { id: "problem-instructions" },
49 domProps: { innerHTML: this.problem.instructions }
50 }
51 )
52 ]
53 )
54 );
55 }
56
57
58 // TODO ici :: instrus + diag interactif + solution
59 my-board + pilotage via movesList + VariantRules !
60
61 <my-problem-preview v-show="stage=='preview'"
62 v-for="(p,idx) in problems"
b5fb8e69 63 v-bind:prob="p" v-bind:preview="false" v-bind:key="idx">
da06a6eb 64 </my-problem-summary>
81da2786
BA
65 if (this.mode == "problem")
66 {
67 // Show problem solution (on click)
68 elementArray.push(
69 h('div',
70 {
71 attrs: { id: "solution-div" },
72 "class": { "section-content": true },
73 },
74 [
75 h('h3',
76 {
77 "class": { clickable: true },
78 domProps: { innerHTML: translations["Show solution"] },
79 on: { click: this.toggleShowSolution },
80 }
81 ),
82 h('p',
83 {
84 attrs: { id: "problem-solution" },
85 domProps: { innerHTML: this.problem.solution }
86 }
87 )
88 ]
89 )
90 );
91 }
92
da06a6eb
BA
93 <input type="checkbox" id="modal-newproblem" class="modal">
94 <div role="dialog" aria-labelledby="newProblemTxt">
81da2786 95 <div v-show="stage=='nothing'" class="card newproblem-form">
da06a6eb 96 <label for="modal-newproblem" class="modal-close"></label>
247356cd 97 <h3 id="newProblemTxt">{{ translate("Add a problem") }}</h3>
45109880 98 <form @submit.prevent="previewNewProblem">
da06a6eb 99 <fieldset>
247356cd 100 <label for="newpbFen">FEN</label>
77fa6d1f 101 <input id="newpbFen" type="text" v-model="newProblem.fen"
e081ffe3 102 :placeholder='translate("Full FEN description")'/>
da06a6eb
BA
103 </fieldset>
104 <fieldset>
247356cd
BA
105 <p class="emphasis">{{ translate("Safe HTML tags allowed") }}</p>
106 <label for="newpbInstructions">{{ translate("Instructions") }}</label>
45109880 107 <textarea id="newpbInstructions" v-model="newProblem.instructions"
d289b043 108 :placeholder='translate("Describe the problem goal")'></textarea>
247356cd 109 <label for="newpbSolution">{{ translate("Solution") }}</label>
45109880 110 <textarea id="newpbSolution" v-model="newProblem.solution"
d289b043 111 :placeholder='translate("How to solve the problem?")'></textarea>
247356cd 112 <button class="center-btn">{{ translate("Preview") }}</button>
da06a6eb 113 </fieldset>
da06a6eb
BA
114 </form>
115 </div>
81da2786 116 <div v-show="stage=='preview'" class="card newproblem-preview">
b5fb8e69 117 <label for="modal-newproblem" class="modal-close"></label>
81da2786 118 <my-problem-preview v-bind:prob="newProblem"></my-problem-summary>
a5d56686 119 <div class="button-group">
247356cd
BA
120 <button @click="newProblem.stage='nothing'">{{ translate("Cancel") }}</button>
121 <button @click="sendNewProblem()">{{ translate("Send") }}</button>
b5fb8e69 122 </div>
45109880 123 </div>
da06a6eb 124 </div>
4ecf423b
BA
125 </div>
126 `,
da06a6eb
BA
127 computed: {
128 sortedProblems: function() {
129 // Newest problem first
da06a6eb 130 },
da06a6eb 131 },
298c42e6 132 created: function() {
81da2786 133 // Analyse URL: if a single problem required, show it. Otherwise,
298c42e6
BA
134 // TODO: fetch most recent problems from server
135 },
da06a6eb 136 methods: {
247356cd
BA
137 translate: function(text) {
138 return translations[text];
139 },
298c42e6
BA
140 // TODO: obsolete:
141// // Propagate "show problem" event to parent component (my-variant)
142// bubbleUp: function(problem) {
143// this.$emit('show-problem', JSON.stringify(problem));
144// },
81da2786
BA
145 toggleShowSolution: function() {
146 let problemSolution = document.getElementById("problem-solution");
147 problemSolution.style.display =
148 !problemSolution.style.display || problemSolution.style.display == "none"
149 ? "block"
150 : "none";
151 },
152 showPreviousProblem: function() {
153 if (this.curIdx == 0)
154 this.fetchProblems("backward");
155 else
156 this.curIdx--;
157 },
158 showNextProblem: function() {
159 if (this.curIdx == this.problems.length - 1)
160 this.fetchProblems("forward");
161 else
162 this.curIdx++;
163 },
164 // TODO: modal "no more problems"
da06a6eb 165 fetchProblems: function(direction) {
7931e479
BA
166 if (this.problems.length == 0)
167 return; //what could we do?!
168 // Search for newest date (or oldest)
169 let last_dt = this.problems[0].added;
170 for (let i=0; i<this.problems.length; i++)
171 {
172 if ((direction == "forward" && this.problems[i].added > last_dt) ||
173 (direction == "backward" && this.problems[i].added < last_dt))
174 {
175 last_dt = this.problems[i].added;
176 }
177 }
8d7e2786 178 ajax("/problems/" + variant.name, "GET", { //TODO: use variant._id ?
7931e479
BA
179 direction: direction,
180 last_dt: last_dt,
181 }, response => {
182 if (response.problems.length > 0)
81da2786
BA
183 {
184 this.problems = response.problems
185 .sort((p1,p2) => { return p1.added - p2.added; });
186 this.curIdx = response.problems.length - 1;
187 }
7931e479 188 });
da06a6eb
BA
189 },
190 showNewproblemModal: function() {
191 document.getElementById("modal-newproblem").checked = true;
192 },
45109880
BA
193 previewNewProblem: function() {
194 if (!V.IsGoodFen(this.newProblem.fen))
e081ffe3 195 return alert(translations["Bad FEN description"]);
6b5517b4
BA
196 if (this.newProblem.instructions.trim().length == 0)
197 return alert(translations["Empty instructions"]);
198 if (this.newProblem.solution.trim().length == 0)
199 return alert(translations["Empty solution"]);
45109880
BA
200 this.newProblem.stage = "preview";
201 },
202 sendNewProblem: function() {
203 // Send it to the server and close modal
8d7e2786 204 ajax("/problems/" + variant.name, "POST", { //TODO: with variant._id ?
45109880
BA
205 fen: this.newProblem.fen,
206 instructions: this.newProblem.instructions,
207 solution: this.newProblem.solution,
7931e479 208 }, response => {
b5fb8e69
BA
209 this.newProblem.added = Date.now();
210 this.problems.push(JSON.parse(JSON.stringify(this.newProblem)));
7931e479 211 document.getElementById("modal-newproblem").checked = false;
45109880 212 this.newProblem.stage = "nothing";
7931e479 213 });
da06a6eb
BA
214 },
215 },
4ecf423b 216})
b6487fb9
BA
217
218// TODO:
219// possibilité de supprimer / éditer si peer ID reconnu comme celui du probleme (champ "uploader")
220// --> côté serveur on vérifie un certain "secret"
221// --> filtre possible "mes problèmes"