From 8eef455cc59193be0266612c42a66e5b413b3c38 Mon Sep 17 00:00:00 2001 From: LASER-Yi Date: Tue, 23 Aug 2022 12:45:27 +0800 Subject: [PATCH] Add support of showing confirmation when leaving settings page with unsaved changes --- .../src/pages/Settings/components/Layout.tsx | 13 +++++- frontend/src/utilities/routers.ts | 40 +++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 frontend/src/utilities/routers.ts diff --git a/frontend/src/pages/Settings/components/Layout.tsx b/frontend/src/pages/Settings/components/Layout.tsx index d79cd2eec..87ed5e80b 100644 --- a/frontend/src/pages/Settings/components/Layout.tsx +++ b/frontend/src/pages/Settings/components/Layout.tsx @@ -3,9 +3,10 @@ import { Toolbox } from "@/components"; import { LoadingProvider } from "@/contexts"; import { useOnValueChange } from "@/utilities"; import { LOG } from "@/utilities/console"; +import { usePrompt } from "@/utilities/routers"; import { useUpdateLocalStorage } from "@/utilities/storage"; import { faSave } from "@fortawesome/free-solid-svg-icons"; -import { Container, Group, LoadingOverlay } from "@mantine/core"; +import { Badge, Container, Group, LoadingOverlay } from "@mantine/core"; import { useDocumentTitle, useForm } from "@mantine/hooks"; import { FunctionComponent, ReactNode, useCallback, useMemo } from "react"; import { enabledLanguageKey, languageProfileKey } from "../keys"; @@ -84,6 +85,11 @@ const Layout: FunctionComponent = (props) => { return Object.keys(object).length; }, [form.values.settings, form.values.storages]); + usePrompt( + totalStagedCount > 0, + `You have ${totalStagedCount} unsaved changes, are you sure you want to leave?` + ); + useDocumentTitle(`${name} - Bazarr (Settings)`); if (settings === undefined) { @@ -101,6 +107,11 @@ const Layout: FunctionComponent = (props) => { icon={faSave} loading={isMutating} disabled={totalStagedCount === 0} + rightIcon={ + + } > Save diff --git a/frontend/src/utilities/routers.ts b/frontend/src/utilities/routers.ts new file mode 100644 index 000000000..b3c91c541 --- /dev/null +++ b/frontend/src/utilities/routers.ts @@ -0,0 +1,40 @@ +// A workaround of built-in hooks in React-Router v6 +// https://gist.github.com/rmorse/426ffcc579922a82749934826fa9f743 + +import type { Blocker, History, Transition } from "history"; +import { useContext, useEffect } from "react"; +import { UNSAFE_NavigationContext } from "react-router-dom"; + +export function useBlocker(blocker: Blocker, when = true) { + const navigator = useContext(UNSAFE_NavigationContext).navigator as History; + + useEffect(() => { + if (!when) return; + + const unblock = navigator.block((tx: Transition) => { + const autoUnblockingTx = { + ...tx, + retry() { + // Automatically unblock the transition so it can play all the way + // through before retrying it. TODO: Figure out how to re-enable + // this block if the transition is cancelled for some reason. + unblock(); + tx.retry(); + }, + }; + + blocker(autoUnblockingTx); + }); + + return unblock; + }, [navigator, blocker, when]); +} + +// TODO: Replace with Mantine's confirmation modal +export function usePrompt(when: boolean, message: string) { + useBlocker((tx) => { + if (window.confirm(message)) { + tx.retry(); + } + }, when); +}