feat: remove a saved game & redirect on missing/deleted game (#20)
CI / build + unit + e2e (pull_request) Successful in 1m14s
CI / build + unit + e2e (pull_request) Successful in 1m14s
- DELETE /api/games/:id route + Db.delete() (404 when absent) - PresetPicker: per-saved-game trash button + confirm dialog (Cancel/Delete) - App: runDeleteGame; useSse onError -> goHome on 'deleted'/'not found' (a deleted/missing game now returns the live tab and any deep link home) - DeleteGameResponse in shared/protocol.ts; api.deleteGame() - unit test (Db.delete) + 2 e2e tests (home-screen delete; in-flight delete returns the active tab home and a fresh deep link lands on home) - version 0.7.0 -> 0.8.0 + changelog Fixes #20
This commit is contained in:
+30
@@ -309,6 +309,12 @@ export default function App() {
|
||||
useSse(game?.id ?? null, {
|
||||
onState: applyState,
|
||||
onTurn: applyState,
|
||||
// The game disappeared while this tab had it open — either deleted from
|
||||
// another tab (the server broadcasts an SSE `error`) or a stale deep link to
|
||||
// a missing game (the SSE connect emits `error`). Return this tab home.
|
||||
onError: (msg) => {
|
||||
if (/deleted|not found/i.test(msg)) latestRef.current?.goHome();
|
||||
},
|
||||
});
|
||||
|
||||
const play = useCallback(async (presetId: string) => {
|
||||
@@ -465,6 +471,29 @@ export default function App() {
|
||||
setAdvisors([]);
|
||||
}, []);
|
||||
|
||||
// Delete a saved game. Removes it from the "Saved games" list, clears it if it
|
||||
// was the remembered active game, and heads home if it's the one currently
|
||||
// shown. The server also broadcasts an SSE `error` to any *other* open tab of
|
||||
// this game, which the `useSse` `onError` handler above turns into a go-home.
|
||||
const runDeleteGame = useCallback(
|
||||
async (id: string) => {
|
||||
try {
|
||||
await api.deleteGame(id);
|
||||
} catch (e) {
|
||||
toastError(e);
|
||||
return;
|
||||
}
|
||||
setSavedGames((prev) => prev.filter((g) => g.id !== id));
|
||||
if (activeGameId === id) {
|
||||
clearActiveGameId();
|
||||
setActiveGameId(null);
|
||||
}
|
||||
if (game?.id === id) goHome();
|
||||
toast.success('Game deleted');
|
||||
},
|
||||
[activeGameId, game, goHome],
|
||||
);
|
||||
|
||||
// Expose the latest game + handlers to the mount-once hashchange listener.
|
||||
latestRef.current = { game, goHome, loadGame };
|
||||
|
||||
@@ -509,6 +538,7 @@ export default function App() {
|
||||
savedGames={savedGames}
|
||||
activeGameId={activeGameId}
|
||||
onResume={resumeGame}
|
||||
onDelete={runDeleteGame}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user