2026-06-04 10:11:00 +03:30
|
|
|
"use client";
|
|
|
|
|
|
|
|
|
|
import { create } from "zustand";
|
|
|
|
|
|
|
|
|
|
export type Screen =
|
|
|
|
|
| "home"
|
|
|
|
|
| "auth"
|
|
|
|
|
| "profile"
|
|
|
|
|
| "friends"
|
|
|
|
|
| "online" // online lobby (create room / play random)
|
|
|
|
|
| "room"
|
|
|
|
|
| "matchmaking"
|
|
|
|
|
| "leaderboard"
|
|
|
|
|
| "shop"
|
2026-06-04 16:28:59 +03:30
|
|
|
| "buycoins"
|
2026-06-04 10:21:44 +03:30
|
|
|
| "chat"
|
2026-06-04 15:52:06 +03:30
|
|
|
| "notifications"
|
2026-06-04 10:11:00 +03:30
|
|
|
| "game"; // the table (used for both ai + online)
|
|
|
|
|
|
2026-06-04 12:56:14 +03:30
|
|
|
const ALL_SCREENS: Screen[] = [
|
|
|
|
|
"home", "auth", "profile", "friends", "online",
|
2026-06-04 16:28:59 +03:30
|
|
|
"room", "matchmaking", "leaderboard", "shop", "buycoins", "chat", "notifications", "game",
|
2026-06-04 12:56:14 +03:30
|
|
|
];
|
|
|
|
|
|
|
|
|
|
/** Screens safe to restore from a URL on a cold load (no transient state needed). */
|
|
|
|
|
export const STATIC_SCREENS: Screen[] = [
|
2026-06-04 16:28:59 +03:30
|
|
|
"home", "auth", "profile", "friends", "online", "leaderboard", "shop", "buycoins", "notifications",
|
2026-06-04 12:56:14 +03:30
|
|
|
];
|
|
|
|
|
|
|
|
|
|
export function screenFromHash(): Screen {
|
|
|
|
|
if (typeof window === "undefined") return "home";
|
|
|
|
|
const h = window.location.hash.replace(/^#\/?/, "");
|
|
|
|
|
return (ALL_SCREENS.includes(h as Screen) ? (h as Screen) : "home");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function urlFor(screen: Screen): string {
|
|
|
|
|
if (typeof window === "undefined") return "/";
|
|
|
|
|
const base = window.location.pathname + window.location.search;
|
|
|
|
|
return screen === "home" ? base : `${base}#/${screen}`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function pushHistory(screen: Screen, replace = false) {
|
|
|
|
|
if (typeof window === "undefined") return;
|
|
|
|
|
const state = { screen };
|
|
|
|
|
if (replace) window.history.replaceState(state, "", urlFor(screen));
|
|
|
|
|
else window.history.pushState(state, "", urlFor(screen));
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-04 10:11:00 +03:30
|
|
|
interface UIStore {
|
|
|
|
|
screen: Screen;
|
|
|
|
|
/** screen to return to from the game table */
|
|
|
|
|
returnTo: Screen;
|
|
|
|
|
dailyModalOpen: boolean;
|
2026-06-04 12:56:14 +03:30
|
|
|
|
2026-06-04 10:11:00 +03:30
|
|
|
go: (screen: Screen) => void;
|
|
|
|
|
goGame: (returnTo?: Screen) => void;
|
2026-06-04 12:56:14 +03:30
|
|
|
/** Browser/hardware back. */
|
|
|
|
|
back: (fallback?: Screen) => void;
|
|
|
|
|
/** Sync from a popstate event (does not touch history). */
|
|
|
|
|
syncFromPop: (screen: Screen) => void;
|
|
|
|
|
/** Establish a home base entry + restore a deep-linked screen on load. */
|
|
|
|
|
initHistory: () => void;
|
|
|
|
|
|
2026-06-04 10:11:00 +03:30
|
|
|
openDaily: () => void;
|
|
|
|
|
closeDaily: () => void;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-04 12:56:14 +03:30
|
|
|
export const useUIStore = create<UIStore>((set, get) => ({
|
2026-06-04 10:11:00 +03:30
|
|
|
screen: "home",
|
|
|
|
|
returnTo: "home",
|
|
|
|
|
dailyModalOpen: false,
|
2026-06-04 12:56:14 +03:30
|
|
|
|
|
|
|
|
go: (screen) => {
|
|
|
|
|
if (get().screen === screen) return;
|
|
|
|
|
pushHistory(screen);
|
|
|
|
|
set({ screen });
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
goGame: (returnTo = "home") => {
|
|
|
|
|
pushHistory("game");
|
|
|
|
|
set({ screen: "game", returnTo });
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
back: (fallback) => {
|
|
|
|
|
if (typeof window !== "undefined") {
|
|
|
|
|
window.history.back();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (fallback) set({ screen: fallback });
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
syncFromPop: (screen) => set({ screen }),
|
|
|
|
|
|
|
|
|
|
initHistory: () => {
|
|
|
|
|
// Always anchor a "home" base entry so back() has somewhere to land.
|
|
|
|
|
pushHistory("home", true);
|
|
|
|
|
const target = screenFromHash();
|
|
|
|
|
if (target !== "home" && STATIC_SCREENS.includes(target)) {
|
|
|
|
|
pushHistory(target);
|
|
|
|
|
set({ screen: target });
|
|
|
|
|
} else {
|
|
|
|
|
set({ screen: "home" });
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
2026-06-04 10:11:00 +03:30
|
|
|
openDaily: () => set({ dailyModalOpen: true }),
|
|
|
|
|
closeDaily: () => set({ dailyModalOpen: false }),
|
|
|
|
|
}));
|