mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-02 15:38:15 +00:00
add context menu for libraries list (#6909)
* add context menu for libraries list * support context menu on grid mode * optimize code --------- Co-authored-by: zhouwenxuan <aries@Mac.local>
This commit is contained in:
@@ -31,3 +31,27 @@ export function showMenu(opts = {}, target) {
|
|||||||
export function hideMenu(opts = {}, target) {
|
export function hideMenu(opts = {}, target) {
|
||||||
dispatchGlobalEvent(MENU_HIDE, assign({}, opts, { type: MENU_HIDE }), target);
|
dispatchGlobalEvent(MENU_HIDE, assign({}, opts, { type: MENU_HIDE }), target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function handleContextClick(event, id, menuList, currentObject = null) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
let x = event.clientX || (event.touches && event.touches[0].pageX);
|
||||||
|
let y = event.clientY || (event.touches && event.touches[0].pageY);
|
||||||
|
|
||||||
|
hideMenu();
|
||||||
|
|
||||||
|
let showMenuConfig = {
|
||||||
|
id: id,
|
||||||
|
position: { x, y },
|
||||||
|
target: event.target,
|
||||||
|
currentObject: currentObject,
|
||||||
|
menuList: menuList,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (menuList.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
showMenu(showMenuConfig);
|
||||||
|
}
|
||||||
|
@@ -32,7 +32,8 @@ const propTypes = {
|
|||||||
onItemUnshare: PropTypes.func.isRequired,
|
onItemUnshare: PropTypes.func.isRequired,
|
||||||
onItemRename: PropTypes.func,
|
onItemRename: PropTypes.func,
|
||||||
onItemDelete: PropTypes.func,
|
onItemDelete: PropTypes.func,
|
||||||
onMonitorRepo: PropTypes.func
|
onMonitorRepo: PropTypes.func,
|
||||||
|
onContextMenu: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
class SharedRepoListItem extends React.Component {
|
class SharedRepoListItem extends React.Component {
|
||||||
@@ -157,7 +158,7 @@ class SharedRepoListItem extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
onMenuItemClick = (e) => {
|
onMenuItemClick = (e) => {
|
||||||
let operation = e.target.dataset.toggle;
|
let operation = e.target.dataset.toggle || e.target.dataset.operation;
|
||||||
switch (operation) {
|
switch (operation) {
|
||||||
case 'Rename':
|
case 'Rename':
|
||||||
this.onItemRenameToggle();
|
this.onItemRenameToggle();
|
||||||
@@ -616,6 +617,10 @@ class SharedRepoListItem extends React.Component {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
handleContextMenu = (e) => {
|
||||||
|
this.props.onContextMenu(e, this.props.repo);
|
||||||
|
};
|
||||||
|
|
||||||
renderPCUI = () => {
|
renderPCUI = () => {
|
||||||
const { isStarred } = this.state;
|
const { isStarred } = this.state;
|
||||||
let { iconUrl, iconTitle, libPath } = this.getRepoComputeParams();
|
let { iconUrl, iconTitle, libPath } = this.getRepoComputeParams();
|
||||||
@@ -627,6 +632,7 @@ class SharedRepoListItem extends React.Component {
|
|||||||
onMouseOver={this.onMouseOver}
|
onMouseOver={this.onMouseOver}
|
||||||
onMouseLeave={this.onMouseLeave}
|
onMouseLeave={this.onMouseLeave}
|
||||||
onFocus={this.onMouseEnter}
|
onFocus={this.onMouseEnter}
|
||||||
|
onContextMenu={this.handleContextMenu}
|
||||||
>
|
>
|
||||||
<td className="text-center">
|
<td className="text-center">
|
||||||
<i
|
<i
|
||||||
@@ -659,6 +665,7 @@ class SharedRepoListItem extends React.Component {
|
|||||||
onMouseEnter={this.onMouseEnter}
|
onMouseEnter={this.onMouseEnter}
|
||||||
onMouseLeave={this.onMouseLeave}
|
onMouseLeave={this.onMouseLeave}
|
||||||
onFocus={this.onMouseEnter}
|
onFocus={this.onMouseEnter}
|
||||||
|
onContextMenu={this.handleContextMenu}
|
||||||
>
|
>
|
||||||
<div className="d-flex align-items-center text-truncate">
|
<div className="d-flex align-items-center text-truncate">
|
||||||
<img src={iconUrl} title={iconTitle} alt={iconTitle} width="36" className="mr-2" />
|
<img src={iconUrl} title={iconTitle} alt={iconTitle} width="36" className="mr-2" />
|
||||||
|
@@ -7,6 +7,8 @@ import toaster from '../toast';
|
|||||||
import LibsMobileThead from '../libs-mobile-thead';
|
import LibsMobileThead from '../libs-mobile-thead';
|
||||||
import Loading from '../loading';
|
import Loading from '../loading';
|
||||||
import { LIST_MODE } from '../dir-view-mode/constants';
|
import { LIST_MODE } from '../dir-view-mode/constants';
|
||||||
|
import ContextMenu from '../context-menu/context-menu';
|
||||||
|
import { hideMenu, handleContextClick } from '../context-menu/actions';
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
currentViewMode: PropTypes.string,
|
currentViewMode: PropTypes.string,
|
||||||
@@ -33,6 +35,7 @@ class SharedRepoListView extends React.Component {
|
|||||||
this.state = {
|
this.state = {
|
||||||
isItemFreezed: false,
|
isItemFreezed: false,
|
||||||
};
|
};
|
||||||
|
this.repoItems = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
sortByName = (e) => {
|
sortByName = (e) => {
|
||||||
@@ -86,13 +89,41 @@ class SharedRepoListView extends React.Component {
|
|||||||
this.props.onItemRename(repo, newName);
|
this.props.onItemRename(repo, newName);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
setRepoItemRef = (index) => item => {
|
||||||
|
this.repoItems[index] = item;
|
||||||
|
};
|
||||||
|
|
||||||
|
getRepoIndex = (repo) => {
|
||||||
|
return this.props.repoList.findIndex(item => {
|
||||||
|
return item.repo_id === repo.repo_id;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
onMenuItemClick = (operation, currentObject, event) => {
|
||||||
|
const index = this.getRepoIndex(currentObject);
|
||||||
|
if (this.repoItems[index]) {
|
||||||
|
this.repoItems[index].onMenuItemClick(event);
|
||||||
|
}
|
||||||
|
hideMenu();
|
||||||
|
};
|
||||||
|
|
||||||
|
onContextMenu = (event, repo) => {
|
||||||
|
event.preventDefault();
|
||||||
|
const { libraryType, currentGroup } = this.props;
|
||||||
|
const isPublic = libraryType === 'public';
|
||||||
|
const id = isPublic ? 'shared-repo-item-menu' : `shared-repo-item-menu-${currentGroup.id}`;
|
||||||
|
const menuList = Utils.getSharedRepoOperationList(repo, currentGroup, isPublic);
|
||||||
|
handleContextClick(event, id, menuList, repo);
|
||||||
|
};
|
||||||
|
|
||||||
renderRepoListView = () => {
|
renderRepoListView = () => {
|
||||||
const { currentViewMode = LIST_MODE } = this.props;
|
const { currentViewMode = LIST_MODE } = this.props;
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
{this.props.repoList.map(repo => {
|
{this.props.repoList.map((repo, index) => {
|
||||||
return (
|
return (
|
||||||
<SharedRepoListItem
|
<SharedRepoListItem
|
||||||
|
ref={this.setRepoItemRef(index)}
|
||||||
key={repo.repo_id}
|
key={repo.repo_id}
|
||||||
repo={repo}
|
repo={repo}
|
||||||
libraryType={this.props.libraryType}
|
libraryType={this.props.libraryType}
|
||||||
@@ -105,6 +136,7 @@ class SharedRepoListView extends React.Component {
|
|||||||
onItemRename={this.props.onItemRename}
|
onItemRename={this.props.onItemRename}
|
||||||
onMonitorRepo={this.props.onMonitorRepo}
|
onMonitorRepo={this.props.onMonitorRepo}
|
||||||
currentViewMode={currentViewMode}
|
currentViewMode={currentViewMode}
|
||||||
|
onContextMenu={this.onContextMenu}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -113,10 +145,11 @@ class SharedRepoListView extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
renderPCUI = () => {
|
renderPCUI = () => {
|
||||||
const { theadHidden = false, currentViewMode = LIST_MODE } = this.props;
|
const { theadHidden = false, currentViewMode = LIST_MODE, currentGroup, libraryType } = this.props;
|
||||||
const { sortByName, sortByTime, sortBySize, sortIcon } = this.getSortMetaData();
|
const { sortByName, sortByTime, sortBySize, sortIcon } = this.getSortMetaData();
|
||||||
|
|
||||||
return currentViewMode == LIST_MODE ? (
|
const content = currentViewMode == LIST_MODE ? (
|
||||||
|
<>
|
||||||
<table className={theadHidden ? 'table-thead-hidden' : ''}>
|
<table className={theadHidden ? 'table-thead-hidden' : ''}>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -133,11 +166,22 @@ 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()}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{content}
|
||||||
|
<ContextMenu
|
||||||
|
id={`${libraryType === 'public' ? 'shared-repo-item-menu' : `shared-repo-item-menu-${currentGroup.id}`}`}
|
||||||
|
onMenuItemClick={this.onMenuItemClick}
|
||||||
|
/>;
|
||||||
|
</>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
renderMobileUI = () => {
|
renderMobileUI = () => {
|
||||||
|
@@ -129,6 +129,7 @@ class GroupItem extends React.Component {
|
|||||||
{group.repos.length === 0 ?
|
{group.repos.length === 0 ?
|
||||||
emptyTip :
|
emptyTip :
|
||||||
<SharedRepoListView
|
<SharedRepoListView
|
||||||
|
key={`group-${group.id}`}
|
||||||
inAllLibs={inAllLibs}
|
inAllLibs={inAllLibs}
|
||||||
theadHidden={true}
|
theadHidden={true}
|
||||||
isShowRepoOwner={false}
|
isShowRepoOwner={false}
|
||||||
|
@@ -34,6 +34,7 @@ const propTypes = {
|
|||||||
onDeleteRepo: PropTypes.func.isRequired,
|
onDeleteRepo: PropTypes.func.isRequired,
|
||||||
onTransferRepo: PropTypes.func.isRequired,
|
onTransferRepo: PropTypes.func.isRequired,
|
||||||
onMonitorRepo: PropTypes.func.isRequired,
|
onMonitorRepo: PropTypes.func.isRequired,
|
||||||
|
onContextMenu: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
class MylibRepoListItem extends React.Component {
|
class MylibRepoListItem extends React.Component {
|
||||||
@@ -303,6 +304,10 @@ class MylibRepoListItem extends React.Component {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
handleContextMenu = (event) => {
|
||||||
|
this.props.onContextMenu(event, this.props.repo);
|
||||||
|
};
|
||||||
|
|
||||||
renderPCUI = () => {
|
renderPCUI = () => {
|
||||||
const { isStarred } = this.state;
|
const { isStarred } = this.state;
|
||||||
const { repo, currentViewMode = LIST_MODE } = this.props;
|
const { repo, currentViewMode = LIST_MODE } = this.props;
|
||||||
@@ -311,7 +316,7 @@ class MylibRepoListItem extends React.Component {
|
|||||||
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 currentViewMode == LIST_MODE ? (
|
return currentViewMode == LIST_MODE ? (
|
||||||
<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} onContextMenu={this.handleContextMenu}>
|
||||||
<td className="text-center">
|
<td className="text-center">
|
||||||
<i
|
<i
|
||||||
role="button"
|
role="button"
|
||||||
@@ -366,6 +371,7 @@ class MylibRepoListItem extends React.Component {
|
|||||||
onMouseEnter={this.onMouseEnter}
|
onMouseEnter={this.onMouseEnter}
|
||||||
onMouseLeave={this.onMouseLeave}
|
onMouseLeave={this.onMouseLeave}
|
||||||
onFocus={this.onFocus}
|
onFocus={this.onFocus}
|
||||||
|
onContextMenu={this.handleContextMenu}
|
||||||
>
|
>
|
||||||
<div className="d-flex align-items-center text-truncate">
|
<div className="d-flex align-items-center text-truncate">
|
||||||
<img src={iconUrl} title={iconTitle} alt={iconTitle} width="36" className="mr-2" />
|
<img src={iconUrl} title={iconTitle} alt={iconTitle} width="36" className="mr-2" />
|
||||||
|
@@ -5,6 +5,9 @@ import { gettext, storages } from '../../utils/constants';
|
|||||||
import MylibRepoListItem from './mylib-repo-list-item';
|
import MylibRepoListItem from './mylib-repo-list-item';
|
||||||
import LibsMobileThead from '../../components/libs-mobile-thead';
|
import LibsMobileThead from '../../components/libs-mobile-thead';
|
||||||
import { LIST_MODE } from '../../components/dir-view-mode/constants';
|
import { LIST_MODE } from '../../components/dir-view-mode/constants';
|
||||||
|
import ContextMenu from '../../components/context-menu/context-menu';
|
||||||
|
import { Utils } from '../../utils/utils';
|
||||||
|
import { hideMenu, handleContextClick } from '../../components/context-menu/actions';
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
sortBy: PropTypes.string.isRequired,
|
sortBy: PropTypes.string.isRequired,
|
||||||
@@ -26,6 +29,7 @@ class MylibRepoListView extends React.Component {
|
|||||||
this.state = {
|
this.state = {
|
||||||
isItemFreezed: false,
|
isItemFreezed: false,
|
||||||
};
|
};
|
||||||
|
this.repoItems = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
onFreezedItem = () => {
|
onFreezedItem = () => {
|
||||||
@@ -57,12 +61,37 @@ class MylibRepoListView extends React.Component {
|
|||||||
this.props.sortRepoList(sortBy, sortOrder);
|
this.props.sortRepoList(sortBy, sortOrder);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onContextMenu = (event, repo) => {
|
||||||
|
event.preventDefault();
|
||||||
|
const id = 'mylib-repo-item-menu';
|
||||||
|
const menuList = Utils.getRepoOperationList(repo);
|
||||||
|
handleContextClick(event, id, menuList, repo);
|
||||||
|
};
|
||||||
|
|
||||||
|
setRepoItemRef = (index) => item => {
|
||||||
|
this.repoItems[index] = item;
|
||||||
|
};
|
||||||
|
|
||||||
|
getRepoIndex = (repo) => {
|
||||||
|
return this.props.repoList.findIndex(item => {
|
||||||
|
return item.repo_id === repo.repo_id;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
onMenuItemClick = (operation, currentObject) => {
|
||||||
|
const index = this.getRepoIndex(currentObject);
|
||||||
|
this.repoItems[index].onMenuItemClick(operation);
|
||||||
|
|
||||||
|
hideMenu();
|
||||||
|
};
|
||||||
|
|
||||||
renderRepoListView = () => {
|
renderRepoListView = () => {
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
{this.props.repoList.map(item => {
|
{this.props.repoList.map((item, index) => {
|
||||||
return (
|
return (
|
||||||
<MylibRepoListItem
|
<MylibRepoListItem
|
||||||
|
ref={this.setRepoItemRef(index)}
|
||||||
key={item.repo_id}
|
key={item.repo_id}
|
||||||
repo={item}
|
repo={item}
|
||||||
isItemFreezed={this.state.isItemFreezed}
|
isItemFreezed={this.state.isItemFreezed}
|
||||||
@@ -73,6 +102,7 @@ class MylibRepoListView extends React.Component {
|
|||||||
onTransferRepo={this.props.onTransferRepo}
|
onTransferRepo={this.props.onTransferRepo}
|
||||||
onMonitorRepo={this.props.onMonitorRepo}
|
onMonitorRepo={this.props.onMonitorRepo}
|
||||||
currentViewMode={this.props.currentViewMode}
|
currentViewMode={this.props.currentViewMode}
|
||||||
|
onContextMenu={this.onContextMenu}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -130,6 +160,10 @@ class MylibRepoListView extends React.Component {
|
|||||||
<MediaQuery query="(max-width: 767.8px)">
|
<MediaQuery query="(max-width: 767.8px)">
|
||||||
{this.renderMobileUI()}
|
{this.renderMobileUI()}
|
||||||
</MediaQuery>
|
</MediaQuery>
|
||||||
|
<ContextMenu
|
||||||
|
id="mylib-repo-item-menu"
|
||||||
|
onMenuItemClick={this.onMenuItemClick}
|
||||||
|
/>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -17,6 +17,8 @@ import ShareDialog from '../../components/dialog/share-dialog';
|
|||||||
import SortOptionsDialog from '../../components/dialog/sort-options';
|
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';
|
||||||
|
|
||||||
class Content extends Component {
|
class Content extends Component {
|
||||||
|
|
||||||
@@ -25,6 +27,7 @@ class Content extends Component {
|
|||||||
this.state = {
|
this.state = {
|
||||||
isItemFreezed: false
|
isItemFreezed: false
|
||||||
};
|
};
|
||||||
|
this.libItems = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
freezeItem = (freezed) => {
|
freezeItem = (freezed) => {
|
||||||
@@ -54,6 +57,30 @@ class Content extends Component {
|
|||||||
this.props.sortItems(sortBy, sortOrder);
|
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() {
|
render() {
|
||||||
const { loading, errorMsg, items, sortBy, sortOrder, theadHidden, inAllLibs, currentViewMode } = this.props;
|
const { loading, errorMsg, items, sortBy, sortOrder, theadHidden, inAllLibs, currentViewMode } = this.props;
|
||||||
|
|
||||||
@@ -95,6 +122,7 @@ class Content extends Component {
|
|||||||
<>
|
<>
|
||||||
{items.map((item, index) => {
|
{items.map((item, index) => {
|
||||||
return <Item
|
return <Item
|
||||||
|
ref={this.setLibItemRef(index)}
|
||||||
key={index}
|
key={index}
|
||||||
data={item}
|
data={item}
|
||||||
isDesktop={isDesktop}
|
isDesktop={isDesktop}
|
||||||
@@ -102,24 +130,35 @@ class Content extends Component {
|
|||||||
freezeItem={this.freezeItem}
|
freezeItem={this.freezeItem}
|
||||||
onMonitorRepo={this.props.onMonitorRepo}
|
onMonitorRepo={this.props.onMonitorRepo}
|
||||||
currentViewMode={currentViewMode}
|
currentViewMode={currentViewMode}
|
||||||
|
onContextMenu={this.onContextMenu}
|
||||||
/>;
|
/>;
|
||||||
})}
|
})}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
const content = currentViewMode == LIST_MODE ? (
|
const content = currentViewMode == LIST_MODE ? (
|
||||||
|
<>
|
||||||
<table className={(isDesktop && !theadHidden) ? '' : 'table-thead-hidden'}>
|
<table className={(isDesktop && !theadHidden) ? '' : 'table-thead-hidden'}>
|
||||||
{isDesktop ? desktopThead : <LibsMobileThead inAllLibs={inAllLibs} />}
|
{isDesktop ? desktopThead : <LibsMobileThead inAllLibs={inAllLibs} />}
|
||||||
<tbody>
|
<tbody>
|
||||||
{itemsContent}
|
{itemsContent}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
<div className="d-flex justify-content-between flex-wrap">
|
<div className="d-flex justify-content-between flex-wrap">
|
||||||
{itemsContent}
|
{itemsContent}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
return items.length ? content : emptyTip;
|
return items.length ? (
|
||||||
|
<>
|
||||||
|
{content}
|
||||||
|
<ContextMenu
|
||||||
|
id="shared-libs-item-menu"
|
||||||
|
onMenuItemClick={this.onMenuItemClick}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
) : emptyTip;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -265,6 +304,29 @@ class Item extends Component {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
handleContextMenu = (event) => {
|
||||||
|
this.props.onContextMenu(event, this.props.data);
|
||||||
|
};
|
||||||
|
|
||||||
|
onMenuItemClick = (operation, event) => {
|
||||||
|
switch (operation) {
|
||||||
|
case 'Share':
|
||||||
|
this.share(event);
|
||||||
|
break;
|
||||||
|
case 'Unshare':
|
||||||
|
this.leaveShare(event);
|
||||||
|
break;
|
||||||
|
case 'Watch File Changes':
|
||||||
|
this.watchFileChanges();
|
||||||
|
break;
|
||||||
|
case 'Unwatch File Changes':
|
||||||
|
this.unwatchFileChanges();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (this.state.unshared) {
|
if (this.state.unshared) {
|
||||||
return null;
|
return null;
|
||||||
@@ -288,7 +350,7 @@ class Item extends Component {
|
|||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
{currentViewMode == LIST_MODE ? (
|
{currentViewMode == LIST_MODE ? (
|
||||||
<tr className={this.state.highlight ? 'tr-highlight' : ''} onMouseOver={this.handleMouseOver} onMouseOut={this.handleMouseOut} onFocus={this.handleMouseOver}>
|
<tr className={this.state.highlight ? 'tr-highlight' : ''} onMouseOver={this.handleMouseOver} onMouseOut={this.handleMouseOut} onFocus={this.handleMouseOver} onContextMenu={this.handleContextMenu}>
|
||||||
<td className="text-center">
|
<td className="text-center">
|
||||||
<i
|
<i
|
||||||
role="button"
|
role="button"
|
||||||
@@ -339,6 +401,7 @@ class Item extends Component {
|
|||||||
onMouseOver={this.handleMouseOver}
|
onMouseOver={this.handleMouseOver}
|
||||||
onMouseOut={this.handleMouseOut}
|
onMouseOut={this.handleMouseOut}
|
||||||
onFocus={this.handleMouseOver}
|
onFocus={this.handleMouseOver}
|
||||||
|
onContextMenu={this.handleContextMenu}
|
||||||
>
|
>
|
||||||
<div className="d-flex align-items-center text-truncate">
|
<div className="d-flex align-items-center text-truncate">
|
||||||
<img src={data.icon_url} title={data.icon_title} alt={data.icon_title} width="36" className="mr-2" />
|
<img src={data.icon_url} title={data.icon_title} alt={data.icon_title} width="36" className="mr-2" />
|
||||||
@@ -459,7 +522,8 @@ Item.propTypes = {
|
|||||||
data: PropTypes.object.isRequired,
|
data: PropTypes.object.isRequired,
|
||||||
isItemFreezed: PropTypes.bool.isRequired,
|
isItemFreezed: PropTypes.bool.isRequired,
|
||||||
freezeItem: PropTypes.func.isRequired,
|
freezeItem: PropTypes.func.isRequired,
|
||||||
onMonitorRepo: PropTypes.func.isRequired
|
onMonitorRepo: PropTypes.func.isRequired,
|
||||||
|
onContextMenu: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
class SharedLibraries extends Component {
|
class SharedLibraries extends Component {
|
||||||
|
@@ -138,6 +138,7 @@ class PublicSharedView extends React.Component {
|
|||||||
{(!this.state.isLoading && this.state.repoList.length === 0) && emptyTip}
|
{(!this.state.isLoading && this.state.repoList.length === 0) && emptyTip}
|
||||||
{(!this.state.isLoading && this.state.repoList.length > 0) &&
|
{(!this.state.isLoading && this.state.repoList.length > 0) &&
|
||||||
<SharedRepoListView
|
<SharedRepoListView
|
||||||
|
key='public-shared-view'
|
||||||
libraryType={this.state.libraryType}
|
libraryType={this.state.libraryType}
|
||||||
repoList={this.state.repoList}
|
repoList={this.state.repoList}
|
||||||
sortBy={this.state.sortBy}
|
sortBy={this.state.sortBy}
|
||||||
|
@@ -135,6 +135,63 @@ const TextTranslation = {
|
|||||||
key: 'Export sdoc',
|
key: 'Export sdoc',
|
||||||
value: gettext('Export as zip')
|
value: gettext('Export as zip')
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// repo operations
|
||||||
|
'TRANSFER': {
|
||||||
|
key: 'Transfer',
|
||||||
|
value: gettext('Transfer')
|
||||||
|
},
|
||||||
|
'FOLDER_PERMISSION': {
|
||||||
|
key: 'Folder Permission',
|
||||||
|
value: gettext('Folder Permission')
|
||||||
|
},
|
||||||
|
'SHARE_ADMIN': {
|
||||||
|
key: 'Share Admin',
|
||||||
|
value: gettext('Share Admin')
|
||||||
|
},
|
||||||
|
'CHANGE_PASSWORD': {
|
||||||
|
key: 'Change Password',
|
||||||
|
value: gettext('Change Password')
|
||||||
|
},
|
||||||
|
'RESET_PASSWORD': {
|
||||||
|
key: 'Reset Password',
|
||||||
|
value: gettext('Reset Password')
|
||||||
|
},
|
||||||
|
'UNWATCH_FILE_CHANGES': {
|
||||||
|
key: 'Unwatch File Changes',
|
||||||
|
value: gettext('Unwatch File Changes')
|
||||||
|
},
|
||||||
|
'WATCH_FILE_CHANGES': {
|
||||||
|
key: 'Watch File Changes',
|
||||||
|
value: gettext('Watch File Changes')
|
||||||
|
},
|
||||||
|
'HISTORY_SETTING': {
|
||||||
|
key: 'History Setting',
|
||||||
|
value: gettext('History Setting')
|
||||||
|
},
|
||||||
|
'ADVANCED': {
|
||||||
|
key: 'advanced',
|
||||||
|
value: gettext('Advanced')
|
||||||
|
},
|
||||||
|
|
||||||
|
// advanced operations
|
||||||
|
'API_TOKEN': {
|
||||||
|
key: 'API Token',
|
||||||
|
value: gettext('API Token')
|
||||||
|
},
|
||||||
|
'LABEL_CURRENT_STATE': {
|
||||||
|
key: 'Label Current State',
|
||||||
|
value: gettext('Label Current State')
|
||||||
|
},
|
||||||
|
'OLD_FILES_AUTO_DELETE': {
|
||||||
|
key: 'Old Files Auto Delete',
|
||||||
|
value: gettext('Old Files Auto Delete')
|
||||||
|
},
|
||||||
|
|
||||||
|
'UNSHARE': {
|
||||||
|
key: 'Unshare',
|
||||||
|
value: gettext('Unshare')
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default TextTranslation;
|
export default TextTranslation;
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { mediaUrl, gettext, serviceURL, siteRoot, isPro, fileAuditEnabled, canGenerateShareLink, canGenerateUploadLink, shareLinkPasswordMinLength, username, folderPermEnabled, onlyofficeConverterExtensions, enableOnlyoffice, enableSeadoc, enableFileTags } from './constants';
|
import { mediaUrl, gettext, serviceURL, siteRoot, isPro, fileAuditEnabled, canGenerateShareLink, canGenerateUploadLink, shareLinkPasswordMinLength, username, folderPermEnabled, onlyofficeConverterExtensions, enableOnlyoffice, enableSeadoc, enableFileTags, enableRepoSnapshotLabel, enableRepoAutoDel, enableResetEncryptedRepoPassword, isEmailConfigured, isSystemStaff } from './constants';
|
||||||
import TextTranslation from './text-translation';
|
import TextTranslation from './text-translation';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import toaster from '../components/toast';
|
import toaster from '../components/toast';
|
||||||
@@ -716,6 +716,154 @@ export const Utils = {
|
|||||||
return operationListGetter(isRepoOwner, currentRepoInfo, dirent, isContextmenu);
|
return operationListGetter(isRepoOwner, currentRepoInfo, dirent, isContextmenu);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getRepoOperationList: function (repo) {
|
||||||
|
const showResetPasswordMenuItem = isPro && repo.encrypted && enableResetEncryptedRepoPassword && isEmailConfigured;
|
||||||
|
const operations = [];
|
||||||
|
const DIVIDER = 'Divider';
|
||||||
|
const { SHARE, DELETE, RENAME, TRANSFER, FOLDER_PERMISSION, SHARE_ADMIN, CHANGE_PASSWORD, RESET_PASSWORD, UNWATCH_FILE_CHANGES, WATCH_FILE_CHANGES, HISTORY_SETTING, ADVANCED } = TextTranslation;
|
||||||
|
|
||||||
|
operations.push(SHARE, DELETE, DIVIDER, RENAME, TRANSFER);
|
||||||
|
|
||||||
|
if (folderPermEnabled) {
|
||||||
|
operations.push(FOLDER_PERMISSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
operations.push(SHARE_ADMIN, DIVIDER);
|
||||||
|
|
||||||
|
if (repo.encrypted) {
|
||||||
|
operations.push(CHANGE_PASSWORD);
|
||||||
|
}
|
||||||
|
if (showResetPasswordMenuItem) {
|
||||||
|
operations.push(RESET_PASSWORD);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPro) {
|
||||||
|
const monitorOp = repo.monitored ? UNWATCH_FILE_CHANGES : WATCH_FILE_CHANGES;
|
||||||
|
operations.push(monitorOp);
|
||||||
|
}
|
||||||
|
|
||||||
|
operations.push(DIVIDER, HISTORY_SETTING);
|
||||||
|
const subOpList = Utils.getAdvancedOperations();
|
||||||
|
operations.push({ ...ADVANCED, subOpList });
|
||||||
|
|
||||||
|
// Remove adjacent excess 'Divider'
|
||||||
|
return operations.filter((op, i, arr) => !(op === DIVIDER && arr[i + 1] === DIVIDER));
|
||||||
|
},
|
||||||
|
|
||||||
|
getAdvancedOperations: function () {
|
||||||
|
const operations = [];
|
||||||
|
const { API_TOKEN, LABEL_CURRENT_STATE, OLD_FILES_AUTO_DELETE } = TextTranslation;
|
||||||
|
|
||||||
|
operations.push(API_TOKEN);
|
||||||
|
|
||||||
|
if (enableRepoSnapshotLabel) {
|
||||||
|
operations.push(LABEL_CURRENT_STATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enableRepoAutoDel) {
|
||||||
|
operations.push(OLD_FILES_AUTO_DELETE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return operations;
|
||||||
|
},
|
||||||
|
|
||||||
|
getSharedLibsOperationList: function (lib) {
|
||||||
|
const { SHARE, UNSHARE, WATCH_FILE_CHANGES, UNWATCH_FILE_CHANGES } = TextTranslation;
|
||||||
|
const operations = [];
|
||||||
|
|
||||||
|
if (isPro && lib.is_admin) {
|
||||||
|
operations.push(SHARE);
|
||||||
|
}
|
||||||
|
operations.push(UNSHARE);
|
||||||
|
|
||||||
|
const monitorOp = lib.monitored ? UNWATCH_FILE_CHANGES : WATCH_FILE_CHANGES;
|
||||||
|
operations.push(monitorOp);
|
||||||
|
|
||||||
|
return operations;
|
||||||
|
},
|
||||||
|
|
||||||
|
getPublicSharedRepoOperationList: function (repo) {
|
||||||
|
const { UNSHARE } = TextTranslation;
|
||||||
|
const operations = [];
|
||||||
|
const isRepoOwner = repo.owner_email === username;
|
||||||
|
|
||||||
|
if (isSystemStaff || isRepoOwner) {
|
||||||
|
operations.push(UNSHARE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return operations;
|
||||||
|
},
|
||||||
|
|
||||||
|
getSharedRepoOperationList: function (repo, currentGroup, isPublic) {
|
||||||
|
const operations = [];
|
||||||
|
const { SHARE, UNSHARE, DELETE, RENAME, FOLDER_PERMISSION, SHARE_ADMIN, UNWATCH_FILE_CHANGES, WATCH_FILE_CHANGES, HISTORY_SETTING, ADVANCED, CHANGE_PASSWORD, RESET_PASSWORD } = TextTranslation;
|
||||||
|
|
||||||
|
const isStaff = currentGroup && currentGroup.admins && currentGroup.admins.indexOf(username) > -1;
|
||||||
|
const isRepoOwner = repo.owner_email === username;
|
||||||
|
const isAdmin = repo.is_admin;
|
||||||
|
const DIVIDER = 'Divider';
|
||||||
|
|
||||||
|
if (isPublic) {
|
||||||
|
if (isSystemStaff || isRepoOwner) {
|
||||||
|
operations.push(UNSHARE);
|
||||||
|
}
|
||||||
|
return operations;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPro) {
|
||||||
|
if (repo.owner_email.indexOf('@seafile_group') !== -1) {
|
||||||
|
// is group admin
|
||||||
|
if (isStaff) {
|
||||||
|
if (repo.owner_email === `${currentGroup.id}@seafile_group`) {
|
||||||
|
operations.push(SHARE, DELETE, RENAME);
|
||||||
|
if (folderPermEnabled) {
|
||||||
|
operations.push(FOLDER_PERMISSION);
|
||||||
|
}
|
||||||
|
operations.push(SHARE_ADMIN, DIVIDER);
|
||||||
|
if (repo.encrypted) {
|
||||||
|
operations.push(CHANGE_PASSWORD);
|
||||||
|
}
|
||||||
|
if (repo.encrypted && enableResetEncryptedRepoPassword && isEmailConfigured) {
|
||||||
|
operations.push(RESET_PASSWORD);
|
||||||
|
}
|
||||||
|
if (repo.permission === 'r' || repo.permission === 'rw') {
|
||||||
|
const monitorOp = repo.monitored ? UNWATCH_FILE_CHANGES : WATCH_FILE_CHANGES;
|
||||||
|
operations.push(monitorOp);
|
||||||
|
}
|
||||||
|
operations.push(DIVIDER, HISTORY_SETTING);
|
||||||
|
if (Utils.isDesktop()) {
|
||||||
|
const subOpList = Utils.getAdvancedOperations();
|
||||||
|
operations.push({ ...ADVANCED, subOpList });
|
||||||
|
}
|
||||||
|
return operations;
|
||||||
|
} else {
|
||||||
|
operations.push(UNSHARE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isRepoOwner || isAdmin) {
|
||||||
|
operations.push(SHARE);
|
||||||
|
}
|
||||||
|
if (isStaff || isRepoOwner || isAdmin) {
|
||||||
|
operations.push(UNSHARE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (repo.permission === 'r' || repo.permission === 'rw') {
|
||||||
|
const monitorOp = repo.monitored ? UNWATCH_FILE_CHANGES : WATCH_FILE_CHANGES;
|
||||||
|
operations.push(monitorOp);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isRepoOwner) {
|
||||||
|
operations.push(SHARE);
|
||||||
|
}
|
||||||
|
if (isStaff || isRepoOwner) {
|
||||||
|
operations.push(UNSHARE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return operations;
|
||||||
|
},
|
||||||
|
|
||||||
sharePerms: function (permission) {
|
sharePerms: function (permission) {
|
||||||
var title;
|
var title;
|
||||||
switch (permission) {
|
switch (permission) {
|
||||||
|
Reference in New Issue
Block a user