DisplaySwitcher.tsx 4.8 KB

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