1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-07-15 07:52:14 +00:00
seahub/frontend/src/components/dialog/repo-history.js
Jerry Ren 890880a5c8
refactor(metadata): remove ui-component (#7492)
1. ModalPortal
2. Icon
3. IconBtn
4. Loading
5. CenteredLoading
6. ClickOutside
7. SearchInput
8. Switch
9. CustomizeAddTool
10. SfCalendar
11. SfFilterCalendar
12. CustomizeSelect
13. CustomizePopover
14. FieldDisplaySettings
15. Formatters
16. remove duplicate codes
2025-03-01 10:12:48 +08:00

316 lines
9.4 KiB
JavaScript

import React from 'react';
import PropTypes from 'prop-types';
import dayjs from 'dayjs';
import { Modal, ModalBody } from 'reactstrap';
import { Utils } from '../../utils/utils';
import { gettext, siteRoot, enableRepoSnapshotLabel as showLabel } from '../../utils/constants';
import { seafileAPI } from '../../utils/seafile-api';
import Loading from '../loading';
import Paginator from '../../components/paginator';
import ModalPortal from '../../components/modal-portal';
import CommitDetails from '../../components/dialog/commit-details';
import UpdateRepoCommitLabels from '../../components/dialog/edit-repo-commit-labels';
import SeahubModalHeader from '@/components/common/seahub-modal-header';
import '../../css/repo-history.css';
const propTypes = {
repoID: PropTypes.string.isRequired,
userPerm: PropTypes.string.isRequired,
currentRepoInfo: PropTypes.object.isRequired,
toggleDialog: PropTypes.func.isRequired
};
class RepoHistory extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
errorMsg: '',
currentPage: 1,
perPage: 100,
hasNextPage: false,
items: []
};
}
componentDidMount() {
this.getItems(this.state.currentPage);
}
getItems = (page) => {
const { repoID } = this.props;
seafileAPI.getRepoHistory(repoID, page, this.state.perPage).then((res) => {
this.setState({
isLoading: false,
currentPage: page,
items: res.data.data,
hasNextPage: res.data.more
});
}).catch((error) => {
this.setState({
isLoading: false,
errorMsg: Utils.getErrorMsg(error, true) // true: show login tip if 403
});
});
};
resetPerPage = (perPage) => {
this.setState({
perPage: perPage
}, () => {
this.getItems(1);
});
};
render() {
const { repoID, userPerm, currentRepoInfo, toggleDialog } = this.props;
const { repo_name: repoName } = currentRepoInfo;
let title = gettext('{placeholder} Modification History');
title = title.replace('{placeholder}', '<span class="op-target text-truncate mx-1">' + Utils.HTMLescape(repoName) + '</span>');
return (
<Modal isOpen={true} toggle={toggleDialog} size='xl' id="repo-history-dialog">
<SeahubModalHeader toggle={toggleDialog}>
<span dangerouslySetInnerHTML={{ __html: title }} className="d-flex mw-100"></span>
</SeahubModalHeader>
<ModalBody>
{userPerm == 'rw' && <p className="repo-snapshot-tip">{gettext('Tip: a snapshot will be generated after modification, which records the library state after the modification.')}</p>}
<Content
isLoading={this.state.isLoading}
errorMsg={this.state.errorMsg}
items={this.state.items}
currentPage={this.state.currentPage}
hasNextPage={this.state.hasNextPage}
curPerPage={this.state.perPage}
resetPerPage={this.resetPerPage}
getListByPage={this.getItems}
repoID={repoID}
userPerm={userPerm}
/>
</ModalBody>
</Modal>
);
}
}
class Content extends React.Component {
constructor(props) {
super(props);
this.theadData = showLabel ? [
{ width: '43%', text: gettext('Description') },
{ width: '12%', text: gettext('Time') },
{ width: '9%', text: gettext('Modifier') },
{ width: '12%', text: `${gettext('Device')} / ${gettext('Version')}` },
{ width: '12%', text: gettext('Labels') },
{ width: '12%', text: '' }
] : [
{ width: '43%', text: gettext('Description') },
{ width: '15%', text: gettext('Time') },
{ width: '15%', text: gettext('Modifier') },
{ width: '15%', text: `${gettext('Device')} / ${gettext('Version')}` },
{ width: '12%', text: '' }
];
}
getPreviousPage = () => {
this.props.getListByPage(this.props.currentPage - 1);
};
getNextPage = () => {
this.props.getListByPage(this.props.currentPage + 1);
};
render() {
const {
isLoading, errorMsg, items,
curPerPage, currentPage, hasNextPage
} = this.props;
if (isLoading) {
return <Loading />;
}
if (errorMsg) {
return <p className="error mt-6 text-center">{errorMsg}</p>;
}
return (
<React.Fragment>
<table className="table-hover">
<thead>
<tr>
{this.theadData.map((item, index) => {
return <th key={index} width={item.width}>{item.text}</th>;
})}
</tr>
</thead>
<tbody>
{items.map((item, index) => {
item.isFirstCommit = (currentPage == 1) && (index == 0);
item.showDetails = hasNextPage || (index != items.length - 1);
return <Item key={index} item={item} repoID={this.props.repoID} userPerm={this.props.userPerm} />;
})}
</tbody>
</table>
<Paginator
gotoPreviousPage={this.getPreviousPage}
gotoNextPage={this.getNextPage}
currentPage={currentPage}
hasNextPage={hasNextPage}
curPerPage={curPerPage}
resetPerPage={this.props.resetPerPage}
noURLUpdate={true}
/>
</React.Fragment>
);
}
}
Content.propTypes = {
isLoading: PropTypes.bool.isRequired,
errorMsg: PropTypes.string.isRequired,
items: PropTypes.array.isRequired,
currentPage: PropTypes.number.isRequired,
hasNextPage: PropTypes.bool.isRequired,
curPerPage: PropTypes.number.isRequired,
resetPerPage: PropTypes.func.isRequired,
getListByPage: PropTypes.func.isRequired,
};
class Item extends React.Component {
constructor(props) {
super(props);
this.state = {
labels: this.props.item.tags,
isIconShown: false,
isCommitLabelUpdateDialogOpen: false,
isCommitDetailsDialogOpen: false
};
}
handleMouseOver = () => {
this.setState({ isIconShown: true });
};
handleMouseOut = () => {
this.setState({ isIconShown: false });
};
showCommitDetails = (e) => {
e.preventDefault();
this.setState({
isCommitDetailsDialogOpen: !this.state.isCommitDetailsDialogOpen
});
};
toggleCommitDetailsDialog = () => {
this.setState({
isCommitDetailsDialogOpen: !this.state.isCommitDetailsDialogOpen
});
};
editLabel = (e) => {
e.preventDefault();
this.setState({
isCommitLabelUpdateDialogOpen: !this.state.isCommitLabelUpdateDialogOpen
});
};
toggleLabelEditDialog = () => {
this.setState({
isCommitLabelUpdateDialogOpen: !this.state.isCommitLabelUpdateDialogOpen
});
};
updateLabels = (labels) => {
this.setState({
labels: labels
});
};
render() {
const { item, repoID, userPerm } = this.props;
const { isIconShown, isCommitLabelUpdateDialogOpen, isCommitDetailsDialogOpen, labels } = this.state;
let name = '';
if (item.email) {
if (!item.second_parent_id) {
name = <a href={`${siteRoot}profile/${encodeURIComponent(item.email)}/`}>{item.name}</a>;
} else {
name = gettext('None');
}
} else {
name = gettext('Unknown');
}
return (
<React.Fragment>
<tr onMouseOver={this.handleMouseOver} onMouseOut={this.handleMouseOut} onFocus={this.handleMouseOver}>
<td>
{item.description}
{item.showDetails &&
<a href="#" className="details" onClick={this.showCommitDetails} role="button">{gettext('Details')}</a>
}
</td>
<td title={dayjs(item.time).format('dddd, MMMM D, YYYY h:mm:ss A')}>{dayjs(item.time).format('YYYY-MM-DD')}</td>
<td>{name}</td>
<td>
{item.client_version ? `${item.device_name} / ${item.client_version}` : 'API / --'}
</td>
{showLabel &&
<td>
{labels.map((item, index) => {
return <span key={index} className="commit-label">{item}</span>;
})}
{userPerm == 'rw' &&
<a href="#" role="button" className={`attr-action-icon sf3-font sf3-font-rename ${isIconShown ? '' : 'invisible'}`} title={gettext('Edit')} aria-label={gettext('Edit')} onClick={this.editLabel}></a>
}
</td>
}
<td>
{userPerm == 'rw' && (
item.isFirstCommit ?
<span className={isIconShown ? '' : 'invisible'}>{gettext('Current Version')}</span> :
<a href={`${siteRoot}repo/${repoID}/snapshot/?commit_id=${item.commit_id}`} className={isIconShown ? '' : 'invisible'}>{gettext('View Snapshot')}</a>
)}
</td>
</tr>
{isCommitDetailsDialogOpen &&
<ModalPortal>
<CommitDetails
repoID={repoID}
commitID={item.commit_id}
commitTime={item.time}
toggleDialog={this.toggleCommitDetailsDialog}
/>
</ModalPortal>
}
{isCommitLabelUpdateDialogOpen &&
<ModalPortal>
<UpdateRepoCommitLabels
repoID={repoID}
commitID={item.commit_id}
commitLabels={labels}
updateCommitLabels={this.updateLabels}
toggleDialog={this.toggleLabelEditDialog}
/>
</ModalPortal>
}
</React.Fragment>
);
}
}
Item.propTypes = {
item: PropTypes.object.isRequired,
};
RepoHistory.propTypes = propTypes;
export default RepoHistory;