1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-05-03 05:27:10 +00:00
seahub/frontend/src/components/dialog/repo-share-admin/upload-links.js
Jerry Ren 890880a5c8
refactor(metadata): remove ui-component ()
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

339 lines
10 KiB
JavaScript

import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import dayjs from 'dayjs';
import classnames from 'classnames';
import { Link } from '@gatsbyjs/reach-router';
import { Utils } from '../../../utils/utils';
import { seafileAPI } from '../../../utils/seafile-api';
import { repoShareAdminAPI } from '../../../utils/repo-share-admin-api';
import { gettext, siteRoot } from '../../../utils/constants';
import Loading from '../../loading';
import toaster from '../../../components/toast';
import EmptyTip from '../../../components/empty-tip';
import CommonOperationConfirmationDialog from '../../../components/dialog/common-operation-confirmation-dialog';
const itemPropTypes = {
item: PropTypes.object.isRequired,
deleteItem: PropTypes.func.isRequired,
toggleSelectLink: PropTypes.func.isRequired
};
class Item extends Component {
constructor(props) {
super(props);
this.state = {
isOperationShow: false
};
}
onMouseEnter = () => {
this.setState({ isOperationShow: true });
};
onMouseLeave = () => {
this.setState({ isOperationShow: false });
};
onDeleteLink = () => {
this.props.deleteItem(this.props.item);
};
cutLink = (link) => {
let length = link.length;
return link.slice(0, 9) + '...' + link.slice(length - 5);
};
toggleSelectLink = (e) => {
const { item } = this.props;
this.props.toggleSelectLink(item, e.target.checked);
};
render() {
let item = this.props.item;
let path = item.path === '/' ? '/' : item.path.slice(0, item.path.length - 1);
let objUrl = `${siteRoot}library/${item.repo_id}/${encodeURIComponent(item.repo_name)}${Utils.encodePath(path)}`;
return (
<tr
onMouseEnter={this.onMouseEnter}
onMouseLeave={this.onMouseLeave}
onFocus={this.onMouseEnter}
className={classnames({ 'tr-highlight': item.isSelected })}
>
<td className="text-center">
<input
type="checkbox"
checked={item.isSelected || false}
className="vam"
onChange={this.toggleSelectLink}
/>
</td>
<td>
<img src={item.creator_avatar} alt={item.creator_name} width="24" className="rounded-circle mr-2" />
<a href={`${siteRoot}profile/${encodeURIComponent(item.creator_email)}/`} target="_blank" className="align-middle" rel="noreferrer">{item.creator_name}</a>
</td>
<td>
<Link to={objUrl}>{item.obj_name}</Link>
</td>
<td>
<a href={item.link} target="_blank" rel="noreferrer">
{this.cutLink(item.link)}
</a>
</td>
<td>
{item.expire_date ? dayjs(item.expire_date).format('YYYY-MM-DD HH:mm') : '--'}
</td>
<td>{item.view_cnt}</td>
<td>
<i
tabIndex="0"
role="button"
className={`sf3-font-delete1 sf3-font op-icon ${this.state.isOperationShow ? '' : 'invisible'}`}
onClick={this.onDeleteLink}
onKeyDown={Utils.onKeyDown}
title={gettext('Delete')}
aria-label={gettext('Delete')}
>
</i>
</td>
</tr>
);
}
}
Item.propTypes = itemPropTypes;
const propTypes = {
repo: PropTypes.object.isRequired,
};
const PER_PAGE = 25;
class RepoShareAdminUploadLinks extends Component {
constructor(props) {
super(props);
this.state = {
loading: true,
hasMore: false,
isLoadingMore: false,
page: 1,
errorMsg: '',
items: [],
isDeleteUploadLinksDialogOpen: false
};
}
componentDidMount() {
const { repo } = this.props;
const { page } = this.state;
repoShareAdminAPI.listRepoUploadLinks(repo.repo_id, page).then((res) => {
this.setState({
loading: false,
hasMore: res.data.length == PER_PAGE,
items: res.data,
});
}).catch((error) => {
this.setState({
loading: false,
errorMsg: Utils.getErrorMsg(error, true) // true: show login tip if 403
});
});
}
deleteItem = (item) => {
seafileAPI.deleteRepoUploadLink(this.props.repo.repo_id, item.token).then(() => {
let items = this.state.items.filter(linkItem => {
return linkItem.token !== item.token;
});
this.setState({ items: items });
let message = gettext('Successfully deleted 1 item');
toaster.success(message);
}).catch((error) => {
let errMessage = Utils.getErrorMsg(error);
toaster.danger(errMessage);
});
};
toggleDeleteUploadLinksDialog = () => {
this.setState({ isDeleteUploadLinksDialogOpen: !this.state.isDeleteUploadLinksDialogOpen });
};
toggleSelectAllLinks = (e) => {
this._toggleSelectAllLinks(e.target.checked);
};
cancelSelectAllLinks = () => {
this._toggleSelectAllLinks(false);
};
_toggleSelectAllLinks = (isSelected) => {
const { items: links } = this.state;
this.setState({
items: links.map(item => {
item.isSelected = isSelected;
return item;
})
});
};
toggleSelectLink = (link, isSelected) => {
const { items: links } = this.state;
this.setState({
items: links.map(item => {
if (item.token == link.token) {
item.isSelected = isSelected;
}
return item;
})
});
};
deleteUploadLinks = () => {
const { items } = this.state;
const tokens = items.filter(item => item.isSelected).map(link => link.token);
repoShareAdminAPI.deleteUploadLinks(tokens).then(res => {
const { success, failed } = res.data;
if (success.length) {
let newLinkList = items.filter(link => {
return !success.some(deletedLink => {
return deletedLink.token == link.token;
});
});
this.setState({
items: newLinkList
});
const length = success.length;
const msg = length == 1 ?
gettext('Successfully deleted 1 upload link') :
gettext('Successfully deleted {number_placeholder} upload links')
.replace('{number_placeholder}', length);
toaster.success(msg);
}
failed.forEach(item => {
const msg = `${item.token}: ${item.error_msg}`;
toaster.danger(msg);
});
}).catch((error) => {
let errMessage = Utils.getErrorMsg(error);
toaster.danger(errMessage);
});
};
getTheadContent = (withCheckbox, isAllLinksSelected) => {
return (
<tr>
<th width="5%" className="text-center">
{withCheckbox && <input type="checkbox" checked={isAllLinksSelected} className="vam" onChange={this.toggleSelectAllLinks} />}
</th>
<th width="20%">{gettext('Creator')}</th>
<th width="24%">{gettext('Name')}</th>
<th width="19%">{gettext('Link')}</th>
<th width="17%">{gettext('Expiration')}</th>
<th width="8%">{gettext('Visits')}</th>
<th width="7%"></th>
</tr>
);
};
handleScroll = (event) => {
if (!this.state.isLoadingMore && this.state.hasMore) {
const clientHeight = event.target.clientHeight;
const scrollHeight = event.target.scrollHeight;
const scrollTop = event.target.scrollTop;
const isBottom = (clientHeight + scrollTop + 1 >= scrollHeight);
if (isBottom) { // scroll to the bottom
this.setState({ isLoadingMore: true }, () => {
this.getMore();
});
}
}
};
getMore = () => {
const { page, items } = this.state;
const { repo } = this.props;
repoShareAdminAPI.listRepoUploadLinks(repo.repo_id, page + 1).then((res) => {
this.setState({
isLoadingMore: false,
hasMore: res.data.length == PER_PAGE,
page: page + 1,
items: items.concat(res.data)
});
}).catch(error => {
this.setState({
isLoadingMore: false
});
let errMessage = Utils.getErrorMsg(error);
toaster.danger(errMessage);
});
};
render() {
const { loading, isLoadingMore, errorMsg, items, isDeleteUploadLinksDialogOpen } = this.state;
const selectedLinks = items.filter(item => item.isSelected);
const isAllLinksSelected = items.length == selectedLinks.length;
return (
<Fragment>
<div className="d-flex justify-content-between align-items-center pb-2 mt-1 pr-1 border-bottom">
<h6 className="font-weight-normal m-0">{gettext('Upload Links')}</h6>
<div className="d-flex">
{selectedLinks.length > 0 && (
<>
<button className="btn btn-sm btn-secondary mr-2" onClick={this.cancelSelectAllLinks}>{gettext('Cancel')}</button>
<button className="btn btn-sm btn-secondary mr-2" onClick={this.toggleDeleteUploadLinksDialog}>{gettext('Delete')}</button>
</>
)}
</div>
</div>
{loading && <Loading />}
{!loading && errorMsg && <p className="error text-center mt-8">{errorMsg}</p>}
{!loading && !errorMsg && !items.length &&
<EmptyTip text={gettext('No upload links')}/>
}
{!loading && !errorMsg && items.length > 0 && (
<>
<table>
<thead>{this.getTheadContent(true, isAllLinksSelected)}</thead>
<tbody></tbody>
</table>
<div className='table-real-container' onScroll={this.handleScroll}>
<table className="table-hover table-thead-hidden">
<thead>{this.getTheadContent(false)}</thead>
<tbody>
{items.map((item, index) => {
return (
<Item
key={index}
item={item}
deleteItem={this.deleteItem}
toggleSelectLink={this.toggleSelectLink}
/>
);
})}
</tbody>
</table>
{isLoadingMore && <Loading />}
</div>
</>
)}
{isDeleteUploadLinksDialogOpen && (
<CommonOperationConfirmationDialog
title={gettext('Delete upload links')}
message={gettext('Are you sure you want to delete the selected upload link(s) ?')}
executeOperation={this.deleteUploadLinks}
confirmBtnText={gettext('Delete')}
toggleDialog={this.toggleDeleteUploadLinksDialog}
/>
)}
</Fragment>
);
}
}
RepoShareAdminUploadLinks.propTypes = propTypes;
export default RepoShareAdminUploadLinks;