-
Notifications
You must be signed in to change notification settings - Fork 151
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
/* | ||
* // This file is part of Invenio-App-Rdm | ||
* // Copyright (C) 2025 CERN. | ||
* // | ||
* // Invenio-App-Rdm is free software; you can redistribute it and/or modify it | ||
* // under the terms of the MIT License; see LICENSE file for more details. | ||
*/ | ||
|
||
import React, { useState } from "react"; | ||
import PropTypes from "prop-types"; | ||
import { Grid, Dropdown, Button } from "semantic-ui-react"; | ||
|
||
export const CompareRevisionsDropdown = ({ | ||
loading, | ||
options, | ||
onCompare, | ||
srcRevision: srcOption, | ||
targetRevision: targetOption, | ||
}) => { | ||
// Local state for selected revisions | ||
const [srcRevision, setSrcRevision] = useState(srcOption); | ||
const [targetRevision, setTargetRevision] = useState(targetOption); | ||
|
||
const handleCompare = () => { | ||
onCompare(srcRevision, targetRevision); | ||
}; | ||
|
||
return ( | ||
<Grid> | ||
<Grid.Column mobile={16} computer={6} tablet={16} largeScreen={6} widescreen={6}> | ||
<label htmlFor="source-revision">From</label> | ||
<Dropdown | ||
id="source-revision" | ||
loading={loading} | ||
placeholder="Source revision..." | ||
fluid | ||
selection | ||
value={srcRevision} | ||
onChange={(e, { value }) => setSrcRevision(value)} | ||
options={options} | ||
scrolling | ||
/> | ||
</Grid.Column> | ||
<Grid.Column mobile={16} computer={6} tablet={16} largeScreen={6} widescreen={6}> | ||
<label htmlFor="target-revision">To</label> | ||
<Dropdown | ||
id="target-revision" | ||
loading={loading} | ||
placeholder="Target revision..." | ||
fluid | ||
selection | ||
value={targetRevision} | ||
onChange={(e, { value }) => setTargetRevision(value)} | ||
options={options} | ||
scrolling | ||
/> | ||
</Grid.Column> | ||
<Grid.Column | ||
verticalAlign="bottom" | ||
mobile={16} | ||
computer={2} | ||
tablet={16} | ||
largeScreen={2} | ||
widescreen={2} | ||
> | ||
<Button | ||
onClick={handleCompare} | ||
disabled={loading || !srcRevision || !targetRevision} | ||
> | ||
Compare | ||
</Button> | ||
</Grid.Column> | ||
</Grid> | ||
); | ||
}; | ||
|
||
CompareRevisionsDropdown.propTypes = { | ||
loading: PropTypes.bool.isRequired, | ||
options: PropTypes.array.isRequired, | ||
onCompare: PropTypes.func.isRequired, | ||
srcRevision: PropTypes.object, | ||
Check warning on line 81 in invenio_app_rdm/theme/assets/semantic-ui/js/invenio_app_rdm/administration/components/CompareRevisionsDropdown.js GitHub Actions / JS / Tests (18.x)
Check warning on line 81 in invenio_app_rdm/theme/assets/semantic-ui/js/invenio_app_rdm/administration/components/CompareRevisionsDropdown.js GitHub Actions / JS / Tests (20.x)
|
||
targetRevision: PropTypes.object, | ||
Check warning on line 82 in invenio_app_rdm/theme/assets/semantic-ui/js/invenio_app_rdm/administration/components/CompareRevisionsDropdown.js GitHub Actions / JS / Tests (18.x)
Check warning on line 82 in invenio_app_rdm/theme/assets/semantic-ui/js/invenio_app_rdm/administration/components/CompareRevisionsDropdown.js GitHub Actions / JS / Tests (20.x)
|
||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
/* | ||
* // This file is part of Invenio-App-Rdm | ||
* // Copyright (C) 2025 CERN. | ||
* // | ||
* // Invenio-App-Rdm is free software; you can redistribute it and/or modify it | ||
* // under the terms of the MIT License; see LICENSE file for more details. | ||
*/ | ||
|
||
import React, { Component } from "react"; | ||
import PropTypes from "prop-types"; | ||
import { Grid, Container } from "semantic-ui-react"; | ||
import { Differ, Viewer } from "json-diff-kit"; | ||
|
||
export class RevisionsDiffViewer extends Component { | ||
constructor(props) { | ||
super(props); | ||
this.differ = new Differ({ | ||
detectCircular: true, | ||
maxDepth: null, | ||
showModifications: true, | ||
arrayDiffMethod: "lcs", | ||
ignoreCase: false, | ||
ignoreCaseForKey: false, | ||
recursiveEqual: true, | ||
}); | ||
|
||
this.viewerProps = { | ||
indent: 4, | ||
lineNumbers: true, | ||
highlightInlineDiff: true, | ||
inlineDiffOptions: { | ||
mode: "word", | ||
wordSeparator: " ", | ||
}, | ||
hideUnchangedLines: true, | ||
syntaxHighlight: false, | ||
virtual: true, | ||
}; | ||
|
||
this.state = { | ||
currentDiff: undefined, | ||
}; | ||
} | ||
|
||
componentDidUpdate(prevProps) { | ||
const { diff } = this.props; | ||
|
||
if (diff !== prevProps.diff) { | ||
this.computeDiff(); | ||
} | ||
} | ||
|
||
computeDiff = () => { | ||
const { diff } = this.props; | ||
const _diff = this.differ.diff(diff?.srcRevision, diff?.targetRevision); | ||
this.setState({ currentDiff: _diff }); | ||
}; | ||
|
||
render() { | ||
const { currentDiff } = this.state; | ||
|
||
return currentDiff ? ( | ||
<Grid> | ||
<Grid.Column width={16}> | ||
<Container fluid> | ||
<Viewer diff={currentDiff} {...this.viewerProps} /> | ||
</Container> | ||
</Grid.Column> | ||
</Grid> | ||
) : null; | ||
} | ||
} | ||
|
||
RevisionsDiffViewer.propTypes = { | ||
diff: PropTypes.object, | ||
Check warning on line 75 in invenio_app_rdm/theme/assets/semantic-ui/js/invenio_app_rdm/administration/components/RevisionsDiffViewer.js GitHub Actions / JS / Tests (18.x)
|
||
viewerProps: PropTypes.object.isRequired, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
/* | ||
* // This file is part of Invenio-App-Rdm | ||
* // Copyright (C) 2025 CERN. | ||
* // | ||
* // Invenio-App-Rdm is free software; you can redistribute it and/or modify it | ||
* // under the terms of the MIT License; see LICENSE file for more details. | ||
*/ | ||
import React, { Component } from "react"; | ||
import PropTypes from "prop-types"; | ||
import { RecordModerationApi } from "./api"; | ||
import { withCancel, ErrorMessage } from "react-invenio-forms"; | ||
import { Modal, Button, Grid } from "semantic-ui-react"; | ||
import { i18next } from "@translations/invenio_app_rdm/i18next"; | ||
import { CompareRevisionsDropdown } from "../components/CompareRevisionsDropdown"; | ||
import { RevisionsDiffViewer } from "../components/RevisionsDiffViewer"; | ||
|
||
export class CompareRevisions extends Component { | ||
constructor(props) { | ||
super(props); | ||
|
||
this.state = { | ||
loading: true, | ||
error: undefined, | ||
allRevisions: {}, | ||
srcRevision: undefined, | ||
targetRevision: undefined, | ||
diff: undefined, | ||
}; | ||
} | ||
|
||
async fetchRevisions() { | ||
Check warning on line 31 in invenio_app_rdm/theme/assets/semantic-ui/js/invenio_app_rdm/administration/records/CompareRevisions.js GitHub Actions / JS / Tests (18.x)
|
||
const { resource } = this.props; | ||
this.setState({ loading: true }); | ||
|
||
try { | ||
this.cancellableAction = withCancel(RecordModerationApi.getRevisions(resource)); | ||
const response = await this.cancellableAction.promise; | ||
const revisions = await response.data; | ||
|
||
this.setState({ | ||
allRevisions: revisions, | ||
targetRevision: revisions[0], | ||
srcRevision: revisions.length > 1 ? revisions[1] : revisions[0], | ||
loading: false, | ||
}); | ||
} catch (error) { | ||
if (error === "UNMOUNTED") return; | ||
this.setState({ error: error, loading: false }); | ||
console.error(error); | ||
} | ||
} | ||
|
||
componentDidMount() { | ||
this.fetchRevisions(); | ||
} | ||
|
||
componentWillUnmount() { | ||
this.cancellableAction && this.cancellableAction.cancel(); | ||
} | ||
|
||
handleModalClose = () => { | ||
const { actionCancelCallback } = this.props; | ||
actionCancelCallback(); | ||
}; | ||
|
||
handleCompare = (srcRevision, targetRevision) => { | ||
this.setState({ diff: { srcRevision, targetRevision } }); | ||
}; | ||
|
||
render() { | ||
const { error, loading, allRevisions, srcRevision, targetRevision, diff } = | ||
Check warning on line 71 in invenio_app_rdm/theme/assets/semantic-ui/js/invenio_app_rdm/administration/records/CompareRevisions.js GitHub Actions / JS / Tests (18.x)
|
||
this.state; | ||
const options = Object.values(allRevisions).map((rev) => ({ | ||
key: rev.updated, | ||
text: `${rev.updated} (${rev.revision_id})`, | ||
value: rev, | ||
})); | ||
|
||
return loading ? ( | ||
<p>Loading...</p> | ||
) : ( | ||
<> | ||
<Modal.Content> | ||
<CompareRevisionsDropdown | ||
loading={loading} | ||
options={options} | ||
srcRevision={srcRevision} | ||
targetRevision={targetRevision} | ||
onCompare={this.handleCompare} | ||
/> | ||
{error && ( | ||
<Modal.Content> | ||
<ErrorMessage | ||
header={i18next.t("Unable to fetch revisions.")} | ||
content={error} | ||
icon="exclamation" | ||
className="text-align-left" | ||
negative | ||
/> | ||
</Modal.Content> | ||
)} | ||
<Modal.Content scrolling> | ||
<RevisionsDiffViewer diff={this.state.diff} /> | ||
Check warning on line 103 in invenio_app_rdm/theme/assets/semantic-ui/js/invenio_app_rdm/administration/records/CompareRevisions.js GitHub Actions / JS / Tests (18.x)
|
||
</Modal.Content> | ||
</Modal.Content> | ||
<Modal.Actions> | ||
<Grid> | ||
<Grid.Column floated="left" width={8} textAlign="left"> | ||
<Button | ||
onClick={this.handleModalClose} | ||
disabled={loading} | ||
loading={loading} | ||
aria-label={i18next.t("Cancel revision comparison")} | ||
> | ||
Close | ||
</Button> | ||
</Grid.Column> | ||
</Grid> | ||
</Modal.Actions> | ||
</> | ||
); | ||
} | ||
} | ||
|
||
CompareRevisions.propTypes = { | ||
resource: PropTypes.object.isRequired, | ||
actionCancelCallback: PropTypes.func.isRequired, | ||
}; |