Просмотр исходного кода

Migrate sidebar state management to Jotai, removing SWR dependencies and enhancing SSR hydration

Yuki Takei 8 месяцев назад
Родитель
Сommit
ecfb64d3bf

+ 21 - 24
apps/app/docs/plan/jotai-migration-todo.md

@@ -42,34 +42,31 @@
 - ✅ JSX階層がフラット
 - ✅ より自然な React Hook の使用感
 
-### 優先度 1: UI 状態(クライアントサイド完結)
+### 優先度 1: UI 状態(クライアントサイド完結)- 完了
 
-#### 1.1 サイドバー関連の残り
-- [ ] `useCurrentSidebarContents`: サイドバーのコンテンツタイプ
-  - **ファイル**: `/workspace/growi/apps/app/src/stores/ui.tsx:156`
-  - **使用箇所**: 
-    - `SidebarContents.tsx`
-    - `PrimaryItem.tsx`
-    - `pages/utils/commons.ts`
-  - **特徴**: 永続化必要(`scheduleToPut({ currentSidebarContents: data })`)
+#### ✅ 1.1 サイドバー関連の完全移行(完了)
+- ✅ `useCurrentSidebarContents`: サイドバーのコンテンツタイプ
+  - **実装**: `currentSidebarContentsAtom` + `currentSidebarContentsAtomExt`
+  - **永続化**: `scheduleToPut({ currentSidebarContents: data })`
+  - **SSRハイドレーション**: 追加済み
 
-- [ ] `useCollapsedContentsOpened`: 折りたたまれたコンテンツの開閉状態
-  - **ファイル**: `/workspace/growi/apps/app/src/stores/ui.tsx:188`
-  - **使用箇所**: 
-    - `ToggleCollapseButton.tsx`
-    - `PrimaryItem.tsx`
-    - `SidebarContents.tsx`
-    - `Sidebar.tsx`
-    - `UISettings.tsx`
+- ✅ `useCollapsedContentsOpened`: 折りたたまれたコンテンツの開閉状態
+  - **実装**: `isCollapsedContentsOpenedAtom`
   - **特徴**: 一時的な状態(永続化不要)
 
-- [ ] `useCurrentProductNavWidth`: プロダクトナビゲーションの幅
-  - **ファイル**: `/workspace/growi/apps/app/src/stores/ui.tsx:175`
-  - **使用箇所**: 
-    - `PagePathNavSticky.tsx`
-    - `Sidebar.tsx`
-    - `pages/utils/commons.ts`
-  - **特徴**: 永続化必要(`scheduleToPut({ currentProductNavWidth: data })`)
+- ✅ `useCurrentProductNavWidth`: プロダクトナビゲーションの幅
+  - **実装**: `currentProductNavWidthAtom` + `currentProductNavWidthAtomExt`
+  - **永続化**: `scheduleToPut({ currentProductNavWidth: data })`
+  - **SSRハイドレーション**: 追加済み
+
+**実施内容:**
+- ✅ 新しい atoms を `states/ui/sidebar.ts` に実装
+- ✅ `preferCollapsedModeAtomExt` パターンに従って永続化対応
+- ✅ SSRハイドレーションに全ての atoms を追加
+- ✅ `useInitSidebarConfig` 関数を完全削除
+- ✅ 使用箇所の更新(imports削除)
+
+**次のステップ**: 既存SWR版の使用箇所をJotai版に置き換え
 
 #### 1.2 ページ関連の UI 状態
 - [ ] `usePageControlsX`: ページコントロールのX座標

+ 4 - 5
apps/app/src/client/components/Me/UISettings.tsx

@@ -5,8 +5,7 @@ import { UncontrolledTooltip } from 'reactstrap';
 
 import { updateUserUISettings } from '~/client/services/user-ui-settings';
 import { toastError, toastSuccess } from '~/client/util/toastr';
