RevisionCompare.jsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. import React, { useState } from 'react';
  2. import PropTypes from 'prop-types';
  3. import { withTranslation } from 'react-i18next';
  4. import { CopyToClipboard } from 'react-copy-to-clipboard';
  5. import {
  6. Dropdown, DropdownToggle, DropdownMenu, DropdownItem,
  7. } from 'reactstrap';
  8. import { withUnstatedContainers } from './UnstatedUtils';
  9. import RevisionCompareContainer from '../services/RevisionCompareContainer';
  10. import RevisionDiff from './PageHistory/RevisionDiff';
  11. /* eslint-disable react/prop-types */
  12. const DropdownItemContents = ({ title, contents }) => (
  13. <>
  14. <div className="h6 mt-1 mb-2"><strong>{title}</strong></div>
  15. <div className="card well mb-1 p-2">{contents}</div>
  16. </>
  17. );
  18. /* eslint-enable react/prop-types */
  19. function encodeSpaces(str) {
  20. if (str == null) {
  21. return null;
  22. }
  23. // Encode SPACE and IDEOGRAPHIC SPACE
  24. return str.replace(/ /g, '%20').replace(/\u3000/g, '%E3%80%80');
  25. }
  26. const RevisionCompare = (props) => {
  27. const [dropdownOpen, setDropdownOpen] = useState(false);
  28. const { t, revisionCompareContainer } = props;
  29. function toggleDropdown() {
  30. setDropdownOpen(!dropdownOpen);
  31. }
  32. const pagePathUrl = () => {
  33. const { origin } = window.location;
  34. const { path } = revisionCompareContainer.pageContainer.state;
  35. const { fromRevision, toRevision } = revisionCompareContainer.state;
  36. const urlParams = (fromRevision && toRevision ? `?compare=${fromRevision._id}...${toRevision._id}` : '');
  37. return encodeSpaces(decodeURI(`${origin}/${path}${urlParams}`));
  38. };
  39. const fromRev = revisionCompareContainer.state.fromRevision;
  40. const toRev = revisionCompareContainer.state.toRevision;
  41. const showDiff = (fromRev && toRev);
  42. return (
  43. <div className="revision-compare">
  44. <div className="d-flex">
  45. <h3 className="align-self-center mb-0">{t('page_history.comparing_versions')}</h3>
  46. <div className="align-self-center ml-3">
  47. <div className="custom-control custom-switch">
  48. <input
  49. type="checkbox"
  50. className="custom-control-input"
  51. id="comparingWithLatest"
  52. checked={revisionCompareContainer.state.compareWithLatest}
  53. onChange={() => revisionCompareContainer.toggleCompareWithLatest()}
  54. />
  55. <label className="custom-control-label" htmlFor="comparingWithLatest">
  56. {t('page_history.comparing_with_latest')}
  57. </label>
  58. </div>
  59. </div>
  60. <Dropdown
  61. className="grw-copy-dropdown align-self-center ml-auto"
  62. isOpen={dropdownOpen}
  63. toggle={() => toggleDropdown()}
  64. >
  65. <DropdownToggle
  66. caret
  67. className="d-block text-muted bg-transparent btn-copy border-0 py-0"
  68. >
  69. <i className="ti-clipboard"></i>
  70. </DropdownToggle>
  71. <DropdownMenu positionFixed modifiers={{ preventOverflow: { boundariesElement: null } }}>
  72. {/* Page path URL */}
  73. <CopyToClipboard text={pagePathUrl()}>
  74. <DropdownItem className="px-3">
  75. <DropdownItemContents title={t('copy_to_clipboard.Page URL')} contents={pagePathUrl()} />
  76. </DropdownItem>
  77. </CopyToClipboard>
  78. <DropdownItem divider className="my-0"></DropdownItem>
  79. </DropdownMenu>
  80. </Dropdown>
  81. </div>
  82. <hr />
  83. <div className="revision-compare-outer">
  84. { showDiff && (
  85. <RevisionDiff
  86. revisionDiffOpened
  87. previousRevision={fromRev}
  88. currentRevision={toRev}
  89. />
  90. )}
  91. </div>
  92. </div>
  93. );
  94. };
  95. /**
  96. * Wrapper component for using unstated
  97. */
  98. const RevisionCompareWrapper = withUnstatedContainers(RevisionCompare, [RevisionCompareContainer]);
  99. RevisionCompare.propTypes = {
  100. t: PropTypes.func.isRequired, // i18next
  101. revisionCompareContainer: PropTypes.instanceOf(RevisionCompareContainer).isRequired,
  102. revisions: PropTypes.array,
  103. };
  104. export default withTranslation()(RevisionCompareWrapper);