if (!boardSize)
{
boardSize = (window.innerWidth >= 768
- ? Math.min(600, 0.5*window.innerWidth) //heuristic...
+ ? 0.75 * Math.min(window.innerWidth, window.innerHeight)
: window.innerWidth);
}
const movesWidth = (window.innerWidth >= 768 ? 280 : 0);
let maxAdded = 0
let augmentedChalls = this.challenges.map(c => {
let priority = 0;
- if (c.to == this.st.user.name)
+ if (!!c.to && c.to == this.st.user.name)
priority = 1;
else if (c.from.sid == this.st.user.sid || c.from.id == this.st.user.id)
priority = 2;
form(@submit.prevent="onSubmit()" @keyup.enter="onSubmit()")
div(v-show="stage!='Login'")
fieldset
- label(for="username") {{ st.tr["Name"] }}
+ label(for="username") {{ st.tr["User name"] }}
input#username(type="text" v-model="st.user.name")
fieldset
label(for="useremail") {{ st.tr["Email"] }}
input#notifyNew(type="checkbox" v-model="st.user.notify")
div(v-show="stage=='Login'")
fieldset
- label(for="nameOrEmail") {{ st.tr["Name or Email"] }}
+ label(for="nameOrEmail") {{ st.tr["User name or Email"] }}
input#nameOrEmail(type="text" v-model="nameOrEmail")
.button-group
button(@click="onSubmit()")
"Mutual agreement": "Mutual agreement",
"My games": "My games",
"My problems": "My problems",
- "Name": "Name",
"Name or Email": "Name or Email",
"New connexion detected: tab now offline": "New connexion detected: tab now offline",
"New correspondance game:": "New correspondance game:",
"No subject. Send anyway?": "No subject. Send anyway?",
"None": "None",
"Notifications by email": "Notifications by email",
+ "Number": "Number",
"Observe": "Observe",
"Offer draw?": "Offer draw?",
"Opponent action": "Opponent action",
"To": "To",
"Unknown": "Unknown",
"Update": "Update",
+ "User name": "User name",
"Variant": "Variant",
"Variants": "Variants",
"Versus": "Versus",
"Mutual agreement": "Acuerdo mutuo",
"My games": "Mis partidas",
"My problems": "Mis problemas",
- "Name": "Nombre",
"Name or Email": "Nombre o Email",
"New connexion detected: tab now offline": "Nueva conexión detectada: pestaña ahora desconectada",
"New correspondance game:": "Nueva partida por correspondencia:",
"No subject. Send anyway?": "Sin asunto. ¿Enviar sin embargo?",
"None": "Ninguno",
"Notifications by email": "Notificaciones por email",
+ "Number": "Número",
"Offer draw?": "¿Ofrecer tablas?",
"Observe": "Observar",
"Opponent action": "Acción del adversario",
"To": "A",
"Unknown": "Desconocido",
"Update": "Actualización",
+ "User name": "Nombre de usuario",
"Variant": "Variante",
"Variants": "Variantes",
"Versus": "Contra",
"Mutual agreement": "Accord mutuel",
"My games": "Mes parties",
"My problems": "Mes problèmes",
- "Name": "Nom",
"Name or Email": "Nom ou Email",
"New connexion detected: tab now offline": "Nouvelle connexion détectée : onglet désormais hors ligne",
"New correspondance game:": "Nouvelle partie par corespondance :",
"No subject. Send anyway?": "Pas de sujet. Envoyer quand-même ??",
"None": "Aucun",
"Notifications by email": "Notifications par email",
+ "Number": "Numéro",
"Offer draw?": "Proposer nulle ?",
"Observe": "Observer",
"Opponent action": "Action de l'adversaire",
"To": "À",
"Unknown": "Inconnu",
"Update": "Mise à jour",
+ "User name": "Nom d'utilisateur",
"Variant": "Variante",
"Variants": "Variantes",
"Versus": "Contre",
:newChat="newChat" @mychat="processChat")
.row
#aboveBoard.col-sm-12.col-md-9.col-md-offset-3.col-lg-10.col-lg-offset-2
- span.variant-info {{ game.vname }}
+ span.variant-cadence {{ game.cadence }}
+ span.variant-name {{ game.vname }}
button#chatBtn(onClick="doClick('modalChat')") Chat
#actions(v-if="game.score=='*'")
button(@click="clickDraw()" :class="{['draw-' + drawOffer]: true}")
case "newchat":
this.newChat = data.data;
if (!document.getElementById("modalChat").checked)
- document.getElementById("chatBtn").style.backgroundColor = "#c5fefe";
+ document.getElementById("chatBtn").classList.add("somethingnew");
break;
}
},
},
resetChatColor: function() {
// TODO: this is called twice, once on opening an once on closing
- document.getElementById("chatBtn").style.backgroundColor = "#e2e2e2";
+ document.getElementById("chatBtn").classList.remove("somethingnew");
},
processChat: function(chat) {
this.send("newchat", {data:chat});
#aboveBoard
margin-left: 30%
-.variant-info
+.variant-cadence
+ padding-right: 10px
+
+.variant-name
font-weight: bold
padding-right: 10px
.draw-threerep, .draw-threerep:hover
background-color: #e4d1fc
+
+.somethingnew
+ background-color: #c5fefe
</style>
button(onClick="doClick('modalNewgame')") {{ st.tr["New game"] }}
.row
.col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
- div
+ div#div2
.button-group
- button#btnClive(@click="setDisplay('c','live',$event)" class="active")
+ button.tabbtn#btnClive(@click="setDisplay('c','live',$event)")
| {{ st.tr["Live challenges"] }}
- button#btnCcorr(@click="setDisplay('c','corr',$event)")
+ button.tabbtn#btnCcorr(@click="setDisplay('c','corr',$event)")
| {{ st.tr["Correspondance challenges"] }}
ChallengeList(v-show="cdisplay=='live'"
:challenges="filterChallenges('live')" @click-challenge="clickChallenge")
ChallengeList(v-show="cdisplay=='corr'"
:challenges="filterChallenges('corr')" @click-challenge="clickChallenge")
- div
+ div#div3
.button-group
- button#btnGlive(@click="setDisplay('g','live',$event)" class="active")
+ button.tabbtn#btnGlive(@click="setDisplay('g','live',$event)")
| {{ st.tr["Live games"] }}
- button#btnGcorr(@click="setDisplay('g','corr',$event)")
+ button.tabbtn#btnGcorr(@click="setDisplay('g','corr',$event)")
| {{ st.tr["Correspondance games"] }}
GameList(v-show="gdisplay=='live'" :games="filterGames('live')"
:showBoth="true" @show-game="showGame")
"GET",
{uid: this.st.user.id, excluded: true},
response => {
- // Show corr tab with timeout, to let enough time for (socket) polling
- setTimeout(
- () => {
- if (response.games.length > 0 &&
- this.games.length == response.games.length)
- {
- this.setDisplay('g', "corr");
- }
- },
- 1000
- );
this.games = this.games.concat(response.games.map(g => {
const type = this.classifyObject(g);
const vname = this.getVname(g.vid);
"GET",
{uid: this.st.user.id},
response => {
- setTimeout(
- () => {
- if (response.challenges.length > 0 &&
- this.challenges.length == response.challenges.length)
- {
- this.setDisplay('c', "corr");
- }
- },
- 1000
- );
// Gather all senders names, and then retrieve full identity:
// (TODO [perf]: some might be online...)
let names = {};
() => { this.newchallenge.cadence = b.innerHTML; }
)}
);
+ const showCtype = localStorage.getItem("type-challenges") || "live";
+ const showGtype = localStorage.getItem("type-games") || "live";
+ this.setDisplay('c', showCtype);
+ this.setDisplay('g', showGtype);
},
beforeDestroy: function() {
this.send("disconnect");
},
setDisplay: function(letter, type, e) {
this[letter + "display"] = type;
+ localStorage.setItem("type-" + (letter == 'c' ? "challenges" : "games"), type);
let elt = !!e
? e.target
: document.getElementById("btn" + letter.toUpperCase() + type);
- // WARNING: this method is called at created in a setTimeout:
- // => the page could have changed and element no longer defined.
- if (!elt)
- return;
elt.classList.add("active");
+ elt.classList.remove("somethingnew"); //in case of
if (!!elt.previousElementSibling)
elt.previousElementSibling.classList.remove("active");
else
},
resetChatColor: function() {
// TODO: this is called twice, once on opening an once on closing
- document.getElementById("peopleBtn").style.backgroundColor = "#e2e2e2";
+ document.getElementById("peopleBtn").classList.remove("somethingnew");
},
processChat: function(chat) {
this.send("newchat", {data:chat});
newChall.from = Object.assign({sid:chall.from}, fromValues);
newChall.vname = this.getVname(newChall.vid);
this.challenges.push(newChall);
- // Adjust visual:
- if (newChall.type == "live" && this.cdisplay == "corr" && !this.challenges.some(c => c.type == "corr"))
- this.setDisplay('c', "live");
- else if (newChall.type == "corr" && this.cdisplay == "live" && !this.challenges.some(c => c.type == "live"))
- this.setDisplay('c', "corr");
+ if ((newChall.type == "live" && this.cdisplay == "corr") ||
+ (newChall.type == "corr" && this.cdisplay == "live"))
+ {
+ document.getElementById("btnC" + newChall.type).classList.add("somethingnew");
+ }
}
break;
}
newGame.rids = [game.rid];
delete newGame["rid"];
this.games.push(newGame);
- // Adjust visual:
- if (newGame.type == "live" && this.gdisplay == "corr" && !this.games.some(g => g.type == "corr"))
- this.setDisplay('g', "live");
- else if (newGame.type == "live" && this.gdisplay == "live" && !this.games.some(g => g.type == "live"))
- this.setDisplay('g', "corr");
+ if ((newGame.type == "live" && this.gdisplay == "corr") ||
+ (newGame.type == "corr" && this.gdisplay == "live"))
+ {
+ document.getElementById("btnG" + newGame.type).classList.add("somethingnew");
+ }
}
else
{
case "newchat":
this.newChat = data.data;
if (!document.getElementById("modalPeople").checked)
- document.getElementById("peopleBtn").style.backgroundColor = "#c5fefe";
+ document.getElementById("peopleBtn").classList.add("somethingnew");
break;
}
},
font-style: italic
button.player-action
margin-left: 32px
+
+.somethingnew
+ background-color: #c5fefe !important
+
+.tabbtn
+ background-color: white
+
+#div2, #div3
+ margin-top: 15px
+@media screen and (max-width: 767px)
+ #div2, #div3
+ margin-top: 0
</style>
.row
.col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
.button-group
- button(@click="display='live'") {{ st.tr["Live games"] }}
- button(@click="display='corr'") {{ st.tr["Correspondance games"] }}
- GameList(v-show="display=='live'" :games="filterGames('live')"
+ button#liveGames(@click="setDisplay('live',$event)") {{ st.tr["Live games"] }}
+ button#corrGames(@click="setDisplay('corr',$event)") {{ st.tr["Correspondance games"] }}
+ GameList(v-show="display=='live'" :games="liveGames"
@show-game="showGame")
- GameList(v-show="display=='corr'" :games="filterGames('corr')"
+ GameList(v-show="display=='corr'" :games="corrGames"
@show-game="showGame")
</template>
return {
st: store.state,
display: "live",
- games: [],
+ liveGames: [],
+ corrGames: [],
};
},
created: function() {
GameStorage.getAll((localGames) => {
localGames.forEach((g) => g.type = this.classifyObject(g));
- //Array.prototype.push.apply(this.games, localGames); //TODO: Vue 3
- this.games = this.games.concat(localGames);
+ this.liveGames = localGames;
});
if (this.st.user.id > 0)
{
ajax("/games", "GET", {uid: this.st.user.id}, (res) => {
res.games.forEach((g) => g.type = this.classifyObject(g));
- //Array.prototype.push.apply(this.games, res.games); //TODO: Vue 3
- this.games = this.games.concat(res.games);
+ this.corrGames = res.games;
});
}
},
+ mounted: function() {
+ const showType = localStorage.getItem("type-myGames") || "live";
+ this.setDisplay(showType);
+ },
methods: {
- // TODO: classifyObject and filterGames are redundant (see Hall.vue)
+ setDisplay: function(type, e) {
+ this.display = type;
+ localStorage.setItem("type-myGames", type);
+ let elt = !!e
+ ? e.target
+ : document.getElementById(type + "Games");
+ elt.classList.add("active");
+ if (!!elt.previousElementSibling)
+ elt.previousElementSibling.classList.remove("active");
+ else
+ elt.nextElementSibling.classList.remove("active");
+ },
+ // TODO: classifyObject is redundant (see Hall.vue)
classifyObject: function(o) {
return (o.cadence.indexOf('d') === -1 ? "live" : "corr");
},
- filterGames: function(type) {
- return this.games.filter(g => g.type == type);
- },
showGame: function(g) {
this.$router.push("/game/" + g.id);
},
},
};
</script>
+
+<style lang="sass" scoped>
+.active
+ color: #42a983
+</style>
button(@click="sendProblem()") {{ st.tr["Send"] }}
#dialog.text-center {{ st.tr[infoMsg] }}
.row(v-if="showOne")
- .col-sm-12.col-md-9.col-md-offset-3.col-lg-10.col-lg-offset-2
+ .col-sm-12.col-md-10.col-md-offset-2
#topPage
- span {{ curproblem.vname }}
+ span.vname {{ curproblem.vname }}
+ span.uname {{ "(" + curproblem.uname + ")" }}
button.marginleft(@click="backToList()") {{ st.tr["Back to list"] }}
button.nomargin(
v-if="st.user.id == curproblem.uid"
)
| {{ st.tr["Delete"] }}
p.clickable(
- v-html="curproblem.uname + ' : ' + parseHtml(curproblem.instruction)"
+ v-html="parseHtml(curproblem.instruction)"
@click="curproblem.showSolution=!curproblem.showSolution"
)
| {{ st.tr["Show solution"] }}
tr
th {{ st.tr["Variant"] }}
th {{ st.tr["Instructions"] }}
+ th {{ st.tr["Number"] }}
tr(
v-for="p in problems"
v-show="displayProblem(p)"
@click="setHrefPid(p)"
)
td {{ p.vname }}
- td(v-html="p.instruction")
+ td {{ firstChars(p.instruction) }}
+ td {{ p.id }}
BaseGame(v-if="showOne" :game="game" :vr="vr")
</template>
this.problems.forEach(p => {
if (p.uid != this.st.user.id)
names[p.uid] = ""; //unknwon for now
- else { console.log("assign " + this.st.user.name);
- p.uname = this.st.user.name; console.log(p); console.log(this.problems); }
+ else
+ p.uname = this.st.user.name;
});
- if (Object.keys(name).length > 0)
+ const showOneIfPid = () => {
+ const pid = this.$route.query["id"];
+ if (!!pid)
+ this.showProblem(this.problems.find(p => p.id == pid));
+ };
+ if (Object.keys(names).length > 0)
{
ajax("/users",
"GET",
res2 => {
res2.users.forEach(u => {names[u.id] = u.name});
this.problems.forEach(p => p.uname = names[p.uid]);
+ showOneIfPid();
}
);
}
- const pid = this.$route.query["id"];
- if (!!pid)
- this.showProblem(this.problems.find(p => p.id == pid));
+ else
+ showOneIfPid();
});
},
mounted: function() {
if (this.problems.length > 0 && this.problems[0].vname == "")
this.problems.forEach(p => this.setVname(p));
},
- "$route": function(to, from) { console.log("ddddd");
+ "$route": function(to, from) {
const pid = to.query["id"];
if (!!pid)
this.showProblem(this.problems.find(p => p.id == pid));
setVname: function(prob) {
prob.vname = this.st.variants.find(v => v.id == prob.vid).name;
},
+ firstChars: function(text) {
+ let preparedText = text
+ // Replace line jumps and <br> by spaces
+ .replace(/\n/g, " " )
+ .replace(/<br\/?>/g, " " )
+ .replace(/<[^>]+>/g, "") //remove remaining HTML tags
+ .replace(/[ ]+/g, " ") //remove series of spaces by only one
+ .trim();
+ const maxLength = 32; //arbitrary...
+ if (preparedText.length > maxLength)
+ return preparedText.substr(0,32) + "...";
+ return preparedText;
+ },
copyProblem: function(p1, p2) {
for (let key in p1)
p2[key] = p1[key];
text-align: center
& > *
margin: 0
+
#topPage
- span
+ span.vname
font-weight: bold
padding-left: var(--universal-margin)
+ span.uname
+ padding-left: var(--universal-margin)
margin: 0 auto
& > .nomargin
margin: 0
& > .marginleft
margin: 0 0 0 15px
+@media screen and (max-width: 767px)
+ #topPage
+ text-align: center
+
</style>
let query =
// NOTE: g.scoreMsg can be NULL
// (in this case score = "*" and no reason to look at it)
- "SELECT g.id, g.vid, g.fen, g.fenStart, g.cadence, g.score, " +
+ "SELECT g.id, g.vid, g.fen, g.fenStart, g.cadence, g.created, g.score, " +
"g.scoreMsg, g.drawOffer, v.name AS vname " +
"FROM Games g " +
"JOIN Variants v " +
getByUser: function(uid, excluded, cb)
{
db.serialize(function() {
- const query =
- "SELECT DISTINCT gid " +
- "FROM Players " +
- "WHERE uid " + (excluded ? "<>" : "=") + " " + uid;
+ let query = "";
+ if (uid == 0)
+ {
+ // Special case anonymous user: show all games
+ query =
+ "SELECT id AS gid " +
+ "FROM Games";
+ }
+ else
+ {
+ // Registered user:
+ query =
+ "SELECT gid " +
+ "FROM Players " +
+ "GROUP BY gid " +
+ "HAVING COUNT(uid = " + uid + " OR NULL) " +
+ (excluded ? " = 0" : " > 0");
+ }
db.all(query, (err,gameIds) => {
- if (!!err)
- return cb(err);
- if (gameIds.length == 0)
- return cb(null, []);
+ if (!!err || gameIds.length == 0)
+ return cb(err, []);
let gameArray = [];
+ let kounter = 0;
for (let i=0; i<gameIds.length; i++)
{
GameModel.getOne(gameIds[i]["gid"], true, (err2,game) => {
if (!!err2)
return cb(err2);
gameArray.push(game);
+ kounter++; //TODO: let's hope this is atomic?!
// Call callback function only when gameArray is complete:
- if (i == gameIds.length - 1)
+ if (kounter == gameIds.length)
return cb(null, gameArray);
});
}