Vue.component('my-problems', {
- props: ["queryHash","settings"],
+ props: ["probId","settings"],
data: function () {
return {
userId: user.id,
curProb: null, //(reference to) current displayed problem (if any)
showSolution: false,
nomoreMessage: "",
+ mode: "analyze", //for game component
pbNum: 0, //to navigate directly to some problem
// New problem (to upload), or existing problem to edit:
modalProb: {
},
};
},
+ // NOTE: always modals first, because otherwise "scroll to the end" undesirable effect
template: `
<div class="col-sm-12 col-md-10 col-md-offset-1 col-lg-8 col-lg-offset-2">
- <div id="problemControls" class="button-group">
- <button :aria-label='translate("Previous problem(s)")' class="tooltip"
- @click="showNext('backward')"
- >
- <i class="material-icons">skip_previous</i>
- </button>
- <button v-if="!!userId" :aria-label='translate("Add a problem")'
- class="tooltip" onClick="doClick('modal-newproblem')"
- >
- {{ translate("New") }}
- </button>
- <button :aria-label='translate("Next problem(s)")' class="tooltip"
- @click="showNext('forward')"
- >
- <i class="material-icons">skip_next</i>
- </button>
- </div>
- <div id="mainBoard" v-if="!!curProb">
- <div id="instructions-div" class="section-content">
- <p id="problem-instructions">{{ curProb.instructions }}</p>
- </div>
- <my-game :fen="curProb.fen" :mode="analyze" :allowMovelist="true"
- :settings="settings"
- >
- </my-game>
- <div id="solution-div" class="section-content">
- <h3 class="clickable" @click="showSolution = !showSolution">
- {{ translations["Show solution"] }}
- </h3>
- <p id="problem-solution" v-show="showSolution">{{ curProb.solution }}</p>
- </div>
- <button @click="displayList">Back to list display</button>
- </div>
- <div>
- <input type="text" placeholder="Type problem number" v-model="pbNum"/>
- <button @click="showProblem">Show problem</button>
- </div>
- <button v-if="!!userId" @click="toggleListDisplay"
- :class="{'only-mine':display=='mine'}"
- >
- My problems (only)
- </button>
- <my-problem-summary v-show="!curProb"
- v-on:edit-problem="editProblem(p)" v-on:delete-problem="deleteProblem(p.id)"
- v-on:show-problem="() => showProblem(p.id)"
- v-for="p in curProblems()" @click="curProb=p"
- v-bind:prob="p" v-bind:userid="userId" v-bind:key="p.id">
- </my-problem-summary>
<input type="checkbox" id="modal-newproblem" class="modal"/>
<div role="dialog" aria-labelledby="modalProblemTxt">
<div v-show="!modalProb.preview" class="card newproblem-form">
<h3 id="nomoreMessage" class="section">{{ nomoreMessage }}</h3>
</div>
</div>
+ <div id="problemControls" class="button-group">
+ <button :aria-label='translate("Previous problem(s)")' class="tooltip"
+ @click="showNext('backward')"
+ >
+ <i class="material-icons">skip_previous</i>
+ </button>
+ <button v-if="!!userId" :aria-label='translate("Add a problem")'
+ class="tooltip" onClick="doClick('modal-newproblem')"
+ >
+ {{ translate("New") }}
+ </button>
+ <button :aria-label='translate("Next problem(s)")' class="tooltip"
+ @click="showNext('forward')"
+ >
+ <i class="material-icons">skip_next</i>
+ </button>
+ </div>
+ <div id="mainBoard" v-if="!!curProb">
+ <div id="instructions-div" class="section-content">
+ <p id="problem-instructions">{{ curProb.instructions }}</p>
+ </div>
+ <my-game :fen="curProb.fen" :mode="mode" :allowMovelist="true"
+ :settings="settings">
+ </my-game>
+ <div id="solution-div" class="section-content">
+ <h3 class="clickable" @click="showSolution = !showSolution">
+ {{ translate("Show solution") }}
+ </h3>
+ <p id="problem-solution" v-show="showSolution">{{ curProb.solution }}</p>
+ </div>
+ <button @click="displayList">Back to list display</button>
+ </div>
+ <div>
+ <input type="text" placeholder="Type problem number" v-model="pbNum"/>
+ <button @click="() => showProblem(pbNum)">Show problem</button>
+ </div>
+ <button v-if="!!userId" @click="toggleListDisplay"
+ :class="{'only-mine':display=='mine'}"
+ >
+ My problems (only)
+ </button>
+ <my-problem-summary v-show="!curProb"
+ v-on:edit-problem="editProblem(p)" v-on:delete-problem="deleteProblem(p.id)"
+ v-on:show-problem="() => showProblem(p.id)"
+ v-for="p in curProblems()" @click="curProb=p"
+ v-bind:prob="p" v-bind:userid="userId" v-bind:key="p.id">
+ </my-problem-summary>
</div>
`,
watch: {
- queryHash: function(newQhash) {
- if (!!newQhash)
- {
- // New query hash = "id=42"; get 42 as problem ID
- const pid = parseInt(newQhash.substr(2));
- this.showProblem(pid);
- }
- else
- this.curProb = null; //(back to) list display
+ probId: function() {
+ this.showProblem(this.probId);
},
},
created: function() {
- if (!!this.queryHash)
- {
- const pid = parseInt(this.queryHash.substr(2));
- this.showProblem(pid);
- }
+ if (!!this.probId)
+ this.showProblem(this.probId);
else
this.firstFetch();
},
methods: {
+ translate: translate,
firstFetch: function() {
// Fetch most recent problems from server, for both lists
this.fetchProblems("others", "bacwkard");
this.fetchProblems("mine", "bacwkard");
this.listsInitialized = true;
},
- showProblem: function(num) {
- const pid = num || this.pbNum;
+ showProblem: function(pid) {
location.hash = "#problems?id=" + pid;
for (let parray of [this.singletons,this.problems,this.myProblems])
{
const pIdx = parray.findIndex(p => p.id == pid);
if (pIdx >= 0)
{
- curProb = parray[pIdx];
+ this.curProb = parray[pIdx];
break;
}
}
- if (!curProb)
+ if (!this.curProb)
{
// Cannot find problem in current set; get from server, and add to singletons.
ajax(
- "/problems/" + variant.name + "/" + pid, //TODO: use variant._id ?
+ "/problems/" + variant.id + "/" + pid, //TODO: variant ID should not be required
"GET",
response => {
if (!!response.problem)
{
this.singletons.push(response.problem);
this.curProb = response.problem;
+ this.display = (response.problem.uid == this.userId ? "mine" : "others");
}
else
this.noMoreProblems("Sorry, problem " + pid + " does not exist");
}
);
}
- },
- translate: function(text) {
- return translations[text];
+ else
+ this.display = (this.curProb.uid == this.userId ? "mine" : "others");
},
curProblems: function() {
switch (this.display)
}
// Boundary case: nothing in current set, need to fetch from server
const curSize = curProbs.length;
- this.fetchProblems(this.display, direction);
- const newSize = curProbs.length;
- if (curSize == newSize) //no problems found
- return this.noMoreProblems("No more problems in this direction");
- // Ok, found something:
- this.curProb = this.findClosestNeighbor(this.curProb, curProbs, direction);
+ this.fetchProblems(this.display, direction, () => {
+ // Ok, found something:
+ this.curProb = this.findClosestNeighbor(this.curProb, curProbs, direction);
+ });
},
findClosestNeighbor: function(problem, probList, direction) {
let neighbor = undefined;
const curIndex = displays.findIndex(item => item == this.display);
this.display = displays[1-curIndex];
},
- fetchProblems: function(type, direction) {
+ fetchProblems: function(type, direction, cb) {
let problems = (type == "others" ? this.problems : this.myProblems);
// "last datetime" set at a value OK for an empty initial array
let last_dt = (direction=="forward" ? 0 : Number.MAX_SAFE_INTEGER);
last_dt: last_dt,
},
response => {
- if (response.problems.length > 0)
- {
- Array.prototype.push.apply(problems,
- response.problems.sort((p1,p2) => { return p2.added - p1.added; }));
- // If one list is empty but not the other, show the non-empty
- const otherArray = (type == "mine" ? this.problems : this.myProblems);
- if (problems.length > 0 && otherArray.length == 0)
- this.display = type;
- }
+ if (response.problems.length == 0)
+ return this.noMoreProblems("No more problems in this direction");
+ Array.prototype.push.apply(problems,
+ response.problems.sort((p1,p2) => { return p2.added - p1.added; }));
+ // If one list is empty but not the other, show the non-empty
+ const otherArray = (type == "mine" ? this.problems : this.myProblems);
+ if (problems.length > 0 && otherArray.length == 0)
+ this.display = type;
+ if (!!cb)
+ cb();
+ else
+ this.$forceUpdate(); //TODO...
}
);
},