diff --git a/frontend/src/components/dialog/my-deleted-repos.js b/frontend/src/components/dialog/my-deleted-repos.js new file mode 100644 index 0000000000..01e2bb6760 --- /dev/null +++ b/frontend/src/components/dialog/my-deleted-repos.js @@ -0,0 +1,165 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { Modal, ModalHeader, ModalBody } from 'reactstrap'; +import moment from 'moment'; +import { gettext, lang, trashReposExpireDays } from '../../utils/constants'; +import { seafileAPI } from '../../utils/seafile-api'; +import { Utils } from '../../utils/utils'; +import toaster from '../../components/toast'; +import Loading from '../../components/loading'; +import EmptyTip from '../../components/empty-tip'; + +import '../../css/my-deleted-repos.css'; + +moment.locale(lang); + +class MyLibsDeleted extends Component { + + constructor(props) { + super(props); + this.state = { + deletedRepoList: [], + isLoading: true, + }; + } + + componentDidMount() { + seafileAPI.listDeletedRepo().then(res => { + this.setState({ + deletedRepoList: res.data, + isLoading: false, + }); + }).catch(error => { + let errMessage = Utils.getErrorMsg(error); + toaster.danger(errMessage); + }); + } + + refreshDeletedRepoList = (repoID) => { + let deletedRepoList = this.state.deletedRepoList.filter(item => { + return item.repo_id !== repoID; + }); + this.setState({ deletedRepoList: deletedRepoList }); + }; + + render() { + const { deletedRepoList: repos } = this.state; + return ( + + + {gettext('Deleted Libraries')} + + + {this.state.isLoading && } + {(!this.state.isLoading && repos.length === 0) && + +

{gettext('No deleted libraries')}

+

{gettext('You have not deleted any libraries in the last {placeholder} days. A deleted library will be cleaned automatically after this period.').replace('{placeholder}', trashReposExpireDays)}

+
+ } + {repos.length !== 0 && +
+

{gettext('Tip: libraries deleted {placeholder} days ago will be cleaned automatically.').replace('{placeholder}', trashReposExpireDays)}

+ + + + + + + + + + + {repos.map((item) => { + return ( + + ); + })} + +
{/* img*/}{gettext('Name')}{gettext('Deleted Time')}
+
+ } +
+
+ ); + } +} + +class DeletedRepoItem extends Component { + constructor(props) { + super(props); + this.state = { + hideRestoreMenu: true, + highlight: false, + }; + } + + onMouseEnter = () => { + this.setState({ + hideRestoreMenu: false, + highlight: true, + }); + }; + + onMouseLeave = () => { + this.setState({ + hideRestoreMenu: true, + highlight: false, + }); + }; + + restoreDeletedRepo = (e) => { + e.preventDefault(); + let repoID = this.props.repo.repo_id; + let repoName = this.props.repo.repo_name; + seafileAPI.restoreDeletedRepo(repoID).then(res => { + let message = gettext('Successfully restored the library {library_name}.').replace('{library_name}', repoName); + toaster.success(message); + this.props.refreshDeletedRepoList(repoID); + }).catch(error => { + const errMessage = Utils.getErrorMsg(error); + toaster.danger(errMessage); + }); + }; + + render() { + let localTime = moment.utc(this.props.repo.del_time).toDate(); + localTime = moment(localTime).fromNow(); + let iconUrl = Utils.getLibIconUrl(this.props.repo); + + return ( + + + {this.props.repo.repo_name} + {localTime} + + + + + + ); + } +} + +DeletedRepoItem.propTypes = { + repo: PropTypes.object.isRequired, + refreshDeletedRepoList: PropTypes.func.isRequired, +}; + +MyLibsDeleted.propTypes = { + toggleDialog: PropTypes.func.isRequired +}; + +export default MyLibsDeleted; diff --git a/frontend/src/components/empty-tip.js b/frontend/src/components/empty-tip.js index ffb8bff656..6398cb9a3f 100644 --- a/frontend/src/components/empty-tip.js +++ b/frontend/src/components/empty-tip.js @@ -5,13 +5,15 @@ import { mediaUrl } from '../utils/constants'; const propTypes = { forDialog: PropTypes.bool, children: PropTypes.any, + className: PropTypes.string }; class EmptyTip extends React.Component { render() { + const { className } = this.props; return ( -
+
{this.props.children}
diff --git a/frontend/src/css/my-deleted-repos.css b/frontend/src/css/my-deleted-repos.css new file mode 100644 index 0000000000..67257c6720 --- /dev/null +++ b/frontend/src/css/my-deleted-repos.css @@ -0,0 +1,35 @@ +.my-deleted-repos-dialog { + max-width: 720px; +} + +.my-deleted-repos-container { + min-height: 200px; + max-height: 500px; + overflow: auto; +} + +.my-deleted-repos-empty-tip { + text-align: center; + margin: 20px 0; +} + +.my-deleted-repos-empty-tip .empty-explanation { + color: #666; + font-size: .875rem; + margin: 0; +} + +@media (min-width: 768px) { + .my-deleted-repos-empty-tip { + margin: 104px 0 216px; + } + + .my-deleted-repos-empty-tip .empty-explanation { + margin: 0 7rem; + } +} + +.my-deleted-repos-tip { + font-size: .8125rem; + color: #9c9c9c; +} diff --git a/frontend/src/pages/my-libs/my-libs.js b/frontend/src/pages/my-libs/my-libs.js index 850af0a3b0..bbc393c83b 100644 --- a/frontend/src/pages/my-libs/my-libs.js +++ b/frontend/src/pages/my-libs/my-libs.js @@ -1,9 +1,7 @@ import React, { Component, Fragment } from 'react'; import cookie from 'react-cookies'; -import { navigate } from '@gatsbyjs/reach-router'; -import { DropdownToggle, Dropdown, DropdownMenu, DropdownItem } from 'reactstrap'; import { seafileAPI } from '../../utils/seafile-api'; -import { gettext, siteRoot } from '../../utils/constants'; +import { gettext } from '../../utils/constants'; import { Utils } from '../../utils/utils'; import toaster from '../../components/toast'; import Repo from '../../models/repo'; @@ -14,6 +12,7 @@ import SortOptionsDialog from '../../components/dialog/sort-options'; import SingleDropdownToolbar from '../../components/toolbar/single-dropdown-toolbar'; import ModalPortal from '../../components/modal-portal'; import CreateRepoDialog from '../../components/dialog/create-repo-dialog'; +import DeletedReposDialog from '../../components/dialog/my-deleted-repos'; class MyLibraries extends Component { constructor(props) { @@ -22,8 +21,8 @@ class MyLibraries extends Component { errorMsg: '', isLoading: true, repoList: [], + isDeletedReposDialogOpen: false, isCreateRepoDialogOpen: false, - isDropdownMenuOpen: false, isSortOptionsDialogOpen: false, sortBy: cookie.load('seafile-repo-dir-sort-by') || 'name', // 'name' or 'time' or 'size' sortOrder: cookie.load('seafile-repo-dir-sort-order') || 'asc', // 'asc' or 'desc' @@ -129,24 +128,13 @@ class MyLibraries extends Component { this.setState({ isCreateRepoDialogOpen: !this.state.isCreateRepoDialogOpen }); }; - toggleDropdownMenu = () => { + toggleDeletedReposDialog = () => { this.setState({ - isDropdownMenuOpen: !this.state.isDropdownMenuOpen + isDeletedReposDialogOpen: !this.state.isDeletedReposDialogOpen }); }; - visitDeleted = () => { - navigate(`${siteRoot}my-libs/deleted/`); - }; - - visitDeletedviaKey = (e) => { - if (e.key == 'Enter' || e.key == 'Space') { - this.visiteDeleted(); - } - }; - render() { - const { isDropdownMenuOpen } = this.state; return (
@@ -155,25 +143,14 @@ class MyLibraries extends Component {

{gettext('My Libraries')}

{(!Utils.isDesktop() && this.state.repoList.length > 0) && } - - - - - {gettext('Deleted Libraries')} - -
@@ -211,6 +188,13 @@ class MyLibraries extends Component { /> )} + {this.state.isDeletedReposDialogOpen && ( + + + + )}
);