Kaynağa Gözat

fix PrivateLegacyPages searching

https://redmine.weseek.co.jp/issues/172177
Yuki Takei 6 ay önce
ebeveyn
işleme
406bebd6ed

+ 6 - 3
apps/app/src/features/search/client/components/PrivateLegacyPages.tsx

@@ -35,6 +35,7 @@ import type { IFormattedSearchResult } from '~/interfaces/search';
 import type { PageMigrationErrorData } from '~/interfaces/websocket';
 import { SocketEventName } from '~/interfaces/websocket';
 import { useIsAdmin } from '~/states/context';
+import { useSearchKeyword, useSetSearchKeyword } from '~/states/search';
 import type { ILegacyPrivatePage } from '~/states/ui/modal/private-legacy-pages-migration';
 import { usePrivateLegacyPagesMigrationModalActions } from '~/states/ui/modal/private-legacy-pages-migration';
 import {
@@ -264,7 +265,9 @@ const PrivateLegacyPages = (): JSX.Element => {
 
   const isAdmin = useIsAdmin();
 
-  const [keyword, setKeyword] = useState<string>(initQ);
+  const keyword = useSearchKeyword();
+  const setSearchKeyword = useSetSearchKeyword('/_private-legacy-pages');
+
   const [offset, setOffset] = useState<number>(0);
   const [limit, setLimit] = useState<number>(INITIAL_PAGING_SIZE);
   const [isOpenConvertModal, setOpenConvertModal] = useState<boolean>(false);
@@ -295,10 +298,10 @@ const PrivateLegacyPages = (): JSX.Element => {
   const searchInvokedHandler = useCallback(
     (_keyword: string) => {
       mutateMigrationStatus();
-      setKeyword(_keyword);
+      setSearchKeyword(_keyword);
       setOffset(0);
     },
-    [mutateMigrationStatus],
+    [mutateMigrationStatus, setSearchKeyword],
   );
 
   const { open: openModal, close: closeModal } =

+ 42 - 21
apps/app/src/features/search/client/components/SearchModal.tsx

@@ -6,6 +6,8 @@ import Downshift, {
 } from 'downshift';
 import { Modal, ModalBody } from 'reactstrap';
 
+import { useSetSearchKeyword } from '~/states/search';
+
 import { isIncludeAiMenthion, removeAiMenthion } from '../../utils/ai';
 import type { DownshiftItem } from '../interfaces/downshift';
 import {
@@ -17,8 +19,15 @@ import { SearchHelp } from './SearchHelp';
 import { SearchMethodMenuItem } from './SearchMethodMenuItem';
 import { SearchResultMenuItem } from './SearchResultMenuItem';
 
-const SearchModalSubstance = (): JSX.Element => {
-  const [searchKeyword, setSearchKeyword] = useState('');
+
+type Props = {
+  onSearch: (keyword: string) => void;
+}
+
+const SearchModalSubstance = (props: Props): JSX.Element => {
+  const { onSearch } = props;
+
+  const [searchInput, setSearchInput] = useState('');
   const [isMenthionedToAi, setMenthionedToAi] = useState(false);
 
   const searchModalData = useSearchModalStatus();
@@ -26,7 +35,7 @@ const SearchModalSubstance = (): JSX.Element => {
   const router = useRouter();
 
   const changeSearchTextHandler = useCallback((searchText: string) => {
-    setSearchKeyword(searchText);
+    setSearchInput(searchText);
   }, []);
 
   const selectSearchMenuItemHandler = useCallback(
@@ -38,11 +47,9 @@ const SearchModalSubstance = (): JSX.Element => {
   );
 
   const submitHandler = useCallback(() => {
-    const url = new URL('_search', 'http://example.com');
-    url.searchParams.set('q', searchKeyword);
-    router.push(url.pathname + url.search);
+    onSearch(searchInput);
     closeSearchModal();
-  }, [closeSearchModal, router, searchKeyword]);
+  }, [closeSearchModal, onSearch, searchInput]);
 
   // Memoize stateReducer to prevent recreation on every render
   const stateReducer = useCallback(
@@ -68,20 +75,20 @@ const SearchModalSubstance = (): JSX.Element => {
       return;
     }
     if (searchModalData?.searchKeyword == null) {
-      setSearchKeyword('');
+      setSearchInput('');
     } else {
-      setSearchKeyword(searchModalData.searchKeyword);
+      setSearchInput(searchModalData.searchKeyword);
     }
   }, [searchModalData?.isOpened, searchModalData?.searchKeyword]);
 
   useEffect(() => {
-    setMenthionedToAi(isIncludeAiMenthion(searchKeyword));
-  }, [searchKeyword]);
+    setMenthionedToAi(isIncludeAiMenthion(searchInput));
+  }, [searchInput]);
 
   // Memoize AI mention removal to prevent recalculation on every render
-  const searchKeywordWithoutAi = useMemo(
-    () => removeAiMenthion(searchKeyword),
-    [searchKeyword],
+  const searchInputWithoutAi = useMemo(
+    () => removeAiMenthion(searchInput),
+    [searchInput],
   );
 
   // Memoize icon selection to prevent recalculation
@@ -115,7 +122,7 @@ const SearchModalSubstance = (): JSX.Element => {
             <div className="text-muted d-flex justify-content-center align-items-center p-1">
               <span className={iconClassName}>{searchIcon}</span>
               <SearchForm
-                searchKeyword={searchKeyword}
+                searchKeyword={searchInput}
                 onChange={changeSearchTextHandler}
                 onSubmit={submitHandler}
                 getInputProps={getInputProps}
@@ -135,12 +142,12 @@ const SearchModalSubstance = (): JSX.Element => {
               <div className="border-top mt-2 mb-2" />
               <SearchMethodMenuItem
                 activeIndex={highlightedIndex}
-                searchKeyword={searchKeywordWithoutAi}
+                searchKeyword={searchInputWithoutAi}
                 getItemProps={getItemProps}
               />
               <SearchResultMenuItem
                 activeIndex={highlightedIndex}
-                searchKeyword={searchKeywordWithoutAi}
+                searchKeyword={searchInputWithoutAi}
                 getItemProps={getItemProps}
               />
               <div className="border-top mt-2 mb-2" />
@@ -154,22 +161,36 @@ const SearchModalSubstance = (): JSX.Element => {
 };
 
 const SearchModal = (): JSX.Element => {
-  const searchModalData = useSearchModalStatus();
+  const { isOpened, onSearchOverride } = useSearchModalStatus();
   const { close: closeSearchModal } = useSearchModalActions();
 
+  const setSearchKeyword = useSetSearchKeyword();
+
+    const searchHandler = useCallback(
+    (keyword: string) => {
+      // invoke override function if exists
+      if (onSearchOverride != null) {
+        onSearchOverride(keyword);
+      } else {
+        setSearchKeyword(keyword);
+      }
+    },
+    [onSearchOverride, setSearchKeyword],
+  );
+
   // Early return for performance optimization
-  if (!searchModalData?.isOpened) {
+  if (!isOpened) {
     return <></>;
   }
 
   return (
     <Modal
       size="lg"
-      isOpen={searchModalData.isOpened}
+      isOpen={isOpened}
       toggle={closeSearchModal}
       data-testid="search-modal"
     >
-      <SearchModalSubstance />
+      <SearchModalSubstance onSearch={searchHandler} />
     </Modal>
   );
 };

+ 15 - 1
apps/app/src/features/search/client/components/SearchPage/SearchControl.tsx

@@ -58,6 +58,20 @@ const SearchControl = React.memo((props: Props): JSX.Element => {
 
   const { t } = useTranslation('');
 
+  const searchBySearchControlHandler = useCallback(
+    (newKeyword: string) => {
+      setKeyword(newKeyword);
+
+      onSearchInvoked?.(newKeyword, {
+        sort,
+        order,
+        includeUserPages,
+        includeTrashPages,
+      });
+    },
+    [includeTrashPages, includeUserPages, onSearchInvoked, order, sort],
+  );
+
   const changeSortHandler = useCallback(
     (nextSort: SORT_AXIS, nextOrder: SORT_ORDER) => {
       setSort(nextSort);
@@ -109,7 +123,7 @@ const SearchControl = React.memo((props: Props): JSX.Element => {
     <div className="shadow-sm">
       <div className="grw-search-page-nav d-flex py-3 align-items-center">
         <div className="flex-grow-1 mx-4">
-          <SearchModalTriggerinput keywordOnInit={keyword} />
+          <SearchModalTriggerinput keywordOnInit={keyword} onSearchInvoked={searchBySearchControlHandler} />
         </div>
       </div>
       {/* TODO: replace the following elements deleteAll button , relevance button and include specificPath button component */}

+ 4 - 3
apps/app/src/features/search/client/components/SearchPage/SearchModalTriggerinput.tsx

@@ -5,16 +5,17 @@ import { useSearchModalActions } from '../../states/modal/search';
 
 type Props = {
   keywordOnInit: string;
+  onSearchInvoked?: (keyword: string) => void;
 };
 
 export const SearchModalTriggerinput: React.FC<Props> = (props: Props) => {
-  const { keywordOnInit } = props;
+  const { keywordOnInit, onSearchInvoked } = props;
 
   const { open: openSearchModal } = useSearchModalActions();
 
   const inputClickHandler = useCallback(() => {
-    openSearchModal(keywordOnInit);
-  }, [openSearchModal, keywordOnInit]);
+    openSearchModal(keywordOnInit, onSearchInvoked);
+  }, [openSearchModal, keywordOnInit, onSearchInvoked]);
 
   return (
     <div className="d-flex align-items-center">

+ 12 - 2
apps/app/src/features/search/client/states/modal/search.ts

@@ -1,13 +1,20 @@
 import { useCallback } from 'react';
 import { atom, useAtomValue, useSetAtom } from 'jotai';
 
+type OnSearch = (keyword: string) => void;
+type OpenSearchModal = (
+  keywordOnInit?: string,
+  onSearchOverride?: OnSearch,
+) => void;
+
 export type SearchModalStatus = {
   isOpened: boolean;
   searchKeyword?: string;
+  onSearchOverride?: OnSearch;
 };
 
 export type SearchModalActions = {
-  open: (keywordOnInit?: string) => void;
+  open: OpenSearchModal;
   close: () => void;
 };
 
@@ -15,6 +22,7 @@ export type SearchModalActions = {
 const searchModalAtom = atom<SearchModalStatus>({
   isOpened: false,
   searchKeyword: undefined,
+  onSearchOverride: undefined,
 });
 
 // Read-only hook (useAtomValue)
@@ -27,10 +35,11 @@ export const useSearchModalActions = (): SearchModalActions => {
   const setStatus = useSetAtom(searchModalAtom);
 
   const open = useCallback(
-    (keywordOnInit?: string) => {
+    (keywordOnInit?: string, onSearchOverride?: OnSearch) => {
       setStatus({
         isOpened: true,
         searchKeyword: keywordOnInit,
+        onSearchOverride,
       });
     },
     [setStatus],
@@ -40,6 +49,7 @@ export const useSearchModalActions = (): SearchModalActions => {
     setStatus({
       isOpened: false,
       searchKeyword: undefined,
+      onSearchOverride: undefined,
     });
   }, [setStatus]);
 

+ 7 - 3
apps/app/src/states/search/keyword-manager.ts

@@ -50,11 +50,15 @@ export const useKeywordManager = (): void => {
   }, [setKeyword]);
 };
 
+type SetSearchKeyword = (newKeyword: string) => void;
+
 /**
  * Hook to set the search keyword and update the URL
  * @returns A function to update the search keyword and push to router history
  */
-export const useSetSearchKeyword = (): ((newKeyword: string) => void) => {
+export const useSetSearchKeyword = (
+  pathname = '/_search',
+): SetSearchKeyword => {
   const router = useRouter();
   const routerRef = useRef(router);
   const setKeyword = useSetAtom(searchKeywordAtom);
@@ -63,7 +67,7 @@ export const useSetSearchKeyword = (): ((newKeyword: string) => void) => {
     (newKeyword: string) => {
       setKeyword((prevKeyword) => {
         if (prevKeyword !== newKeyword) {
-          const newUrl = new URL('/_search', 'http://example.com');
+          const newUrl = new URL(pathname, 'http://example.com');
           newUrl.searchParams.append('q', newKeyword);
           routerRef.current.push(`${newUrl.pathname}${newUrl.search}`, '');
         }
@@ -71,6 +75,6 @@ export const useSetSearchKeyword = (): ((newKeyword: string) => void) => {
         return newKeyword;
       });
     },
-    [setKeyword],
+    [setKeyword, pathname],
   );
 };