1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-11 11:51:27 +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:
llj
2024-11-25 10:23:42 +08:00
committed by GitHub
parent e7a4e29239
commit bacc9e2aa5
13 changed files with 653 additions and 505 deletions

View File

@@ -24,7 +24,7 @@ import ShareAdminLibraries from './pages/share-admin/libraries';
import ShareAdminFolders from './pages/share-admin/folders'; import ShareAdminFolders from './pages/share-admin/folders';
import ShareAdminShareLinks from './pages/share-admin/share-links'; import ShareAdminShareLinks from './pages/share-admin/share-links';
import ShareAdminUploadLinks from './pages/share-admin/upload-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 ShareWithOCM from './pages/share-with-ocm/shared-with-ocm';
import OCMViaWebdav from './pages/ocm-via-webdav/ocm-via-webdav'; import OCMViaWebdav from './pages/ocm-via-webdav/ocm-via-webdav';
import OCMRepoDir from './pages/share-with-ocm/remote-dir-view'; import OCMRepoDir from './pages/share-with-ocm/remote-dir-view';

View File

@@ -7,7 +7,7 @@ import TextTranslation from '../../utils/text-translation';
import SeahubPopover from '../common/seahub-popover'; import SeahubPopover from '../common/seahub-popover';
import ListTagPopover from '../popover/list-tag-popover'; import ListTagPopover from '../popover/list-tag-popover';
import ViewModes from '../../components/view-modes'; 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 MetadataViewToolBar from '../../metadata/components/view-toolbar';
import { PRIVATE_FILE_TYPE } from '../../constants'; import { PRIVATE_FILE_TYPE } from '../../constants';
import { DIRENT_DETAIL_MODE } from '../dir-view-mode/constants'; import { DIRENT_DETAIL_MODE } from '../dir-view-mode/constants';
@@ -36,13 +36,6 @@ class DirTool extends React.Component {
isRepoTagDialogOpen: false, isRepoTagDialogOpen: false,
isDropdownMenuOpen: 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 = () => { toggleDropdownMenu = () => {
@@ -111,13 +104,6 @@ class DirTool extends React.Component {
const isFileExtended = currentPath.startsWith('/' + PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES + '/'); const isFileExtended = currentPath.startsWith('/' + PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES + '/');
const isTagView = currentPath.startsWith('/' + PRIVATE_FILE_TYPE.TAGS_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) { if (isFileExtended) {
return ( return (
<div className="dir-tool"> <div className="dir-tool">
@@ -142,7 +128,7 @@ class DirTool extends React.Component {
<React.Fragment> <React.Fragment>
<div className="dir-tool d-flex"> <div className="dir-tool d-flex">
<ViewModes currentViewMode={currentMode} switchViewMode={this.props.switchViewMode} /> <ViewModes currentViewMode={currentMode} switchViewMode={this.props.switchViewMode} />
<ReposSortMenu sortOptions={sortOptions} onSelectSortOption={this.onSelectSortOption}/> <SortMenu sortBy={sortBy} sortOrder={sortOrder} onSelectSortOption={this.onSelectSortOption} />
{(!isCustomPermission) && {(!isCustomPermission) &&
<div className="cur-view-path-btn" onClick={this.showDirentDetail}> <div className="cur-view-path-btn" onClick={this.showDirentDetail}>
<span className="sf3-font sf3-font-info" aria-label={propertiesText} title={propertiesText}></span> <span className="sf3-font sf3-font-info" aria-label={propertiesText} title={propertiesText}></span>

View File

@@ -25,7 +25,6 @@ const propTypes = {
onItemRename: PropTypes.func, onItemRename: PropTypes.func,
hasNextPage: PropTypes.bool, hasNextPage: PropTypes.bool,
onMonitorRepo: PropTypes.func, onMonitorRepo: PropTypes.func,
theadHidden: PropTypes.bool,
inAllLibs: PropTypes.bool, inAllLibs: PropTypes.bool,
onTransferRepo: PropTypes.func, onTransferRepo: PropTypes.func,
}; };
@@ -119,7 +118,7 @@ class SharedRepoListView extends React.Component {
}; };
renderRepoListView = () => { renderRepoListView = () => {
const { currentViewMode = LIST_MODE } = this.props; const { currentViewMode } = this.props;
return ( return (
<Fragment> <Fragment>
{this.props.repoList.map((repo, index) => { {this.props.repoList.map((repo, index) => {
@@ -148,12 +147,11 @@ class SharedRepoListView extends React.Component {
}; };
renderPCUI = () => { 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 { sortByName, sortByTime, sortBySize, sortIcon } = this.getSortMetaData();
const content = currentViewMode == LIST_MODE ? ( const content = currentViewMode == LIST_MODE ? (
<> <table className={classNames({ 'table-thead-hidden': inAllLibs })}>
<table className={classNames({ 'table-thead-hidden': theadHidden }, { 'repos-container': !inAllLibs })}>
<thead> <thead>
<tr> <tr>
<th width="4%"></th> <th width="4%"></th>
@@ -169,7 +167,6 @@ class SharedRepoListView extends React.Component {
{this.renderRepoListView()} {this.renderRepoListView()}
</tbody> </tbody>
</table> </table>
</>
) : ( ) : (
<div className="d-flex justify-content-between flex-wrap"> <div className="d-flex justify-content-between flex-wrap">
{this.renderRepoListView()} {this.renderRepoListView()}

View File

@@ -4,14 +4,21 @@ import { Dropdown, DropdownMenu, DropdownToggle, DropdownItem } from 'reactstrap
import { gettext } from '../utils/constants'; import { gettext } from '../utils/constants';
const propTypes = { const propTypes = {
sortOptions: PropTypes.array, sortBy: PropTypes.string,
sortOrder: PropTypes.string,
onSelectSortOption: PropTypes.func.isRequired onSelectSortOption: PropTypes.func.isRequired
}; };
class ReposSortMenu extends React.Component { class SortMenu extends React.Component {
constructor(props) { constructor(props) {
super(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 = { this.state = {
isDropdownMenuOpen: false isDropdownMenuOpen: false
}; };
@@ -25,7 +32,13 @@ class ReposSortMenu extends React.Component {
render() { render() {
const { isDropdownMenuOpen } = this.state; 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 ( return (
<Dropdown <Dropdown
isOpen={isDropdownMenuOpen} isOpen={isDropdownMenuOpen}
@@ -59,6 +72,6 @@ class ReposSortMenu extends React.Component {
} }
ReposSortMenu.propTypes = propTypes; SortMenu.propTypes = propTypes;
export default ReposSortMenu; export default SortMenu;

View File

@@ -169,7 +169,7 @@
} }
.repos-container { .repos-container {
margin-bottom: 10rem; padding-bottom: 10rem;
} }
.table-drop-active::before { .table-drop-active::before {

View File

@@ -1,6 +1,7 @@
import React, { Fragment } from 'react'; import React, { Fragment } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import cookie from 'react-cookies'; import cookie from 'react-cookies';
import classnames from 'classnames';
import { gettext, username, canAddRepo } from '../../utils/constants'; import { gettext, username, canAddRepo } from '../../utils/constants';
import { seafileAPI } from '../../utils/seafile-api'; import { seafileAPI } from '../../utils/seafile-api';
import { Utils } from '../../utils/utils'; 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 SharedRepoListView from '../../components/shared-repo-list-view/shared-repo-list-view';
import SortOptionsDialog from '../../components/dialog/sort-options'; import SortOptionsDialog from '../../components/dialog/sort-options';
import SingleDropdownToolbar from '../../components/toolbar/single-dropdown-toolbar'; 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'; import '../../css/group-view.css';
@@ -41,6 +45,7 @@ class GroupView extends React.Component {
currentRepo: null, currentRepo: null,
isStaff: false, isStaff: false,
isOwner: 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' 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' sortOrder: cookie.load('seafile-repo-dir-sort-order') || 'asc', // 'asc' or 'desc'
isSortOptionsDialogOpen: false, isSortOptionsDialogOpen: false,
@@ -386,8 +391,27 @@ class GroupView extends React.Component {
return opList; 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() { 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; let useRate = 0;
if (isDepartmentGroup && currentGroup.group_quota) { if (isDepartmentGroup && currentGroup.group_quota) {
@@ -409,11 +433,11 @@ class GroupView extends React.Component {
<span>{currentGroup.name}</span> <span>{currentGroup.name}</span>
<SingleDropdownToolbar opList={opList} /> <SingleDropdownToolbar opList={opList} />
</div> </div>
<div className="path-tool"> <div className="path-tool d-flex align-items-center">
{isDepartmentGroup && ( {isDepartmentGroup && (
<> <>
{currentGroup.group_quota > 0 && {currentGroup.group_quota > 0 &&
<div className="department-usage-container"> <div className="department-usage-container mr-3">
<div className="department-usage"> <div className="department-usage">
<span id="quota-bar" className="department-quota-bar"><span id="quota-usage" className="usage" style={{ width: useRate }}></span></span> <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> <span className="department-quota-info">{Utils.bytesToSize(currentGroup.group_quota_usage)} / {Utils.bytesToSize(currentGroup.group_quota)}</span>
@@ -422,25 +446,39 @@ 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) && {(!Utils.isDesktop() && this.state.repoList.length > 0) &&
<span className="sf3-font sf3-font-sort action-icon" onClick={this.toggleSortOptionsDialog}></span>} <span className="sf3-font sf3-font-sort action-icon" onClick={this.toggleSortOptionsDialog}></span>}
{this.state.isSortOptionsDialogOpen && {this.state.isSortOptionsDialogOpen &&
<SortOptionsDialog <SortOptionsDialog
toggleDialog={this.toggleSortOptionsDialog} sortBy={sortBy}
sortBy={this.state.sortBy} sortOrder={sortOrder}
sortOrder={this.state.sortOrder}
sortItems={this.sortItems} sortItems={this.sortItems}
toggleDialog={this.toggleSortOptionsDialog}
/> />
} }
</div> </div>
</Fragment> </Fragment>
)} )}
</div> </div>
<div className="cur-view-content d-block" onScroll={this.handleScroll}> <div
{this.state.isLoading && <Loading />} className={classnames('cur-view-content', 'd-block', 'repos-container', { 'pt-3': currentViewMode != LIST_MODE })}
{(!this.state.isLoading && errMessage) && <div className="error text-center mt-2">{errMessage}</div>} onScroll={this.handleScroll}
{(!this.state.isLoading && this.state.repoList.length === 0) && emptyTip} >
{(!this.state.isLoading && this.state.repoList.length > 0) && {isLoading
? <Loading />
: errMessage
? <p className="error text-center mt-2">{errMessage}</p>
: repoList.length == 0
? emptyTip
: (
<SharedRepoListView <SharedRepoListView
repoList={this.state.repoList} repoList={this.state.repoList}
hasNextPage={this.state.hasNextPage} hasNextPage={this.state.hasNextPage}
@@ -453,7 +491,9 @@ class GroupView extends React.Component {
onItemRename={this.onItemRename} onItemRename={this.onItemRename}
onMonitorRepo={this.onMonitorRepo} onMonitorRepo={this.onMonitorRepo}
onTransferRepo={this.onItemTransfer} onTransferRepo={this.onItemTransfer}
currentViewMode={currentViewMode}
/> />
)
} }
</div> </div>
</div> </div>

View File

@@ -1,20 +1,20 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import cookie from 'react-cookies'; import cookie from 'react-cookies';
import { Utils } from '../../utils/utils';
import { seafileAPI } from '../../utils/seafile-api'; import { seafileAPI } from '../../utils/seafile-api';
import { gettext, canAddRepo, canViewOrg } from '../../utils/constants'; import { gettext, canAddRepo, canViewOrg } from '../../utils/constants';
import { Utils } from '../../utils/utils';
import toaster from '../../components/toast';
import Repo from '../../models/repo'; import Repo from '../../models/repo';
import Group from '../../models/group'; import Group from '../../models/group';
import toaster from '../../components/toast';
import Loading from '../../components/loading'; import Loading from '../../components/loading';
import ViewModes from '../../components/view-modes'; 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 SingleDropdownToolbar from '../../components/toolbar/single-dropdown-toolbar';
import SortOptionsDialog from '../../components/dialog/sort-options'; import SortOptionsDialog from '../../components/dialog/sort-options';
import GuideForNewDialog from '../../components/dialog/guide-for-new-dialog'; import GuideForNewDialog from '../../components/dialog/guide-for-new-dialog';
import CreateRepoDialog from '../../components/dialog/create-repo-dialog'; import CreateRepoDialog from '../../components/dialog/create-repo-dialog';
import MylibRepoListView from '../../pages/my-libs/mylib-repo-list-view'; 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 SharedWithAll from '../../pages/shared-with-all';
import GroupItem from '../../pages/groups/group-item'; import GroupItem from '../../pages/groups/group-item';
import { LIST_MODE } from '../../components/dir-view-mode/constants'; import { LIST_MODE } from '../../components/dir-view-mode/constants';
@@ -24,13 +24,6 @@ import '../../css/files.css';
class Libraries extends Component { class Libraries extends Component {
constructor(props) { constructor(props) {
super(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 = { this.state = {
// for 'my libs' // for 'my libs'
errorMsg: '', errorMsg: '',
@@ -250,12 +243,6 @@ class Libraries extends Component {
const { isLoading, currentViewMode, sortBy, sortOrder, groupList } = this.state; const { isLoading, currentViewMode, sortBy, sortOrder, groupList } = this.state;
const isDesktop = Utils.isDesktop(); const isDesktop = Utils.isDesktop();
const sortOptions = this.sortOptions.map(item => {
return {
...item,
isSelected: item.value == `${sortBy}-${sortOrder}`
};
});
return ( return (
<> <>
<div className="main-panel-center flex-row"> <div className="main-panel-center flex-row">
@@ -267,14 +254,13 @@ class Libraries extends Component {
<div className="mr-2"> <div className="mr-2">
<ViewModes currentViewMode={currentViewMode} switchViewMode={this.switchViewMode} /> <ViewModes currentViewMode={currentViewMode} switchViewMode={this.switchViewMode} />
</div> </div>
<ReposSortMenu sortOptions={sortOptions} onSelectSortOption={this.onSelectSortOption}/> <ReposSortMenu sortBy={sortBy} sortOrder={sortOrder} onSelectSortOption={this.onSelectSortOption} />
</div> </div>
} }
</div> </div>
{isLoading ? <div className="cur-view-content repos-container" id="files-content-container">
<Loading /> : {isLoading ? <Loading /> : (
<div className="cur-view-content" id="files-content-container"> <>
{(Utils.isDesktop() && currentViewMode == LIST_MODE) && ( {(Utils.isDesktop() && currentViewMode == LIST_MODE) && (
<table aria-hidden={true} className="my-3"> <table aria-hidden={true} className="my-3">
<thead> <thead>
@@ -305,10 +291,11 @@ class Libraries extends Component {
<span className="sf3-font sf3-font-sort action-icon" onClick={this.toggleSortOptionsDialog}></span> <span className="sf3-font sf3-font-sort action-icon" onClick={this.toggleSortOptionsDialog}></span>
} }
</div> </div>
{this.state.errorMsg ? <p className="error text-center mt-8">{this.state.errorMsg}</p> : ( {this.state.errorMsg
this.state.repoList.length === 0 ? ( ? <p className="error text-center mt-8">{this.state.errorMsg}</p>
<p className={`libraries-empty-tip-in-${currentViewMode}-mode`}>{gettext('No libraries')}</p> : this.state.repoList.length == 0
) : ( ? <p className={`libraries-empty-tip-in-${currentViewMode}-mode`}>{gettext('No libraries')}</p>
: (
<MylibRepoListView <MylibRepoListView
sortBy={this.state.sortBy} sortBy={this.state.sortBy}
sortOrder={this.state.sortOrder} sortOrder={this.state.sortOrder}
@@ -322,10 +309,11 @@ class Libraries extends Component {
inAllLibs={true} inAllLibs={true}
currentViewMode={currentViewMode} currentViewMode={currentViewMode}
/> />
)) )
} }
</div> </div>
)} )}
<div className="pb-3"> <div className="pb-3">
<SharedLibraries <SharedLibraries
repoList={this.state.sharedRepoList} repoList={this.state.sharedRepoList}
@@ -333,6 +321,7 @@ class Libraries extends Component {
currentViewMode={currentViewMode} currentViewMode={currentViewMode}
/> />
</div> </div>
{canViewOrg && {canViewOrg &&
<div className="pb-3"> <div className="pb-3">
<SharedWithAll <SharedWithAll
@@ -342,7 +331,7 @@ class Libraries extends Component {
/> />
</div> </div>
} }
<div className="repos-container">
{groupList.length > 0 && groupList.map((group) => { {groupList.length > 0 && groupList.map((group) => {
return ( return (
<GroupItem <GroupItem
@@ -355,10 +344,10 @@ class Libraries extends Component {
/> />
); );
})} })}
</>
)}
</div> </div>
</div> </div>
}
</div>
{!isLoading && !this.state.errorMsg && this.state.isGuideForNewDialogOpen && {!isLoading && !this.state.errorMsg && this.state.isGuideForNewDialogOpen &&
<GuideForNewDialog <GuideForNewDialog
toggleDialog={this.toggleGuideForNewDialog} toggleDialog={this.toggleGuideForNewDialog}

View File

@@ -1,18 +1,22 @@
import React, { Component, Fragment } from 'react'; import React, { Component, Fragment } from 'react';
import cookie from 'react-cookies'; import cookie from 'react-cookies';
import classnames from 'classnames';
import Repo from '../../models/repo';
import { seafileAPI } from '../../utils/seafile-api'; import { seafileAPI } from '../../utils/seafile-api';
import { gettext } from '../../utils/constants'; import { gettext } from '../../utils/constants';
import { Utils } from '../../utils/utils'; import { Utils } from '../../utils/utils';
import toaster from '../../components/toast'; import toaster from '../../components/toast';
import Repo from '../../models/repo';
import Loading from '../../components/loading'; import Loading from '../../components/loading';
import EmptyTip from '../../components/empty-tip'; 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 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 CreateRepoDialog from '../../components/dialog/create-repo-dialog';
import DeletedReposDialog from '../../components/dialog/my-deleted-repos'; 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 { class MyLibraries extends Component {
constructor(props) { constructor(props) {
@@ -24,6 +28,7 @@ class MyLibraries extends Component {
isDeletedReposDialogOpen: false, isDeletedReposDialogOpen: false,
isCreateRepoDialogOpen: false, isCreateRepoDialogOpen: false,
isSortOptionsDialogOpen: 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' 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' 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() { render() {
const { isLoading, errorMsg, currentViewMode, sortBy, sortOrder, repoList } = this.state;
const isDesktop = Utils.isDesktop();
return ( return (
<Fragment> <Fragment>
<div className="main-panel-center flex-row"> <div className="main-panel-center flex-row">
@@ -150,33 +172,49 @@ class MyLibraries extends Component {
]} ]}
/> />
</h3> </h3>
<div> {isDesktop ? (
{(!Utils.isDesktop() && this.state.repoList.length > 0) && <span className="sf3-font sf3-font-sort action-icon" onClick={this.toggleSortOptionsDialog}></span>} <div className="d-flex align-items-center">
<div className="mr-2">
<ViewModes currentViewMode={currentViewMode} switchViewMode={this.switchViewMode} />
</div> </div>
<ReposSortMenu sortBy={sortBy} sortOrder={sortOrder} onSelectSortOption={this.onSelectSortOption} />
</div> </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>} {repoList.length > 0 &&
{!this.state.isLoading && !this.state.errorMsg && this.state.repoList.length === 0 && this.emptyTip} <span className="sf3-font sf3-font-sort action-icon" onClick={this.toggleSortOptionsDialog}></span>
{!this.state.isLoading && !this.state.errorMsg && this.state.repoList.length > 0 && }
</>
)}
</div>
<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 <MylibRepoListView
sortBy={this.state.sortBy} sortBy={sortBy}
sortOrder={this.state.sortOrder} sortOrder={sortOrder}
repoList={this.state.repoList} repoList={this.state.repoList}
onRenameRepo={this.onRenameRepo} onRenameRepo={this.onRenameRepo}
onDeleteRepo={this.onDeleteRepo} onDeleteRepo={this.onDeleteRepo}
onTransferRepo={this.onTransferRepo} onTransferRepo={this.onTransferRepo}
onMonitorRepo={this.onMonitorRepo} onMonitorRepo={this.onMonitorRepo}
sortRepoList={this.sortRepoList} sortRepoList={this.sortRepoList}
currentViewMode={currentViewMode}
/> />
)
} }
</div> </div>
</div> </div>
{this.state.isSortOptionsDialogOpen && {this.state.isSortOptionsDialogOpen &&
<SortOptionsDialog <SortOptionsDialog
toggleDialog={this.toggleSortOptionsDialog} toggleDialog={this.toggleSortOptionsDialog}
sortBy={this.state.sortBy} sortBy={sortBy}
sortOrder={this.state.sortOrder} sortOrder={sortOrder}
sortItems={this.sortRepoList} sortItems={this.sortRepoList}
/> />
} }

View File

@@ -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>; 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 ? ( return currentViewMode == LIST_MODE ? (
<table className={classNames({ 'table-thead-hidden': inAllLibs }, { 'repos-container': !inAllLibs })}> <table className={classNames({ 'table-thead-hidden': inAllLibs })}>
<thead> <thead>
<tr> <tr>
<th width="4%"></th> <th width="4%"></th>

View 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;

View 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;

View File

@@ -2,184 +2,19 @@ import React, { Component, Fragment } from 'react';
import { Dropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap'; import { Dropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import classNames from 'classnames';
import relativeTime from 'dayjs/plugin/relativeTime'; import relativeTime from 'dayjs/plugin/relativeTime';
import cookie from 'react-cookies';
import { Link, navigate } from '@gatsbyjs/reach-router'; import { Link, navigate } from '@gatsbyjs/reach-router';
import { gettext, siteRoot, isPro } from '../../utils/constants'; import { gettext, siteRoot, isPro } from '../../utils/constants';
import { seafileAPI } from '../../utils/seafile-api'; import { seafileAPI } from '../../utils/seafile-api';
import { Utils } from '../../utils/utils'; import { Utils } from '../../utils/utils';
import toaster from '../../components/toast'; 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 ModalPortal from '../../components/modal-portal';
import ShareDialog from '../../components/dialog/share-dialog'; import ShareDialog from '../../components/dialog/share-dialog';
import SortOptionsDialog from '../../components/dialog/sort-options';
import RepoMonitoredIcon from '../../components/repo-monitored-icon'; import RepoMonitoredIcon from '../../components/repo-monitored-icon';
import { GRID_MODE, LIST_MODE } from '../../components/dir-view-mode/constants'; 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); 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 { class Item extends Component {
constructor(props) { constructor(props) {
@@ -530,141 +365,4 @@ Item.propTypes = {
onContextMenu: PropTypes.func.isRequired, onContextMenu: PropTypes.func.isRequired,
}; };
class SharedLibraries extends Component { export default Item;
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;

View File

@@ -1,6 +1,7 @@
import React, { Fragment } from 'react'; import React, { Fragment } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import cookie from 'react-cookies'; import cookie from 'react-cookies';
import classnames from 'classnames';
import { seafileAPI } from '../../utils/seafile-api'; import { seafileAPI } from '../../utils/seafile-api';
import { gettext, canAddPublicRepo } from '../../utils/constants'; import { gettext, canAddPublicRepo } from '../../utils/constants';
import { Utils } from '../../utils/utils'; import { Utils } from '../../utils/utils';
@@ -15,6 +16,8 @@ import ModalPortal from '../../components/modal-portal';
import CreateRepoDialog from '../../components/dialog/create-repo-dialog'; import CreateRepoDialog from '../../components/dialog/create-repo-dialog';
import ShareRepoDialog from '../../components/dialog/share-repo-dialog'; import ShareRepoDialog from '../../components/dialog/share-repo-dialog';
import { LIST_MODE } from '../../components/dir-view-mode/constants'; import { LIST_MODE } from '../../components/dir-view-mode/constants';
import ViewModes from '../../components/view-modes';
import ReposSortMenu from '../../components/sort-menu';
const propTypes = { const propTypes = {
currentViewMode: PropTypes.string, currentViewMode: PropTypes.string,
@@ -32,6 +35,7 @@ class SharedWithAll extends React.Component {
repoList: [], repoList: [],
isCreateRepoDialogOpen: false, isCreateRepoDialogOpen: false,
isSelectRepoDialogOpen: 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' 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' sortOrder: cookie.load('seafile-repo-dir-sort-order') || 'asc', // 'asc' or 'desc'
isSortOptionsDialogOpen: false, isSortOptionsDialogOpen: false,
@@ -120,11 +124,12 @@ class SharedWithAll extends React.Component {
}); });
}; };
renderContent = () => { renderContent = (currentViewMode) => {
const { inAllLibs = false, currentViewMode = LIST_MODE } = this.props; // inAllLibs: in 'All Libs'('Files') page const { inAllLibs = false } = this.props; // inAllLibs: in 'All Libs'('Files') page
const { errMessage } = this.state; const { isLoading, errMessage, repoList } = this.state;
const emptyTip = inAllLibs ? const emptyTip = inAllLibs
<p className={`libraries-empty-tip-in-${currentViewMode}-mode`}>{gettext('No public libraries')}</p> : ( ? <p className={`libraries-empty-tip-in-${currentViewMode}-mode`}>{gettext('No public libraries')}</p>
: (
<EmptyTip <EmptyTip
title={gettext('No public libraries')} 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.')} 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,10 +138,13 @@ class SharedWithAll extends React.Component {
); );
return ( return (
<> <>
{this.state.isLoading && <Loading />} {isLoading
{(!this.state.isLoading && errMessage) && errMessage} ? <Loading />
{(!this.state.isLoading && this.state.repoList.length === 0) && emptyTip} : errMessage
{(!this.state.isLoading && this.state.repoList.length > 0) && ? <p className="error text-center">{errMessage}</p>
: repoList.length == 0
? emptyTip
: (
<SharedRepoListView <SharedRepoListView
key='public-shared-view' key='public-shared-view'
libraryType={this.state.libraryType} libraryType={this.state.libraryType}
@@ -146,11 +154,10 @@ class SharedWithAll extends React.Component {
sortItems={this.sortItems} sortItems={this.sortItems}
onItemUnshare={this.onItemUnshare} onItemUnshare={this.onItemUnshare}
onItemDelete={this.onItemDelete} onItemDelete={this.onItemDelete}
theadHidden={inAllLibs}
currentViewMode={currentViewMode} currentViewMode={currentViewMode}
inAllLibs={inAllLibs} 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() { 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) { if (inAllLibs) {
return ( return (
@@ -216,7 +240,7 @@ class SharedWithAll extends React.Component {
</h4> </h4>
{this.renderSortIconInMobile()} {this.renderSortIconInMobile()}
</div> </div>
{this.renderContent()} {this.renderContent(currentViewMode)}
</> </>
); );
} }
@@ -237,10 +261,18 @@ class SharedWithAll extends React.Component {
/> />
} }
</h3> </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()} {this.renderSortIconInMobile()}
</div> </div>
<div className="cur-view-content"> <div className={classnames('cur-view-content', 'repos-container', { 'pt-3': currentViewMode != LIST_MODE })}>
{this.renderContent()} {this.renderContent(currentViewMode)}
</div> </div>
</div> </div>
</div> </div>