Browse Source

configure biome for app services and stores dir

Futa Arai 5 months ago
parent
commit
5645e1f471
37 changed files with 1866 additions and 1108 deletions
  1. 1 0
      apps/app/.eslintrc.js
  2. 0 1
      apps/app/src/services/renderer/rehype-plugins/relative-links-by-pukiwiki-like-linker.spec.ts
  3. 0 1
      apps/app/src/services/renderer/rehype-plugins/relative-links.ts
  4. 14 4
      apps/app/src/stores/activity.ts
  5. 2 3
      apps/app/src/stores/admin/app-settings.tsx
  6. 20 22
      apps/app/src/stores/admin/customize.tsx
  7. 37 28
      apps/app/src/stores/admin/sidebar-config.tsx
  8. 30 20
      apps/app/src/stores/alert.tsx
  9. 47 37
      apps/app/src/stores/attachment.tsx
  10. 7 5
      apps/app/src/stores/bookmark-folder.ts
  11. 25 15
      apps/app/src/stores/bookmark.ts
  12. 51 38
      apps/app/src/stores/comment.tsx
  13. 48 30
      apps/app/src/stores/editor.tsx
  14. 18 13
      apps/app/src/stores/global-notification.ts
  15. 30 25
      apps/app/src/stores/in-app-notification.ts
  16. 14 9
      apps/app/src/stores/maintenanceMode.tsx
  17. 3 2
      apps/app/src/stores/middlewares/user.ts
  18. 436 277
      apps/app/src/stores/modal.tsx
  19. 118 78
      apps/app/src/stores/page-listing.tsx
  20. 3 2
      apps/app/src/stores/page-redirect.tsx
  21. 14 9
      apps/app/src/stores/page-timeline.tsx
  22. 183 106
      apps/app/src/stores/page.tsx
  23. 70 46
      apps/app/src/stores/personal-settings.tsx
  24. 50 28
      apps/app/src/stores/remote-latest-page.ts
  25. 85 37
      apps/app/src/stores/renderer.tsx
  26. 42 35
      apps/app/src/stores/search.tsx
  27. 7 3
      apps/app/src/stores/share-link.tsx
  28. 0 1
      apps/app/src/stores/socket-io.ts
  29. 2 3
      apps/app/src/stores/staff.tsx
  30. 15 4
      apps/app/src/stores/tag.tsx
  31. 287 134
      apps/app/src/stores/ui.tsx
  32. 6 2
      apps/app/src/stores/use-editing-clients.ts
  33. 93 31
      apps/app/src/stores/user-group.tsx
  34. 45 28
      apps/app/src/stores/user.tsx
  35. 21 9
      apps/app/src/stores/websocket.tsx
  36. 42 20
      apps/app/src/stores/yjs.ts
  37. 0 2
      biome.json

+ 1 - 0
apps/app/.eslintrc.js

