diff --git a/frontend/src/components/common/go-back/index.css b/frontend/src/components/common/go-back/index.css
new file mode 100644
index 0000000000..e3a8c2f9cc
--- /dev/null
+++ b/frontend/src/components/common/go-back/index.css
@@ -0,0 +1,9 @@
+.go-back {
+ margin-right: 0.75rem;
+ color: #c0c0c0;
+}
+
+.go-back:hover {
+ color: #ff9933;
+ cursor: pointer;
+}
diff --git a/frontend/src/components/common/go-back/index.js b/frontend/src/components/common/go-back/index.js
new file mode 100644
index 0000000000..93bb45e009
--- /dev/null
+++ b/frontend/src/components/common/go-back/index.js
@@ -0,0 +1,21 @@
+import React, { Component } from 'react';
+
+import './index.css';
+
+class GoBack extends Component {
+
+ onBackClick = (event) => {
+ event.preventDefault();
+ window.history.back();
+ }
+
+ render() {
+ return (
+
+
+
+ );
+ }
+}
+
+export default GoBack;
diff --git a/frontend/src/components/common/switch/index.js b/frontend/src/components/common/switch/index.js
new file mode 100644
index 0000000000..7628ba018e
--- /dev/null
+++ b/frontend/src/components/common/switch/index.js
@@ -0,0 +1,37 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import classnames from 'classnames';
+
+import '../../../css/switch.css';
+
+function Switch(props) {
+ const { onChange, checked, placeholder, disabled, className, size } = props;
+
+ return(
+
+
+
+ );
+}
+
+Switch.propTypes = {
+ checked: PropTypes.bool,
+ disabled: PropTypes.bool,
+ placeholder: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
+ className: PropTypes.string,
+ size: PropTypes.oneOf(['large', 'small', undefined]),
+ onChange: PropTypes.func.isRequired,
+};
+
+export default Switch;
diff --git a/frontend/src/components/history-list-view/history-list-item.js b/frontend/src/components/history-list-view/history-list-item.js
index 2fc09a7ca3..976b4f372c 100644
--- a/frontend/src/components/history-list-view/history-list-item.js
+++ b/frontend/src/components/history-list-view/history-list-item.js
@@ -5,6 +5,8 @@ import { Dropdown, DropdownToggle, DropdownMenu, DropdownItem} from 'reactstrap'
import { gettext, filePath } from '../../utils/constants';
import URLDecorator from '../../utils/url-decorator';
+import '../../css/history-record-item.css';
+
moment.locale(window.app.config.lang);
const propTypes = {
diff --git a/frontend/src/css/file-history.css b/frontend/src/css/file-history.css
index a9880391d1..e3c5f8f444 100644
--- a/frontend/src/css/file-history.css
+++ b/frontend/src/css/file-history.css
@@ -76,54 +76,6 @@
background-color: #ffe7d5;
}
-.item-active {
- color: #fff;
- background-color: #fdc297 !important;
-}
-
-.item-active i {
- color: #fff;
-}
-
-.history-list-item .history-info {
- flex: 1;
- padding: 0 0.5rem;
-}
-
-.history-list-item .history-operation {
- width: 1.5rem;
- display: flex;
- align-items: center;
- justify-content: center;
-}
-
-.history-info .time {
- color: #000;
-}
-
-.history-info .owner {
- margin-top: 0.25rem;
- color: #888;
- display: flex;
- align-items: center;
-}
-
-.owner .squire-icon {
- width: 0.5rem;
- height: 0.5rem;
- background-color: #549b5a;
- margin-right: 0.25rem;
-}
-
-.history-body .dropdown-menu {
- min-width: 8rem;
-}
-
-.history-body .dropdown-menu a {
- text-decoration: none;
- color: #6e7687;
-}
-
.history-content .main-panel {
flex: 1 1 auto;
}
diff --git a/frontend/src/css/history-record-item.css b/frontend/src/css/history-record-item.css
new file mode 100644
index 0000000000..64332da8bd
--- /dev/null
+++ b/frontend/src/css/history-record-item.css
@@ -0,0 +1,47 @@
+.item-active {
+ color: #fff;
+ background-color: #fdc297 !important;
+}
+
+.item-active i {
+ color: #fff;
+}
+
+.history-list-item .history-info {
+ flex: 1;
+ padding: 0 0.5rem;
+}
+
+.history-list-item .history-operation {
+ width: 1.5rem;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.history-info .time {
+ color: #000;
+}
+
+.history-info .owner {
+ margin-top: 0.25rem;
+ color: #888;
+ display: flex;
+ align-items: center;
+}
+
+.owner .squire-icon {
+ width: 0.5rem;
+ height: 0.5rem;
+ background-color: #549b5a;
+ margin-right: 0.25rem;
+}
+
+.history-body .dropdown-menu {
+ min-width: 8rem;
+}
+
+.history-body .dropdown-menu a {
+ text-decoration: none;
+ color: #6e7687;
+}
diff --git a/frontend/src/css/sdoc-file-history.css b/frontend/src/css/sdoc-file-history.css
new file mode 100644
index 0000000000..32e408e1a7
--- /dev/null
+++ b/frontend/src/css/sdoc-file-history.css
@@ -0,0 +1,206 @@
+.sdoc-file-history .sdoc-file-history-container {
+ flex: 1;
+ overflow-x: hidden;
+}
+
+.sdoc-file-history .sdoc-file-history-header {
+ height: 50px;
+ border-bottom: 1px solid #e5e5e5;
+ background-color: #f4f4f7;
+}
+
+.sdoc-file-history .sdoc-file-history-header .sdoc-file-history-header-left {
+ font-size: 1.25rem;
+ flex: 1;
+}
+
+.sdoc-file-history .sdoc-file-history-header .file-name {
+ flex: 1;
+}
+
+.sdoc-file-history .sdoc-file-history-header .sdoc-file-history-header-right {
+ height: 100%;
+ min-width: 100px;
+}
+
+.sdoc-file-history .sdoc-file-history-header .sdoc-file-changes-container {
+ height: 32px;
+ border: 1px solid #e5e5e5;
+ border-radius: 3px;
+}
+
+.sdoc-file-history .sdoc-file-history-header .sdoc-file-changes-divider {
+ border-right: 1px solid #e5e5e5;
+ height: 100%;
+ width: 1px;
+}
+
+.sdoc-file-history .sdoc-file-history-header .sdoc-file-changes-last,
+.sdoc-file-history .sdoc-file-history-header .sdoc-file-changes-next {
+ padding: 0 8px;
+ color: #000;
+ opacity: .5;
+ height: 100%;
+}
+
+.sdoc-file-history .sdoc-file-history-header .sdoc-file-changes-last:hover,
+.sdoc-file-history .sdoc-file-history-header .sdoc-file-changes-next:hover {
+ cursor: pointer;
+ opacity: .75;
+}
+
+.sdoc-file-history .sdoc-file-history-content {
+ flex: 1;
+ min-height: 0;
+ padding: 20px 40px;
+ background-color: #fafaf9;
+ overflow-y: scroll;
+}
+
+.sdoc-file-history .sdoc-file-history-content .sdoc-file-history-viewer {
+ width: 100%;
+ min-height: 120px;
+ flex: 1;
+ background-color: #fff;
+ word-break: break-word;
+ border: 1px solid #e6e6dd;
+}
+
+.sdoc-file-history .sdoc-file-history-content .sdoc-editor-content {
+ background-color: #fff;
+}
+
+.sdoc-file-history .sdoc-file-history-content .article {
+ width: 100%;
+ margin: 0;
+}
+
+/* panel */
+.sdoc-file-history .sdoc-file-history-panel {
+ width: 260px;
+ border-left: 1px solid #e5e5e5;
+}
+
+.sdoc-file-history .sdoc-file-history-panel .sdoc-file-history-select-range {
+ padding: 10px 18px;
+ height: 50px;;
+ border-bottom: 1px solid #e5e5e5;
+ background-color: rgb(250, 250, 249);
+}
+
+.sdoc-file-history .sdoc-file-history-panel .sdoc-file-history-select-range-title {
+ height: 100%;
+ width: 100%;
+ font-size: 1rem;
+ font-weight: bolder;
+ line-height: 29px;
+}
+
+.sdoc-file-history .sdoc-file-history-panel .sdoc-file-history-diff-switch {
+ padding: 0 18px;
+ height: 50px;;
+ border-top: 1px solid #e5e5e5;
+}
+
+.sdoc-file-history .sdoc-file-history-diff-switch .custom-switch {
+ width: 100%;
+ padding-left: 0;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ cursor: pointer;
+}
+
+.sdoc-file-history .sdoc-file-history-diff-switch .custom-switch-description {
+ margin-left: 0;
+ flex: 1;
+ padding-right: 8px;
+}
+
+/* history versions */
+.sdoc-file-history-versions {
+ flex: 1;
+ flex-direction: column;
+ min-height: 0;
+ overflow: auto;
+}
+
+.sdoc-file-history-versions .history-list-item {
+ padding: 5px 10px;
+ display: flex;
+ flex: 1;
+ border-bottom: 1px solid #e5e5e5;
+}
+
+.sdoc-file-history-versions .history-list-item:last-child {
+ border-bottom: none;
+}
+
+.sdoc-file-history-versions .history-list-item:hover {
+ background-color: #ffe7d5;
+ cursor: pointer;
+}
+
+.history-info .owner {
+ min-height: 22.5px;
+}
+
+.sdoc-file-history-versions .dropdown-menu {
+ min-width: 8rem;
+}
+
+.sdoc-file-history-versions .dropdown-menu a {
+ text-decoration: none;
+ color: #6e7687;
+}
+
+.history-content .main-panel {
+ flex: 1 1 auto;
+}
+
+.history-content .history-side-panel {
+ flex: 0 0 auto;
+ user-select: none;
+ border-left: 1px solid #e5e5e5;
+ background-color: #fff;
+ display: flex;
+ flex-direction: column;
+}
+
+.history-content .history-side-panel .history-side-panel-title {
+ height: 50px;
+ border-bottom: 1px solid #e5e5e5;
+ line-height: 50px;
+ font-size: 1rem;
+ padding: 0 10px;
+ background-color: rgb(250,250,249);
+ font-weight: bolder;
+}
+
+@media (min-width:992px) {
+
+ .history-side-panel {
+ width: 260px;
+ }
+
+}
+
+@media (max-width:768px) {
+
+ .sdoc-file-history .sdoc-file-history-content {
+ padding: 0;
+ }
+
+ .sdoc-file-history .sdoc-file-history-content .sdoc-file-history-viewer {
+ border: none;
+ }
+
+ .markdown-viewer-render-content {
+ margin: 20px;
+ }
+
+ .markdown-viewer-render-content .diff-view {
+ padding: 20px;
+ }
+
+}
diff --git a/frontend/src/css/switch.css b/frontend/src/css/switch.css
new file mode 100644
index 0000000000..8b8e83852e
--- /dev/null
+++ b/frontend/src/css/switch.css
@@ -0,0 +1,14 @@
+.seahub-switch.small .custom-switch-indicator {
+ width: 22px;
+ height: 12px;
+ border-radius: 6px;
+}
+
+.seahub-switch.small .custom-switch-indicator:before {
+ height: 8px;
+ width: 8px;
+}
+
+.seahub-switch.small .custom-switch-input:checked~.custom-switch-indicator:before {
+ left: 12px;
+}
diff --git a/frontend/src/models/file-history.js b/frontend/src/models/file-history.js
new file mode 100644
index 0000000000..ed9206bf27
--- /dev/null
+++ b/frontend/src/models/file-history.js
@@ -0,0 +1,17 @@
+import moment from 'moment';
+moment.locale(window.app.config.lang);
+
+export default class FileHistory {
+
+ constructor(object) {
+ this.commitId = object.commit_id || undefined;
+ this.ctime = object.ctime ? moment(object.ctime).format('YYYY-MM-DD HH:mm') : '';
+ this.creatorName = object.creator_name || '';
+ this.size = object.size || 0;
+ this.revRenamedOldPath = object.rev_renamed_old_path || '';
+ this.revFileId = object.rev_file_id || '';
+ this.path = object.path || '';
+ this.description = object.description || '';
+ }
+
+}
diff --git a/frontend/src/pages/sdoc-file-history/history-version.js b/frontend/src/pages/sdoc-file-history/history-version.js
new file mode 100644
index 0000000000..fb89ca4cfe
--- /dev/null
+++ b/frontend/src/pages/sdoc-file-history/history-version.js
@@ -0,0 +1,99 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Dropdown, DropdownToggle, DropdownMenu, DropdownItem} from 'reactstrap';
+import { gettext, filePath } from '../../utils/constants';
+import URLDecorator from '../../utils/url-decorator';
+
+import '../../css/history-record-item.css';
+
+class HistoryVersion extends React.Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ isShowOperationIcon: false,
+ isMenuShow: false,
+ };
+ }
+
+ onMouseEnter = () => {
+ const { currentVersion, historyVersion } = this.props;
+ if (currentVersion.commitId === historyVersion.commitId) return;
+ this.setState({ isShowOperationIcon: true });
+ }
+
+ onMouseLeave = () => {
+ const { currentVersion, historyVersion } = this.props;
+ if (currentVersion.commitId === historyVersion.commitId) return;
+ this.setState({ isShowOperationIcon: false });
+ }
+
+ onToggleClick = (e) => {
+ this.setState({ isMenuShow: !this.state.isMenuShow });
+ }
+
+ onClick = () => {
+ this.setState({ isShowOperationIcon: false });
+ const { currentVersion, historyVersion } = this.props;
+ if (currentVersion.commitId === historyVersion.commitId) return;
+ this.props.onSelectHistoryVersion(historyVersion);
+ }
+
+ onRestore = () => {
+ const { historyVersion } = this.props;
+ this.props.onRestore(historyVersion);
+ }
+
+ onItemDownload = () => {
+ // nothing todo
+ }
+
+ render() {
+ const { currentVersion, historyVersion } = this.props;
+ if (!currentVersion || !historyVersion) return null;
+ const { ctime, commitId, creatorName, revFileId } = historyVersion;
+ const isHighlightItem = commitId === currentVersion.commitId;
+ const url = URLDecorator.getUrl({ type: 'download_historic_file', filePath: filePath, objID: revFileId });
+ return (
+
+
+
{ctime}
+
+
+ {creatorName}
+
+
+
+
+
+
+ {(this.props.index !== 0) && {gettext('Restore')}}
+ {gettext('Download')}
+
+
+
+
+ );
+ }
+}
+
+HistoryVersion.propTypes = {
+ index: PropTypes.number,
+ currentVersion: PropTypes.object.isRequired,
+ historyVersion: PropTypes.object,
+ onSelectHistoryVersion: PropTypes.func.isRequired,
+ onRestore: PropTypes.func.isRequired,
+};
+
+export default HistoryVersion;
diff --git a/frontend/src/pages/sdoc-file-history/index.js b/frontend/src/pages/sdoc-file-history/index.js
index 1454b25c22..bd9e00da9e 100644
--- a/frontend/src/pages/sdoc-file-history/index.js
+++ b/frontend/src/pages/sdoc-file-history/index.js
@@ -1,259 +1,173 @@
-import React, { Fragment } from 'react';
+import React from 'react';
import ReactDom from 'react-dom';
-import { Button } from 'reactstrap';
-import { Utils } from '../../utils/utils';
+import { UncontrolledTooltip } from 'reactstrap';
+import classnames from 'classnames';
+import DiffViewer from '@seafile/sdoc-editor/dist/pages/diff-viewer';
import { seafileAPI } from '../../utils/seafile-api';
-import { gettext, PER_PAGE, filePath, fileName, historyRepoID, canDownload, canCompare } from '../../utils/constants';
-import editUtilities from '../../utils/editor-utilities';
+import { gettext, fileName, historyRepoID } from '../../utils/constants';
import Loading from '../../components/loading';
-import Logo from '../../components/logo';
-import CommonToolbar from '../../components/toolbar/common-toolbar';
-import HistoryItem from '../file-history-old/history-item';
+import GoBack from '../../components/common/go-back';
+import SidePanel from './side-panel';
+import { Utils } from '../../utils/utils';
+import toaster from '../../components/toast';
import '../../css/layout.css';
-import '../../css/toolbar.css';
-import '../../css/search.css';
-import '../../css/file-history-old.css';
+import '../../css/sdoc-file-history.css';
class SdocFileHistory extends React.Component {
constructor(props) {
super(props);
+ const isShowChanges = localStorage.getItem('seahub-sdoc-history-show-changes') === 'false' ? false : true;
this.state = {
- historyList: [],
- currentPage: 1,
- hasMore: false,
- nextCommit: undefined,
- filePath: '',
- oldFilePath: '',
isLoading: true,
- isReloadingData: false,
+ isShowChanges,
+ currentVersion: {},
+ currentVersionContent: '',
+ lastVersionContent: '',
+ changes: [],
+ currentDiffIndex: 0,
};
}
- componentDidMount() {
- this.listOldHistoryRecords(historyRepoID, filePath);
- }
-
- listNewHistoryRecords = (filePath, PER_PAGE) => {
- editUtilities.listFileHistoryRecords(filePath, 1, PER_PAGE).then(res => {
- let historyData = res.data;
- if (!historyData) {
- this.setState({isLoading: false});
- throw Error('There is an error in server.');
- }
- this.initNewRecords(res.data);
- });
- }
-
- listOldHistoryRecords = (repoID, filePath) => {
- seafileAPI.listOldFileHistoryRecords(repoID, filePath).then((res) => {
- let historyData = res.data;
- if (!historyData) {
- this.setState({isLoading: false});
- throw Error('There is an error in server.');
- }
- this.initOldRecords(res.data);
- });
- }
-
- initNewRecords(result) {
- if (result.total_count < 5) {
- if (result.data.length) {
- let commitID = result.data[result.data.length-1].commit_id;
- let path = result.data[result.data.length-1].path;
- let oldPath = result.data[result.data.length-1].old_path;
- path = oldPath ? oldPath : path;
- seafileAPI.listOldFileHistoryRecords(historyRepoID, path, commitID).then((res) => {
- if (!res.data) {
- this.setState({isLoading: false});
- throw Error('There is an error in server.');
- }
- this.setState({
- historyList: result.data.concat(res.data.data.slice(1, res.data.data.length)),
- isLoading: false,
- });
+ onSelectHistoryVersion = (currentVersion, lastVersion) => {
+ this.setState({ isLoading: true, currentVersion });
+ seafileAPI.getFileRevision(historyRepoID, currentVersion.commitId, currentVersion.path).then(res => {
+ return seafileAPI.getFileContent(res.data);
+ }).then(res => {
+ const currentVersionContent = res.data;
+ if (lastVersion) {
+ seafileAPI.getFileRevision(historyRepoID, lastVersion.commitId, lastVersion.path).then(res => {
+ return seafileAPI.getFileContent(res.data);
+ }).then(res => {
+ const lastVersionContent = res.data;
+ this.setContent(currentVersionContent, lastVersionContent);
+ }).catch(error => {
+ const errorMessage = Utils.getErrorMsg(error, true);
+ toaster.danger(gettext(errorMessage));
+ this.setContent(currentVersionContent, '');
});
} else {
- seafileAPI.listOldFileHistoryRecords(historyRepoID, filePath).then((res) => {
- if (!res.data) {
- this.setState({isLoading: false});
- throw Error('There is an error in server.');
- }
- this.setState({
- historyList: res.data.data,
- isLoading: false,
- });
- });
+ this.setContent(currentVersionContent, '');
}
- } else {
- this.setState({
- historyList: result.data,
- currentPage: result.page,
- hasMore: result.total_count > (PER_PAGE * this.state.currentPage),
- isLoading: false,
- });
- }
- }
-
- initOldRecords(result) {
- if (result.data.length) {
- this.setState({
- historyList: result.data,
- nextCommit: result.next_start_commit,
- filePath: result.data[result.data.length-1].path,
- oldFilePath: result.data[result.data.length-1].rev_renamed_old_path,
- isLoading: false,
- });
- } else {
- this.setState({nextCommit: result.next_start_commit,});
- if (this.state.nextCommit) {
- seafileAPI.listOldFileHistoryRecords(historyRepoID, filePath, this.state.nextCommit).then((res) => {
- this.initOldRecords(res.data);
- });
- } else {
- this.setState({isLoading: false});
- }
- }
- }
-
- onScrollHandler = (event) => {
- const clientHeight = event.target.clientHeight;
- const scrollHeight = event.target.scrollHeight;
- const scrollTop = event.target.scrollTop;
- const isBottom = (clientHeight + scrollTop + 1 >= scrollHeight);
- let hasMore = this.state.hasMore;
- if (isBottom && hasMore) {
- this.reloadMore();
- }
- }
-
- reloadMore = () => {
- if (!this.state.isReloadingData) {
- const commitID = this.state.nextCommit;
- const filePath = this.state.filePath;
- const oldFilePath = this.state.oldFilePath;
- this.setState({ isReloadingData: true });
- if (oldFilePath) {
- seafileAPI.listOldFileHistoryRecords(historyRepoID, oldFilePath, commitID).then((res) => {
- this.updateOldRecords(res.data, oldFilePath);
- });
- } else {
- seafileAPI.listOldFileHistoryRecords(historyRepoID, filePath, commitID).then((res) => {
- this.updateOldRecords(res.data, filePath);
- });
- }
- }
- }
-
- updateNewRecords(result) {
- this.setState({
- historyList: [...this.state.historyList, ...result.data],
- currentPage: result.page,
- hasMore: result.total_count > (PER_PAGE * this.state.currentPage),
- isReloadingData: false,
+ }).catch(error => {
+ const errorMessage = Utils.getErrorMsg(error, true);
+ toaster.danger(gettext(errorMessage));
+ this.setContent('', '');
});
}
- updateOldRecords(result, filePath) {
- if (result.data.length) {
- this.setState({
- historyList: [...this.state.historyList, ...result.data],
- nextCommit: result.next_start_commit,
- filePath: result.data[result.data.length-1].path,
- oldFilePath: result.data[result.data.length-1].rev_renamed_old_path,
- isReloadingData: false,
- });
- } else {
- this.setState({nextCommit: result.next_start_commit,});
- if (this.state.nextCommit) {
- seafileAPI.listOldFileHistoryRecords(historyRepoID, filePath, this.state.nextCommit).then((res) => {
- this.updateOldRecords(res.data, filePath);
- });
- }
- }
+ setContent = (currentVersionContent = '', lastVersionContent = '') => {
+ this.setState({ currentVersionContent, lastVersionContent, isLoading: false, changes: [], currentDiffIndex: 0 });
}
- onItemRestore = (item) => {
- let commitId = item.commit_id;
- let filePath = item.path;
- editUtilities.revertFile(filePath, commitId).then(res => {
- if (res.data.success) {
- this.setState({ isLoading: true });
- this.refreshFileList();
+ onShowChanges = (isShowChanges) => {
+ this.setState({ isShowChanges }, () => {
+ localStorage.setItem('seahub-sdoc-history-show-changes', isShowChanges + '');
+ });
+ }
+
+ setDiffCount = (diff = { value: [], changes: [] }) => {
+ const { changes } = diff;
+ this.setState({ changes, currentDiffIndex: 0 });
+ }
+
+ jumpToElement = (currentDiffIndex) => {
+ this.setState({ currentDiffIndex }, () => {
+ const { currentDiffIndex, changes } = this.state;
+ const change = changes[currentDiffIndex];
+ const changeElement = document.querySelectorAll(`[data-id=${change}]`)[0];
+ if (changeElement) {
+ this.historyContentRef.scrollTop = changeElement.offsetTop - 10;
}
});
}
- refreshFileList() {
- seafileAPI.listOldFileHistoryRecords(historyRepoID, filePath).then((res) => {
- this.initOldRecords(res.data);
- });
+ lastChange = () => {
+ const { currentDiffIndex, changes } = this.state;
+ if (currentDiffIndex === 0) {
+ this.jumpToElement(changes.length - 1);
+ return;
+ }
+ this.jumpToElement(currentDiffIndex - 1);
}
- onSearchedClick = (searchedItem) => {
- Utils.handleSearchedItemClick(searchedItem);
- }
-
- onBackClick = (event) => {
- event.preventDefault();
- window.history.back();
+ nextChange = () => {
+ const { currentDiffIndex, changes } = this.state;
+ if (currentDiffIndex === changes.length - 1) {
+ this.jumpToElement(0);
+ return;
+ }
+ this.jumpToElement(currentDiffIndex + 1);
}
render() {
+ const { currentVersion, isShowChanges, currentVersionContent, lastVersionContent, isLoading, changes, currentDiffIndex } = this.state;
+ const changesCount = changes ? changes.length : 0;
+ const isShowChangesTips = isShowChanges && changesCount > 0;
+
return (
-
-