From 36d235eeb8d08c841d475db2a354ad05d3f268d7 Mon Sep 17 00:00:00 2001 From: Innei Date: Tue, 3 Dec 2024 22:04:01 +0800 Subject: [PATCH] fix: attempt to fix the page freeze issue when re-opening modal after closing Signed-off-by: Innei --- .../src/components/ui/modal/stacked/hooks.tsx | 84 ++++++++++--------- .../ui/modal/stacked/internal/use-animate.ts | 8 +- .../src/components/ui/modal/stacked/modal.tsx | 18 ++-- apps/renderer/src/modules/app/Titlebar.tsx | 2 + 4 files changed, 64 insertions(+), 48 deletions(-) diff --git a/apps/renderer/src/components/ui/modal/stacked/hooks.tsx b/apps/renderer/src/components/ui/modal/stacked/hooks.tsx index 12d898d8cb..b8b9bd1bfa 100644 --- a/apps/renderer/src/components/ui/modal/stacked/hooks.tsx +++ b/apps/renderer/src/components/ui/modal/stacked/hooks.tsx @@ -23,50 +23,52 @@ export const useModalStack = (options?: ModalStackOptions) => { const currentCount = useRef(0) const { wrapper } = options || {} - const presentSync = (props: ModalProps & { id?: string }) => { - const fallbackModelId = `${id}-${++currentCount.current}` - const modalId = props.id ?? fallbackModelId - - const currentStack = jotaiStore.get(modalStackAtom) - - const existingModal = currentStack.find((item) => item.id === modalId) - if (existingModal) { - // Move to top - jotaiStore.set(modalStackAtom, (p) => { - const index = p.indexOf(existingModal) - return [...p.slice(0, index), ...p.slice(index + 1), existingModal] - }) - } else { - // NOTE: The props of the Command Modal are immutable, so we'll just take the store value and inject it. - // There is no need to inject `overlay` props, this is rendered responsively based on ui changes. - const uiSettings = getUISettings() - const modalConfig: Partial = { - draggable: uiSettings.modalDraggable, - modal: true, - } - jotaiStore.set(modalStackAtom, (p) => { - const modalProps: ModalProps = { - ...modalConfig, - ...props, + return { + present: useEventCallback((props: ModalProps & { id?: string }) => { + const presentSync = (props: ModalProps & { id?: string }) => { + const fallbackModelId = `${id}-${++currentCount.current}` + const modalId = props.id ?? fallbackModelId + + const currentStack = jotaiStore.get(modalStackAtom) + + const existingModal = currentStack.find((item) => item.id === modalId) + + if (existingModal) { + // Move to top + jotaiStore.set(modalStackAtom, (p) => { + const index = p.indexOf(existingModal) + return [...p.slice(0, index), ...p.slice(index + 1), existingModal] + }) + } else { + // NOTE: The props of the Command Modal are immutable, so we'll just take the store value and inject it. + // There is no need to inject `overlay` props, this is rendered responsively based on ui changes. + const uiSettings = getUISettings() + const modalConfig: Partial = { + draggable: uiSettings.modalDraggable, + modal: true, + } + jotaiStore.set(modalStackAtom, (p) => { + const modalProps: ModalProps = { + ...modalConfig, + ...props, + + wrapper, + } + modalIdToPropsMap[modalId] = modalProps + return p.concat({ + id: modalId, + ...modalProps, + }) + }) + } - wrapper, + return () => { + jotaiStore.set(modalStackAtom, (p) => p.filter((item) => item.id !== modalId)) } - modalIdToPropsMap[modalId] = modalProps - return p.concat({ - id: modalId, - ...modalProps, - }) - }) - } + } - return () => { - jotaiStore.set(modalStackAtom, (p) => p.filter((item) => item.id !== modalId)) - } - } - return { - present: useEventCallback((props: ModalProps & { id?: string }) => - nextFrame(() => presentSync(props)), - ), + return nextFrame(() => presentSync(props)) + }), ...actions, } diff --git a/apps/renderer/src/components/ui/modal/stacked/internal/use-animate.ts b/apps/renderer/src/components/ui/modal/stacked/internal/use-animate.ts index 212696892a..68f77f64ca 100644 --- a/apps/renderer/src/components/ui/modal/stacked/internal/use-animate.ts +++ b/apps/renderer/src/components/ui/modal/stacked/internal/use-animate.ts @@ -1,6 +1,7 @@ import { nextFrame } from "@follow/utils/dom" import { useAnimationControls } from "framer-motion" -import { useCallback, useEffect } from "react" +import { useCallback, useEffect, useLayoutEffect } from "react" +import { useEventCallback } from "usehooks-ts" import { modalMontionConfig } from "../constants" @@ -30,7 +31,7 @@ export const useModalAnimate = (isTop: boolean) => { }) }, [animateController]) - useEffect(() => { + useLayoutEffect(() => { if (isTop) return animateController.start({ scale: 0.96, @@ -52,5 +53,8 @@ export const useModalAnimate = (isTop: boolean) => { return { noticeModal, animateController, + dismissing: useEventCallback(async () => { + await animateController.start(modalMontionConfig.exit) + }), } } diff --git a/apps/renderer/src/components/ui/modal/stacked/modal.tsx b/apps/renderer/src/components/ui/modal/stacked/modal.tsx index 66dc1f0f13..d0dd389876 100644 --- a/apps/renderer/src/components/ui/modal/stacked/modal.tsx +++ b/apps/renderer/src/components/ui/modal/stacked/modal.tsx @@ -86,13 +86,22 @@ export const ModalInternal = memo( const setStack = useSetAtom(modalStackAtom) const [currentIsClosing, setCurrentIsClosing] = useState(false) + const { noticeModal, animateController, dismissing } = useModalAnimate(!!isTop) const close = useEventCallback((forceClose = false) => { if (!canClose && !forceClose) return setCurrentIsClosing(true) - nextFrame(() => { - setStack((p) => p.filter((modal) => modal.id !== item.id)) - }) + + if (!CustomModalComponent) { + dismissing().then(() => { + setStack((p) => p.filter((modal) => modal.id !== item.id)) + setCurrentIsClosing(false) + }) + } else { + nextFrame(() => { + setStack((p) => p.filter((modal) => modal.id !== item.id)) + }) + } onPropsClose?.(false) }) @@ -133,8 +142,6 @@ export const ModalInternal = memo( draggable, }) - const { noticeModal, animateController } = useModalAnimate(!!isTop) - const getIndex = useEventCallback(() => index) const [modalContentRef, setModalContentRef] = useState(null) const ModalProps: ModalActionsInternal = useMemo( @@ -298,6 +305,7 @@ export const ModalInternal = memo( >
{ return (