DisplaySwitcher.tsx 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. import React, { useMemo } from 'react';
  2. import { pagePathUtils } from '@growi/core';
  3. import { useTranslation } from 'next-i18next';
  4. import dynamic from 'next/dynamic';
  5. // import { smoothScrollIntoView } from '~/client/util/smooth-scroll';
  6. import {
  7. useCurrentPagePath, useIsSharedUser, useIsEditable, useShareLinkId, useIsNotFound,
  8. } from '~/stores/context';
  9. import { useDescendantsPageListModal } from '~/stores/modal';
  10. import { useSWRxCurrentPage } from '~/stores/page';
  11. import { EditorMode, useEditorMode } from '~/stores/ui';
  12. import CountBadge from '../Common/CountBadge';
  13. import { ContentLinkButtonsProps } from '../ContentLinkButtons';
  14. import CustomTabContent from '../CustomNavigation/CustomTabContent';
  15. import PageListIcon from '../Icons/PageListIcon';
  16. import { Page } from '../Page';
  17. import TableOfContents from '../TableOfContents';
  18. import { UserInfoProps } from '../User/UserInfo';
  19. import styles from './DisplaySwitcher.module.scss';
  20. const { isTopPage, isUsersHomePage } = pagePathUtils;
  21. const PageEditor = dynamic(() => import('../PageEditor'), { ssr: false });
  22. const PageEditorByHackmd = dynamic(() => import('../PageEditorByHackmd').then(mod => mod.PageEditorByHackmd), { ssr: false });
  23. const EditorNavbarBottom = dynamic(() => import('../PageEditor/EditorNavbarBottom'), { ssr: false });
  24. const HashChanged = dynamic(() => import('../EventListeneres/HashChanged'), { ssr: false });
  25. const ContentLinkButtons = dynamic<ContentLinkButtonsProps>(() => import('../ContentLinkButtons').then(mod => mod.ContentLinkButtons), { ssr: false });
  26. const NotFoundPage = dynamic(() => import('../NotFoundPage'), { ssr: false });
  27. const UserInfo = dynamic<UserInfoProps>(() => import('../User/UserInfo').then(mod => mod.UserInfo), { ssr: false });
  28. const PageView = React.memo((): JSX.Element => {
  29. const { t } = useTranslation();
  30. const { data: currentPagePath } = useCurrentPagePath();
  31. const { data: isSharedUser } = useIsSharedUser();
  32. const { data: shareLinkId } = useShareLinkId();
  33. const { data: isNotFound } = useIsNotFound();
  34. const { data: currentPage } = useSWRxCurrentPage(shareLinkId ?? undefined);
  35. const { open: openDescendantPageListModal } = useDescendantsPageListModal();
  36. const isTopPagePath = isTopPage(currentPagePath ?? '');
  37. const isUsersHomePagePath = isUsersHomePage(currentPagePath ?? '');
  38. return (
  39. <div className="d-flex flex-column flex-lg-row">
  40. <div className="flex-grow-1 flex-basis-0 mw-0">
  41. { isUsersHomePagePath && <UserInfo author={currentPage?.creator} /> }
  42. { !isNotFound && <Page /> }
  43. { isNotFound && <NotFoundPage /> }
  44. </div>
  45. { !isNotFound && (
  46. <div className="grw-side-contents-container">
  47. <div className="grw-side-contents-sticky-container">
  48. {/* Page list */}
  49. <div className={`grw-page-accessories-control ${styles['grw-page-accessories-control']}`}>
  50. { currentPagePath != null && !isSharedUser && (
  51. <button
  52. type="button"
  53. className="btn btn-block btn-outline-secondary grw-btn-page-accessories rounded-pill d-flex justify-content-between align-items-center"
  54. onClick={() => openDescendantPageListModal(currentPagePath)}
  55. data-testid="pageListButton"
  56. >
  57. <div className="grw-page-accessories-control-icon">
  58. <PageListIcon />
  59. </div>
  60. {t('page_list')}
  61. <CountBadge count={currentPage?.descendantCount} offset={1} />
  62. </button>
  63. ) }
  64. </div>
  65. {/* Comments */}
  66. {/* { getCommentListDom != null && !isTopPagePath && ( */}
  67. { !isTopPagePath && (
  68. <div className={`mt-2 grw-page-accessories-control ${styles['grw-page-accessories-control']}`}>
  69. <button
  70. type="button"
  71. className="btn btn-block btn-outline-secondary grw-btn-page-accessories rounded-pill d-flex justify-content-between align-items-center"
  72. // onClick={() => smoothScrollIntoView(getCommentListDom, WIKI_HEADER_LINK)}
  73. >
  74. <i className="icon-fw icon-bubbles grw-page-accessories-control-icon"></i>
  75. <span>Comments</span>
  76. <CountBadge count={currentPage?.commentCount} />
  77. </button>
  78. </div>
  79. ) }
  80. <div className="d-none d-lg-block">
  81. <TableOfContents />
  82. { isUsersHomePagePath && <ContentLinkButtons author={currentPage?.creator} /> }
  83. </div>
  84. </div>
  85. </div>
  86. ) }
  87. </div>
  88. );
  89. });
  90. PageView.displayName = 'PageView';
  91. const DisplaySwitcher = React.memo((): JSX.Element => {
  92. // get element for smoothScroll
  93. // const getCommentListDom = useMemo(() => { return document.getElementById('page-comments-list') }, []);
  94. const { data: isEditable } = useIsEditable();
  95. const { data: editorMode = EditorMode.View } = useEditorMode();
  96. const isViewMode = editorMode === EditorMode.View;
  97. const navTabMapping = useMemo(() => {
  98. return {
  99. [EditorMode.View]: {
  100. Content: () => (
  101. <div data-testid="page-view" id="page-view">
  102. <PageView />
  103. </div>
  104. ),
  105. },
  106. [EditorMode.Editor]: {
  107. Content: () => (
  108. isEditable
  109. ? (
  110. <div data-testid="page-editor" id="page-editor">
  111. <PageEditor />
  112. </div>
  113. )
  114. : <></>
  115. ),
  116. },
  117. [EditorMode.HackMD]: {
  118. Content: () => (
  119. isEditable
  120. ? (
  121. <div id="page-editor-with-hackmd">
  122. <PageEditorByHackmd />
  123. </div>
  124. )
  125. : <></>
  126. ),
  127. },
  128. };
  129. }, [isEditable]);
  130. return (
  131. <>
  132. <CustomTabContent activeTab={editorMode} navTabMapping={navTabMapping} />
  133. { isEditable && !isViewMode && <EditorNavbarBottom /> }
  134. { isEditable && <HashChanged></HashChanged> }
  135. </>
  136. );
  137. });
  138. DisplaySwitcher.displayName = 'DisplaySwitcher';
  139. export default DisplaySwitcher;