From 55695f2189a24c6bcfc1b42518e2054bfb66588f Mon Sep 17 00:00:00 2001 From: Zeng1998 <1129142694@qq.com> Date: Sun, 18 Dec 2022 18:09:12 +0800 Subject: [PATCH] feat: `esc` key to exit multiple dialogs (#692) * fix: `esc` key to exit multiple dialogs * update * update * update * Update web/src/components/Dialog/BaseDialog.tsx Co-authored-by: boojack --- web/src/components/AboutSiteDialog.tsx | 1 + web/src/components/ArchivedMemoDialog.tsx | 1 + .../components/ChangeMemberPasswordDialog.tsx | 1 + .../components/ChangeMemoCreatedTsDialog.tsx | 1 + web/src/components/ChangePasswordDialog.tsx | 1 + .../ChangeResourceFilenameDialog.tsx | 1 + web/src/components/CreateShortcutDialog.tsx | 1 + web/src/components/DailyReviewDialog.tsx | 1 + web/src/components/Dialog/BaseDialog.tsx | 13 ++++++-- web/src/components/Dialog/CommonDialog.tsx | 2 ++ web/src/components/PreviewImageDialog.tsx | 1 + web/src/components/ResourcesDialog.tsx | 3 ++ .../components/ResourcesSelectorDialog.tsx | 1 + web/src/components/SettingDialog.tsx | 1 + web/src/components/Settings/MemberSection.tsx | 2 ++ .../components/Settings/MyAccountSection.tsx | 1 + web/src/components/ShareMemoDialog.tsx | 1 + web/src/components/UpdateAccountDialog.tsx | 1 + web/src/store/index.ts | 2 ++ web/src/store/module/dialog.ts | 23 ++++++++++++++ web/src/store/module/index.ts | 1 + web/src/store/reducer/dialog.ts | 30 +++++++++++++++++++ 22 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 web/src/store/module/dialog.ts create mode 100644 web/src/store/reducer/dialog.ts diff --git a/web/src/components/AboutSiteDialog.tsx b/web/src/components/AboutSiteDialog.tsx index 6aead83..0475989 100644 --- a/web/src/components/AboutSiteDialog.tsx +++ b/web/src/components/AboutSiteDialog.tsx @@ -59,6 +59,7 @@ export default function showAboutSiteDialog(): void { generateDialog( { className: "about-site-dialog", + dialogName: "about-site-dialog", }, AboutSiteDialog ); diff --git a/web/src/components/ArchivedMemoDialog.tsx b/web/src/components/ArchivedMemoDialog.tsx index a68ac57..e5951fb 100644 --- a/web/src/components/ArchivedMemoDialog.tsx +++ b/web/src/components/ArchivedMemoDialog.tsx @@ -69,6 +69,7 @@ export default function showArchivedMemoDialog(): void { generateDialog( { className: "archived-memo-dialog", + dialogName: "archived-memo-dialog", }, ArchivedMemoDialog, {} diff --git a/web/src/components/ChangeMemberPasswordDialog.tsx b/web/src/components/ChangeMemberPasswordDialog.tsx index ce49643..5101f90 100644 --- a/web/src/components/ChangeMemberPasswordDialog.tsx +++ b/web/src/components/ChangeMemberPasswordDialog.tsx @@ -117,6 +117,7 @@ function showChangeMemberPasswordDialog(user: User) { generateDialog( { className: "change-member-password-dialog", + dialogName: "change-member-password-dialog", }, ChangeMemberPasswordDialog, { user } diff --git a/web/src/components/ChangeMemoCreatedTsDialog.tsx b/web/src/components/ChangeMemoCreatedTsDialog.tsx index 02a1889..e8d9fc3 100644 --- a/web/src/components/ChangeMemoCreatedTsDialog.tsx +++ b/web/src/components/ChangeMemoCreatedTsDialog.tsx @@ -97,6 +97,7 @@ function showChangeMemoCreatedTsDialog(memoId: MemoId) { generateDialog( { className: "change-memo-created-ts-dialog", + dialogName: "change-memo-created-ts-dialog", }, ChangeMemoCreatedTsDialog, { diff --git a/web/src/components/ChangePasswordDialog.tsx b/web/src/components/ChangePasswordDialog.tsx index 599eeda..6842c62 100644 --- a/web/src/components/ChangePasswordDialog.tsx +++ b/web/src/components/ChangePasswordDialog.tsx @@ -115,6 +115,7 @@ function showChangePasswordDialog() { generateDialog( { className: "change-password-dialog", + dialogName: "change-password-dialog", }, ChangePasswordDialog ); diff --git a/web/src/components/ChangeResourceFilenameDialog.tsx b/web/src/components/ChangeResourceFilenameDialog.tsx index 82878f9..c7720e7 100644 --- a/web/src/components/ChangeResourceFilenameDialog.tsx +++ b/web/src/components/ChangeResourceFilenameDialog.tsx @@ -87,6 +87,7 @@ function showChangeResourceFilenameDialog(resourceId: ResourceId, resourceFilena generateDialog( { className: "change-resource-filename-dialog", + dialogName: "change-resource-filename-dialog", }, ChangeResourceFilenameDialog, { diff --git a/web/src/components/CreateShortcutDialog.tsx b/web/src/components/CreateShortcutDialog.tsx index ee71d9f..f711a15 100644 --- a/web/src/components/CreateShortcutDialog.tsx +++ b/web/src/components/CreateShortcutDialog.tsx @@ -291,6 +291,7 @@ export default function showCreateShortcutDialog(shortcutId?: ShortcutId): void generateDialog( { className: "create-shortcut-dialog", + dialogName: "create-shortcut-dialog", }, CreateShortcutDialog, { shortcutId } diff --git a/web/src/components/DailyReviewDialog.tsx b/web/src/components/DailyReviewDialog.tsx index 502f94c..3969b80 100644 --- a/web/src/components/DailyReviewDialog.tsx +++ b/web/src/components/DailyReviewDialog.tsx @@ -115,6 +115,7 @@ export default function showDailyReviewDialog(datestamp: DateStamp = Date.now()) generateDialog( { className: "daily-review-dialog", + dialogName: "daily-review-dialog", }, DailyReviewDialog, { currentDateStamp: datestamp } diff --git a/web/src/components/Dialog/BaseDialog.tsx b/web/src/components/Dialog/BaseDialog.tsx index 2373d09..85a76cc 100644 --- a/web/src/components/Dialog/BaseDialog.tsx +++ b/web/src/components/Dialog/BaseDialog.tsx @@ -3,13 +3,15 @@ import { createRoot } from "react-dom/client"; import { Provider } from "react-redux"; import { ANIMATION_DURATION } from "../../helpers/consts"; import store from "../../store"; -import "../../less/base-dialog.less"; +import { useDialogStore } from "../../store/module"; import { CssVarsProvider } from "@mui/joy"; import theme from "../../theme"; +import "../../less/base-dialog.less"; interface DialogConfig { className: string; clickSpaceDestroy?: boolean; + dialogName: string; } interface Props extends DialogConfig, DialogProps { @@ -17,12 +19,17 @@ interface Props extends DialogConfig, DialogProps { } const BaseDialog: React.FC = (props: Props) => { - const { children, className, clickSpaceDestroy, destroy } = props; + const { children, className, clickSpaceDestroy, dialogName, destroy } = props; + const dialogStore = useDialogStore(); useEffect(() => { + dialogStore.pushDialogStack(dialogName); const handleKeyDown = (event: KeyboardEvent) => { if (event.code === "Escape") { - destroy(); + if (dialogName === dialogStore.topDialogStack()) { + dialogStore.popDialogStack(); + destroy(); + } } }; diff --git a/web/src/components/Dialog/CommonDialog.tsx b/web/src/components/Dialog/CommonDialog.tsx index 50d4bfe..dc56a18 100644 --- a/web/src/components/Dialog/CommonDialog.tsx +++ b/web/src/components/Dialog/CommonDialog.tsx @@ -72,6 +72,7 @@ interface CommonDialogProps { content: string; className?: string; style?: DialogStyle; + dialogName: string; closeBtnText?: string; confirmBtnText?: string; onClose?: () => void; @@ -82,6 +83,7 @@ export const showCommonDialog = (props: CommonDialogProps) => { generateDialog( { className: `common-dialog ${props?.className ?? ""}`, + dialogName: `common-dialog ${props?.className ?? ""}`, }, CommonDialog, props diff --git a/web/src/components/PreviewImageDialog.tsx b/web/src/components/PreviewImageDialog.tsx index 5420116..e60f879 100644 --- a/web/src/components/PreviewImageDialog.tsx +++ b/web/src/components/PreviewImageDialog.tsx @@ -124,6 +124,7 @@ export default function showPreviewImageDialog(imgUrls: string[] | string, initi generateDialog( { className: "preview-image-dialog", + dialogName: "preview-image-dialog", }, PreviewImageDialog, { diff --git a/web/src/components/ResourcesDialog.tsx b/web/src/components/ResourcesDialog.tsx index dbd2b8b..e741e6f 100644 --- a/web/src/components/ResourcesDialog.tsx +++ b/web/src/components/ResourcesDialog.tsx @@ -125,6 +125,7 @@ const ResourcesDialog: React.FC = (props: Props) => { title: t("resources.delete-resource"), content: warningText, style: "warning", + dialogName: "delete-unused-resources", onConfirm: async () => { for (const resource of unusedResources) { await resourceStore.deleteResourceById(resource.id); @@ -143,6 +144,7 @@ const ResourcesDialog: React.FC = (props: Props) => { title: t("resources.delete-resource"), content: warningText, style: "warning", + dialogName: "delete-resource-dialog", onConfirm: async () => { await resourceStore.deleteResourceById(resource.id); }, @@ -242,6 +244,7 @@ export default function showResourcesDialog() { generateDialog( { className: "resources-dialog", + dialogName: "resources-dialog", }, ResourcesDialog, {} diff --git a/web/src/components/ResourcesSelectorDialog.tsx b/web/src/components/ResourcesSelectorDialog.tsx index d0ca80f..c1fa300 100644 --- a/web/src/components/ResourcesSelectorDialog.tsx +++ b/web/src/components/ResourcesSelectorDialog.tsx @@ -148,6 +148,7 @@ export default function showResourcesSelectorDialog() { generateDialog( { className: "resources-selector-dialog", + dialogName: "resources-selector-dialog", }, ResourcesSelectorDialog, {} diff --git a/web/src/components/SettingDialog.tsx b/web/src/components/SettingDialog.tsx index 646c2a0..da2df9a 100644 --- a/web/src/components/SettingDialog.tsx +++ b/web/src/components/SettingDialog.tsx @@ -92,6 +92,7 @@ export default function showSettingDialog(): void { generateDialog( { className: "setting-dialog", + dialogName: "setting-dialog", }, SettingDialog, {} diff --git a/web/src/components/Settings/MemberSection.tsx b/web/src/components/Settings/MemberSection.tsx index 79d8399..ab1d3ca 100644 --- a/web/src/components/Settings/MemberSection.tsx +++ b/web/src/components/Settings/MemberSection.tsx @@ -79,6 +79,7 @@ const PreferencesSection = () => { title: `Archive Member`, content: `❗️Are you sure to archive ${user.username}?`, style: "warning", + dialogName: "archive-user-dialog", onConfirm: async () => { await userStore.patchUser({ id: user.id, @@ -102,6 +103,7 @@ const PreferencesSection = () => { title: `Delete Member`, content: `Are you sure to delete ${user.username}? THIS ACTION IS IRREVERSIABLE.❗️`, style: "warning", + dialogName: "delete-user-dialog", onConfirm: async () => { await userStore.deleteUser({ id: user.id, diff --git a/web/src/components/Settings/MyAccountSection.tsx b/web/src/components/Settings/MyAccountSection.tsx index b732fe8..bed7870 100644 --- a/web/src/components/Settings/MyAccountSection.tsx +++ b/web/src/components/Settings/MyAccountSection.tsx @@ -16,6 +16,7 @@ const MyAccountSection = () => { title: "Reset Open API", content: "❗️The existing API will be invalidated and a new one will be generated, are you sure you want to reset?", style: "warning", + dialogName: "reset-openid-dialog", onConfirm: async () => { await userStore.patchUser({ id: user.id, diff --git a/web/src/components/ShareMemoDialog.tsx b/web/src/components/ShareMemoDialog.tsx index b561050..bbb8e4c 100644 --- a/web/src/components/ShareMemoDialog.tsx +++ b/web/src/components/ShareMemoDialog.tsx @@ -190,6 +190,7 @@ export default function showShareMemoDialog(memo: Memo): void { generateDialog( { className: "share-memo-dialog", + dialogName: "share-memo-dialog", }, ShareMemoDialog, { memo } diff --git a/web/src/components/UpdateAccountDialog.tsx b/web/src/components/UpdateAccountDialog.tsx index 01493e9..a2ba615 100644 --- a/web/src/components/UpdateAccountDialog.tsx +++ b/web/src/components/UpdateAccountDialog.tsx @@ -139,6 +139,7 @@ function showUpdateAccountDialog() { generateDialog( { className: "update-account-dialog", + dialogName: "update-account-dialog", }, UpdateAccountDialog ); diff --git a/web/src/store/index.ts b/web/src/store/index.ts index 98349ae..e08ecfa 100644 --- a/web/src/store/index.ts +++ b/web/src/store/index.ts @@ -7,6 +7,7 @@ import editorReducer from "./reducer/editor"; import shortcutReducer from "./reducer/shortcut"; import locationReducer from "./reducer/location"; import resourceReducer from "./reducer/resource"; +import dialogReducer from "./reducer/dialog"; const store = configureStore({ reducer: { @@ -17,6 +18,7 @@ const store = configureStore({ shortcut: shortcutReducer, location: locationReducer, resource: resourceReducer, + dialog: dialogReducer, }, }); diff --git a/web/src/store/module/dialog.ts b/web/src/store/module/dialog.ts new file mode 100644 index 0000000..7143468 --- /dev/null +++ b/web/src/store/module/dialog.ts @@ -0,0 +1,23 @@ +import store, { useAppSelector } from ".."; +import { pushDialogStack, popDialogStack } from "../reducer/dialog"; +import { last } from "lodash"; + +export const useDialogStore = () => { + const state = useAppSelector((state) => state.editor); + + return { + state, + getState: () => { + return store.getState().dialog; + }, + pushDialogStack: (dialogName: string) => { + store.dispatch(pushDialogStack(dialogName)); + }, + popDialogStack: () => { + store.dispatch(popDialogStack()); + }, + topDialogStack: () => { + return last(store.getState().dialog.dialogStack); + }, + }; +}; diff --git a/web/src/store/module/index.ts b/web/src/store/module/index.ts index 95a237f..72614a9 100644 --- a/web/src/store/module/index.ts +++ b/web/src/store/module/index.ts @@ -5,3 +5,4 @@ export * from "./memo"; export * from "./resource"; export * from "./shortcut"; export * from "./user"; +export * from "./dialog"; diff --git a/web/src/store/reducer/dialog.ts b/web/src/store/reducer/dialog.ts new file mode 100644 index 0000000..dce4de8 --- /dev/null +++ b/web/src/store/reducer/dialog.ts @@ -0,0 +1,30 @@ +import { createSlice, PayloadAction } from "@reduxjs/toolkit"; + +interface State { + dialogStack: string[]; +} + +const dialogSlice = createSlice({ + name: "dialog", + initialState: { + dialogStack: [], + } as State, + reducers: { + pushDialogStack: (state, action: PayloadAction) => { + return { + ...state, + dialogStack: [...state.dialogStack, action.payload], + }; + }, + popDialogStack: (state) => { + return { + ...state, + dialogStack: state.dialogStack.slice(0, state.dialogStack.length - 1), + }; + }, + }, +}); + +export const { pushDialogStack, popDialogStack } = dialogSlice.actions; + +export default dialogSlice.reducer;