DisplaySwitcher.tsx 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. import React, { useMemo } from 'react';
  2. import { type IPagePopulatedToShowRevision, pagePathUtils } from '@growi/core';
  3. import dynamic from 'next/dynamic';
  4. import { useHackmdDraftUpdatedEffect } from '~/client/services/side-effects/hackmd-draft-updated';
  5. import { useHashChangedEffect } from '~/client/services/side-effects/hash-changed';
  6. import { usePageUpdatedEffect } from '~/client/services/side-effects/page-updated';
  7. import type { RendererConfig } from '~/interfaces/services/renderer';
  8. import { useIsEditable } from '~/stores/context';
  9. import { EditorMode, useEditorMode } from '~/stores/ui';
  10. import { LazyRenderer } from '../Common/LazyRenderer';
  11. import { MainPane } from '../Layout/MainPane';
  12. import { PageAlerts } from '../PageAlert/PageAlerts';
  13. import { PageContentFooter } from '../PageContentFooter';
  14. import type { PageSideContentsProps } from '../PageSideContents';
  15. import { UserInfo } from '../User/UserInfo';
  16. import type { UsersHomePageFooterProps } from '../UsersHomePageFooter';
  17. import { Page2 } from './Page2';
  18. const { isUsersHomePage } = pagePathUtils;
  19. const NotCreatablePage = dynamic(() => import('../NotCreatablePage').then(mod => mod.NotCreatablePage), { ssr: false });
  20. const ForbiddenPage = dynamic(() => import('../ForbiddenPage'), { ssr: false });
  21. const NotFoundPage = dynamic(() => import('../NotFoundPage'), { ssr: false });
  22. const PageSideContents = dynamic<PageSideContentsProps>(() => import('../PageSideContents').then(mod => mod.PageSideContents), { ssr: false });
  23. const Comments = dynamic(() => import('../Comments').then(mod => mod.Comments), { ssr: false });
  24. const UsersHomePageFooter = dynamic<UsersHomePageFooterProps>(() => import('../UsersHomePageFooter')
  25. .then(mod => mod.UsersHomePageFooter), { ssr: false });
  26. const PageEditor = dynamic(() => import('../PageEditor'), { ssr: false });
  27. const PageEditorByHackmd = dynamic(() => import('../PageEditorByHackmd').then(mod => mod.PageEditorByHackmd), { ssr: false });
  28. const EditorNavbarBottom = dynamic(() => import('../PageEditor/EditorNavbarBottom'), { ssr: false });
  29. const IdenticalPathPage = (): JSX.Element => {
  30. const IdenticalPathPage = dynamic(() => import('../IdenticalPathPage').then(mod => mod.IdenticalPathPage), { ssr: false });
  31. return <IdenticalPathPage />;
  32. };
  33. type Props = {
  34. rendererConfig: RendererConfig,
  35. pagePath: string,
  36. page?: IPagePopulatedToShowRevision,
  37. isIdenticalPathPage?: boolean,
  38. isNotFound?: boolean,
  39. isForbidden?: boolean,
  40. isNotCreatable?: boolean,
  41. }
  42. const View = (props: Props): JSX.Element => {
  43. const {
  44. rendererConfig,
  45. pagePath, page,
  46. isIdenticalPathPage, isNotFound, isForbidden, isNotCreatable,
  47. } = props;
  48. const pageId = page?._id;
  49. const specialContents = useMemo(() => {
  50. if (isIdenticalPathPage) {
  51. return <IdenticalPathPage />;
  52. }
  53. if (isForbidden) {
  54. return <ForbiddenPage />;
  55. }
  56. if (isNotCreatable) {
  57. return <NotCreatablePage />;
  58. }
  59. if (isNotFound) {
  60. return <NotFoundPage />;
  61. }
  62. }, [isForbidden, isIdenticalPathPage, isNotCreatable, isNotFound]);
  63. const sideContents = !isNotFound && !isNotCreatable
  64. ? (
  65. <PageSideContents page={page} />
  66. )
  67. : <></>;
  68. const footerContents = !isIdenticalPathPage && !isNotFound && page != null
  69. ? (
  70. <>
  71. { pageId != null && pagePath != null && (
  72. <Comments pageId={pageId} pagePath={pagePath} revision={page.revision} />
  73. ) }
  74. { pagePath != null && isUsersHomePage(pagePath) && (
  75. <UsersHomePageFooter creatorId={page.creator._id}/>
  76. ) }
  77. <PageContentFooter page={page} />
  78. </>
  79. )
  80. : <></>;
  81. const isUsersHomePagePath = isUsersHomePage(pagePath);
  82. return (
  83. <MainPane
  84. sideContents={sideContents}
  85. footerContents={footerContents}
  86. >
  87. <PageAlerts />
  88. { specialContents }
  89. { specialContents == null && (
  90. <>
  91. { isUsersHomePagePath && <UserInfo author={page?.creator} /> }
  92. <Page2 rendererConfig={rendererConfig} pagePath={pagePath} markdownForSSR={page?.revision.body} />
  93. </>
  94. ) }
  95. </MainPane>
  96. );
  97. };
  98. export const DisplaySwitcher = (props: Props): JSX.Element => {
  99. const { data: editorMode = EditorMode.View } = useEditorMode();
  100. const { data: isEditable } = useIsEditable();
  101. usePageUpdatedEffect();
  102. useHashChangedEffect();
  103. useHackmdDraftUpdatedEffect();
  104. const isViewMode = editorMode === EditorMode.View;
  105. return (
  106. <>
  107. { isViewMode && <View {...props} /> }
  108. <LazyRenderer shouldRender={isEditable === true && editorMode === EditorMode.Editor}>
  109. <div data-testid="page-editor" id="page-editor" className="editor-root">
  110. <PageEditor />
  111. </div>
  112. </LazyRenderer>
  113. <LazyRenderer shouldRender={isEditable === true && editorMode === EditorMode.HackMD}>
  114. <div id="page-editor-with-hackmd" className="editor-root">
  115. <PageEditorByHackmd />
  116. </div>
  117. </LazyRenderer>
  118. { isEditable && !isViewMode && <EditorNavbarBottom /> }
  119. </>
  120. );
  121. };