mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-09 19:01:42 +00:00
['libs' pages] added 'view mode' selector & 'sort' menu for 'my (#7096)
libs, shared with me, shared with all, group, department' pages - besides that, fixed bugs & improved many lines of code
This commit is contained in:
@@ -24,7 +24,7 @@ import ShareAdminLibraries from './pages/share-admin/libraries';
|
||||
import ShareAdminFolders from './pages/share-admin/folders';
|
||||
import ShareAdminShareLinks from './pages/share-admin/share-links';
|
||||
import ShareAdminUploadLinks from './pages/share-admin/upload-links';
|
||||
import SharedLibraries from './pages/shared-libs/shared-libraries';
|
||||
import SharedLibraries from './pages/shared-libs';
|
||||
import ShareWithOCM from './pages/share-with-ocm/shared-with-ocm';
|
||||
import OCMViaWebdav from './pages/ocm-via-webdav/ocm-via-webdav';
|
||||
import OCMRepoDir from './pages/share-with-ocm/remote-dir-view';
|
||||
|
@@ -7,7 +7,7 @@ import TextTranslation from '../../utils/text-translation';
|
||||
import SeahubPopover from '../common/seahub-popover';
|
||||
import ListTagPopover from '../popover/list-tag-popover';
|
||||
import ViewModes from '../../components/view-modes';
|
||||
import ReposSortMenu from '../../components/repos-sort-menu';
|
||||
import SortMenu from '../../components/sort-menu';
|
||||
import MetadataViewToolBar from '../../metadata/components/view-toolbar';
|
||||
import { PRIVATE_FILE_TYPE } from '../../constants';
|
||||
import { DIRENT_DETAIL_MODE } from '../dir-view-mode/constants';
|
||||
@@ -36,13 +36,6 @@ class DirTool extends React.Component {
|
||||
isRepoTagDialogOpen: false,
|
||||
isDropdownMenuOpen: false,
|
||||
};
|
||||
|
||||
this.sortOptions = [
|
||||
{ value: 'name-asc', text: gettext('By name ascending') },
|
||||
{ value: 'name-desc', text: gettext('By name descending') },
|
||||
{ value: 'time-asc', text: gettext('By time ascending') },
|
||||
{ value: 'time-desc', text: gettext('By time descending') }
|
||||
];
|
||||
}
|
||||
|
||||
toggleDropdownMenu = () => {
|
||||
@@ -111,13 +104,6 @@ class DirTool extends React.Component {
|
||||
const isFileExtended = currentPath.startsWith('/' + PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES + '/');
|
||||
const isTagView = currentPath.startsWith('/' + PRIVATE_FILE_TYPE.TAGS_PROPERTIES + '/');
|
||||
|
||||
const sortOptions = this.sortOptions.map(item => {
|
||||
return {
|
||||
...item,
|
||||
isSelected: item.value === `${sortBy}-${sortOrder}`
|
||||
};
|
||||
});
|
||||
|
||||
if (isFileExtended) {
|
||||
return (
|
||||
<div className="dir-tool">
|
||||
@@ -142,7 +128,7 @@ class DirTool extends React.Component {
|
||||
<React.Fragment>
|
||||
<div className="dir-tool d-flex">
|
||||
<ViewModes currentViewMode={currentMode} switchViewMode={this.props.switchViewMode} />
|
||||
<ReposSortMenu sortOptions={sortOptions} onSelectSortOption={this.onSelectSortOption}/>
|
||||
<SortMenu sortBy={sortBy} sortOrder={sortOrder} onSelectSortOption={this.onSelectSortOption} />
|
||||
{(!isCustomPermission) &&
|
||||
<div className="cur-view-path-btn" onClick={this.showDirentDetail}>
|
||||
<span className="sf3-font sf3-font-info" aria-label={propertiesText} title={propertiesText}></span>
|
||||
|
@@ -25,7 +25,6 @@ const propTypes = {
|
||||
onItemRename: PropTypes.func,
|
||||
hasNextPage: PropTypes.bool,
|
||||
onMonitorRepo: PropTypes.func,
|
||||
theadHidden: PropTypes.bool,
|
||||
inAllLibs: PropTypes.bool,
|
||||
onTransferRepo: PropTypes.func,
|
||||
};
|
||||
@@ -119,7 +118,7 @@ class SharedRepoListView extends React.Component {
|
||||
};
|
||||
|
||||
renderRepoListView = () => {
|
||||
const { currentViewMode = LIST_MODE } = this.props;
|
||||
const { currentViewMode } = this.props;
|
||||
return (
|
||||
<Fragment>
|
||||
{this.props.repoList.map((repo, index) => {
|
||||
@@ -148,28 +147,26 @@ class SharedRepoListView extends React.Component {
|
||||
};
|
||||
|
||||
renderPCUI = () => {
|
||||
const { theadHidden = false, currentViewMode = LIST_MODE, currentGroup, libraryType, inAllLibs } = this.props;
|
||||
const { currentViewMode, currentGroup, libraryType, inAllLibs } = this.props;
|
||||
const { sortByName, sortByTime, sortBySize, sortIcon } = this.getSortMetaData();
|
||||
|
||||
const content = currentViewMode == LIST_MODE ? (
|
||||
<>
|
||||
<table className={classNames({ 'table-thead-hidden': theadHidden }, { 'repos-container': !inAllLibs })}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="4%"></th>
|
||||
<th width="3%"><span className="sr-only">{gettext('Library Type')}</span></th>
|
||||
<th width="35%"><a className="d-block table-sort-op" href="#" onClick={this.sortByName}>{gettext('Name')} {sortByName && sortIcon}</a></th>
|
||||
<th width="10%"><span className="sr-only">{gettext('Actions')}</span></th>
|
||||
<th width="14%"><a className="d-block table-sort-op" href="#" onClick={this.sortBySize}>{gettext('Size')} {sortBySize && sortIcon}</a></th>
|
||||
<th width="17%"><a className="d-block table-sort-op" href="#" onClick={this.sortByTime}>{gettext('Last Update')} {sortByTime && sortIcon}</a></th>
|
||||
<th width="17%">{gettext('Owner')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{this.renderRepoListView()}
|
||||
</tbody>
|
||||
</table>
|
||||
</>
|
||||
<table className={classNames({ 'table-thead-hidden': inAllLibs })}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="4%"></th>
|
||||
<th width="3%"><span className="sr-only">{gettext('Library Type')}</span></th>
|
||||
<th width="35%"><a className="d-block table-sort-op" href="#" onClick={this.sortByName}>{gettext('Name')} {sortByName && sortIcon}</a></th>
|
||||
<th width="10%"><span className="sr-only">{gettext('Actions')}</span></th>
|
||||
<th width="14%"><a className="d-block table-sort-op" href="#" onClick={this.sortBySize}>{gettext('Size')} {sortBySize && sortIcon}</a></th>
|
||||
<th width="17%"><a className="d-block table-sort-op" href="#" onClick={this.sortByTime}>{gettext('Last Update')} {sortByTime && sortIcon}</a></th>
|
||||
<th width="17%">{gettext('Owner')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{this.renderRepoListView()}
|
||||
</tbody>
|
||||
</table>
|
||||
) : (
|
||||
<div className="d-flex justify-content-between flex-wrap">
|
||||
{this.renderRepoListView()}
|
||||
|
@@ -4,14 +4,21 @@ import { Dropdown, DropdownMenu, DropdownToggle, DropdownItem } from 'reactstrap
|
||||
import { gettext } from '../utils/constants';
|
||||
|
||||
const propTypes = {
|
||||
sortOptions: PropTypes.array,
|
||||
sortBy: PropTypes.string,
|
||||
sortOrder: PropTypes.string,
|
||||
onSelectSortOption: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
class ReposSortMenu extends React.Component {
|
||||
class SortMenu extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.sortOptions = [
|
||||
{ value: 'name-asc', text: gettext('By name ascending') },
|
||||
{ value: 'name-desc', text: gettext('By name descending') },
|
||||
{ value: 'time-asc', text: gettext('By time ascending') },
|
||||
{ value: 'time-desc', text: gettext('By time descending') }
|
||||
];
|
||||
this.state = {
|
||||
isDropdownMenuOpen: false
|
||||
};
|
||||
@@ -25,7 +32,13 @@ class ReposSortMenu extends React.Component {
|
||||
|
||||
render() {
|
||||
const { isDropdownMenuOpen } = this.state;
|
||||
const { sortOptions } = this.props;
|
||||
const { sortBy, sortOrder } = this.props;
|
||||
const sortOptions = this.sortOptions.map(item => {
|
||||
return {
|
||||
...item,
|
||||
isSelected: item.value == `${sortBy}-${sortOrder}`
|
||||
};
|
||||
});
|
||||
return (
|
||||
<Dropdown
|
||||
isOpen={isDropdownMenuOpen}
|
||||
@@ -59,6 +72,6 @@ class ReposSortMenu extends React.Component {
|
||||
|
||||
}
|
||||
|
||||
ReposSortMenu.propTypes = propTypes;
|
||||
SortMenu.propTypes = propTypes;
|
||||
|
||||
export default ReposSortMenu;
|
||||
export default SortMenu;
|
@@ -169,7 +169,7 @@
|
||||
}
|
||||
|
||||
.repos-container {
|
||||
margin-bottom: 10rem;
|
||||
padding-bottom: 10rem;
|
||||
}
|
||||
|
||||
.table-drop-active::before {
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import cookie from 'react-cookies';
|
||||
import classnames from 'classnames';
|
||||
import { gettext, username, canAddRepo } from '../../utils/constants';
|
||||
import { seafileAPI } from '../../utils/seafile-api';
|
||||
import { Utils } from '../../utils/utils';
|
||||
@@ -20,6 +21,9 @@ import LeaveGroupDialog from '../../components/dialog/leave-group-dialog';
|
||||
import SharedRepoListView from '../../components/shared-repo-list-view/shared-repo-list-view';
|
||||
import SortOptionsDialog from '../../components/dialog/sort-options';
|
||||
import SingleDropdownToolbar from '../../components/toolbar/single-dropdown-toolbar';
|
||||
import ViewModes from '../../components/view-modes';
|
||||
import ReposSortMenu from '../../components/sort-menu';
|
||||
import { LIST_MODE } from '../../components/dir-view-mode/constants';
|
||||
|
||||
import '../../css/group-view.css';
|
||||
|
||||
@@ -41,6 +45,7 @@ class GroupView extends React.Component {
|
||||
currentRepo: null,
|
||||
isStaff: false,
|
||||
isOwner: false,
|
||||
currentViewMode: localStorage.getItem('sf_repo_list_view_mode') || LIST_MODE,
|
||||
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'
|
||||
isSortOptionsDialogOpen: false,
|
||||
@@ -386,8 +391,27 @@ class GroupView extends React.Component {
|
||||
return opList;
|
||||
};
|
||||
|
||||
switchViewMode = (newMode) => {
|
||||
this.setState({
|
||||
currentViewMode: newMode
|
||||
}, () => {
|
||||
localStorage.setItem('sf_repo_list_view_mode', newMode);
|
||||
});
|
||||
};
|
||||
|
||||
onSelectSortOption = (sortOption) => {
|
||||
const [sortBy, sortOrder] = sortOption.value.split('-');
|
||||
this.setState({ sortBy, sortOrder }, () => {
|
||||
this.sortItems(sortBy, sortOrder);
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { errMessage, emptyTip, currentGroup, isDepartmentGroup, isMembersDialogOpen } = this.state;
|
||||
const {
|
||||
isLoading, repoList, errMessage, emptyTip,
|
||||
currentGroup, isDepartmentGroup, isMembersDialogOpen,
|
||||
currentViewMode, sortBy, sortOrder
|
||||
} = this.state;
|
||||
|
||||
let useRate = 0;
|
||||
if (isDepartmentGroup && currentGroup.group_quota) {
|
||||
@@ -409,11 +433,11 @@ class GroupView extends React.Component {
|
||||
<span>{currentGroup.name}</span>
|
||||
<SingleDropdownToolbar opList={opList} />
|
||||
</div>
|
||||
<div className="path-tool">
|
||||
<div className="path-tool d-flex align-items-center">
|
||||
{isDepartmentGroup && (
|
||||
<>
|
||||
{currentGroup.group_quota > 0 &&
|
||||
<div className="department-usage-container">
|
||||
<div className="department-usage-container mr-3">
|
||||
<div className="department-usage">
|
||||
<span id="quota-bar" className="department-quota-bar"><span id="quota-usage" className="usage" style={{ width: useRate }}></span></span>
|
||||
<span className="department-quota-info">{Utils.bytesToSize(currentGroup.group_quota_usage)} / {Utils.bytesToSize(currentGroup.group_quota)}</span>
|
||||
@@ -422,38 +446,54 @@ class GroupView extends React.Component {
|
||||
}
|
||||
</>
|
||||
)}
|
||||
{Utils.isDesktop() && (
|
||||
<div className="d-flex align-items-center">
|
||||
<div className="mr-2">
|
||||
<ViewModes currentViewMode={currentViewMode} switchViewMode={this.switchViewMode} />
|
||||
</div>
|
||||
<ReposSortMenu sortBy={sortBy} sortOrder={sortOrder} onSelectSortOption={this.onSelectSortOption}/>
|
||||
</div>
|
||||
)}
|
||||
{(!Utils.isDesktop() && this.state.repoList.length > 0) &&
|
||||
<span className="sf3-font sf3-font-sort action-icon" onClick={this.toggleSortOptionsDialog}></span>}
|
||||
{this.state.isSortOptionsDialogOpen &&
|
||||
<SortOptionsDialog
|
||||
toggleDialog={this.toggleSortOptionsDialog}
|
||||
sortBy={this.state.sortBy}
|
||||
sortOrder={this.state.sortOrder}
|
||||
sortBy={sortBy}
|
||||
sortOrder={sortOrder}
|
||||
sortItems={this.sortItems}
|
||||
toggleDialog={this.toggleSortOptionsDialog}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
</Fragment>
|
||||
)}
|
||||
</div>
|
||||
<div className="cur-view-content d-block" onScroll={this.handleScroll}>
|
||||
{this.state.isLoading && <Loading />}
|
||||
{(!this.state.isLoading && errMessage) && <div className="error text-center mt-2">{errMessage}</div>}
|
||||
{(!this.state.isLoading && this.state.repoList.length === 0) && emptyTip}
|
||||
{(!this.state.isLoading && this.state.repoList.length > 0) &&
|
||||
<SharedRepoListView
|
||||
repoList={this.state.repoList}
|
||||
hasNextPage={this.state.hasNextPage}
|
||||
currentGroup={this.state.currentGroup}
|
||||
sortBy={this.state.sortBy}
|
||||
sortOrder={this.state.sortOrder}
|
||||
sortItems={this.sortItems}
|
||||
onItemUnshare={this.onItemUnshare}
|
||||
onItemDelete={this.onItemDelete}
|
||||
onItemRename={this.onItemRename}
|
||||
onMonitorRepo={this.onMonitorRepo}
|
||||
onTransferRepo={this.onItemTransfer}
|
||||
/>
|
||||
<div
|
||||
className={classnames('cur-view-content', 'd-block', 'repos-container', { 'pt-3': currentViewMode != LIST_MODE })}
|
||||
onScroll={this.handleScroll}
|
||||
>
|
||||
{isLoading
|
||||
? <Loading />
|
||||
: errMessage
|
||||
? <p className="error text-center mt-2">{errMessage}</p>
|
||||
: repoList.length == 0
|
||||
? emptyTip
|
||||
: (
|
||||
<SharedRepoListView
|
||||
repoList={this.state.repoList}
|
||||
hasNextPage={this.state.hasNextPage}
|
||||
currentGroup={this.state.currentGroup}
|
||||
sortBy={this.state.sortBy}
|
||||
sortOrder={this.state.sortOrder}
|
||||
sortItems={this.sortItems}
|
||||
onItemUnshare={this.onItemUnshare}
|
||||
onItemDelete={this.onItemDelete}
|
||||
onItemRename={this.onItemRename}
|
||||
onMonitorRepo={this.onMonitorRepo}
|
||||
onTransferRepo={this.onItemTransfer}
|
||||
currentViewMode={currentViewMode}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -1,20 +1,20 @@
|
||||
import React, { Component } from 'react';
|
||||
import cookie from 'react-cookies';
|
||||
import { Utils } from '../../utils/utils';
|
||||
import { seafileAPI } from '../../utils/seafile-api';
|
||||
import { gettext, canAddRepo, canViewOrg } from '../../utils/constants';
|
||||
import { Utils } from '../../utils/utils';
|
||||
import toaster from '../../components/toast';
|
||||
import Repo from '../../models/repo';
|
||||
import Group from '../../models/group';
|
||||
import toaster from '../../components/toast';
|
||||
import Loading from '../../components/loading';
|
||||
import ViewModes from '../../components/view-modes';
|
||||
import ReposSortMenu from '../../components/repos-sort-menu';
|
||||
import ReposSortMenu from '../../components/sort-menu';
|
||||
import SingleDropdownToolbar from '../../components/toolbar/single-dropdown-toolbar';
|
||||
import SortOptionsDialog from '../../components/dialog/sort-options';
|
||||
import GuideForNewDialog from '../../components/dialog/guide-for-new-dialog';
|
||||
import CreateRepoDialog from '../../components/dialog/create-repo-dialog';
|
||||
import MylibRepoListView from '../../pages/my-libs/mylib-repo-list-view';
|
||||
import SharedLibraries from '../shared-libs/shared-libraries';
|
||||
import SharedLibraries from '../../pages/shared-libs';
|
||||
import SharedWithAll from '../../pages/shared-with-all';
|
||||
import GroupItem from '../../pages/groups/group-item';
|
||||
import { LIST_MODE } from '../../components/dir-view-mode/constants';
|
||||
@@ -24,13 +24,6 @@ import '../../css/files.css';
|
||||
class Libraries extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.sortOptions = [
|
||||
{ value: 'name-asc', text: gettext('By name ascending') },
|
||||
{ value: 'name-desc', text: gettext('By name descending') },
|
||||
{ value: 'time-asc', text: gettext('By time ascending') },
|
||||
{ value: 'time-desc', text: gettext('By time descending') }
|
||||
];
|
||||
|
||||
this.state = {
|
||||
// for 'my libs'
|
||||
errorMsg: '',
|
||||
@@ -250,12 +243,6 @@ class Libraries extends Component {
|
||||
const { isLoading, currentViewMode, sortBy, sortOrder, groupList } = this.state;
|
||||
const isDesktop = Utils.isDesktop();
|
||||
|
||||
const sortOptions = this.sortOptions.map(item => {
|
||||
return {
|
||||
...item,
|
||||
isSelected: item.value == `${sortBy}-${sortOrder}`
|
||||
};
|
||||
});
|
||||
return (
|
||||
<>
|
||||
<div className="main-panel-center flex-row">
|
||||
@@ -267,73 +254,75 @@ class Libraries extends Component {
|
||||
<div className="mr-2">
|
||||
<ViewModes currentViewMode={currentViewMode} switchViewMode={this.switchViewMode} />
|
||||
</div>
|
||||
<ReposSortMenu sortOptions={sortOptions} onSelectSortOption={this.onSelectSortOption}/>
|
||||
<ReposSortMenu sortBy={sortBy} sortOrder={sortOrder} onSelectSortOption={this.onSelectSortOption} />
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
{isLoading ?
|
||||
<Loading /> :
|
||||
<div className="cur-view-content" id="files-content-container">
|
||||
<div className="cur-view-content repos-container" id="files-content-container">
|
||||
{isLoading ? <Loading /> : (
|
||||
<>
|
||||
{(Utils.isDesktop() && currentViewMode == LIST_MODE) && (
|
||||
<table aria-hidden={true} className="my-3">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="4%"></th>
|
||||
<th width="3%"><span className="sr-only">{gettext('Library Type')}</span></th>
|
||||
<th width="35%">{gettext('Name')}</th>
|
||||
<th width="10%"><span className="sr-only">{gettext('Actions')}</span></th>
|
||||
<th width="14%">{gettext('Size')}</th>
|
||||
<th width="17%">{gettext('Last Update')}</th>
|
||||
<th width="17%">{gettext('Owner')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
)}
|
||||
|
||||
{(Utils.isDesktop() && currentViewMode == LIST_MODE) && (
|
||||
<table aria-hidden={true} className="my-3">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="4%"></th>
|
||||
<th width="3%"><span className="sr-only">{gettext('Library Type')}</span></th>
|
||||
<th width="35%">{gettext('Name')}</th>
|
||||
<th width="10%"><span className="sr-only">{gettext('Actions')}</span></th>
|
||||
<th width="14%">{gettext('Size')}</th>
|
||||
<th width="17%">{gettext('Last Update')}</th>
|
||||
<th width="17%">{gettext('Owner')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
)}
|
||||
|
||||
{canAddRepo && (
|
||||
<div className="pb-3">
|
||||
<div className={`d-flex justify-content-between mt-3 py-1 ${currentViewMode == LIST_MODE ? 'sf-border-bottom' : ''}`}>
|
||||
<h4 className="sf-heading m-0 d-flex align-items-center">
|
||||
<span className="sf3-font-mine sf3-font nav-icon" aria-hidden="true"></span>
|
||||
{gettext('My Libraries')}
|
||||
<SingleDropdownToolbar
|
||||
opList={[{ 'text': gettext('New Library'), 'onClick': this.toggleCreateRepoDialog }]}
|
||||
/>
|
||||
</h4>
|
||||
{(!Utils.isDesktop() && this.state.repoList.length > 0) &&
|
||||
<span className="sf3-font sf3-font-sort action-icon" onClick={this.toggleSortOptionsDialog}></span>
|
||||
{canAddRepo && (
|
||||
<div className="pb-3">
|
||||
<div className={`d-flex justify-content-between mt-3 py-1 ${currentViewMode == LIST_MODE ? 'sf-border-bottom' : ''}`}>
|
||||
<h4 className="sf-heading m-0 d-flex align-items-center">
|
||||
<span className="sf3-font-mine sf3-font nav-icon" aria-hidden="true"></span>
|
||||
{gettext('My Libraries')}
|
||||
<SingleDropdownToolbar
|
||||
opList={[{ 'text': gettext('New Library'), 'onClick': this.toggleCreateRepoDialog }]}
|
||||
/>
|
||||
</h4>
|
||||
{(!Utils.isDesktop() && this.state.repoList.length > 0) &&
|
||||
<span className="sf3-font sf3-font-sort action-icon" onClick={this.toggleSortOptionsDialog}></span>
|
||||
}
|
||||
</div>
|
||||
{this.state.errorMsg
|
||||
? <p className="error text-center mt-8">{this.state.errorMsg}</p>
|
||||
: this.state.repoList.length == 0
|
||||
? <p className={`libraries-empty-tip-in-${currentViewMode}-mode`}>{gettext('No libraries')}</p>
|
||||
: (
|
||||
<MylibRepoListView
|
||||
sortBy={this.state.sortBy}
|
||||
sortOrder={this.state.sortOrder}
|
||||
repoList={this.state.repoList}
|
||||
onRenameRepo={this.onRenameRepo}
|
||||
onDeleteRepo={this.onDeleteRepo}
|
||||
onTransferRepo={this.onTransferRepo}
|
||||
onMonitorRepo={this.onMonitorRepo}
|
||||
onRepoClick={this.onRepoClick}
|
||||
sortRepoList={this.sortRepoList}
|
||||
inAllLibs={true}
|
||||
currentViewMode={currentViewMode}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
{this.state.errorMsg ? <p className="error text-center mt-8">{this.state.errorMsg}</p> : (
|
||||
this.state.repoList.length === 0 ? (
|
||||
<p className={`libraries-empty-tip-in-${currentViewMode}-mode`}>{gettext('No libraries')}</p>
|
||||
) : (
|
||||
<MylibRepoListView
|
||||
sortBy={this.state.sortBy}
|
||||
sortOrder={this.state.sortOrder}
|
||||
repoList={this.state.repoList}
|
||||
onRenameRepo={this.onRenameRepo}
|
||||
onDeleteRepo={this.onDeleteRepo}
|
||||
onTransferRepo={this.onTransferRepo}
|
||||
onMonitorRepo={this.onMonitorRepo}
|
||||
onRepoClick={this.onRepoClick}
|
||||
sortRepoList={this.sortRepoList}
|
||||
inAllLibs={true}
|
||||
currentViewMode={currentViewMode}
|
||||
/>
|
||||
))
|
||||
}
|
||||
)}
|
||||
|
||||
<div className="pb-3">
|
||||
<SharedLibraries
|
||||
repoList={this.state.sharedRepoList}
|
||||
inAllLibs={true}
|
||||
currentViewMode={currentViewMode}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="pb-3">
|
||||
<SharedLibraries
|
||||
repoList={this.state.sharedRepoList}
|
||||
inAllLibs={true}
|
||||
currentViewMode={currentViewMode}
|
||||
/>
|
||||
</div>
|
||||
{canViewOrg &&
|
||||
|
||||
{canViewOrg &&
|
||||
<div className="pb-3">
|
||||
<SharedWithAll
|
||||
repoList={this.state.publicRepoList}
|
||||
@@ -341,8 +330,8 @@ class Libraries extends Component {
|
||||
currentViewMode={currentViewMode}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
<div className="repos-container">
|
||||
}
|
||||
|
||||
{groupList.length > 0 && groupList.map((group) => {
|
||||
return (
|
||||
<GroupItem
|
||||
@@ -355,9 +344,9 @@ class Libraries extends Component {
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{!isLoading && !this.state.errorMsg && this.state.isGuideForNewDialogOpen &&
|
||||
<GuideForNewDialog
|
||||
|
@@ -1,18 +1,22 @@
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import cookie from 'react-cookies';
|
||||
import classnames from 'classnames';
|
||||
import Repo from '../../models/repo';
|
||||
import { seafileAPI } from '../../utils/seafile-api';
|
||||
import { gettext } from '../../utils/constants';
|
||||
import { Utils } from '../../utils/utils';
|
||||
import toaster from '../../components/toast';
|
||||
import Repo from '../../models/repo';
|
||||
import Loading from '../../components/loading';
|
||||
import EmptyTip from '../../components/empty-tip';
|
||||
import MylibRepoListView from './mylib-repo-list-view';
|
||||
import SortOptionsDialog from '../../components/dialog/sort-options';
|
||||
import SingleDropdownToolbar from '../../components/toolbar/single-dropdown-toolbar';
|
||||
import ModalPortal from '../../components/modal-portal';
|
||||
import ViewModes from '../../components/view-modes';
|
||||
import ReposSortMenu from '../../components/sort-menu';
|
||||
import SingleDropdownToolbar from '../../components/toolbar/single-dropdown-toolbar';
|
||||
import SortOptionsDialog from '../../components/dialog/sort-options';
|
||||
import CreateRepoDialog from '../../components/dialog/create-repo-dialog';
|
||||
import DeletedReposDialog from '../../components/dialog/my-deleted-repos';
|
||||
import { LIST_MODE } from '../../components/dir-view-mode/constants';
|
||||
import MylibRepoListView from './mylib-repo-list-view';
|
||||
|
||||
class MyLibraries extends Component {
|
||||
constructor(props) {
|
||||
@@ -24,6 +28,7 @@ class MyLibraries extends Component {
|
||||
isDeletedReposDialogOpen: false,
|
||||
isCreateRepoDialogOpen: false,
|
||||
isSortOptionsDialogOpen: false,
|
||||
currentViewMode: localStorage.getItem('sf_repo_list_view_mode') || LIST_MODE,
|
||||
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'
|
||||
};
|
||||
@@ -135,7 +140,24 @@ class MyLibraries extends Component {
|
||||
});
|
||||
};
|
||||
|
||||
switchViewMode = (newMode) => {
|
||||
this.setState({
|
||||
currentViewMode: newMode
|
||||
}, () => {
|
||||
localStorage.setItem('sf_repo_list_view_mode', newMode);
|
||||
});
|
||||
};
|
||||
|
||||
onSelectSortOption = (sortOption) => {
|
||||
const [sortBy, sortOrder] = sortOption.value.split('-');
|
||||
this.setState({ sortBy, sortOrder }, () => {
|
||||
this.sortRepoList(sortBy, sortOrder);
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { isLoading, errorMsg, currentViewMode, sortBy, sortOrder, repoList } = this.state;
|
||||
const isDesktop = Utils.isDesktop();
|
||||
return (
|
||||
<Fragment>
|
||||
<div className="main-panel-center flex-row">
|
||||
@@ -150,33 +172,49 @@ class MyLibraries extends Component {
|
||||
]}
|
||||
/>
|
||||
</h3>
|
||||
<div>
|
||||
{(!Utils.isDesktop() && this.state.repoList.length > 0) && <span className="sf3-font sf3-font-sort action-icon" onClick={this.toggleSortOptionsDialog}></span>}
|
||||
</div>
|
||||
{isDesktop ? (
|
||||
<div className="d-flex align-items-center">
|
||||
<div className="mr-2">
|
||||
<ViewModes currentViewMode={currentViewMode} switchViewMode={this.switchViewMode} />
|
||||
</div>
|
||||
<ReposSortMenu sortBy={sortBy} sortOrder={sortOrder} onSelectSortOption={this.onSelectSortOption} />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{repoList.length > 0 &&
|
||||
<span className="sf3-font sf3-font-sort action-icon" onClick={this.toggleSortOptionsDialog}></span>
|
||||
}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className="cur-view-content">
|
||||
{this.state.isLoading && <Loading />}
|
||||
{!this.state.isLoading && this.state.errorMsg && <p className="error text-center mt-8">{this.state.errorMsg}</p>}
|
||||
{!this.state.isLoading && !this.state.errorMsg && this.state.repoList.length === 0 && this.emptyTip}
|
||||
{!this.state.isLoading && !this.state.errorMsg && this.state.repoList.length > 0 &&
|
||||
<MylibRepoListView
|
||||
sortBy={this.state.sortBy}
|
||||
sortOrder={this.state.sortOrder}
|
||||
repoList={this.state.repoList}
|
||||
onRenameRepo={this.onRenameRepo}
|
||||
onDeleteRepo={this.onDeleteRepo}
|
||||
onTransferRepo={this.onTransferRepo}
|
||||
onMonitorRepo={this.onMonitorRepo}
|
||||
sortRepoList={this.sortRepoList}
|
||||
/>
|
||||
<div className={classnames('cur-view-content', 'repos-container', { 'pt-3': currentViewMode != LIST_MODE })}>
|
||||
{isLoading
|
||||
? <Loading />
|
||||
: errorMsg
|
||||
? <p className="error text-center mt-8">{errorMsg}</p>
|
||||
: repoList.length == 0
|
||||
? this.emptyTip
|
||||
: (
|
||||
<MylibRepoListView
|
||||
sortBy={sortBy}
|
||||
sortOrder={sortOrder}
|
||||
repoList={this.state.repoList}
|
||||
onRenameRepo={this.onRenameRepo}
|
||||
onDeleteRepo={this.onDeleteRepo}
|
||||
onTransferRepo={this.onTransferRepo}
|
||||
onMonitorRepo={this.onMonitorRepo}
|
||||
sortRepoList={this.sortRepoList}
|
||||
currentViewMode={currentViewMode}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
{this.state.isSortOptionsDialogOpen &&
|
||||
<SortOptionsDialog
|
||||
toggleDialog={this.toggleSortOptionsDialog}
|
||||
sortBy={this.state.sortBy}
|
||||
sortOrder={this.state.sortOrder}
|
||||
sortBy={sortBy}
|
||||
sortOrder={sortOrder}
|
||||
sortItems={this.sortRepoList}
|
||||
/>
|
||||
}
|
||||
|
@@ -117,7 +117,7 @@ class MylibRepoListView extends React.Component {
|
||||
const sortIcon = this.props.sortOrder === 'asc' ? <span className="sf3-font sf3-font-down rotate-180 d-inline-block"></span> : <span className="sf3-font sf3-font-down"></span>;
|
||||
|
||||
return currentViewMode == LIST_MODE ? (
|
||||
<table className={classNames({ 'table-thead-hidden': inAllLibs }, { 'repos-container': !inAllLibs })}>
|
||||
<table className={classNames({ 'table-thead-hidden': inAllLibs })}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="4%"></th>
|
||||
|
177
frontend/src/pages/shared-libs/content.js
Normal file
177
frontend/src/pages/shared-libs/content.js
Normal file
@@ -0,0 +1,177 @@
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import { gettext } from '../../utils/constants';
|
||||
import { Utils } from '../../utils/utils';
|
||||
import Loading from '../../components/loading';
|
||||
import EmptyTip from '../../components/empty-tip';
|
||||
import LibsMobileThead from '../../components/libs-mobile-thead';
|
||||
import { LIST_MODE } from '../../components/dir-view-mode/constants';
|
||||
import ContextMenu from '../../components/context-menu/context-menu';
|
||||
import { hideMenu, handleContextClick } from '../../components/context-menu/actions';
|
||||
import Item from './item';
|
||||
|
||||
class Content extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isItemFreezed: false
|
||||
};
|
||||
this.libItems = [];
|
||||
}
|
||||
|
||||
freezeItem = (freezed) => {
|
||||
this.setState({
|
||||
isItemFreezed: freezed
|
||||
});
|
||||
};
|
||||
|
||||
sortByName = (e) => {
|
||||
e.preventDefault();
|
||||
const sortBy = 'name';
|
||||
const sortOrder = this.props.sortOrder == 'asc' ? 'desc' : 'asc';
|
||||
this.props.sortItems(sortBy, sortOrder);
|
||||
};
|
||||
|
||||
sortByTime = (e) => {
|
||||
e.preventDefault();
|
||||
const sortBy = 'time';
|
||||
const sortOrder = this.props.sortOrder == 'asc' ? 'desc' : 'asc';
|
||||
this.props.sortItems(sortBy, sortOrder);
|
||||
};
|
||||
|
||||
sortBySize = (e) => {
|
||||
e.preventDefault();
|
||||
const sortBy = 'size';
|
||||
const sortOrder = this.props.sortOrder == 'asc' ? 'desc' : 'asc';
|
||||
this.props.sortItems(sortBy, sortOrder);
|
||||
};
|
||||
|
||||
onContextMenu = (event, repo) => {
|
||||
event.preventDefault();
|
||||
const id = 'shared-libs-item-menu';
|
||||
const menuList = Utils.getSharedLibsOperationList(repo);
|
||||
handleContextClick(event, id, menuList, repo);
|
||||
};
|
||||
|
||||
setLibItemRef = (index) => item => {
|
||||
this.libItems[index] = item;
|
||||
};
|
||||
|
||||
getLibIndex = (lib) => {
|
||||
return this.props.items.findIndex(item => {
|
||||
return item.repo_id === lib.repo_id;
|
||||
});
|
||||
};
|
||||
|
||||
onMenuItemClick = (operation, currentObject, event) => {
|
||||
const index = this.getLibIndex(currentObject);
|
||||
this.libItems[index].onMenuItemClick(operation, event);
|
||||
|
||||
hideMenu();
|
||||
};
|
||||
|
||||
render() {
|
||||
const { loading, errorMsg, items, sortBy, sortOrder, inAllLibs, currentViewMode } = this.props;
|
||||
|
||||
const emptyTip = inAllLibs ?
|
||||
<p className={`libraries-empty-tip-in-${currentViewMode}-mode`}>{gettext('No shared libraries')}</p> :
|
||||
<EmptyTip
|
||||
title={gettext('No shared libraries')}
|
||||
text={gettext('No libraries have been shared directly with you. A shared library can be shared with full or restricted permission. If you need access to a library owned by another user, ask the user to share the library with you.')}
|
||||
>
|
||||
</EmptyTip>;
|
||||
|
||||
if (loading) {
|
||||
return <Loading />;
|
||||
} else if (errorMsg) {
|
||||
return <p className="error text-center">{errorMsg}</p>;
|
||||
} else {
|
||||
// sort
|
||||
const sortByName = sortBy == 'name';
|
||||
const sortByTime = sortBy == 'time';
|
||||
const sortBySize = sortBy == 'size';
|
||||
const sortIcon = sortOrder == 'asc' ? <span className="sf3-font sf3-font-down rotate-180 d-inline-block"></span> : <span className="sf3-font sf3-font-down"></span>;
|
||||
|
||||
const isDesktop = Utils.isDesktop();
|
||||
const itemsContent = (
|
||||
<>
|
||||
{items.map((item, index) => {
|
||||
return <Item
|
||||
ref={this.setLibItemRef(index)}
|
||||
key={index}
|
||||
data={item}
|
||||
isDesktop={isDesktop}
|
||||
isItemFreezed={this.state.isItemFreezed}
|
||||
freezeItem={this.freezeItem}
|
||||
onMonitorRepo={this.props.onMonitorRepo}
|
||||
currentViewMode={currentViewMode}
|
||||
onContextMenu={this.onContextMenu}
|
||||
/>;
|
||||
})}
|
||||
</>
|
||||
);
|
||||
|
||||
let content;
|
||||
if (isDesktop) {
|
||||
content = currentViewMode == LIST_MODE ? (
|
||||
<table className={classNames({ 'table-thead-hidden': inAllLibs })}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="4%"></th>
|
||||
<th width="3%"><span className="sr-only">{gettext('Library Type')}</span></th>
|
||||
<th width="35%"><a className="d-block table-sort-op" href="#" onClick={this.sortByName}>{gettext('Name')} {sortByName && sortIcon}</a></th>
|
||||
<th width="10%"><span className="sr-only">{gettext('Actions')}</span></th>
|
||||
<th width="14%"><a className="d-block table-sort-op" href="#" onClick={this.sortBySize}>{gettext('Size')} {sortBySize && sortIcon}</a></th>
|
||||
<th width="17%"><a className="d-block table-sort-op" href="#" onClick={this.sortByTime}>{gettext('Last Update')} {sortByTime && sortIcon}</a></th>
|
||||
<th width="17%">{gettext('Owner')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{itemsContent}
|
||||
</tbody>
|
||||
</table>
|
||||
) : (
|
||||
<div className="d-flex justify-content-between flex-wrap">
|
||||
{itemsContent}
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
// mobile
|
||||
content = (
|
||||
<table className="table-thead-hidden">
|
||||
{<LibsMobileThead inAllLibs={inAllLibs} />}
|
||||
<tbody>
|
||||
{itemsContent}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
|
||||
return items.length ? (
|
||||
<>
|
||||
{content}
|
||||
<ContextMenu
|
||||
id="shared-libs-item-menu"
|
||||
onMenuItemClick={this.onMenuItemClick}
|
||||
/>
|
||||
</>
|
||||
) : emptyTip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Content.propTypes = {
|
||||
currentViewMode: PropTypes.string,
|
||||
inAllLibs: PropTypes.bool.isRequired,
|
||||
loading: PropTypes.bool.isRequired,
|
||||
errorMsg: PropTypes.string.isRequired,
|
||||
items: PropTypes.array.isRequired,
|
||||
sortBy: PropTypes.string.isRequired,
|
||||
sortOrder: PropTypes.string.isRequired,
|
||||
sortItems: PropTypes.func.isRequired,
|
||||
onMonitorRepo: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default Content;
|
178
frontend/src/pages/shared-libs/index.js
Normal file
178
frontend/src/pages/shared-libs/index.js
Normal file
@@ -0,0 +1,178 @@
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classnames from 'classnames';
|
||||
import cookie from 'react-cookies';
|
||||
import Repo from '../../models/repo';
|
||||
import { gettext } from '../../utils/constants';
|
||||
import { seafileAPI } from '../../utils/seafile-api';
|
||||
import { Utils } from '../../utils/utils';
|
||||
import ViewModes from '../../components/view-modes';
|
||||
import ReposSortMenu from '../../components/sort-menu';
|
||||
import SortOptionsDialog from '../../components/dialog/sort-options';
|
||||
import { LIST_MODE } from '../../components/dir-view-mode/constants';
|
||||
import Content from './content';
|
||||
|
||||
class SharedLibraries extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
loading: true,
|
||||
errorMsg: '',
|
||||
items: [],
|
||||
currentViewMode: localStorage.getItem('sf_repo_list_view_mode') || LIST_MODE,
|
||||
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'
|
||||
isSortOptionsDialogOpen: false
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (!this.props.repoList) {
|
||||
seafileAPI.listRepos({ type: 'shared' }).then((res) => {
|
||||
let repoList = res.data.repos.map((item) => {
|
||||
return new Repo(item);
|
||||
});
|
||||
this.setState({
|
||||
loading: false,
|
||||
items: Utils.sortRepos(repoList, this.state.sortBy, this.state.sortOrder)
|
||||
});
|
||||
}).catch((error) => {
|
||||
this.setState({
|
||||
loading: false,
|
||||
errorMsg: Utils.getErrorMsg(error, true)
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
loading: false,
|
||||
items: this.props.repoList
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
sortItems = (sortBy, sortOrder) => {
|
||||
cookie.save('seafile-repo-dir-sort-by', sortBy);
|
||||
cookie.save('seafile-repo-dir-sort-order', sortOrder);
|
||||
this.setState({
|
||||
sortBy: sortBy,
|
||||
sortOrder: sortOrder,
|
||||
items: Utils.sortRepos(this.state.items, sortBy, sortOrder)
|
||||
});
|
||||
};
|
||||
|
||||
toggleSortOptionsDialog = () => {
|
||||
this.setState({
|
||||
isSortOptionsDialogOpen: !this.state.isSortOptionsDialogOpen
|
||||
});
|
||||
};
|
||||
|
||||
onMonitorRepo = (repo, monitored) => {
|
||||
let items = this.state.items.map(item => {
|
||||
if (item.repo_id === repo.repo_id) {
|
||||
item.monitored = monitored;
|
||||
}
|
||||
return item;
|
||||
});
|
||||
this.setState({ items: items });
|
||||
};
|
||||
|
||||
renderContent = (currentViewMode) => {
|
||||
const { inAllLibs = false, repoList } = this.props; // inAllLibs: in 'All Libs'('Files') page
|
||||
const { items } = this.state;
|
||||
return (
|
||||
<Content
|
||||
loading={this.state.loading}
|
||||
errorMsg={this.state.errorMsg}
|
||||
items={inAllLibs ? repoList : items}
|
||||
sortBy={this.state.sortBy}
|
||||
sortOrder={this.state.sortOrder}
|
||||
sortItems={this.sortItems}
|
||||
onMonitorRepo={this.onMonitorRepo}
|
||||
inAllLibs={inAllLibs}
|
||||
currentViewMode={currentViewMode}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
renderSortIconInMobile = () => {
|
||||
return (
|
||||
<>
|
||||
{(!Utils.isDesktop() && this.state.items.length > 0) && <span className="sf3-font sf3-font-sort action-icon" onClick={this.toggleSortOptionsDialog}></span>}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
switchViewMode = (newMode) => {
|
||||
this.setState({
|
||||
currentViewMode: newMode
|
||||
}, () => {
|
||||
localStorage.setItem('sf_repo_list_view_mode', newMode);
|
||||
});
|
||||
};
|
||||
|
||||
onSelectSortOption = (sortOption) => {
|
||||
const [sortBy, sortOrder] = sortOption.value.split('-');
|
||||
this.setState({ sortBy, sortOrder }, () => {
|
||||
this.sortItems(sortBy, sortOrder);
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { inAllLibs = false, currentViewMode: propCurrentViewMode } = this.props; // inAllLibs: in 'All Libs'('Files') page
|
||||
const { sortBy, sortOrder, currentViewMode: stateCurrentViewMode } = this.state;
|
||||
const currentViewMode = inAllLibs ? propCurrentViewMode : stateCurrentViewMode;
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{inAllLibs ? (
|
||||
<>
|
||||
<div className={`d-flex justify-content-between mt-3 py-1 ${currentViewMode == LIST_MODE ? 'sf-border-bottom' : ''}`}>
|
||||
<h4 className="sf-heading m-0">
|
||||
<span className="sf3-font-share-with-me sf3-font nav-icon" aria-hidden="true"></span>
|
||||
{gettext('Shared with me')}
|
||||
</h4>
|
||||
{this.renderSortIconInMobile()}
|
||||
</div>
|
||||
{this.renderContent(currentViewMode)}
|
||||
</>
|
||||
) : (
|
||||
<div className="main-panel-center">
|
||||
<div className="cur-view-container">
|
||||
<div className="cur-view-path">
|
||||
<h3 className="sf-heading m-0">{gettext('Shared with me')}</h3>
|
||||
{Utils.isDesktop() && (
|
||||
<div className="d-flex align-items-center">
|
||||
<div className="mr-2">
|
||||
<ViewModes currentViewMode={currentViewMode} switchViewMode={this.switchViewMode} />
|
||||
</div>
|
||||
<ReposSortMenu sortBy={sortBy} sortOrder={sortOrder} onSelectSortOption={this.onSelectSortOption}/>
|
||||
</div>
|
||||
)}
|
||||
{this.renderSortIconInMobile()}
|
||||
</div>
|
||||
<div className={classnames('cur-view-content', 'repos-container', { 'pt-3': currentViewMode != LIST_MODE })}>
|
||||
{this.renderContent(currentViewMode)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{this.state.isSortOptionsDialogOpen &&
|
||||
<SortOptionsDialog
|
||||
toggleDialog={this.toggleSortOptionsDialog}
|
||||
sortBy={this.state.sortBy}
|
||||
sortOrder={this.state.sortOrder}
|
||||
sortItems={this.sortItems}
|
||||
/>
|
||||
}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
SharedLibraries.propTypes = {
|
||||
currentViewMode: PropTypes.string,
|
||||
inAllLibs: PropTypes.bool,
|
||||
repoList: PropTypes.array,
|
||||
};
|
||||
|
||||
export default SharedLibraries;
|
@@ -2,184 +2,19 @@ import React, { Component, Fragment } from 'react';
|
||||
import { Dropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap';
|
||||
import PropTypes from 'prop-types';
|
||||
import dayjs from 'dayjs';
|
||||
import classNames from 'classnames';
|
||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||
import cookie from 'react-cookies';
|
||||
import { Link, navigate } from '@gatsbyjs/reach-router';
|
||||
import { gettext, siteRoot, isPro } from '../../utils/constants';
|
||||
import { seafileAPI } from '../../utils/seafile-api';
|
||||
import { Utils } from '../../utils/utils';
|
||||
import toaster from '../../components/toast';
|
||||
import Repo from '../../models/repo';
|
||||
import Loading from '../../components/loading';
|
||||
import EmptyTip from '../../components/empty-tip';
|
||||
import LibsMobileThead from '../../components/libs-mobile-thead';
|
||||
import ModalPortal from '../../components/modal-portal';
|
||||
import ShareDialog from '../../components/dialog/share-dialog';
|
||||
import SortOptionsDialog from '../../components/dialog/sort-options';
|
||||
import RepoMonitoredIcon from '../../components/repo-monitored-icon';
|
||||
import { GRID_MODE, LIST_MODE } from '../../components/dir-view-mode/constants';
|
||||
import ContextMenu from '../../components/context-menu/context-menu';
|
||||
import { hideMenu, handleContextClick } from '../../components/context-menu/actions';
|
||||
|
||||
dayjs.extend(relativeTime);
|
||||
|
||||
class Content extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isItemFreezed: false
|
||||
};
|
||||
this.libItems = [];
|
||||
}
|
||||
|
||||
freezeItem = (freezed) => {
|
||||
this.setState({
|
||||
isItemFreezed: freezed
|
||||
});
|
||||
};
|
||||
|
||||
sortByName = (e) => {
|
||||
e.preventDefault();
|
||||
const sortBy = 'name';
|
||||
const sortOrder = this.props.sortOrder == 'asc' ? 'desc' : 'asc';
|
||||
this.props.sortItems(sortBy, sortOrder);
|
||||
};
|
||||
|
||||
sortByTime = (e) => {
|
||||
e.preventDefault();
|
||||
const sortBy = 'time';
|
||||
const sortOrder = this.props.sortOrder == 'asc' ? 'desc' : 'asc';
|
||||
this.props.sortItems(sortBy, sortOrder);
|
||||
};
|
||||
|
||||
sortBySize = (e) => {
|
||||
e.preventDefault();
|
||||
const sortBy = 'size';
|
||||
const sortOrder = this.props.sortOrder == 'asc' ? 'desc' : 'asc';
|
||||
this.props.sortItems(sortBy, sortOrder);
|
||||
};
|
||||
|
||||
onContextMenu = (event, repo) => {
|
||||
event.preventDefault();
|
||||
const id = 'shared-libs-item-menu';
|
||||
const menuList = Utils.getSharedLibsOperationList(repo);
|
||||
handleContextClick(event, id, menuList, repo);
|
||||
};
|
||||
|
||||
setLibItemRef = (index) => item => {
|
||||
this.libItems[index] = item;
|
||||
};
|
||||
|
||||
getLibIndex = (lib) => {
|
||||
return this.props.items.findIndex(item => {
|
||||
return item.repo_id === lib.repo_id;
|
||||
});
|
||||
};
|
||||
|
||||
onMenuItemClick = (operation, currentObject, event) => {
|
||||
const index = this.getLibIndex(currentObject);
|
||||
this.libItems[index].onMenuItemClick(operation, event);
|
||||
|
||||
hideMenu();
|
||||
};
|
||||
|
||||
render() {
|
||||
const { loading, errorMsg, items, sortBy, sortOrder, theadHidden, inAllLibs, currentViewMode } = this.props;
|
||||
|
||||
const emptyTip = inAllLibs ?
|
||||
<p className={`libraries-empty-tip-in-${currentViewMode}-mode`}>{gettext('No shared libraries')}</p> :
|
||||
<EmptyTip
|
||||
title={gettext('No shared libraries')}
|
||||
text={gettext('No libraries have been shared directly with you. A shared library can be shared with full or restricted permission. If you need access to a library owned by another user, ask the user to share the library with you.')}
|
||||
>
|
||||
</EmptyTip>;
|
||||
|
||||
if (loading) {
|
||||
return <Loading />;
|
||||
} else if (errorMsg) {
|
||||
return <p className="error text-center">{errorMsg}</p>;
|
||||
} else {
|
||||
// sort
|
||||
const sortByName = sortBy == 'name';
|
||||
const sortByTime = sortBy == 'time';
|
||||
const sortBySize = sortBy == 'size';
|
||||
const sortIcon = sortOrder == 'asc' ? <span className="sf3-font sf3-font-down rotate-180 d-inline-block"></span> : <span className="sf3-font sf3-font-down"></span>;
|
||||
|
||||
const desktopThead = (
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="4%"></th>
|
||||
<th width="3%"><span className="sr-only">{gettext('Library Type')}</span></th>
|
||||
<th width="35%"><a className="d-block table-sort-op" href="#" onClick={this.sortByName}>{gettext('Name')} {sortByName && sortIcon}</a></th>
|
||||
<th width="10%"><span className="sr-only">{gettext('Actions')}</span></th>
|
||||
<th width="14%"><a className="d-block table-sort-op" href="#" onClick={this.sortBySize}>{gettext('Size')} {sortBySize && sortIcon}</a></th>
|
||||
<th width="17%"><a className="d-block table-sort-op" href="#" onClick={this.sortByTime}>{gettext('Last Update')} {sortByTime && sortIcon}</a></th>
|
||||
<th width="17%">{gettext('Owner')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
);
|
||||
|
||||
const isDesktop = Utils.isDesktop();
|
||||
const itemsContent = (
|
||||
<>
|
||||
{items.map((item, index) => {
|
||||
return <Item
|
||||
ref={this.setLibItemRef(index)}
|
||||
key={index}
|
||||
data={item}
|
||||
isDesktop={isDesktop}
|
||||
isItemFreezed={this.state.isItemFreezed}
|
||||
freezeItem={this.freezeItem}
|
||||
onMonitorRepo={this.props.onMonitorRepo}
|
||||
currentViewMode={currentViewMode}
|
||||
onContextMenu={this.onContextMenu}
|
||||
/>;
|
||||
})}
|
||||
</>
|
||||
);
|
||||
const content = currentViewMode == LIST_MODE ? (
|
||||
<>
|
||||
<table className={classNames({ 'repos-container': !inAllLibs }, { 'table-thead-hidden': !(isDesktop && !theadHidden) })}>
|
||||
{isDesktop ? desktopThead : <LibsMobileThead inAllLibs={inAllLibs} />}
|
||||
<tbody>
|
||||
{itemsContent}
|
||||
</tbody>
|
||||
</table>
|
||||
</>
|
||||
) : (
|
||||
<div className="d-flex justify-content-between flex-wrap">
|
||||
{itemsContent}
|
||||
</div>
|
||||
);
|
||||
|
||||
return items.length ? (
|
||||
<>
|
||||
{content}
|
||||
<ContextMenu
|
||||
id="shared-libs-item-menu"
|
||||
onMenuItemClick={this.onMenuItemClick}
|
||||
/>
|
||||
</>
|
||||
) : emptyTip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Content.propTypes = {
|
||||
currentViewMode: PropTypes.string,
|
||||
inAllLibs: PropTypes.bool.isRequired,
|
||||
theadHidden: PropTypes.bool.isRequired,
|
||||
loading: PropTypes.bool.isRequired,
|
||||
errorMsg: PropTypes.string.isRequired,
|
||||
items: PropTypes.array.isRequired,
|
||||
sortBy: PropTypes.string.isRequired,
|
||||
sortOrder: PropTypes.string.isRequired,
|
||||
sortItems: PropTypes.func.isRequired,
|
||||
onMonitorRepo: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
class Item extends Component {
|
||||
|
||||
constructor(props) {
|
||||
@@ -530,141 +365,4 @@ Item.propTypes = {
|
||||
onContextMenu: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
class SharedLibraries extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
loading: true,
|
||||
errorMsg: '',
|
||||
items: [],
|
||||
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'
|
||||
isSortOptionsDialogOpen: false
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (!this.props.repoList) {
|
||||
seafileAPI.listRepos({ type: 'shared' }).then((res) => {
|
||||
let repoList = res.data.repos.map((item) => {
|
||||
return new Repo(item);
|
||||
});
|
||||
this.setState({
|
||||
loading: false,
|
||||
items: Utils.sortRepos(repoList, this.state.sortBy, this.state.sortOrder)
|
||||
});
|
||||
}).catch((error) => {
|
||||
this.setState({
|
||||
loading: false,
|
||||
errorMsg: Utils.getErrorMsg(error, true)
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
loading: false,
|
||||
items: this.props.repoList
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
sortItems = (sortBy, sortOrder) => {
|
||||
cookie.save('seafile-repo-dir-sort-by', sortBy);
|
||||
cookie.save('seafile-repo-dir-sort-order', sortOrder);
|
||||
this.setState({
|
||||
sortBy: sortBy,
|
||||
sortOrder: sortOrder,
|
||||
items: Utils.sortRepos(this.state.items, sortBy, sortOrder)
|
||||
});
|
||||
};
|
||||
|
||||
toggleSortOptionsDialog = () => {
|
||||
this.setState({
|
||||
isSortOptionsDialogOpen: !this.state.isSortOptionsDialogOpen
|
||||
});
|
||||
};
|
||||
|
||||
onMonitorRepo = (repo, monitored) => {
|
||||
let items = this.state.items.map(item => {
|
||||
if (item.repo_id === repo.repo_id) {
|
||||
item.monitored = monitored;
|
||||
}
|
||||
return item;
|
||||
});
|
||||
this.setState({ items: items });
|
||||
};
|
||||
|
||||
renderContent = () => {
|
||||
const { inAllLibs = false, currentViewMode = LIST_MODE, repoList } = this.props; // inAllLibs: in 'All Libs'('Files') page
|
||||
const { items } = this.state;
|
||||
return (
|
||||
<Content
|
||||
loading={this.state.loading}
|
||||
errorMsg={this.state.errorMsg}
|
||||
items={inAllLibs ? repoList : items}
|
||||
sortBy={this.state.sortBy}
|
||||
sortOrder={this.state.sortOrder}
|
||||
sortItems={this.sortItems}
|
||||
onMonitorRepo={this.onMonitorRepo}
|
||||
theadHidden={inAllLibs}
|
||||
inAllLibs={inAllLibs}
|
||||
currentViewMode={currentViewMode}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
renderSortIconInMobile = () => {
|
||||
return (
|
||||
<>
|
||||
{(!Utils.isDesktop() && this.state.items.length > 0) && <span className="sf3-font sf3-font-sort action-icon" onClick={this.toggleSortOptionsDialog}></span>}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { inAllLibs = false, currentViewMode = LIST_MODE } = this.props; // inAllLibs: in 'All Libs'('Files') page
|
||||
return (
|
||||
<Fragment>
|
||||
{inAllLibs ? (
|
||||
<>
|
||||
<div className={`d-flex justify-content-between mt-3 py-1 ${currentViewMode == LIST_MODE ? 'sf-border-bottom' : ''}`}>
|
||||
<h4 className="sf-heading m-0">
|
||||
<span className="sf3-font-share-with-me sf3-font nav-icon" aria-hidden="true"></span>
|
||||
{gettext('Shared with me')}
|
||||
</h4>
|
||||
{this.renderSortIconInMobile()}
|
||||
</div>
|
||||
{this.renderContent()}
|
||||
</>
|
||||
) : (
|
||||
<div className="main-panel-center">
|
||||
<div className="cur-view-container">
|
||||
<div className="cur-view-path">
|
||||
<h3 className="sf-heading m-0">{gettext('Shared with me')}</h3>
|
||||
{this.renderSortIconInMobile()}
|
||||
</div>
|
||||
<div className="cur-view-content">
|
||||
{this.renderContent()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{this.state.isSortOptionsDialogOpen &&
|
||||
<SortOptionsDialog
|
||||
toggleDialog={this.toggleSortOptionsDialog}
|
||||
sortBy={this.state.sortBy}
|
||||
sortOrder={this.state.sortOrder}
|
||||
sortItems={this.sortItems}
|
||||
/>
|
||||
}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
SharedLibraries.propTypes = {
|
||||
currentViewMode: PropTypes.string,
|
||||
inAllLibs: PropTypes.bool,
|
||||
repoList: PropTypes.array,
|
||||
};
|
||||
|
||||
export default SharedLibraries;
|
||||
export default Item;
|
@@ -1,6 +1,7 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import cookie from 'react-cookies';
|
||||
import classnames from 'classnames';
|
||||
import { seafileAPI } from '../../utils/seafile-api';
|
||||
import { gettext, canAddPublicRepo } from '../../utils/constants';
|
||||
import { Utils } from '../../utils/utils';
|
||||
@@ -15,6 +16,8 @@ import ModalPortal from '../../components/modal-portal';
|
||||
import CreateRepoDialog from '../../components/dialog/create-repo-dialog';
|
||||
import ShareRepoDialog from '../../components/dialog/share-repo-dialog';
|
||||
import { LIST_MODE } from '../../components/dir-view-mode/constants';
|
||||
import ViewModes from '../../components/view-modes';
|
||||
import ReposSortMenu from '../../components/sort-menu';
|
||||
|
||||
const propTypes = {
|
||||
currentViewMode: PropTypes.string,
|
||||
@@ -32,6 +35,7 @@ class SharedWithAll extends React.Component {
|
||||
repoList: [],
|
||||
isCreateRepoDialogOpen: false,
|
||||
isSelectRepoDialogOpen: false,
|
||||
currentViewMode: localStorage.getItem('sf_repo_list_view_mode') || LIST_MODE,
|
||||
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'
|
||||
isSortOptionsDialogOpen: false,
|
||||
@@ -120,11 +124,12 @@ class SharedWithAll extends React.Component {
|
||||
});
|
||||
};
|
||||
|
||||
renderContent = () => {
|
||||
const { inAllLibs = false, currentViewMode = LIST_MODE } = this.props; // inAllLibs: in 'All Libs'('Files') page
|
||||
const { errMessage } = this.state;
|
||||
const emptyTip = inAllLibs ?
|
||||
<p className={`libraries-empty-tip-in-${currentViewMode}-mode`}>{gettext('No public libraries')}</p> : (
|
||||
renderContent = (currentViewMode) => {
|
||||
const { inAllLibs = false } = this.props; // inAllLibs: in 'All Libs'('Files') page
|
||||
const { isLoading, errMessage, repoList } = this.state;
|
||||
const emptyTip = inAllLibs
|
||||
? <p className={`libraries-empty-tip-in-${currentViewMode}-mode`}>{gettext('No public libraries')}</p>
|
||||
: (
|
||||
<EmptyTip
|
||||
title={gettext('No public libraries')}
|
||||
text={gettext('No public libraries have been created yet. A public library is accessible by all users. You can create a public library by clicking the "Add Library" item in the dropdown menu.')}
|
||||
@@ -133,24 +138,26 @@ class SharedWithAll extends React.Component {
|
||||
);
|
||||
return (
|
||||
<>
|
||||
{this.state.isLoading && <Loading />}
|
||||
{(!this.state.isLoading && errMessage) && errMessage}
|
||||
{(!this.state.isLoading && this.state.repoList.length === 0) && emptyTip}
|
||||
{(!this.state.isLoading && this.state.repoList.length > 0) &&
|
||||
<SharedRepoListView
|
||||
key='public-shared-view'
|
||||
libraryType={this.state.libraryType}
|
||||
repoList={this.state.repoList}
|
||||
sortBy={this.state.sortBy}
|
||||
sortOrder={this.state.sortOrder}
|
||||
sortItems={this.sortItems}
|
||||
onItemUnshare={this.onItemUnshare}
|
||||
onItemDelete={this.onItemDelete}
|
||||
theadHidden={inAllLibs}
|
||||
currentViewMode={currentViewMode}
|
||||
inAllLibs={inAllLibs}
|
||||
/>
|
||||
}
|
||||
{isLoading
|
||||
? <Loading />
|
||||
: errMessage
|
||||
? <p className="error text-center">{errMessage}</p>
|
||||
: repoList.length == 0
|
||||
? emptyTip
|
||||
: (
|
||||
<SharedRepoListView
|
||||
key='public-shared-view'
|
||||
libraryType={this.state.libraryType}
|
||||
repoList={this.state.repoList}
|
||||
sortBy={this.state.sortBy}
|
||||
sortOrder={this.state.sortOrder}
|
||||
sortItems={this.sortItems}
|
||||
onItemUnshare={this.onItemUnshare}
|
||||
onItemDelete={this.onItemDelete}
|
||||
currentViewMode={currentViewMode}
|
||||
inAllLibs={inAllLibs}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -203,8 +210,25 @@ class SharedWithAll extends React.Component {
|
||||
});
|
||||
};
|
||||
|
||||
switchViewMode = (newMode) => {
|
||||
this.setState({
|
||||
currentViewMode: newMode
|
||||
}, () => {
|
||||
localStorage.setItem('sf_repo_list_view_mode', newMode);
|
||||
});
|
||||
};
|
||||
|
||||
onSelectSortOption = (sortOption) => {
|
||||
const [sortBy, sortOrder] = sortOption.value.split('-');
|
||||
this.setState({ sortBy, sortOrder }, () => {
|
||||
this.sortItems(sortBy, sortOrder);
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { inAllLibs = false, currentViewMode = LIST_MODE } = this.props; // inAllLibs: in 'All Libs'('Files') page
|
||||
const { inAllLibs = false, currentViewMode: propCurrentViewMode } = this.props; // inAllLibs: in 'All Libs'('Files') page
|
||||
const { sortBy, sortOrder, currentViewMode: stateCurrentViewMode } = this.state;
|
||||
const currentViewMode = inAllLibs ? propCurrentViewMode : stateCurrentViewMode;
|
||||
|
||||
if (inAllLibs) {
|
||||
return (
|
||||
@@ -216,7 +240,7 @@ class SharedWithAll extends React.Component {
|
||||
</h4>
|
||||
{this.renderSortIconInMobile()}
|
||||
</div>
|
||||
{this.renderContent()}
|
||||
{this.renderContent(currentViewMode)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -237,10 +261,18 @@ class SharedWithAll extends React.Component {
|
||||
/>
|
||||
}
|
||||
</h3>
|
||||
{Utils.isDesktop() && (
|
||||
<div className="d-flex align-items-center">
|
||||
<div className="mr-2">
|
||||
<ViewModes currentViewMode={currentViewMode} switchViewMode={this.switchViewMode} />
|
||||
</div>
|
||||
<ReposSortMenu sortBy={sortBy} sortOrder={sortOrder} onSelectSortOption={this.onSelectSortOption}/>
|
||||
</div>
|
||||
)}
|
||||
{this.renderSortIconInMobile()}
|
||||
</div>
|
||||
<div className="cur-view-content">
|
||||
{this.renderContent()}
|
||||
<div className={classnames('cur-view-content', 'repos-container', { 'pt-3': currentViewMode != LIST_MODE })}>
|
||||
{this.renderContent(currentViewMode)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
Reference in New Issue
Block a user