RevisionLoader.tsx 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. import React, { useState, useEffect } from 'react';
  2. import type { Ref, IRevision, IRevisionHasId } from '@growi/core';
  3. import { useTranslation } from 'next-i18next';
  4. import type { RendererOptions } from '~/interfaces/renderer-options';
  5. import { useSWRxPageRevision } from '~/stores/page';
  6. import loggerFactory from '~/utils/logger';
  7. import RevisionRenderer from './RevisionRenderer';
  8. export const ROOT_ELEM_ID = 'revision-loader' as const;
  9. export type RevisionLoaderProps = {
  10. rendererOptions: RendererOptions,
  11. pageId: string,
  12. revisionId: Ref<IRevision>,
  13. onRevisionLoaded?: (revision: IRevisionHasId) => void,
  14. }
  15. const logger = loggerFactory('growi:Page:RevisionLoader');
  16. // Always render '#revision-loader' for MutationObserver of SearchResultContent
  17. const RevisionLoaderRoot = (props: React.HTMLAttributes<HTMLDivElement>): JSX.Element => (
  18. <div id={ROOT_ELEM_ID} {...props}>{props.children}</div>
  19. );
  20. /**
  21. * Load data from server and render RevisionBody component
  22. */
  23. export const RevisionLoader = (props: RevisionLoaderProps): JSX.Element => {
  24. const { t } = useTranslation();
  25. const {
  26. rendererOptions, pageId, revisionId, onRevisionLoaded,
  27. } = props;
  28. const { data: pageRevision, isLoading, error } = useSWRxPageRevision(pageId, revisionId);
  29. const [markdown, setMarkdown] = useState<string>('');
  30. useEffect(() => {
  31. if (pageRevision != null) {
  32. setMarkdown(pageRevision?.body ?? '');
  33. if (onRevisionLoaded != null) {
  34. onRevisionLoaded(pageRevision);
  35. }
  36. }
  37. }, [onRevisionLoaded, pageRevision]);
  38. useEffect(() => {
  39. if (error != null) {
  40. const isForbidden = error != null && error[0].code === 'forbidden-page';
  41. if (isForbidden) {
  42. setMarkdown(`<i class="icon-exclamation p-1"></i>${t('not_allowed_to_see_this_page')}`);
  43. }
  44. else {
  45. const errorMessages = error.map((error) => {
  46. return `<i class="icon-exclamation p-1"></i><span class="text-muted"><em>${error.message}</em></span>`;
  47. });
  48. setMarkdown(errorMessages.join('\n'));
  49. }
  50. }
  51. }, [error, t]);
  52. if (isLoading) {
  53. return (
  54. <div className="wiki">
  55. <div className="text-muted text-center">
  56. <i className="fa fa-2x fa-spinner fa-pulse mr-1"></i>
  57. </div>
  58. </div>
  59. );
  60. }
  61. return (
  62. <RevisionLoaderRoot>
  63. <RevisionRenderer
  64. rendererOptions={rendererOptions}
  65. markdown={markdown}
  66. />
  67. </RevisionLoaderRoot>
  68. );
  69. };