Add History API routing so browser/Android back navigates screens

- ui-store syncs screens to history (push on go/goGame, hash URLs like #/profile),
  back() = history.back(), initHistory anchors a home base + restores deep links
- page.tsx listens to popstate; resolveScreen() guards transient screens
  (game/room/chat/matchmaking fall back to home when their state is gone)
- ScreenHeader + ChatScreen back buttons use history back; chat cleans up on unmount

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-04 12:56:14 +03:30
parent 0a3ffa6314
commit a3b797c8a3
4 changed files with 106 additions and 11 deletions
+27 -1
View File
@@ -14,7 +14,25 @@ import { ChatScreen } from "@/components/screens/ChatScreen";
import { AuthScreen } from "@/components/screens/AuthScreen";
import { DailyRewardModal } from "@/components/online/DailyRewardModal";
import { useSessionStore } from "@/lib/session-store";
import { useUIStore } from "@/lib/ui-store";
import { useGameStore } from "@/lib/game-store";
import { useOnlineStore } from "@/lib/online-store";
import { screenFromHash, useUIStore, type Screen } from "@/lib/ui-store";
/** Transient screens can't be restored without their state — fall back to home. */
function resolveScreen(s: Screen): Screen {
switch (s) {
case "game":
return useGameStore.getState().started ? s : "home";
case "room":
return useOnlineStore.getState().room ? s : "home";
case "chat":
return useOnlineStore.getState().activeChatFriend ? s : "home";
case "matchmaking":
return useOnlineStore.getState().matchmaking.phase !== "idle" ? s : "home";
default:
return s;
}
}
export default function Page() {
const screen = useUIStore((s) => s.screen);
@@ -23,6 +41,14 @@ export default function Page() {
useEffect(() => {
init();
useUIStore.getState().initHistory();
const onPop = (e: PopStateEvent) => {
const raw = ((e.state?.screen as Screen) ?? screenFromHash());
useUIStore.getState().syncFromPop(resolveScreen(raw));
};
window.addEventListener("popstate", onPop);
return () => window.removeEventListener("popstate", onPop);
}, [init]);
return (