PageContents.tsx 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. import React, { useEffect } from 'react';
  2. import { pagePathUtils } from '@growi/core';
  3. import { useTranslation } from 'next-i18next';
  4. import { HtmlElementNode } from 'rehype-toc';
  5. import { useDrawioModalLauncherForView } from '~/client/services/side-effects/drawio-modal-launcher-for-view';
  6. import { useHandsontableModalLauncherForView } from '~/client/services/side-effects/handsontable-modal-launcher-for-view';
  7. import { toastSuccess, toastError } from '~/client/util/toastr';
  8. import {
  9. useIsGuestUser, useCurrentPathname,
  10. } from '~/stores/context';
  11. import { useEditingMarkdown } from '~/stores/editor';
  12. import { useSWRxCurrentPage } from '~/stores/page';
  13. import { useViewOptions } from '~/stores/renderer';
  14. import {
  15. useCurrentPageTocNode,
  16. useIsMobile,
  17. } from '~/stores/ui';
  18. import { registerGrowiFacade } from '~/utils/growi-facade';
  19. import loggerFactory from '~/utils/logger';
  20. import RevisionRenderer from './RevisionRenderer';
  21. import styles from './PageContents.module.scss';
  22. const logger = loggerFactory('growi:Page');
  23. export const PageContents = (): JSX.Element => {
  24. const { t } = useTranslation();
  25. const { data: currentPathname } = useCurrentPathname();
  26. const isSharedPage = pagePathUtils.isSharedPage(currentPathname ?? '');
  27. const { data: currentPage, mutate: mutateCurrentPage } = useSWRxCurrentPage();
  28. const { mutate: mutateEditingMarkdown } = useEditingMarkdown();
  29. const { data: isGuestUser } = useIsGuestUser();
  30. const { data: isMobile } = useIsMobile();
  31. const { mutate: mutateCurrentPageTocNode } = useCurrentPageTocNode();
  32. const { data: rendererOptions, mutate: mutateRendererOptions } = useViewOptions((toc: HtmlElementNode) => {
  33. mutateCurrentPageTocNode(toc);
  34. });
  35. // register to facade
  36. useEffect(() => {
  37. registerGrowiFacade({
  38. markdownRenderer: {
  39. optionsMutators: {
  40. viewOptionsMutator: mutateRendererOptions,
  41. },
  42. },
  43. });
  44. }, [mutateRendererOptions]);
  45. useHandsontableModalLauncherForView({
  46. onSaveSuccess: (newMarkdown) => {
  47. toastSuccess(t('toaster.save_succeeded'));
  48. // rerender
  49. if (!isSharedPage) {
  50. mutateCurrentPage();
  51. }
  52. mutateEditingMarkdown(newMarkdown);
  53. },
  54. onSaveError: (error) => {
  55. toastError(error);
  56. },
  57. });
  58. useDrawioModalLauncherForView({
  59. onSaveSuccess: (newMarkdown) => {
  60. toastSuccess(t('toaster.save_succeeded'));
  61. // rerender
  62. if (!isSharedPage) {
  63. mutateCurrentPage();
  64. }
  65. mutateEditingMarkdown(newMarkdown);
  66. },
  67. onSaveError: (error) => {
  68. toastError(error);
  69. },
  70. });
  71. if (currentPage == null || rendererOptions == null) {
  72. const entries = Object.entries({
  73. currentPage, isGuestUser, rendererOptions,
  74. })
  75. .map(([key, value]) => [key, value == null ? 'null' : undefined])
  76. .filter(([, value]) => value != null);
  77. logger.warn('Some of materials are missing.', Object.fromEntries(entries));
  78. return <></>;
  79. }
  80. const { _id: revisionId, body: markdown } = currentPage.revision;
  81. return (
  82. <div className={`mb-5 ${isMobile ? `page-mobile ${styles['page-mobile']}` : ''}`}>
  83. { revisionId != null && (
  84. <RevisionRenderer rendererOptions={rendererOptions} markdown={markdown} />
  85. )}
  86. </div>
  87. );
  88. };