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

Merge branch 'master' into imprv/show-options-selector

Haku Mizuki 3 лет назад
Родитель
Сommit
4a3661bbe8

+ 2 - 2
packages/app/next.config.js

@@ -46,8 +46,8 @@ const setupTranspileModules = () => {
     ...listPrefixedPackages(['remark-', 'rehype-', 'hast-', 'mdast-', 'micromark-', 'unist-']),
   ];
 
-  logger.info('{bold:Listing scoped packages for transpiling:}');
-  logger.unprefixed('info', `{grey:${JSON.stringify(packages, null, 2)}}`);
+  // logger.info('{bold:Listing scoped packages for transpiling:}');
+  // logger.unprefixed('info', `{grey:${JSON.stringify(packages, null, 2)}}`);
 
   return require('next-transpile-modules')(packages);
 };

+ 5 - 2
packages/app/src/components/DescendantsPageList.tsx

@@ -10,7 +10,9 @@ import {
 } from '~/interfaces/page';
 import { IPagingResult } from '~/interfaces/paging-result';
 import { OnDeletedFunction, OnPutBackedFunction } from '~/interfaces/ui';
-import { useIsGuestUser, useIsSharedUser, useIsTrashPage } from '~/stores/context';
+import {
+  useIsGuestUser, useIsSharedUser, useIsTrashPage, useShowPageLimitationXL,
+} from '~/stores/context';
 import {
   usePageTreeTermManager, useDescendantsPageListForCurrentPathTermManager, useSWRxDescendantsPageListForCurrrentPath,
   useSWRxPageInfoForList, useSWRxPageList,
@@ -167,7 +169,8 @@ export const DescendantsPageListForCurrentPath = (): JSX.Element => {
   const [activePage, setActivePage] = useState(1);
 
   const { data: isTrashPage } = useIsTrashPage();
-  const { data: pagingResult, error, mutate } = useSWRxDescendantsPageListForCurrrentPath(activePage);
+  const { data: limit } = useShowPageLimitationXL();
+  const { data: pagingResult, error, mutate } = useSWRxDescendantsPageListForCurrrentPath(activePage, limit);
 
   if (error != null) {
     return (

+ 21 - 15
packages/app/src/components/Layout/BasicLayout.tsx

@@ -1,6 +1,8 @@
 import React, { ReactNode } from 'react';
 
 import dynamic from 'next/dynamic';
+import { DndProvider } from 'react-dnd';
+import { HTML5Backend } from 'react-dnd-html5-backend';
 
 import { GrowiNavbar } from '../Navbar/GrowiNavbar';
 import Sidebar from '../Sidebar';
@@ -39,27 +41,31 @@ export const BasicLayout = ({
 
   return (
     <RawLayout title={title} className={myClassName}>
-      <GrowiNavbar />
 
-      <div className="page-wrapper d-flex d-print-block">
-        <div className="grw-sidebar-wrapper">
-          <Sidebar />
-        </div>
+      <DndProvider backend={HTML5Backend}>
+        <GrowiNavbar />
+
+        <div className="page-wrapper d-flex d-print-block">
+          <div className="grw-sidebar-wrapper">
+            <Sidebar />
+          </div>
 
-        <div className="flex-fill mw-0" style={{ position: 'relative' }}>
-          {children}
+          <div className="flex-fill mw-0" style={{ position: 'relative' }}>
+            {children}
+          </div>
         </div>
-      </div>
 
-      <GrowiNavbarBottom />
+        <GrowiNavbarBottom />
+
+        <PageCreateModal />
+        <PageDuplicateModal />
+        <PageDeleteModal />
+        <PageRenameModal />
+        <PageAccessoriesModal />
+        <DrawioModal />
+      </DndProvider>
 
-      <PageCreateModal />
-      <PageDuplicateModal />
-      <PageDeleteModal />
-      <PageRenameModal />
       <PagePresentationModal />
-      <PageAccessoriesModal />
-      <DrawioModal />
       <HotkeysManager />
 
       <Fab />

+ 10 - 7
packages/app/src/components/Layout/RawLayout.tsx

@@ -4,10 +4,11 @@ import Head from 'next/head';
 import { useIsomorphicLayoutEffect } from 'usehooks-ts';
 
 import { useGrowiTheme } from '~/stores/context';
-import { ColorScheme, useNextThemes } from '~/stores/use-next-themes';
+import { ColorScheme, useNextThemes, NextThemesProvider } from '~/stores/use-next-themes';
 import loggerFactory from '~/utils/logger';
 
-import { ThemeProvider } from '../Theme/utils/ThemeProvider';
+
+import { ThemeProvider as GrowiThemeProvider } from '../Theme/utils/ThemeProvider';
 
 
 const logger = loggerFactory('growi:cli:RawLayout');
@@ -43,11 +44,13 @@ export const RawLayout = ({ children, title, className }: Props): JSX.Element =>
         <meta charSet="utf-8" />
         <meta name="viewport" content="initial-scale=1.0, width=device-width" />
       </Head>
-      <ThemeProvider theme={growiTheme} colorScheme={colorScheme}>
-        <div className={classNames.join(' ')} data-color-scheme={colorScheme}>
-          {children}
-        </div>
-      </ThemeProvider>
+      <NextThemesProvider>
+        <GrowiThemeProvider theme={growiTheme} colorScheme={colorScheme}>
+          <div className={classNames.join(' ')} data-color-scheme={colorScheme}>
+            {children}
+          </div>
+        </GrowiThemeProvider>
+      </NextThemesProvider>
     </>
   );
 };

+ 3 - 3
packages/app/src/components/SearchPage.tsx

@@ -9,7 +9,7 @@ import { useRouter } from 'next/router';
 
 import { ISelectableAll, ISelectableAndIndeterminatable } from '~/client/interfaces/selectable-all';
 import { IFormattedSearchResult } from '~/interfaces/search';
-import { useIsSearchServiceReachable } from '~/stores/context';
+import { useIsSearchServiceReachable, useShowPageLimitationL } from '~/stores/context';
 import { ISearchConditions, ISearchConfigurations, useSWRxSearch } from '~/stores/search';
 
 import PaginationWrapper from './PaginationWrapper';
@@ -89,6 +89,7 @@ SearchResultListHead.displayName = 'SearchResultListHead';
 
 export const SearchPage = (): JSX.Element => {
   const { t } = useTranslation();
+  const { data: showPageLimitationL } = useShowPageLimitationL();
   const router = useRouter();
 
   // parse URL Query
@@ -97,9 +98,8 @@ export const SearchPage = (): JSX.Element => {
 
   const [keyword, setKeyword] = useState<string>(initQ);
   const [offset, setOffset] = useState<number>(0);
-  const [limit, setLimit] = useState<number>(INITIAL_PAGIONG_SIZE);
+  const [limit, setLimit] = useState<number>(showPageLimitationL ?? INITIAL_PAGIONG_SIZE);
   const [configurationsByControl, setConfigurationsByControl] = useState<Partial<ISearchConfigurations>>({});
-
   const selectAllControlRef = useRef<ISelectableAndIndeterminatable|null>(null);
   const searchPageBaseRef = useRef<ISelectableAll & IReturnSelectedPageIds|null>(null);
 

+ 0 - 0
packages/app/src/pages/UnsavedAlertDialog.tsx → packages/app/src/components/UnsavedAlertDialog.tsx


+ 18 - 13
packages/app/src/pages/[[...path]].page.tsx

@@ -4,10 +4,12 @@ import React, { useEffect } from 'react';
 import EventEmitter from 'events';
 
 import {
-  IDataWithMeta, IPageInfoForEntity, IPagePopulatedToShowRevision, isClient, isIPageInfoForEntity, IUser, IUserHasId, pagePathUtils, pathUtils,
+  isClient, isIPageInfoForEntity, pagePathUtils, pathUtils,
+} from '@growi/core';
+import type {
+  IDataWithMeta, IPageInfoForEntity, IPagePopulatedToShowRevision, IUser, IUserHasId,
 } from '@growi/core';
 import ExtensibleCustomError from 'extensible-custom-error';
-import { model as mongooseModel } from 'mongoose';
 import {
   NextPage, GetServerSideProps, GetServerSidePropsContext,
 } from 'next';
@@ -22,19 +24,18 @@ import { PageAlerts } from '~/components/PageAlert/PageAlerts';
 // import { useTranslation } from '~/i18n';
 import { CurrentPageContentFooter } from '~/components/PageContentFooter';
 import { UsersHomePageFooterProps } from '~/components/UsersHomePageFooter';
-import { CrowiRequest } from '~/interfaces/crowi-request';
+import type { CrowiRequest } from '~/interfaces/crowi-request';
 // import { renderScriptTagByName, renderHighlightJsStyleTag } from '~/service/cdn-resources-loader';
 // import { useRendererSettings } from '~/stores/renderer';
 // import { EditorMode, useEditorMode, useIsMobile } from '~/stores/ui';
-import { EditorConfig } from '~/interfaces/editor-settings';
-import { CustomWindow } from '~/interfaces/global';
-import { RendererConfig } from '~/interfaces/services/renderer';
-import { ISidebarConfig } from '~/interfaces/sidebar-config';
-import { IUserUISettings } from '~/interfaces/user-ui-settings';
-import { PageModel, PageDocument } from '~/server/models/page';
-import { PageRedirectModel } from '~/server/models/page-redirect';
-import { UserUISettingsModel } from '~/server/models/user-ui-settings';
-import { useSWRxLayoutSetting } from '~/stores/admin/customize';
+import type { EditorConfig } from '~/interfaces/editor-settings';
+import type { CustomWindow } from '~/interfaces/global';
+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 { useSWRxCurrentPage, useSWRxIsGrantNormalized, useSWRxPageInfo } from '~/stores/page';
 import { useRedirectFrom } from '~/stores/page-redirect';
 import {
@@ -74,7 +75,7 @@ import { calcIsContainerFluid } from './utils/layout';
 
 const NotCreatablePage = dynamic(() => import('../components/NotCreatablePage').then(mod => mod.NotCreatablePage), { ssr: false });
 const ForbiddenPage = dynamic(() => import('../components/ForbiddenPage'), { ssr: false });
-const UnsavedAlertDialog = dynamic(() => import('./UnsavedAlertDialog'), { ssr: false });
+const UnsavedAlertDialog = dynamic(() => import('../components/UnsavedAlertDialog'), { ssr: false });
 const GrowiSubNavigationSwitcher = dynamic(() => import('../components/Navbar/GrowiSubNavigationSwitcher'), { ssr: false });
 const UsersHomePageFooter = dynamic<UsersHomePageFooterProps>(() => import('../components/UsersHomePageFooter')
   .then(mod => mod.UsersHomePageFooter), { ssr: false });
@@ -373,6 +374,8 @@ class MultiplePagesHitsError extends ExtensibleCustomError {
 }
 
 async function injectPageData(context: GetServerSidePropsContext, props: Props): Promise<void> {
+  const { model: mongooseModel } = await import('mongoose');
+
   const req: CrowiRequest = context.req as CrowiRequest;
   const { crowi } = req;
   const { revisionId } = req.query;
@@ -425,6 +428,8 @@ async function injectPageData(context: GetServerSidePropsContext, 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;

+ 1 - 8
packages/app/src/pages/_app.page.tsx

@@ -3,8 +3,6 @@ import React, { useEffect } from 'react';
 import { isServer } from '@growi/core';
 import { appWithTranslation } from 'next-i18next';
 import { AppProps } from 'next/app';
-import { DndProvider } from 'react-dnd';
-import { HTML5Backend } from 'react-dnd-html5-backend';
 import { SWRConfig } from 'swr';
 
 import * as nextI18nConfig from '^/config/next-i18next.config';
@@ -13,7 +11,6 @@ import { useI18nextHMR } from '~/services/i18next-hmr';
 import {
   useAppTitle, useConfidential, useGrowiTheme, useGrowiVersion, useSiteUrl,
 } from '~/stores/context';
-import { NextThemesProvider } from '~/stores/use-next-themes';
 import { SWRConfigValue, swrGlobalConfiguration } from '~/utils/swr-utils';
 
 
@@ -59,11 +56,7 @@ function GrowiApp({ Component, pageProps }: GrowiAppProps): JSX.Element {
 
   return (
     <SWRConfig value={swrConfig}>
-      <NextThemesProvider>
-        <DndProvider backend={HTML5Backend}>
-          <Component {...pageProps} />
-        </DndProvider>
-      </NextThemesProvider>
+      <Component {...pageProps} />
     </SWRConfig>
   );
 }

+ 9 - 7
packages/app/src/pages/_private-legacy-pages.page.tsx

@@ -5,13 +5,12 @@ import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
 import dynamic from 'next/dynamic';
 import Head from 'next/head';
 
-import { BasicLayout } from '~/components/Layout/BasicLayout';
-import { CrowiRequest } from '~/interfaces/crowi-request';
-import { RendererConfig } from '~/interfaces/services/renderer';
-import { ISidebarConfig } from '~/interfaces/sidebar-config';
-import { IUser, IUserHasId } from '~/interfaces/user';
-import { IUserUISettings } from '~/interfaces/user-ui-settings';
-import UserUISettings from '~/server/models/user-ui-settings';
+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, useIsSearchPage, useIsSearchScopeChildrenAsDefault,
   useIsSearchServiceConfigured, useIsSearchServiceReachable, useRendererConfig,
@@ -89,9 +88,12 @@ 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();

+ 17 - 9
packages/app/src/pages/_search.page.tsx

@@ -5,17 +5,15 @@ import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
 import dynamic from 'next/dynamic';
 import Head from 'next/head';
 
-import { BasicLayout } from '~/components/Layout/BasicLayout';
-import { CrowiRequest } from '~/interfaces/crowi-request';
-import { RendererConfig } from '~/interfaces/services/renderer';
-import { ISidebarConfig } from '~/interfaces/sidebar-config';
-import { IUser, IUserHasId } from '~/interfaces/user';
-import { IUserUISettings } from '~/interfaces/user-ui-settings';
-import UserUISettings from '~/server/models/user-ui-settings';
-import Xss from '~/services/xss';
+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, useIsSearchPage, useIsSearchScopeChildrenAsDefault,
-  useIsSearchServiceConfigured, useIsSearchServiceReachable, useRendererConfig,
+  useIsSearchServiceConfigured, useIsSearchServiceReachable, useRendererConfig, useShowPageLimitationL,
 } from '~/stores/context';
 import {
   usePreferDrawerModeByUser, usePreferDrawerModeOnEditByUser, useSidebarCollapsed,
@@ -45,6 +43,9 @@ type Props = CommonProps & {
   // Render config
   rendererConfig: RendererConfig,
 
+  // search limit
+  showPageLimitationL: number
+
 };
 
 const SearchResultPage: NextPage<Props> = (props: Props) => {
@@ -71,6 +72,8 @@ const SearchResultPage: NextPage<Props> = (props: Props) => {
   // render config
   useRendererConfig(props.rendererConfig);
 
+  useShowPageLimitationL(props.showPageLimitationL);
+
   const PutbackPageModal = (): JSX.Element => {
     const PutbackPageModal = dynamic(() => import('../components/PutbackPageModal'), { ssr: false });
     return <PutbackPageModal />;
@@ -102,9 +105,12 @@ const SearchResultPage: 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();
@@ -140,6 +146,8 @@ function injectServerConfigurations(context: GetServerSidePropsContext, props: P
     tagWhiteList: crowi.xssService.getTagWhiteList(),
     highlightJsStyleBorder: crowi.configManager.getConfig('crowi', 'customize:highlightJsStyleBorder'),
   };
+
+  props.showPageLimitationL = configManager.getConfig('crowi', 'customize:showPageLimitationL');
 }
 
 /**

+ 2 - 2
packages/app/src/pages/invited.page.tsx

@@ -1,13 +1,13 @@
 import React from 'react';
 
-import { IUserHasId, IUser } from '@growi/core';
+import type { IUserHasId, IUser } from '@growi/core';
 import { NextPage, GetServerSideProps, GetServerSidePropsContext } from 'next';
 import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
 import dynamic from 'next/dynamic';
 
 import { InvitedFormProps } from '~/components/InvitedForm';
 import { NoLoginLayout } from '~/components/Layout/NoLoginLayout';
-import { CrowiRequest } from '~/interfaces/crowi-request';
+import type { CrowiRequest } from '~/interfaces/crowi-request';
 
 import { useCsrfToken, useCurrentPathname, useCurrentUser } from '../stores/context';
 

+ 1 - 1
packages/app/src/pages/login.page.tsx

@@ -8,7 +8,7 @@ import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
 
 import { NoLoginLayout } from '~/components/Layout/NoLoginLayout';
 import { LoginForm } from '~/components/LoginForm';
-import { CrowiRequest } from '~/interfaces/crowi-request';
+import type { CrowiRequest } from '~/interfaces/crowi-request';
 
 import {
   useCsrfToken,

+ 2 - 4
packages/app/src/pages/maintenance.page.tsx

@@ -1,13 +1,11 @@
-import {
-  IUser, IUserHasId,
-} from '@growi/core';
+import type { IUser, IUserHasId } from '@growi/core';
 import { NextPage, GetServerSideProps, GetServerSidePropsContext } from 'next';
 import { useTranslation } from 'next-i18next';
 import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
 
 import { toastError } from '~/client/util/apiNotification';
 import { apiv3Post } from '~/client/util/apiv3-client';
-import { CrowiRequest } from '~/interfaces/crowi-request';
+import type { CrowiRequest } from '~/interfaces/crowi-request';
 import { useCurrentUser } from '~/stores/context';
 
 import {

+ 9 - 7
packages/app/src/pages/tags.page.tsx

@@ -1,17 +1,15 @@
 import React, { useState, useCallback } from 'react';
 
-import {
-  IUser, IUserHasId,
-} from '@growi/core';
+import type { IUser, IUserHasId } from '@growi/core';
 import { NextPage, GetServerSideProps, GetServerSidePropsContext } from 'next';
 import { useTranslation } from 'next-i18next';
 import dynamic from 'next/dynamic';
 import Head from 'next/head';
 
-import { CrowiRequest } from '~/interfaces/crowi-request';
-import { IDataTagCount } from '~/interfaces/tag';
-import { IUserUISettings } from '~/interfaces/user-ui-settings';
-import UserUISettings from '~/server/models/user-ui-settings';
+import type { CrowiRequest } from '~/interfaces/crowi-request';
+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 { BasicLayout } from '../components/Layout/BasicLayout';
@@ -96,8 +94,12 @@ const TagPage: NextPage<CommonProps> = (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) {

+ 14 - 8
packages/app/src/pages/trash.page.tsx

@@ -1,21 +1,19 @@
 import React from 'react';
 
-import {
-  IUser, IUserHasId,
-} from '@growi/core';
+import type { IUser, IUserHasId } from '@growi/core';
 import { NextPage, GetServerSideProps, GetServerSidePropsContext } from 'next';
 import dynamic from 'next/dynamic';
 
-import { CrowiRequest } from '~/interfaces/crowi-request';
-import { IUserUISettings } from '~/interfaces/user-ui-settings';
-import UserUISettings from '~/server/models/user-ui-settings';
+import type { CrowiRequest } from '~/interfaces/crowi-request';
+import type { IUserUISettings } from '~/interfaces/user-ui-settings';
+import type { UserUISettingsModel } from '~/server/models/user-ui-settings';
 
 import { BasicLayout } from '../components/Layout/BasicLayout';
 import GrowiContextualSubNavigation from '../components/Navbar/GrowiContextualSubNavigation';
 import {
   useCurrentUser, useCurrentPageId, useCurrentPagePath, useCurrentPathname,
   useIsSearchServiceConfigured, useIsSearchServiceReachable,
-  useIsSearchScopeChildrenAsDefault, useIsSearchPage,
+  useIsSearchScopeChildrenAsDefault, useIsSearchPage, useShowPageLimitationXL,
 } from '../stores/context';
 
 import {
@@ -31,7 +29,8 @@ type Props = CommonProps & {
   isSearchServiceConfigured: boolean,
   isSearchServiceReachable: boolean,
   isSearchScopeChildrenAsDefault: boolean,
-  userUISettings?: IUserUISettings
+  userUISettings?: IUserUISettings,
+  showPageLimitationXL: number,
 };
 
 const TrashPage: NextPage<CommonProps> = (props: Props) => {
@@ -46,6 +45,8 @@ const TrashPage: NextPage<CommonProps> = (props: Props) => {
   useCurrentPathname('/trash');
   useCurrentPagePath('/trash');
 
+  useShowPageLimitationXL(props.showPageLimitationXL);
+
   return (
     <>
       <BasicLayout title={useCustomTitle(props, 'GROWI')} >
@@ -67,8 +68,12 @@ const TrashPage: NextPage<CommonProps> = (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) {
@@ -86,6 +91,7 @@ function injectServerConfigurations(context: GetServerSidePropsContext, props: P
   props.isSearchServiceConfigured = searchService.isConfigured;
   props.isSearchServiceReachable = searchService.isReachable;
   props.isSearchScopeChildrenAsDefault = configManager.getConfig('crowi', 'customize:isSearchScopeChildrenAsDefault');
+  props.showPageLimitationXL = crowi.configManager.getConfig('crowi', 'customize:showPageLimitationXL');
 }
 
 export const getServerSideProps: GetServerSideProps = async(context: GetServerSidePropsContext) => {

+ 6 - 5
packages/app/src/server/crowi/index.js

@@ -418,20 +418,21 @@ Crowi.prototype.getTokens = function() {
 
 Crowi.prototype.start = async function() {
   const dev = process.env.NODE_ENV !== 'production';
-  this.nextApp = next({ dev });
 
+  await this.init();
+  await this.buildServer();
+
+  // setup Next.js
+  this.nextApp = next({ dev });
   await this.nextApp.prepare();
 
-  // init CrowiDev
+  // setup CrowiDev
   if (dev) {
     const CrowiDev = require('./dev');
     this.crowiDev = new CrowiDev(this);
     this.crowiDev.init();
   }
 
-  await this.init();
-  await this.buildServer();
-
   const { express, configManager } = this;
 
   const app = (this.node_env === 'development') ? this.crowiDev.setupServer(express) : express;

+ 4 - 0
packages/app/src/stores/context.tsx

@@ -255,6 +255,10 @@ export const useIsUploadableFile = (initialData?: boolean): SWRResponse<boolean,
   return useStaticSWR('isUploadableFile', initialData);
 };
 
+export const useShowPageLimitationL = (initialData?: number): SWRResponse<number, Error> => {
+  return useStaticSWR('showPageLimitationL', initialData);
+};
+
 export const useShowPageLimitationXL = (initialData?: number): SWRResponse<number, Error> => {
   return useStaticSWR('showPageLimitationXL', initialData);
 };

+ 17 - 7
packages/app/src/stores/page-listing.tsx

@@ -44,11 +44,21 @@ export const useSWRInifinitexRecentlyUpdated = () : SWRInfiniteResponse<(IPageHa
   );
 };
 // eslint-disable-next-line @typescript-eslint/no-unused-vars
-export const useSWRxPageList = (path: string | null, pageNumber?: number, termNumber?: number): SWRResponse<IPagingResult<IPageHasId>, Error> => {
-
-  const key = path != null
-    ? [`/pages/list?path=${path}&page=${pageNumber ?? 1}`, termNumber]
-    : null;
+export const useSWRxPageList = (
+    path: string | null, pageNumber?: number, termNumber?: number, limit?: number,
+): SWRResponse<IPagingResult<IPageHasId>, Error> => {
+
+  let key;
+  // if path not exist then the key is null
+  if (path == null) {
+    key = null;
+  }
+  else {
+    const pageListPath = `/pages/list?path=${path}&page=${pageNumber ?? 1}`;
+    // if limit exist then add it as query string
+    const requestPath = limit == null ? pageListPath : `${pageListPath}&limit=${limit}`;
+    key = [requestPath, termNumber];
+  }
 
   return useSWR(
     key,
@@ -66,7 +76,7 @@ export const useDescendantsPageListForCurrentPathTermManager = (isDisabled?: boo
   return useTermNumberManager(isDisabled === true ? null : 'descendantsPageListForCurrentPathTermNumber');
 };
 
-export const useSWRxDescendantsPageListForCurrrentPath = (pageNumber?: number): SWRResponse<IPagingResult<IPageHasId>, Error> => {
+export const useSWRxDescendantsPageListForCurrrentPath = (pageNumber?: number, limit?:number): SWRResponse<IPagingResult<IPageHasId>, Error> => {
   const { data: currentPagePath } = useCurrentPagePath();
   const { data: termNumber } = useDescendantsPageListForCurrentPathTermManager();
 
@@ -74,7 +84,7 @@ export const useSWRxDescendantsPageListForCurrrentPath = (pageNumber?: number):
     ? null
     : currentPagePath;
 
-  return useSWRxPageList(path, pageNumber, termNumber);
+  return useSWRxPageList(path, pageNumber, termNumber, limit);
 };