Quellcode durchsuchen

implement me page

Yuki Takei vor 7 Monaten
Ursprung
Commit
78843718ee

+ 2 - 2
apps/app/src/client/components/Me/BasicInfoSettings.tsx

@@ -5,12 +5,12 @@ import { useTranslation, i18n } from 'next-i18next';
 import { i18n as i18nConfig } from '^/config/next-i18next.config';
 import { i18n as i18nConfig } from '^/config/next-i18next.config';
 
 
 import { toastSuccess, toastError } from '~/client/util/toastr';
 import { toastSuccess, toastError } from '~/client/util/toastr';
-import { useRegistrationWhitelist } from '~/stores-universal/context';
+import { useRegistrationWhitelist } from '~/states/server-configurations';
 import { usePersonalSettings } from '~/stores/personal-settings';
 import { usePersonalSettings } from '~/stores/personal-settings';
 
 
 export const BasicInfoSettings = (): JSX.Element => {
 export const BasicInfoSettings = (): JSX.Element => {
   const { t } = useTranslation();
   const { t } = useTranslation();
-  const { data: registrationWhitelist } = useRegistrationWhitelist();
+  const [registrationWhitelist] = useRegistrationWhitelist();
 
 
   const {
   const {
     data: personalSettingsInfo, mutate: mutatePersonalSettings, sync, updateBasicInfo, error,
     data: personalSettingsInfo, mutate: mutatePersonalSettings, sync, updateBasicInfo, error,

+ 49 - 126
apps/app/src/pages/me/[[...path]]/index.page.tsx

@@ -4,7 +4,6 @@ import type {
   GetServerSideProps, GetServerSidePropsContext,
   GetServerSideProps, GetServerSidePropsContext,
 } from 'next';
 } from 'next';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
-import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
 import dynamic from 'next/dynamic';
 import dynamic from 'next/dynamic';
 import Head from 'next/head';
 import Head from 'next/head';
 import { useRouter } from 'next/router';
 import { useRouter } from 'next/router';
@@ -12,40 +11,25 @@ import { useRouter } from 'next/router';
 import { BasicLayout } from '~/components/Layout/BasicLayout';
 import { BasicLayout } from '~/components/Layout/BasicLayout';
 import { GroundGlassBar } from '~/components/Navbar/GroundGlassBar';
 import { GroundGlassBar } from '~/components/Navbar/GroundGlassBar';
 import type { CrowiRequest } from '~/interfaces/crowi-request';
 import type { CrowiRequest } from '~/interfaces/crowi-request';
-import type { RendererConfig } from '~/interfaces/services/renderer';
-import type { ISidebarConfig } from '~/interfaces/sidebar-config';
-import { useHydratePageAtoms } from '~/states/hydrate/page';
-import { useHydrateSidebarAtoms } from '~/states/hydrate/sidebar';
-import {
-  useCurrentUser, useIsSearchPage, useGrowiCloudUri,
-  useIsSearchServiceConfigured, useIsSearchServiceReachable,
-  useCsrfToken, useIsSearchScopeChildrenAsDefault,
-  useRegistrationWhitelist, useShowPageLimitationXL, useRendererConfig, useIsEnabledMarp, useCurrentPathname,
-} from '~/stores-universal/context';
+import type { BasicLayoutConfigurationProps } from '~/pages/basic-layout-page';
+import { getServerSideBasicLayoutProps } from '~/pages/basic-layout-page';
+import { useHydrateBasicLayoutConfigurationAtoms } from '~/pages/basic-layout-page/hydrate';
+import { useCustomTitle } from '~/pages/utils/page-title-customization';
+import { mergeGetServerSidePropsResults } from '~/pages/utils/server-side-props';
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 
 
 import type { NextPageWithLayout } from '../../_app.page';
 import type { NextPageWithLayout } from '../../_app.page';
-import type { CommonProps } from '../../common-props';
+import type { CommonEachProps, CommonInitialProps } from '../../common-props';
 import {
 import {
-  getNextI18NextConfig, getServerSideCommonProps, generateCustomTitle,
+  getServerSideCommonEachProps, getServerSideCommonInitialProps, getServerSideI18nProps,
 } from '../../common-props';
 } from '../../common-props';
 
 
+import type { ServerConfigurationProps } from './types';
+import { useHydrateServerConfigurationAtoms } from './use-hydrate-server-configurations';
 
 
-const logger = loggerFactory('growi:pages:me');
-
-type Props = CommonProps & {
-  isSearchServiceConfigured: boolean,
-  isSearchServiceReachable: boolean,
-  isSearchScopeChildrenAsDefault: boolean,
-  isEnabledMarp: boolean,
-  rendererConfig: RendererConfig,
-  showPageLimitationXL: number,
 
 
-  // config
-  registrationWhitelist: string[],
+const logger = loggerFactory('growi:pages:me');
 
 
-  sidebarConfig: ISidebarConfig,
-};
 
 
 const PersonalSettings = dynamic(() => import('~/client/components/Me/PersonalSettings'), { ssr: false });
 const PersonalSettings = dynamic(() => import('~/client/components/Me/PersonalSettings'), { ssr: false });
 // const MyDraftList = dynamic(() => import('~/components/MyDraftList/MyDraftList'), { ssr: false });
 // const MyDraftList = dynamic(() => import('~/components/MyDraftList/MyDraftList'), { ssr: false });
@@ -53,7 +37,12 @@ const InAppNotificationPage = dynamic(
   () => import('~/client/components/InAppNotification/InAppNotificationPage').then(mod => mod.InAppNotificationPage), { ssr: false },
   () => import('~/client/components/InAppNotification/InAppNotificationPage').then(mod => mod.InAppNotificationPage), { ssr: false },
 );
 );
 
 
+
+type Props = CommonInitialProps & CommonEachProps & BasicLayoutConfigurationProps & ServerConfigurationProps;
+
 const MePage: NextPageWithLayout<Props> = (props: Props) => {
 const MePage: NextPageWithLayout<Props> = (props: Props) => {
+  useHydrateServerConfigurationAtoms(props.serverConfig);
+
   const router = useRouter();
   const router = useRouter();
   const { t } = useTranslation(['translation', 'commons']);
   const { t } = useTranslation(['translation', 'commons']);
   const { path } = router.query;
   const { path } = router.query;
@@ -91,32 +80,12 @@ const MePage: NextPageWithLayout<Props> = (props: Props) => {
 
 
   const targetPage = getTargetPageToRender(mePagesMap, pagePathKeys);
   const targetPage = getTargetPageToRender(mePagesMap, pagePathKeys);
 
 
-  useIsSearchPage(false);
-
-  useRegistrationWhitelist(props.registrationWhitelist);
+  // // clear the cache for the current page
+  // //  in order to fix https://redmine.weseek.co.jp/issues/135811
+  // useHydratePageAtoms(undefined);
+  // useCurrentPathname('/me');
 
 
-  useShowPageLimitationXL(props.showPageLimitationXL);
-
-  // commons
-  useCsrfToken(props.csrfToken);
-  useGrowiCloudUri(props.growiCloudUri);
-
-  useCurrentUser(props.currentUser ?? null);
-
-  // clear the cache for the current page
-  //  in order to fix https://redmine.weseek.co.jp/issues/135811
-  useHydratePageAtoms(undefined);
-  useCurrentPathname('/me');
-
-  // page
-  useIsSearchServiceConfigured(props.isSearchServiceConfigured);
-  useIsSearchServiceReachable(props.isSearchServiceReachable);
-  useIsSearchScopeChildrenAsDefault(props.isSearchScopeChildrenAsDefault);
-
-  useRendererConfig(props.rendererConfig);
-  useIsEnabledMarp(props.rendererConfig.isEnabledMarp);
-
-  const title = generateCustomTitle(props, targetPage.title);
+  const title = useCustomTitle(targetPage.title);
 
 
   return (
   return (
     <>
     <>
@@ -146,7 +115,7 @@ type LayoutProps = Props & {
 }
 }
 
 
 const Layout = ({ children, ...props }: LayoutProps): JSX.Element => {
 const Layout = ({ children, ...props }: LayoutProps): JSX.Element => {
-  useHydrateSidebarAtoms(props.sidebarConfig, props.userUISettings);
+  useHydrateBasicLayoutConfigurationAtoms(props.searchConfig, props.sidebarConfig, props.userUISettings);
 
 
   return (
   return (
     <BasicLayout>
     <BasicLayout>
@@ -159,86 +128,40 @@ MePage.getLayout = function getLayout(page) {
   return <Layout {...page.props}>{page}</Layout>;
   return <Layout {...page.props}>{page}</Layout>;
 };
 };
 
 
-async function injectServerConfigurations(context: GetServerSidePropsContext, props: Props): Promise<void> {
+const getServerSideConfigurationProps: GetServerSideProps<ServerConfigurationProps> = async(context: GetServerSidePropsContext) => {
   const req: CrowiRequest = context.req as CrowiRequest;
   const req: CrowiRequest = context.req as CrowiRequest;
   const { crowi } = req;
   const { crowi } = req;
-  const {
-    searchService,
-    configManager,
-  } = crowi;
-
-  props.isSearchServiceConfigured = searchService.isConfigured;
-  props.isSearchServiceReachable = searchService.isReachable;
-  props.isSearchScopeChildrenAsDefault = configManager.getConfig('customize:isSearchScopeChildrenAsDefault');
-
-  props.registrationWhitelist = configManager.getConfig('security:registrationWhitelist');
+  const { configManager } = crowi;
 
 
-  props.showPageLimitationXL = crowi.configManager.getConfig('customize:showPageLimitationXL');
-
-  props.sidebarConfig = {
-    isSidebarCollapsedMode: configManager.getConfig('customize:isSidebarCollapsedMode'),
-    isSidebarClosedAtDockMode: configManager.getConfig('customize:isSidebarClosedAtDockMode'),
-  };
-
-  props.rendererConfig = {
-    isEnabledLinebreaks: configManager.getConfig('markdown:isEnabledLinebreaks'),
-    isEnabledLinebreaksInComments: configManager.getConfig('markdown:isEnabledLinebreaksInComments'),
-    isEnabledMarp: configManager.getConfig('customize:isEnabledMarp'),
-    adminPreferredIndentSize: configManager.getConfig('markdown:adminPreferredIndentSize'),
-    isIndentSizeForced: configManager.getConfig('markdown:isIndentSizeForced'),
-
-    drawioUri: configManager.getConfig('app:drawioUri'),
-    plantumlUri: configManager.getConfig('app:plantumlUri'),
-
-    // XSS Options
-    isEnabledXssPrevention: configManager.getConfig('markdown:rehypeSanitize:isEnabledPrevention'),
-    sanitizeType: configManager.getConfig('markdown:rehypeSanitize:option'),
-    customTagWhitelist: crowi.configManager.getConfig('markdown:rehypeSanitize:tagNames'),
-    customAttrWhitelist: configManager.getConfig('markdown:rehypeSanitize:attributes') != null
-      ? JSON.parse(configManager.getConfig('markdown:rehypeSanitize:attributes'))
-      : undefined,
-    highlightJsStyleBorder: crowi.configManager.getConfig('customize:highlightJsStyleBorder'),
+  return {
+    props: {
+      serverConfig: {
+        registrationWhitelist: configManager.getConfig('security:registrationWhitelist'),
+        showPageLimitationXL: configManager.getConfig('customize:showPageLimitationXL'),
+      },
+    },
   };
   };
-}
-
-// /**
-//  * for Server Side Translations
-//  * @param context
-//  * @param props
-//  * @param namespacesRequired
-//  */
-async function injectNextI18NextConfigurations(context: GetServerSidePropsContext, props: Props, namespacesRequired?: string[] | undefined): Promise<void> {
-  // preload all languages because of language lists in user setting
-  const nextI18NextConfig = await getNextI18NextConfig(serverSideTranslations, context, namespacesRequired, true);
-  props._nextI18Next = nextI18NextConfig._nextI18Next;
-}
+};
 
 
 export const getServerSideProps: GetServerSideProps = async(context: GetServerSidePropsContext) => {
 export const getServerSideProps: GetServerSideProps = async(context: GetServerSidePropsContext) => {
-  const req = context.req as CrowiRequest;
-  const { user, crowi } = req;
-
-  const result = await getServerSideCommonProps(context);
-
-  // check for presence
-  // see: https://github.com/vercel/next.js/issues/19271#issuecomment-730006862
-  if (!('props' in result)) {
-    throw new Error('invalid getSSP result');
-  }
-
-  const props: Props = result.props as Props;
-
-  if (user != null) {
-    const User = crowi.model('User');
-    const userData = await User.findById(user.id).populate({ path: 'imageAttachment', select: 'filePathProxied' });
-    props.currentUser = userData.toObject();
-  }
-
-  await injectServerConfigurations(context, props);
-  await injectNextI18NextConfigurations(context, props, ['translation', 'admin', 'commons']);
-
-  return {
-    props,
-  };
+  const [
+    commonInitialResult,
+    commonEachResult,
+    basicLayoutResult,
+    serverConfigResult,
+    i18nPropsResult,
+  ] = await Promise.all([
+    getServerSideCommonInitialProps(context),
+    getServerSideCommonEachProps(context),
+    getServerSideBasicLayoutProps(context),
+    getServerSideConfigurationProps(context),
+    getServerSideI18nProps(context, ['translation', 'admin']),
+  ]);
+
+  return mergeGetServerSidePropsResults(commonInitialResult,
+    mergeGetServerSidePropsResults(commonEachResult,
+      mergeGetServerSidePropsResults(basicLayoutResult,
+        mergeGetServerSidePropsResults(serverConfigResult, i18nPropsResult))));
 };
 };
 
 
 export default MePage;
 export default MePage;

+ 6 - 0
apps/app/src/pages/me/[[...path]]/types.ts

@@ -0,0 +1,6 @@
+export type ServerConfigurationProps = {
+  serverConfig: {
+    showPageLimitationXL: number,
+    registrationWhitelist: string[],
+  },
+}

+ 22 - 0
apps/app/src/pages/me/[[...path]]/use-hydrate-server-configurations.ts

@@ -0,0 +1,22 @@
+import { useHydrateAtoms } from 'jotai/utils';
+
+import {
+  showPageLimitationXLAtom,
+  registrationWhitelistAtom,
+} from '~/states/server-configurations';
+
+import type { ServerConfigurationProps } from './types';
+
+/**
+ * Hook for hydrating server configuration atoms with server-side data
+ * This should be called early in the app component to ensure atoms are properly initialized before rendering
+ */
+export const useHydrateServerConfigurationAtoms = (
+    serverConfig: ServerConfigurationProps['serverConfig'] | undefined,
+): void => {
+  // Hydrate server configuration atoms with server-side data
+  useHydrateAtoms(serverConfig == null ? [] : [
+    [showPageLimitationXLAtom, serverConfig.showPageLimitationXL],
+    [registrationWhitelistAtom, serverConfig.registrationWhitelist],
+  ]);
+};

+ 11 - 0
apps/app/src/states/server-configurations/server-configurations.ts

@@ -234,6 +234,17 @@ export const useIsAclEnabled = (): UseAtom<typeof isAclEnabledAtom> => {
   return useAtom(isAclEnabledAtom);
   return useAtom(isAclEnabledAtom);
 };
 };
 
 
+/**
+ * Atom for registration whitelist
+ */
+export const registrationWhitelistAtom = atom<string[] | null>(null);
+
+export const useRegistrationWhitelist = (): UseAtom<
+  typeof registrationWhitelistAtom
+> => {
+  return useAtom(registrationWhitelistAtom);
+};
+
 /**
 /**
  * Atom for upload all file allowed
  * Atom for upload all file allowed
  */
  */

+ 0 - 11
apps/app/src/stores-universal/context.tsx

@@ -17,17 +17,6 @@ declare global {
   var globalEmitter: EventEmitter;
   var globalEmitter: EventEmitter;
 }
 }
 
 
-type Nullable<T> = T | null;
-
-export const useRegistrationWhitelist = (
-  initialData?: Nullable<string[]>,
-): SWRResponse<Nullable<string[]>, Error> => {
-  return useContextSWR<Nullable<string[]>, Error>(
-    'registrationWhitelist',
-    initialData,
-  );
-};
-
 export const useAuditLogEnabled = (
 export const useAuditLogEnabled = (
   initialData?: boolean,
   initialData?: boolean,
 ): SWRResponse<boolean, Error> => {
 ): SWRResponse<boolean, Error> => {