ad8c54c014148bec9fa063cbd00bacd2328439c5
[vchess.git] / public / javascripts / components / problems.js
1 Vue.component('my-problems', {
2 data: function () {
3 return {
4 problems: [], //oldest first
5 curIdx: 0, //index in problems array
6 stage: "nothing", //or "preview" after new problem is filled
7 newProblem: {
8 fen: "",
9 instructions: "",
10 solution: "",
11 },
12 };
13 },
14 template: `
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">
17 <button :aria-label='translate("Load previous problem")' class="tooltip"
18 @click="showPreviousProblem()">
19 <i class="material-icons">skip_previous</i>
20 </button>
21 <button :aria-label='translate("Add a problem")' class="tooltip"
22 @click="showNewproblemModal">
23 {{ translate("New") }}
24 </button>
25 <button :aria-label='translate("Load next problem")' class="tooltip"
26 @click="showNextProblem()">
27 <i class="material-icons">skip_next</i>
28 </button>
29 </div>
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"
63 v-bind:prob="p" v-bind:preview="false" v-bind:key="idx">
64 </my-problem-summary>
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
93 <input type="checkbox" id="modal-newproblem" class="modal">
94 <div role="dialog" aria-labelledby="newProblemTxt">
95 <div v-show="stage=='nothing'" class="card newproblem-form">
96 <label for="modal-newproblem" class="modal-close"></label>
97 <h3 id="newProblemTxt">{{ translate("Add a problem") }}</h3>
98 <form @submit.prevent="previewNewProblem">
99 <fieldset>
100 <label for="newpbFen">FEN</label>
101 <input id="newpbFen" type="text" v-model="newProblem.fen"
102 :placeholder='translate("Full FEN description")'/>
103 </fieldset>
104 <fieldset>
105 <p class="emphasis">{{ translate("Safe HTML tags allowed") }}</p>
106 <label for="newpbInstructions">{{ translate("Instructions") }}</label>
107 <textarea id="newpbInstructions" v-model="newProblem.instructions"
108 :placeholder='translate("Describe the problem goal")'></textarea>
109 <label for="newpbSolution">{{ translate("Solution") }}</label>
110 <textarea id="newpbSolution" v-model="newProblem.solution"
111 :placeholder='translate("How to solve the problem?")'></textarea>
112 <button class="center-btn">{{ translate("Preview") }}</button>
113 </fieldset>
114 </form>
115 </div>
116 <div v-show="stage=='preview'" class="card newproblem-preview">
117 <label for="modal-newproblem" class="modal-close"></label>
118 <my-problem-preview v-bind:prob="newProblem"></my-problem-summary>
119 <div class="button-group">
120 <button @click="newProblem.stage='nothing'">{{ translate("Cancel") }}</button>
121 <button @click="sendNewProblem()">{{ translate("Send") }}</button>
122 </div>
123 </div>
124 </div>
125 </div>
126 `,
127 computed: {
128 sortedProblems: function() {
129 // Newest problem first
130 },
131 },
132 created: function() {
133 // Analyse URL: if a single problem required, show it. Otherwise,
134 // TODO: fetch most recent problems from server
135 },
136 methods: {
137 translate: function(text) {
138 return translations[text];
139 },
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 // },
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"
165 fetchProblems: function(direction) {
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 }
178 ajax("/problems/" + variant, "GET", {
179 direction: direction,
180 last_dt: last_dt,
181 }, response => {
182 if (response.problems.length > 0)
183 {
184 this.problems = response.problems
185 .sort((p1,p2) => { return p1.added - p2.added; });
186 this.curIdx = response.problems.length - 1;
187 }
188 });
189 },
190 showNewproblemModal: function() {
191 document.getElementById("modal-newproblem").checked = true;
192 },
193 previewNewProblem: function() {
194 if (!V.IsGoodFen(this.newProblem.fen))
195 return alert(translations["Bad FEN description"]);
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"]);
200 this.newProblem.stage = "preview";
201 },
202 sendNewProblem: function() {
203 // Send it to the server and close modal
204 ajax("/problems/" + variant, "POST", {
205 fen: this.newProblem.fen,
206 instructions: this.newProblem.instructions,
207 solution: this.newProblem.solution,
208 }, response => {
209 this.newProblem.added = Date.now();
210 this.problems.push(JSON.parse(JSON.stringify(this.newProblem)));
211 document.getElementById("modal-newproblem").checked = false;
212 this.newProblem.stage = "nothing";
213 });
214 },
215 },
216 })
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"