import React, { Fragment } from 'react'; import PropTypes from 'prop-types'; import { siteRoot, gettext, thumbnailSizeForOriginal, username, isPro, enableFileComment, fileAuditEnabled, folderPermEnabled, canGenerateShareLink } from '../../utils/constants'; import { Utils } from '../../utils/utils'; import TextTranslation from '../../utils/text-translation'; import { seafileAPI } from '../../utils/seafile-api'; import URLDecorator from '../../utils/url-decorator'; import Loading from '../loading'; import toaster from '../toast'; import ModalPortal from '../modal-portal'; import CreateFile from '../dialog/create-file-dialog'; import CreateFolder from '../dialog/create-folder-dialog'; import ImageDialog from '../dialog/image-dialog'; import ZipDownloadDialog from '../dialog/zip-download-dialog'; import MoveDirentDialog from '../dialog/move-dirent-dialog'; import CopyDirentDialog from '../dialog/copy-dirent-dialog'; import DirentListItem from './dirent-list-item'; import ContextMenu from '../context-menu/context-menu'; import { hideMenu, showMenu } from '../context-menu/actions'; import DirentsDraggedPreview from '../draggable/dirents-dragged-preview'; const propTypes = { path: PropTypes.string.isRequired, repoID: PropTypes.string.isRequired, currentRepoInfo: PropTypes.object, isAllItemSelected: PropTypes.bool.isRequired, isDirentListLoading: PropTypes.bool.isRequired, direntList: PropTypes.array.isRequired, sortBy: PropTypes.string.isRequired, sortOrder: PropTypes.string.isRequired, sortItems: PropTypes.func.isRequired, onAddFile: PropTypes.func.isRequired, onAddFolder: PropTypes.func.isRequired, onItemDelete: PropTypes.func.isRequired, onAllItemSelected: PropTypes.func.isRequired, onItemSelected: PropTypes.func.isRequired, onItemRename: PropTypes.func.isRequired, onItemClick: PropTypes.func.isRequired, onItemMove: PropTypes.func.isRequired, onItemCopy: PropTypes.func.isRequired, onDirentClick: PropTypes.func.isRequired, updateDirent: PropTypes.func.isRequired, selectedDirentList: PropTypes.array.isRequired, onItemsMove: PropTypes.func.isRequired, onItemsCopy: PropTypes.func.isRequired, onItemsDelete: PropTypes.func.isRequired, onFileTagChanged: PropTypes.func, enableDirPrivateShare: PropTypes.bool.isRequired, isGroupOwnedRepo: PropTypes.bool.isRequired, userPerm: PropTypes.string, showDirentDetail: PropTypes.func.isRequired, loadDirentList: PropTypes.func.isRequired, }; class DirentListView extends React.Component { constructor(props) { super(props); this.state = { isItemFreezed: false, isImagePopupOpen: false, imageItems: [], imageIndex: 0, fileType: '', isCreateFileDialogShow: false, isCreateFolderDialogShow: false, isMoveDialogShow: false, isCopyDialogShow: false, isProgressDialogShow: false, downloadItems: [], isMutipleOperation: true, activeDirent: null, isListDropTipShow: false, isShowDirentsDraggablePreview: false, }; this.enteredCounter = 0; // Determine whether to enter the child element to avoid dragging bubbling bugs。 this.isRepoOwner = props.currentRepoInfo.owner_email === username; this.isAdmin = props.currentRepoInfo.is_admin; this.repoEncrypted = props.currentRepoInfo.encrypted; this.clickedDirent = null; this.direntItems = []; this.currentItemRef = null; this.zipToken = null; const { userPerm } = props; this.canDrop = userPerm === 'rw'; const { isCustomPermission, customPermission } = Utils.getUserPermission(userPerm); if (isCustomPermission) { const { modify } = customPermission.permission; this.canDrop = modify; } } freezeItem = () => { this.setState({isItemFreezed: true}); } unfreezeItem = () => { this.setState({isItemFreezed: false}); } onItemRename = (dirent, newName) => { let isDuplicated = this.props.direntList.some(item => { return item.name === newName; }); if (isDuplicated) { let errMessage = gettext('The name "{name}" is already taken. Please choose a different name.'); errMessage = errMessage.replace('{name}', Utils.HTMLescape(newName)); toaster.danger(errMessage); return false; } this.props.onItemRename(dirent, newName); } onItemRenameToggle = () => { this.freezeItem(); } onItemSelected = (dirent) => { this.setState({activeDirent: null}); this.props.onItemSelected(dirent); } onDirentClick = (dirent) => { if (this.props.selectedDirentList.length > 0 && !this.state.activeDirent ) { return; } this.setState({activeDirent: dirent}); this.props.onDirentClick(dirent); } sortByName = (e) => { e.preventDefault(); const sortBy = 'name'; const sortOrder = this.props.sortOrder == 'asc' ? 'desc' : 'asc'; this.props.sortItems(sortBy, sortOrder); } sortByTime = (e) => { e.preventDefault(); const sortBy = 'time'; const sortOrder = this.props.sortOrder == 'asc' ? 'desc' : 'asc'; this.props.sortItems(sortBy, sortOrder); } sortBySize = (e) => { e.preventDefault(); const sortBy = 'size'; const sortOrder = this.props.sortOrder == 'asc' ? 'desc' : 'asc'; this.props.sortItems(sortBy, sortOrder); } // for image popup prepareImageItem = (item) => { const useThumbnail = !this.repoEncrypted; const name = item.name; const fileExt = name.substr(name.lastIndexOf('.') + 1).toLowerCase(); const isGIF = fileExt == 'gif'; const path = Utils.encodePath(Utils.joinPath(this.props.path, name)); const repoID = this.props.repoID; let src; if (useThumbnail && !isGIF) { src = `${siteRoot}thumbnail/${repoID}/${thumbnailSizeForOriginal}${path}`; } else { src = `${siteRoot}repo/${repoID}/raw${path}`; } return { 'name': name, 'url': `${siteRoot}lib/${repoID}/file${path}`, 'src': src }; } showImagePopup = (curItem) => { let items = this.props.fullDirentList.filter((item) => { return Utils.imageCheck(item.name); }); const imageItems = items.map((item) => { return this.prepareImageItem(item); }); this.setState({ isImagePopupOpen: true, imageItems: imageItems, imageIndex: items.indexOf(curItem) }); } moveToPrevImage = () => { const imageItemsLength = this.state.imageItems.length; this.setState((prevState) => ({ imageIndex: (prevState.imageIndex + imageItemsLength - 1) % imageItemsLength })); } moveToNextImage = () => { const imageItemsLength = this.state.imageItems.length; this.setState((prevState) => ({ imageIndex: (prevState.imageIndex + 1) % imageItemsLength })); } closeImagePopup = () => { this.setState({isImagePopupOpen: false}); } onCreateFileToggle = () => { this.setState({isCreateFileDialogShow: !this.state.isCreateFileDialogShow}); } onCreateFolderToggle = () => { this.setState({isCreateFolderDialogShow: !this.state.isCreateFolderDialogShow}); } onAddFile = (filePath, isDraft) => { this.setState({isCreateFileDialogShow: false}); this.props.onAddFile(filePath, isDraft); } onAddFolder = (dirPath) => { this.setState({isCreateFolderDialogShow: false}); this.props.onAddFolder(dirPath); } checkDuplicatedName = (newName) => { let direntList = this.props.direntList; let isDuplicated = direntList.some(object => { return object.name === newName; }); return isDuplicated; } onMoveToggle = () => { this.setState({isMoveDialogShow: !this.state.isMoveDialogShow}); } onCopyToggle = () => { this.setState({isCopyDialogShow: !this.state.isCopyDialogShow}); } onItemsDownload = () => { let { path, repoID, selectedDirentList } = this.props; if (selectedDirentList.length) { if (selectedDirentList.length === 1 && !selectedDirentList[0].isDir()) { let direntPath = Utils.joinPath(path, selectedDirentList[0].name); let url = URLDecorator.getUrl({type: 'download_file_url', repoID: repoID, filePath: direntPath}); location.href= url; return; } let selectedDirentNames = selectedDirentList.map(dirent => { return dirent.name; }); this.setState({ isProgressDialogShow: true, downloadItems: selectedDirentNames }); } } onCloseZipDownloadDialog = () => { this.setState({isProgressDialogShow: false}); } // common contextmenu handle onMouseDown = (event) => { event.stopPropagation(); if (event.button === 2) { return; } } 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); if (this.props.posX) { x -= this.props.posX; } if (this.props.posY) { y -= this.props.posY; } hideMenu(); let showMenuConfig = { id: id, position: { x, y }, target: event.target, currentObject: currentObject, menuList: menuList, }; if (menuList.length === 0) { return; } showMenu(showMenuConfig); } // table-container contextmenu handle onContainerClick = () => { if (this.state.activeDirent) { this.onDirentClick(null); } } onContainerMouseDown = (event) => { this.onMouseDown(event); } onContainerContextMenu = (event) => { event.preventDefault(); // Display menu items based on the permissions of the current path let permission = this.props.userPerm; const { isCustomPermission, customPermission } = Utils.getUserPermission(this.props.userPerm); if (permission !== 'admin' && permission !== 'rw' && !isCustomPermission) { return; } if (this.props.selectedDirentList.length === 0) { let id = 'dirent-container-menu'; // custom permission judgement if (isCustomPermission) { const { modify } = customPermission.permission; if (!modify) return; } let menuList = [TextTranslation.NEW_FOLDER, TextTranslation.NEW_FILE]; this.handleContextClick(event, id, menuList); } else { if (this.props.selectedDirentList.length === 1) { if (!this.state.activeDirent) { let id = 'dirent-item-menu'; let dirent = this.props.selectedDirentList[0]; let menuList = this.getDirentItemMenuList(dirent, true); this.handleContextClick(event, id, menuList, dirent); } else { this.onDirentClick(null); event.preventDefault(); event.persist(); // custom permission judgement if (isCustomPermission) { const { modify } = customPermission.permission; if (!modify) return; } setTimeout(() => { let id = 'dirent-container-menu'; let menuList = [TextTranslation.NEW_FOLDER, TextTranslation.NEW_FILE]; this.handleContextClick(event, id, menuList); }, 0); } } else { let id = 'dirents-menu'; let menuList = []; if (isCustomPermission) { const { modify: canModify, copy: canCopy, download: canDownload, delete: canDelete } = customPermission.permission; canModify && menuList.push(TextTranslation.MOVE); canCopy && menuList.push(TextTranslation.COPY); canDownload && menuList.push(TextTranslation.DOWNLOAD); canDelete && menuList.push(TextTranslation.DELETE); } else { menuList = [TextTranslation.MOVE, TextTranslation.COPY, TextTranslation.DOWNLOAD, TextTranslation.DELETE]; } this.handleContextClick(event, id, menuList); } } } onContainerMenuItemClick = (operation) => { switch(operation) { case 'New Folder': this.onCreateFolderToggle(); break; case 'New File': this.onCreateFileToggle(); break; default: break; } hideMenu(); } onDirentsMenuItemClick = (operation) => { switch(operation) { case 'Move': this.onMoveToggle(); break; case 'Copy': this.onCopyToggle(); break; case 'Download': this.onItemsDownload(); break; case 'Delete': this.props.onItemsDelete(); break; default: break; } hideMenu(); } // table-thread contextmenu handle -- Shield event onThreadMouseDown = (event) => { this.onMouseDown(event); } onThreadContextMenu = (event) => { event.stopPropagation(); } // table-dirent-item contextmenu handle onItemMouseDown = (event) => { this.onMouseDown(event); } onItemContextMenu = (event, dirent) => { // Display menu items according to the current dirent permission if (this.props.selectedDirentList.length > 1) { return; } this.onDirentClick(dirent); let id = 'dirent-item-menu'; let menuList = this.getDirentItemMenuList(dirent, true); this.handleContextClick(event, id, menuList, dirent); } setDirentItemRef = (index) => item => { this.direntItems[index] = item; } onMenuItemClick = (operation, currentObject, event) => { let index = this.getDirentIndex(currentObject); this.direntItems[index].onMenuItemClick(operation, event); hideMenu(); } onShowMenu = (e) => { this.freezeItem(); } onHideMenu = (e) => { this.unfreezeItem(); } // contextmenu utils getDirentIndex = (dirent) => { let direntList = this.props.direntList; let index = 0; for (let i = 0; i < direntList.length; i++) { if (direntList[i].name === dirent.name) { index = i; break; } } return index; } getDirentItemMenuList = (dirent, isContextmenu) => { const isRepoOwner = this.isRepoOwner; const currentRepoInfo = this.props.currentRepoInfo; return Utils.getDirentOperationList(isRepoOwner, currentRepoInfo, dirent, isContextmenu); } onTableDragEnter = (e) => { if (Utils.isIEBrower() || !this.canDrop) { return false; } this.enteredCounter++; if (this.enteredCounter !== 0) { if (this.state.isListDropTipShow) { return ; } this.setState({isListDropTipShow: true}); } } onTableDragOver = (e) => { if (Utils.isIEBrower() || !this.canDrop) { return false; } if (e.dataTransfer.dropEffect === 'copy') { return; } e.preventDefault(); e.dataTransfer.dropEffect = 'move'; } onTableDragLeave = (e) => { if (Utils.isIEBrower() || !this.canDrop) { return false; } this.enteredCounter--; if (this.enteredCounter === 0) { this.setState({isListDropTipShow: false}); } } tableDrop = (e) => { if (Utils.isIEBrower() || !this.canDrop) { return false; } e.persist(); this.enteredCounter = 0; this.setState({isListDropTipShow: false}); if (e.dataTransfer.files.length) { // uploaded files return; } let dragStartItemData = e.dataTransfer.getData('applicaiton/drag-item-info'); dragStartItemData = JSON.parse(dragStartItemData); let {nodeDirent, nodeParentPath, nodeRootPath} = dragStartItemData; if (Array.isArray(dragStartItemData)) { //selected items return; } if (nodeRootPath === this.props.path || nodeParentPath === this.props.path) { return; } if (this.props.path.indexOf(nodeRootPath) !== -1) { return; } this.props.onItemMove(this.props.currentRepoInfo, nodeDirent, this.props.path, nodeParentPath); } onShowDirentsDraggablePreview = () => { this.setState({ isShowDirentsDraggablePreview: true, }); } onHideDirentsDraggablePreview = () => { this.setState({ isShowDirentsDraggablePreview: false }); } render() { const { direntList, sortBy, sortOrder } = this.props; if (this.props.isDirentListLoading) { return (); } // sort const sortByName = sortBy == 'name'; const sortByTime = sortBy == 'time'; const sortBySize = sortBy == 'size'; const sortIcon = sortOrder == 'asc' ? : ; const isDesktop = Utils.isDesktop(); return (
{isDesktop ? ( ) : ( )} {direntList.map((dirent, index) => { return ( ); })}
{/*icon */} {/*star */} {gettext('Name')} {sortByName && sortIcon} {/*tag */} {/*operation */} {gettext('Size')} {sortBySize && sortIcon} {gettext('Last Update')} {sortByTime && sortIcon}
{this.state.isShowDirentsDraggablePreview && } {this.state.isImagePopupOpen && ( )} {this.state.isCreateFolderDialogShow && ( )} {this.state.isCreateFileDialogShow && ( )} {this.state.isMoveDialogShow && } {this.state.isCopyDialogShow && } {this.state.isProgressDialogShow && }
); } } DirentListView.propTypes = propTypes; export default DirentListView;