diff --git a/apps/client/src/features/editor/components/search-and-replace/search-and-replace-dialog.tsx b/apps/client/src/features/editor/components/search-and-replace/search-and-replace-dialog.tsx index 834a949ed..f473f8ff5 100644 --- a/apps/client/src/features/editor/components/search-and-replace/search-and-replace-dialog.tsx +++ b/apps/client/src/features/editor/components/search-and-replace/search-and-replace-dialog.tsx @@ -181,10 +181,10 @@ function SearchAndReplaceDialog({ editor, editable = true }: PageFindDialogDialo const location = useLocation(); useEffect(() => { - if(pageFindState.isOpen){ + if (pageFindState.isOpen) { closeDialog(); } - }, [location]); + }, [location.pathname]); return ( ; @@ -18,8 +23,11 @@ interface SearchNavigationEvent extends CustomEvent { } function SearchNavigationDialog({ editor }: SearchNavigationDialogProps) { - const {t} = useTranslation() + const { t } = useTranslation(); + const location = useLocation(); + const navigate = useNavigate(); const [open, setOpen] = useState(false); + const openRef = useRef(false); const [resultState, setResultState] = useState({ resultIndex: 0, resultsLength: 0, @@ -56,6 +64,28 @@ function SearchNavigationDialog({ editor }: SearchNavigationDialogProps) { goToSelection(); }; + const close = useCallback(() => { + if (!openRef.current) return; + + openRef.current = false; + setOpen(false); + if (isEditorReady(editor)) { + editor.commands.setSearchTerms([""]); + } + const nextParams = new URLSearchParams(location.search); + nextParams.delete("q"); + nextParams.delete("m"); + const nextSearch = nextParams.toString(); + navigate( + { + pathname: location.pathname, + search: nextSearch ? `?${nextSearch}` : "", + hash: location.hash, + }, + { replace: true }, + ); + }, [editor, location.hash, location.pathname, location.search, navigate]); + useEffect(() => { const handleOpen = (event: Event) => { const { searchTerms: terms, wholeWord = true } = ( @@ -64,12 +94,19 @@ function SearchNavigationDialog({ editor }: SearchNavigationDialogProps) { if (!terms?.length || !isEditorReady(editor)) return; - setOpen(true); + openRef.current = false; editor.commands.setSearchTerms(terms); editor.commands.setWholeWord(wholeWord); editor.commands.resetIndex(); const { results, resultIndex } = editor.storage.searchAndReplace; + openRef.current = true; + if (results.length === 0) { + close(); + return; + } + + setOpen(true); setResultState({ resultIndex, resultsLength: results.length, @@ -79,25 +116,29 @@ function SearchNavigationDialog({ editor }: SearchNavigationDialogProps) { }; const handleClose = () => { - setOpen(false); + if (openRef.current) { + close(); + } }; document.addEventListener("openSearchNavigationDialog", handleOpen); document.addEventListener("openFindDialogFromEditor", handleClose); + document.addEventListener("closeFindDialogFromEditor", handleClose); return () => { document.removeEventListener("openSearchNavigationDialog", handleOpen); document.removeEventListener("openFindDialogFromEditor", handleClose); + document.removeEventListener("closeFindDialogFromEditor", handleClose); }; - }, [editor]); + }, [close, editor]); useEffect(() => { const handleTransaction = () => { - if (!open || editor.isDestroyed) return; + if (!openRef.current || editor.isDestroyed) return; const { results } = editor.storage.searchAndReplace; if (results.length === 0) { - setOpen(false); + close(); } }; @@ -105,12 +146,7 @@ function SearchNavigationDialog({ editor }: SearchNavigationDialogProps) { return () => { editor.off("transaction", handleTransaction); }; - }, [editor, open]); - - const close = () => { - editor.commands.setSearchTerms([""]); - setOpen(false); - }; + }, [close, editor]); return ( ; + isSynced: boolean; + pageId: string; + searchParams: URLSearchParams; + showStatic: boolean; +} + +export function useSearchNavigationParams({ + editor, + isSynced, + pageId, + searchParams, + showStatic, +}: UseSearchNavigationParamsProps) { + const appliedSearchKeyRef = useRef(null); + const searchKey = `${pageId}:${searchParams.toString()}`; + + useEffect(() => { + const searchQueries = searchParams.getAll("q"); + if (!searchQueries.length) { + appliedSearchKeyRef.current = null; + return; + } + + if ( + !editor || + editor.isDestroyed || + !editor.view.dom.isConnected || + appliedSearchKeyRef.current === searchKey + ) { + return; + } + + const match = searchParams.get("m"); + appliedSearchKeyRef.current = searchKey; + + document.dispatchEvent( + new CustomEvent("openSearchNavigationDialog", { + detail: { searchTerms: searchQueries, wholeWord: match === "whole" }, + }), + ); + }, [editor, isSynced, searchKey, searchParams, showStatic]); +} diff --git a/apps/client/src/features/editor/page-editor.tsx b/apps/client/src/features/editor/page-editor.tsx index e7dde321f..2cf3584e8 100644 --- a/apps/client/src/features/editor/page-editor.tsx +++ b/apps/client/src/features/editor/page-editor.tsx @@ -62,6 +62,7 @@ import DrawioMenu from "./components/drawio/drawio-menu"; import { useCollabToken } from "@/features/auth/queries/auth-query.tsx"; import SearchAndReplaceDialog from "@/features/editor/components/search-and-replace/search-and-replace-dialog.tsx"; import SearchNavigationDialog from "@/features/editor/components/search-and-replace/search-navigation-dialog.tsx"; +import { useSearchNavigationParams } from "@/features/editor/components/search-and-replace/use-search-navigation-params.ts"; import { useDebouncedCallback, useDocumentVisibility } from "@mantine/hooks"; import { useIdle } from "@/hooks/use-idle.ts"; import { queryClient } from "@/main.tsx"; @@ -417,31 +418,13 @@ function CollabPageEditor({ const hasConnectedOnceRef = useRef(false); const [showStatic, setShowStatic] = useState(true); - const appliedSearchKeyRef = useRef(null); - const searchKey = `${pageId}:${searchParams.toString()}`; - - useEffect(() => { - if ( - !editor || - editor.isDestroyed || - !editor.view.dom.isConnected || - appliedSearchKeyRef.current === searchKey - ) { - return; - } - - const searchQueries = searchParams.getAll("q"); - const match = searchParams.get("m"); - if (!searchQueries.length) return; - - appliedSearchKeyRef.current = searchKey; - - document.dispatchEvent( - new CustomEvent("openSearchNavigationDialog", { - detail: { searchTerms: searchQueries, wholeWord: match === "whole" }, - }) - ); - }, [editor, isSynced, showStatic, searchKey, searchParams]); + useSearchNavigationParams({ + editor, + isSynced, + pageId, + searchParams, + showStatic, + }); useEffect(() => { if ( diff --git a/apps/server/src/ee b/apps/server/src/ee index 13ca295ed..5e7120dcc 160000 --- a/apps/server/src/ee +++ b/apps/server/src/ee @@ -1 +1 @@ -Subproject commit 13ca295ed9e13c44994f3e9805f09583668b31e3 +Subproject commit 5e7120dcc86a344f5af09ccda0a29d5784659938