-
Notifications
You must be signed in to change notification settings - Fork 150
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
/* | ||
* // 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 from "react"; | ||
import PropTypes from "prop-types"; | ||
import { Grid, Dropdown, Button } from "semantic-ui-react"; | ||
|
||
export const CompareRevisionsDropdown = ({ | ||
loading, | ||
options, | ||
srcRevision, | ||
targetRevision, | ||
onSrcChange, | ||
onTargetChange, | ||
onCompare, | ||
}) => { | ||
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 }) => onSrcChange(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 }) => onTargetChange(value)} | ||
options={options} | ||
scrolling | ||
/> | ||
</Grid.Column> | ||
<Grid.Column | ||
verticalAlign="bottom" | ||
mobile={16} | ||
computer={2} | ||
tablet={16} | ||
largeScreen={2} | ||
widescreen={2} | ||
> | ||
<Button onClick={onCompare}>Compare</Button> | ||
</Grid.Column> | ||
</Grid> | ||
); | ||
}; | ||
|
||
CompareRevisionsDropdown.propTypes = { | ||
loading: PropTypes.bool.isRequired, | ||
options: PropTypes.array.isRequired, | ||
srcRevision: PropTypes.object, | ||
Check warning on line 69 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 69 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 70 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 70 in invenio_app_rdm/theme/assets/semantic-ui/js/invenio_app_rdm/administration/components/CompareRevisionsDropdown.js GitHub Actions / JS / Tests (20.x)
|
||
onSrcChange: PropTypes.func.isRequired, | ||
onTargetChange: PropTypes.func.isRequired, | ||
onCompare: PropTypes.func.isRequired, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
/* | ||
* // 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, Grid, Segment, Button } from "semantic-ui-react"; | ||
import { i18next } from "@translations/invenio_app_rdm/i18next"; | ||
import { Differ, Viewer } from "json-diff-kit"; | ||
import { CompareRevisionsDropdown } from "../components/CompareRevisionsDropdown"; | ||
|
||
export class CompareRevisions 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 = { | ||
loading: true, | ||
error: undefined, | ||
currentDiff: undefined, | ||
allRevisions: {}, | ||
srcRevision: undefined, | ||
targetRevision: undefined, | ||
}; | ||
} | ||
|
||
componentWillUnmount() { | ||
Check warning on line 53 in invenio_app_rdm/theme/assets/semantic-ui/js/invenio_app_rdm/administration/records/CompareRevisions.js GitHub Actions / JS / Tests (18.x)
|
||
this.cancellableAction && this.cancellableAction.cancel(); | ||
} | ||
|
||
async componentDidMount() { | ||
const { resource } = this.props; | ||
this.setState({ loading: true }); | ||
Check failure on line 59 in invenio_app_rdm/theme/assets/semantic-ui/js/invenio_app_rdm/administration/records/CompareRevisions.js GitHub Actions / JS / Tests (18.x)
|
||
try { | ||
this.cancellableAction = withCancel(RecordModerationApi.getRevisions(resource)); | ||
const response = await this.cancellableAction.promise; | ||
const revisions = await response.data; | ||
console.log(revisions); | ||
|
||
this.setState({ | ||
Check failure on line 66 in invenio_app_rdm/theme/assets/semantic-ui/js/invenio_app_rdm/administration/records/CompareRevisions.js GitHub Actions / JS / Tests (18.x)
|
||
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 }); | ||
Check failure on line 74 in invenio_app_rdm/theme/assets/semantic-ui/js/invenio_app_rdm/administration/records/CompareRevisions.js GitHub Actions / JS / Tests (18.x)
|
||
console.error(error); | ||
} | ||
} | ||
|
||
computeDiff = () => { | ||
const { srcRevision, targetRevision } = this.state; | ||
if (srcRevision && targetRevision) { | ||
const diff = this.differ.diff(srcRevision, targetRevision); | ||
this.setState({ currentDiff: diff }); | ||
} | ||
}; | ||
|
||
handleModalClose = () => { | ||
const { actionCancelCallback } = this.props; | ||
actionCancelCallback(); | ||
}; | ||
|
||
render() { | ||
const { error, loading, currentDiff, allRevisions, srcRevision, targetRevision } = | ||
this.state; | ||
|
||
const options = Object.values(allRevisions).map((rev) => ({ | ||
key: rev.updated, | ||
text: `${rev.updated} (${rev.revision_id})`, | ||
value: rev, | ||
})); | ||
|
||
return ( | ||
<> | ||
<Modal.Content> | ||
<CompareRevisionsDropdown | ||
loading={loading} | ||
options={options} | ||
srcRevision={srcRevision} | ||
targetRevision={targetRevision} | ||
onSrcChange={(value) => this.setState({ srcRevision: value })} | ||
onTargetChange={(value) => this.setState({ targetRevision: value })} | ||
onCompare={this.computeDiff} | ||
/> | ||
{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> | ||
{loading && <p>Loading...</p>} | ||
{!loading && currentDiff && ( | ||
<Grid> | ||
<Grid.Column width={14}> | ||
<Segment> | ||
<Viewer diff={currentDiff} {...this.viewerProps} /> | ||
</Segment> | ||
</Grid.Column> | ||
</Grid> | ||
)} | ||
</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, | ||
actionSuccessCallback: PropTypes.func.isRequired, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -552,3 +552,5 @@ dl.details-list { | |
vertical-align: sub; | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
{# -*- coding: utf-8 -*- | ||
|
||
This file is part of Invenio. | ||
Copyright (C) 2015-2025 CERN. | ||
|
||
Invenio is free software; you can redistribute it and/or modify it | ||
under the terms of the MIT License; see LICENSE file for more details. | ||
#} | ||
|
||
{%- extends "invenio_theme/page.html" -%} | ||
|
||
{%- block css %} | ||
{{ super()}} | ||
<link rel="stylesheet" href="/static/css/json-diff-kit.css"> | ||
{%- endblock css %} |