-import { usePreferCollapsedMode, useSidebarMode } from '~/states/ui/sidebar';
-import { useCollapsedContentsOpened } from '~/stores/ui';
+import { usePreferCollapsedMode, useSidebarMode, useCollapsedContentsOpened } from '~/states/ui/sidebar';
 
 import styles from './UISettings.module.scss';
 
@@ -30,12 +29,12 @@ export const UISettings = (): JSX.Element => {
     isDockMode, isCollapsedMode,
   } = useSidebarMode();
   const [, setPreferCollapsedMode] = usePreferCollapsedMode();
-  const { mutate: mutateCollapsedContentsOpened } = useCollapsedContentsOpened();
+  const [, setCollapsedContentsOpened] = useCollapsedContentsOpened();
 
   const toggleCollapsed = useCallback(() => {
     setPreferCollapsedMode(!isCollapsedMode());
-    mutateCollapsedContentsOpened(false);
-  }, [setPreferCollapsedMode, isCollapsedMode, mutateCollapsedContentsOpened]);
+    setCollapsedContentsOpened(false);
+  }, [setPreferCollapsedMode, isCollapsedMode, setCollapsedContentsOpened]);
 
   const updateButtonHandler = useCallback(async() => {
     try {

+ 3 - 3
apps/app/src/client/components/PagePathNavSticky/PagePathNavSticky.tsx

@@ -7,9 +7,9 @@ import { pagePathUtils } from '@growi/core/dist/utils';
 import Sticky from 'react-stickynode';
 
 import LinkedPagePath from '~/models/linked-page-path';
-import { useSidebarMode } from '~/states/ui/sidebar';
+import { useSidebarMode, useCurrentProductNavWidth } from '~/states/ui/sidebar';
 import {
-  usePageControlsX, useCurrentProductNavWidth,
+  usePageControlsX,
 } from '~/stores/ui';
 
 import { PagePathHierarchicalLink } from '../../../components/Common/PagePathHierarchicalLink';
@@ -29,7 +29,7 @@ export const PagePathNavSticky = (props: PagePathNavLayoutProps): JSX.Element =>
   const { pagePath } = props;
 
   const { data: pageControlsX } = usePageControlsX();
-  const { data: sidebarWidth } = useCurrentProductNavWidth();
+  const [sidebarWidth] = useCurrentProductNavWidth();
   const { sidebarMode } = useSidebarMode();
   const pagePathNavRef = useRef<HTMLDivElement>(null);
 

+ 20 - 20
apps/app/src/client/components/Sidebar/Sidebar.tsx

@@ -8,20 +8,20 @@ import SimpleBar from 'simplebar-react';
 import { useIsomorphicLayoutEffect } from 'usehooks-ts';
 
 import { SidebarMode } from '~/interfaces/ui';
-import { useIsSearchPage } from '~/stores-universal/context';
-import { EditorMode, useEditorMode } from '~/stores-universal/ui';
-import {
-  useCollapsedContentsOpened,
-  useCurrentProductNavWidth,
-  useSidebarScrollerRef,
-  useIsDeviceLargerThanMd,
-} from '~/stores/ui';
 import { useDeviceLargerThanXl } from '~/states/ui/device';
 import {
   useDrawerOpened,
   usePreferCollapsedMode,
   useSidebarMode,
+  useCollapsedContentsOpened,
+  useCurrentProductNavWidth,
 } from '~/states/ui/sidebar';
+import { useIsSearchPage } from '~/stores-universal/context';
+import { EditorMode, useEditorMode } from '~/stores-universal/ui';
+import {
+  useSidebarScrollerRef,
+  useIsDeviceLargerThanMd,
+} from '~/stores/ui';
 
 import { DrawerToggler } from '../Common/DrawerToggler';
 
@@ -73,9 +73,9 @@ const ResizableContainer = memo((props: ResizableContainerProps): JSX.Element =>
 
   const { isDrawerMode, isCollapsedMode, isDockMode } = useSidebarMode();
   const [, setIsDrawerOpened] = useDrawerOpened();
-  const { data: currentProductNavWidth, mutateAndSave: mutateProductNavWidth } = useCurrentProductNavWidth();
+  const [currentProductNavWidth, setCurrentProductNavWidth] = useCurrentProductNavWidth();
   const [, setPreferCollapsedMode] = usePreferCollapsedMode();
-  const { mutate: mutateCollapsedContentsOpened } = useCollapsedContentsOpened();
+  const [, setCollapsedContentsOpened] = useCollapsedContentsOpened();
 
   const [isClient, setClient] = useState(false);
   const [resizableAreaWidth, setResizableAreaWidth] = useState<number|undefined>(
@@ -87,13 +87,13 @@ const ResizableContainer = memo((props: ResizableContainerProps): JSX.Element =>
   }, []);
 
   const resizeDoneHandler = useCallback((newWidth: number) => {
-    mutateProductNavWidth(newWidth, false);
-  }, [mutateProductNavWidth]);
+    setCurrentProductNavWidth(newWidth);
+  }, [setCurrentProductNavWidth]);
 
   const collapsedByResizableAreaHandler = useCallback(() => {
     setPreferCollapsedMode(true);
-    mutateCollapsedContentsOpened(false);
-  }, [mutateCollapsedContentsOpened, setPreferCollapsedMode]);
+    setCollapsedContentsOpened(false);
+  }, [setCollapsedContentsOpened, setPreferCollapsedMode]);
 
   useIsomorphicLayoutEffect(() => {
     setClient(true);
@@ -142,8 +142,8 @@ const CollapsibleContainer = memo((props: CollapsibleContainerProps): JSX.Elemen
   const { Nav, className, children } = props;
 
   const { isCollapsedMode } = useSidebarMode();
-  const { data: currentProductNavWidth } = useCurrentProductNavWidth();
-  const { data: isCollapsedContentsOpened, mutate: mutateCollapsedContentsOpened } = useCollapsedContentsOpened();
+  const [currentProductNavWidth] = useCurrentProductNavWidth();
+  const [isCollapsedContentsOpened, setCollapsedContentsOpened] = useCollapsedContentsOpened();
 
   const sidebarScrollerRef = useRef<HTMLDivElement>(null);
   const { mutate: mutateSidebarScroller } = useSidebarScrollerRef();
@@ -157,8 +157,8 @@ const CollapsibleContainer = memo((props: CollapsibleContainerProps): JSX.Elemen
       return;
     }
 
-    mutateCollapsedContentsOpened(true);
-  }, [isCollapsedMode, mutateCollapsedContentsOpened]);
+    setCollapsedContentsOpened(true);
+  }, [isCollapsedMode, setCollapsedContentsOpened]);
 
   // close menu when collapsed mode
   const mouseLeaveHandler = useCallback(() => {
@@ -167,8 +167,8 @@ const CollapsibleContainer = memo((props: CollapsibleContainerProps): JSX.Elemen
       return;
     }
 
-    mutateCollapsedContentsOpened(false);
-  }, [isCollapsedMode, mutateCollapsedContentsOpened]);
+    setCollapsedContentsOpened(false);
+  }, [isCollapsedMode, setCollapsedContentsOpened]);
 
   const closedClass = isCollapsedMode() && !isCollapsedContentsOpened ? 'd-none' : '';
   const openedClass = isCollapsedMode() && isCollapsedContentsOpened ? 'open' : '';

