1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-17 15:53:28 +00:00

Files view mode sort (#6210)

* ['Files'] added 'view mode' options & added 'grid' mode for 'My Libraries' & 'Shared with me'

* ['Files'] added 'grid' mode for 'Shared with all' & 'department/group'; redesigned the empty tip for 'grid' mode; replaced 'star/unstar/monitored' icons

* ['Files'] added 'sort'(WIP)

* ['Files' page] added 'sort' for all the libraries
This commit is contained in:
llj
2024-06-17 09:32:05 +08:00
committed by GitHub
parent 140fd4856e
commit 2641d89553
11 changed files with 448 additions and 153 deletions

View File

@@ -1,22 +1,20 @@
import React, { Fragment } from 'react'; import React, { Fragment } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { UncontrolledTooltip } from 'reactstrap'; import { UncontrolledTooltip } from 'reactstrap';
import Icon from '../components/icon';
import { gettext } from '../utils/constants'; import { gettext } from '../utils/constants';
const propTypes = { const propTypes = {
repoID: PropTypes.string.isRequired repoID: PropTypes.string.isRequired,
className: PropTypes.string
}; };
class RepoMonitoredIcon extends React.Component { class RepoMonitoredIcon extends React.Component {
render() { render() {
const { repoID } = this.props; const { repoID, className } = this.props;
return ( return (
<Fragment> <Fragment>
<span id={`watching-${repoID}`} className="ml-1"> <i id={`watching-${repoID}`} className={`sf3-font-monitor sf3-font ${className ? className : ''}`}></i>
<Icon symbol='monitor' />
</span>
<UncontrolledTooltip <UncontrolledTooltip
placement="bottom" placement="bottom"
target={`#watching-${repoID}`} target={`#watching-${repoID}`}

View File

@@ -22,6 +22,7 @@ import RepoShareAdminDialog from '../dialog/repo-share-admin-dialog';
import RepoMonitoredIcon from '../../components/repo-monitored-icon'; import RepoMonitoredIcon from '../../components/repo-monitored-icon';
const propTypes = { const propTypes = {
currentViewMode: PropTypes.string,
currentGroup: PropTypes.object, currentGroup: PropTypes.object,
libraryType: PropTypes.string, libraryType: PropTypes.string,
repo: PropTypes.object.isRequired, repo: PropTypes.object.isRequired,
@@ -140,9 +141,10 @@ class SharedRepoListItem extends React.Component {
}; };
getRepoComputeParams = () => { getRepoComputeParams = () => {
let repo = this.props.repo; const { repo, currentViewMode } = this.props;
let iconUrl = Utils.getLibIconUrl(repo); const useBigLibaryIcon = currentViewMode == 'grid';
const iconUrl = Utils.getLibIconUrl(repo, useBigLibaryIcon);
let iconTitle = Utils.getLibIconTitle(repo); let iconTitle = Utils.getLibIconTitle(repo);
let libPath = `${siteRoot}library/${repo.repo_id}/${Utils.encodePath(repo.repo_name)}/`; let libPath = `${siteRoot}library/${repo.repo_id}/${Utils.encodePath(repo.repo_name)}/`;
@@ -630,31 +632,63 @@ class SharedRepoListItem extends React.Component {
renderPCUI = () => { renderPCUI = () => {
let { iconUrl, iconTitle, libPath } = this.getRepoComputeParams(); let { iconUrl, iconTitle, libPath } = this.getRepoComputeParams();
let { repo } = this.props; const { repo, currentViewMode } = this.props;
return ( return currentViewMode == 'list' ? (
<Fragment> <tr className={this.state.highlight ? 'tr-highlight' : ''} onMouseEnter={this.onMouseEnter} onMouseOver={this.onMouseOver} onMouseLeave={this.onMouseLeave} onFocus={this.onMouseEnter}>
<tr className={this.state.highlight ? 'tr-highlight' : ''} onMouseEnter={this.onMouseEnter} onMouseOver={this.onMouseOver} onMouseLeave={this.onMouseLeave} onFocus={this.onMouseEnter}> <td className="text-center">
<td className="text-center"> <i
<a href="#" role="button" aria-label={this.state.isStarred ? gettext('Unstar') : gettext('Star')} onClick={this.onToggleStarRepo}> role="button"
<i className={`fa-star ${this.state.isStarred ? 'fas' : 'far star-empty'}`}></i> title={this.state.isStarred ? gettext('Unstar') : gettext('Star')}
</a> aria-label={this.state.isStarred ? gettext('Unstar') : gettext('Star')}
</td> onClick={this.onToggleStarRepo}
<td><img src={iconUrl} title={iconTitle} alt={iconTitle} width="24" /></td> className={`op-icon m-0 ${this.state.isStarred ? 'sf3-font-star' : 'sf3-font-star-empty'} sf3-font`}
<td> >
{this.state.isRenaming ? </i>
<Rename name={repo.repo_name} onRenameConfirm={this.onRenameConfirm} onRenameCancel={this.onRenameCancel}/> : </td>
<Fragment> <td><img src={iconUrl} title={iconTitle} alt={iconTitle} width="24" /></td>
<Link to={libPath}>{repo.repo_name}</Link> <td>
{repo.monitored && <RepoMonitoredIcon repoID={repo.repo_id} />} {this.state.isRenaming ?
</Fragment> <Rename name={repo.repo_name} onRenameConfirm={this.onRenameConfirm} onRenameCancel={this.onRenameCancel}/> :
} <Fragment>
</td> <Link to={libPath}>{repo.repo_name}</Link>
<td>{this.state.isOperationShow && this.generatorPCMenu()}</td> {repo.monitored && <RepoMonitoredIcon repoID={repo.repo_id} className="ml-1 op-icon" />}
<td>{repo.size}</td> </Fragment>
<td title={moment(repo.last_modified).format('llll')}>{moment(repo.last_modified).fromNow()}</td> }
<td title={repo.owner_contact_email}>{repo.owner_name}</td> </td>
</tr> <td>{this.state.isOperationShow && this.generatorPCMenu()}</td>
</Fragment> <td>{repo.size}</td>
<td title={moment(repo.last_modified).format('llll')}>{moment(repo.last_modified).fromNow()}</td>
<td title={repo.owner_contact_email}>{repo.owner_name}</td>
</tr>
) : (
<div
className="library-grid-item px-3 d-flex justify-content-between align-items-center"
onMouseEnter={this.onMouseEnter}
onMouseLeave={this.onMouseLeave}
onFocus={this.onMouseEnter}
>
<div className="d-flex align-items-center">
<img src={iconUrl} title={iconTitle} alt={iconTitle} width="36" className="mr-2" />
{this.state.isRenaming ?
<Rename name={repo.repo_name} onRenameConfirm={this.onRenameConfirm} onRenameCancel={this.onRenameCancel}/> :
<Fragment>
<Link to={libPath}>{repo.repo_name}</Link>
<i
role="button"
title={this.state.isStarred ? gettext('Unstar') : gettext('Star')}
aria-label={this.state.isStarred ? gettext('Unstar') : gettext('Star')}
onClick={this.onToggleStarRepo}
className={`op-icon library-grid-item-icon ${this.state.isStarred ? 'sf3-font-star' : 'sf3-font-star-empty'} sf3-font`}
>
</i>
{repo.monitored && <RepoMonitoredIcon repoID={repo.repo_id} className="op-icon library-grid-item-icon" />}
</Fragment>
}
</div>
<div>
{this.state.isOperationShow && this.generatorPCMenu()}
</div>
</div>
); );
}; };
@@ -677,7 +711,7 @@ class SharedRepoListItem extends React.Component {
<Rename name={repo.repo_name} onRenameConfirm={this.onRenameConfirm} onRenameCancel={this.onRenameCancel} /> : <Rename name={repo.repo_name} onRenameConfirm={this.onRenameConfirm} onRenameCancel={this.onRenameCancel} /> :
<Fragment> <Fragment>
<Link to={libPath}>{repo.repo_name}</Link> <Link to={libPath}>{repo.repo_name}</Link>
{repo.monitored && <RepoMonitoredIcon repoID={repo.repo_id} />} {repo.monitored && <RepoMonitoredIcon repoID={repo.repo_id} className="ml-1 op-icon" />}
</Fragment> </Fragment>
} }
<br /> <br />

View File

@@ -8,6 +8,7 @@ import LibsMobileThead from '../libs-mobile-thead';
import Loading from '../loading'; import Loading from '../loading';
const propTypes = { const propTypes = {
currentViewMode: PropTypes.string,
libraryType: PropTypes.string, libraryType: PropTypes.string,
currentGroup: PropTypes.object, currentGroup: PropTypes.object,
isShowTableThread: PropTypes.bool, isShowTableThread: PropTypes.bool,
@@ -84,6 +85,7 @@ class SharedRepoListView extends React.Component {
}; };
renderRepoListView = () => { renderRepoListView = () => {
const { currentViewMode = 'list' } = this.props;
return ( return (
<Fragment> <Fragment>
{this.props.repoList.map(repo => { {this.props.repoList.map(repo => {
@@ -100,6 +102,7 @@ class SharedRepoListView extends React.Component {
onItemDelete={this.props.onItemDelete} onItemDelete={this.props.onItemDelete}
onItemRename={this.props.onItemRename} onItemRename={this.props.onItemRename}
onMonitorRepo={this.props.onMonitorRepo} onMonitorRepo={this.props.onMonitorRepo}
currentViewMode={currentViewMode}
/> />
); );
})} })}
@@ -108,10 +111,10 @@ class SharedRepoListView extends React.Component {
}; };
renderPCUI = () => { renderPCUI = () => {
const { theadHidden = false } = this.props; const { theadHidden = false, currentViewMode = 'list' } = this.props;
const { sortByName, sortByTime, sortBySize, sortIcon } = this.getSortMetaData(); const { sortByName, sortByTime, sortBySize, sortIcon } = this.getSortMetaData();
return ( return currentViewMode == 'list' ? (
<table className={theadHidden ? 'table-thead-hidden' : ''}> <table className={theadHidden ? 'table-thead-hidden' : ''}>
<thead> <thead>
<tr> <tr>
@@ -128,6 +131,10 @@ class SharedRepoListView extends React.Component {
{this.renderRepoListView()} {this.renderRepoListView()}
</tbody> </tbody>
</table> </table>
) : (
<div className="d-flex justify-content-between flex-wrap">
{this.renderRepoListView()}
</div>
); );
}; };

View File

@@ -4,8 +4,10 @@ import PropTypes from 'prop-types';
import '../css/single-selector.css'; import '../css/single-selector.css';
const propTypes = { const propTypes = {
isDropdownToggleShown: PropTypes.bool.isRequired, customSelectorToggle: PropTypes.object,
currentSelectedOption: PropTypes.object.isRequired, menuCustomClass: PropTypes.string,
isDropdownToggleShown: PropTypes.bool,
currentSelectedOption: PropTypes.object,
options: PropTypes.array.isRequired, options: PropTypes.array.isRequired,
selectOption: PropTypes.func.isRequired, selectOption: PropTypes.func.isRequired,
operationBeforeSelect: PropTypes.func, operationBeforeSelect: PropTypes.func,
@@ -63,15 +65,21 @@ class Selector extends Component {
render() { render() {
const { isPopoverOpen } = this.state; const { isPopoverOpen } = this.state;
const { currentSelectedOption, options, isDropdownToggleShown } = this.props; const { currentSelectedOption, options, isDropdownToggleShown, menuCustomClass = '',
customSelectorToggle = null
} = this.props;
return ( return (
<div className="sf-single-selector position-relative"> <div className="sf-single-selector position-relative">
<span className="cur-option" onClick={this.onToggleClick}> <div onClick={this.onToggleClick}>
{currentSelectedOption.text} {customSelectorToggle ? customSelectorToggle : (
{isDropdownToggleShown && <i className="fas fa-caret-down ml-2 toggle-icon"></i>} <span className="cur-option">
</span> {currentSelectedOption.text}
{isDropdownToggleShown && <i className="fas fa-caret-down ml-2 toggle-icon"></i>}
</span>
)}
</div>
{isPopoverOpen && ( {isPopoverOpen && (
<div className="options-container position-absolute rounded shadow mt-1" ref={ref => this.selector = ref}> <div className={`options-container position-absolute rounded shadow mt-1 ${menuCustomClass}`} ref={ref => this.selector = ref}>
<ul className="option-list list-unstyled py-3 o-auto"> <ul className="option-list list-unstyled py-3 o-auto">
{options.map((item, index) => { {options.map((item, index) => {
return ( return (

View File

@@ -1,12 +1,43 @@
.repos-sort-menu-toggle:hover {
background: #e5e6e7;
}
.sf-single-selector .options-container.repos-sort-menu {
min-width: 215px;
}
#files-content-container td:first-child { #files-content-container td:first-child {
text-align: left!important; text-align: left!important;
padding-left: 20px; padding-left: 20px;
} }
#files-content-container .libraries-empty-tip { #files-content-container .libraries-empty-tip-in-list-mode {
color: #a4a4a4; color: #a4a4a4;
text-align: center; text-align: center;
padding: 4px 0; padding: 4px 0;
border-bottom: 1px solid #e8e8e8; border-bottom: 1px solid #e8e8e8;
} }
#files-content-container .libraries-empty-tip-in-grid-mode {
color: #a4a4a4;
text-align: center;
height: 56px;
line-height: 52px;
border: 2px dashed #eeeeee;
border-radius: 4px;
}
.library-grid-item {
width: 48%;
height: 56px;
border-radius: 3px;
}
.library-grid-item:hover {
background: #f5f5f5;
}
.library-grid-item-icon {
margin-left: 6px;
}

View File

@@ -12,6 +12,7 @@ import Repo from '../../models/repo';
import '../../css/groups.css'; import '../../css/groups.css';
const propTypes = { const propTypes = {
currentViewMode: PropTypes.string,
group: PropTypes.object.isRequired, group: PropTypes.object.isRequired,
updateGroup: PropTypes.func.isRequired updateGroup: PropTypes.func.isRequired
}; };
@@ -107,14 +108,14 @@ class GroupItem extends React.Component {
render() { render() {
const { group } = this.props; const { group, currentViewMode = 'list' } = this.props;
const { parent_group_id, admins } = group; const { parent_group_id, admins } = group;
const emptyTip = <p className="group-item-empty-tip">{gettext('No libraries')}</p>; const emptyTip = <p className={`libraries-empty-tip-in-${currentViewMode}-mode`}>{gettext('No libraries')}</p>;
const isDeptAdmin = parent_group_id != 0 && admins.indexOf(username) > -1; const isDeptAdmin = parent_group_id != 0 && admins.indexOf(username) > -1;
return ( return (
<div className="pb-3"> <div className="pb-3">
<div className="d-flex justify-content-between mt-3 py-1 sf-border-bottom"> <div className={`d-flex justify-content-between mt-3 py-1 ${currentViewMode == 'list' ? 'sf-border-bottom' : ''}`}>
<h4 className="sf-heading m-0 d-flex align-items-center"> <h4 className="sf-heading m-0 d-flex align-items-center">
<span className={`${group.parent_group_id == 0 ? 'sf3-font-group' : 'sf3-font-department'} sf3-font nav-icon`} aria-hidden="true"></span> <span className={`${group.parent_group_id == 0 ? 'sf3-font-group' : 'sf3-font-department'} sf3-font nav-icon`} aria-hidden="true"></span>
<a href={`${siteRoot}group/${group.id}/`} title={group.name} className="ellipsis">{group.name}</a> <a href={`${siteRoot}group/${group.id}/`} title={group.name} className="ellipsis">{group.name}</a>
@@ -136,6 +137,7 @@ class GroupItem extends React.Component {
onItemDelete={this.onItemDelete} onItemDelete={this.onItemDelete}
onItemRename={this.onItemRename} onItemRename={this.onItemRename}
onMonitorRepo={this.onMonitorRepo} onMonitorRepo={this.onMonitorRepo}
currentViewMode={currentViewMode}
/> />
} }
{this.state.isCreateRepoDialogOpen && {this.state.isCreateRepoDialogOpen &&

View File

@@ -8,13 +8,14 @@ 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 Loading from '../../components/loading'; import Loading from '../../components/loading';
import Selector from '../../components/single-selector';
import TopToolbar from '../../components/toolbar/top-toolbar'; import TopToolbar from '../../components/toolbar/top-toolbar';
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 SharedLibs from '../../pages/shared-libs/shared-libs.js'; import SharedLibs from '../../pages/shared-libs/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';
@@ -28,19 +29,27 @@ const propTypes = {
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: '',
isLoading: true, isLoading: true,
repoList: [], repoList: [],
isSortOptionsDialogOpen: false, isSortOptionsDialogOpen: false,
sortBy: cookie.load('seafile-repo-dir-sort-by') || 'name', // 'name' or 'time' or 'size'
sortOrder: cookie.load('seafile-repo-dir-sort-order') || 'asc', // 'asc' or 'desc'
isGuideForNewDialogOpen: window.app.pageOptions.guideEnabled, isGuideForNewDialogOpen: window.app.pageOptions.guideEnabled,
groupList: [], groupList: [],
sharedRepoList:[], sharedRepoList:[],
publicRepoList: [], publicRepoList: [],
isCreateRepoDialogOpen: false isCreateRepoDialogOpen: false,
currentViewMode: localStorage.getItem('sf_repo_list_view_mode') || 'list',
sortBy: localStorage.getItem('sf_repos_sort_by') || 'name', // 'name' or 'time'
sortOrder: localStorage.getItem('sf_repos_sort_order') || 'asc', // 'asc' or 'desc'
}; };
} }
@@ -49,21 +58,20 @@ class Libraries extends Component {
const promiseListGroups = seafileAPI.listGroups(true); const promiseListGroups = seafileAPI.listGroups(true);
Promise.all([promiseListRepos, promiseListGroups]).then(res => { Promise.all([promiseListRepos, promiseListGroups]).then(res => {
const [resListRepos, resListGroups] = res; const [resListRepos, resListGroups] = res;
const allRepoList = resListRepos.data.repos.map((item) => new Repo(item)); const repoList = resListRepos.data.repos.map((item) => new Repo(item));
const myRepoList = allRepoList.filter(item => item.type === 'mine'); const groups = resListGroups.data.map(item => {
const sharedRepoList = allRepoList.filter(item => item.type === 'shared');
const publicRepoList = allRepoList.filter(item => item.type === 'public');
const groupList = resListGroups.data.map(item => {
let group = new Group(item); let group = new Group(item);
group.repos = item.repos.map(item => new Repo(item)); group.repos = item.repos.map(item => new Repo(item));
return group; return group;
}).sort((a, b) => a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1 ); }).sort((a, b) => a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1 );
const { allRepoList, myRepoList, sharedRepoList, publicRepoList, groupList } = this.sortRepos(repoList, groups);
this.setState({ this.setState({
isLoading: false, isLoading: false,
allRepoList,
groupList, groupList,
sharedRepoList, sharedRepoList,
publicRepoList, publicRepoList,
repoList: Utils.sortRepos(myRepoList, this.state.sortBy, this.state.sortOrder), repoList: myRepoList
}); });
}).catch((error) => { }).catch((error) => {
this.setState({ this.setState({
@@ -73,6 +81,18 @@ class Libraries extends Component {
}); });
} }
sortRepos = (repoList, groups) => {
const allRepoList = Utils.sortRepos(repoList, this.state.sortBy, this.state.sortOrder);
const myRepoList = allRepoList.filter(item => item.type === 'mine');
const sharedRepoList = allRepoList.filter(item => item.type === 'shared');
const publicRepoList = allRepoList.filter(item => item.type === 'public');
const groupList = groups.map(item => {
item.repos = Utils.sortRepos(item.repos, this.state.sortBy, this.state.sortOrder);
return item;
});
return { allRepoList, myRepoList, sharedRepoList, publicRepoList, groupList };
};
toggleSortOptionsDialog = () => { toggleSortOptionsDialog = () => {
this.setState({ this.setState({
isSortOptionsDialogOpen: !this.state.isSortOptionsDialogOpen isSortOptionsDialogOpen: !this.state.isSortOptionsDialogOpen
@@ -100,6 +120,23 @@ class Libraries extends Component {
}); });
}; };
onSelectSortOption = (sortOption) => {
const [sortBy, sortOrder] = sortOption.value.split('-');
this.setState({sortBy, sortOrder}, () => {
localStorage.setItem('sf_repos_sort_by', sortBy);
localStorage.setItem('sf_repos_sort_order', sortOrder);
const { allRepoList: repoList, groupList: groups } = this.state;
const { allRepoList, myRepoList, sharedRepoList, publicRepoList, groupList } = this.sortRepos(repoList, groups);
this.setState({
allRepoList,
groupList,
sharedRepoList,
publicRepoList,
repoList: myRepoList
});
});
};
sortRepoList = (sortBy, sortOrder) => { sortRepoList = (sortBy, sortOrder) => {
cookie.save('seafile-repo-dir-sort-by', sortBy); cookie.save('seafile-repo-dir-sort-by', sortBy);
cookie.save('seafile-repo-dir-sort-order', sortOrder); cookie.save('seafile-repo-dir-sort-order', sortOrder);
@@ -181,8 +218,33 @@ class Libraries extends Component {
}); });
}; };
switchViewMode = (newMode) => {
this.setState({
currentViewMode: newMode
}, () => {
localStorage.setItem('sf_repo_list_view_mode', newMode);
});
};
render() { render() {
const { isLoading } = this.state; const { isLoading, currentViewMode, sortBy, sortOrder } = this.state;
const baseClass = 'btn btn-secondary btn-icon sf-view-mode-btn ';
const isDesktop = Utils.isDesktop();
const sortOptions = this.sortOptions.map(item => {
return {
...item,
isSelected: item.value == `${sortBy}-${sortOrder}`
};
});
const customSelectorToggle = (
<button className="btn btn-secondary border-0 op-btn repos-sort-menu-toggle">
<i className="sf3-font-sort2 sf3-font"></i>
<i className="sf3-font-down sf3-font ml-1"></i>
</button>
);
return ( return (
<Fragment> <Fragment>
<TopToolbar <TopToolbar
@@ -194,28 +256,45 @@ class Libraries extends Component {
<div className="cur-view-container"> <div className="cur-view-container">
<div className="cur-view-path"> <div className="cur-view-path">
<h3 className="sf-heading m-0">{gettext('Files')}</h3> <h3 className="sf-heading m-0">{gettext('Files')}</h3>
{isDesktop &&
<div className="d-flex align-items-center">
<div className="view-modes mr-2">
<button className={`${baseClass} sf3-font-list-view sf3-font ${currentViewMode === 'list' ? 'current-mode' : ''}`} id='list' title={gettext('List')} aria-label={gettext('List')} onClick={this.switchViewMode.bind(this, 'list')}></button>
<button className={`${baseClass} sf3-font-grid-view sf3-font ${currentViewMode === 'grid' ? 'current-mode' : ''}`} id='grid' title={gettext('Grid')} aria-label={gettext('Grid')} onClick={this.switchViewMode.bind(this, 'grid')}></button>
</div>
<Selector
customSelectorToggle={customSelectorToggle}
options={sortOptions}
selectOption={this.onSelectSortOption}
menuCustomClass='repos-sort-menu dropdown-menu-right'
/>
</div>
}
</div> </div>
{isLoading ? {isLoading ?
<Loading /> : <Loading /> :
<div className="cur-view-content" id="files-content-container"> <div className="cur-view-content" id="files-content-container">
<table aria-hidden={true} className="my-3"> {(Utils.isDesktop() && currentViewMode == 'list') && (
<thead> <table aria-hidden={true} className="my-3">
<tr> <thead>
<th width="4%"></th> <tr>
<th width="3%"><span className="sr-only">{gettext('Library Type')}</span></th> <th width="4%"></th>
<th width="35%">{gettext('Name')}</th> <th width="3%"><span className="sr-only">{gettext('Library Type')}</span></th>
<th width="10%"><span className="sr-only">{gettext('Actions')}</span></th> <th width="35%">{gettext('Name')}</th>
<th width="14%">{gettext('Size')}</th> <th width="10%"><span className="sr-only">{gettext('Actions')}</span></th>
<th width="17%">{gettext('Last Update')}</th> <th width="14%">{gettext('Size')}</th>
<th width="17%">{gettext('Owner')}</th> <th width="17%">{gettext('Last Update')}</th>
</tr> <th width="17%">{gettext('Owner')}</th>
</thead> </tr>
</table> </thead>
</table>
)}
{canAddRepo && ( {canAddRepo && (
<div className="pb-3"> <div className="pb-3">
<div className="d-flex justify-content-between mt-3 py-1 sf-border-bottom"> <div className={`d-flex justify-content-between mt-3 py-1 ${currentViewMode == 'list' ? 'sf-border-bottom' : ''}`}>
<h4 className="sf-heading m-0 d-flex align-items-center"> <h4 className="sf-heading m-0 d-flex align-items-center">
<span className="sf3-font-mine sf3-font nav-icon" aria-hidden="true"></span> <span className="sf3-font-mine sf3-font nav-icon" aria-hidden="true"></span>
{gettext('My Libraries')} {gettext('My Libraries')}
@@ -229,7 +308,7 @@ class Libraries extends Component {
</div> </div>
{this.state.errorMsg ? <p className="error text-center mt-8">{this.state.errorMsg}</p> : ( {this.state.errorMsg ? <p className="error text-center mt-8">{this.state.errorMsg}</p> : (
this.state.repoList.length === 0 ? ( this.state.repoList.length === 0 ? (
<p className="libraries-empty-tip">{gettext('No libraries')}</p> <p className={`libraries-empty-tip-in-${currentViewMode}-mode`}>{gettext('No libraries')}</p>
) : ( ) : (
<MylibRepoListView <MylibRepoListView
sortBy={this.state.sortBy} sortBy={this.state.sortBy}
@@ -242,17 +321,26 @@ class Libraries extends Component {
onRepoClick={this.onRepoClick} onRepoClick={this.onRepoClick}
sortRepoList={this.sortRepoList} sortRepoList={this.sortRepoList}
inAllLibs={true} inAllLibs={true}
currentViewMode={currentViewMode}
/> />
)) ))
} }
</div> </div>
)} )}
<div className="pb-3"> <div className="pb-3">
<SharedLibs inAllLibs={true} repoList={this.state.sharedRepoList} /> <SharedLibs
repoList={this.state.sharedRepoList}
inAllLibs={true}
currentViewMode={currentViewMode}
/>
</div> </div>
{canViewOrg && {canViewOrg &&
<div className="pb-3"> <div className="pb-3">
<SharedWithAll inAllLibs={true} repoList={this.state.publicRepoList} /> <SharedWithAll
repoList={this.state.publicRepoList}
inAllLibs={true}
currentViewMode={currentViewMode}
/>
</div> </div>
} }
<div className="group-list-panel"> <div className="group-list-panel">
@@ -263,6 +351,7 @@ class Libraries extends Component {
key={index} key={index}
group={group} group={group}
updateGroup={this.updateGroup} updateGroup={this.updateGroup}
currentViewMode={currentViewMode}
/> />
); );
}) })

View File

@@ -25,6 +25,7 @@ import LibOldFilesAutoDelDialog from '../../components/dialog/lib-old-files-auto
import RepoMonitoredIcon from '../../components/repo-monitored-icon'; import RepoMonitoredIcon from '../../components/repo-monitored-icon';
const propTypes = { const propTypes = {
currentViewMode: PropTypes.string,
repo: PropTypes.object.isRequired, repo: PropTypes.object.isRequired,
isItemFreezed: PropTypes.bool.isRequired, isItemFreezed: PropTypes.bool.isRequired,
onFreezedItem: PropTypes.func.isRequired, onFreezedItem: PropTypes.func.isRequired,
@@ -318,16 +319,21 @@ class MylibRepoListItem extends React.Component {
}; };
renderPCUI = () => { renderPCUI = () => {
let repo = this.props.repo; const { repo, currentViewMode = 'list' } = this.props;
let iconUrl = Utils.getLibIconUrl(repo); let useBigLibaryIcon = currentViewMode == 'grid';
let iconUrl = Utils.getLibIconUrl(repo, useBigLibaryIcon);
let iconTitle = Utils.getLibIconTitle(repo); let iconTitle = Utils.getLibIconTitle(repo);
let repoURL = `${siteRoot}library/${repo.repo_id}/${Utils.encodePath(repo.repo_name)}/`; let repoURL = `${siteRoot}library/${repo.repo_id}/${Utils.encodePath(repo.repo_name)}/`;
return ( return currentViewMode == 'list' ? (
<tr className={this.state.highlight ? 'tr-highlight' : ''} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave} onFocus={this.onFocus}> <tr className={this.state.highlight ? 'tr-highlight' : ''} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave} onFocus={this.onFocus}>
<td className="text-center"> <td className="text-center">
<a href="#" role="button" aria-label={this.state.isStarred ? gettext('Unstar') : gettext('Star')} onClick={this.onToggleStarRepo}> <i
<i className={`fa-star ${this.state.isStarred ? 'fas' : 'far star-empty'}`}></i> role="button"
</a> title={this.state.isStarred ? gettext('Unstar') : gettext('Star')}
aria-label={this.state.isStarred ? gettext('Unstar') : gettext('Star')}
onClick={this.onToggleStarRepo}
className={`op-icon m-0 ${this.state.isStarred ? 'sf3-font-star' : 'sf3-font-star-empty'} sf3-font`}
></i>
</td> </td>
<td><img src={iconUrl} title={iconTitle} alt={iconTitle} width="24" /></td> <td><img src={iconUrl} title={iconTitle} alt={iconTitle} width="24" /></td>
<td> <td>
@@ -341,7 +347,7 @@ class MylibRepoListItem extends React.Component {
{!this.state.isRenaming && repo.repo_name && ( {!this.state.isRenaming && repo.repo_name && (
<Fragment> <Fragment>
<Link to={repoURL}>{repo.repo_name}</Link> <Link to={repoURL}>{repo.repo_name}</Link>
{repo.monitored && <RepoMonitoredIcon repoID={repo.repo_id} />} {repo.monitored && <RepoMonitoredIcon repoID={repo.repo_id} className="ml-1 op-icon" />}
</Fragment> </Fragment>
)} )}
{!this.state.isRenaming && !repo.repo_name && {!this.state.isRenaming && !repo.repo_name &&
@@ -367,6 +373,54 @@ class MylibRepoListItem extends React.Component {
{storages.length > 0 && <td>{repo.storage_name}</td>} {storages.length > 0 && <td>{repo.storage_name}</td>}
<td title={moment(repo.last_modified).format('llll')}>{moment(repo.last_modified).fromNow()}</td> <td title={moment(repo.last_modified).format('llll')}>{moment(repo.last_modified).fromNow()}</td>
</tr> </tr>
) : (
<div
className="library-grid-item px-3 d-flex justify-content-between align-items-center"
onMouseEnter={this.onMouseEnter}
onMouseLeave={this.onMouseLeave}
onFocus={this.onFocus}
>
<div className="d-flex align-items-center">
<img src={iconUrl} title={iconTitle} alt={iconTitle} width="36" className="mr-2" />
{this.state.isRenaming && (
<Rename
name={repo.repo_name}
onRenameConfirm={this.onRenameConfirm}
onRenameCancel={this.onRenameCancel}
/>
)}
{!this.state.isRenaming && repo.repo_name && (
<Fragment>
<Link to={repoURL}>{repo.repo_name}</Link>
<i
role="button"
title={this.state.isStarred ? gettext('Unstar') : gettext('Star')}
aria-label={this.state.isStarred ? gettext('Unstar') : gettext('Star')}
className={`op-icon library-grid-item-icon ${this.state.isStarred ? 'sf3-font-star' : 'sf3-font-star-empty'} sf3-font`}
onClick={this.onToggleStarRepo}
>
</i>
{repo.monitored && <RepoMonitoredIcon repoID={repo.repo_id} className="op-icon library-grid-item-icon" />}
</Fragment>
)}
{!this.state.isRenaming && !repo.repo_name &&
(<span>{gettext('Broken (please contact your administrator to fix this library)')}</span>)
}
</div>
{(repo.repo_name && this.state.isOpIconShow) && (
<div>
<a href="#" className="op-icon sf3-font-share sf3-font" title={gettext('Share')} role="button" aria-label={gettext('Share')} onClick={this.onShareToggle}></a>
<a href="#" className="op-icon sf3-font-delete1 sf3-font" title={gettext('Delete')} role="button" aria-label={gettext('Delete')} onClick={this.onDeleteToggle}></a>
<MylibRepoMenu
isPC={true}
repo={this.props.repo}
onMenuItemClick={this.onMenuItemClick}
onFreezedItem={this.props.onFreezedItem}
onUnfreezedItem={this.onUnfreezedItem}
/>
</div>
)}
</div>
); );
}; };
@@ -390,7 +444,7 @@ class MylibRepoListItem extends React.Component {
{!this.state.isRenaming && repo.repo_name && ( {!this.state.isRenaming && repo.repo_name && (
<div> <div>
<Link to={repoURL}>{repo.repo_name}</Link> <Link to={repoURL}>{repo.repo_name}</Link>
{repo.monitored && <RepoMonitoredIcon repoID={repo.repo_id} />} {repo.monitored && <RepoMonitoredIcon repoID={repo.repo_id} className="ml-1 op-icon" />}
</div> </div>
)} )}
{!this.state.isRenaming && !repo.repo_name && {!this.state.isRenaming && !repo.repo_name &&

View File

@@ -70,6 +70,7 @@ class MylibRepoListView extends React.Component {
onDeleteRepo={this.props.onDeleteRepo} onDeleteRepo={this.props.onDeleteRepo}
onTransferRepo={this.props.onTransferRepo} onTransferRepo={this.props.onTransferRepo}
onMonitorRepo={this.props.onMonitorRepo} onMonitorRepo={this.props.onMonitorRepo}
currentViewMode={this.props.currentViewMode}
/> />
); );
})} })}
@@ -78,11 +79,11 @@ class MylibRepoListView extends React.Component {
}; };
renderPCUI = () => { renderPCUI = () => {
const { inAllLibs } = this.props; const { inAllLibs, currentViewMode = 'list' } = this.props;
const showStorageBackend = !inAllLibs && storages.length > 0; const showStorageBackend = !inAllLibs && storages.length > 0;
const sortIcon = this.props.sortOrder === 'asc' ? <span className="fas fa-caret-up"></span> : <span className="fas fa-caret-down"></span>; const sortIcon = this.props.sortOrder === 'asc' ? <span className="fas fa-caret-up"></span> : <span className="fas fa-caret-down"></span>;
return ( return currentViewMode == 'list' ? (
<table className={inAllLibs ? 'table-thead-hidden' : ''}> <table className={inAllLibs ? 'table-thead-hidden' : ''}>
<thead> <thead>
<tr> <tr>
@@ -99,6 +100,10 @@ class MylibRepoListView extends React.Component {
{this.renderRepoListView()} {this.renderRepoListView()}
</tbody> </tbody>
</table> </table>
) : (
<div className="d-flex justify-content-between flex-wrap">
{this.renderRepoListView()}
</div>
); );
}; };

View File

@@ -54,10 +54,10 @@ class Content extends Component {
}; };
render() { render() {
const { loading, errorMsg, items, sortBy, sortOrder, theadHidden, inAllLibs } = this.props; const { loading, errorMsg, items, sortBy, sortOrder, theadHidden, inAllLibs, currentViewMode } = this.props;
const emptyTip = inAllLibs ? const emptyTip = inAllLibs ?
<p className="libraries-empty-tip">{gettext('No shared libraries')}</p> : <p className={`libraries-empty-tip-in-${currentViewMode}-mode`}>{gettext('No shared libraries')}</p> :
<EmptyTip> <EmptyTip>
<h2>{gettext('No shared libraries')}</h2> <h2>{gettext('No shared libraries')}</h2>
<p>{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.')}</p> <p>{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.')}</p>
@@ -89,30 +89,41 @@ class Content extends Component {
); );
const isDesktop = Utils.isDesktop(); const isDesktop = Utils.isDesktop();
const table = ( const itemsContent = (
<>
{items.map((item, index) => {
return <Item
key={index}
data={item}
isDesktop={isDesktop}
isItemFreezed={this.state.isItemFreezed}
freezeItem={this.freezeItem}
onMonitorRepo={this.props.onMonitorRepo}
currentViewMode={currentViewMode}
/>;
})}
</>
);
const content = currentViewMode == 'list' ? (
<table className={(isDesktop && !theadHidden)? '' : 'table-thead-hidden'}> <table className={(isDesktop && !theadHidden)? '' : 'table-thead-hidden'}>
{isDesktop ? desktopThead : <LibsMobileThead />} {isDesktop ? desktopThead : <LibsMobileThead />}
<tbody> <tbody>
{items.map((item, index) => { {itemsContent}
return <Item
key={index}
data={item}
isDesktop={isDesktop}
isItemFreezed={this.state.isItemFreezed}
freezeItem={this.freezeItem}
onMonitorRepo={this.props.onMonitorRepo}
/>;
})}
</tbody> </tbody>
</table> </table>
) : (
<div className="d-flex justify-content-between flex-wrap">
{itemsContent}
</div>
); );
return items.length ? table : emptyTip; return items.length ? content : emptyTip;
} }
} }
} }
Content.propTypes = { Content.propTypes = {
currentViewMode: PropTypes.string,
inAllLibs: PropTypes.bool.isRequired, inAllLibs: PropTypes.bool.isRequired,
theadHidden: PropTypes.bool.isRequired, theadHidden: PropTypes.bool.isRequired,
loading: PropTypes.bool.isRequired, loading: PropTypes.bool.isRequired,
@@ -257,9 +268,9 @@ class Item extends Component {
return null; return null;
} }
const data = this.props.data; const { data, currentViewMode = 'list' } = this.props;
const useBigLibaryIcon = currentViewMode == 'grid';
data.icon_url = Utils.getLibIconUrl(data); data.icon_url = Utils.getLibIconUrl(data, useBigLibaryIcon);
data.icon_title = Utils.getLibIconTitle(data); data.icon_title = Utils.getLibIconTitle(data);
let iconVisibility = this.state.showOpIcon ? '' : ' invisible'; let iconVisibility = this.state.showOpIcon ? '' : ' invisible';
@@ -272,46 +283,96 @@ class Item extends Component {
const desktopItem = ( const desktopItem = (
<Fragment> <Fragment>
<tr className={this.state.highlight ? 'tr-highlight' : ''} onMouseOver={this.handleMouseOver} onMouseOut={this.handleMouseOut} onFocus={this.handleMouseOver}> {currentViewMode == 'list' ? (
<td className="text-center"> <tr className={this.state.highlight ? 'tr-highlight' : ''} onMouseOver={this.handleMouseOver} onMouseOut={this.handleMouseOut} onFocus={this.handleMouseOver}>
<a href="#" role="button" aria-label={this.state.isStarred ? gettext('Unstar') : gettext('Star')} onClick={this.onToggleStarRepo}> <td className="text-center">
<i className={`fa-star ${this.state.isStarred ? 'fas' : 'far star-empty'}`}></i> <i
</a>
</td>
<td><img src={data.icon_url} title={data.icon_title} alt={data.icon_title} width="24" /></td>
<td>
<Fragment>
<Link to={shareRepoUrl}>{data.repo_name}</Link>
{data.monitored && <RepoMonitoredIcon repoID={data.repo_id} />}
</Fragment>
</td>
<td>
{(isPro && data.is_admin) &&
<a href="#" className={shareIconClassName} title={gettext('Share')} role="button" aria-label={gettext('Share')} onClick={this.share}></a>
}
<a href="#" className={leaveShareIconClassName} title={gettext('Leave Share')} role="button" aria-label={gettext('Leave Share')} onClick={this.leaveShare}></a>
{enableMonitorRepo &&
<Dropdown isOpen={this.state.isOpMenuOpen} toggle={this.toggleOpMenu}>
<DropdownToggle
tag="i"
role="button" role="button"
tabIndex="0" title={this.state.isStarred ? gettext('Unstar') : gettext('Star')}
className={`sf-dropdown-toggle sf3-font-more sf3-font ${iconVisibility}`} aria-label={this.state.isStarred ? gettext('Unstar') : gettext('Star')}
title={gettext('More operations')} onClick={this.onToggleStarRepo}
aria-label={gettext('More operations')} className={`op-icon m-0 ${this.state.isStarred ? 'sf3-font-star' : 'sf3-font-star-empty'} sf3-font`}
data-toggle="dropdown" ></i>
aria-expanded={this.state.isOpMenuOpen} </td>
/> <td><img src={data.icon_url} title={data.icon_title} alt={data.icon_title} width="24" /></td>
<DropdownMenu> <td>
<DropdownItem onClick={data.monitored ? this.unwatchFileChanges : this.watchFileChanges}>{data.monitored ? gettext('Unwatch File Changes') : gettext('Watch File Changes')}</DropdownItem> <Fragment>
</DropdownMenu> <Link to={shareRepoUrl}>{data.repo_name}</Link>
</Dropdown> {data.monitored && <RepoMonitoredIcon repoID={data.repo_id} className="ml-1 op-icon" />}
} </Fragment>
</td> </td>
<td>{data.size}</td> <td>
<td title={moment(data.last_modified).format('llll')}>{moment(data.last_modified).fromNow()}</td> {(isPro && data.is_admin) &&
<td title={data.owner_contact_email}>{data.owner_name}</td> <a href="#" className={shareIconClassName} title={gettext('Share')} role="button" aria-label={gettext('Share')} onClick={this.share}></a>
</tr> }
<a href="#" className={leaveShareIconClassName} title={gettext('Leave Share')} role="button" aria-label={gettext('Leave Share')} onClick={this.leaveShare}></a>
{enableMonitorRepo &&
<Dropdown isOpen={this.state.isOpMenuOpen} toggle={this.toggleOpMenu}>
<DropdownToggle
tag="i"
role="button"
tabIndex="0"
className={`sf-dropdown-toggle sf3-font-more sf3-font ${iconVisibility}`}
title={gettext('More operations')}
aria-label={gettext('More operations')}
data-toggle="dropdown"
aria-expanded={this.state.isOpMenuOpen}
/>
<DropdownMenu>
<DropdownItem onClick={data.monitored ? this.unwatchFileChanges : this.watchFileChanges}>{data.monitored ? gettext('Unwatch File Changes') : gettext('Watch File Changes')}</DropdownItem>
</DropdownMenu>
</Dropdown>
}
</td>
<td>{data.size}</td>
<td title={moment(data.last_modified).format('llll')}>{moment(data.last_modified).fromNow()}</td>
<td title={data.owner_contact_email}>{data.owner_name}</td>
</tr>
): (
<div
className="library-grid-item px-3 d-flex justify-content-between align-items-center"
onMouseOver={this.handleMouseOver}
onMouseOut={this.handleMouseOut}
onFocus={this.handleMouseOver}
>
<div className="d-flex align-items-center">
<img src={data.icon_url} title={data.icon_title} alt={data.icon_title} width="36" className="mr-2" />
<Link to={shareRepoUrl}>{data.repo_name}</Link>
<i
role="button"
title={this.state.isStarred ? gettext('Unstar') : gettext('Star')}
aria-label={this.state.isStarred ? gettext('Unstar') : gettext('Star')}
onClick={this.onToggleStarRepo}
className={`op-icon library-grid-item-icon ${this.state.isStarred ? 'sf3-font-star' : 'sf3-font-star-empty'} sf3-font`}
></i>
{data.monitored && <RepoMonitoredIcon repoID={data.repo_id} className="op-icon library-grid-item-icon" />}
</div>
<div>
{(isPro && data.is_admin) &&
<a href="#" className={shareIconClassName} title={gettext('Share')} role="button" aria-label={gettext('Share')} onClick={this.share}></a>
}
<a href="#" className={leaveShareIconClassName} title={gettext('Leave Share')} role="button" aria-label={gettext('Leave Share')} onClick={this.leaveShare}></a>
{enableMonitorRepo &&
<Dropdown isOpen={this.state.isOpMenuOpen} toggle={this.toggleOpMenu}>
<DropdownToggle
tag="i"
role="button"
tabIndex="0"
className={`sf-dropdown-toggle sf3-font-more sf3-font ${iconVisibility}`}
title={gettext('More operations')}
aria-label={gettext('More operations')}
data-toggle="dropdown"
aria-expanded={this.state.isOpMenuOpen}
/>
<DropdownMenu>
<DropdownItem onClick={data.monitored ? this.unwatchFileChanges : this.watchFileChanges}>{data.monitored ? gettext('Unwatch File Changes') : gettext('Watch File Changes')}</DropdownItem>
</DropdownMenu>
</Dropdown>
}
</div>
</div>
)}
{this.state.isShowSharedDialog && ( {this.state.isShowSharedDialog && (
<ModalPotal> <ModalPotal>
<ShareDialog <ShareDialog
@@ -336,7 +397,7 @@ class Item extends Component {
<td onClick={this.visitRepo}><img src={data.icon_url} title={data.icon_title} alt={data.icon_title} width="24" /></td> <td onClick={this.visitRepo}><img src={data.icon_url} title={data.icon_title} alt={data.icon_title} width="24" /></td>
<td onClick={this.visitRepo}> <td onClick={this.visitRepo}>
<Link to={shareRepoUrl}>{data.repo_name}</Link> <Link to={shareRepoUrl}>{data.repo_name}</Link>
{data.monitored && <RepoMonitoredIcon repoID={data.repo_id} />} {data.monitored && <RepoMonitoredIcon repoID={data.repo_id} className="ml-1 op-icon" />}
<br /> <br />
<span className="item-meta-info" title={data.owner_contact_email}>{data.owner_name}</span> <span className="item-meta-info" title={data.owner_contact_email}>{data.owner_name}</span>
<span className="item-meta-info">{data.size}</span> <span className="item-meta-info">{data.size}</span>
@@ -386,6 +447,7 @@ class Item extends Component {
} }
Item.propTypes = { Item.propTypes = {
currentViewMode: PropTypes.string,
isDesktop: PropTypes.bool.isRequired, isDesktop: PropTypes.bool.isRequired,
data: PropTypes.object.isRequired, data: PropTypes.object.isRequired,
isItemFreezed: PropTypes.bool.isRequired, isItemFreezed: PropTypes.bool.isRequired,
@@ -425,7 +487,7 @@ class SharedLibraries extends Component {
} else { } else {
this.setState({ this.setState({
loading: false, loading: false,
items: Utils.sortRepos(this.props.repoList, this.state.sortBy, this.state.sortOrder) items: this.props.repoList
}); });
} }
} }
@@ -457,18 +519,20 @@ class SharedLibraries extends Component {
}; };
renderContent = () => { renderContent = () => {
const { inAllLibs = false } = this.props; // inAllLibs: in 'All Libs'('Files') page const { inAllLibs = false, currentViewMode = 'list', repoList } = this.props; // inAllLibs: in 'All Libs'('Files') page
const { items } = this.state;
return ( return (
<Content <Content
loading={this.state.loading} loading={this.state.loading}
errorMsg={this.state.errorMsg} errorMsg={this.state.errorMsg}
items={this.state.items} items={inAllLibs ? repoList : items}
sortBy={this.state.sortBy} sortBy={this.state.sortBy}
sortOrder={this.state.sortOrder} sortOrder={this.state.sortOrder}
sortItems={this.sortItems} sortItems={this.sortItems}
onMonitorRepo={this.onMonitorRepo} onMonitorRepo={this.onMonitorRepo}
theadHidden={inAllLibs} theadHidden={inAllLibs}
inAllLibs={inAllLibs} inAllLibs={inAllLibs}
currentViewMode={currentViewMode}
/> />
); );
}; };
@@ -482,14 +546,14 @@ class SharedLibraries extends Component {
}; };
render() { render() {
const { inAllLibs = false } = this.props; // inAllLibs: in 'All Libs'('Files') page const { inAllLibs = false, currentViewMode = 'list' } = this.props; // inAllLibs: in 'All Libs'('Files') page
return ( return (
<Fragment> <Fragment>
{inAllLibs ? ( {inAllLibs ? (
<> <>
<div className="d-flex justify-content-between mt-3 py-1 sf-border-bottom"> <div className={`d-flex justify-content-between mt-3 py-1 ${currentViewMode == 'list' ? 'sf-border-bottom' : ''}`}>
<h4 className="sf-heading m-0"> <h4 className="sf-heading m-0">
{inAllLibs && <span className="sf3-font-share-with-me sf3-font nav-icon" aria-hidden="true"></span>} <span className="sf3-font-share-with-me sf3-font nav-icon" aria-hidden="true"></span>
{gettext('Shared with me')} {gettext('Shared with me')}
</h4> </h4>
{this.renderSortIconInMobile()} {this.renderSortIconInMobile()}
@@ -523,6 +587,7 @@ class SharedLibraries extends Component {
} }
SharedLibraries.propTypes = { SharedLibraries.propTypes = {
currentViewMode: PropTypes.string,
inAllLibs: PropTypes.bool, inAllLibs: PropTypes.bool,
repoList: PropTypes.array, repoList: PropTypes.array,
}; };

View File

@@ -16,6 +16,7 @@ import TopToolbar from './top-toolbar';
const propTypes = { const propTypes = {
onShowSidePanel: PropTypes.func, onShowSidePanel: PropTypes.func,
onSearchedClick: PropTypes.func, onSearchedClick: PropTypes.func,
currentViewMode: PropTypes.string,
inAllLibs: PropTypes.bool, inAllLibs: PropTypes.bool,
repoList: PropTypes.array, repoList: PropTypes.array,
}; };
@@ -117,10 +118,10 @@ class PublicSharedView extends React.Component {
}; };
renderContent = () => { renderContent = () => {
const { inAllLibs = false } = this.props; // inAllLibs: in 'All Libs'('Files') page const { inAllLibs = false, currentViewMode = 'list' } = this.props; // inAllLibs: in 'All Libs'('Files') page
const { errMessage } = this.state; const { errMessage } = this.state;
const emptyTip = inAllLibs ? const emptyTip = inAllLibs ?
<p className="libraries-empty-tip">{gettext('No public libraries')}</p> : ( <p className={`libraries-empty-tip-in-${currentViewMode}-mode`}>{gettext('No public libraries')}</p> : (
<EmptyTip> <EmptyTip>
<h2>{gettext('No public libraries')}</h2> <h2>{gettext('No public libraries')}</h2>
<p>{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" button in the menu bar.')}</p> <p>{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" button in the menu bar.')}</p>
@@ -141,6 +142,7 @@ class PublicSharedView extends React.Component {
onItemUnshare={this.onItemUnshare} onItemUnshare={this.onItemUnshare}
onItemDelete={this.onItemDelete} onItemDelete={this.onItemDelete}
theadHidden={inAllLibs} theadHidden={inAllLibs}
currentViewMode={currentViewMode}
/> />
} }
</> </>
@@ -157,12 +159,12 @@ class PublicSharedView extends React.Component {
}; };
render() { render() {
const { inAllLibs = false } = this.props; // inAllLibs: in 'All Libs'('Files') page const { inAllLibs = false, currentViewMode = 'list' } = this.props; // inAllLibs: in 'All Libs'('Files') page
if (inAllLibs) { if (inAllLibs) {
return ( return (
<> <>
<div className="d-flex justify-content-between mt-3 py-1 sf-border-bottom"> <div className={`d-flex justify-content-between mt-3 py-1 ${currentViewMode == 'list' ? 'sf-border-bottom' : ''}`}>
<h4 className="sf-heading m-0"> <h4 className="sf-heading m-0">
<span className="sf3-font-share-with-all sf3-font nav-icon" aria-hidden="true"></span> <span className="sf3-font-share-with-all sf3-font nav-icon" aria-hidden="true"></span>
{gettext('Shared with all')} {gettext('Shared with all')}