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

add utils and detect next.js routing type

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

+ 2 - 0
apps/app/package.json

@@ -146,6 +146,7 @@
     "is-absolute-url": "^4.0.1",
     "is-iso-date": "^0.0.1",
     "jotai": "^2.12.3",
+    "js-cookie": "^3.0.5",
     "js-tiktoken": "^1.0.15",
     "js-yaml": "^4.1.0",
     "jsonrepair": "^3.12.0",
@@ -281,6 +282,7 @@
     "@types/express": "^4.17.21",
     "@types/hast": "^3.0.4",
     "@types/jest": "^29.5.2",
+    "@types/js-cookie": "^3.0.6",
     "@types/ldapjs": "^2.2.5",
     "@types/mdast": "^4.0.4",
     "@types/node-cron": "^3.0.11",

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

@@ -1,5 +1,5 @@
 import type { ReactNode, JSX } from 'react';
-import React, { useEffect, useRef } from 'react';
+import React, { useEffect } from 'react';
 
 import EventEmitter from 'events';
 
@@ -66,6 +66,7 @@ import {
   getServerSidePageTitleCustomizationProps,
   getNextI18NextConfig, getServerSideCommonProps, generateCustomTitleForPage, skipSSR, addActivity,
 } from './utils/commons';
+import { detectNextjsRoutingType } from './utils/nextjs-routing-utils';
 
 
 declare global {
@@ -628,10 +629,17 @@ const getAction = (props: Props): SupportedActionType => {
   return SupportedAction.ACTION_PAGE_VIEW;
 };
 
+const NEXT_JS_ROUTING_PAGE = '[[...path]]';
+
 export const getServerSideProps: GetServerSideProps = async(context: GetServerSidePropsContext) => {
   const req = context.req as CrowiRequest;
   const { user } = req;
 
+  // detect Next.js routing type
+  const nextjsRoutingType = detectNextjsRoutingType(context, NEXT_JS_ROUTING_PAGE);
+
+  console.log('=== getServerSideProps ===', { nextjsRoutingType });
+
   const commonPropsResult = await getServerSideCommonProps(context);
   const pageTitleCustomizeationPropsResult = await getServerSidePageTitleCustomizationProps(context);
 
@@ -644,6 +652,7 @@ export const getServerSideProps: GetServerSideProps = async(context: GetServerSi
   const props: Props = {
     ...commonPropsResult.props,
     ...pageTitleCustomizeationPropsResult.props,
+    nextjsRoutingPage: NEXT_JS_ROUTING_PAGE,
   } as Props;
 
   if (props.redirectDestination != null) {

+ 9 - 6
apps/app/src/pages/_app.page.tsx

@@ -18,6 +18,7 @@ import { useHydrateGlobalAtoms } from '~/states/hydrate/global';
 import { swrGlobalConfiguration } from '~/utils/swr-utils';
 
 import { getLocaleAtServerSide, type CommonProps } from './utils/commons';
+import { useNextjsRoutingPageRegister } from './utils/nextjs-routing-utils';
 import { registerTransformerForObjectId } from './utils/objectid-transformer';
 
 import '~/styles/prebuilt/vendor.css';
@@ -39,6 +40,14 @@ registerTransformerForObjectId();
 function GrowiApp({ Component, pageProps, userLocale }: GrowiAppProps): JSX.Element {
   const router = useRouter();
 
+  const commonPageProps = pageProps as CommonProps;
+
+  // Hydrate global atoms with server-side data
+  useHydrateGlobalAtoms(commonPageProps);
+  useAutoUpdateGlobalAtoms(commonPageProps);
+
+  useNextjsRoutingPageRegister(commonPageProps.nextjsRoutingPage);
+
   useEffect(() => {
     const updateLangAttribute = () => {
       if (document.documentElement.getAttribute('lang') !== userLocale) {
@@ -55,12 +64,6 @@ function GrowiApp({ Component, pageProps, userLocale }: GrowiAppProps): JSX.Elem
     import('bootstrap/dist/js/bootstrap');
   }, []);
 
-  const commonPageProps = pageProps as CommonProps;
-
-  // Hydrate global atoms with server-side data
-  useHydrateGlobalAtoms(commonPageProps);
-  useAutoUpdateGlobalAtoms(commonPageProps);
-
   // Use the layout defined at the page level, if available
   const getLayout = Component.getLayout ?? (page => page);
 

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

@@ -18,6 +18,7 @@ import { getGrowiVersion } from '~/utils/growi-version';
 export type CommonProps = {
   currentPathname: string,
   currentUser?: IUserHasId,
+  nextjsRoutingPage?: string,
   appTitle: string,
   siteUrl: string | undefined,
   csrfToken: string,

+ 42 - 0
apps/app/src/pages/utils/nextjs-routing-utils.ts

@@ -0,0 +1,42 @@
+import { useEffect } from 'react';
+
+import Cookies from 'js-cookie';
+import { type GetServerSidePropsContext } from 'next';
+
+const COOKIE_NAME = 'nextjsRoutingPage';
+
+export const useNextjsRoutingPageRegister = (nextjsRoutingPage: string | undefined): void => {
+  useEffect(() => {
+    if (nextjsRoutingPage == null) {
+      Cookies.remove(COOKIE_NAME);
+      return;
+    }
+
+    Cookies.set(COOKIE_NAME, nextjsRoutingPage, { path: '/', expires: 1 / 24 }); // expires in 1 hour
+  }, [nextjsRoutingPage]);
+};
+
+const NextjsRoutingType = {
+  INITIAL: 'initial',
+  SAME_ROUTE: 'same-route',
+  FROM_OUTSIDE: 'from-outside',
+} as const;
+type NextjsRoutingType = (typeof NextjsRoutingType)[keyof typeof NextjsRoutingType];
+
+export const detectNextjsRoutingType = (context: GetServerSidePropsContext, previousRoutingPage: string): NextjsRoutingType => {
+  const isCSR = !!context.req.headers['x-nextjs-data'];
+
+  if (!isCSR) {
+    return NextjsRoutingType.INITIAL;
+  }
+
+  // Read cookie from server-side context
+  const nextjsRoutingPage = context.req.cookies[COOKIE_NAME];
+
+  if (nextjsRoutingPage != null && nextjsRoutingPage === previousRoutingPage) {
+    return NextjsRoutingType.SAME_ROUTE;
+  }
+
+  return NextjsRoutingType.FROM_OUTSIDE;
+
+};

+ 18 - 0
pnpm-lock.yaml

@@ -451,6 +451,9 @@ importers:
       jotai:
         specifier: ^2.12.3
         version: 2.12.3(@types/react@18.3.3)(react@18.2.0)
+      js-cookie:
+        specifier: ^3.0.5
+        version: 3.0.5
       js-tiktoken:
         specifier: ^1.0.15
         version: 1.0.15
@@ -833,6 +836,9 @@ importers:
       '@types/jest':
         specifier: ^29.5.2
         version: 29.5.12
+      '@types/js-cookie':
+        specifier: ^3.0.6
+        version: 3.0.6
       '@types/ldapjs':
         specifier: ^2.2.5
         version: 2.2.5
@@ -3135,6 +3141,7 @@ packages:
 
   '@handsontable/react@2.1.0':
     resolution: {integrity: sha512-Du73MFU2y1Bfe9m7mvxY70lB2R/VigFSpOwWZjDnUt/HwNPbNr+UQcY40w6u7acllQeee45H7jRdEExzsrvDKw==}
+    deprecated: Handsontable for React is now available as @handsontable/react-wrapper.
     peerDependencies:
       handsontable: '>=6.0.0'
 
@@ -5567,6 +5574,9 @@ packages:
   '@types/jest@29.5.12':
     resolution: {integrity: sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==}
 
+  '@types/js-cookie@3.0.6':
+    resolution: {integrity: sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==}
+
   '@types/json-schema@7.0.15':
     resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
 
@@ -10195,6 +10205,10 @@ packages:
   jpeg-js@0.4.4:
     resolution: {integrity: sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==}
 
+  js-cookie@3.0.5:
+    resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==}
+    engines: {node: '>=14'}
+
   js-levenshtein@1.1.6:
     resolution: {integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==}
     engines: {node: '>=0.10.0'}
@@ -20921,6 +20935,8 @@ snapshots:
       expect: 29.7.0
       pretty-format: 29.7.0
 
+  '@types/js-cookie@3.0.6': {}
+
   '@types/json-schema@7.0.15': {}
 
   '@types/json-schema@7.0.6': {}
@@ -26285,6 +26301,8 @@ snapshots:
 
   jpeg-js@0.4.4: {}
 
+  js-cookie@3.0.5: {}
+
   js-levenshtein@1.1.6: {}
 
   js-sha256@0.9.0: {}