i.material-icons send
button(v-if="stage!='Update'" @click="toggleStage()")
span {{ stage=="Login" ? "Register" : "Login" }}
- button(v-else @click="doLogout()")
+ button#logoutBtn(v-else @click="doLogout()")
span Logout
#dialog(:style="{display: displayInfo}") {{ infoMsg }}
</template>
);
},
doLogout: function() {
- ajax(
- "/logout",
- "GET",
- () => {
- this.user.id = 0;
- this.user.name = "";
- this.user.email = "";
- this.user.notify = false;
- delete localStorage["myid"];
- delete localStorage["myname"];
- }
- );
+ let logoutBtn = document.getElementById("logoutBtn");
+ logoutBtn.disabled = true;
+ // NOTE: this local cleaning would logically happen when we're sure
+ // that token is erased. But in the case a user clear the cookies,
+ // it would lead to situations where he cannot ("locally") log out.
+ // At worst, if token deletion fails the user can erase cookie manually.
+ this.user.id = 0;
+ this.user.name = "";
+ this.user.email = "";
+ this.user.notify = false;
+ localStorage.removeItem("myid");
+ localStorage.removeItem("myname");
+ ajax("/logout", "GET", () => {
+ logoutBtn.disabled = false; //for symmetry, but not very useful...
+ document.getElementById("modalUser").checked = false;
+ // this.$router.push("/") will fail if logout from Hall, so:
+ document.location.reload(true);
+ });
},
},
};
mysid = getRandString();
localStorage["mysid"] = mysid; //done only once (unless user clear browser data)
}
+ // Quick user setup using local storage:
this.state.user = {
id: localStorage["myid"] || 0,
name: localStorage["myname"] || "", //"" for "anonymous"
notify: false, //email notifications
sid: mysid,
};
- if (this.state.user.id > 0)
- {
- ajax("/whoami", "GET", res => {
- this.state.user.email = res.email;
- this.state.user.notify = res.notify;
- });
- }
+ // Slow verification through the server:
+ // NOTE: still superficial identity usurpation possible, but difficult.
+ ajax("/whoami", "GET", res => {
+ this.state.user.id = res.id;
+ this.state.user.name = res.name;
+ this.state.user.email = res.email;
+ this.state.user.notify = res.notify;
+ });
this.state.conn = new WebSocket(params.socketUrl + "/?sid=" + mysid +
"&page=" + encodeURIComponent(page));
// Settings initialized with values from localStorage
sendSomethingTo: function(to, code, obj, warnDisconnected) {
const doSend = (code, obj, sid) => {
this.st.conn.send(JSON.stringify(Object.assign(
- {},
{code: code},
obj,
{target: sid}
if (!targetSid)
{
if (!!warnDisconnected)
- alert("Warning: " + pname + " is not connected");
+ alert("Warning: " + to + " is not connected");
+ return false;
}
else
doSend(code, obj, targetSid);
}
else
{
- // Open challenge: send to all connected players (except us)
+ // Open challenge: send to all connected players (me excepted)
Object.keys(this.people).forEach(sid => {
if (sid != this.st.user.sid)
doSend(code, obj, sid);
});
}
+ return true;
},
// Messaging center:
socketMessageListener: function(msg) {
case "askchallenge":
{
// Send my current live challenge (if any)
- const cIdx = this.challenges
- .findIndex(c => c.from.sid == this.st.user.sid && c.type == "live");
+ const cIdx = this.challenges.findIndex(c =>
+ c.from.sid == this.st.user.sid && c.type == "live");
if (cIdx >= 0)
{
const c = this.challenges[cIdx];
+ if (!!c.to)
+ {
+ // Only share targeted challenges to the targets:
+ const toSid = Object.keys(this.people).find(k =>
+ this.people[k].name == c.to);
+ if (toSid != data.from)
+ return;
+ }
const myChallenge =
{
// Minimal challenge informations: (from not required)
to: c.to,
fen: c.fen,
vid: c.vid,
- timeControl: c.timeControl
+ timeControl: c.timeControl,
};
this.st.conn.send(JSON.stringify({code:"challenge",
chall:myChallenge, target:data.from}));
}
case "refusechallenge":
{
- alert(this.people[data.from].name + " declined your challenge");
ArrayFun.remove(this.challenges, c => c.id == data.cid);
+ alert(this.people[data.from].name + " declined your challenge");
break;
}
case "deletechallenge":
const finishAddChallenge = (cid,warnDisconnected) => {
chall.id = cid || "c" + getRandString();
// Send challenge to peers (if connected)
- this.sendSomethingTo(chall.to, "challenge", {chall:chall}, !!warnDisconnected);
+ const isSent = this.sendSomethingTo(chall.to, "challenge",
+ {chall:chall}, !!warnDisconnected);
+ if (!isSent)
+ return;
+ // Remove old challenge if any (only one at a time):
+ const cIdx = this.challenges.findIndex(c =>
+ c.from.sid == this.st.user.sid && c.type == ctype);
+ if (cIdx >= 0)
+ {
+ // Delete current challenge (will be replaced now)
+ this.sendSomethingTo(this.challenges[cIdx].to,
+ "deletechallenge", {cid:this.challenges[cIdx].id});
+ if (ctype == "corr")
+ {
+ ajax(
+ "/challenges",
+ "DELETE",
+ {id: this.challenges[cIdx].id}
+ );
+ }
+ this.challenges.splice(cIdx, 1);
+ }
+ // Add new challenge:
chall.added = Date.now();
// NOTE: vname and type are redundant (can be deduced from timeControl + vid)
chall.type = ctype;
localStorage.setItem("challenge", JSON.stringify(chall));
document.getElementById("modalNewgame").checked = false;
};
- const cIdx = this.challenges.findIndex(
- c => c.from.sid == this.st.user.sid && c.type == ctype);
- if (cIdx >= 0)
- {
- // Delete current challenge (will be replaced now)
- this.sendSomethingTo(this.challenges[cIdx].to,
- "deletechallenge", {cid:this.challenges[cIdx].id});
- if (ctype == "corr")
- {
- ajax(
- "/challenges",
- "DELETE",
- {id: this.challenges[cIdx].id}
- );
- }
- this.challenges.splice(cIdx, 1);
- }
if (ctype == "live")
{
// Live challenges have a random ID
code: "refusechallenge",
cid: c.id, target: c.from.sid}));
}
+ this.sendSomethingTo((!!c.to ? c.from : null), "deletechallenge", {cid:c.id});
}
else //my challenge
{
}
else //live
localStorage.removeItem("challenge");
+ this.sendSomethingTo(c.to, "deletechallenge", {cid:c.id});
}
- // In (almost) all cases, the challenge is consumed:
+ // In all cases, the challenge is consumed:
ArrayFun.remove(this.challenges, ch => ch.id == c.id);
- // NOTE: deletechallenge event might be redundant (but it's easier this way)
- this.sendSomethingTo((!!c.to ? c.from : null), "deletechallenge", {cid:c.id});
},
// NOTE: when launching game, the challenge is already deleted
launchGame: async function(c) {
vname: c.vname, //theoretically vid is enough, but much easier with vname
timeControl: c.timeControl,
};
- let target = c.from.sid; //may not be defined if corr + offline opp
- if (!target)
+ let oppsid = c.from.sid; //may not be defined if corr + offline opp
+ if (!oppsid)
{
- target = Object.keys(this.people).find(sid =>
+ oppsid = Object.keys(this.people).find(sid =>
this.people[sid].id == c.from.id);
}
const tryNotifyOpponent = () => {
- if (!!target) //opponent is online
+ if (!!oppsid) //opponent is online
{
this.st.conn.send(JSON.stringify({code:"newgame",
- gameInfo:gameInfo, target:target, cid:c.id}));
+ gameInfo:gameInfo, target:oppsid, cid:c.id}));
}
};
if (c.type == "live")
);
}
// Send game info to everyone except opponent (and me)
- this.st.conn.send(JSON.stringify({code:"game",
- game: { //minimal game info:
- id: gameInfo.id,
- players: gameInfo.players.map(p => p.name),
- vid: gameInfo.vid,
- timeControl: gameInfo.timeControl,
- },
- oppsid: target}));
+ const playersNames = gameInfo.players.map(p => {name: p.name});
+ Object.keys(this.people).forEach(sid => {
+ if (![this.st.user.sid,target].includes(sid))
+ {
+ this.st.conn.send(JSON.stringify({code:"game",
+ game: { //minimal game info:
+ id: gameInfo.id,
+ players: playersNames,
+ vid: gameInfo.vid,
+ timeControl: gameInfo.timeControl,
+ },
+ target: sid}));
+ }
+ });
},
// NOTE: for live games only (corr games start on the server)
startNewGame: function(gameInfo) {