componentDidMount() {
this._onKeyDown = (e) => this.on_key_down(e);
document.addEventListener("keydown", this._onKeyDown);
+
+ /* Periodic state consistency check (debug aid). */
+ this._debug_interval = setInterval(() => this._check_state(), 5000);
}
componentWillUnmount() {
document.removeEventListener("keydown", this._onKeyDown);
+ if (this._debug_interval) clearInterval(this._debug_interval);
+ }
+
+ _check_state() {
+ const { center, revealing, claim_active, claim_player } = this.state;
+ const center_ids = new Set(center.map(t => t.id));
+
+ for (const id of Object.keys(revealing)) {
+ if (!center_ids.has(Number(id))) {
+ console.warn("[anagrams] Stale revealing entry for tile", id,
+ "not in center — cleaning up");
+ this.setState(prev => {
+ const r = { ...prev.revealing };
+ delete r[id];
+ return { revealing: r };
+ });
+ }
+ }
+
+ if (claim_active && !claim_player) {
+ console.warn("[anagrams] claim_active but no claim_player");
+ }
}
/*****************************************************
new_count++;
}
}
- this.setState({ center: tiles, tile_positions: positions }, () => {
+ /* Clean up revealing entries for tiles no longer in center. */
+ const tile_ids = new Set(tiles.map(t => t.id));
+ const revealing = { ...this.state.revealing };
+ let reveal_cleaned = false;
+ for (const id of Object.keys(revealing)) {
+ if (!tile_ids.has(Number(id))) {
+ delete revealing[id];
+ reveal_cleaned = true;
+ }
+ }
+ const update = { center: tiles, tile_positions: positions };
+ if (reveal_cleaned) update.revealing = revealing;
+
+ this.setState(update, () => {
/* On bulk load (reconnection), arrange tiles in a grid. */
if (new_count > 1) this.shuffle_tiles();
});
* each fading from full opacity to transparent. */
const phase_ms = countdown_ms / 3;
const start = Date.now();
+ let running = true;
const step = () => {
- this.setState(prev => {
- const r = { ...prev.revealing };
- const entry = r[tile.id];
- if (!entry) return { revealing: r };
+ if (!running) return;
- const elapsed = Date.now() - start;
+ const elapsed = Date.now() - start;
- if (elapsed >= countdown_ms) {
+ if (elapsed >= countdown_ms) {
+ running = false;
+ this.setState(prev => {
+ const r = { ...prev.revealing };
delete r[tile.id];
return { revealing: r };
- }
+ });
+ return;
+ }
- const phase = Math.min(2, Math.floor(elapsed / phase_ms));
- const current_number = 3 - phase;
- const frac = (elapsed - phase * phase_ms) / phase_ms;
+ const phase = Math.min(2, Math.floor(elapsed / phase_ms));
+ const current_number = 3 - phase;
+ const frac = (elapsed - phase * phase_ms) / phase_ms;
+ this.setState(prev => {
+ const r = { ...prev.revealing };
+ if (!r[tile.id]) { running = false; return null; }
r[tile.id] = { number: current_number, opacity: 1 - frac };
return { revealing: r };
});
- if (this.state.revealing[tile.id]) {
- requestAnimationFrame(step);
- }
+ requestAnimationFrame(step);
};
requestAnimationFrame(step);
}