]> git.cworth.org Git - lmno.games/commitdiff
anagram: Add a guard against the same letter getting taken twice
authorCarl Worth <cworth@cworth.org>
Mon, 9 Mar 2026 03:10:05 +0000 (23:10 -0400)
committerCarl Worth <cworth@cworth.org>
Mon, 9 Mar 2026 03:10:05 +0000 (23:10 -0400)
This is effectively debouncing taps (or keypresses) of letters being
taken from the center of the play field and put into the rack. If the
same letter tile is taken multiple times then a player can end up with
"ghost" tile in the rack that never goes away but also can't be
interacted with.

anagrams/anagrams.jsx

index 4a13e90d5021893f722f14572316dd6a376ddc4b..dd974230eb78c31b7a0c717fc6bacc03016490cd 100644 (file)
@@ -140,6 +140,7 @@ class Game extends React.Component {
       dragging_center_tile: null
     };
     this._claim_interval = null;
+    this._taking_tiles = new Set(); /* tile IDs with in-flight take requests */
   }
 
   componentDidMount() {
@@ -491,6 +492,11 @@ class Game extends React.Component {
   }
 
   async take_center_letter(tile) {
+    /* Guard against duplicate takes of the same tile (e.g. rapid
+     * double-tap on mobile before React processes the first setState). */
+    if (this._taking_tiles.has(tile.id)) return;
+    this._taking_tiles.add(tile.id);
+
     /* Optimistically add to rack so letters appear in keystroke
      * order regardless of HTTP response timing. */
     this.setState(prev => ({
@@ -505,6 +511,7 @@ class Game extends React.Component {
     const response = await fetch_post_json("take-letter", {
       letter_id: tile.id
     });
+    this._taking_tiles.delete(tile.id);
     if (!response.ok) {
       /* Revert: remove from rack and restore to center. */
       this.setState(prev => ({