GrowiContextualSubNavigation.tsx 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. import React, { useCallback } from 'react';
  2. import PropTypes from 'prop-types';
  3. import { withUnstatedContainers } from '../UnstatedUtils';
  4. import EditorContainer from '~/client/services/EditorContainer';
  5. import {
  6. EditorMode, useDrawerMode, useEditorMode, useIsDeviceSmallerThanMd, useIsAbleToShowPageManagement, useIsAbleToShowTagLabel,
  7. useIsAbleToShowPageEditorModeManager, useIsAbleToShowPageAuthors,
  8. } from '~/stores/ui';
  9. import {
  10. useCurrentCreatedAt, useCurrentUpdatedAt, useCurrentPageId, useRevisionId, useCurrentPagePath,
  11. useCreator, useRevisionAuthor, useIsGuestUser, useIsSharedUser,
  12. } from '~/stores/context';
  13. import { useSWRTagsInfo } from '~/stores/page';
  14. import { SubNavButtons } from './SubNavButtons';
  15. import PageEditorModeManager from './PageEditorModeManager';
  16. import { toastSuccess, toastError } from '~/client/util/apiNotification';
  17. import { apiPost } from '~/client/util/apiv1-client';
  18. import { IPageHasId } from '~/interfaces/page';
  19. import { GrowiSubNavigation } from './GrowiSubNavigation';
  20. const GrowiContextualSubNavigation = (props) => {
  21. const { data: isDeviceSmallerThanMd } = useIsDeviceSmallerThanMd();
  22. const { data: isDrawerMode } = useDrawerMode();
  23. const { data: editorMode, mutate: mutateEditorMode } = useEditorMode();
  24. const { data: createdAt } = useCurrentCreatedAt();
  25. const { data: updatedAt } = useCurrentUpdatedAt();
  26. const { data: pageId } = useCurrentPageId();
  27. const { data: revisionId } = useRevisionId();
  28. const { data: path } = useCurrentPagePath();
  29. const { data: creator } = useCreator();
  30. const { data: revisionAuthor } = useRevisionAuthor();
  31. const { data: isGuestUser } = useIsGuestUser();
  32. const { data: isSharedUser } = useIsSharedUser();
  33. const { data: isAbleToShowPageManagement } = useIsAbleToShowPageManagement();
  34. const { data: isAbleToShowTagLabel } = useIsAbleToShowTagLabel();
  35. const { data: isAbleToShowPageEditorModeManager } = useIsAbleToShowPageEditorModeManager();
  36. const { data: isAbleToShowPageAuthors } = useIsAbleToShowPageAuthors();
  37. const { mutate: mutateSWRTagsInfo, data: tagsInfoData } = useSWRTagsInfo(pageId);
  38. const {
  39. editorContainer, isCompactMode,
  40. } = props;
  41. const isViewMode = editorMode === EditorMode.View;
  42. const tagsUpdatedHandler = useCallback(async(newTags: string[]) => {
  43. // It will not be reflected in the DB until the page is refreshed
  44. if (editorMode === EditorMode.Editor) {
  45. return editorContainer.setState({ tags: newTags });
  46. }
  47. try {
  48. const { tags } = await apiPost('/tags.update', { pageId, revisionId, tags: newTags }) as { tags };
  49. // revalidate SWRTagsInfo
  50. mutateSWRTagsInfo();
  51. // update editorContainer.state
  52. editorContainer.setState({ tags });
  53. toastSuccess('updated tags successfully');
  54. }
  55. catch (err) {
  56. toastError(err, 'fail to update tags');
  57. }
  58. // eslint-disable-next-line react-hooks/exhaustive-deps
  59. }, [pageId]);
  60. const ControlComponents = useCallback(() => {
  61. function onPageEditorModeButtonClicked(viewType) {
  62. mutateEditorMode(viewType);
  63. }
  64. return (
  65. <>
  66. <div className="h-50 d-flex flex-column align-items-end justify-content-center">
  67. { isViewMode && (
  68. <SubNavButtons
  69. isCompactMode={isCompactMode}
  70. pageId={pageId}
  71. disableSeenUserInfoPopover={isSharedUser}
  72. showPageControlDropdown={isAbleToShowPageManagement}
  73. />
  74. ) }
  75. </div>
  76. <div className="h-50 d-flex flex-column align-items-end justify-content-center">
  77. {isAbleToShowPageEditorModeManager && (
  78. <PageEditorModeManager
  79. onPageEditorModeButtonClicked={onPageEditorModeButtonClicked}
  80. isBtnDisabled={isGuestUser}
  81. editorMode={editorMode}
  82. isDeviceSmallerThanMd={isDeviceSmallerThanMd}
  83. />
  84. )}
  85. </div>
  86. </>
  87. );
  88. }, [
  89. pageId,
  90. editorMode, mutateEditorMode,
  91. isCompactMode, isDeviceSmallerThanMd, isGuestUser, isSharedUser,
  92. isViewMode, isAbleToShowPageEditorModeManager, isAbleToShowPageManagement,
  93. ]);
  94. if (path == null) {
  95. return <></>;
  96. }
  97. const currentPage: Partial<IPageHasId> = {
  98. _id: pageId ?? undefined,
  99. path,
  100. revision: revisionId ?? undefined,
  101. creator: creator ?? undefined,
  102. lastUpdateUser: revisionAuthor,
  103. createdAt: createdAt ?? undefined,
  104. updatedAt: updatedAt ?? undefined,
  105. };
  106. return (
  107. <GrowiSubNavigation
  108. page={currentPage}
  109. showDrawerToggler={isDrawerMode}
  110. showTagLabel={isAbleToShowTagLabel}
  111. showPageAuthors={isAbleToShowPageAuthors}
  112. tags={tagsInfoData?.tags || []}
  113. tagsUpdatedHandler={tagsUpdatedHandler}
  114. controls={ControlComponents}
  115. />
  116. );
  117. };
  118. /**
  119. * Wrapper component for using unstated
  120. */
  121. const GrowiContextualSubNavigationWrapper = withUnstatedContainers(GrowiContextualSubNavigation, [EditorContainer]);
  122. GrowiContextualSubNavigation.propTypes = {
  123. editorContainer: PropTypes.instanceOf(EditorContainer).isRequired,
  124. isCompactMode: PropTypes.bool,
  125. };
  126. export default GrowiContextualSubNavigationWrapper;