trash.page.tsx 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. import type { ReactNode } from 'react';
  2. import React from 'react';
  3. import type { IUser, IUserHasId } from '@growi/core';
  4. import type { GetServerSideProps, GetServerSidePropsContext } from 'next';
  5. import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
  6. import dynamic from 'next/dynamic';
  7. import Head from 'next/head';
  8. import { useIsomorphicLayoutEffect } from 'usehooks-ts';
  9. import { PagePathNavSticky } from '~/components/Common/PagePathNav';
  10. import type { CrowiRequest } from '~/interfaces/crowi-request';
  11. import type { RendererConfig } from '~/interfaces/services/renderer';
  12. import { useCurrentPageId, useSWRxCurrentPage } from '~/stores/page';
  13. import { BasicLayout } from '../components/Layout/BasicLayout';
  14. import {
  15. useCurrentUser, useCurrentPathname, useGrowiCloudUri,
  16. useIsSearchServiceConfigured, useIsSearchServiceReachable,
  17. useIsSearchScopeChildrenAsDefault, useIsSearchPage, useShowPageLimitationXL,
  18. } from '../stores/context';
  19. import type { NextPageWithLayout } from './_app.page';
  20. import type { CommonProps } from './utils/commons';
  21. import {
  22. getServerSideCommonProps, getNextI18NextConfig, generateCustomTitleForPage, useInitSidebarConfig,
  23. } from './utils/commons';
  24. const TrashPageList = dynamic(() => import('~/components/TrashPageList').then(mod => mod.TrashPageList), { ssr: false });
  25. const EmptyTrashModal = dynamic(() => import('~/components/EmptyTrashModal'), { ssr: false });
  26. type Props = CommonProps & {
  27. currentUser: IUser,
  28. isSearchServiceConfigured: boolean,
  29. isSearchServiceReachable: boolean,
  30. isSearchScopeChildrenAsDefault: boolean,
  31. showPageLimitationXL: number,
  32. rendererConfig: RendererConfig,
  33. };
  34. const TrashPage: NextPageWithLayout<CommonProps> = (props: Props) => {
  35. useCurrentUser(props.currentUser ?? null);
  36. // clear the cache for the current page
  37. // in order to fix https://redmine.weseek.co.jp/issues/135811
  38. useSWRxCurrentPage(null);
  39. useCurrentPageId(null);
  40. useCurrentPathname('/trash');
  41. useGrowiCloudUri(props.growiCloudUri);
  42. useIsSearchServiceConfigured(props.isSearchServiceConfigured);
  43. useIsSearchServiceReachable(props.isSearchServiceReachable);
  44. useIsSearchScopeChildrenAsDefault(props.isSearchScopeChildrenAsDefault);
  45. useIsSearchPage(false);
  46. // init sidebar config with UserUISettings and sidebarConfig
  47. useInitSidebarConfig(props.sidebarConfig, props.userUISettings);
  48. useShowPageLimitationXL(props.showPageLimitationXL);
  49. const title = generateCustomTitleForPage(props, '/trash');
  50. return (
  51. <>
  52. <Head>
  53. <title>{title}</title>
  54. </Head>
  55. <div className="dynamic-layout-root">
  56. <div className="content-main container-lg mt-5 ms-md-5 ms-xl-0">
  57. <PagePathNavSticky pagePath="/trash" />
  58. <TrashPageList />
  59. </div>
  60. </div>
  61. </>
  62. );
  63. };
  64. type LayoutProps = Props & {
  65. children?: ReactNode,
  66. }
  67. const Layout = ({ children, ...props }: LayoutProps): JSX.Element => {
  68. // init sidebar config with UserUISettings and sidebarConfig
  69. useInitSidebarConfig(props.sidebarConfig, props.userUISettings);
  70. return <BasicLayout>{children}</BasicLayout>;
  71. };
  72. TrashPage.getLayout = function getLayout(page) {
  73. return (
  74. <>
  75. <Layout {...page.props}>
  76. {page}
  77. </Layout>
  78. <EmptyTrashModal />
  79. </>
  80. );
  81. };
  82. function injectServerConfigurations(context: GetServerSidePropsContext, props: Props): void {
  83. const req: CrowiRequest = context.req as CrowiRequest;
  84. const { crowi } = req;
  85. const {
  86. searchService, configManager,
  87. } = crowi;
  88. props.isSearchServiceConfigured = searchService.isConfigured;
  89. props.isSearchServiceReachable = searchService.isReachable;
  90. props.isSearchScopeChildrenAsDefault = configManager.getConfig('crowi', 'customize:isSearchScopeChildrenAsDefault');
  91. props.showPageLimitationXL = crowi.configManager.getConfig('crowi', 'customize:showPageLimitationXL');
  92. props.sidebarConfig = {
  93. isSidebarCollapsedMode: configManager.getConfig('crowi', 'customize:isSidebarCollapsedMode'),
  94. };
  95. }
  96. /**
  97. * for Server Side Translations
  98. * @param context
  99. * @param props
  100. * @param namespacesRequired
  101. */
  102. async function injectNextI18NextConfigurations(context: GetServerSidePropsContext, props: Props, namespacesRequired?: string[] | undefined): Promise<void> {
  103. const nextI18NextConfig = await getNextI18NextConfig(serverSideTranslations, context, namespacesRequired);
  104. props._nextI18Next = nextI18NextConfig._nextI18Next;
  105. }
  106. export const getServerSideProps: GetServerSideProps = async(context: GetServerSidePropsContext) => {
  107. const req = context.req as CrowiRequest;
  108. const { user } = req;
  109. const result = await getServerSideCommonProps(context);
  110. if (!('props' in result)) {
  111. throw new Error('invalid getSSP result');
  112. }
  113. const props: Props = result.props as Props;
  114. if (user != null) {
  115. props.currentUser = user.toObject();
  116. }
  117. injectServerConfigurations(context, props);
  118. await injectNextI18NextConfigurations(context, props, ['translation']);
  119. return {
  120. props,
  121. };
  122. };
  123. export default TrashPage;