@@ -50,6 +50,7 @@ module.exports = {
     'src/interfaces/**',
     'src/utils/**',
     'src/services/**',
+    'src/stores/**',
   ],
   settings: {
     // resolve path aliases by eslint-import-resolver-typescript

+ 0 - 1
apps/app/src/services/renderer/rehype-plugins/relative-links-by-pukiwiki-like-linker.spec.ts

@@ -5,7 +5,6 @@ import rehype from 'remark-rehype';
 import { unified } from 'unified';
 
 import { pukiwikiLikeLinker } from '../remark-plugins/pukiwiki-like-linker';
-
 import { relativeLinksByPukiwikiLikeLinker } from './relative-links-by-pukiwiki-like-linker';
 
 describe('relativeLinksByPukiwikiLikeLinker', () => {

+ 0 - 1
apps/app/src/services/renderer/rehype-plugins/relative-links.ts

@@ -1,5 +1,4 @@
 import assert from 'assert';
-
 import type { Element, Nodes as HastNode } from 'hast';
 import { selectAll } from 'hast-util-select';
 import isAbsolute from 'is-absolute-url';

+ 14 - 4
apps/app/src/stores/activity.ts

@@ -6,13 +6,23 @@ import type { IActivityHasId, ISearchFilter } from '~/interfaces/activity';
 import type { PaginateResult } from '~/interfaces/mongoose-utils';
 import { useAuditLogEnabled } from '~/stores-universal/context';
 
-export const useSWRxActivity = (limit?: number, offset?: number, searchFilter?: ISearchFilter): SWRResponse<PaginateResult<IActivityHasId>, Error> => {
+export const useSWRxActivity = (
+  limit?: number,
+  offset?: number,
+  searchFilter?: ISearchFilter,
+): SWRResponse<PaginateResult<IActivityHasId>, Error> => {
   const { data: auditLogEnabled } = useAuditLogEnabled();
 
   const stringifiedSearchFilter = JSON.stringify(searchFilter);
   return useSWRImmutable(
-    auditLogEnabled ? ['/activity', limit, offset, stringifiedSearchFilter] : null,
-    ([endpoint, limit, offset, stringifiedSearchFilter]) => apiv3Get(endpoint, { limit, offset, searchFilter: stringifiedSearchFilter })
-      .then(result => result.data.serializedPaginationResult),
+    auditLogEnabled
+      ? ['/activity', limit, offset, stringifiedSearchFilter]
+      : null,
+    ([endpoint, limit, offset, stringifiedSearchFilter]) =>
+      apiv3Get(endpoint, {
+        limit,
+        offset,
+        searchFilter: stringifiedSearchFilter,
+      }).then((result) => result.data.serializedPaginationResult),
   );
 };

+ 2 - 3
apps/app/src/stores/admin/app-settings.tsx

@@ -5,9 +5,8 @@ import { apiv3Get } from '~/client/util/apiv3-client';
 import type { IResAppSettings } from '~/interfaces/res/admin/app-settings';
 
 export const useSWRxAppSettings = (): SWRResponse<IResAppSettings, Error> => {
-  return useSWR(
-    '/app-settings/',
-    endpoint => apiv3Get(endpoint).then((response) => {
+  return useSWR('/app-settings/', (endpoint) =>
+    apiv3Get(endpoint).then((response) => {
       return response.data.appSettingsParams;
     }),
   );

+ 20 - 22
apps/app/src/stores/admin/customize.tsx

@@ -1,26 +1,28 @@
 import { useCallback } from 'react';
-
 import type { SWRResponse } from 'swr';
 import useSWR from 'swr';
 import useSWRImmutable from 'swr/immutable';
 
 import { apiv3Get, apiv3Put } from '~/client/util/apiv3-client';
 import type { updateConfigMethodForAdmin } from '~/interfaces/admin';
-import type { IResLayoutSetting, IResGrowiTheme } from '~/interfaces/customize';
-
-export const useSWRxLayoutSetting = (): SWRResponse<IResLayoutSetting, Error> & updateConfigMethodForAdmin<IResLayoutSetting> => {
+import type { IResGrowiTheme, IResLayoutSetting } from '~/interfaces/customize';
 
-  const fetcher = useCallback(async() => {
+export const useSWRxLayoutSetting = (): SWRResponse<IResLayoutSetting, Error> &
+  updateConfigMethodForAdmin<IResLayoutSetting> => {
+  const fetcher = useCallback(async () => {
     const res = await apiv3Get('/customize-setting/layout');
     return res.data;
   }, []);
 
   const swrResponse = useSWRImmutable('/customize-setting/layout', fetcher);
 
-  const update = useCallback(async(layoutSetting: IResLayoutSetting) => {
-    await apiv3Put('/customize-setting/layout', layoutSetting);
-    await swrResponse.mutate();
-  }, [swrResponse]);
+  const update = useCallback(
+    async (layoutSetting: IResLayoutSetting) => {
+      await apiv3Put('/customize-setting/layout', layoutSetting);
+      await swrResponse.mutate();
+    },
+    [swrResponse],
+  );
 
   return {
     ...swrResponse,
@@ -29,19 +31,18 @@ export const useSWRxLayoutSetting = (): SWRResponse<IResLayoutSetting, Error> &
 };
 
 type UpdateThemeArgs = {
-  theme: string,
-}
-export const useSWRxGrowiThemeSetting = (): SWRResponse<IResGrowiTheme, Error> & updateConfigMethodForAdmin<UpdateThemeArgs> => {
-
-  const fetcher = useCallback(async() => {
+  theme: string;
+};
+export const useSWRxGrowiThemeSetting = (): SWRResponse<IResGrowiTheme, Error> &
+  updateConfigMethodForAdmin<UpdateThemeArgs> => {
+  const fetcher = useCallback(async () => {
     const res = await apiv3Get<IResGrowiTheme>('/customize-setting/theme');
     return res.data;
   }, []);
 
   const swrResponse = useSWR('/customize-setting/theme', fetcher);
 
-  const update = async({ theme }: UpdateThemeArgs) => {
-
+  const update = async ({ theme }: UpdateThemeArgs) => {
     await apiv3Put('/customize-setting/theme', { theme });
 
     if (swrResponse.data == null) {
@@ -53,10 +54,7 @@ export const useSWRxGrowiThemeSetting = (): SWRResponse<IResGrowiTheme, Error> &
     swrResponse.mutate(newData, { optimisticData: newData });
   };
 
-  return Object.assign(
-    swrResponse,
-    {
-      update,
-    },
-  );
+  return Object.assign(swrResponse, {
+    update,
+  });
 };

+ 37 - 28
apps/app/src/stores/admin/sidebar-config.tsx

@@ -1,5 +1,4 @@
 import { useCallback } from 'react';
-
 import type { SWRResponse } from 'swr';
 import useSWRImmutable from 'swr/immutable';
 
@@ -7,16 +6,20 @@ import { apiv3Get, apiv3Put } from '~/client/util/apiv3-client';
 import type { ISidebarConfig } from '~/interfaces/sidebar-config';
 
 type SidebarConfigOption = {
-  update: () => Promise<void>,
+  update: () => Promise<void>;
 
-  setIsSidebarCollapsedMode: (isSidebarCollapsedMode: boolean) => void,
-  setIsSidebarClosedAtDockMode: (isSidebarClosedAtDockMode: boolean | undefined) => void,
-}
+  setIsSidebarCollapsedMode: (isSidebarCollapsedMode: boolean) => void;
+  setIsSidebarClosedAtDockMode: (
+    isSidebarClosedAtDockMode: boolean | undefined,
+  ) => void;
+};
 
-export const useSWRxSidebarConfig = (): SWRResponse<ISidebarConfig, Error> & SidebarConfigOption => {
+export const useSWRxSidebarConfig = (): SWRResponse<ISidebarConfig, Error> &
+  SidebarConfigOption => {
   const swrResponse = useSWRImmutable<ISidebarConfig>(
     '/customize-setting/sidebar',
-    endpoint => apiv3Get<ISidebarConfig>(endpoint).then(result => result.data),
+    (endpoint) =>
+      apiv3Get<ISidebarConfig>(endpoint).then((result) => result.data),
     {
       keepPreviousData: true,
     },
@@ -26,7 +29,7 @@ export const useSWRxSidebarConfig = (): SWRResponse<ISidebarConfig, Error> & Sid
 
   return {
     ...swrResponse,
-    update: useCallback(async() => {
+    update: useCallback(async () => {
       if (data == null) {
         return;
       }
@@ -35,25 +38,31 @@ export const useSWRxSidebarConfig = (): SWRResponse<ISidebarConfig, Error> & Sid
       await apiv3Put<ISidebarConfig>('/customize-setting/sidebar', data);
     }, [data]),
 
-    setIsSidebarCollapsedMode: useCallback((isSidebarCollapsedMode) => {
-      // update isSidebarCollapsedMode in cache, not revalidate
-      mutate((prevData) => {
-        if (prevData == null) {
-          return;
-        }
-
-        return { ...prevData, isSidebarCollapsedMode };
-      }, false);
-    }, [mutate]),
-
-    setIsSidebarClosedAtDockMode: useCallback((isSidebarClosedAtDockMode) => {
-      // update isSidebarClosedAtDockMode in cache, not revalidate
-      mutate((prevData) => {
-        if (prevData == null) {
-          return;
-        }
-        return { ...prevData, isSidebarClosedAtDockMode };
-      }, false);
-    }, [mutate]),
+    setIsSidebarCollapsedMode: useCallback(
+      (isSidebarCollapsedMode) => {
+        // update isSidebarCollapsedMode in cache, not revalidate
+        mutate((prevData) => {
+          if (prevData == null) {
+            return;
+          }
+
+          return { ...prevData, isSidebarCollapsedMode };
+        }, false);
+      },
+      [mutate],
+    ),
+
+    setIsSidebarClosedAtDockMode: useCallback(
+      (isSidebarClosedAtDockMode) => {
+        // update isSidebarClosedAtDockMode in cache, not revalidate
+        mutate((prevData) => {
+          if (prevData == null) {
+            return;
+          }
+          return { ...prevData, isSidebarClosedAtDockMode };
+        }, false);
+      },
+      [mutate],
+    ),
   };
 };

+ 30 - 20
apps/app/src/stores/alert.tsx

@@ -1,38 +1,48 @@
 import { useCallback } from 'react';
-
 import { useSWRStatic } from '@growi/core/dist/swr';
 import type { SWRResponse } from 'swr';
 
 import type { EditorMode } from '../stores-universal/ui';
 
 /*
-* PageStatusAlert
-*/
+ * PageStatusAlert
+ */
 type OpenPageStatusAlertOptions = {
-  hideEditorMode?: EditorMode
-  onRefleshPage?: () => void
-  onResolveConflict?: () => void
-}
+  hideEditorMode?: EditorMode;
+  onRefleshPage?: () => void;
+  onResolveConflict?: () => void;
+};
 
 type PageStatusAlertStatus = {
-  isOpen: boolean
-  hideEditorMode?: EditorMode,
-  onRefleshPage?: () => void
-  onResolveConflict?: () => void
-}
+  isOpen: boolean;
+  hideEditorMode?: EditorMode;
+  onRefleshPage?: () => void;
+  onResolveConflict?: () => void;
+};
 
 type PageStatusAlertUtils = {
-  open: (openPageStatusAlert: OpenPageStatusAlertOptions) => void,
-  close: () => void,
-}
-export const usePageStatusAlert = (): SWRResponse<PageStatusAlertStatus, Error> & PageStatusAlertUtils => {
+  open: (openPageStatusAlert: OpenPageStatusAlertOptions) => void;
+  close: () => void;
+};
+export const usePageStatusAlert = (): SWRResponse<
+  PageStatusAlertStatus,
+  Error
+> &
+  PageStatusAlertUtils => {
   const initialData: PageStatusAlertStatus = { isOpen: false };
-  const swrResponse = useSWRStatic<PageStatusAlertStatus, Error>('pageStatusAlert', undefined, { fallbackData: initialData });
+  const swrResponse = useSWRStatic<PageStatusAlertStatus, Error>(
+    'pageStatusAlert',
+    undefined,
+    { fallbackData: initialData },
+  );
   const { mutate } = swrResponse;
 
-  const open = useCallback(({ ...options }) => {
-    mutate({ isOpen: true, ...options });
-  }, [mutate]);
+  const open = useCallback(
+    ({ ...options }) => {
+      mutate({ isOpen: true, ...options });
+    },
+    [mutate],
+  );
 
   const close = useCallback(() => {
     mutate({ isOpen: false });

+ 47 - 37
apps/app/src/stores/attachment.tsx

@@ -1,11 +1,6 @@
 import { useCallback } from 'react';
-
-import type {
-  IAttachmentHasId, Nullable,
-} from '@growi/core';
-import {
-  type SWRResponseWithUtils, withUtils,
-} from '@growi/core/dist/swr';
+import type { IAttachmentHasId, Nullable } from '@growi/core';
+import { type SWRResponseWithUtils, withUtils } from '@growi/core/dist/swr';
 import type { Util } from 'reactstrap';
 import useSWR, { useSWRConfig } from 'swr';
 
@@ -14,44 +9,54 @@ import { apiv3Get } from '~/client/util/apiv3-client';
 import type { IResAttachmentList } from '~/interfaces/attachment';
 
 type Util = {
-  remove(body: { attachment_id: string }): Promise<void>
+  remove(body: { attachment_id: string }): Promise<void>;
 };
 
 type IDataAttachmentList = {
-  attachments: IAttachmentHasId[]
-  totalAttachments: number
-  limit: number
+  attachments: IAttachmentHasId[];
+  totalAttachments: number;
+  limit: number;
 };
 
-export const useSWRxAttachment = (attachmentId: string): SWRResponseWithUtils<Util, IAttachmentHasId, Error> => {
+export const useSWRxAttachment = (
+  attachmentId: string,
+): SWRResponseWithUtils<Util, IAttachmentHasId, Error> => {
   const swrResponse = useSWR(
     [`/attachment/${attachmentId}`],
-    useCallback(async([endpoint]) => {
+    useCallback(async ([endpoint]) => {
       const res = await apiv3Get(endpoint);
       return res.data.attachment;
     }, []),
   );
 
   // Utils
-  const remove = useCallback(async(body: { attachment_id: string }) => {
-    try {
-      await apiPost('/attachments.remove', body);
-      swrResponse.mutate(body.attachment_id);
-    }
-    catch (err) {
-      throw err;
-    }
-  }, [swrResponse]);
+  const remove = useCallback(
+    async (body: { attachment_id: string }) => {
+      try {
+        await apiPost('/attachments.remove', body);
+        swrResponse.mutate(body.attachment_id);
+      } catch (err) {
+        throw err;
+      }
+    },
+    [swrResponse],
+  );
 
   return withUtils<Util, IAttachmentHasId, Error>(swrResponse, { remove });
 };
 
-export const useSWRxAttachments = (pageId?: Nullable<string>, pageNumber?: number): SWRResponseWithUtils<Util, IDataAttachmentList, Error> => {
+export const useSWRxAttachments = (
+  pageId?: Nullable<string>,
+  pageNumber?: number,
+): SWRResponseWithUtils<Util, IDataAttachmentList, Error> => {
   const { mutate: mutateUseSWRxAttachment } = useSWRConfig();
   const shouldFetch = pageId != null && pageNumber != null;
 
-  const fetcher = useCallback(async([endpoint, pageId, pageNumber]) => {
-    const res = await apiv3Get<IResAttachmentList>(endpoint, { pageId, pageNumber });
+  const fetcher = useCallback(async ([endpoint, pageId, pageNumber]) => {
+    const res = await apiv3Get<IResAttachmentList>(endpoint, {
+      pageId,
+      pageNumber,
+    });
     const resAttachmentList = res.data;
     const { paginateResult } = resAttachmentList;
     return {
@@ -67,19 +72,24 @@ export const useSWRxAttachments = (pageId?: Nullable<string>, pageNumber?: numbe
   );
 
   // Utils
-  const remove = useCallback(async(body: { attachment_id: string }) => {
-    const { mutate } = swrResponse;
+  const remove = useCallback(
+    async (body: { attachment_id: string }) => {
+      const { mutate } = swrResponse;
 
-    try {
-      await apiPost('/attachments.remove', body);
-      mutate();
-      // Mutation for rich attachment rendering
-      mutateUseSWRxAttachment([`/attachment/${body.attachment_id}`], body.attachment_id);
-    }
-    catch (err) {
-      throw err;
-    }
-  }, [mutateUseSWRxAttachment, swrResponse]);
+      try {
+        await apiPost('/attachments.remove', body);
+        mutate();
+        // Mutation for rich attachment rendering
+        mutateUseSWRxAttachment(
+          [`/attachment/${body.attachment_id}`],
+          body.attachment_id,
+        );
+      } catch (err) {
+        throw err;
+      }
+    },
+    [mutateUseSWRxAttachment, swrResponse],
+  );
 
   return withUtils<Util, IDataAttachmentList, Error>(swrResponse, { remove });
 };

+ 7 - 5
apps/app/src/stores/bookmark-folder.ts

@@ -4,12 +4,14 @@ import useSWRImmutable from 'swr/immutable';
 import { apiv3Get } from '~/client/util/apiv3-client';
 import type { BookmarkFolderItems } from '~/interfaces/bookmark-info';
 
-export const useSWRxBookmarkFolderAndChild = (userId?: string): SWRResponse<BookmarkFolderItems[], Error> => {
-
+export const useSWRxBookmarkFolderAndChild = (
+  userId?: string,
+): SWRResponse<BookmarkFolderItems[], Error> => {
   return useSWRImmutable(
     userId != null ? `/bookmark-folder/list/${userId}` : null,
-    endpoint => apiv3Get(endpoint).then((response) => {
-      return response.data.bookmarkFolderItems;
-    }),
+    (endpoint) =>
+      apiv3Get(endpoint).then((response) => {
+        return response.data.bookmarkFolderItems;
+      }),
   );
 };

+ 25 - 15
apps/app/src/stores/bookmark.ts

@@ -1,41 +1,51 @@
-import type { IUser, IPageHasId } from '@growi/core';
+import type { IPageHasId, IUser } from '@growi/core';
 import type { SWRResponse } from 'swr';
 import useSWR from 'swr';
 import useSWRImmutable from 'swr/immutable';
 import useSWRMutation, { type SWRMutationResponse } from 'swr/mutation';
 
-
 import { useCurrentUser } from '~/stores-universal/context';
 
 import { apiv3Get } from '../client/util/apiv3-client';
 import type { IBookmarkInfo } from '../interfaces/bookmark-info';
 
-
-export const useSWRxBookmarkedUsers = (pageId: string | null): SWRResponse<IUser[], Error> => {
+export const useSWRxBookmarkedUsers = (
+  pageId: string | null,
+): SWRResponse<IUser[], Error> => {
   return useSWR(
     pageId != null ? `/bookmarks/info?pageId=${pageId}` : null,
-    endpoint => apiv3Get<IBookmarkInfo>(endpoint).then(response => response.data.bookmarkedUsers),
+    (endpoint) =>
+      apiv3Get<IBookmarkInfo>(endpoint).then(
+        (response) => response.data.bookmarkedUsers,
+      ),
   );
 };
 
-export const useSWRxUserBookmarks = (userId: string | null): SWRResponse<(IPageHasId | null)[], Error> => {
+export const useSWRxUserBookmarks = (
+  userId: string | null,
+): SWRResponse<(IPageHasId | null)[], Error> => {
   return useSWRImmutable(
     userId != null ? `/bookmarks/${userId}` : null,
-    endpoint => apiv3Get(endpoint).then((response) => {
-      const { userRootBookmarks } = response.data;
-      return userRootBookmarks.map(item => item.page); // page will be null if the page is deleted
-    }),
+    (endpoint) =>
+      apiv3Get(endpoint).then((response) => {
+        const { userRootBookmarks } = response.data;
+        return userRootBookmarks.map((item) => item.page); // page will be null if the page is deleted
+      }),
   );
 };
 
-export const useSWRMUTxCurrentUserBookmarks = (): SWRMutationResponse<(IPageHasId | null)[], Error> => {
+export const useSWRMUTxCurrentUserBookmarks = (): SWRMutationResponse<
+  (IPageHasId | null)[],
+  Error
+> => {
   const { data: currentUser } = useCurrentUser();
 
   return useSWRMutation(
     currentUser != null ? `/bookmarks/${currentUser?._id}` : null,
-    endpoint => apiv3Get(endpoint).then((response) => {
-      const { userRootBookmarks } = response.data;
-      return userRootBookmarks.map(item => item.page); // page will be null if the page is deleted
-    }),
+    (endpoint) =>
+      apiv3Get(endpoint).then((response) => {
+        const { userRootBookmarks } = response.data;
+        return userRootBookmarks.map((item) => item.page); // page will be null if the page is deleted
+      }),
   );
 };

+ 51 - 38
apps/app/src/stores/comment.tsx

@@ -1,63 +1,76 @@
 import { useCallback } from 'react';
-
 import type { Nullable } from '@growi/core';
 import type { SWRResponse } from 'swr';
 import useSWR from 'swr';
 
 import { apiGet, apiPost } from '~/client/util/apiv1-client';
 
-import type { ICommentHasIdList, ICommentPostArgs } from '../interfaces/comment';
+import type {
+  ICommentHasIdList,
+  ICommentPostArgs,
+} from '../interfaces/comment';
 
 type IResponseComment = {
-  comments: ICommentHasIdList,
-  ok: boolean,
-}
+  comments: ICommentHasIdList;
+  ok: boolean;
+};
 
 type CommentOperation = {
-  update(comment: string, revisionId: string, commentId: string): Promise<void>,
-  post(args: ICommentPostArgs): Promise<void>
-}
+  update(comment: string, revisionId: string, commentId: string): Promise<void>;
+  post(args: ICommentPostArgs): Promise<void>;
+};
 
-export const useSWRxPageComment = (pageId: Nullable<string>): SWRResponse<ICommentHasIdList, Error> & CommentOperation => {
+export const useSWRxPageComment = (
+  pageId: Nullable<string>,
+): SWRResponse<ICommentHasIdList, Error> & CommentOperation => {
   const shouldFetch: boolean = pageId != null;
 
   const swrResponse = useSWR(
     shouldFetch ? ['/comments.get', pageId] : null,
-    ([endpoint, pageId]) => apiGet(endpoint, { page_id: pageId }).then((response:IResponseComment) => response.comments),
+    ([endpoint, pageId]) =>
+      apiGet(endpoint, { page_id: pageId }).then(
+        (response: IResponseComment) => response.comments,
+      ),
   );
 
   const { mutate } = swrResponse;
 
-  const update = useCallback(async(comment: string, revisionId: string, commentId: string) => {
-    await apiPost('/comments.update', {
-      commentForm: {
-        comment,
-        revision_id: revisionId,
-        comment_id: commentId,
-      },
-    });
-    mutate();
-  }, [mutate]);
+  const update = useCallback(
+    async (comment: string, revisionId: string, commentId: string) => {
+      await apiPost('/comments.update', {
+        commentForm: {
+          comment,
+          revision_id: revisionId,
+          comment_id: commentId,
+        },
+      });
+      mutate();
+    },
+    [mutate],
+  );
 
-  const post = useCallback(async(args: ICommentPostArgs) => {
-    const { commentForm, slackNotificationForm } = args;
-    const { comment, revisionId, replyTo } = commentForm;
-    const { isSlackEnabled, slackChannels } = slackNotificationForm;
+  const post = useCallback(
+    async (args: ICommentPostArgs) => {
+      const { commentForm, slackNotificationForm } = args;
+      const { comment, revisionId, replyTo } = commentForm;
+      const { isSlackEnabled, slackChannels } = slackNotificationForm;
 
-    await apiPost('/comments.add', {
-      commentForm: {
-        comment,
-        page_id: pageId,
-        revision_id: revisionId,
-        replyTo,
-      },
-      slackNotificationForm: {
-        isSlackEnabled,
-        slackChannels,
-      },
-    });
-    mutate();
-  }, [mutate, pageId]);
+      await apiPost('/comments.add', {
+        commentForm: {
+          comment,
+          page_id: pageId,
+          revision_id: revisionId,
+          replyTo,
+        },
+        slackNotificationForm: {
+          isSlackEnabled,
+          slackChannels,
+        },
+      });
+      mutate();
+    },
+    [mutate, pageId],
+  );
 
   return {
     ...swrResponse,

+ 48 - 30
apps/app/src/stores/editor.tsx

@@ -1,7 +1,10 @@
 import { useCallback, useEffect } from 'react';
-
-import { type Nullable } from '@growi/core';
-import { withUtils, type SWRResponseWithUtils, useSWRStatic } from '@growi/core/dist/swr';
+import type { Nullable } from '@growi/core';
+import {
+  type SWRResponseWithUtils,
+  useSWRStatic,
+  withUtils,
+} from '@growi/core/dist/swr';
 import type { EditorSettings } from '@growi/editor';
 import useSWR, { type SWRResponse } from 'swr';
 import useSWRImmutable from 'swr/immutable';
@@ -10,39 +13,49 @@ import { apiGet } from '~/client/util/apiv1-client';
 import { apiv3Get, apiv3Put } from '~/client/util/apiv3-client';
 import type { SlackChannels } from '~/interfaces/user-trigger-notification';
 import {
-  useCurrentUser, useDefaultIndentSize, useIsGuestUser, useIsReadOnlyUser,
+  useCurrentUser,
+  useDefaultIndentSize,
+  useIsGuestUser,
+  useIsReadOnlyUser,
 } from '~/stores-universal/context';
 
 // import { localStorageMiddleware } from './middlewares/sync-to-storage';
 import { useSWRxTagsInfo } from './page';
 
-
 export const useWaitingSaveProcessing = (): SWRResponse<boolean, Error> => {
-  return useSWRStatic('waitingSaveProcessing', undefined, { fallbackData: false });
+  return useSWRStatic('waitingSaveProcessing', undefined, {
+    fallbackData: false,
+  });
 };
 
-
-export const useEditingMarkdown = (initialData?: string): SWRResponse<string, Error> => {
+export const useEditingMarkdown = (
+  initialData?: string,
+): SWRResponse<string, Error> => {
   return useSWRStatic('editingMarkdown', initialData);
 };
 
-
 type EditorSettingsOperation = {
-  update: (updateData: Partial<EditorSettings>) => Promise<void>,
-}
+  update: (updateData: Partial<EditorSettings>) => Promise<void>;
+};
 
 // TODO: Enable localStorageMiddleware
 //   - Unabling localStorageMiddleware occurrs a flickering problem when loading theme.
 //   - see: https://github.com/growilabs/growi/pull/6781#discussion_r1000285786
-export const useEditorSettings = (): SWRResponseWithUtils<EditorSettingsOperation, EditorSettings, Error> => {
+export const useEditorSettings = (): SWRResponseWithUtils<
+  EditorSettingsOperation,
+  EditorSettings,
+  Error
+> => {
   const { data: currentUser } = useCurrentUser();
   const { data: isGuestUser } = useIsGuestUser();
   const { data: isReadOnlyUser } = useIsReadOnlyUser();
 
   const swrResult = useSWRImmutable(
-    (isGuestUser || isReadOnlyUser) ? null : ['/personal-setting/editor-settings', currentUser?.username],
+    isGuestUser || isReadOnlyUser
+      ? null
+      : ['/personal-setting/editor-settings', currentUser?.username],
     ([endpoint]) => {
-      return apiv3Get(endpoint).then(result => result.data);
+      return apiv3Get(endpoint).then((result) => result.data);
     },
     {
       // use: [localStorageMiddleware], // store to localStorage for initialization fastly
@@ -51,7 +64,7 @@ export const useEditorSettings = (): SWRResponseWithUtils<EditorSettingsOperatio
   );
 
   return withUtils<EditorSettingsOperation, EditorSettings, Error>(swrResult, {
-    update: async(updateData) => {
+    update: async (updateData) => {
       const { data, mutate } = swrResult;
 
       if (data == null) {
@@ -76,13 +89,18 @@ export const useCurrentIndentSize = (): SWRResponse<number, Error> => {
 };
 
 /*
-* Slack Notification
-*/
-export const useSWRxSlackChannels = (currentPagePath: Nullable<string>): SWRResponse<string[], Error> => {
+ * Slack Notification
+ */
+export const useSWRxSlackChannels = (
+  currentPagePath: Nullable<string>,
+): SWRResponse<string[], Error> => {
   const shouldFetch: boolean = currentPagePath != null;
   return useSWR(
     shouldFetch ? ['/pages.updatePost', currentPagePath] : null,
-    ([endpoint, path]) => apiGet(endpoint, { path }).then((response: SlackChannels) => response.updatePost),
+    ([endpoint, path]) =>
+      apiGet(endpoint, { path }).then(
+        (response: SlackChannels) => response.updatePost,
+      ),
     {
       revalidateOnFocus: false,
       fallbackData: [''],
@@ -91,18 +109,16 @@ export const useSWRxSlackChannels = (currentPagePath: Nullable<string>): SWRResp
 };
 
 export const useIsSlackEnabled = (): SWRResponse<boolean, Error> => {
-  return useSWRStatic(
-    'isSlackEnabled',
-    undefined,
-    { fallbackData: false },
-  );
+  return useSWRStatic('isSlackEnabled', undefined, { fallbackData: false });
 };
 
 export type IPageTagsForEditorsOption = {
   sync: (tags?: string[]) => void;
-}
+};
 
-export const usePageTagsForEditors = (pageId: Nullable<string>): SWRResponse<string[], Error> & IPageTagsForEditorsOption => {
+export const usePageTagsForEditors = (
+  pageId: Nullable<string>,
+): SWRResponse<string[], Error> & IPageTagsForEditorsOption => {
   const { data: tagsInfoData } = useSWRxTagsInfo(pageId);
   const swrResult = useSWRStatic<string[], Error>('pageTags', undefined);
   const { mutate } = swrResult;
@@ -120,10 +136,12 @@ export const useIsEnabledUnsavedWarning = (): SWRResponse<boolean, Error> => {
   return useSWRStatic<boolean, Error>('isEnabledUnsavedWarning');
 };
 
-
-export const useReservedNextCaretLine = (initialData?: number): SWRResponse<number> => {
-
-  const swrResponse = useSWRStatic('saveNextCaretLine', initialData, { fallbackData: 0 });
+export const useReservedNextCaretLine = (
+  initialData?: number,
+): SWRResponse<number> => {
+  const swrResponse = useSWRStatic('saveNextCaretLine', initialData, {
+    fallbackData: 0,
+  });
   const { mutate } = swrResponse;
 
   useEffect(() => {

+ 18 - 13
apps/app/src/stores/global-notification.ts

@@ -5,24 +5,26 @@ import type { IGlobalNotification } from '~/client/interfaces/global-notificatio
 
 import { apiv3Get, apiv3Put } from '../client/util/apiv3-client';
 
-
 type Util = {
-  update(updateData: any): Promise<void>
+  update(updateData: any): Promise<void>;
 };
 
-
-export const useSWRxGlobalNotification = (globalNotificationId: string): SWRResponseWithUtils<Util, any, Error> => {
+export const useSWRxGlobalNotification = (
+  globalNotificationId: string,
+): SWRResponseWithUtils<Util, any, Error> => {
   const swrResult = useSWRImmutable(
-    globalNotificationId != null ? `/notification-setting/global-notification/${globalNotificationId}` : null,
-    endpoint => apiv3Get(endpoint).then((response) => {
-      return {
-        globalNotification: response.data.globalNotification,
-      };
-    }),
+    globalNotificationId != null
+      ? `/notification-setting/global-notification/${globalNotificationId}`
+      : null,
+    (endpoint) =>
+      apiv3Get(endpoint).then((response) => {
+        return {
+          globalNotification: response.data.globalNotification,
+        };
+      }),
   );
 
-
-  const update = async(updateData: IGlobalNotification) => {
+  const update = async (updateData: IGlobalNotification) => {
     const { data } = swrResult;
 
     if (data == null) {
@@ -30,7 +32,10 @@ export const useSWRxGlobalNotification = (globalNotificationId: string): SWRResp
     }
 
     // invoke API
-    await apiv3Put(`/notification-setting/global-notification/${globalNotificationId}`, updateData);
+    await apiv3Put(
+      `/notification-setting/global-notification/${globalNotificationId}`,
+      updateData,
+    );
   };
 
   return withUtils<Util, any, Error>(swrResult, { update });

+ 30 - 25
apps/app/src/stores/in-app-notification.ts

@@ -1,9 +1,12 @@
 import type { SWRConfiguration, SWRResponse } from 'swr';
 import useSWR from 'swr';
 
-
 import { SupportedTargetModel } from '~/interfaces/activity';
-import type { InAppNotificationStatuses, IInAppNotification, PaginateResult } from '~/interfaces/in-app-notification';
+import type {
+  IInAppNotification,
+  InAppNotificationStatuses,
+  PaginateResult,
+} from '~/interfaces/in-app-notification';
 import * as userSerializers from '~/models/serializers/in-app-notification-snapshot/user';
 import loggerFactory from '~/utils/logger';
 
@@ -11,38 +14,40 @@ import { apiv3Get } from '../client/util/apiv3-client';
 
 const logger = loggerFactory('growi:cli:InAppNotification');
 
-type inAppNotificationPaginateResult = PaginateResult<IInAppNotification>
+type inAppNotificationPaginateResult = PaginateResult<IInAppNotification>;
 
 export const useSWRxInAppNotifications = (
-    limit: number,
-    offset?: number,
-    status?: InAppNotificationStatuses,
-    config?: SWRConfiguration,
+  limit: number,
+  offset?: number,
+  status?: InAppNotificationStatuses,
+  config?: SWRConfiguration,
 ): SWRResponse<PaginateResult<IInAppNotification>, Error> => {
   return useSWR(
     ['/in-app-notification/list', limit, offset, status],
-    ([endpoint]) => apiv3Get(endpoint, { limit, offset, status }).then((response) => {
-      const inAppNotificationPaginateResult = response.data as inAppNotificationPaginateResult;
-      inAppNotificationPaginateResult.docs.forEach((doc) => {
-        try {
-          if (doc.targetModel === SupportedTargetModel.MODEL_USER) {
-            doc.parsedSnapshot = userSerializers.parseSnapshot(doc.snapshot);
+    ([endpoint]) =>
+      apiv3Get(endpoint, { limit, offset, status }).then((response) => {
+        const inAppNotificationPaginateResult =
+          response.data as inAppNotificationPaginateResult;
+        inAppNotificationPaginateResult.docs.forEach((doc) => {
+          try {
+            if (doc.targetModel === SupportedTargetModel.MODEL_USER) {
+              doc.parsedSnapshot = userSerializers.parseSnapshot(doc.snapshot);
+            }
+          } catch (err) {
+            logger.warn('Failed to parse snapshot', err);
           }
-        }
-        catch (err) {
-          logger.warn('Failed to parse snapshot', err);
-        }
-      });
-      return inAppNotificationPaginateResult;
-    }),
+        });
+        return inAppNotificationPaginateResult;
+      }),
     config,
   );
 };
 
-export const useSWRxInAppNotificationStatus = (
-): SWRResponse<number, Error> => {
-  return useSWR(
-    '/in-app-notification/status',
-    endpoint => apiv3Get(endpoint).then(response => response.data.count),
+export const useSWRxInAppNotificationStatus = (): SWRResponse<
+  number,
+  Error
+> => {
+  return useSWR('/in-app-notification/status', (endpoint) =>
+    apiv3Get(endpoint).then((response) => response.data.count),
   );
 };

+ 14 - 9
apps/app/src/stores/maintenanceMode.tsx

@@ -1,26 +1,31 @@
-import { withUtils, type SWRResponseWithUtils } from '@growi/core/dist/swr';
+import { type SWRResponseWithUtils, withUtils } from '@growi/core/dist/swr';
 
 import { apiv3Post } from '~/client/util/apiv3-client';
 
 import { useStaticSWR } from './use-static-swr';
 
-
 type maintenanceModeUtils = {
-  start(): Promise<void>,
-  end(): Promise<void>,
-}
+  start(): Promise<void>;
+  end(): Promise<void>;
+};
 
-export const useIsMaintenanceMode = (initialData?: boolean): SWRResponseWithUtils<maintenanceModeUtils, boolean> => {
-  const swrResult = useStaticSWR<boolean, Error>('isMaintenanceMode', initialData, { fallbackData: false });
+export const useIsMaintenanceMode = (
+  initialData?: boolean,
+): SWRResponseWithUtils<maintenanceModeUtils, boolean> => {
+  const swrResult = useStaticSWR<boolean, Error>(
+    'isMaintenanceMode',
+    initialData,
+    { fallbackData: false },
+  );
 
   const utils = {
-    start: async() => {
+    start: async () => {
       const { mutate } = swrResult;
       await apiv3Post('/app-settings/maintenance-mode', { flag: true });
       mutate(true);
     },
 
-    end: async() => {
+    end: async () => {
       const { mutate } = swrResult;
       await apiv3Post('/app-settings/maintenance-mode', { flag: false });
       mutate(false);

+ 3 - 2
apps/app/src/stores/middlewares/user.ts

@@ -3,11 +3,12 @@ import type { Middleware, SWRHook } from 'swr';
 
 import { apiv3Put } from '~/client/util/apiv3-client';
 
-export const checkAndUpdateImageUrlCached: Middleware = (useSWRNext: SWRHook) => {
+export const checkAndUpdateImageUrlCached: Middleware = (
+  useSWRNext: SWRHook,
+) => {
   return (key, fetcher, config) => {
     const swrNext = useSWRNext(key, fetcher, config);
     if (swrNext.data != null) {
-
       const userIds = Object(swrNext.data)
         .filter((user: IUserHasId) => user.imageUrlCached == null)
         .map((user: IUserHasId) => user._id);

File diff suppressed because it is too large
+ 436 - 277
apps/app/src/stores/modal.tsx


+ 118 - 78
apps/app/src/stores/page-listing.tsx

@@ -1,11 +1,17 @@
-import assert from 'assert';
-
 import type {
-  Nullable, HasObjectId,
-  IDataWithMeta, IPageHasId, IPageInfoForListing, IPageInfoForOperation,
+  HasObjectId,
+  IDataWithMeta,
+  IPageHasId,
+  IPageInfoForListing,
+  IPageInfoForOperation,
+  Nullable,
 } from '@growi/core';
+import assert from 'assert';
 import useSWR, {
-  mutate, type SWRConfiguration, type SWRResponse, type Arguments,
+  type Arguments,
+  mutate,
+  type SWRConfiguration,
+  type SWRResponse,
 } from 'swr';
 import useSWRImmutable from 'swr/immutable';
 import type { SWRInfiniteResponse } from 'swr/infinite';
@@ -15,48 +21,60 @@ import type { IPagingResult } from '~/interfaces/paging-result';
 
 import { apiv3Get } from '../client/util/apiv3-client';
 import type {
-  ChildrenResult, V5MigrationStatus, RootPageResult,
+  ChildrenResult,
+  RootPageResult,
+  V5MigrationStatus,
 } from '../interfaces/page-listing-results';
 
-
-export const useSWRxPagesByPath = (path?: Nullable<string>): SWRResponse<IPageHasId[], Error> => {
+export const useSWRxPagesByPath = (
+  path?: Nullable<string>,
+): SWRResponse<IPageHasId[], Error> => {
   const findAll = true;
   const includeEmpty = true;
   return useSWR(
     path != null ? ['/page', path, findAll, includeEmpty] : null,
-    ([endpoint, path, findAll, includeEmpty]) => apiv3Get(endpoint, { path, findAll, includeEmpty }).then(result => result.data.pages),
+    ([endpoint, path, findAll, includeEmpty]) =>
+      apiv3Get(endpoint, { path, findAll, includeEmpty }).then(
+        (result) => result.data.pages,
+      ),
   );
 };
 
 type RecentApiResult = {
-  pages: IPageHasId[],
-  totalCount: number,
-  offset: number,
-}
+  pages: IPageHasId[];
+  totalCount: number;
+  offset: number;
+};
 
 export const getRecentlyUpdatedKey = (
-    pageIndex: number,
-    previousPageData: RecentApiResult | null,
-    includeWipPage?: boolean,
+  pageIndex: number,
+  previousPageData: RecentApiResult | null,
+  includeWipPage?: boolean,
 ): [string, number | undefined, boolean | undefined] | null => {
-  if (previousPageData != null && previousPageData.pages.length === 0) return null;
+  if (previousPageData != null && previousPageData.pages.length === 0)
+    return null;
 
   if (pageIndex === 0 || previousPageData == null) {
     return ['/pages/recent', undefined, includeWipPage];
   }
   const offset = previousPageData.offset + previousPageData.pages.length;
   return ['/pages/recent', offset, includeWipPage];
-
 };
 
 export const useSWRINFxRecentlyUpdated = (
-    includeWipPage?: boolean,
-    config?: SWRConfiguration,
+  includeWipPage?: boolean,
+  config?: SWRConfiguration,
 ): SWRInfiniteResponse<RecentApiResult, Error> => {
   const PER_PAGE = 20;
   return useSWRInfinite(
-    (pageIndex, previousPageData) => getRecentlyUpdatedKey(pageIndex, previousPageData, includeWipPage),
-    ([endpoint, offset, includeWipPage]) => apiv3Get<RecentApiResult>(endpoint, { offset, limit: PER_PAGE, includeWipPage }).then(response => response.data),
+    (pageIndex, previousPageData) =>
+      getRecentlyUpdatedKey(pageIndex, previousPageData, includeWipPage),
+    ([endpoint, offset, includeWipPage]) =>
+      apiv3Get<RecentApiResult>(endpoint, {
+        offset,
+        limit: PER_PAGE,
+        includeWipPage,
+      }).then((response) => response.data),
     {
       ...config,
       revalidateFirstPage: false,
@@ -65,43 +83,46 @@ export const useSWRINFxRecentlyUpdated = (
   );
 };
 
-export const mutateRecentlyUpdated = async(): Promise<undefined> => {
-  [true, false].forEach(includeWipPage => mutate(
-    unstable_serialize(
-      (pageIndex, previousPageData) => getRecentlyUpdatedKey(pageIndex, previousPageData, includeWipPage),
+export const mutateRecentlyUpdated = async (): Promise<undefined> => {
+  [true, false].forEach((includeWipPage) =>
+    mutate(
+      unstable_serialize((pageIndex, previousPageData) =>
+        getRecentlyUpdatedKey(pageIndex, previousPageData, includeWipPage),
+      ),
     ),
-  ));
+  );
   return;
 };
 
-export const mutatePageList = async(): Promise<void[]> => {
-  return mutate(
-    key => Array.isArray(key) && key[0] === '/pages/list',
-  );
+export const mutatePageList = async (): Promise<void[]> => {
+  return mutate((key) => Array.isArray(key) && key[0] === '/pages/list');
 };
 
 export const useSWRxPageList = (
-    path: string | null, pageNumber?: number, limit?: number,
+  path: string | null,
+  pageNumber?: number,
+  limit?: number,
 ): SWRResponse<IPagingResult<IPageHasId>, Error> => {
   return useSWR(
-    path == null
-      ? null
-      : ['/pages/list', path, pageNumber, limit],
+    path == null ? null : ['/pages/list', path, pageNumber, limit],
     ([endpoint, path, pageNumber, limit]) => {
       const args = Object.assign(
         { path, page: pageNumber ?? 1 },
         // if limit exist then add it as query string
-        (limit != null) ? { limit } : {},
+        limit != null ? { limit } : {},
       );
 
-      return apiv3Get<{pages: IPageHasId[], totalCount: number, limit: number}>(endpoint, args)
-        .then((response) => {
-          return {
-            items: response.data.pages,
-            totalCount: response.data.totalCount,
-            limit: response.data.limit,
-          };
-        });
+      return apiv3Get<{
+        pages: IPageHasId[];
+        totalCount: number;
+        limit: number;
+      }>(endpoint, args).then((response) => {
+        return {
+          items: response.data.pages,
+          totalCount: response.data.totalCount,
+          limit: response.data.limit,
+        };
+      });
     },
     {
       keepPreviousData: true,
@@ -109,33 +130,44 @@ export const useSWRxPageList = (
   );
 };
 
-
 type PageInfoInjector = {
-  injectTo: <D extends HasObjectId>(pages: (D | IDataWithMeta<D>)[]) => IDataWithMeta<D, IPageInfoForOperation>[],
-}
+  injectTo: <D extends HasObjectId>(
+    pages: (D | IDataWithMeta<D>)[],
+  ) => IDataWithMeta<D, IPageInfoForOperation>[];
+};
 
-const isIDataWithMeta = (item: HasObjectId | IDataWithMeta): item is IDataWithMeta => {
+const isIDataWithMeta = (
+  item: HasObjectId | IDataWithMeta,
+): item is IDataWithMeta => {
   return 'data' in item;
 };
 
 export const useSWRxPageInfoForList = (
-    pageIds: string[] | null | undefined,
-    path: string | null | undefined = null,
-    attachBookmarkCount = false,
-    attachShortBody = false,
-): SWRResponse<Record<string, IPageInfoForListing>, Error> & PageInfoInjector => {
-
+  pageIds: string[] | null | undefined,
+  path: string | null | undefined = null,
+  attachBookmarkCount = false,
+  attachShortBody = false,
+): SWRResponse<Record<string, IPageInfoForListing>, Error> &
+  PageInfoInjector => {
   const shouldFetch = (pageIds != null && pageIds.length > 0) || path != null;
 
   const swrResult = useSWRImmutable(
-    shouldFetch ? ['/page-listing/info', pageIds, path, attachBookmarkCount, attachShortBody] : null,
+    shouldFetch
+      ? [
+          '/page-listing/info',
+          pageIds,
+          path,
+          attachBookmarkCount,
+          attachShortBody,
+        ]
+      : null,
     ([endpoint, pageIds, path, attachBookmarkCount, attachShortBody]) => {
       return apiv3Get(endpoint, {
         pageIds: pageIds != null ? pageIds : undefined, // Do not pass null to avoid empty query parameter
         path: path != null ? path : undefined, // Do not pass null to avoid empty query parameter
         attachBookmarkCount,
         attachShortBody,
-      }).then(response => response.data);
+      }).then((response) => response.data);
     },
   );
 
@@ -158,14 +190,17 @@ export const useSWRxPageInfoForList = (
   };
 };
 
-export const useSWRxRootPage = (config?: SWRConfiguration): SWRResponse<RootPageResult, Error> => {
+export const useSWRxRootPage = (
+  config?: SWRConfiguration,
+): SWRResponse<RootPageResult, Error> => {
   return useSWR(
     '/page-listing/root',
-    endpoint => apiv3Get(endpoint).then((response) => {
-      return {
-        rootPage: response.data.rootPage,
-      };
-    }),
+    (endpoint) =>
+      apiv3Get(endpoint).then((response) => {
+        return {
+          rootPage: response.data.rootPage,
+        };
+      }),
     {
       ...config,
       keepPreviousData: true,
@@ -179,15 +214,16 @@ const MUTATION_ID_FOR_PAGETREE = 'pageTree';
 const keyMatcherForPageTree = (key: Arguments): boolean => {
   return Array.isArray(key) && key[0] === MUTATION_ID_FOR_PAGETREE;
 };
-export const mutatePageTree = async(): Promise<undefined[]> => {
+export const mutatePageTree = async (): Promise<undefined[]> => {
   return mutate(keyMatcherForPageTree);
 };
 
-
 export const useSWRxPageChildren = (
-    id?: string | null,
+  id?: string | null,
 ): SWRResponse<ChildrenResult, Error> => {
-  const key = id ? [MUTATION_ID_FOR_PAGETREE, '/page-listing/children', id] : null;
+  const key = id
+    ? [MUTATION_ID_FOR_PAGETREE, '/page-listing/children', id]
+    : null;
 
   if (key != null) {
     assert(keyMatcherForPageTree(key));
@@ -195,11 +231,12 @@ export const useSWRxPageChildren = (
 
   return useSWR(
     key,
-    ([, endpoint, id]) => apiv3Get(endpoint, { id }).then((response) => {
-      return {
-        children: response.data.children,
-      };
-    }),
+    ([, endpoint, id]) =>
+      apiv3Get(endpoint, { id }).then((response) => {
+        return {
+          children: response.data.children,
+        };
+      }),
     {
       keepPreviousData: true,
       revalidateOnFocus: false,
@@ -208,15 +245,18 @@ export const useSWRxPageChildren = (
   );
 };
 
-export const useSWRxV5MigrationStatus = (config?: SWRConfiguration): SWRResponse<V5MigrationStatus, Error> => {
+export const useSWRxV5MigrationStatus = (
+  config?: SWRConfiguration,
+): SWRResponse<V5MigrationStatus, Error> => {
   return useSWRImmutable(
     '/pages/v5-migration-status',
-    endpoint => apiv3Get(endpoint).then((response) => {
-      return {
-        isV5Compatible: response.data.isV5Compatible,
-        migratablePagesCount: response.data.migratablePagesCount,
-      };
-    }),
+    (endpoint) =>
+      apiv3Get(endpoint).then((response) => {
+        return {
+          isV5Compatible: response.data.isV5Compatible,
+          migratablePagesCount: response.data.migratablePagesCount,
+        };
+      }),
     config,
   );
 };

+ 3 - 2
apps/app/src/stores/page-redirect.tsx

@@ -2,7 +2,8 @@ import type { SWRResponse } from 'swr';
 
 import { useStaticSWR } from './use-static-swr';
 
-
-export const useRedirectFrom = (initialData?: string | null): SWRResponse<string | null, Error> => {
+export const useRedirectFrom = (
+  initialData?: string | null,
+): SWRResponse<string | null, Error> => {
   return useStaticSWR('redirectFrom', initialData);
 };

+ 14 - 9
apps/app/src/stores/page-timeline.tsx

@@ -1,25 +1,30 @@
-
 import type { IPageHasId } from '@growi/core';
 import type { SWRInfiniteResponse } from 'swr/infinite';
 import useSWRInfinite from 'swr/infinite';
 
 import { apiv3Get } from '~/client/util/apiv3-client';
 
-
 type PageTimelineResult = {
-  pages: IPageHasId[],
-  totalCount: number,
-  offset: number,
-}
-export const useSWRINFxPageTimeline = (path: string | undefined, limit: number) : SWRInfiniteResponse<PageTimelineResult, Error> => {
+  pages: IPageHasId[];
+  totalCount: number;
+  offset: number;
+};
+export const useSWRINFxPageTimeline = (
+  path: string | undefined,
+  limit: number,
+): SWRInfiniteResponse<PageTimelineResult, Error> => {
   return useSWRInfinite(
     (pageIndex, previousPageData) => {
-      if (previousPageData != null && previousPageData.pages.length === 0) return null;
+      if (previousPageData != null && previousPageData.pages.length === 0)
+        return null;
       if (path === undefined) return null;
 
       return ['/pages/list', path, pageIndex + 1, limit];
     },
-    ([endpoint, path, page, limit]) => apiv3Get<PageTimelineResult>(endpoint, { path, page, limit }).then(response => response.data),
+    ([endpoint, path, page, limit]) =>
+      apiv3Get<PageTimelineResult>(endpoint, { path, page, limit }).then(
+        (response) => response.data,
+      ),
     {
       revalidateFirstPage: false,
       revalidateAll: false,

+ 183 - 106
apps/app/src/stores/page.tsx

@@ -1,16 +1,22 @@
 import { useEffect, useMemo } from 'react';
-
 import type {
-  Ref, Nullable,
-  IPageInfoForEntity, IPagePopulatedToShowRevision,
+  IPageInfo,
+  IPageInfoForEntity,
+  IPageInfoForOperation,
+  IPagePopulatedToShowRevision,
+  IRevision,
+  IRevisionHasId,
+  Nullable,
+  Ref,
   SWRInfinitePageRevisionsResponse,
-  IPageInfo, IPageInfoForOperation,
-  IRevision, IRevisionHasId,
 } from '@growi/core';
 import { useSWRStatic } from '@growi/core/dist/swr';
 import { isClient, pagePathUtils } from '@growi/core/dist/utils';
 import useSWR, {
-  mutate, useSWRConfig, type SWRResponse, type SWRConfiguration,
+  mutate,
+  type SWRConfiguration,
+  type SWRResponse,
+  useSWRConfig,
 } from 'swr';
 import useSWRImmutable from 'swr/immutable';
 import useSWRInfinite, { type SWRInfiniteResponse } from 'swr/infinite';
@@ -19,43 +25,59 @@ import useSWRMutation, { type SWRMutationResponse } from 'swr/mutation';
 import { apiGet } from '~/client/util/apiv1-client';
 import { apiv3Get } from '~/client/util/apiv3-client';
 import type { IPagePathWithDescendantCount } from '~/interfaces/page';
-import type { IRecordApplicableGrant, IResCurrentGrantData } from '~/interfaces/page-grant';
+import type {
+  IRecordApplicableGrant,
+  IResCurrentGrantData,
+} from '~/interfaces/page-grant';
 import {
-  useCurrentPathname, useShareLinkId, useIsGuestUser, useIsReadOnlyUser,
+  useCurrentPathname,
+  useIsGuestUser,
+  useIsReadOnlyUser,
+  useShareLinkId,
 } from '~/stores-universal/context';
 import type { AxiosResponse } from '~/utils/axios';
 
 import type { IPageTagsInfo } from '../interfaces/tag';
-
-
 import { useRemoteRevisionId } from './remote-latest-page';
 
-
 const { isPermalink: _isPermalink } = pagePathUtils;
 
-
-export const useCurrentPageId = (initialData?: Nullable<string>): SWRResponse<Nullable<string>, Error> => {
+export const useCurrentPageId = (
+  initialData?: Nullable<string>,
+): SWRResponse<Nullable<string>, Error> => {
   return useSWRStatic<Nullable<string>, Error>('currentPageId', initialData);
 };
 
-export const useIsLatestRevision = (initialData?: boolean): SWRResponse<boolean, any> => {
+export const useIsLatestRevision = (
+  initialData?: boolean,
+): SWRResponse<boolean, any> => {
   return useSWRStatic('isLatestRevision', initialData);
 };
 
-export const useIsNotFound = (initialData?: boolean): SWRResponse<boolean, Error> => {
-  return useSWRStatic<boolean, Error>('isNotFound', initialData, { fallbackData: false });
+export const useIsNotFound = (
+  initialData?: boolean,
+): SWRResponse<boolean, Error> => {
+  return useSWRStatic<boolean, Error>('isNotFound', initialData, {
+    fallbackData: false,
+  });
 };
 
-export const useTemplateTagData = (initialData?: string[]): SWRResponse<string[], Error> => {
+export const useTemplateTagData = (
+  initialData?: string[],
+): SWRResponse<string[], Error> => {
   return useSWRStatic<string[], Error>('templateTagData', initialData);
 };
 
-export const useTemplateBodyData = (initialData?: string): SWRResponse<string, Error> => {
+export const useTemplateBodyData = (
+  initialData?: string,
+): SWRResponse<string, Error> => {
   return useSWRStatic<string, Error>('templateBodyData', initialData);
 };
 
 /** "useSWRxCurrentPage" is intended for initial data retrieval only. Use "useSWRMUTxCurrentPage" for revalidation */
-export const useSWRxCurrentPage = (initialData?: IPagePopulatedToShowRevision | null): SWRResponse<IPagePopulatedToShowRevision | null> => {
+export const useSWRxCurrentPage = (
+  initialData?: IPagePopulatedToShowRevision | null,
+): SWRResponse<IPagePopulatedToShowRevision | null> => {
   const key = 'currentPage';
 
   const { data: isLatestRevision } = useIsLatestRevision();
@@ -76,7 +98,8 @@ export const useSWRxCurrentPage = (initialData?: IPagePopulatedToShowRevision |
       return true;
     }
 
-    const cachedData = cache.get(key)?.data as IPagePopulatedToShowRevision | null;
+    const cachedData = cache.get(key)
+      ?.data as IPagePopulatedToShowRevision | null;
     if (initialData._id !== cachedData?._id) {
       return true;
     }
@@ -87,9 +110,11 @@ export const useSWRxCurrentPage = (initialData?: IPagePopulatedToShowRevision |
     }
 
     // mutate when opening a previous revision.
-    if (!isLatestRevision
-      && cachedData.revision?._id != null && initialData.revision?._id != null
-      && cachedData.revision._id !== initialData.revision._id
+    if (
+      !isLatestRevision &&
+      cachedData.revision?._id != null &&
+      initialData.revision?._id != null &&
+      cachedData.revision._id !== initialData.revision._id
     ) {
       return true;
     }
@@ -113,7 +138,9 @@ export const useSWRxCurrentPage = (initialData?: IPagePopulatedToShowRevision |
 };
 
 const getPageApiErrorHandler = (errs: AxiosResponse[]) => {
-  if (!Array.isArray(errs)) { throw Error('error is not array') }
+  if (!Array.isArray(errs)) {
+    throw Error('error is not array');
+  }
 
   const statusCode = errs[0].status;
   if (statusCode === 403 || statusCode === 404) {
@@ -123,45 +150,55 @@ const getPageApiErrorHandler = (errs: AxiosResponse[]) => {
   throw Error('failed to get page');
 };
 
-export const useSWRMUTxCurrentPage = (): SWRMutationResponse<IPagePopulatedToShowRevision | null> => {
-  const key = 'currentPage';
-
-  const { data: currentPageId } = useCurrentPageId();
-  const { data: shareLinkId } = useShareLinkId();
+export const useSWRMUTxCurrentPage =
+  (): SWRMutationResponse<IPagePopulatedToShowRevision | null> => {
+    const key = 'currentPage';
 
-  // Get URL parameter for specific revisionId
-  let revisionId: string | undefined;
-  if (isClient()) {
-    const urlParams = new URLSearchParams(window.location.search);
-    const requestRevisionId = urlParams.get('revisionId');
-    revisionId = requestRevisionId != null ? requestRevisionId : undefined;
-  }
+    const { data: currentPageId } = useCurrentPageId();
+    const { data: shareLinkId } = useShareLinkId();
 
-  return useSWRMutation(
-    key,
-    () => apiv3Get<{ page: IPagePopulatedToShowRevision }>('/page', { pageId: currentPageId, shareLinkId, revisionId })
-      .then((result) => {
-        const newData = result.data.page;
-
-        // for the issue https://redmine.weseek.co.jp/issues/156150
-        mutate('currentPage', newData, false);
+    // Get URL parameter for specific revisionId
+    let revisionId: string | undefined;
+    if (isClient()) {
+      const urlParams = new URLSearchParams(window.location.search);
+      const requestRevisionId = urlParams.get('revisionId');
+      revisionId = requestRevisionId != null ? requestRevisionId : undefined;
+    }
 
-        return newData;
-      })
-      .catch(getPageApiErrorHandler),
-    {
-      populateCache: true,
-      revalidate: false,
-    },
-  );
-};
+    return useSWRMutation(
+      key,
+      () =>
+        apiv3Get<{ page: IPagePopulatedToShowRevision }>('/page', {
+          pageId: currentPageId,
+          shareLinkId,
+          revisionId,
+        })
+          .then((result) => {
+            const newData = result.data.page;
+
+            // for the issue https://redmine.weseek.co.jp/issues/156150
+            mutate('currentPage', newData, false);
+
+            return newData;
+          })
+          .catch(getPageApiErrorHandler),
+      {
+        populateCache: true,
+        revalidate: false,
+      },
+    );
+  };
 
-export const useSWRxPageByPath = (path?: string, config?: SWRConfiguration): SWRResponse<IPagePopulatedToShowRevision | null, Error> => {
+export const useSWRxPageByPath = (
+  path?: string,
+  config?: SWRConfiguration,
+): SWRResponse<IPagePopulatedToShowRevision | null, Error> => {
   return useSWR(
     path != null ? ['/page', path] : null,
-    ([endpoint, path]) => apiv3Get<{ page: IPagePopulatedToShowRevision }>(endpoint, { path })
-      .then(result => result.data.page)
-      .catch(getPageApiErrorHandler),
+    ([endpoint, path]) =>
+      apiv3Get<{ page: IPagePopulatedToShowRevision }>(endpoint, { path })
+        .then((result) => result.data.page)
+        .catch(getPageApiErrorHandler),
     {
       ...config,
       keepPreviousData: true,
@@ -171,16 +208,20 @@ export const useSWRxPageByPath = (path?: string, config?: SWRConfiguration): SWR
   );
 };
 
-export const useSWRxTagsInfo = (pageId: Nullable<string>, config?: SWRConfiguration): SWRResponse<IPageTagsInfo | null, Error> => {
+export const useSWRxTagsInfo = (
+  pageId: Nullable<string>,
+  config?: SWRConfiguration,
+): SWRResponse<IPageTagsInfo | null, Error> => {
   const { data: shareLinkId } = useShareLinkId();
 
   const endpoint = `/pages.getPageTag?pageId=${pageId}`;
 
   return useSWR(
     shareLinkId == null && pageId != null ? [endpoint, pageId] : null,
-    ([endpoint, pageId]) => apiGet<IPageTagsInfo>(endpoint, { pageId })
-      .then(result => result)
-      .catch(getPageApiErrorHandler),
+    ([endpoint, pageId]) =>
+      apiGet<IPageTagsInfo>(endpoint, { pageId })
+        .then((result) => result)
+        .catch(getPageApiErrorHandler),
     {
       ...config,
       revalidateOnFocus: false,
@@ -190,17 +231,14 @@ export const useSWRxTagsInfo = (pageId: Nullable<string>, config?: SWRConfigurat
 };
 
 export const mutateAllPageInfo = (): Promise<void[]> => {
-  return mutate(
-    key => Array.isArray(key) && key[0] === '/page/info',
-  );
+  return mutate((key) => Array.isArray(key) && key[0] === '/page/info');
 };
 
 export const useSWRxPageInfo = (
-    pageId: string | null | undefined,
-    shareLinkId?: string | null,
-    initialData?: IPageInfoForEntity,
+  pageId: string | null | undefined,
+  shareLinkId?: string | null,
+  initialData?: IPageInfoForEntity,
 ): SWRResponse<IPageInfo | IPageInfoForOperation> => {
-
   // Cache remains from guest mode when logging in via the Login lead, so add 'isGuestUser' key
   const { data: isGuestUser } = useIsGuestUser();
 
@@ -208,12 +246,17 @@ export const useSWRxPageInfo = (
   const fixedShareLinkId = shareLinkId ?? null;
 
   const key = useMemo(() => {
-    return pageId != null ? ['/page/info', pageId, fixedShareLinkId, isGuestUser] : null;
+    return pageId != null
+      ? ['/page/info', pageId, fixedShareLinkId, isGuestUser]
+      : null;
   }, [fixedShareLinkId, isGuestUser, pageId]);
 
   const swrResult = useSWRImmutable(
     key,
-    ([endpoint, pageId, shareLinkId]: [string, string, string | null]) => apiv3Get(endpoint, { pageId, shareLinkId }).then(response => response.data),
+    ([endpoint, pageId, shareLinkId]: [string, string, string | null]) =>
+      apiv3Get(endpoint, { pageId, shareLinkId }).then(
+        (response) => response.data,
+      ),
     { fallbackData: initialData },
   );
 
@@ -231,10 +274,9 @@ export const useSWRxPageInfo = (
 };
 
 export const useSWRMUTxPageInfo = (
-    pageId: string | null | undefined,
-    shareLinkId?: string | null,
+  pageId: string | null | undefined,
+  shareLinkId?: string | null,
 ): SWRMutationResponse<IPageInfo | IPageInfoForOperation> => {
-
   // Cache remains from guest mode when logging in via the Login lead, so add 'isGuestUser' key
   const { data: isGuestUser } = useIsGuestUser();
 
@@ -242,20 +284,29 @@ export const useSWRMUTxPageInfo = (
   const fixedShareLinkId = shareLinkId ?? null;
 
   const key = useMemo(() => {
-    return pageId != null ? ['/page/info', pageId, fixedShareLinkId, isGuestUser] : null;
+    return pageId != null
+      ? ['/page/info', pageId, fixedShareLinkId, isGuestUser]
+      : null;
   }, [fixedShareLinkId, isGuestUser, pageId]);
 
   return useSWRMutation(
     key,
-    ([endpoint, pageId, shareLinkId]: [string, string, string | null]) => apiv3Get(endpoint, { pageId, shareLinkId }).then(response => response.data),
+    ([endpoint, pageId, shareLinkId]: [string, string, string | null]) =>
+      apiv3Get(endpoint, { pageId, shareLinkId }).then(
+        (response) => response.data,
+      ),
   );
 };
 
-export const useSWRxPageRevision = (pageId: string, revisionId: Ref<IRevision>): SWRResponse<IRevisionHasId> => {
+export const useSWRxPageRevision = (
+  pageId: string,
+  revisionId: Ref<IRevision>,
+): SWRResponse<IRevisionHasId> => {
   const key = [`/revisions/${revisionId}`, pageId, revisionId];
-  return useSWRImmutable(
-    key,
-    () => apiv3Get<{ revision: IRevisionHasId }>(`/revisions/${revisionId}`, { pageId }).then(response => response.data.revision),
+  return useSWRImmutable(key, () =>
+    apiv3Get<{ revision: IRevisionHasId }>(`/revisions/${revisionId}`, {
+      pageId,
+    }).then((response) => response.data.revision),
   );
 };
 
@@ -264,12 +315,16 @@ export const useSWRxPageRevision = (pageId: string, revisionId: Ref<IRevision>):
  */
 
 export const useSWRxInfinitePageRevisions = (
-    pageId: string,
-    limit: number,
+  pageId: string,
+  limit: number,
 ): SWRInfiniteResponse<SWRInfinitePageRevisionsResponse, Error> => {
   return useSWRInfinite(
     (pageIndex, previousRevisionData) => {
-      if (previousRevisionData != null && previousRevisionData.revisions.length === 0) return null;
+      if (
+        previousRevisionData != null &&
+        previousRevisionData.revisions.length === 0
+      )
+        return null;
 
       if (pageIndex === 0 || previousRevisionData == null) {
         return ['/revisions/list', pageId, undefined, limit];
@@ -277,7 +332,12 @@ export const useSWRxInfinitePageRevisions = (
       const offset = previousRevisionData.offset + limit;
       return ['/revisions/list', pageId, offset, limit];
     },
-    ([endpoint, pageId, offset, limit]) => apiv3Get<SWRInfinitePageRevisionsResponse>(endpoint, { pageId, offset, limit }).then(response => response.data),
+    ([endpoint, pageId, offset, limit]) =>
+      apiv3Get<SWRInfinitePageRevisionsResponse>(endpoint, {
+        pageId,
+        offset,
+        limit,
+      }).then((response) => response.data),
     {
       revalidateFirstPage: true,
       revalidateAll: false,
@@ -289,39 +349,40 @@ export const useSWRxInfinitePageRevisions = (
  * Grant data fetching hooks
  */
 export const useSWRxCurrentGrantData = (
-    pageId: string | null | undefined,
+  pageId: string | null | undefined,
 ): SWRResponse<IResCurrentGrantData, Error> => {
-
   const { data: isGuestUser } = useIsGuestUser();
   const { data: isReadOnlyUser } = useIsReadOnlyUser();
   const { data: isNotFound } = useIsNotFound();
 
-  const key = !isGuestUser && !isReadOnlyUser && !isNotFound && pageId != null
-    ? ['/page/grant-data', pageId]
-    : null;
+  const key =
+    !isGuestUser && !isReadOnlyUser && !isNotFound && pageId != null
+      ? ['/page/grant-data', pageId]
+      : null;
 
-  return useSWR(
-    key,
-    ([endpoint, pageId]) => apiv3Get(endpoint, { pageId }).then(response => response.data),
+  return useSWR(key, ([endpoint, pageId]) =>
+    apiv3Get(endpoint, { pageId }).then((response) => response.data),
   );
 };
 
 export const useSWRxApplicableGrant = (
-    pageId: string | null | undefined,
+  pageId: string | null | undefined,
 ): SWRResponse<IRecordApplicableGrant, Error> => {
-
   return useSWR(
     pageId != null ? ['/page/applicable-grant', pageId] : null,
-    ([endpoint, pageId]) => apiv3Get(endpoint, { pageId }).then(response => response.data),
+    ([endpoint, pageId]) =>
+      apiv3Get(endpoint, { pageId }).then((response) => response.data),
   );
 };
 
-
 /** **********************************************************
  *                     Computed states
  *********************************************************** */
 
-export const useCurrentPagePath = (): SWRResponse<string | undefined, Error> => {
+export const useCurrentPagePath = (): SWRResponse<
+  string | undefined,
+  Error
+> => {
   const { data: currentPage } = useSWRxCurrentPage();
   const { data: currentPathname } = useCurrentPathname();
 
@@ -359,21 +420,37 @@ export const useIsRevisionOutdated = (): SWRResponse<boolean, Error> => {
   const currentRevisionId = currentPage?.revision?._id;
 
   return useSWRImmutable(
-    currentRevisionId != null && remoteRevisionId != null ? ['useIsRevisionOutdated', currentRevisionId, remoteRevisionId] : null,
-    ([, remoteRevisionId, currentRevisionId]) => { return remoteRevisionId !== currentRevisionId },
+    currentRevisionId != null && remoteRevisionId != null
+      ? ['useIsRevisionOutdated', currentRevisionId, remoteRevisionId]
+      : null,
+    ([, remoteRevisionId, currentRevisionId]) => {
+      return remoteRevisionId !== currentRevisionId;
+    },
   );
 };
 
-
 export const useSWRxPagePathsWithDescendantCount = (
-    paths?: string[], userGroups?: string[], isIncludeEmpty?: boolean, includeAnyoneWithTheLink?: boolean,
+  paths?: string[],
+  userGroups?: string[],
+  isIncludeEmpty?: boolean,
+  includeAnyoneWithTheLink?: boolean,
 ): SWRResponse<IPagePathWithDescendantCount[], Error> => {
   return useSWR(
-    (paths != null && paths.length !== 0) ? ['/page/page-paths-with-descendant-count', paths, userGroups, isIncludeEmpty, includeAnyoneWithTheLink] : null,
-    ([endpoint, paths, userGroups, isIncludeEmpty, includeAnyoneWithTheLink]) => apiv3Get(
-      endpoint, {
-        paths, userGroups, isIncludeEmpty, includeAnyoneWithTheLink,
-      },
-    ).then(result => result.data.pagePathsWithDescendantCount),
+    paths != null && paths.length !== 0
+      ? [
+          '/page/page-paths-with-descendant-count',
+          paths,
+          userGroups,
+          isIncludeEmpty,
+          includeAnyoneWithTheLink,
+        ]
+      : null,
+    ([endpoint, paths, userGroups, isIncludeEmpty, includeAnyoneWithTheLink]) =>
+      apiv3Get(endpoint, {
+        paths,
+        userGroups,
+        isIncludeEmpty,
+        includeAnyoneWithTheLink,
+      }).then((result) => result.data.pagePathsWithDescendantCount),
   );
 };

+ 70 - 46
apps/app/src/stores/personal-settings.tsx

@@ -1,61 +1,81 @@
 import { useCallback } from 'react';
-
-import type { HasObjectId, IExternalAccount, IUser } from '@growi/core/dist/interfaces';
+import type {
+  HasObjectId,
+  IExternalAccount,
+  IUser,
+} from '@growi/core/dist/interfaces';
 import { useTranslation } from 'next-i18next';
 import type { SWRConfiguration, SWRResponse } from 'swr';
 import useSWR from 'swr';
 
 import type {
-  IResGenerateAccessToken, IResGetAccessToken, IAccessTokenInfo,
+  IAccessTokenInfo,
+  IResGenerateAccessToken,
+  IResGetAccessToken,
 } from '~/interfaces/access-token';
 import type { IExternalAuthProviderType } from '~/interfaces/external-auth-provider';
 import { useIsGuestUser } from '~/stores-universal/context';
 import loggerFactory from '~/utils/logger';
 
 import {
-  apiv3Delete, apiv3Get, apiv3Put, apiv3Post,
+  apiv3Delete,
+  apiv3Get,
+  apiv3Post,
+  apiv3Put,
 } from '../client/util/apiv3-client';
-
 import { useStaticSWR } from './use-static-swr';
 
-
 const logger = loggerFactory('growi:stores:personal-settings');
 
-
-export const useSWRxPersonalSettings = (config?: SWRConfiguration): SWRResponse<IUser, Error> => {
+export const useSWRxPersonalSettings = (
+  config?: SWRConfiguration,
+): SWRResponse<IUser, Error> => {
   const { data: isGuestUser } = useIsGuestUser();
 
   const key = !isGuestUser ? '/personal-setting' : null;
 
   return useSWR(
     key,
-    endpoint => apiv3Get(endpoint).then(response => response.data.currentUser),
+    (endpoint) =>
+      apiv3Get(endpoint).then((response) => response.data.currentUser),
     config,
   );
 };
 
 export type IPersonalSettingsInfoOption = {
-  sync: () => void,
-  updateBasicInfo: () => Promise<void>,
-  associateLdapAccount: (account: { username: string, password: string }) => Promise<void>,
-  disassociateLdapAccount: (account: { providerType: IExternalAuthProviderType, accountId: string }) => Promise<void>,
-}
+  sync: () => void;
+  updateBasicInfo: () => Promise<void>;
+  associateLdapAccount: (account: {
+    username: string;
+    password: string;
+  }) => Promise<void>;
+  disassociateLdapAccount: (account: {
+    providerType: IExternalAuthProviderType;
+    accountId: string;
+  }) => Promise<void>;
+};
 
-export const usePersonalSettings = (config?: SWRConfiguration): SWRResponse<IUser, Error> & IPersonalSettingsInfoOption => {
+export const usePersonalSettings = (
+  config?: SWRConfiguration,
+): SWRResponse<IUser, Error> & IPersonalSettingsInfoOption => {
   const { i18n } = useTranslation();
-  const { data: personalSettingsDataFromDB, mutate: revalidate } = useSWRxPersonalSettings(config);
-  const key = personalSettingsDataFromDB != null ? 'personalSettingsInfo' : null;
+  const { data: personalSettingsDataFromDB, mutate: revalidate } =
+    useSWRxPersonalSettings(config);
+  const key =
+    personalSettingsDataFromDB != null ? 'personalSettingsInfo' : null;
 
-  const swrResult = useStaticSWR<IUser, Error>(key, undefined, { fallbackData: personalSettingsDataFromDB });
+  const swrResult = useStaticSWR<IUser, Error>(key, undefined, {
+    fallbackData: personalSettingsDataFromDB,
+  });
 
   // Sync with database
-  const sync = async(): Promise<void> => {
+  const sync = async (): Promise<void> => {
     const { mutate } = swrResult;
     const result = await revalidate();
     mutate(result);
   };
 
-  const updateBasicInfo = async(): Promise<void> => {
+  const updateBasicInfo = async (): Promise<void> => {
     const { data } = swrResult;
 
     if (data == null) {
@@ -74,29 +94,25 @@ export const usePersonalSettings = (config?: SWRConfiguration): SWRResponse<IUse
     try {
       await apiv3Put('/personal-setting/', updateData);
       i18n.changeLanguage(updateData.lang);
-    }
-    catch (errs) {
+    } catch (errs) {
       logger.error(errs);
       throw errs;
     }
   };
 
-
-  const associateLdapAccount = async(account): Promise<void> => {
+  const associateLdapAccount = async (account): Promise<void> => {
     try {
       await apiv3Put('/personal-setting/associate-ldap', account);
-    }
-    catch (err) {
+    } catch (err) {
       logger.error(err);
       throw new Error('Failed to associate ldap account');
     }
   };
 
-  const disassociateLdapAccount = async(account): Promise<void> => {
+  const disassociateLdapAccount = async (account): Promise<void> => {
     try {
       await apiv3Put('/personal-setting/disassociate-ldap', account);
-    }
-    catch (err) {
+    } catch (err) {
       logger.error(err);
       throw new Error('Failed to disassociate ldap account');
     }
@@ -111,35 +127,44 @@ export const usePersonalSettings = (config?: SWRConfiguration): SWRResponse<IUse
   };
 };
 
-export const useSWRxPersonalExternalAccounts = (): SWRResponse<(IExternalAccount<IExternalAuthProviderType> & HasObjectId)[], Error> => {
-  return useSWR(
-    '/personal-setting/external-accounts',
-    endpoint => apiv3Get(endpoint).then(response => response.data.externalAccounts),
+export const useSWRxPersonalExternalAccounts = (): SWRResponse<
+  (IExternalAccount<IExternalAuthProviderType> & HasObjectId)[],
+  Error
+> => {
+  return useSWR('/personal-setting/external-accounts', (endpoint) =>
+    apiv3Get(endpoint).then((response) => response.data.externalAccounts),
   );
 };
 
-
 interface IAccessTokenOption {
-  generateAccessToken: (info: IAccessTokenInfo) => Promise<IResGenerateAccessToken>,
-  deleteAccessToken: (tokenId: string) => Promise<void>,
-  deleteAllAccessTokens: (userId: string) => Promise<void>,
+  generateAccessToken: (
+    info: IAccessTokenInfo,
+  ) => Promise<IResGenerateAccessToken>;
+  deleteAccessToken: (tokenId: string) => Promise<void>;
+  deleteAllAccessTokens: (userId: string) => Promise<void>;
 }
 
-export const useSWRxAccessToken = (): SWRResponse< IResGetAccessToken[] | null, Error> & IAccessTokenOption => {
-  const generateAccessToken = useCallback(async(info) => {
-    const res = await apiv3Post<IResGenerateAccessToken>('/personal-setting/access-token', info);
+export const useSWRxAccessToken = (): SWRResponse<
+  IResGetAccessToken[] | null,
+  Error
+> &
+  IAccessTokenOption => {
+  const generateAccessToken = useCallback(async (info) => {
+    const res = await apiv3Post<IResGenerateAccessToken>(
+      '/personal-setting/access-token',
+      info,
+    );
     return res.data;
   }, []);
-  const deleteAccessToken = useCallback(async(tokenId: string) => {
+  const deleteAccessToken = useCallback(async (tokenId: string) => {
     await apiv3Delete('/personal-setting/access-token', { tokenId });
   }, []);
-  const deleteAllAccessTokens = useCallback(async() => {
+  const deleteAllAccessTokens = useCallback(async () => {
     await apiv3Delete('/personal-setting/access-token/all');
   }, []);
 
-  const swrResult = useSWR(
-    '/personal-setting/access-token',
-    endpoint => apiv3Get(endpoint).then(response => response.data.accessTokens),
+  const swrResult = useSWR('/personal-setting/access-token', (endpoint) =>
+    apiv3Get(endpoint).then((response) => response.data.accessTokens),
   );
 
   return {
@@ -148,5 +173,4 @@ export const useSWRxAccessToken = (): SWRResponse< IResGetAccessToken[] | null,
     deleteAccessToken,
     deleteAllAccessTokens,
   };
-
 };

+ 50 - 28
apps/app/src/stores/remote-latest-page.ts

@@ -1,55 +1,77 @@
-import { useMemo, useCallback } from 'react';
-
+import { useCallback, useMemo } from 'react';
 import type { IUserHasId } from '@growi/core';
 import { useSWRStatic } from '@growi/core/dist/swr';
 import type { SWRResponse } from 'swr';
 
-
-export const useRemoteRevisionId = (initialData?: string): SWRResponse<string, Error> => {
+export const useRemoteRevisionId = (
+  initialData?: string,
+): SWRResponse<string, Error> => {
   return useSWRStatic<string, Error>('remoteRevisionId', initialData);
 };
 
-export const useRemoteRevisionBody = (initialData?: string): SWRResponse<string, Error> => {
+export const useRemoteRevisionBody = (
+  initialData?: string,
+): SWRResponse<string, Error> => {
   return useSWRStatic<string, Error>('remoteRevisionBody', initialData);
 };
 
-export const useRemoteRevisionLastUpdateUser = (initialData?: IUserHasId): SWRResponse<IUserHasId, Error> => {
-  return useSWRStatic<IUserHasId, Error>('remoteRevisionLastUpdateUser', initialData);
+export const useRemoteRevisionLastUpdateUser = (
+  initialData?: IUserHasId,
+): SWRResponse<IUserHasId, Error> => {
+  return useSWRStatic<IUserHasId, Error>(
+    'remoteRevisionLastUpdateUser',
+    initialData,
+  );
 };
 
-export const useRemoteRevisionLastUpdatedAt = (initialData?: Date): SWRResponse<Date, Error> => {
+export const useRemoteRevisionLastUpdatedAt = (
+  initialData?: Date,
+): SWRResponse<Date, Error> => {
   return useSWRStatic<Date, Error>('remoteRevisionLastUpdatedAt', initialData);
 };
 
 export type RemoteRevisionData = {
-  remoteRevisionId: string,
-  remoteRevisionBody: string,
-  remoteRevisionLastUpdateUser?: IUserHasId,
-  remoteRevisionLastUpdatedAt: Date,
-}
-
+  remoteRevisionId: string;
+  remoteRevisionBody: string;
+  remoteRevisionLastUpdateUser?: IUserHasId;
+  remoteRevisionLastUpdatedAt: Date;
+};
 
 // set remote data all at once
-export const useSetRemoteLatestPageData = (): { setRemoteLatestPageData: (pageData: RemoteRevisionData) => void } => {
+export const useSetRemoteLatestPageData = (): {
+  setRemoteLatestPageData: (pageData: RemoteRevisionData) => void;
+} => {
   const { mutate: mutateRemoteRevisionId } = useRemoteRevisionId();
   const { mutate: mutateRemoteRevisionBody } = useRemoteRevisionBody();
-  const { mutate: mutateRemoteRevisionLastUpdateUser } = useRemoteRevisionLastUpdateUser();
-  const { mutate: mutateRemoteRevisionLastUpdatedAt } = useRemoteRevisionLastUpdatedAt();
-
-  const setRemoteLatestPageData = useCallback((remoteRevisionData: RemoteRevisionData) => {
-    const {
-      remoteRevisionId, remoteRevisionBody, remoteRevisionLastUpdateUser, remoteRevisionLastUpdatedAt,
-    } = remoteRevisionData;
-    mutateRemoteRevisionId(remoteRevisionId);
-    mutateRemoteRevisionBody(remoteRevisionBody);
-    mutateRemoteRevisionLastUpdateUser(remoteRevisionLastUpdateUser);
-    mutateRemoteRevisionLastUpdatedAt(remoteRevisionLastUpdatedAt);
-  }, [mutateRemoteRevisionBody, mutateRemoteRevisionId, mutateRemoteRevisionLastUpdateUser, mutateRemoteRevisionLastUpdatedAt]);
+  const { mutate: mutateRemoteRevisionLastUpdateUser } =
+    useRemoteRevisionLastUpdateUser();
+  const { mutate: mutateRemoteRevisionLastUpdatedAt } =
+    useRemoteRevisionLastUpdatedAt();
+
+  const setRemoteLatestPageData = useCallback(
+    (remoteRevisionData: RemoteRevisionData) => {
+      const {
+        remoteRevisionId,
+        remoteRevisionBody,
+        remoteRevisionLastUpdateUser,
+        remoteRevisionLastUpdatedAt,
+      } = remoteRevisionData;
+      mutateRemoteRevisionId(remoteRevisionId);
+      mutateRemoteRevisionBody(remoteRevisionBody);
+      mutateRemoteRevisionLastUpdateUser(remoteRevisionLastUpdateUser);
+      mutateRemoteRevisionLastUpdatedAt(remoteRevisionLastUpdatedAt);
+    },
+    [
+      mutateRemoteRevisionBody,
+      mutateRemoteRevisionId,
+      mutateRemoteRevisionLastUpdateUser,
+      mutateRemoteRevisionLastUpdatedAt,
+    ],
+  );
 
   return useMemo(() => {
     return {
       setRemoteLatestPageData,
     };
   }, [setRemoteLatestPageData]);
-
 };

+ 85 - 37
apps/app/src/stores/renderer.tsx

@@ -1,5 +1,4 @@
 import { useCallback, useEffect } from 'react';
-
 import type { HtmlElementNode } from 'rehype-toc';
 import useSWR, { type SWRConfiguration, type SWRResponse } from 'swr';
 
@@ -19,36 +18,52 @@ const useRendererConfigExt = (): RendererConfigExt | null => {
   const { data: rendererConfig } = useRendererConfig();
   const { isDarkMode } = useNextThemes();
 
-  return rendererConfig == null ? null : {
-    ...rendererConfig,
-    isDarkMode,
-  } satisfies RendererConfigExt;
+  return rendererConfig == null
+    ? null
+    : ({
+        ...rendererConfig,
+        isDarkMode,
+      } satisfies RendererConfigExt);
 };
 
-
 export const useViewOptions = (): SWRResponse<RendererOptions, Error> => {
   const { data: currentPagePath } = useCurrentPagePath();
   const rendererConfig = useRendererConfigExt();
   const { mutate: mutateCurrentPageTocNode } = useCurrentPageTocNode();
 
-  const storeTocNodeHandler = useCallback((toc: HtmlElementNode) => {
-    mutateCurrentPageTocNode(toc, { revalidate: false });
-  }, [mutateCurrentPageTocNode]);
+  const storeTocNodeHandler = useCallback(
+    (toc: HtmlElementNode) => {
+      mutateCurrentPageTocNode(toc, { revalidate: false });
+    },
+    [mutateCurrentPageTocNode],
+  );
 
   const isAllDataValid = currentPagePath != null && rendererConfig != null;
-  const customGenerater = getGrowiFacade().markdownRenderer?.optionsGenerators?.customGenerateViewOptions;
+  const customGenerater =
+    getGrowiFacade().markdownRenderer?.optionsGenerators
+      ?.customGenerateViewOptions;
 
   return useSWR(
     isAllDataValid
       ? ['viewOptions', currentPagePath, rendererConfig, customGenerater]
       : null,
-    async([, currentPagePath, rendererConfig]) => {
+    async ([, currentPagePath, rendererConfig]) => {
       if (customGenerater != null) {
-        return customGenerater(currentPagePath, rendererConfig, storeTocNodeHandler);
+        return customGenerater(
+          currentPagePath,
+          rendererConfig,
+          storeTocNodeHandler,
+        );
       }
 
-      const { generateViewOptions } = await import('~/client/services/renderer/renderer');
-      return generateViewOptions(currentPagePath, rendererConfig, storeTocNodeHandler);
+      const { generateViewOptions } = await import(
+        '~/client/services/renderer/renderer'
+      );
+      return generateViewOptions(
+        currentPagePath,
+        rendererConfig,
+        storeTocNodeHandler,
+      );
     },
     {
       keepPreviousData: true,
@@ -63,14 +78,17 @@ export const useTocOptions = (): SWRResponse<RendererOptions, Error> => {
   const rendererConfig = useRendererConfigExt();
   const { data: tocNode } = useCurrentPageTocNode();
 
-  const isAllDataValid = currentPagePath != null && rendererConfig != null && tocNode != null;
+  const isAllDataValid =
+    currentPagePath != null && rendererConfig != null && tocNode != null;
 
   return useSWR(
     isAllDataValid
       ? ['tocOptions', currentPagePath, tocNode, rendererConfig]
       : null,
-    async([, , tocNode, rendererConfig]) => {
-      const { generateTocOptions } = await import('~/client/services/renderer/renderer');
+    async ([, , tocNode, rendererConfig]) => {
+      const { generateTocOptions } = await import(
+        '~/client/services/renderer/renderer'
+      );
       return generateTocOptions(rendererConfig, tocNode);
     },
     {
@@ -86,18 +104,22 @@ export const usePreviewOptions = (): SWRResponse<RendererOptions, Error> => {
   const rendererConfig = useRendererConfigExt();
 
   const isAllDataValid = currentPagePath != null && rendererConfig != null;
-  const customGenerater = getGrowiFacade().markdownRenderer?.optionsGenerators?.customGeneratePreviewOptions;
+  const customGenerater =
+    getGrowiFacade().markdownRenderer?.optionsGenerators
+      ?.customGeneratePreviewOptions;
 
   return useSWR(
     isAllDataValid
       ? ['previewOptions', rendererConfig, currentPagePath, customGenerater]
       : null,
-    async([, rendererConfig, pagePath]) => {
+    async ([, rendererConfig, pagePath]) => {
       if (customGenerater != null) {
         return customGenerater(rendererConfig, pagePath);
       }
 
-      const { generatePreviewOptions } = await import('~/client/services/renderer/renderer');
+      const { generatePreviewOptions } = await import(
+        '~/client/services/renderer/renderer'
+      );
       return generatePreviewOptions(rendererConfig, pagePath);
     },
     {
@@ -108,7 +130,10 @@ export const usePreviewOptions = (): SWRResponse<RendererOptions, Error> => {
   );
 };
 
-export const useCommentForCurrentPageOptions = (): SWRResponse<RendererOptions, Error> => {
+export const useCommentForCurrentPageOptions = (): SWRResponse<
+  RendererOptions,
+  Error
+> => {
   const { data: currentPagePath } = useCurrentPagePath();
   const rendererConfig = useRendererConfigExt();
 
@@ -118,8 +143,10 @@ export const useCommentForCurrentPageOptions = (): SWRResponse<RendererOptions,
     isAllDataValid
       ? ['commentPreviewOptions', rendererConfig, currentPagePath]
       : null,
-    async([, rendererConfig, currentPagePath]) => {
-      const { generateSimpleViewOptions } = await import('~/client/services/renderer/renderer');
+    async ([, rendererConfig, currentPagePath]) => {
+      const { generateSimpleViewOptions } = await import(
+        '~/client/services/renderer/renderer'
+      );
       return generateSimpleViewOptions(
         rendererConfig,
         currentPagePath,
@@ -136,18 +163,32 @@ export const useCommentForCurrentPageOptions = (): SWRResponse<RendererOptions,
 };
 export const useCommentPreviewOptions = useCommentForCurrentPageOptions;
 
-export const useSelectedPagePreviewOptions = (pagePath: string, highlightKeywords?: string | string[]): SWRResponse<RendererOptions, Error> => {
+export const useSelectedPagePreviewOptions = (
+  pagePath: string,
+  highlightKeywords?: string | string[],
+): SWRResponse<RendererOptions, Error> => {
   const rendererConfig = useRendererConfigExt();
 
   const isAllDataValid = rendererConfig != null;
 
   return useSWR(
     isAllDataValid
-      ? ['selectedPagePreviewOptions', rendererConfig, pagePath, highlightKeywords]
+      ? [
+          'selectedPagePreviewOptions',
+          rendererConfig,
+          pagePath,
+          highlightKeywords,
+        ]
       : null,
-    async([, rendererConfig, pagePath, highlightKeywords]) => {
-      const { generateSimpleViewOptions } = await import('~/client/services/renderer/renderer');
-      return generateSimpleViewOptions(rendererConfig, pagePath, highlightKeywords);
+    async ([, rendererConfig, pagePath, highlightKeywords]) => {
+      const { generateSimpleViewOptions } = await import(
+        '~/client/services/renderer/renderer'
+      );
+      return generateSimpleViewOptions(
+        rendererConfig,
+        pagePath,
+        highlightKeywords,
+      );
     },
     {
       revalidateOnFocus: false,
@@ -159,17 +200,19 @@ export const useSearchResultOptions = useSelectedPagePreviewOptions;
 
 export const useTimelineOptions = useSelectedPagePreviewOptions;
 
-export const useCustomSidebarOptions = (config?: SWRConfiguration): SWRResponse<RendererOptions, Error> => {
+export const useCustomSidebarOptions = (
+  config?: SWRConfiguration,
+): SWRResponse<RendererOptions, Error> => {
   const rendererConfig = useRendererConfigExt();
 
   const isAllDataValid = rendererConfig != null;
 
   return useSWR(
-    isAllDataValid
-      ? ['customSidebarOptions', rendererConfig]
-      : null,
-    async([, rendererConfig]) => {
-      const { generateSimpleViewOptions } = await import('~/client/services/renderer/renderer');
+    isAllDataValid ? ['customSidebarOptions', rendererConfig] : null,
+    async ([, rendererConfig]) => {
+      const { generateSimpleViewOptions } = await import(
+        '~/client/services/renderer/renderer'
+      );
       return generateSimpleViewOptions(rendererConfig, '/');
     },
     {
@@ -181,7 +224,10 @@ export const useCustomSidebarOptions = (config?: SWRConfiguration): SWRResponse<
   );
 };
 
-export const usePresentationViewOptions = (): SWRResponse<RendererOptions, Error> => {
+export const usePresentationViewOptions = (): SWRResponse<
+  RendererOptions,
+  Error
+> => {
   const { data: currentPagePath } = useCurrentPagePath();
   const rendererConfig = useRendererConfigExt();
 
@@ -197,8 +243,10 @@ export const usePresentationViewOptions = (): SWRResponse<RendererOptions, Error
     isAllDataValid
       ? ['presentationViewOptions', currentPagePath, rendererConfig]
       : null,
-    async([, currentPagePath, rendererConfig]) => {
-      const { generatePresentationViewOptions } = await import('~/client/services/renderer/renderer');
+    async ([, currentPagePath, rendererConfig]) => {
+      const { generatePresentationViewOptions } = await import(
+        '~/client/services/renderer/renderer'
+      );
       return generatePresentationViewOptions(rendererConfig, currentPagePath);
     },
     {

+ 42 - 35
apps/app/src/stores/search.tsx

@@ -5,31 +5,34 @@ import { apiGet } from '~/client/util/apiv1-client';
 import type { IFormattedSearchResult } from '~/interfaces/search';
 import { SORT_AXIS, SORT_ORDER } from '~/interfaces/search';
 
-
 export type ISearchConfigurations = {
-  limit: number,
-  offset?: number,
-  sort?: SORT_AXIS,
-  order?: SORT_ORDER,
-  includeTrashPages?: boolean,
-  includeUserPages?: boolean,
-}
+  limit: number;
+  offset?: number;
+  sort?: SORT_AXIS;
+  order?: SORT_ORDER;
+  includeTrashPages?: boolean;
+  includeUserPages?: boolean;
+};
 
 type ISearchConfigurationsFixed = {
-  limit: number,
-  offset: number,
-  sort: SORT_AXIS,
-  order: SORT_ORDER,
-  includeTrashPages: boolean,
-  includeUserPages: boolean,
-}
+  limit: number;
+  offset: number;
+  sort: SORT_AXIS;
+  order: SORT_ORDER;
+  includeTrashPages: boolean;
+  includeUserPages: boolean;
+};
 
 export type ISearchConditions = ISearchConfigurationsFixed & {
-  keyword: string | null,
-  rawQuery: string,
-}
+  keyword: string | null;
+  rawQuery: string;
+};
 
-const createSearchQuery = (keyword: string, includeTrashPages: boolean, includeUserPages: boolean): string => {
+const createSearchQuery = (
+  keyword: string,
+  includeTrashPages: boolean,
+  includeUserPages: boolean,
+): string => {
   let query = keyword;
 
   // pages included in specific path are not retrived when prefix is added
@@ -43,18 +46,19 @@ const createSearchQuery = (keyword: string, includeTrashPages: boolean, includeU
   return query;
 };
 
-export const mutateSearching = async(): Promise<void[]> => {
-  return mutate(
-    key => Array.isArray(key) && key[0] === '/search',
-  );
+export const mutateSearching = async (): Promise<void[]> => {
+  return mutate((key) => Array.isArray(key) && key[0] === '/search');
 };
 
 export const useSWRxSearch = (
-    keyword: string | null, nqName: string | null, configurations: ISearchConfigurations,
-): SWRResponse<IFormattedSearchResult, Error> & { conditions: ISearchConditions } => {
-  const {
-    limit, offset, sort, order, includeTrashPages, includeUserPages,
-  } = configurations;
+  keyword: string | null,
+  nqName: string | null,
+  configurations: ISearchConfigurations,
+): SWRResponse<IFormattedSearchResult, Error> & {
+  conditions: ISearchConditions;
+} => {
+  const { limit, offset, sort, order, includeTrashPages, includeUserPages } =
+    configurations;
 
   const fixedConfigurations: ISearchConfigurationsFixed = {
     limit,
@@ -64,19 +68,22 @@ export const useSWRxSearch = (
     includeTrashPages: includeTrashPages ?? false,
     includeUserPages: includeUserPages ?? false,
   };
-  const rawQuery = createSearchQuery(keyword ?? '', fixedConfigurations.includeTrashPages, fixedConfigurations.includeUserPages);
+  const rawQuery = createSearchQuery(
+    keyword ?? '',
+    fixedConfigurations.includeTrashPages,
+    fixedConfigurations.includeUserPages,
+  );
 
   const isKeywordValid = keyword != null && keyword.length > 0;
 
   const swrResult = useSWR(
     isKeywordValid ? ['/search', keyword, fixedConfigurations] : null,
     ([endpoint, , fixedConfigurations]) => {
-      const {
-        limit, offset, sort, order,
-      } = fixedConfigurations;
+      const { limit, offset, sort, order } = fixedConfigurations;
 
       return apiGet(
-        endpoint, {
+        endpoint,
+        {
           q: encodeURIComponent(rawQuery),
           nq: typeof nqName === 'string' ? encodeURIComponent(nqName) : null,
           limit,
@@ -84,8 +91,8 @@ export const useSWRxSearch = (
           sort,
           order,
         },
-      // eslint-disable-next-line @typescript-eslint/no-explicit-any
-      ).then(result => result as IFormattedSearchResult);
+        // eslint-disable-next-line @typescript-eslint/no-explicit-any
+      ).then((result) => result as IFormattedSearchResult);
     },
     {
       keepPreviousData: true,

+ 7 - 3
apps/app/src/stores/share-link.tsx

@@ -5,12 +5,16 @@ import useSWR from 'swr';
 import { apiv3Get } from '~/client/util/apiv3-client';
 import type { IResShareLinkList } from '~/interfaces/share-link';
 
-const fetchShareLinks = async(endpoint, pageId) => {
-  const res = await apiv3Get<IResShareLinkList>(endpoint, { relatedPage: pageId });
+const fetchShareLinks = async (endpoint, pageId) => {
+  const res = await apiv3Get<IResShareLinkList>(endpoint, {
+    relatedPage: pageId,
+  });
   return res.data.shareLinksResult;
 };
 
-export const useSWRxSharelink = (currentPageId: Nullable<string>): SWRResponse<IResShareLinkList['shareLinksResult'], Error> => {
+export const useSWRxSharelink = (
+  currentPageId: Nullable<string>,
+): SWRResponse<IResShareLinkList['shareLinksResult'], Error> => {
   return useSWR(
     currentPageId == null ? null : ['/share-links/', currentPageId],
     ([endpoint]) => fetchShareLinks(endpoint, currentPageId),

+ 0 - 1
apps/app/src/stores/socket-io.ts

@@ -7,7 +7,6 @@ import loggerFactory from '~/utils/logger';
 
 const logger = loggerFactory('growi:cli:stores:socket-io');
 
-
 const socketFactory = (namespace: string): Socket => {
   const socket = io(namespace, {
     transports: ['websocket'],

+ 2 - 3
apps/app/src/stores/staff.tsx

@@ -4,9 +4,8 @@ import useSWR from 'swr';
 import { apiv3Get } from '~/client/util/apiv3-client';
 
 export const useSWRxStaffs = (): SWRResponse<any, Error> => {
-  return useSWR(
-    '/staffs',
-    endpoint => apiv3Get(endpoint).then((response) => {
+  return useSWR('/staffs', (endpoint) =>
+    apiv3Get(endpoint).then((response) => {
       return response.data.contributors;
     }),
   );

+ 15 - 4
apps/app/src/stores/tag.tsx

@@ -4,10 +4,16 @@ import useSWR from 'swr';
 import { apiGet } from '~/client/util/apiv1-client';
 import type { IResTagsListApiv1, IResTagsSearchApiv1 } from '~/interfaces/tag';
 
-export const useSWRxTagsList = (limit?: number, offset?: number): SWRResponse<IResTagsListApiv1, Error> => {
+export const useSWRxTagsList = (
+  limit?: number,
+  offset?: number,
+): SWRResponse<IResTagsListApiv1, Error> => {
   return useSWR(
     ['/tags.list', limit, offset],
-    ([endpoint, limit, offset]) => apiGet(endpoint, { limit, offset }).then((result: IResTagsListApiv1) => result),
+    ([endpoint, limit, offset]) =>
+      apiGet(endpoint, { limit, offset }).then(
+        (result: IResTagsListApiv1) => result,
+      ),
     {
       keepPreviousData: true,
       revalidateOnFocus: false,
@@ -16,10 +22,15 @@ export const useSWRxTagsList = (limit?: number, offset?: number): SWRResponse<IR
   );
 };
 
-export const useSWRxTagsSearch = (query: string): SWRResponse<IResTagsSearchApiv1, Error> => {
+export const useSWRxTagsSearch = (
+  query: string,
+): SWRResponse<IResTagsSearchApiv1, Error> => {
   return useSWR(
     ['/tags.search', query],
-    ([endpoint, query]) => apiGet(endpoint, { q: query }).then((result: IResTagsSearchApiv1) => result),
+    ([endpoint, query]) =>
+      apiGet(endpoint, { q: query }).then(
+        (result: IResTagsSearchApiv1) => result,
+      ),
     {
       keepPreviousData: true,
       revalidateOnFocus: false,

+ 287 - 134
apps/app/src/stores/ui.tsx

@@ -1,19 +1,20 @@
+import { type RefObject, useCallback, useEffect, useLayoutEffect } from 'react';
+import { useRouter } from 'next/router';
+import { type Nullable, PageGrant } from '@growi/core';
 import {
-  type RefObject, useCallback, useEffect,
-  useLayoutEffect,
-} from 'react';
-
-import { PageGrant, type Nullable } from '@growi/core';
-import { type SWRResponseWithUtils, useSWRStatic, withUtils } from '@growi/core/dist/swr';
-import { pagePathUtils, isClient } from '@growi/core/dist/utils';
+  type SWRResponseWithUtils,
+  useSWRStatic,
+  withUtils,
+} from '@growi/core/dist/swr';
+import { isClient, pagePathUtils } from '@growi/core/dist/utils';
 import { Breakpoint } from '@growi/ui/dist/interfaces';
-import { addBreakpointListener, cleanupBreakpointListener } from '@growi/ui/dist/utils';
-import { useRouter } from 'next/router';
+import {
+  addBreakpointListener,
+  cleanupBreakpointListener,
+} from '@growi/ui/dist/utils';
 import type { HtmlElementNode } from 'rehype-toc';
 import type { MutatorOptions } from 'swr';
-import {
-  useSWRConfig, type SWRResponse, type Key,
-} from 'swr';
+import { type Key, type SWRResponse, useSWRConfig } from 'swr';
 import useSWRImmutable from 'swr/immutable';
 
 import { scheduleToPut } from '~/client/services/user-ui-settings';
@@ -21,13 +22,20 @@ import type { IPageSelectedGrant } from '~/interfaces/page';
 import { SidebarContentsType, SidebarMode } from '~/interfaces/ui';
 import type { UpdateDescCountData } from '~/interfaces/websocket';
 import {
-  useIsEditable, useIsReadOnlyUser,
-  useIsSharedUser, useIsIdenticalPath, useCurrentUser, useShareLinkId,
+  useCurrentPageId,
+  useCurrentPagePath,
+  useIsNotFound,
+  useIsTrashPage,
+} from '~/stores/page';
+import {
+  useCurrentUser,
+  useIsEditable,
+  useIsIdenticalPath,
+  useIsReadOnlyUser,
+  useIsSharedUser,
+  useShareLinkId,
 } from '~/stores-universal/context';
 import { EditorMode, useEditorMode } from '~/stores-universal/ui';
-import {
-  useIsNotFound, useCurrentPagePath, useIsTrashPage, useCurrentPageId,
-} from '~/stores/page';
 import loggerFactory from '~/utils/logger';
 
 import { useStaticSWR } from './use-static-swr';
@@ -36,7 +44,6 @@ const { isTrashTopPage, isUsersTopPage } = pagePathUtils;
 
 const logger = loggerFactory('growi:stores:ui');
 
-
 /** **********************************************************
  *                     Storing objects to ref
  *********************************************************** */
@@ -52,8 +59,13 @@ export const useCurrentPageTocNode = (): SWRResponse<HtmlElementNode, any> => {
  *                      for switching UI
  *********************************************************** */
 
-export const useSidebarScrollerRef = (initialData?: RefObject<HTMLDivElement | null>): SWRResponse<RefObject<HTMLDivElement | null>, Error> => {
-  return useSWRStatic<RefObject<HTMLDivElement | null>, Error>('sidebarScrollerRef', initialData);
+export const useSidebarScrollerRef = (
+  initialData?: RefObject<HTMLDivElement | null>,
+): SWRResponse<RefObject<HTMLDivElement | null>, Error> => {
+  return useSWRStatic<RefObject<HTMLDivElement | null>, Error>(
+    'sidebarScrollerRef',
+    initialData,
+  );
 };
 
 //
@@ -65,21 +77,21 @@ export const useIsMobile = (): SWRResponse<boolean, Error> => {
   };
 
   if (isClient()) {
-
     // Ref: https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent#mobile_device_detection
     let hasTouchScreen = false;
-    hasTouchScreen = ('maxTouchPoints' in navigator) ? navigator?.maxTouchPoints > 0 : false;
+    hasTouchScreen =
+      'maxTouchPoints' in navigator ? navigator?.maxTouchPoints > 0 : false;
 
     if (!hasTouchScreen) {
       const mQ = matchMedia?.('(pointer:coarse)');
       if (mQ?.media === '(pointer:coarse)') {
         hasTouchScreen = !!mQ.matches;
-      }
-      else {
-      // Only as a last resort, fall back to user agent sniffing
+      } else {
+        // Only as a last resort, fall back to user agent sniffing
         const UA = navigator.userAgent;
-        hasTouchScreen = /\b(BlackBerry|webOS|iPhone|IEMobile)\b/i.test(UA)
-      || /\b(Android|Windows Phone|iPad|iPod)\b/i.test(UA);
+        hasTouchScreen =
+          /\b(BlackBerry|webOS|iPhone|IEMobile)\b/i.test(UA) ||
+          /\b(Android|Windows Phone|iPad|iPod)\b/i.test(UA);
       }
     }
 
@@ -98,7 +110,7 @@ export const useIsDeviceLargerThanMd = (): SWRResponse<boolean, Error> => {
 
   useEffect(() => {
     if (key != null) {
-      const mdOrAvobeHandler = function(this: MediaQueryList): void {
+      const mdOrAvobeHandler = function (this: MediaQueryList): void {
         // sm -> md: matches will be true
         // md -> sm: matches will be false
         mutate(key, this.matches);
@@ -126,7 +138,7 @@ export const useIsDeviceLargerThanLg = (): SWRResponse<boolean, Error> => {
 
   useEffect(() => {
     if (key != null) {
-      const lgOrAvobeHandler = function(this: MediaQueryList): void {
+      const lgOrAvobeHandler = function (this: MediaQueryList): void {
         // md -> lg: matches will be true
         // lg -> md: matches will be false
         mutate(key, this.matches);
@@ -154,7 +166,7 @@ export const useIsDeviceLargerThanXl = (): SWRResponse<boolean, Error> => {
 
   useEffect(() => {
     if (key != null) {
-      const xlOrAvobeHandler = function(this: MediaQueryList): void {
+      const xlOrAvobeHandler = function (this: MediaQueryList): void {
         // lg -> xl: matches will be true
         // xl -> lg: matches will be false
         mutate(key, this.matches);
@@ -175,23 +187,34 @@ export const useIsDeviceLargerThanXl = (): SWRResponse<boolean, Error> => {
   return useSWRStatic(key);
 };
 
-
-type MutateAndSaveUserUISettings<Data> = (data: Data, opts?: boolean | MutatorOptions<Data>) => Promise<Data | undefined>;
+type MutateAndSaveUserUISettings<Data> = (
+  data: Data,
+  opts?: boolean | MutatorOptions<Data>,
+) => Promise<Data | undefined>;
 type MutateAndSaveUserUISettingsUtils<Data> = {
   mutateAndSave: MutateAndSaveUserUISettings<Data>;
-}
+};
 
 export const useCurrentSidebarContents = (
-    initialData?: SidebarContentsType,
-): SWRResponseWithUtils<MutateAndSaveUserUISettingsUtils<SidebarContentsType>, SidebarContentsType> => {
-  const swrResponse = useSWRStatic('sidebarContents', initialData, { fallbackData: SidebarContentsType.TREE });
+  initialData?: SidebarContentsType,
+): SWRResponseWithUtils<
+  MutateAndSaveUserUISettingsUtils<SidebarContentsType>,
+  SidebarContentsType
+> => {
+  const swrResponse = useSWRStatic('sidebarContents', initialData, {
+    fallbackData: SidebarContentsType.TREE,
+  });
 
   const { mutate } = swrResponse;
 
-  const mutateAndSave: MutateAndSaveUserUISettings<SidebarContentsType> = useCallback((data, opts?) => {
-    scheduleToPut({ currentSidebarContents: data });
-    return mutate(data, opts);
-  }, [mutate]);
+  const mutateAndSave: MutateAndSaveUserUISettings<SidebarContentsType> =
+    useCallback(
+      (data, opts?) => {
+        scheduleToPut({ currentSidebarContents: data });
+        return mutate(data, opts);
+      },
+      [mutate],
+    );
 
   return withUtils(swrResponse, { mutateAndSave });
 };
@@ -200,74 +223,131 @@ export const usePageControlsX = (initialData?: number): SWRResponse<number> => {
   return useSWRStatic('pageControlsX', initialData);
 };
 
-export const useCurrentProductNavWidth = (initialData?: number): SWRResponseWithUtils<MutateAndSaveUserUISettingsUtils<number>, number> => {
-  const swrResponse = useSWRStatic('productNavWidth', initialData, { fallbackData: 320 });
+export const useCurrentProductNavWidth = (
+  initialData?: number,
+): SWRResponseWithUtils<MutateAndSaveUserUISettingsUtils<number>, number> => {
+  const swrResponse = useSWRStatic('productNavWidth', initialData, {
+    fallbackData: 320,
+  });
 
   const { mutate } = swrResponse;
 
-  const mutateAndSave: MutateAndSaveUserUISettings<number> = useCallback((data, opts?) => {
-    scheduleToPut({ currentProductNavWidth: data });
-    return mutate(data, opts);
-  }, [mutate]);
+  const mutateAndSave: MutateAndSaveUserUISettings<number> = useCallback(
+    (data, opts?) => {
+      scheduleToPut({ currentProductNavWidth: data });
+      return mutate(data, opts);
+    },
+    [mutate],
+  );
 
   return withUtils(swrResponse, { mutateAndSave });
 };
 
-export const usePreferCollapsedMode = (initialData?: boolean): SWRResponseWithUtils<MutateAndSaveUserUISettingsUtils<boolean>, boolean> => {
-  const swrResponse = useSWRStatic('isPreferCollapsedMode', initialData, { fallbackData: false });
+export const usePreferCollapsedMode = (
+  initialData?: boolean,
+): SWRResponseWithUtils<MutateAndSaveUserUISettingsUtils<boolean>, boolean> => {
+  const swrResponse = useSWRStatic('isPreferCollapsedMode', initialData, {
+    fallbackData: false,
+  });
 
   const { mutate } = swrResponse;
 
-  const mutateAndSave: MutateAndSaveUserUISettings<boolean> = useCallback((data, opts?) => {
-    scheduleToPut({ preferCollapsedModeByUser: data });
-    return mutate(data, opts);
-  }, [mutate]);
+  const mutateAndSave: MutateAndSaveUserUISettings<boolean> = useCallback(
+    (data, opts?) => {
+      scheduleToPut({ preferCollapsedModeByUser: data });
+      return mutate(data, opts);
+    },
+    [mutate],
+  );
 
   return withUtils(swrResponse, { mutateAndSave });
 };
 
-export const useCollapsedContentsOpened = (initialData?: boolean): SWRResponse<boolean> => {
-  return useSWRStatic('isCollapsedContentsOpened', initialData, { fallbackData: false });
+export const useCollapsedContentsOpened = (
+  initialData?: boolean,
+): SWRResponse<boolean> => {
+  return useSWRStatic('isCollapsedContentsOpened', initialData, {
+    fallbackData: false,
+  });
 };
 
-export const useDrawerOpened = (isOpened?: boolean): SWRResponse<boolean, Error> => {
+export const useDrawerOpened = (
+  isOpened?: boolean,
+): SWRResponse<boolean, Error> => {
   return useSWRStatic('isDrawerOpened', isOpened, { fallbackData: false });
 };
 
 type DetectSidebarModeUtils = {
-  isDrawerMode(): boolean
-  isCollapsedMode(): boolean
-  isDockMode(): boolean
-}
+  isDrawerMode(): boolean;
+  isCollapsedMode(): boolean;
+  isDockMode(): boolean;
+};
 
-export const useSidebarMode = (): SWRResponseWithUtils<DetectSidebarModeUtils, SidebarMode> => {
+export const useSidebarMode = (): SWRResponseWithUtils<
+  DetectSidebarModeUtils,
+  SidebarMode
+> => {
   const { data: isDeviceLargerThanXl } = useIsDeviceLargerThanXl();
   const { data: editorMode } = useEditorMode();
   const { data: isCollapsedModeUnderDockMode } = usePreferCollapsedMode();
 
-  const condition = isDeviceLargerThanXl != null && editorMode != null && isCollapsedModeUnderDockMode != null;
+  const condition =
+    isDeviceLargerThanXl != null &&
+    editorMode != null &&
+    isCollapsedModeUnderDockMode != null;
 
   const isEditorMode = editorMode === EditorMode.Editor;
 
-  const fetcher = useCallback((
-      [, isDeviceLargerThanXl, isEditorMode, isCollapsedModeUnderDockMode]: [Key, boolean|undefined, boolean|undefined, boolean|undefined],
-  ) => {
-    if (!isDeviceLargerThanXl) {
-      return SidebarMode.DRAWER;
-    }
-    return isEditorMode || isCollapsedModeUnderDockMode ? SidebarMode.COLLAPSED : SidebarMode.DOCK;
-  }, []);
+  const fetcher = useCallback(
+    ([, isDeviceLargerThanXl, isEditorMode, isCollapsedModeUnderDockMode]: [
+      Key,
+      boolean | undefined,
+      boolean | undefined,
+      boolean | undefined,
+    ]) => {
+      if (!isDeviceLargerThanXl) {
+        return SidebarMode.DRAWER;
+      }
+      return isEditorMode || isCollapsedModeUnderDockMode
+        ? SidebarMode.COLLAPSED
+        : SidebarMode.DOCK;
+    },
+    [],
+  );
 
   const swrResponse = useSWRImmutable(
-    condition ? ['sidebarMode', isDeviceLargerThanXl, isEditorMode, isCollapsedModeUnderDockMode] : null,
+    condition
+      ? [
+          'sidebarMode',
+          isDeviceLargerThanXl,
+          isEditorMode,
+          isCollapsedModeUnderDockMode,
+        ]
+      : null,
     // calcDrawerMode,
     fetcher,
-    { fallbackData: fetcher(['sidebarMode', isDeviceLargerThanXl, isEditorMode, isCollapsedModeUnderDockMode]) },
+    {
+      fallbackData: fetcher([
+        'sidebarMode',
+        isDeviceLargerThanXl,
+        isEditorMode,
+        isCollapsedModeUnderDockMode,
+      ]),
+    },
   );
 
-  const _isDrawerMode = useCallback(() => swrResponse.data === SidebarMode.DRAWER, [swrResponse.data]);
-  const _isCollapsedMode = useCallback(() => swrResponse.data === SidebarMode.COLLAPSED, [swrResponse.data]);
-  const _isDockMode = useCallback(() => swrResponse.data === SidebarMode.DOCK, [swrResponse.data]);
+  const _isDrawerMode = useCallback(
+    () => swrResponse.data === SidebarMode.DRAWER,
+    [swrResponse.data],
+  );
+  const _isCollapsedMode = useCallback(
+    () => swrResponse.data === SidebarMode.COLLAPSED,
+    [swrResponse.data],
+  );
+  const _isDockMode = useCallback(
+    () => swrResponse.data === SidebarMode.DOCK,
+    [swrResponse.data],
+  );
 
   return {
     ...swrResponse,
@@ -277,63 +357,93 @@ export const useSidebarMode = (): SWRResponseWithUtils<DetectSidebarModeUtils, S
   };
 };
 
-export const useSelectedGrant = (initialData?: Nullable<IPageSelectedGrant>): SWRResponse<Nullable<IPageSelectedGrant>, Error> => {
-  return useSWRStatic<Nullable<IPageSelectedGrant>, Error>('selectedGrant', initialData, { fallbackData: { grant: PageGrant.GRANT_PUBLIC } });
+export const useSelectedGrant = (
+  initialData?: Nullable<IPageSelectedGrant>,
+): SWRResponse<Nullable<IPageSelectedGrant>, Error> => {
+  return useSWRStatic<Nullable<IPageSelectedGrant>, Error>(
+    'selectedGrant',
+    initialData,
+    { fallbackData: { grant: PageGrant.GRANT_PUBLIC } },
+  );
 };
 
 type PageTreeDescCountMapUtils = {
-  update(newData?: UpdateDescCountData): Promise<UpdateDescCountData | undefined>
-  getDescCount(pageId?: string): number | null | undefined
-}
+  update(
+    newData?: UpdateDescCountData,
+  ): Promise<UpdateDescCountData | undefined>;
+  getDescCount(pageId?: string): number | null | undefined;
+};
 
-export const usePageTreeDescCountMap = (initialData?: UpdateDescCountData): SWRResponse<UpdateDescCountData, Error> & PageTreeDescCountMapUtils => {
+export const usePageTreeDescCountMap = (
+  initialData?: UpdateDescCountData,
+): SWRResponse<UpdateDescCountData, Error> & PageTreeDescCountMapUtils => {
   const key = 'pageTreeDescCountMap';
 
-  const swrResponse = useStaticSWR<UpdateDescCountData, Error>(key, initialData, { fallbackData: new Map() });
+  const swrResponse = useStaticSWR<UpdateDescCountData, Error>(
+    key,
+    initialData,
+    { fallbackData: new Map() },
+  );
 
   return {
     ...swrResponse,
-    getDescCount: (pageId?: string) => (pageId != null ? swrResponse.data?.get(pageId) : null),
-    update: (newData: UpdateDescCountData) => swrResponse.mutate(new Map([...(swrResponse.data || new Map()), ...newData])),
+    getDescCount: (pageId?: string) =>
+      pageId != null ? swrResponse.data?.get(pageId) : null,
+    update: (newData: UpdateDescCountData) =>
+      swrResponse.mutate(
+        new Map([...(swrResponse.data || new Map()), ...newData]),
+      ),
   };
 };
 
-
 type UseCommentEditorDirtyMapOperation = {
-  evaluate(key: string, commentBody: string): Promise<number>,
-  clean(key: string): Promise<number>,
-}
+  evaluate(key: string, commentBody: string): Promise<number>;
+  clean(key: string): Promise<number>;
+};
 
-export const useCommentEditorDirtyMap = (): SWRResponse<Map<string, boolean>, Error> & UseCommentEditorDirtyMapOperation => {
+export const useCommentEditorDirtyMap = (): SWRResponse<
+  Map<string, boolean>,
+  Error
+> &
+  UseCommentEditorDirtyMapOperation => {
   const router = useRouter();
 
-  const swrResponse = useSWRStatic<Map<string, boolean>, Error>('editingCommentsNum', undefined, { fallbackData: new Map() });
+  const swrResponse = useSWRStatic<Map<string, boolean>, Error>(
+    'editingCommentsNum',
+    undefined,
+    { fallbackData: new Map() },
+  );
 
   const { mutate } = swrResponse;
 
-  const evaluate = useCallback(async(key: string, commentBody: string) => {
-    const newMap = await mutate((map) => {
-      if (map == null) return new Map();
-
-      if (commentBody.length === 0) {
+  const evaluate = useCallback(
+    async (key: string, commentBody: string) => {
+      const newMap = await mutate((map) => {
+        if (map == null) return new Map();
+
+        if (commentBody.length === 0) {
+          map.delete(key);
+        } else {
+          map.set(key, true);
+        }
+
+        return map;
+      });
+      return newMap?.size ?? 0;
+    },
+    [mutate],
+  );
+  const clean = useCallback(
+    async (key: string) => {
+      const newMap = await mutate((map) => {
+        if (map == null) return new Map();
         map.delete(key);
-      }
-      else {
-        map.set(key, true);
-      }
-
-      return map;
-    });
-    return newMap?.size ?? 0;
-  }, [mutate]);
-  const clean = useCallback(async(key: string) => {
-    const newMap = await mutate((map) => {
-      if (map == null) return new Map();
-      map.delete(key);
-      return map;
-    });
-    return newMap?.size ?? 0;
-  }, [mutate]);
+        return map;
+      });
+      return newMap?.size ?? 0;
+    },
+    [mutate],
+  );
 
   const reset = useCallback(() => mutate(new Map()), [mutate]);
 
@@ -351,13 +461,15 @@ export const useCommentEditorDirtyMap = (): SWRResponse<Map<string, boolean>, Er
   };
 };
 
-
 /** **********************************************************
  *                          SWR Hooks
  *                Determined value by context
  *********************************************************** */
 
-export const useIsAbleToShowTrashPageManagementButtons = (): SWRResponse<boolean, Error> => {
+export const useIsAbleToShowTrashPageManagementButtons = (): SWRResponse<
+  boolean,
+  Error
+> => {
   const key = 'isAbleToShowTrashPageManagementButtons';
 
   const { data: _currentUser } = useCurrentUser();
@@ -371,15 +483,27 @@ export const useIsAbleToShowTrashPageManagementButtons = (): SWRResponse<boolean
   const isTrashPage = isPageExist && _isTrashPage === true;
   const isReadOnlyUser = isPageExist && _isReadOnlyUser === true;
 
-  const includesUndefined = [_currentUser, _currentPageId, _isNotFound, _isReadOnlyUser, _isTrashPage].some(v => v === undefined);
+  const includesUndefined = [
+    _currentUser,
+    _currentPageId,
+    _isNotFound,
+    _isReadOnlyUser,
+    _isTrashPage,
+  ].some((v) => v === undefined);
 
   return useSWRImmutable(
-    includesUndefined ? null : [key, isTrashPage, isCurrentUserExist, isReadOnlyUser],
-    ([, isTrashPage, isCurrentUserExist, isReadOnlyUser]) => isTrashPage && isCurrentUserExist && !isReadOnlyUser,
+    includesUndefined
+      ? null
+      : [key, isTrashPage, isCurrentUserExist, isReadOnlyUser],
+    ([, isTrashPage, isCurrentUserExist, isReadOnlyUser]) =>
+      isTrashPage && isCurrentUserExist && !isReadOnlyUser,
   );
 };
 
-export const useIsAbleToShowPageManagement = (): SWRResponse<boolean, Error> => {
+export const useIsAbleToShowPageManagement = (): SWRResponse<
+  boolean,
+  Error
+> => {
   const key = 'isAbleToShowPageManagement';
   const { data: currentPageId } = useCurrentPageId();
   const { data: _isTrashPage } = useIsTrashPage();
@@ -387,15 +511,23 @@ export const useIsAbleToShowPageManagement = (): SWRResponse<boolean, Error> =>
   const { data: isNotFound } = useIsNotFound();
 
   const pageId = currentPageId;
-  const includesUndefined = [pageId, _isTrashPage, _isSharedUser, isNotFound].some(v => v === undefined);
-  const isPageExist = (pageId != null) && isNotFound === false;
-  const isEmptyPage = (pageId != null) && isNotFound === true;
+  const includesUndefined = [
+    pageId,
+    _isTrashPage,
+    _isSharedUser,
+    isNotFound,
+  ].some((v) => v === undefined);
+  const isPageExist = pageId != null && isNotFound === false;
+  const isEmptyPage = pageId != null && isNotFound === true;
   const isTrashPage = isPageExist && _isTrashPage === true;
   const isSharedUser = isPageExist && _isSharedUser === true;
 
   return useSWRImmutable(
-    includesUndefined ? null : [key, pageId, isPageExist, isEmptyPage, isTrashPage, isSharedUser],
-    ([, , isPageExist, isEmptyPage, isTrashPage, isSharedUser]) => (isPageExist && !isTrashPage && !isSharedUser) || isEmptyPage,
+    includesUndefined
+      ? null
+      : [key, pageId, isPageExist, isEmptyPage, isTrashPage, isSharedUser],
+    ([, , isPageExist, isEmptyPage, isTrashPage, isSharedUser]) =>
+      (isPageExist && !isTrashPage && !isSharedUser) || isEmptyPage,
   );
 };
 
@@ -408,15 +540,35 @@ export const useIsAbleToShowTagLabel = (): SWRResponse<boolean, Error> => {
   const { data: editorMode } = useEditorMode();
   const { data: shareLinkId } = useShareLinkId();
 
-  const includesUndefined = [currentPagePath, isIdenticalPath, isNotFound, editorMode].some(v => v === undefined);
+  const includesUndefined = [
+    currentPagePath,
+    isIdenticalPath,
+    isNotFound,
+    editorMode,
+  ].some((v) => v === undefined);
 
   const isViewMode = editorMode === EditorMode.View;
 
   return useSWRImmutable(
-    includesUndefined ? null : [key, pageId, currentPagePath, isIdenticalPath, isNotFound, editorMode, shareLinkId],
+    includesUndefined
+      ? null
+      : [
+          key,
+          pageId,
+          currentPagePath,
+          isIdenticalPath,
+          isNotFound,
+          editorMode,
+          shareLinkId,
+        ],
     // "/trash" page does not exist on page collection and unable to add tags
     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-    () => !isUsersTopPage(currentPagePath!) && !isTrashTopPage(currentPagePath!) && shareLinkId == null && !isIdenticalPath && !(isViewMode && isNotFound),
+    () =>
+      !isUsersTopPage(currentPagePath!) &&
+      !isTrashTopPage(currentPagePath!) &&
+      shareLinkId == null &&
+      !isIdenticalPath &&
+      !(isViewMode && isNotFound),
   );
 };
 
@@ -425,7 +577,9 @@ export const useIsAbleToChangeEditorMode = (): SWRResponse<boolean, Error> => {
   const { data: isEditable } = useIsEditable();
   const { data: isSharedUser } = useIsSharedUser();
 
-  const includesUndefined = [isEditable, isSharedUser].some(v => v === undefined);
+  const includesUndefined = [isEditable, isSharedUser].some(
+    (v) => v === undefined,
+  );
 
   return useSWRImmutable(
     includesUndefined ? null : [key, isEditable, isSharedUser],
@@ -439,8 +593,10 @@ export const useIsAbleToShowPageAuthors = (): SWRResponse<boolean, Error> => {
   const { data: pagePath } = useCurrentPagePath();
   const { data: isNotFound } = useIsNotFound();
 
-  const includesUndefined = [pageId, pagePath, isNotFound].some(v => v === undefined);
-  const isPageExist = (pageId != null) && !isNotFound;
+  const includesUndefined = [pageId, pagePath, isNotFound].some(
+    (v) => v === undefined,
+  );
+  const isPageExist = pageId != null && !isNotFound;
   const isUsersTopPagePath = pagePath != null && isUsersTopPage(pagePath);
 
   return useSWRImmutable(
@@ -454,10 +610,7 @@ export const useIsUntitledPage = (): SWRResponse<boolean> => {
 
   const { data: pageId } = useCurrentPageId();
 
-  return useSWRStatic(
-    pageId == null ? null : [key, pageId],
-    undefined,
-    { fallbackData: false },
-  );
-
+  return useSWRStatic(pageId == null ? null : [key, pageId], undefined, {
+    fallbackData: false,
+  });
 };

+ 6 - 2
apps/app/src/stores/use-editing-clients.ts

@@ -2,6 +2,10 @@ import { useSWRStatic } from '@growi/core/dist/swr';
 import type { EditingClient } from '@growi/editor';
 import type { SWRResponse } from 'swr';
 
-export const useEditingClients = (status?: EditingClient[]): SWRResponse<EditingClient[], Error> => {
-  return useSWRStatic<EditingClient[], Error>('editingUsers', status, { fallbackData: [] });
+export const useEditingClients = (
+  status?: EditingClient[],
+): SWRResponse<EditingClient[], Error> => {
+  return useSWRStatic<EditingClient[], Error>('editingUsers', status, {
+    fallbackData: [],
+  });
 };

+ 93 - 31
apps/app/src/stores/user-group.tsx

@@ -1,5 +1,7 @@
 import type {
-  IPageHasId, IUserGroupHasId, IUserGroupRelationHasId,
+  IPageHasId,
+  IUserGroupHasId,
+  IUserGroupRelationHasId,
 } from '@growi/core';
 import { type SWRResponseWithUtils, withUtils } from '@growi/core/dist/swr';
 import type { SWRResponse } from 'swr';
@@ -7,23 +9,41 @@ import useSWRImmutable from 'swr/immutable';
 
 import { apiv3Get, apiv3Put } from '~/client/util/apiv3-client';
 import type {
+  AncestorUserGroupsResult,
+  ChildUserGroupListResult,
   IUserGroupRelationHasIdPopulatedUser,
-  UserGroupResult, UserGroupListResult, ChildUserGroupListResult, UserGroupRelationListResult, UserGroupRelationsResult,
-  UserGroupPagesResult, SelectableParentUserGroupsResult, SelectableUserChildGroupsResult, AncestorUserGroupsResult,
+  SelectableParentUserGroupsResult,
+  SelectableUserChildGroupsResult,
+  UserGroupListResult,
+  UserGroupPagesResult,
+  UserGroupRelationListResult,
+  UserGroupRelationsResult,
+  UserGroupResult,
 } from '~/interfaces/user-group-response';
 
-export const useSWRxUserGroup = (groupId: string | null): SWRResponse<IUserGroupHasId, Error> => {
+export const useSWRxUserGroup = (
+  groupId: string | null,
+): SWRResponse<IUserGroupHasId, Error> => {
   return useSWRImmutable(
     groupId != null ? `/user-groups/${groupId}` : null,
-    endpoint => apiv3Get<UserGroupResult>(endpoint).then(result => result.data.userGroup),
+    (endpoint) =>
+      apiv3Get<UserGroupResult>(endpoint).then(
+        (result) => result.data.userGroup,
+      ),
   );
 };
 
-export const useSWRxUserGroupList = (initialData?: IUserGroupHasId[], isExternalGroup = false): SWRResponse<IUserGroupHasId[], Error> => {
+export const useSWRxUserGroupList = (
+  initialData?: IUserGroupHasId[],
+  isExternalGroup = false,
+): SWRResponse<IUserGroupHasId[], Error> => {
   const url = isExternalGroup ? '/external-user-groups' : '/user-groups';
   return useSWRImmutable(
     url,
-    endpoint => apiv3Get<UserGroupListResult>(endpoint, { pagination: false }).then(result => result.data.userGroups),
+    (endpoint) =>
+      apiv3Get<UserGroupListResult>(endpoint, { pagination: false }).then(
+        (result) => result.data.userGroups,
+      ),
     {
       fallbackData: initialData,
     },
@@ -31,21 +51,30 @@ export const useSWRxUserGroupList = (initialData?: IUserGroupHasId[], isExternal
 };
 
 type ChildUserGroupListUtils = {
-  updateChild(childGroupData: IUserGroupHasId): Promise<void>, // update one child and refresh list
-}
+  updateChild(childGroupData: IUserGroupHasId): Promise<void>; // update one child and refresh list
+};
 export const useSWRxChildUserGroupList = (
-    parentIds?: string[], includeGrandChildren?: boolean,
-): SWRResponseWithUtils<ChildUserGroupListUtils, ChildUserGroupListResult, Error> => {
+  parentIds?: string[],
+  includeGrandChildren?: boolean,
+): SWRResponseWithUtils<
+  ChildUserGroupListUtils,
+  ChildUserGroupListResult,
+  Error
+> => {
   const shouldFetch = parentIds != null && parentIds.length > 0;
 
   const swrResponse = useSWRImmutable(
-    shouldFetch ? ['/user-groups/children', parentIds, includeGrandChildren] : null,
-    ([endpoint, parentIds, includeGrandChildren]) => apiv3Get<ChildUserGroupListResult>(
-      endpoint, { parentIds, includeGrandChildren },
-    ).then((result => result.data)),
+    shouldFetch
+      ? ['/user-groups/children', parentIds, includeGrandChildren]
+      : null,
+    ([endpoint, parentIds, includeGrandChildren]) =>
+      apiv3Get<ChildUserGroupListResult>(endpoint, {
+        parentIds,
+        includeGrandChildren,
+      }).then((result) => result.data),
   );
 
-  const updateChild = async(childGroupData: IUserGroupHasId) => {
+  const updateChild = async (childGroupData: IUserGroupHasId) => {
     await apiv3Put(`/user-groups/${childGroupData._id}`, {
       name: childGroupData.name,
       description: childGroupData.description,
@@ -57,51 +86,84 @@ export const useSWRxChildUserGroupList = (
   return withUtils(swrResponse, { updateChild });
 };
 
-export const useSWRxUserGroupRelations = (groupId: string | null): SWRResponse<IUserGroupRelationHasIdPopulatedUser[], Error> => {
+export const useSWRxUserGroupRelations = (
+  groupId: string | null,
+): SWRResponse<IUserGroupRelationHasIdPopulatedUser[], Error> => {
   return useSWRImmutable(
     groupId != null ? `/user-groups/${groupId}/user-group-relations` : null,
-    endpoint => apiv3Get<UserGroupRelationsResult>(endpoint).then(result => result.data.userGroupRelations),
+    (endpoint) =>
+      apiv3Get<UserGroupRelationsResult>(endpoint).then(
+        (result) => result.data.userGroupRelations,
+      ),
   );
 };
 
 export const useSWRxUserGroupRelationList = (
-    groupIds: string[] | null, childGroupIds?: string[], initialData?: IUserGroupRelationHasId[],
+  groupIds: string[] | null,
+  childGroupIds?: string[],
+  initialData?: IUserGroupRelationHasId[],
 ): SWRResponse<IUserGroupRelationHasId[], Error> => {
   return useSWRImmutable(
-    groupIds != null ? ['/user-group-relations', groupIds, childGroupIds] : null,
-    ([endpoint, groupIds, childGroupIds]) => apiv3Get<UserGroupRelationListResult>(
-      endpoint, { groupIds, childGroupIds },
-    ).then(result => result.data.userGroupRelations),
+    groupIds != null
+      ? ['/user-group-relations', groupIds, childGroupIds]
+      : null,
+    ([endpoint, groupIds, childGroupIds]) =>
+      apiv3Get<UserGroupRelationListResult>(endpoint, {
+        groupIds,
+        childGroupIds,
+      }).then((result) => result.data.userGroupRelations),
     {
       fallbackData: initialData,
     },
   );
 };
 
-export const useSWRxUserGroupPages = (groupId: string | undefined, limit: number, offset: number): SWRResponse<IPageHasId[], Error> => {
+export const useSWRxUserGroupPages = (
+  groupId: string | undefined,
+  limit: number,
+  offset: number,
+): SWRResponse<IPageHasId[], Error> => {
   return useSWRImmutable(
     groupId != null ? [`/user-groups/${groupId}/pages`, limit, offset] : null,
-    ([endpoint, limit, offset]) => apiv3Get<UserGroupPagesResult>(endpoint, { limit, offset }).then(result => result.data.pages),
+    ([endpoint, limit, offset]) =>
+      apiv3Get<UserGroupPagesResult>(endpoint, { limit, offset }).then(
+        (result) => result.data.pages,
+      ),
   );
 };
 
-export const useSWRxSelectableParentUserGroups = (groupId: string | null): SWRResponse<IUserGroupHasId[], Error> => {
+export const useSWRxSelectableParentUserGroups = (
+  groupId: string | null,
+): SWRResponse<IUserGroupHasId[], Error> => {
   return useSWRImmutable(
     groupId != null ? ['/user-groups/selectable-parent-groups', groupId] : null,
-    ([endpoint, groupId]) => apiv3Get<SelectableParentUserGroupsResult>(endpoint, { groupId }).then(result => result.data.selectableParentGroups),
+    ([endpoint, groupId]) =>
+      apiv3Get<SelectableParentUserGroupsResult>(endpoint, { groupId }).then(
+        (result) => result.data.selectableParentGroups,
+      ),
   );
 };
 
-export const useSWRxSelectableChildUserGroups = (groupId: string | null): SWRResponse<IUserGroupHasId[], Error> => {
+export const useSWRxSelectableChildUserGroups = (
+  groupId: string | null,
+): SWRResponse<IUserGroupHasId[], Error> => {
   return useSWRImmutable(
     groupId != null ? ['/user-groups/selectable-child-groups', groupId] : null,
-    ([endpoint, groupId]) => apiv3Get<SelectableUserChildGroupsResult>(endpoint, { groupId }).then(result => result.data.selectableChildGroups),
+    ([endpoint, groupId]) =>
+      apiv3Get<SelectableUserChildGroupsResult>(endpoint, { groupId }).then(
+        (result) => result.data.selectableChildGroups,
+      ),
   );
 };
 
-export const useSWRxAncestorUserGroups = (groupId: string | null): SWRResponse<IUserGroupHasId[], Error> => {
+export const useSWRxAncestorUserGroups = (
+  groupId: string | null,
+): SWRResponse<IUserGroupHasId[], Error> => {
   return useSWRImmutable(
     groupId != null ? ['/user-groups/ancestors', groupId] : null,
-    ([endpoint, groupId]) => apiv3Get<AncestorUserGroupsResult>(endpoint, { groupId }).then(result => result.data.ancestorUserGroups),
+    ([endpoint, groupId]) =>
+      apiv3Get<AncestorUserGroupsResult>(endpoint, { groupId }).then(
+        (result) => result.data.ancestorUserGroups,
+      ),
   );
 };

+ 45 - 28
apps/app/src/stores/user.tsx

@@ -7,13 +7,17 @@ import { apiv3Get } from '~/client/util/apiv3-client';
 import type { PopulatedGrantedGroup } from '~/interfaces/page-grant';
 import { checkAndUpdateImageUrlCached } from '~/stores/middlewares/user';
 
-export const useSWRxUsersList = (userIds: string[]): SWRResponse<IUserHasId[], Error> => {
-  const distinctUserIds = userIds.length > 0 ? Array.from(new Set(userIds)).sort() : [];
+export const useSWRxUsersList = (
+  userIds: string[],
+): SWRResponse<IUserHasId[], Error> => {
+  const distinctUserIds =
+    userIds.length > 0 ? Array.from(new Set(userIds)).sort() : [];
   return useSWR(
     distinctUserIds.length > 0 ? ['/users/list', distinctUserIds] : null,
-    ([endpoint, userIds]) => apiv3Get(endpoint, { userIds: userIds.join(',') }).then((response) => {
-      return response.data.users;
-    }),
+    ([endpoint, userIds]) =>
+      apiv3Get(endpoint, { userIds: userIds.join(',') }).then((response) => {
+        return response.data.users;
+      }),
     {
       use: [checkAndUpdateImageUrlCached],
       revalidateOnFocus: false,
@@ -22,42 +26,55 @@ export const useSWRxUsersList = (userIds: string[]): SWRResponse<IUserHasId[], E
   );
 };
 
-
 type usernameRequestOptions = {
-  isIncludeActiveUser?: boolean,
-  isIncludeInactiveUser?: boolean,
-  isIncludeActivitySnapshotUser?: boolean,
-  isIncludeMixedUsernames?: boolean,
-}
+  isIncludeActiveUser?: boolean;
+  isIncludeInactiveUser?: boolean;
+  isIncludeActivitySnapshotUser?: boolean;
+  isIncludeMixedUsernames?: boolean;
+};
 
 type userData = {
-  usernames: string[]
-  totalCount: number
-}
+  usernames: string[];
+  totalCount: number;
+};
 
 type usernameResult = {
-  activeUser?: userData
-  inactiveUser?: userData
-  activitySnapshotUser?: userData
-  mixedUsernames?: string[]
-}
+  activeUser?: userData;
+  inactiveUser?: userData;
+  activitySnapshotUser?: userData;
+  mixedUsernames?: string[];
+};
 
-export const useSWRxUsernames = (q: string, offset?: number, limit?: number, options?: usernameRequestOptions): SWRResponse<usernameResult, Error> => {
+export const useSWRxUsernames = (
+  q: string,
+  offset?: number,
+  limit?: number,
+  options?: usernameRequestOptions,
+): SWRResponse<usernameResult, Error> => {
   return useSWRImmutable(
-    (q != null && q.trim() !== '') ? ['/users/usernames', q, offset, limit, JSON.stringify(options)] : null,
-    ([endpoint, q, offset, limit, options]) => apiv3Get(endpoint, {
-      q, offset, limit, options,
-    }).then(result => result.data),
+    q != null && q.trim() !== ''
+      ? ['/users/usernames', q, offset, limit, JSON.stringify(options)]
+      : null,
+    ([endpoint, q, offset, limit, options]) =>
+      apiv3Get(endpoint, {
+        q,
+        offset,
+        limit,
+        options,
+      }).then((result) => result.data),
   );
 };
 
 type RelatedGroupsResponse = {
-  relatedGroups: PopulatedGrantedGroup[]
-}
+  relatedGroups: PopulatedGrantedGroup[];
+};
 
-export const useSWRxUserRelatedGroups = (): SWRResponse<RelatedGroupsResponse, Error> => {
+export const useSWRxUserRelatedGroups = (): SWRResponse<
+  RelatedGroupsResponse,
+  Error
+> => {
   return useSWRImmutable<RelatedGroupsResponse>(
     ['/user/related-groups'],
-    ([endpoint]) => apiv3Get(endpoint).then(response => response.data),
+    ([endpoint]) => apiv3Get(endpoint).then((response) => response.data),
   );
 };

+ 21 - 9
apps/app/src/stores/websocket.tsx

@@ -1,7 +1,8 @@
 import { useEffect } from 'react';
-
 import {
-  useGlobalSocket, GLOBAL_SOCKET_NS, useSWRStatic,
+  GLOBAL_SOCKET_NS,
+  useGlobalSocket,
+  useSWRStatic,
 } from '@growi/core/dist/swr';
 import type { Socket } from 'socket.io-client';
 import type { SWRResponse } from 'swr';
@@ -19,7 +20,6 @@ export const GLOBAL_ADMIN_SOCKET_KEY = 'globalAdminSocket';
  * Global Socket
  */
 export const useSetupGlobalSocket = (): void => {
-
   const { data: socket, mutate } = useGlobalSocket();
   const { data: isGuestUser } = useIsGuestUser();
 
@@ -35,21 +35,29 @@ export const useSetupGlobalSocket = (): void => {
       return;
     }
 
-    mutate(async() => {
+    mutate(async () => {
       const { io } = await import('socket.io-client');
       const newSocket = io(GLOBAL_SOCKET_NS, {
         transports: ['websocket'],
       });
 
-      newSocket.on('error', (err) => { logger.error(err) });
-      newSocket.on('connect_error', (err) => { logger.error('Failed to connect with websocket.', err) });
+      newSocket.on('error', (err) => {
+        logger.error(err);
+      });
+      newSocket.on('connect_error', (err) => {
+        logger.error('Failed to connect with websocket.', err);
+      });
 
       return newSocket;
     });
 
     // Cleanup function to disconnect socket when component unmounts or user logs out
     return () => {
-      if (socket != null && typeof socket === 'object' && 'disconnect' in socket) {
+      if (
+        socket != null &&
+        typeof socket === 'object' &&
+        'disconnect' in socket
+      ) {
         logger.debug('Disconnecting Socket.IO connection');
         (socket as Socket).disconnect();
         mutate(undefined, false); // Clear the SWR cache without revalidation
@@ -81,11 +89,15 @@ export const useGlobalAdminSocket = (): SWRResponse<Socket, Error> => {
   return useSWRStatic(GLOBAL_ADMIN_SOCKET_KEY);
 };
 
-export const useSetupGlobalSocketForPage = (pageId: string | undefined): void => {
+export const useSetupGlobalSocketForPage = (
+  pageId: string | undefined,
+): void => {
   const { data: socket } = useGlobalSocket();
 
   useEffect(() => {
-    if (socket == null || pageId == null) { return }
+    if (socket == null || pageId == null) {
+      return;
+    }
 
     socket.emit(SocketEventName.JoinPage, { pageId });
 

+ 42 - 20
apps/app/src/stores/yjs.ts

@@ -1,5 +1,4 @@
 import { useCallback } from 'react';
-
 import { useSWRStatic } from '@growi/core/dist/swr';
 import type { SWRResponse } from 'swr';
 import useSWRMutation, { type SWRMutationResponse } from 'swr/mutation';
@@ -11,41 +10,64 @@ import { useIsGuestUser } from '~/stores-universal/context';
 import { useCurrentPageId } from './page';
 
 type CurrentPageYjsDataUtils = {
-  updateHasYdocsNewerThanLatestRevision(hasYdocsNewerThanLatestRevision: boolean): void
-  updateAwarenessStateSize(awarenessStateSize: number): void
-}
+  updateHasYdocsNewerThanLatestRevision(
+    hasYdocsNewerThanLatestRevision: boolean,
+  ): void;
+  updateAwarenessStateSize(awarenessStateSize: number): void;
+};
 
-export const useCurrentPageYjsData = (): SWRResponse<CurrentPageYjsData, Error> & CurrentPageYjsDataUtils => {
+export const useCurrentPageYjsData = (): SWRResponse<
+  CurrentPageYjsData,
+  Error
+> &
+  CurrentPageYjsDataUtils => {
   const { data: currentPageId } = useCurrentPageId();
   const { data: isGuestUser } = useIsGuestUser();
 
-  const key = !isGuestUser && currentPageId != null
-    ? `/page/${currentPageId}/yjs-data`
-    : null;
+  const key =
+    !isGuestUser && currentPageId != null
+      ? `/page/${currentPageId}/yjs-data`
+      : null;
 
   const swrResponse = useSWRStatic<CurrentPageYjsData, Error>(key, undefined);
 
-  const updateHasYdocsNewerThanLatestRevision = useCallback((hasYdocsNewerThanLatestRevision: boolean) => {
-    swrResponse.mutate({ ...swrResponse.data, hasYdocsNewerThanLatestRevision });
-  }, [swrResponse]);
+  const updateHasYdocsNewerThanLatestRevision = useCallback(
+    (hasYdocsNewerThanLatestRevision: boolean) => {
+      swrResponse.mutate({
+        ...swrResponse.data,
+        hasYdocsNewerThanLatestRevision,
+      });
+    },
+    [swrResponse],
+  );
 
-  const updateAwarenessStateSize = useCallback((awarenessStateSize: number) => {
-    swrResponse.mutate({ ...swrResponse.data, awarenessStateSize });
-  }, [swrResponse]);
+  const updateAwarenessStateSize = useCallback(
+    (awarenessStateSize: number) => {
+      swrResponse.mutate({ ...swrResponse.data, awarenessStateSize });
+    },
+    [swrResponse],
+  );
 
-  return Object.assign(swrResponse, { updateHasYdocsNewerThanLatestRevision, updateAwarenessStateSize });
+  return Object.assign(swrResponse, {
+    updateHasYdocsNewerThanLatestRevision,
+    updateAwarenessStateSize,
+  });
 };
 
-export const useSWRMUTxCurrentPageYjsData = (): SWRMutationResponse<CurrentPageYjsData, Error> => {
+export const useSWRMUTxCurrentPageYjsData = (): SWRMutationResponse<
+  CurrentPageYjsData,
+  Error
+> => {
   const { data: currentPageId } = useCurrentPageId();
 
-  const key = currentPageId != null
-    ? `/page/${currentPageId}/yjs-data`
-    : null;
+  const key = currentPageId != null ? `/page/${currentPageId}/yjs-data` : null;
 
   return useSWRMutation(
     key,
-    endpoint => apiv3Get<{ yjsData: CurrentPageYjsData }>(endpoint).then(result => result.data.yjsData),
+    (endpoint) =>
+      apiv3Get<{ yjsData: CurrentPageYjsData }>(endpoint).then(
+        (result) => result.data.yjsData,
+      ),
     { populateCache: true, revalidate: false },
   );
 };

+ 0 - 2
biome.json

@@ -33,8 +33,6 @@
       "!apps/app/src/features/openai",
       "!apps/app/src/pages",
       "!apps/app/src/server",
-      "!apps/app/src/services",
-      "!apps/app/src/stores",
       "!apps/app/src/styles",
       "!apps/app/test-with-vite",
       "!apps/app/tmp"

Some files were not shown because too many files changed in this diff