From a3b797c8a36ded49b3e85b6181937b00bc33de97 Mon Sep 17 00:00:00 2001 From: "soroush.asadi" Date: Thu, 4 Jun 2026 12:56:14 +0330 Subject: [PATCH] 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 --- src/app/page.tsx | 28 +++++++++- src/components/online/ScreenHeader.tsx | 4 +- src/components/screens/ChatScreen.tsx | 10 ++-- src/lib/ui-store.ts | 75 ++++++++++++++++++++++++-- 4 files changed, 106 insertions(+), 11 deletions(-) diff --git a/src/app/page.tsx b/src/app/page.tsx index e7e9be0..75df668 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -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 ( diff --git a/src/components/online/ScreenHeader.tsx b/src/components/online/ScreenHeader.tsx index ca1d906..5c8c48f 100644 --- a/src/components/online/ScreenHeader.tsx +++ b/src/components/online/ScreenHeader.tsx @@ -13,13 +13,13 @@ export function ScreenHeader({ back?: Screen; right?: React.ReactNode; }) { - const go = useUIStore((s) => s.go); + const navBack = useUIStore((s) => s.back); const { locale } = useI18n(); const Chevron = locale === "fa" ? ChevronRight : ChevronLeft; return (