From 7962bc994a9463ee7a2e75bb9507bdd5a94f8729 Mon Sep 17 00:00:00 2001 From: Carl Worth Date: Sat, 7 Mar 2026 18:05:55 -0500 Subject: [PATCH] letterrip: Fix blank-letter to work even if letter selection is cancelled If a user is moving a blank tile off of their rack onto the board, then they are required to choose a letter for the blank. In this situation, cancelling letter selection cause the blank tile to be returned to the rack. In contrast, when moving a blank tile from one space on the board to the other, the blank will already have a selected letter, and it's reasonable for that selection to be unchanged. So in this case, cancelling the letter selection should let the movement work, and should not send the letter back to the rack. This commit fixes that scenario by performing the movement prior to bringing up the selection modal. That way, if the selection modal is cancelled, there's nothing else to do and the user gets the desired behavior (the tile moved, unchanged). --- letterrip/letterrip.jsx | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/letterrip/letterrip.jsx b/letterrip/letterrip.jsx index a6bc234..6ea7fdb 100644 --- a/letterrip/letterrip.jsx +++ b/letterrip/letterrip.jsx @@ -736,7 +736,7 @@ class Game extends React.Component { const updates = { selected: null }; if (cell && cell.isBlank) { const [r, c] = grid_key.split(",").map(Number); - updates.blank_pending = { r, c, tileIndex: cell.tileIndex, reassign: true }; + updates.blank_pending = { r, c, tileIndex: cell.tileIndex }; } this.setState(updates); return; @@ -803,8 +803,16 @@ class Game extends React.Component { delete new_grid[sel.grid_key]; } - /* Blank tile — always show the modal so the user can pick a letter. */ + /* Blank tile — show the modal so the user can pick a letter. + * If the blank already had a letter (moved from grid), place it + * at the destination with that letter first. The modal then just + * offers a chance to change it, and cancel is a no-op. */ if (is_blank) { + const existing = sel.from === "grid" ? this.state.grid[sel.grid_key] : null; + if (existing && existing.letter) { + new_grid[grid_key] = { letter: existing.letter, + tileIndex: tile_index, isBlank: true }; + } this.setState({ grid: new_grid, rack: new_rack, @@ -888,14 +896,16 @@ class Game extends React.Component { this.choose_blank_letter(l)} onCancel={() => { - /* Put the blank tile back on the rack if cancelled, - * but only if it was freshly placed (not a reassignment - * of a blank already on the grid). */ - if (state.blank_pending.reassign) { + const pending = state.blank_pending; + const key = pending.r + "," + pending.c; + if (state.grid[key]) { + /* Tile is already on the grid (moved with an + * existing letter, or reassigning in place). + * Just dismiss the modal. */ this.setState({ blank_pending: null }); } else { - const { tileIndex } = state.blank_pending; - const new_rack = [...state.rack, tileIndex]; + /* Fresh blank from rack — return it. */ + const new_rack = [...state.rack, pending.tileIndex]; this.setState({ blank_pending: null, rack: new_rack }); } }} -- 2.45.2