Yuki Takei 2 лет назад
Родитель
Сommit
e9b8313c31

+ 4 - 5
apps/app/src/components/Navbar/GlobalSearch.tsx

@@ -9,6 +9,7 @@ import { useTranslation } from 'next-i18next';
 import { useRouter } from 'next/router';
 import { useRouter } from 'next/router';
 
 
 import { IFocusable } from '~/client/interfaces/focusable';
 import { IFocusable } from '~/client/interfaces/focusable';
+import { useKeywordManager } from '~/client/services/search-operation';
 import { IPageWithSearchMeta } from '~/interfaces/search';
 import { IPageWithSearchMeta } from '~/interfaces/search';
 import {
 import {
   useIsSearchScopeChildrenAsDefault, useIsSearchServiceReachable,
   useIsSearchScopeChildrenAsDefault, useIsSearchServiceReachable,
@@ -46,6 +47,8 @@ export const GlobalSearch = (props: GlobalSearchProps): JSX.Element => {
   const [isScopeChildren, setScopeChildren] = useState<boolean|undefined>(isSearchScopeChildrenAsDefault ?? false);
   const [isScopeChildren, setScopeChildren] = useState<boolean|undefined>(isSearchScopeChildrenAsDefault ?? false);
   const [isFocused, setFocused] = useState<boolean>(false);
   const [isFocused, setFocused] = useState<boolean>(false);
 
 
+  const { pushState } = useKeywordManager();
+
   useEffect(() => {
   useEffect(() => {
     setScopeChildren(isSearchScopeChildrenAsDefault);
     setScopeChildren(isSearchScopeChildrenAsDefault);
   }, [isSearchScopeChildrenAsDefault]);
   }, [isSearchScopeChildrenAsDefault]);
@@ -63,17 +66,13 @@ export const GlobalSearch = (props: GlobalSearchProps): JSX.Element => {
   }, [returnPathForURL, router]);
   }, [returnPathForURL, router]);
 
 
   const search = useCallback(() => {
   const search = useCallback(() => {
-    const url = new URL(window.location.href);
-    url.pathname = '/_search';
-
     // construct search query
     // construct search query
     let q = text;
     let q = text;
     if (isScopeChildren) {
     if (isScopeChildren) {
       q += ` prefix:${currentPagePath ?? window.location.pathname}`;
       q += ` prefix:${currentPagePath ?? window.location.pathname}`;
     }
     }
-    url.searchParams.append('q', q);
 
 
-    router.push(url.href);
+    pushState(q);
   }, [currentPagePath, isScopeChildren, router, text]);
   }, [currentPagePath, isScopeChildren, router, text]);
 
 
   const scopeLabel = isScopeChildren
   const scopeLabel = isScopeChildren

+ 5 - 1
apps/app/src/components/Page/RenderTagLabels.tsx

@@ -2,6 +2,8 @@ import React from 'react';
 
 
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 
 
+import { useKeywordManager } from '~/client/services/search-operation';
+
 import { NotAvailableForGuest } from '../NotAvailableForGuest';
 import { NotAvailableForGuest } from '../NotAvailableForGuest';
 import { NotAvailableForReadOnlyUser } from '../NotAvailableForReadOnlyUser';
 import { NotAvailableForReadOnlyUser } from '../NotAvailableForReadOnlyUser';
 
 
