Add logic to have optional page specific back button logic

Makes it possible to trigger actions on back button click and optionally prevent the actual back navigation
This commit is contained in:
schroda
2026-03-20 01:28:31 +01:00
parent e8a64c613a
commit 9aae34b8ad
2 changed files with 40 additions and 6 deletions

View File

@@ -6,15 +6,44 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { createContext, useContext } from 'react';
import { createContext, useCallback, useContext, useMemo, useRef } from 'react';
import { useHistory } from '@/base/hooks/useHistory.ts';
import { noOp } from '@/lib/HelperFunctions.ts';
import { useForceUpdate } from '@mantine/hooks';
export const AppPageHistoryContext = createContext<string[]>([]);
interface IAppPageHistoryContext {
history: string[];
onBack: () => Promise<boolean> | boolean;
setOnBack: (subscriber: (() => Promise<boolean> | boolean) | null) => void;
}
export const AppPageHistoryContext = createContext<IAppPageHistoryContext>({
history: [],
onBack: () => true,
setOnBack: noOp,
});
export const useAppPageHistoryContext = () => useContext(AppPageHistoryContext);
export const AppPageHistoryContextProvider = ({ children }: { children: React.ReactNode }) => {
const forceUpdate = useForceUpdate();
const history = useHistory();
return <AppPageHistoryContext.Provider value={history}>{children}</AppPageHistoryContext.Provider>;
const onBackRef = useRef<IAppPageHistoryContext['onBack']>(() => true);
const setOnBack = useCallback((callback: (() => Promise<boolean> | boolean) | null) => {
onBackRef.current = callback ?? (() => true);
forceUpdate();
}, []);
const value = useMemo(
() => ({
history,
onBack: onBackRef.current,
setOnBack,
}),
[history, onBackRef.current, setOnBack],
);
return <AppPageHistoryContext.Provider value={value}>{children}</AppPageHistoryContext.Provider>;
};

View File

@@ -17,9 +17,9 @@ const PAGES_TO_IGNORE: readonly RegExp[] = [READER_REGEX];
export const useBackButton = () => {
const navigate = useNavigate();
const location = useLocation();
const history = useAppPageHistoryContext();
const { history, onBack } = useAppPageHistoryContext();
return useCallback(() => {
return useCallback(async () => {
const getDelta = (historyToCheck: string[] = history, delta: number = 0) => {
const isHistoryEmpty = !historyToCheck.length;
if (isHistoryEmpty) {
@@ -45,6 +45,11 @@ export const useBackButton = () => {
return getDelta(historyToCheck.slice(0, -1), delta - 1);
};
const isAllowedToGoBack = await onBack();
if (!isAllowedToGoBack) {
return;
}
const backDelta = getDelta();
const canNavigateBack = backDelta < 0;
@@ -54,5 +59,5 @@ export const useBackButton = () => {
}
navigate(backDelta);
}, [history, location.pathname]);
}, [history, onBack, location.pathname]);
};