import React, { Fragment } from 'react'; import PropTypes from 'prop-types'; import moment from 'moment'; import { Dropdown, DropdownMenu, DropdownToggle, DropdownItem } from 'reactstrap'; import { Link, navigate } from '@gatsbyjs/reach-router'; import { Utils } from '../../utils/utils'; import { gettext, siteRoot, isPro, username, folderPermEnabled, isSystemStaff, enableResetEncryptedRepoPassword, isEmailConfigured, enableRepoAutoDel, enableSeaTableIntegration } from '../../utils/constants'; import ModalPortal from '../../components/modal-portal'; import ShareDialog from '../../components/dialog/share-dialog'; import LibSubFolderPermissionDialog from '../../components/dialog/lib-sub-folder-permission-dialog'; import DeleteRepoDialog from '../../components/dialog/delete-repo-dialog'; import ChangeRepoPasswordDialog from '../../components/dialog/change-repo-password-dialog'; import ResetEncryptedRepoPasswordDialog from '../../components/dialog/reset-encrypted-repo-password-dialog'; import LibOldFilesAutoDelDialog from '../../components/dialog/lib-old-files-auto-del-dialog'; import RepoSeaTableIntegrationDialog from '../../components/dialog/repo-seatable-integration-dialog'; import Rename from '../rename'; import { seafileAPI } from '../../utils/seafile-api'; import LibHistorySettingDialog from '../dialog/lib-history-setting-dialog'; import toaster from '../toast'; import RepoAPITokenDialog from '../dialog/repo-api-token-dialog'; import RepoShareAdminDialog from '../dialog/repo-share-admin-dialog'; import RepoMonitoredIcon from '../../components/repo-monitored-icon'; const propTypes = { currentGroup: PropTypes.object, libraryType: PropTypes.string, repo: PropTypes.object.isRequired, isItemFreezed: PropTypes.bool.isRequired, onFreezedItem: PropTypes.func.isRequired, onUnfreezedItem: PropTypes.func.isRequired, onItemUnshare: PropTypes.func.isRequired, onItemDetails: PropTypes.func, onItemRename: PropTypes.func, onItemDelete: PropTypes.func, onMonitorRepo: PropTypes.func }; class SharedRepoListItem extends React.Component { constructor(props) { super(props); this.state = { highlight: false, isOperationShow: false, isItemMenuShow: false, isAdvancedMenuShown: false, isShowSharedDialog: false, isRenaming: false, isStarred: this.props.repo.starred, isFolderPermissionDialogOpen: false, isHistorySettingDialogShow: false, isDeleteDialogShow: false, isAPITokenDialogShow: false, isRepoShareAdminDialogOpen: false, isRepoDeleted: false, isChangePasswordDialogShow: false, isResetPasswordDialogShow: false, isOldFilesAutoDelDialogOpen: false, isSeaTableIntegrationShow: false, }; this.isDeparementOnwerGroupMember = false; } onMouseEnter = () => { if (!this.props.isItemFreezed) { this.setState({ highlight: true, isOperationShow: true, }); } }; onMouseOver = () => { if (!this.props.isItemFreezed) { this.setState({ highlight: true, isOperationShow: true, }); } }; onMouseLeave = () => { if (!this.props.isItemFreezed) { this.setState({ highlight: false, isOperationShow: false, }); } }; clickOperationMenuToggle = (e) => { this.toggleOperationMenu(e); }; onDropdownToggleKeyDown = (e) => { if (e.key == 'Enter' || e.key == 'Space') { this.clickOperationMenuToggle(e); } }; toggleOperationMenu = (e) => { let dataset = e.target ? e.target.dataset : null; if (dataset && dataset.toggle && dataset.toggle === 'Rename') { this.setState({isItemMenuShow: !this.state.isItemMenuShow}); return; } this.setState( {isItemMenuShow: !this.state.isItemMenuShow}, () => { if (this.state.isItemMenuShow) { this.props.onFreezedItem(); } else { this.props.onUnfreezedItem(); this.setState({ highlight: false, isOperationShow: false, }); } } ); }; toggleAdvancedMenuShown = (e) => { this.setState({ isAdvancedMenuShown: true }); }; toggleAdvancedMenu = (e) => { e.stopPropagation(); this.setState({ isAdvancedMenuShown: !this.state.isAdvancedMenuShown }, () => { this.toggleOperationMenu(e); }); }; onDropDownMouseMove = (e) => { if (this.state.isAdvancedMenuShown && e.target && e.target.className === 'dropdown-item') { this.setState({ isAdvancedMenuShown: false }); } }; getRepoComputeParams = () => { let repo = this.props.repo; let iconUrl = Utils.getLibIconUrl(repo); let iconTitle = Utils.getLibIconTitle(repo); let libPath = `${siteRoot}library/${repo.repo_id}/${Utils.encodePath(repo.repo_name)}/`; return { iconUrl, iconTitle, libPath }; }; onMenuItemKeyDown = (e) => { if (e.key == 'Enter' || e.key == 'Space') { this.onMenuItemClick(e); } }; onMenuItemClick = (e) => { let operation = e.target.dataset.toggle; switch(operation) { case 'Rename': this.onItemRenameToggle(); break; case 'Folder Permission': this.onItemFolderPermissionToggle(); break; case 'Details': this.onItemDetails(); break; case 'Share': this.onItemShare(); break; case 'Unshare': this.onItemUnshare(); break; case 'History Setting': this.onHistorySettingToggle(); break; case 'API Token': this.onAPITokenToggle(); break; case 'Share Admin': this.toggleRepoShareAdminDialog(); break; case 'Change Password': this.onChangePasswordToggle(); break; case 'Reset Password': this.onResetPasswordToggle(); break; case 'Watch File Changes': this.watchFileChanges(); break; case 'Unwatch File Changes': this.unwatchFileChanges(); break; case 'Old Files Auto Delete': this.toggleOldFilesAutoDelDialog(); break; case 'SeaTable integration': this.onSeaTableIntegrationToggle(); break; // no default } }; watchFileChanges = () => { const { repo } = this.props; seafileAPI.monitorRepo(repo.repo_id).then(() => { this.props.onMonitorRepo(repo, true); }).catch(error => { let errMessage = Utils.getErrorMsg(error); toaster.danger(errMessage); }); }; unwatchFileChanges = () => { const { repo } = this.props; seafileAPI.unMonitorRepo(repo.repo_id).then(() => { this.props.onMonitorRepo(repo, false); }).catch(error => { let errMessage = Utils.getErrorMsg(error); toaster.danger(errMessage); }); }; onItemRenameToggle = () => { this.props.onFreezedItem(); this.setState({ isRenaming: !this.state.isRenaming, isOperationShow: !this.state.isOperationShow }); }; onRenameConfirm = (name) => { this.props.onItemRename(this.props.repo, name); this.onRenameCancel(); }; onRenameCancel = () => { this.props.onUnfreezedItem(); this.setState({isRenaming: !this.state.isRenaming}); }; onItemFolderPermissionToggle = () => { this.setState({isFolderPermissionDialogOpen: !this.state.isFolderPermissionDialogOpen}); }; onHistorySettingToggle = () => { this.setState({isHistorySettingDialogShow: !this.state.isHistorySettingDialogShow}); }; onItemDetails = () => { this.props.onItemDetails(this.props.repo); }; onItemShare = (e) => { e.preventDefault(); this.setState({isShowSharedDialog: true}); }; onItemUnshare = (e) => { e.preventDefault(); this.props.onItemUnshare(this.props.repo); }; onItemDeleteToggle = (e) => { e.preventDefault(); this.setState({isDeleteDialogShow: !this.state.isDeleteDialogShow}); }; onItemDelete = () => { const { currentGroup, repo } = this.props; if (!currentGroup) { // repo can not be deleted in share all module return; } const groupID = currentGroup.id; seafileAPI.deleteGroupOwnedLibrary(groupID, repo.repo_id).then(() => { this.setState({ isRepoDeleted: true, isDeleteDialogShow: false, }); this.props.onItemDelete(repo); let name = repo.repo_name; var msg = gettext('Successfully deleted {name}.').replace('{name}', name); toaster.success(msg); }).catch((error) => { let errMessage = Utils.getErrorMsg(error); if (errMessage === gettext('Error')) { let name = repo.repo_name; errMessage = gettext('Failed to delete {name}.').replace('{name}', name); } toaster.danger(errMessage); this.setState({isRepoDeleted: false}); }); }; toggleShareDialog = () => { this.setState({isShowSharedDialog: false}); }; toggleRepoShareAdminDialog = () => { this.setState({isRepoShareAdminDialogOpen: !this.state.isRepoShareAdminDialogOpen}); }; toggleOldFilesAutoDelDialog = () => { this.setState({isOldFilesAutoDelDialogOpen: !this.state.isOldFilesAutoDelDialogOpen}); }; onSeaTableIntegrationToggle = () => { this.setState({isSeaTableIntegrationShow: !this.state.isSeaTableIntegrationShow}); }; onAPITokenToggle = () => { this.setState({isAPITokenDialogShow: !this.state.isAPITokenDialogShow}); }; onChangePasswordToggle = () => { this.setState({isChangePasswordDialogShow: !this.state.isChangePasswordDialogShow}); }; onResetPasswordToggle = () => { this.setState({isResetPasswordDialogShow: !this.state.isResetPasswordDialogShow}); }; translateMenuItem = (menuItem) => { let translateResult = ''; switch(menuItem) { case 'Rename': translateResult = gettext('Rename'); break; case 'Folder Permission': translateResult = gettext('Folder Permission'); break; case 'Details': translateResult = gettext('Details'); break; case 'Unshare': translateResult = gettext('Unshare'); break; case 'Share': translateResult = gettext('Share'); break; case 'History Setting': translateResult = gettext('History Setting'); break; case 'Share Admin': translateResult = gettext('Share Admin'); break; case 'Change Password': translateResult = gettext('Change Password'); break; case 'Reset Password': translateResult = gettext('Reset Password'); break; case 'Watch File Changes': translateResult = gettext('Watch File Changes'); break; case 'Unwatch File Changes': translateResult = gettext('Unwatch File Changes'); break; case 'Old Files Auto Delete': translateResult = gettext('Auto Deletion Setting'); break; case 'API Token': translateResult = 'API Token'; // translation is not needed here break; case 'Advanced': translateResult = gettext('Advanced'); break; case 'SeaTable integration': translateResult = gettext('SeaTable integration'); break; default: break; } return translateResult; }; getAdvancedOperations = () => { const operations = []; operations.push('API Token'); if (enableRepoAutoDel) { operations.push('Old Files Auto Delete'); } if (enableSeaTableIntegration) { operations.push('SeaTable integration'); } return operations; }; generatorOperations = () => { let { repo, currentGroup } = this.props; //todo this have a bug; use current api is not return admins param; let isStaff = currentGroup && currentGroup.admins && currentGroup.admins.indexOf(username) > -1; //for group repolist; let isRepoOwner = repo.owner_email === username; let isAdmin = repo.is_admin; let operations = []; if (isPro) { if (repo.owner_email.indexOf('@seafile_group') != -1) { // is group admin if (isStaff) { if (repo.owner_email == currentGroup.id + '@seafile_group') { this.isDeparementOnwerGroupMember = true; operations = ['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', 'Details'); if (Utils.isDesktop()) { operations.push('Advanced'); } 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; }; generatorMobileMenu = () => { let operations = []; if (this.props.libraryType && this.props.libraryType === 'public') { let isRepoOwner = this.props.repo.owner_email === username; if (isSystemStaff || isRepoOwner) { operations.push('Unshare'); } } else { operations = this.generatorOperations(); if (this.isDeparementOnwerGroupMember) { operations.unshift('Unshare'); operations.unshift('Share'); } } if (!operations.length) { return null; } return (
{operations.map((item, index) => { return ( {this.translateMenuItem(item)} ); })}
); }; generatorPCMenu = () => { let operations = []; if (this.props.libraryType && this.props.libraryType === 'public') { let isRepoOwner = this.props.repo.owner_email === username; if (isSystemStaff || isRepoOwner) { operations.push('Unshare'); } } else { operations = this.generatorOperations(); } const shareOperation = ; const unshareOperation = ; const deleteOperation = ; if (this.isDeparementOnwerGroupMember) { const advancedOperations = this.getAdvancedOperations(); return ( {shareOperation} {deleteOperation} {operations.map((item, index)=> { if (item == 'Divider') { return ; } else if (item == 'Advanced') { return ( {e.stopPropagation();}} > {this.translateMenuItem(item)} {advancedOperations.map((item, index)=> { return ({this.translateMenuItem(item)}); })} ); } else { return ({this.translateMenuItem(item)}); } })} ); } else { return ( {operations.map(item => { switch (item) { case 'Share': return {shareOperation}; case 'Unshare': return {unshareOperation}; case 'Watch File Changes': case 'Unwatch File Changes': return ( {[item].map((item, index) => { return {this.translateMenuItem(item)}; })} ); // no default default: return null; } })} ); } }; onToggleStarRepo = (e) => { e.preventDefault(); const { repo_name: repoName } = this.props.repo; if (this.state.isStarred) { seafileAPI.unstarItem(this.props.repo.repo_id, '/').then(() => { this.setState({isStarred: !this.state.isStarred}); const msg = gettext('Successfully unstarred {library_name_placeholder}.') .replace('{library_name_placeholder}', repoName); toaster.success(msg); }).catch(error => { let errMessage = Utils.getErrorMsg(error); toaster.danger(errMessage); }); } else { seafileAPI.starItem(this.props.repo.repo_id, '/').then(() => { this.setState({isStarred: !this.state.isStarred}); const msg = gettext('Successfully starred {library_name_placeholder}.') .replace('{library_name_placeholder}', repoName); toaster.success(msg); }).catch(error => { let errMessage = Utils.getErrorMsg(error); toaster.danger(errMessage); }); } }; renderPCUI = () => { let { iconUrl, iconTitle, libPath } = this.getRepoComputeParams(); let { repo } = this.props; return ( {iconTitle} {this.state.isRenaming ? : {repo.repo_name} {repo.monitored && } } {this.state.isOperationShow && this.generatorPCMenu()} {repo.size} {moment(repo.last_modified).fromNow()} {repo.owner_name} ); }; visitRepo = () => { if (!this.state.isRenaming) { navigate(this.repoURL); } }; renderMobileUI = () => { let { iconUrl, iconTitle, libPath } = this.getRepoComputeParams(); let { repo } = this.props; this.repoURL = libPath; return ( {iconTitle}/ {this.state.isRenaming ? : {repo.repo_name} {repo.monitored && } }
{repo.owner_name} {repo.size} {moment(repo.last_modified).fromNow()} {this.generatorMobileMenu()}
); }; render() { let { repo } = this.props; let isGroupOwnedRepo = repo.owner_email.indexOf('@seafile_group') > -1; return ( {Utils.isDesktop() ? this.renderPCUI() : this.renderMobileUI()} {this.state.isShowSharedDialog && ( )} {this.state.isFolderPermissionDialogOpen && ( )} {this.state.isDeleteDialogShow && } {this.state.isHistorySettingDialogShow && ( )} {this.state.isAPITokenDialogShow && ( )} {this.state.isRepoShareAdminDialogOpen && ( )} {this.state.isChangePasswordDialogShow && ( )} {this.state.isResetPasswordDialogShow && ( )} {this.state.isOldFilesAutoDelDialogOpen && ( )} {this.state.isSeaTableIntegrationShow && ( )} ); } } SharedRepoListItem.propTypes = propTypes; export default SharedRepoListItem;