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

Merge pull request #7397 from weseek/imprv/initialize-user-ui-settings

imprv: Initialize UserUISettings
Yuki Takei 3 лет назад
Родитель
Сommit
7fb1060499

+ 7 - 34
packages/app/src/pages/[[...path]].page.tsx

@@ -26,11 +26,8 @@ import type { CrowiRequest } from '~/interfaces/crowi-request';
 import type { EditorConfig } from '~/interfaces/editor-settings';
 import type { IPageGrantData } from '~/interfaces/page';
 import type { RendererConfig } from '~/interfaces/services/renderer';
-import type { ISidebarConfig } from '~/interfaces/sidebar-config';
-import type { IUserUISettings } from '~/interfaces/user-ui-settings';
 import type { PageModel, PageDocument } from '~/server/models/page';
 import type { PageRedirectModel } from '~/server/models/page-redirect';
-import type { UserUISettingsModel } from '~/server/models/user-ui-settings';
 import {
   useCurrentUser,
   useIsLatestRevision,
@@ -175,11 +172,6 @@ type Props = CommonProps & {
   grantData?: IPageGrantData,
 
   rendererConfig: RendererConfig,
-
-  // UI
-  userUISettings?: IUserUISettings
-  // Sidebar
-  sidebarConfig: ISidebarConfig,
 };
 
 const Page: NextPageWithLayout<Props> = (props: Props) => {
@@ -196,9 +188,6 @@ const Page: NextPageWithLayout<Props> = (props: Props) => {
   useEditorConfig(props.editorConfig);
   useCsrfToken(props.csrfToken);
 
-  // init sidebar config with UserUISettings and sidebarConfig
-  useInitSidebarConfig(props.sidebarConfig, props.userUISettings);
-
   // page
   useIsLatestRevision(props.isLatestRevision);
   useIsContainerFluid(props.isContainerFluid);
@@ -326,14 +315,16 @@ const Page: NextPageWithLayout<Props> = (props: Props) => {
   );
 };
 
-type LayoutProps = {
+type LayoutProps = Props & {
   children?: ReactNode
-  className?: string
 }
 
-const Layout = ({ children }: LayoutProps): JSX.Element => {
+const Layout = ({ children, ...props }: LayoutProps): JSX.Element => {
   const className = useEditorModeClassName();
 
+  // init sidebar config with UserUISettings and sidebarConfig
+  useInitSidebarConfig(props.sidebarConfig, props.userUISettings);
+
   return (
     <BasicLayout className={className}>
       {children}
@@ -341,12 +332,12 @@ const Layout = ({ children }: LayoutProps): JSX.Element => {
   );
 };
 
-Page.getLayout = function getLayout(page) {
+Page.getLayout = function getLayout(page: React.ReactElement<Props>) {
   return (
     <>
       <DrawioViewerScript />
 
-      <Layout>
+      <Layout {...page.props}>
         {page}
       </Layout>
       <UnsavedAlertDialog />
@@ -456,19 +447,6 @@ async function injectPageData(context: GetServerSidePropsContext, props: Props):
   props.pageWithMeta = pageWithMeta;
 }
 
-async function injectUserUISettings(context: GetServerSidePropsContext, props: Props): Promise<void> {
-  const { model: mongooseModel } = await import('mongoose');
-
-  const req = context.req as CrowiRequest<IUserHasId & any>;
-  const { user } = req;
-  const UserUISettings = mongooseModel('UserUISettings') as UserUISettingsModel;
-
-  const userUISettings = user == null ? req.session.uiSettings : await UserUISettings.findOne({ user: user._id }).exec();
-  if (userUISettings != null) {
-    props.userUISettings = userUISettings.toObject?.() ?? userUISettings;
-  }
-}
-
 async function injectRoutingInformation(context: GetServerSidePropsContext, props: Props): Promise<void> {
   const req: CrowiRequest = context.req as CrowiRequest;
   const { crowi } = req;
@@ -574,10 +552,6 @@ function injectServerConfigurations(context: GetServerSidePropsContext, props: P
     highlightJsStyleBorder: crowi.configManager.getConfig('crowi', 'customize:highlightJsStyleBorder'),
   };
 
-  props.sidebarConfig = {
-    isSidebarDrawerMode: configManager.getConfig('crowi', 'customize:isSidebarDrawerMode'),
-    isSidebarClosedAtDockMode: configManager.getConfig('crowi', 'customize:isSidebarClosedAtDockMode'),
-  };
 }
 
 /**
@@ -630,7 +604,6 @@ export const getServerSideProps: GetServerSideProps = async(context: GetServerSi
     }
   }
 
-  await injectUserUISettings(context, props);
   await injectRoutingInformation(context, props);
   injectServerConfigurations(context, props);
   await injectNextI18NextConfigurations(context, props, ['translation']);

+ 0 - 3
packages/app/src/pages/_app.page.tsx

@@ -50,7 +50,6 @@ function GrowiApp({ Component, pageProps }: GrowiAppProps): JSX.Element {
 
 
   const commonPageProps = pageProps as CommonProps;
-  // useInterceptorManager(new InterceptorManager());
   useAppTitle(commonPageProps.appTitle);
   useSiteUrl(commonPageProps.siteUrl);
   useConfidential(commonPageProps.confidential);
@@ -68,6 +67,4 @@ function GrowiApp({ Component, pageProps }: GrowiAppProps): JSX.Element {
   );
 }
 
-// export default appWithTranslation(GrowiApp);
-
 export default appWithTranslation(GrowiApp, nextI18nConfig);

+ 2 - 29
packages/app/src/pages/_private-legacy-pages.page.tsx

@@ -9,21 +9,15 @@ import Head from 'next/head';
 import { DrawioViewerScript } from '~/components/Script/DrawioViewerScript';
 import type { CrowiRequest } from '~/interfaces/crowi-request';
 import type { RendererConfig } from '~/interfaces/services/renderer';
-import type { ISidebarConfig } from '~/interfaces/sidebar-config';
 import type { IUser, IUserHasId } from '~/interfaces/user';
-import type { IUserUISettings } from '~/interfaces/user-ui-settings';
-import type { UserUISettingsModel } from '~/server/models/user-ui-settings';
 import {
   useCsrfToken, useCurrentUser, useDrawioUri, useIsSearchPage, useIsSearchScopeChildrenAsDefault,
   useIsSearchServiceConfigured, useIsSearchServiceReachable, useRendererConfig,
 } from '~/stores/context';
-import {
-  usePreferDrawerModeByUser, usePreferDrawerModeOnEditByUser, useSidebarCollapsed,
-  useCurrentSidebarContents, useCurrentProductNavWidth,
-} from '~/stores/ui';
 
+import type { CommonProps } from './utils/commons';
 import {
-  CommonProps, getNextI18NextConfig, getServerSideCommonProps, generateCustomTitle, useInitSidebarConfig,
+  getNextI18NextConfig, getServerSideCommonProps, generateCustomTitle, useInitSidebarConfig,
 } from './utils/commons';
 
 const SearchResultLayout = dynamic(() => import('~/components/Layout/SearchResultLayout'), { ssr: false });
@@ -37,11 +31,6 @@ type Props = CommonProps & {
 
   drawioUri: string | null,
 
-  // UI
-  userUISettings?: IUserUISettings
-  // Sidebar
-  sidebarConfig: ISidebarConfig,
-
   // Render config
   rendererConfig: RendererConfig,
 
@@ -50,8 +39,6 @@ type Props = CommonProps & {
 const PrivateLegacyPage: NextPage<Props> = (props: Props) => {
   const { t } = useTranslation();
 
-  const { userUISettings } = props;
-
   const PrivateLegacyPages = dynamic(() => import('~/components/PrivateLegacyPages'), { ssr: false });
 
   // commons
@@ -92,19 +79,6 @@ const PrivateLegacyPage: NextPage<Props> = (props: Props) => {
   );
 };
 
-async function injectUserUISettings(context: GetServerSidePropsContext, props: Props): Promise<void> {
-  const { model: mongooseModel } = await import('mongoose');
-
-  const req = context.req as CrowiRequest<IUserHasId & any>;
-  const { user } = req;
-
-  const UserUISettings = mongooseModel('UserUISettings') as UserUISettingsModel;
-  const userUISettings = user == null ? null : await UserUISettings.findOne({ user: user._id }).exec();
-  if (userUISettings != null) {
-    props.userUISettings = userUISettings.toObject();
-  }
-}
-
 async function injectServerConfigurations(context: GetServerSidePropsContext, props: Props): Promise<void> {
   const req: CrowiRequest = context.req as CrowiRequest;
   const { crowi } = req;
@@ -168,7 +142,6 @@ export const getServerSideProps: GetServerSideProps = async(context: GetServerSi
     props.currentUser = user.toObject();
   }
 
-  await injectUserUISettings(context, props);
   await injectServerConfigurations(context, props);
   await injectNextI18NextConfigurations(context, props, ['translation']);
 

+ 20 - 30
packages/app/src/pages/_search.page.tsx

@@ -1,3 +1,5 @@
+import { ReactNode } from 'react';
+
 import type { GetServerSideProps, GetServerSidePropsContext } from 'next';
 import { useTranslation } from 'next-i18next';
 import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
@@ -8,24 +10,18 @@ import SearchResultLayout from '~/components/Layout/SearchResultLayout';
 import { DrawioViewerScript } from '~/components/Script/DrawioViewerScript';
 import type { CrowiRequest } from '~/interfaces/crowi-request';
 import type { RendererConfig } from '~/interfaces/services/renderer';
-import type { ISidebarConfig } from '~/interfaces/sidebar-config';
 import type { IUser, IUserHasId } from '~/interfaces/user';
-import type { IUserUISettings } from '~/interfaces/user-ui-settings';
-import type { UserUISettingsModel } from '~/server/models/user-ui-settings';
 import {
   useCsrfToken, useCurrentUser, useDrawioUri, useIsContainerFluid, useIsSearchPage, useIsSearchScopeChildrenAsDefault,
   useIsSearchServiceConfigured, useIsSearchServiceReachable, useRendererConfig, useShowPageLimitationL,
 } from '~/stores/context';
-import {
-  usePreferDrawerModeByUser, usePreferDrawerModeOnEditByUser, useSidebarCollapsed,
-  useCurrentSidebarContents, useCurrentProductNavWidth,
-} from '~/stores/ui';
 
 import { SearchPage } from '../components/SearchPage';
 
 import type { NextPageWithLayout } from './_app.page';
+import type { CommonProps } from './utils/commons';
 import {
-  getNextI18NextConfig, getServerSideCommonProps, generateCustomTitle, CommonProps, useInitSidebarConfig,
+  getNextI18NextConfig, getServerSideCommonProps, generateCustomTitle, useInitSidebarConfig,
 } from './utils/commons';
 
 
@@ -38,11 +34,6 @@ type Props = CommonProps & {
 
   drawioUri: string | null,
 
-  // UI
-  userUISettings?: IUserUISettings
-  // Sidebar
-  sidebarConfig: ISidebarConfig,
-
   // Render config
   rendererConfig: RendererConfig,
 
@@ -54,8 +45,6 @@ type Props = CommonProps & {
 };
 
 const SearchResultPage: NextPageWithLayout<Props> = (props: Props) => {
-  const { userUISettings } = props;
-
   const { t } = useTranslation();
 
   // commons
@@ -102,28 +91,30 @@ const SearchResultPage: NextPageWithLayout<Props> = (props: Props) => {
   );
 };
 
+type LayoutProps = Props & {
+  children?: ReactNode
+}
+
+const Layout = ({ children, ...props }: LayoutProps): JSX.Element => {
+  // init sidebar config with UserUISettings and sidebarConfig
+  useInitSidebarConfig(props.sidebarConfig, props.userUISettings);
+
+  return (
+    <SearchResultLayout>
+      {children}
+    </SearchResultLayout>
+  );
+};
+
 SearchResultPage.getLayout = function getLayout(page) {
   return (
     <>
       <DrawioViewerScript />
-      <SearchResultLayout>{page}</SearchResultLayout>
+      <Layout {...page.props}>{page}</Layout>
     </>
   );
 };
 
-async function injectUserUISettings(context: GetServerSidePropsContext, props: Props): Promise<void> {
-  const { model: mongooseModel } = await import('mongoose');
-
-  const req = context.req as CrowiRequest<IUserHasId & any>;
-  const { user } = req;
-
-  const UserUISettings = mongooseModel('UserUISettings') as UserUISettingsModel;
-  const userUISettings = user == null ? null : await UserUISettings.findOne({ user: user._id }).exec();
-  if (userUISettings != null) {
-    props.userUISettings = userUISettings.toObject();
-  }
-}
-
 function injectServerConfigurations(context: GetServerSidePropsContext, props: Props): void {
   const req: CrowiRequest = context.req as CrowiRequest;
   const { crowi } = req;
@@ -190,7 +181,6 @@ export const getServerSideProps: GetServerSideProps = async(context: GetServerSi
     props.currentUser = user.toObject();
   }
 
-  await injectUserUISettings(context, props);
   injectServerConfigurations(context, props);
   await injectNextI18NextConfigurations(context, props, ['translation']);
 

+ 2 - 22
packages/app/src/pages/me/[[...path]].page.tsx

@@ -1,7 +1,6 @@
 import React, { useMemo } from 'react';
 
 import { IUserHasId } from '@growi/core';
-import { model as mongooseModel } from 'mongoose';
 import {
   GetServerSideProps, GetServerSidePropsContext,
 } from 'next';
@@ -14,23 +13,18 @@ import { useRouter } from 'next/router';
 import { BasicLayout } from '~/components/Layout/BasicLayout';
 import { CrowiRequest } from '~/interfaces/crowi-request';
 import type { RendererConfig } from '~/interfaces/services/renderer';
-import { ISidebarConfig } from '~/interfaces/sidebar-config';
-import { IUserUISettings } from '~/interfaces/user-ui-settings';
-import { UserUISettingsModel } from '~/server/models/user-ui-settings';
 import {
   useCurrentUser, useIsSearchPage,
   useIsSearchServiceConfigured, useIsSearchServiceReachable,
   useCsrfToken, useIsSearchScopeChildrenAsDefault,
   useRegistrationWhiteList, useShowPageLimitationXL, useRendererConfig,
 } from '~/stores/context';
-import {
-  usePreferDrawerModeByUser, usePreferDrawerModeOnEditByUser, useSidebarCollapsed, useCurrentSidebarContents, useCurrentProductNavWidth,
-} from '~/stores/ui';
 import loggerFactory from '~/utils/logger';
 
 import { NextPageWithLayout } from '../_app.page';
+import type { CommonProps } from '../utils/commons';
 import {
-  CommonProps, getNextI18NextConfig, getServerSideCommonProps, generateCustomTitle, useInitSidebarConfig,
+  getNextI18NextConfig, getServerSideCommonProps, generateCustomTitle, useInitSidebarConfig,
 } from '../utils/commons';
 
 
@@ -40,8 +34,6 @@ type Props = CommonProps & {
   isSearchServiceConfigured: boolean,
   isSearchServiceReachable: boolean,
   isSearchScopeChildrenAsDefault: boolean,
-  userUISettings?: IUserUISettings
-  sidebarConfig: ISidebarConfig,
   rendererConfig: RendererConfig,
   showPageLimitationXL: number,
 
@@ -139,17 +131,6 @@ MePage.getLayout = function getLayout(page) {
   );
 };
 
-async function injectUserUISettings(context: GetServerSidePropsContext, props: Props): Promise<void> {
-  const req = context.req as CrowiRequest<IUserHasId & any>;
-  const { user } = req;
-  const UserUISettings = mongooseModel('UserUISettings') as UserUISettingsModel;
-
-  const userUISettings = user == null ? null : await UserUISettings.findOne({ user: user._id }).exec();
-  if (userUISettings != null) {
-    props.userUISettings = userUISettings.toObject();
-  }
-}
-
 async function injectServerConfigurations(context: GetServerSidePropsContext, props: Props): Promise<void> {
   const req: CrowiRequest = context.req as CrowiRequest;
   const { crowi } = req;
@@ -221,7 +202,6 @@ export const getServerSideProps: GetServerSideProps = async(context: GetServerSi
     props.currentUser = userData.toObject();
   }
 
-  await injectUserUISettings(context, props);
   await injectServerConfigurations(context, props);
   await injectNextI18NextConfigurations(context, props, ['translation', 'admin', 'commons']);
 

+ 18 - 31
packages/app/src/pages/tags.page.tsx

@@ -1,7 +1,7 @@
-import React, { useState, useCallback } from 'react';
+import React, { useState, useCallback, ReactNode } from 'react';
 
 import type { IUser, IUserHasId } from '@growi/core';
-import { NextPage, GetServerSideProps, GetServerSidePropsContext } from 'next';
+import { GetServerSideProps, GetServerSidePropsContext } from 'next';
 import { useTranslation } from 'next-i18next';
 import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
 import dynamic from 'next/dynamic';
@@ -9,14 +9,8 @@ import Head from 'next/head';
 
 import type { CrowiRequest } from '~/interfaces/crowi-request';
 import type { RendererConfig } from '~/interfaces/services/renderer';
-import type { ISidebarConfig } from '~/interfaces/sidebar-config';
 import type { IDataTagCount } from '~/interfaces/tag';
-import type { IUserUISettings } from '~/interfaces/user-ui-settings';
-import type { UserUISettingsModel } from '~/server/models/user-ui-settings';
 import { useSWRxTagsList } from '~/stores/tag';
-import {
-  usePreferDrawerModeByUser, usePreferDrawerModeOnEditByUser, useSidebarCollapsed, useCurrentSidebarContents, useCurrentProductNavWidth,
-} from '~/stores/ui';
 
 import { BasicLayout } from '../components/Layout/BasicLayout';
 import {
@@ -26,8 +20,9 @@ import {
 } from '../stores/context';
 
 import { NextPageWithLayout } from './_app.page';
+import type { CommonProps } from './utils/commons';
 import {
-  CommonProps, getServerSideCommonProps, getNextI18NextConfig, generateCustomTitle, useInitSidebarConfig,
+  getServerSideCommonProps, getNextI18NextConfig, generateCustomTitle, useInitSidebarConfig,
 } from './utils/commons';
 
 const PAGING_LIMIT = 10;
@@ -38,12 +33,6 @@ type Props = CommonProps & {
   isSearchServiceReachable: boolean,
   isSearchScopeChildrenAsDefault: boolean,
 
-  // ui
-  userUISettings?: IUserUISettings
-
-  // sidebar
-  sidebarConfig: ISidebarConfig,
-
   rendererConfig: RendererConfig,
 };
 
@@ -115,26 +104,25 @@ const TagPage: NextPageWithLayout<CommonProps> = (props: Props) => {
   );
 };
 
+type LayoutProps = Props & {
+  children?: ReactNode
+}
+
+const Layout = ({ children, ...props }: LayoutProps): JSX.Element => {
+  // init sidebar config with UserUISettings and sidebarConfig
+  useInitSidebarConfig(props.sidebarConfig, props.userUISettings);
+
+  return <BasicLayout>{children}</BasicLayout>;
+};
+
 TagPage.getLayout = function getLayout(page) {
   return (
-    <BasicLayout>{page}</BasicLayout>
+    <Layout {...page.props}>
+      {page}
+    </Layout>
   );
 };
 
-async function injectUserUISettings(context: GetServerSidePropsContext, props: Props): Promise<void> {
-  const { model: mongooseModel } = await import('mongoose');
-
-  const req = context.req as CrowiRequest<IUserHasId & any>;
-  const { user } = req;
-
-  const UserUISettings = mongooseModel('UserUISettings') as UserUISettingsModel;
-  const userUISettings = user == null ? null : await UserUISettings.findOne({ user: user._id }).exec();
-
-  if (userUISettings != null) {
-    props.userUISettings = userUISettings.toObject();
-  }
-}
-
 function injectServerConfigurations(context: GetServerSidePropsContext, props: Props): void {
   const req: CrowiRequest = context.req as CrowiRequest;
   const { crowi } = req;
@@ -194,7 +182,6 @@ export const getServerSideProps: GetServerSideProps = async(context: GetServerSi
     props.currentUser = user.toObject();
   }
 
-  await injectUserUISettings(context, props);
   injectServerConfigurations(context, props);
   await injectNextI18NextConfigurations(context, props, ['translation']);
 

+ 17 - 30
packages/app/src/pages/trash.page.tsx

@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { ReactNode } from 'react';
 
 import type { IUser, IUserHasId } from '@growi/core';
 import type { GetServerSideProps, GetServerSidePropsContext } from 'next';
@@ -9,12 +9,7 @@ import Head from 'next/head';
 import { GrowiSubNavigation } from '~/components/Navbar/GrowiSubNavigation';
 import type { CrowiRequest } from '~/interfaces/crowi-request';
 import type { RendererConfig } from '~/interfaces/services/renderer';
-import type { ISidebarConfig } from '~/interfaces/sidebar-config';
-import type { IUserUISettings } from '~/interfaces/user-ui-settings';
-import type { UserUISettingsModel } from '~/server/models/user-ui-settings';
-import {
-  useCurrentProductNavWidth, useCurrentSidebarContents, useDrawerMode, usePreferDrawerModeByUser, usePreferDrawerModeOnEditByUser, useSidebarCollapsed,
-} from '~/stores/ui';
+import { useDrawerMode } from '~/stores/ui';
 
 import { BasicLayout } from '../components/Layout/BasicLayout';
 import {
@@ -24,8 +19,9 @@ import {
 } from '../stores/context';
 
 import type { NextPageWithLayout } from './_app.page';
+import type { CommonProps } from './utils/commons';
 import {
-  getServerSideCommonProps, getNextI18NextConfig, generateCustomTitleForPage, CommonProps, useInitSidebarConfig,
+  getServerSideCommonProps, getNextI18NextConfig, generateCustomTitleForPage, useInitSidebarConfig,
 } from './utils/commons';
 
 const TrashPageList = dynamic(() => import('~/components/TrashPageList').then(mod => mod.TrashPageList), { ssr: false });
@@ -39,11 +35,6 @@ type Props = CommonProps & {
   isSearchScopeChildrenAsDefault: boolean,
   showPageLimitationXL: number,
 
-  // UI
-  userUISettings?: IUserUISettings
-  // Sidebar
-  sidebarConfig: ISidebarConfig,
-
   rendererConfig: RendererConfig,
 };
 
@@ -96,32 +87,29 @@ const TrashPage: NextPageWithLayout<CommonProps> = (props: Props) => {
   );
 };
 
+type LayoutProps = Props & {
+  children?: ReactNode,
+}
+
+const Layout = ({ children, ...props }: LayoutProps): JSX.Element => {
+  // init sidebar config with UserUISettings and sidebarConfig
+  useInitSidebarConfig(props.sidebarConfig, props.userUISettings);
+
+  return <BasicLayout>{children}</BasicLayout>;
+};
+
 TrashPage.getLayout = function getLayout(page) {
   return (
     <>
-      <BasicLayout>
+      <Layout {...page.props}>
         {page}
-      </BasicLayout>
+      </Layout>
       <EmptyTrashModal />
       <PutbackPageModal />
     </>
   );
 };
 
-async function injectUserUISettings(context: GetServerSidePropsContext, props: Props): Promise<void> {
-  const { model: mongooseModel } = await import('mongoose');
-
-  const req = context.req as CrowiRequest<IUserHasId & any>;
-  const { user } = req;
-
-  const UserUISettings = mongooseModel('UserUISettings') as UserUISettingsModel;
-  const userUISettings = user == null ? null : await UserUISettings.findOne({ user: user._id }).exec();
-
-  if (userUISettings != null) {
-    props.userUISettings = userUISettings.toObject();
-  }
-}
-
 function injectServerConfigurations(context: GetServerSidePropsContext, props: Props): void {
   const req: CrowiRequest = context.req as CrowiRequest;
   const { crowi } = req;
@@ -181,7 +169,6 @@ export const getServerSideProps: GetServerSideProps = async(context: GetServerSi
   if (user != null) {
     props.currentUser = user.toObject();
   }
-  await injectUserUISettings(context, props);
   injectServerConfigurations(context, props);
   await injectNextI18NextConfigurations(context, props, ['translation']);
 

+ 15 - 0
packages/app/src/pages/utils/commons.ts

@@ -11,6 +11,7 @@ import { detectLocaleFromBrowserAcceptLanguage } from '~/client/util/locale-util
 import type { CrowiRequest } from '~/interfaces/crowi-request';
 import type { ISidebarConfig } from '~/interfaces/sidebar-config';
 import type { IUserUISettings } from '~/interfaces/user-ui-settings';
+import { UserUISettingsDocument } from '~/server/models/user-ui-settings';
 import {
   useCurrentProductNavWidth, useCurrentSidebarContents, usePreferDrawerModeByUser, usePreferDrawerModeOnEditByUser, useSidebarCollapsed,
 } from '~/stores/ui';
@@ -30,10 +31,13 @@ export type CommonProps = {
   isDefaultLogo: boolean,
   currentUser?: IUserHasId,
   forcedColorScheme?: ColorScheme,
+  sidebarConfig: ISidebarConfig,
+  userUISettings?: IUserUISettings
 } & Partial<SSRConfig>;
 
 // eslint-disable-next-line max-len
 export const getServerSideCommonProps: GetServerSideProps<CommonProps> = async(context: GetServerSidePropsContext) => {
+  const getModelSafely = await import('~/server/util/mongoose-utils').then(mod => mod.getModelSafely);
 
   const req = context.req as CrowiRequest<IUserHasId & any>;
   const { crowi, user } = req;
@@ -70,6 +74,12 @@ export const getServerSideCommonProps: GetServerSideProps<CommonProps> = async(c
   const isDefaultLogo = crowi.configManager.getConfig('crowi', 'customize:isDefaultLogo') || !isCustomizedLogoUploaded;
   const forcedColorScheme = crowi.customizeService.forcedColorScheme;
 
+  // retrieve UserUISettings
+  const UserUISettings = getModelSafely<UserUISettingsDocument>('UserUISettings');
+  const userUISettings = user != null && UserUISettings != null
+    ? await UserUISettings.findOne({ user: user._id }).exec()
+    : req.session.uiSettings; // for guests
+
   const props: CommonProps = {
     namespacesRequired: ['translation'],
     currentPathname,
@@ -85,6 +95,11 @@ export const getServerSideCommonProps: GetServerSideProps<CommonProps> = async(c
     currentUser,
     isDefaultLogo,
     forcedColorScheme,
+    sidebarConfig: {
+      isSidebarDrawerMode: configManager.getConfig('crowi', 'customize:isSidebarDrawerMode'),
+      isSidebarClosedAtDockMode: configManager.getConfig('crowi', 'customize:isSidebarClosedAtDockMode'),
+    },
+    userUISettings: userUISettings?.toObject?.() ?? userUISettings,
   };
 
   return { props };

+ 0 - 37
packages/app/src/server/middlewares/inject-user-ui-settings-to-localvars.ts

@@ -1,37 +0,0 @@
-import { IUserUISettings } from '~/interfaces/user-ui-settings';
-import loggerFactory from '~/utils/logger';
-
-import UserUISettings from '../models/user-ui-settings';
-
-const logger = loggerFactory('growi:middleware:inject-user-ui-settings-to-localvars');
-
-async function getSettings(userId: string): Promise<IUserUISettings | null> {
-  const doc = await UserUISettings.findOne({ user: userId }).exec();
-
-  let userUISettings: IUserUISettings | null = null;
-  if (doc != null) {
-    const obj = doc.toObject();
-    // omit user property
-    // eslint-disable-next-line @typescript-eslint/no-unused-vars
-    userUISettings = (({ user, ...rest }) => rest)(obj);
-  }
-
-  return userUISettings;
-}
-
-module.exports = () => {
-  return async(req, res, next) => {
-    if (req.user == null) {
-      return next();
-    }
-
-    try {
-      res.locals.userUISettings = await getSettings(req.user._id);
-    }
-    catch (err: unknown) {
-      logger.error(err);
-    }
-
-    next();
-  };
-};

+ 2 - 2
packages/app/src/server/models/user-ui-settings.ts

@@ -1,11 +1,11 @@
-import { Ref, IUser } from '@growi/core';
+import type { Ref, IUser } from '@growi/core';
 import {
   Schema, Model, Document,
 } from 'mongoose';
 
 
 import { SidebarContentsType } from '~/interfaces/ui';
-import { IUserUISettings } from '~/interfaces/user-ui-settings';
+import type { IUserUISettings } from '~/interfaces/user-ui-settings';
 
 import { getOrCreateModel } from '../util/mongoose-utils';
 

+ 2 - 1
packages/app/src/server/util/mongoose-utils.ts

@@ -1,4 +1,5 @@
-import mongoose, {
+import mongoose from 'mongoose';
+import type {
   Model, Document, Schema, ConnectOptions,
 } from 'mongoose';
 

+ 5 - 12
packages/app/src/stores/use-context-swr.tsx

@@ -1,9 +1,10 @@
 import assert from 'assert';
 
 import {
-  Key, SWRConfiguration, SWRResponse, useSWRConfig,
+  Key, SWRConfiguration, SWRResponse,
 } from 'swr';
-import useSWRImmutable from 'swr/immutable';
+
+import { useStaticSWR } from './use-static-swr';
 
 
 export function useContextSWR<Data, Error>(key: Key): SWRResponse<Data, Error>;
@@ -20,17 +21,9 @@ export function useContextSWR<Data, Error>(
 
   assert.notStrictEqual(configuration?.fetcher, null, 'useContextSWR does not support \'configuration.fetcher\'');
 
-  const { cache } = useSWRConfig();
-  const swrResponse = useSWRImmutable(key, null, {
-    ...configuration,
-    fallbackData: configuration?.fallbackData ?? cache.get(key)?.data,
-  });
-
-  // write data to cache directly
-  if (data !== undefined) {
-    cache.set(key, { ...cache.get(key), data });
-  }
+  const swrResponse = useStaticSWR(key, data, configuration);
 
+  // overwrite mutate
   const result = Object.assign(swrResponse, { mutate: () => { throw Error('mutate can not be used in context') } });
 
   return result;

+ 10 - 10
packages/app/src/stores/use-static-swr.tsx

@@ -1,9 +1,7 @@
-import { useEffect } from 'react';
-
 import assert from 'assert';
 
 import {
-  mutate, Key, SWRConfiguration, SWRResponse,
+  Key, SWRConfiguration, SWRResponse, useSWRConfig,
 } from 'swr';
 import useSWRImmutable from 'swr/immutable';
 
@@ -22,14 +20,16 @@ export function useStaticSWR<Data, Error>(
 
   assert.notStrictEqual(configuration?.fetcher, null, 'useStaticSWR does not support \'configuration.fetcher\'');
 
-  const swrResponse = useSWRImmutable(key, null, configuration);
+  const { cache } = useSWRConfig();
+  const swrResponse = useSWRImmutable(key, null, {
+    ...configuration,
+    fallbackData: configuration?.fallbackData ?? cache.get(key)?.data,
+  });
 
-  // Do mutate with `data` from args
-  useEffect(() => {
-    if (data !== undefined) {
-      mutate(key, data);
-    }
-  }, [data, key]);
+  // write data to cache directly
+  if (data !== undefined) {
+    cache.set(key, { ...cache.get(key), data });
+  }
 
   return swrResponse;
 }