RevisionComparer.jsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. import React, { useState } from 'react';
  2. import { pagePathUtils } from '@growi/core';
  3. import PropTypes from 'prop-types';
  4. import { CopyToClipboard } from 'react-copy-to-clipboard';
  5. import { withTranslation } from 'react-i18next';
  6. import {
  7. Dropdown, DropdownToggle, DropdownMenu, DropdownItem,
  8. } from 'reactstrap';
  9. import RevisionComparerContainer from '~/client/services/RevisionComparerContainer';
  10. import RevisionDiff from '../PageHistory/RevisionDiff';
  11. import { withUnstatedContainers } from '../UnstatedUtils';
  12. const { encodeSpaces } = pagePathUtils;
  13. /* eslint-disable react/prop-types */
  14. const DropdownItemContents = ({ title, contents }) => (
  15. <>
  16. <div className="h6 mt-1 mb-2"><strong>{title}</strong></div>
  17. <div className="card well mb-1 p-2">{contents}</div>
  18. </>
  19. );
  20. /* eslint-enable react/prop-types */
  21. const RevisionComparer = (props) => {
  22. const [dropdownOpen, setDropdownOpen] = useState(false);
  23. const { t, revisionComparerContainer } = props;
  24. const { path, pageId } = revisionComparerContainer.pageContainer.state;
  25. function toggleDropdown() {
  26. setDropdownOpen(!dropdownOpen);
  27. }
  28. const generateURL = (pathName) => {
  29. const { origin } = window.location;
  30. const { sourceRevision, targetRevision } = revisionComparerContainer.state;
  31. const url = new URL(pathName, origin);
  32. if (sourceRevision != null && targetRevision != null) {
  33. const urlParams = `${sourceRevision._id}...${targetRevision._id}`;
  34. url.searchParams.set('compare', urlParams);
  35. }
  36. return encodeSpaces(decodeURI(url));
  37. };
  38. const { sourceRevision, targetRevision } = revisionComparerContainer.state;
  39. if (sourceRevision == null || targetRevision == null) {
  40. return null;
  41. }
  42. const isNodiff = sourceRevision._id === targetRevision._id;
  43. return (
  44. <div className="revision-compare">
  45. <div className="d-flex">
  46. <h4 className="align-self-center">{ t('page_history.comparing_revisions') }</h4>
  47. <Dropdown
  48. className="grw-copy-dropdown align-self-center ml-auto"
  49. isOpen={dropdownOpen}
  50. toggle={() => toggleDropdown()}
  51. >
  52. <DropdownToggle
  53. caret
  54. className="d-block text-muted bg-transparent btn-copy border-0 py-0"
  55. >
  56. <i className="ti-clipboard"></i>
  57. </DropdownToggle>
  58. <DropdownMenu positionFixed right modifiers={{ preventOverflow: { boundariesElement: undefined } }}>
  59. {/* Page path URL */}
  60. <CopyToClipboard text={generateURL(path)}>
  61. <DropdownItem className="px-3">
  62. <DropdownItemContents title={t('copy_to_clipboard.Page URL')} contents={generateURL(path)} />
  63. </DropdownItem>
  64. </CopyToClipboard>
  65. {/* Permanent Link URL */}
  66. <CopyToClipboard text={generateURL(pageId)}>
  67. <DropdownItem className="px-3">
  68. <DropdownItemContents title={t('copy_to_clipboard.Permanent link')} contents={generateURL(pageId)} />
  69. </DropdownItem>
  70. </CopyToClipboard>
  71. <DropdownItem divider className="my-0"></DropdownItem>
  72. </DropdownMenu>
  73. </Dropdown>
  74. </div>
  75. <div className={`revision-compare-container ${isNodiff ? 'nodiff' : ''}`}>
  76. { isNodiff
  77. ? (
  78. <span className="h3 text-muted">{t('No diff')}</span>
  79. )
  80. : (
  81. <RevisionDiff
  82. revisionDiffOpened
  83. previousRevision={sourceRevision}
  84. currentRevision={targetRevision}
  85. />
  86. )
  87. }
  88. </div>
  89. </div>
  90. );
  91. };
  92. /**
  93. * Wrapper component for using unstated
  94. */
  95. const RevisionComparerWrapper = withUnstatedContainers(RevisionComparer, [RevisionComparerContainer]);
  96. RevisionComparer.propTypes = {
  97. t: PropTypes.func.isRequired, // i18next
  98. revisionComparerContainer: PropTypes.instanceOf(RevisionComparerContainer).isRequired,
  99. revisions: PropTypes.array,
  100. };
  101. export default withTranslation()(RevisionComparerWrapper);