role="dialog"
data-checkbox="modalChat"
)
- #chat.card
+ .card
label.modal-close(for="modalChat")
#participants
span {{ Object.keys(people).length + " " + st.tr["participant(s):"] }}
@mychat="processChat"
@chatcleared="clearChat"
)
+ input#modalConfirm.modal(type="checkbox")
+ div#confirmDiv(role="dialog")
+ .card
+ .diagram(v-html="curDiag")
+ .button-group#buttonsConfirm
+ // onClick for acceptBtn: set dynamically
+ button.acceptBtn
+ span {{ st.tr["Validate"] }}
+ button.refuseBtn(@click="cancelMove()")
+ span {{ st.tr["Cancel"] }}
.row
#aboveBoard.col-sm-12.col-md-9.col-md-offset-3.col-lg-10.col-lg-offset-2
span.variant-cadence {{ game.cadence }}
span.variant-name {{ game.vname }}
- button#chatBtn(onClick="window.doClick('modalChat')") Chat
+ span#nextGame(
+ v-if="nextIds.length > 0"
+ @click="showNextGame()"
+ )
+ | {{ st.tr["Next_g"] }}
+ button#chatBtn.tooltip(
+ onClick="window.doClick('modalChat')"
+ aria-label="Chat"
+ )
+ img(src="/images/icons/chat.svg")
#actions(v-if="game.score=='*'")
- button(
+ button.tooltip(
@click="clickDraw()"
:class="{['draw-' + drawOffer]: true}"
+ :aria-label="st.tr['Draw']"
)
- | {{ st.tr["Draw"] }}
- button(
+ img(src="/images/icons/draw.svg")
+ button.tooltip(
v-if="!!game.mycolor"
@click="abortGame()"
+ :aria-label="st.tr['Abort']"
)
- | {{ st.tr["Abort"] }}
- button(
+ img(src="/images/icons/abort.svg")
+ button.tooltip(
v-if="!!game.mycolor"
@click="resign()"
+ :aria-label="st.tr['Resign']"
)
- | {{ st.tr["Resign"] }}
+ img(src="/images/icons/resign.svg")
+ button.tooltip(
+ v-else-if="!!game.mycolor"
+ @click="rematch()"
+ :aria-label="st.tr['Rematch']"
+ )
+ img(src="/images/icons/rematch.svg")
#playersInfo
p
span.name(:class="{connected: isConnected(0)}")
import { ajax } from "@/utils/ajax";
import { extractTime } from "@/utils/timeControl";
import { getRandString } from "@/utils/alea";
+import { getDiagram } from "@/utils/printDiagram";
import { processModalClick } from "@/utils/modalClick";
-import { getFullNotation } from "@/utils/notation";
import { playMove, getFilteredMove } from "@/utils/playUndo";
import { getScoreMessage } from "@/utils/scoring";
import { ArrayFun } from "@/utils/array";
chats: [],
rendered: false
},
+ nextIds: [],
virtualClocks: [[0,0], [0,0]], //initialized with true game.clocks
vr: null, //"variant rules" object initialized from FEN
drawOffer: "",
onMygames: [], //opponents (or me) on "MyGames" page
lastate: undefined, //used if opponent send lastate before game is ready
repeat: {}, //detect position repetition
+ curDiag: "", //for corr moves confirmation
newChat: "",
conn: null,
roomInitialized: false,
const my = this.st.user;
this.$set(this.people, my.sid, { id: my.id, name: my.name });
this.gameRef.id = this.$route.params["id"];
- this.gameRef.rid = this.$route.query["rid"]; //may be undefined
+ // rid = remote ID to find an observed live game,
+ // next = next corr games IDs to navigate faster
+ // (Both might be undefined)
+ this.gameRef.rid = this.$route.query["rid"];
+ this.nextIds = JSON.parse(this.$route.query["next"] || "[]");
// Initialize connection
this.connexionString =
params.socketUrl +
"&tmpId=" +
getRandString() +
"&page=" +
- encodeURIComponent(this.$route.path);
+ // Discard potential "/?next=[...]" for page indication:
+ encodeURIComponent(this.$route.path.match(/\/game\/[a-zA-Z0-9]+/)[0]);
this.conn = new WebSocket(this.connexionString);
this.conn.onmessage = this.socketMessageListener;
this.conn.onclose = this.socketCloseListener;
document
.getElementById("chatWrap")
.addEventListener("click", processModalClick);
+ if ("ontouchstart" in window) {
+ // Disable tooltips on smartphones:
+ document.getElementsByClassName("tooltip").forEach(elt => {
+ elt.classList.remove("tooltip");
+ });
+ }
},
beforeDestroy: function() {
this.send("disconnect");
const player = this.game.players[index];
// Is it me ?
if (this.st.user.sid == player.sid || this.st.user.id == player.uid)
- return true;
+ // Still have to check for name (because of potential multi-accounts
+ // on same browser, although this should be rare...)
+ return (!this.st.user.name || this.st.user.name == player.name);
// Try to find a match in people:
return (
(
notifyTurn: function(sid) {
const player = this.people[sid];
const colorIdx = this.game.players.findIndex(
- p => p.sid == sid || p.id == player.id);
+ p => p.sid == sid || p.uid == player.id);
const color = ["w","b"][colorIdx];
const movesCount = this.game.moves.length;
const yourTurn =
(color == "b" && movesCount % 2 == 1);
this.send("turnchange", { target: sid, yourTurn: yourTurn });
},
+ showNextGame: function() {
+ // Did I play in current game? If not, add it to nextIds list
+ if (this.game.score == "*" && this.vr.turn == this.game.mycolor)
+ this.nextIds.unshift(this.game.id);
+ const nextGid = this.nextIds.pop();
+ this.$router.push(
+ "/game/" + nextGid + "/?next=" + JSON.stringify(this.nextIds));
+ },
+ rematch: function() {
+ alert("Unimplemented yet (soon :) )");
+ // TODO: same logic as for draw, but re-click remove rematch offer (toggle)
+ },
askGameAgain: function() {
this.gameIsLoading = true;
const doAskGame = () => {
}
break;
case "askfullgame":
- this.send("fullgame", { data: this.game, target: data.from });
+ const gameToSend = Object.keys(this.game)
+ .filter(k =>
+ [
+ "id","fen","players","vid","cadence","fenStart","vname",
+ "moves","clocks","initime","score","drawOffer"
+ ].includes(k))
+ .reduce(
+ (obj, k) => {
+ obj[k] = this.game[k];
+ return obj;
+ },
+ {}
+ );
+ this.send("fullgame", { data: gameToSend, target: data.from });
break;
case "fullgame":
// Callback "roomInit" to poll clients only after game is loaded
this.gameOver("1/2", message);
} else if (this.drawOffer == "") {
// No effect if drawOffer == "sent"
- if (!!this.game.mycolor != this.vr.turn) {
+ if (this.game.mycolor != this.vr.turn) {
alert(this.st.tr["Draw offer only in your turn"]);
return;
}
}
// NOTE: clocks in seconds, initime in milliseconds
game.moves.sort((m1, m2) => m1.idx - m2.idx); //in case of
+ game.clocks = [tc.mainTime, tc.mainTime];
const L = game.moves.length;
if (game.score == "*") {
// Set clocks + initime
- game.clocks = [tc.mainTime, tc.mainTime];
game.initime = [0, 0];
if (L >= 1) {
const gameLastupdate = game.moves[L-1].played;
}
}
}
- if (game.drawOffer) {
+ if (!!game.drawOffer) {
if (game.drawOffer == "t")
// Three repetitions
this.drawOffer = "threerep";
moveCol == this.game.mycolor &&
!data.receiveMyMove
) {
- setTimeout(() => {
- // TODO: remplacer cette confirm box par qqch de plus discret
- // (et de même pour challenge accepté / refusé)
- if (
- !confirm(
- this.st.tr["Move played:"] +
- " " +
- getFullNotation(move) +
- "\n" +
- this.st.tr["Are you sure?"]
- )
- ) {
- this.$refs["basegame"].cancelLastMove();
- return;
+ let el = document.querySelector("#buttonsConfirm > .acceptBtn");
+ // We may play several moves in a row: in case of, remove listener:
+ let elClone = el.cloneNode(true);
+ el.parentNode.replaceChild(elClone, el);
+ elClone.addEventListener(
+ "click",
+ () => {
+ document.getElementById("modalConfirm").checked = false;
+ doProcessMove();
}
- doProcessMove();
- // Let small time to finish drawing current move attempt:
- }, 500);
+ );
+ this.vr.play(move);
+ const parsedFen = V.ParseFen(this.vr.getFen());
+ this.vr.undo(move);
+ this.curDiag = getDiagram({
+ position: parsedFen.position,
+ orientation: this.game.mycolor
+ });
+ document.getElementById("modalConfirm").checked = true;
}
else doProcessMove();
},
+ cancelMove: function() {
+ document.getElementById("modalConfirm").checked = false;
+ this.$refs["basegame"].cancelLastMove();
+ },
gameOver: function(score, scoreMsg) {
this.game.score = score;
this.$set(this.game, "scoreMsg", scoreMsg || getScoreMessage(score));
#actions
display: inline-block
margin: 0
- button
- display: inline-block
- margin: 0
+
+button
+ display: inline-block
+ margin: 0
+ display: inline-flex
+ img
+ height: 24px
+ display: flex
+ @media screen and (max-width: 767px)
+ height: 18px
@media screen and (max-width: 767px)
#aboveBoard
font-weight: bold
padding-right: 10px
+span#nextGame
+ background-color: #edda99
+ cursor: pointer
+ display: inline-block
+ margin-right: 10px
+
span.name
font-size: 1.5rem
padding: 0 3px
display: inline-block
margin: 0 15px
-#chat
+#chatWrap > .card
padding-top: 20px
max-width: 767px
- border: none;
+ border: none
-#chatBtn
- margin: 0 10px 0 0
+#confirmDiv > .card
+ max-width: 767px
+ max-height: 100%
.draw-sent, .draw-sent:hover
background-color: lightyellow
.somethingnew
background-color: #c5fefe
+
+.diagram
+ margin: 0 auto
+ max-width: 400px
+ // width: 100% required for Firefox
+ width: 100%
+
+#buttonsConfirm
+ margin: 0
+ & > button > span
+ width: 100%
+ text-align: center
+
+button.acceptBtn
+ background-color: lightgreen
+button.refuseBtn
+ background-color: red
</style>