document.addEventListener("touchend", this._onTouchEnd);
}
+ componentDidUpdate(prevProps, prevState) {
+ if (prevState.grid !== this.state.grid) {
+ if (this._skip_sync) {
+ this._skip_sync = false;
+ } else {
+ this.sync_placements(prevState.grid, this.state.grid);
+ }
+ }
+ }
+
componentWillUnmount() {
document.removeEventListener("touchmove", this._onTouchMove);
document.removeEventListener("touchend", this._onTouchEnd);
}
+ /* Send tile placement changes to the server. */
+ sync_placements(old_grid, new_grid) {
+ /* Find tiles that were removed or moved. */
+ for (const key of Object.keys(old_grid)) {
+ const old_cell = old_grid[key];
+ const new_cell = new_grid[key];
+ if (!new_cell || new_cell.tileIndex !== old_cell.tileIndex) {
+ /* This tile was removed from this position. Check if it moved
+ * to a new position (handled below) or returned to rack. */
+ const still_on_grid = Object.values(new_grid).some(
+ c => c.tileIndex === old_cell.tileIndex
+ );
+ if (!still_on_grid) {
+ fetch("place", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ tileIndex: old_cell.tileIndex,
+ r: null, c: null })
+ });
+ }
+ }
+ }
+
+ /* Find tiles that were placed or moved to a new position. */
+ for (const key of Object.keys(new_grid)) {
+ const new_cell = new_grid[key];
+ const old_cell = old_grid[key];
+ if (!old_cell || old_cell.tileIndex !== new_cell.tileIndex) {
+ const [r, c] = key.split(",").map(Number);
+ fetch("place", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ tileIndex: new_cell.tileIndex, r, c,
+ letter: new_cell.letter,
+ isBlank: new_cell.isBlank })
+ });
+ }
+ }
+ }
+
/*****************************************************
* SSE event handlers (called from window.game) *
*****************************************************/
this.setState({ joined: true, tiles: data.tiles, rack: data.tiles.map((_, i) => i) });
}
+ receive_board(board) {
+ /* Restore grid from server on reconnect. Board is an object
+ * like { "r,c": { tileIndex, letter, isBlank } }. */
+ const grid = {};
+ const placed = new Set();
+ for (const key of Object.keys(board)) {
+ grid[key] = board[key];
+ placed.add(board[key].tileIndex);
+ }
+ /* Remove placed tiles from the rack. Skip syncing back to server
+ * since this data came from the server. */
+ const rack = this.state.rack.filter(i => !placed.has(i));
+ this._skip_sync = true;
+ this.setState({ grid, rack });
+ }
+
receive_new_tiles(data) {
const old_len = this.state.tiles.length;
const new_tiles = [...this.state.tiles, ...data.tiles];
window.game.receive_tiles(JSON.parse(event.data));
});
+events.addEventListener("board", event => {
+ window.game.receive_board(JSON.parse(event.data));
+});
+
events.addEventListener("new-tiles", event => {
window.game.receive_new_tiles(JSON.parse(event.data));
});