@@ -15,6 +17,8 @@ const RenderTagLabels = React.memo((props: RenderTagLabelsProps) => {
   const { tags, isTagLabelsDisabled, openEditorModal } = props;
   const { tags, isTagLabelsDisabled, openEditorModal } = props;
   const { t } = useTranslation();
   const { t } = useTranslation();
 
 
+  const { pushState } = useKeywordManager();
+
   function openEditorHandler() {
   function openEditorHandler() {
     if (openEditorModal == null) {
     if (openEditorModal == null) {
       return;
       return;
@@ -28,7 +32,7 @@ const RenderTagLabels = React.memo((props: RenderTagLabelsProps) => {
     <>
     <>
       {tags.map((tag) => {
       {tags.map((tag) => {
         return (
         return (
-          <a key={tag} href={`/_search?q=tag:${tag}`} className="grw-tag-label badge badge-secondary mr-2">
+          <a key={tag} type="button" className="grw-tag-label badge badge-secondary mr-2" onClick={() => pushState(tag)}>
             {tag}
             {tag}
           </a>
           </a>
         );
         );

+ 10 - 7
apps/app/src/components/Sidebar/RecentChanges.tsx

@@ -6,8 +6,8 @@ import { isPopulated, type IPageHasId } from '@growi/core';
 import { DevidedPagePath } from '@growi/core/dist/models';
 import { DevidedPagePath } from '@growi/core/dist/models';
 import { UserPicture, FootstampIcon } from '@growi/ui/dist/components';
 import { UserPicture, FootstampIcon } from '@growi/ui/dist/components';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
-import Link from 'next/link';
 
 
+import { useKeywordManager } from '~/client/services/search-operation';
 import PagePathHierarchicalLink from '~/components/PagePathHierarchicalLink';
 import PagePathHierarchicalLink from '~/components/PagePathHierarchicalLink';
 import LinkedPagePath from '~/models/linked-page-path';
 import LinkedPagePath from '~/models/linked-page-path';
 import { useSWRINFxRecentlyUpdated } from '~/stores/page-listing';
 import { useSWRINFxRecentlyUpdated } from '~/stores/page-listing';
@@ -30,6 +30,7 @@ type PageItemLowerProps = {
 
 
 type PageItemProps = PageItemLowerProps & {
 type PageItemProps = PageItemLowerProps & {
   isSmall: boolean
   isSmall: boolean
+  onClickTag?: (tagName: string) => void,
 }
 }
 
 
 const PageItemLower = memo(({ page }: PageItemLowerProps): JSX.Element => {
 const PageItemLower = memo(({ page }: PageItemLowerProps): JSX.Element => {
@@ -49,7 +50,7 @@ const PageItemLower = memo(({ page }: PageItemLowerProps): JSX.Element => {
 });
 });
 PageItemLower.displayName = 'PageItemLower';
 PageItemLower.displayName = 'PageItemLower';
 
 
-const PageItem = memo(({ page, isSmall }: PageItemProps): JSX.Element => {
+const PageItem = memo(({ page, isSmall, onClickTag }: PageItemProps): JSX.Element => {
   const dPagePath = new DevidedPagePath(page.path, false, true);
   const dPagePath = new DevidedPagePath(page.path, false, true);
   const linkedPagePathFormer = new LinkedPagePath(dPagePath.former);
   const linkedPagePathFormer = new LinkedPagePath(dPagePath.former);
   const linkedPagePathLatter = new LinkedPagePath(dPagePath.latter);
   const linkedPagePathLatter = new LinkedPagePath(dPagePath.latter);
@@ -70,14 +71,14 @@ const PageItem = memo(({ page, isSmall }: PageItemProps): JSX.Element => {
       return <></>;
       return <></>;
     }
     }
     return (
     return (
-      <Link
+      <a
         key={tag.name}
         key={tag.name}
-        href={`/_search?q=tag:${tag.name}`}
+        type="button"
         className="grw-tag-label badge badge-secondary mr-2 small"
         className="grw-tag-label badge badge-secondary mr-2 small"
-        prefetch={false}
+        onClick={() => onClickTag?.(tag.name)}
       >
       >
         {tag.name}
         {tag.name}
-      </Link>
+      </a>
     );
     );
   });
   });
 
 
@@ -113,6 +114,8 @@ const RecentChanges = (): JSX.Element => {
     data, mutate, isLoading,
     data, mutate, isLoading,
   } = swrInifinitexRecentlyUpdated;
   } = swrInifinitexRecentlyUpdated;
 
 
+  const { pushState } = useKeywordManager();
+
   const [isRecentChangesSidebarSmall, setIsRecentChangesSidebarSmall] = useState(false);
   const [isRecentChangesSidebarSmall, setIsRecentChangesSidebarSmall] = useState(false);
   const isEmpty = data?.[0]?.pages.length === 0;
   const isEmpty = data?.[0]?.pages.length === 0;
   const isReachingEnd = isEmpty || (data != null && data[data.length - 1]?.pages.length < PER_PAGE);
   const isReachingEnd = isEmpty || (data != null && data[data.length - 1]?.pages.length < PER_PAGE);
@@ -162,7 +165,7 @@ const RecentChanges = (): JSX.Element => {
               >
               >
                 { data != null && data.map(apiResult => apiResult.pages).flat()
                 { data != null && data.map(apiResult => apiResult.pages).flat()
                   .map(page => (
                   .map(page => (
-                    <PageItem key={page._id} page={page} isSmall={isRecentChangesSidebarSmall} />
+                    <PageItem key={page._id} page={page} isSmall={isRecentChangesSidebarSmall} onClickTag={tagName => pushState(`tag:${tagName}`)} />
                   ))
                   ))
                 }
                 }
               </InfiniteScroll>
               </InfiniteScroll>

+ 7 - 9
apps/app/src/components/TagCloudBox.tsx

@@ -1,7 +1,6 @@
 import React, { FC, memo } from 'react';
 import React, { FC, memo } from 'react';
 
 
-import Link from 'next/link';
-
+import { useKeywordManager } from '~/client/services/search-operation';
 import { IDataTagCount } from '~/interfaces/tag';
 import { IDataTagCount } from '~/interfaces/tag';
 
 
 
 
@@ -23,21 +22,20 @@ const TagCloudBox: FC<Props> = memo((props:(Props & typeof defaultProps)) => {
   const { tags } = props;
   const { tags } = props;
   const maxTagTextLength: number = props.maxTagTextLength ?? MAX_TAG_TEXT_LENGTH;
   const maxTagTextLength: number = props.maxTagTextLength ?? MAX_TAG_TEXT_LENGTH;
 
 
+  const { pushState } = useKeywordManager();
+
   const tagElements = tags.map((tag:IDataTagCount) => {
   const tagElements = tags.map((tag:IDataTagCount) => {
     const tagNameFormat = (tag.name).length > maxTagTextLength ? `${(tag.name).slice(0, maxTagTextLength)}...` : tag.name;
     const tagNameFormat = (tag.name).length > maxTagTextLength ? `${(tag.name).slice(0, maxTagTextLength)}...` : tag.name;
 
 
-    const url = new URL('/_search', 'https://example.com');
-    url.searchParams.append('q', `tag:${tag.name}`);
-
     return (
     return (
-      <Link
+      <a
         key={tag.name}
         key={tag.name}
-        href={`${url.pathname}${url.search}`}
+        type="button"
         className="grw-tag-label badge badge-secondary mr-2"
         className="grw-tag-label badge badge-secondary mr-2"
-        prefetch={false}
+        onClick={() => pushState(`tag:${tag.name}`)}
       >
       >
         {tagNameFormat}
         {tagNameFormat}
-      </Link>
+      </a>
     );
     );
   });
   });