+ 3 - 4
apps/app/src/client/components/Sidebar/SidebarContents.tsx

@@ -2,9 +2,8 @@ import React, { memo, useMemo } from 'react';
 
 import { AiAssistant } from '~/features/openai/client/components/AiAssistant/Sidebar/AiAssistant';
 import { SidebarContentsType } from '~/interfaces/ui';
+import { useSidebarMode, useCollapsedContentsOpened, useCurrentSidebarContents } from '~/states/ui/sidebar';
 import { useIsAiEnabled, useIsGuestUser } from '~/stores-universal/context';
-import { useSidebarMode } from '~/states/ui/sidebar';
-import { useCollapsedContentsOpened, useCurrentSidebarContents } from '~/stores/ui';
 
 
 import { Bookmarks } from './Bookmarks';
@@ -22,8 +21,8 @@ export const SidebarContents = memo(() => {
   const { data: isGuestUser } = useIsGuestUser();
   const { data: isAiEnabled } = useIsAiEnabled();
 
-  const { data: isCollapsedContentsOpened } = useCollapsedContentsOpened();
-  const { data: currentSidebarContents } = useCurrentSidebarContents();
+  const [isCollapsedContentsOpened] = useCollapsedContentsOpened();
+  const [currentSidebarContents] = useCurrentSidebarContents();
 
   const Contents = useMemo(() => {
     switch (currentSidebarContents) {

+ 4 - 7
apps/app/src/client/components/Sidebar/SidebarHead/ToggleCollapseButton.tsx

@@ -3,11 +3,8 @@ import {
 } from 'react';
 
 import {
-  useDrawerOpened, usePreferCollapsedMode, useSidebarMode,
+  useDrawerOpened, usePreferCollapsedMode, useSidebarMode, useCollapsedContentsOpened,
 } from '~/states/ui/sidebar';
-import {
-  useCollapsedContentsOpened,
-} from '~/stores/ui';
 
 
 import styles from './ToggleCollapseButton.module.scss';
@@ -18,7 +15,7 @@ export const ToggleCollapseButton = memo((): JSX.Element => {
   const { isDrawerMode, isCollapsedMode } = useSidebarMode();
   const [isDrawerOpened, setIsDrawerOpened] = useDrawerOpened();
   const [, setPreferCollapsedMode] = usePreferCollapsedMode();
-  const { mutate: mutateCollapsedContentsOpened } = useCollapsedContentsOpened();
+  const [, setCollapsedContentsOpened] = useCollapsedContentsOpened();
 
   const toggleDrawer = useCallback(() => {
     setIsDrawerOpened(!isDrawerOpened);
@@ -26,8 +23,8 @@ export const ToggleCollapseButton = memo((): JSX.Element => {
 
   const toggleCollapsed = useCallback(() => {
     setPreferCollapsedMode(!isCollapsedMode());
-    mutateCollapsedContentsOpened(false);
-  }, [isCollapsedMode, mutateCollapsedContentsOpened, setPreferCollapsedMode]);
+    setCollapsedContentsOpened(false);
+  }, [isCollapsedMode, setCollapsedContentsOpened, setPreferCollapsedMode]);
 
   const rotationClass = isCollapsedMode() ? 'rotate180' : '';
   const icon = useMemo(() => {

+ 6 - 5
apps/app/src/client/components/Sidebar/SidebarNav/PrimaryItem.tsx

@@ -5,10 +5,11 @@ import { UncontrolledTooltip } from 'reactstrap';
 
 import type { SidebarContentsType } from '~/interfaces/ui';
 import { SidebarMode } from '~/interfaces/ui';
-import { useCollapsedContentsOpened, useCurrentSidebarContents, useIsMobile } from '~/stores/ui';
+import { useCollapsedContentsOpened, useCurrentSidebarContents } from '~/states/ui/sidebar';
+import { useIsMobile } from '~/stores/ui';
 
 const useIndicator = (sidebarMode: SidebarMode, isSelected: boolean): string => {
-  const { data: isCollapsedContentsOpened } = useCollapsedContentsOpened();
+  const [isCollapsedContentsOpened] = useCollapsedContentsOpened();
 
   if (sidebarMode === SidebarMode.COLLAPSED && !isCollapsedContentsOpened) {
     return '';
@@ -34,15 +35,15 @@ export const PrimaryItem = (props: PrimaryItemProps): JSX.Element => {
     onClick, onHover,
   } = props;
 
-  const { data: currentContents, mutateAndSave: mutateContents } = useCurrentSidebarContents();
+  const [currentContents, setCurrentContents] = useCurrentSidebarContents();
 
   const indicatorClass = useIndicator(sidebarMode, contents === currentContents);
   const { data: isMobile } = useIsMobile();
   const { t } = useTranslation();
 
   const selectThisItem = useCallback(() => {
-    mutateContents(contents, false);
-  }, [contents, mutateContents]);
+    setCurrentContents(contents);
+  }, [contents, setCurrentContents]);
 
   const itemClickedHandler = useCallback(() => {
     // do nothing ONLY WHEN the collapse mode

+ 1 - 4
apps/app/src/pages/[[...path]].page.tsx

@@ -64,7 +64,7 @@ import loggerFactory from '~/utils/logger';
 import type { NextPageWithLayout } from './_app.page';
 import type { CommonProps } from './utils/commons';
 import {
-  getNextI18NextConfig, getServerSideCommonProps, generateCustomTitleForPage, useInitSidebarConfig, skipSSR, addActivity,
+  getNextI18NextConfig, getServerSideCommonProps, generateCustomTitleForPage, skipSSR, addActivity,
 } from './utils/commons';
 
 
@@ -410,9 +410,6 @@ const Layout = ({ children, ...props }: LayoutProps): JSX.Element => {
   // Hydrate sidebar atoms with server-side data
   useHydrateSidebarAtoms(props.sidebarConfig, props.userUISettings);
 
-  // init sidebar config with UserUISettings and sidebarConfig
-  useInitSidebarConfig(props.sidebarConfig, props.userUISettings);
-
   return <BasicLayoutWithEditor>{children}</BasicLayoutWithEditor>;
 };
 

+ 1 - 4
apps/app/src/pages/_private-legacy-pages.page.tsx

@@ -20,7 +20,7 @@ import { useCurrentPageId, useSWRxCurrentPage } from '~/stores/page';
 
 import type { CommonProps } from './utils/commons';
 import {
-  getNextI18NextConfig, getServerSideCommonProps, generateCustomTitle, useInitSidebarConfig,
+  getNextI18NextConfig, getServerSideCommonProps, generateCustomTitle,
 } from './utils/commons';
 
 const SearchResultLayout = dynamic(() => import('~/components/Layout/SearchResultLayout'), { ssr: false });
@@ -66,9 +66,6 @@ const PrivateLegacyPage: NextPage<Props> = (props: Props) => {
   // Hydrate sidebar atoms with server-side data
   useHydrateSidebarAtoms(props.sidebarConfig, props.userUISettings);
 
-  // init sidebar config with UserUISettings and sidebarConfig
-  useInitSidebarConfig(props.sidebarConfig, props.userUISettings);
-
   // render config
   useRendererConfig(props.rendererConfig);
 

+ 0 - 11
apps/app/src/pages/utils/commons.ts

@@ -9,12 +9,10 @@ import * as nextI18NextConfig from '^/config/next-i18next.config';
 
 import { type SupportedActionType } from '~/interfaces/activity';
 import type { CrowiRequest } from '~/interfaces/crowi-request';
-import type { ISidebarConfig } from '~/interfaces/sidebar-config';
 import type { IUserUISettings } from '~/interfaces/user-ui-settings';
 import type { PageDocument } from '~/server/models/page';
 import type { UserUISettingsDocument } from '~/server/models/user-ui-settings';
 import { detectLocaleFromBrowserAcceptLanguage } from '~/server/util/locale-utils';
-import { useCurrentProductNavWidth, useCurrentSidebarContents } from '~/stores/ui';
 import { getGrowiVersion } from '~/utils/growi-version';
 
 export type CommonProps = {
@@ -182,15 +180,6 @@ export const generateCustomTitleForPage = (props: CommonProps, pagePath: string)
     .replace('{{pagename}}', dPagePath.latter);
 };
 
-export const useInitSidebarConfig = (sidebarConfig: ISidebarConfig, userUISettings?: IUserUISettings): void => {
-  // NOTE: UI state initialization is now handled by useHydrateSidebarAtoms hook
-  // This function is kept for backwards compatibility with existing SWR-based states
-  // TODO: Remove this function after migrating all UI states to Jotai
-
-  useCurrentSidebarContents(userUISettings?.currentSidebarContents);
-  useCurrentProductNavWidth(userUISettings?.currentProductNavWidth);
-};
-
 export const skipSSR = async(page: PageDocument, ssrMaxRevisionBodyLength: number): Promise<boolean> => {
   if (!isServer()) {
     throw new Error('This method is not available on the client-side');

+ 11 - 4
apps/app/src/states/hydrate/sidebar.ts

@@ -1,8 +1,13 @@
 import { useHydrateAtoms } from 'jotai/utils';
 
 import type { ISidebarConfig } from '../../interfaces/sidebar-config';
+import { SidebarContentsType } from '../../interfaces/ui';
 import type { IUserUISettings } from '../../interfaces/user-ui-settings';
-import { preferCollapsedModeAtom } from '../ui/sidebar';
+import {
+  preferCollapsedModeAtom,
+  currentSidebarContentsAtom,
+  currentProductNavWidthAtom,
+} from '../ui/sidebar';
 
 /**
  * Hook for hydrating sidebar-related UI state atoms with server-side data
@@ -16,8 +21,10 @@ export const useHydrateSidebarAtoms = (sidebarConfig: ISidebarConfig, userUISett
     // Use user preference from DB if available, otherwise use system default
     [preferCollapsedModeAtom, userUISettings?.preferCollapsedModeByUser ?? sidebarConfig.isSidebarCollapsedMode],
 
-    // TODO: Add other sidebar UI state atoms when migrated from SWR
-    // [currentSidebarContentsAtom, userUISettings?.currentSidebarContents ?? SidebarContentsType.TREE],
-    // [currentProductNavWidthAtom, userUISettings?.currentProductNavWidth ?? 320],
+    // Sidebar contents type (with default fallback)
+    [currentSidebarContentsAtom, userUISettings?.currentSidebarContents ?? SidebarContentsType.TREE],
+
+    // Product navigation width (with default fallback)
+    [currentProductNavWidthAtom, userUISettings?.currentProductNavWidth ?? 320],
   ]);
 };

+ 42 - 7
apps/app/src/states/ui/sidebar.ts

@@ -1,7 +1,7 @@
 import { atom, useAtom } from 'jotai';
 
 import { scheduleToPut } from '~/client/services/user-ui-settings';
-import { SidebarMode } from '~/interfaces/ui';
+import { SidebarContentsType, SidebarMode } from '~/interfaces/ui';
 import { EditorMode } from '~/stores-universal/ui';
 
 import { isDeviceLargerThanXlAtom } from './device';
@@ -29,14 +29,49 @@ export const usePreferCollapsedMode = (): UseAtom<typeof preferCollapsedModeAtom
   return useAtom(preferCollapsedModeAtomExt);
 };
 
-// Export the base atom for SSR hydration
-export { preferCollapsedModeAtom };
+// Collapsed contents opened state (temporary UI state, no persistence needed)
+const isCollapsedContentsOpenedAtom = atom(false);
 
-// TODO: Migrate from SWR - Current sidebar contents atom
-// const currentSidebarContentsAtom = atom<SidebarContentsType>(SidebarContentsType.TREE);
+export const useCollapsedContentsOpened = (): UseAtom<typeof isCollapsedContentsOpenedAtom> => {
+  return useAtom(isCollapsedContentsOpenedAtom);
+};
+
+// Current sidebar contents state (with persistence)
+const currentSidebarContentsAtom = atom<SidebarContentsType>(SidebarContentsType.TREE);
+const currentSidebarContentsAtomExt = atom(
+  get => get(currentSidebarContentsAtom),
+  (get, set, update: SidebarContentsType) => {
+    set(currentSidebarContentsAtom, update);
+    scheduleToPut({ currentSidebarContents: update });
+  },
+);
+
+export const useCurrentSidebarContents = (): UseAtom<typeof currentSidebarContentsAtom> => {
+  return useAtom(currentSidebarContentsAtomExt);
+};
+
+// Current product navigation width state (with persistence)
+const currentProductNavWidthAtom = atom<number>(320);
+const currentProductNavWidthAtomExt = atom(
+  get => get(currentProductNavWidthAtom),
+  (get, set, update: number) => {
+    set(currentProductNavWidthAtom, update);
+    scheduleToPut({ currentProductNavWidth: update });
+  },
+);
+
+export const useCurrentProductNavWidth = (): UseAtom<typeof currentProductNavWidthAtom> => {
+  return useAtom(currentProductNavWidthAtomExt);
+};
+
+// Export base atoms for SSR hydration
+export {
+  preferCollapsedModeAtom,
+  isCollapsedContentsOpenedAtom,
+  currentSidebarContentsAtom,
+  currentProductNavWidthAtom,
+};
 
-// TODO: Migrate from SWR - Product navigation width atom
-// const currentProductNavWidthAtom = atom<number>(320);
 const sidebarModeAtom = atom(
   (get) => {
     const isDeviceLargerThanXl = get(isDeviceLargerThanXlAtom);

+ 1 - 41
apps/app/src/stores/ui.tsx

@@ -4,21 +4,18 @@ import {
 } from 'react';
 
 import { PageGrant, type Nullable } from '@growi/core';
-import { type SWRResponseWithUtils, useSWRStatic, withUtils } from '@growi/core/dist/swr';
+import { useSWRStatic } from '@growi/core/dist/swr';
 import { pagePathUtils, isClient } 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 type { HtmlElementNode } from 'rehype-toc';
-import type { MutatorOptions } from 'swr';
 import {
   useSWRConfig, type SWRResponse, type Key,
 } from 'swr';
 import useSWRImmutable from 'swr/immutable';
 
-import { scheduleToPut } from '~/client/services/user-ui-settings';
 import type { IPageSelectedGrant } from '~/interfaces/page';
-import { SidebarContentsType } from '~/interfaces/ui';
 import type { UpdateDescCountData } from '~/interfaces/websocket';
 import {
   useIsEditable, useIsReadOnlyUser,
@@ -148,47 +145,10 @@ export const useIsDeviceLargerThanLg = (): SWRResponse<boolean, Error> => {
 };
 
 
-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 });
-
-  const { mutate } = swrResponse;
-
-  const mutateAndSave: MutateAndSaveUserUISettings<SidebarContentsType> = useCallback((data, opts?) => {
-    scheduleToPut({ currentSidebarContents: data });
-    return mutate(data, opts);
-  }, [mutate]);
-
-  return withUtils(swrResponse, { mutateAndSave });
-};
-
 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 });
-
-  const { mutate } = swrResponse;
-
-  const mutateAndSave: MutateAndSaveUserUISettings<number> = useCallback((data, opts?) => {
-    scheduleToPut({ currentProductNavWidth: 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 useSelectedGrant = (initialData?: Nullable<IPageSelectedGrant>): SWRResponse<Nullable<IPageSelectedGrant>, Error> => {
   return useSWRStatic<Nullable<IPageSelectedGrant>, Error>('selectedGrant', initialData, { fallbackData: { grant: PageGrant.GRANT_PUBLIC } });
 };