import { createContext, useCallback, useContext, useMemo, useRef, useState, type ReactNode, } from "react"; export interface RouteState { route: string; previousRoute: string | null; history: string[]; } interface RouteStoreContextValue extends RouteState { navigate: (nextRoute: string) => void; replace: (nextRoute: string) => void; back: () => boolean; reset: () => void; canGoBack: boolean; } const RouteStoreContext = createContext(null); export interface RouteStoreProviderProps { children: ReactNode; initialRoute?: string; } export function createInitialRouteState(initialRoute: string): RouteState { return { route: initialRoute, previousRoute: null, history: [initialRoute], }; } export function applyNavigate(state: RouteState, nextRoute: string): RouteState { return { route: nextRoute, previousRoute: state.route, history: [...state.history, nextRoute], }; } export function applyReplace(state: RouteState, nextRoute: string): RouteState { const nextHistory = state.history.length > 0 ? [...state.history] : [nextRoute]; if (nextHistory.length === 0) { nextHistory.push(nextRoute); } else { nextHistory[nextHistory.length - 1] = nextRoute; } return { route: nextRoute, previousRoute: state.route, history: nextHistory, }; } export function applyBack(state: RouteState): RouteState { if (!canApplyBack(state)) { return state; } const nextHistory = state.history.slice(0, -1); const nextRoute = nextHistory[nextHistory.length - 1] ?? state.route; return { route: nextRoute, previousRoute: state.route, history: nextHistory, }; } export function canApplyBack(state: RouteState): boolean { return state.history.length > 1; } export function RouteStoreProvider({ children, initialRoute = "home" }: RouteStoreProviderProps) { const initialRouteRef = useRef(initialRoute); const initialState = useMemo(() => createInitialRouteState(initialRoute), [initialRoute]); const stateRef = useRef(initialState); const [state, setState] = useState(initialState); const updateState = useCallback((updater: (current: RouteState) => RouteState) => { const next = updater(stateRef.current); stateRef.current = next; setState(next); }, []); const navigate = useCallback( (nextRoute: string) => { updateState((prev) => applyNavigate(prev, nextRoute)); }, [updateState], ); const replace = useCallback( (nextRoute: string) => { updateState((prev) => applyReplace(prev, nextRoute)); }, [updateState], ); const back = useCallback((): boolean => { if (!canApplyBack(stateRef.current)) { return false; } updateState((prev) => applyBack(prev)); return true; }, [updateState]); const reset = useCallback(() => { updateState(() => createInitialRouteState(initialRouteRef.current)); }, [updateState]); const value = useMemo( () => ({ ...state, navigate, replace, back, reset, canGoBack: state.history.length > 1, }), [back, navigate, replace, reset, state], ); return {children}; } export interface RouteStore { route: string; previousRoute: string | null; history: string[]; navigate: (nextRoute: string) => void; replace: (nextRoute: string) => void; back: () => boolean; reset: () => void; canGoBack: boolean; } export function useRouteStore(): RouteStore { const context = useContext(RouteStoreContext); if (!context) { throw new Error("Route store is not available. Wrap your app in ."); } return context; }