diff --git a/src/features/checker/store/CheckerProvider.tsx b/src/features/checker/store/CheckerProvider.tsx index 07ac3eb6..55afd546 100644 --- a/src/features/checker/store/CheckerProvider.tsx +++ b/src/features/checker/store/CheckerProvider.tsx @@ -1,4 +1,4 @@ -import { PropsWithChildren, useReducer } from "react"; +import { PropsWithChildren, useReducer, useEffect } from "react"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; @@ -15,6 +15,39 @@ const queryClient = new QueryClient(); export const CheckerProvider = ({ children }: PropsWithChildren) => { const [state, dispatch] = useReducer(checkerReducer, initialState); + + useEffect(() => { + // Initialize history state on first load + if (window.history.state === null) { + window.history.replaceState( + { + from: "external", + route: state.route, + }, + "", + ); + } + + const handlePopState = (event: PopStateEvent) => { + if (event.state?.route) { + // If navigating back to external route, let parent app handle it + if (event.state.from === "external") { + return; + } + dispatch({ + type: "SET_ROUTE_HISTORY", + payload: { + route: event.state.route, + replace: true, + }, + }); + } + }; + + window.addEventListener("popstate", handlePopState); + return () => window.removeEventListener("popstate", handlePopState); + }, []); + return ( <QueryClientProvider client={queryClient}> <CheckerContext.Provider value={state}> diff --git a/src/features/checker/store/actions/actions.ts b/src/features/checker/store/actions/actions.ts index 5540854a..9b467cdb 100644 --- a/src/features/checker/store/actions/actions.ts +++ b/src/features/checker/store/actions/actions.ts @@ -6,6 +6,7 @@ import { SetInitialStateAction, SetPoolDataAction, SetPoolFetchStateAction, + SetRouteHistoryAction, } from "./types"; export const setInitialStateAction = ( @@ -48,3 +49,10 @@ export const setPoolFetchStateAction = ( type: "SET_POOL_DATA_FETCH_STATE", payload, }); + +export const setRouteHistoryAction = ( + payload: SetRouteHistoryAction["payload"], +): SetRouteHistoryAction => ({ + type: "SET_ROUTE_HISTORY", + payload, +}); diff --git a/src/features/checker/store/actions/types.ts b/src/features/checker/store/actions/types.ts index 429e3684..53143df6 100644 --- a/src/features/checker/store/actions/types.ts +++ b/src/features/checker/store/actions/types.ts @@ -1,6 +1,6 @@ import { Hex } from "viem"; -import { CheckerPoolData, CheckerPoolFetchState } from "../types"; +import { CheckerContextRoute, CheckerPoolData, CheckerPoolFetchState, CheckerRoute } from "../types"; export interface SetInitialStateAction { type: "SET_INITIAL_STATE"; @@ -43,7 +43,16 @@ export interface SetPoolFetchStateAction { payload: CheckerPoolFetchState; } +export type SetRouteHistoryAction = { + type: "SET_ROUTE_HISTORY"; + payload: { + route: CheckerContextRoute; + replace?: boolean; + }; +}; + export type CheckerAction = + | SetRouteHistoryAction | SetInitialStateAction | SetPoolDataAction | GoToReviewApplicationsAction diff --git a/src/features/checker/store/checkerReducer.ts b/src/features/checker/store/checkerReducer.ts index 65c318b3..826a38f7 100644 --- a/src/features/checker/store/checkerReducer.ts +++ b/src/features/checker/store/checkerReducer.ts @@ -1,7 +1,7 @@ import { generatePoolUUID } from "~checker/utils/generatePoolUUID"; import { CheckerAction } from "./actions"; -import { CheckerContextType, CheckerRoute } from "./types"; +import { CheckerContextRoute, CheckerContextType, CheckerRoute } from "./types"; export const checkerReducer = ( state: CheckerContextType, @@ -22,26 +22,69 @@ export const checkerReducer = ( }, }; } - case "GO_TO_REVIEW_APPLICATIONS": - return { ...state, route: { id: CheckerRoute.ReviewApplications } }; - case "GO_TO_APPLICATION_EVALUATION_OVERVIEW": - return { - ...state, - route: { - id: CheckerRoute.ApplicationEvaluationOverview, - projectId: action.payload.projectId, + case "GO_TO_REVIEW_APPLICATIONS": { + const newRoute: CheckerContextRoute = { id: CheckerRoute.ReviewApplications }; + window.history.pushState( + { + route: newRoute, + from: "internal", }, + "", + ); + return { ...state, route: newRoute }; + } + case "GO_TO_APPLICATION_EVALUATION_OVERVIEW": { + const newRoute: CheckerContextRoute = { + id: CheckerRoute.ApplicationEvaluationOverview, + projectId: action.payload.projectId, }; - case "GO_TO_SUBMIT_APPLICATION_EVALUATION": - return { - ...state, - route: { - id: CheckerRoute.SubmitApplicationEvaluation, - projectId: action.payload.projectId, + window.history.pushState( + { + route: newRoute, + from: "internal", }, + "", + ); + return { ...state, route: newRoute }; + } + case "GO_TO_SUBMIT_APPLICATION_EVALUATION": { + const newRoute: CheckerContextRoute = { + id: CheckerRoute.SubmitApplicationEvaluation, + projectId: action.payload.projectId, }; - case "GO_TO_SUBMIT_FINAL_EVALUATION": - return { ...state, route: { id: CheckerRoute.SubmitFinalEvaluation } }; + window.history.pushState( + { + route: newRoute, + from: "internal", + }, + "", + ); + return { ...state, route: newRoute }; + } + case "GO_TO_SUBMIT_FINAL_EVALUATION": { + const newRoute: CheckerContextRoute = { id: CheckerRoute.SubmitFinalEvaluation }; + window.history.pushState( + { + route: newRoute, + from: "internal", + }, + "", + ); + return { ...state, route: newRoute }; + } + case "SET_ROUTE_HISTORY": { + const { route, replace = false } = action.payload; + if (replace) { + window.history.replaceState( + { + route, + from: "internal", + }, + "", + ); + } + return { ...state, route }; + } case "SET_POOL_DATA": { const { poolId, chainId } = action.payload; const poolUUID = generatePoolUUID(poolId, chainId); diff --git a/src/features/checker/store/types.ts b/src/features/checker/store/types.ts index f9488229..80939eba 100644 --- a/src/features/checker/store/types.ts +++ b/src/features/checker/store/types.ts @@ -53,21 +53,23 @@ export enum CheckerRoute { ApplicationEvaluation = "application-evaluation", } +export type CheckerContextRoute = + | { id: CheckerRoute.SubmitFinalEvaluation } + | { id: CheckerRoute.ReviewApplications } + | { + id: CheckerRoute.ApplicationEvaluationOverview | CheckerRoute.SubmitApplicationEvaluation; + projectId: string; + } + | { + id: CheckerRoute.ApplicationEvaluation; + projectId: string; + }; + export interface CheckerContextType { poolsData: Record<string, CheckerPoolData>; poolsFetchState: Record<string, CheckerPoolFetchState>; poolId?: string; chainId?: number; address?: Hex; - route: - | { id: CheckerRoute.SubmitFinalEvaluation } - | { id: CheckerRoute.ReviewApplications } - | { - id: CheckerRoute.ApplicationEvaluationOverview | CheckerRoute.SubmitApplicationEvaluation; - projectId: string; - } - | { - id: CheckerRoute.ApplicationEvaluation; - projectId: string; - }; + route: CheckerContextRoute; }