2019-02-20 03:54:25 +00:00
|
|
|
import React, { Fragment } from 'react';
|
|
|
|
import PropTypes from 'prop-types';
|
|
|
|
import TreeView from '../../components/tree-view/tree-view';
|
|
|
|
import Loading from '../../components/loading';
|
|
|
|
import ModalPortal from '../../components/modal-portal';
|
|
|
|
import Delete from '../../components/dialog/delete-dialog';
|
|
|
|
import Rename from '../../components/dialog/rename-dialog';
|
2019-03-20 03:04:36 +00:00
|
|
|
import Copy from '../../components/dialog/copy-dirent-dialog';
|
|
|
|
import Move from '../../components/dialog/move-dirent-dialog';
|
2019-02-20 03:54:25 +00:00
|
|
|
import CreateFolder from '../../components/dialog/create-folder-dialog';
|
|
|
|
import CreateFile from '../../components/dialog/create-file-dialog';
|
2019-03-15 07:48:16 +00:00
|
|
|
import { siteRoot, gettext, thumbnailSizeForOriginal } from '../../utils/constants';
|
2019-02-27 09:34:39 +00:00
|
|
|
import { Utils } from '../../utils/utils';
|
2019-02-20 03:54:25 +00:00
|
|
|
|
2019-03-15 07:48:16 +00:00
|
|
|
import Lightbox from 'react-image-lightbox';
|
|
|
|
import 'react-image-lightbox/style.css';
|
|
|
|
|
2019-02-20 03:54:25 +00:00
|
|
|
const propTypes = {
|
2019-02-21 09:37:04 +00:00
|
|
|
currentPath: PropTypes.string.isRequired,
|
2019-02-20 03:54:25 +00:00
|
|
|
repoPermission: PropTypes.bool.isRequired,
|
|
|
|
isTreeDataLoading: PropTypes.bool.isRequired,
|
|
|
|
treeData: PropTypes.object.isRequired,
|
2019-02-21 09:37:04 +00:00
|
|
|
currentNode: PropTypes.object,
|
2019-02-20 03:54:25 +00:00
|
|
|
onNodeClick: PropTypes.func.isRequired,
|
|
|
|
onNodeCollapse: PropTypes.func.isRequired,
|
|
|
|
onNodeExpanded: PropTypes.func.isRequired,
|
|
|
|
onRenameNode: PropTypes.func.isRequired,
|
|
|
|
onDeleteNode: PropTypes.func.isRequired,
|
|
|
|
onAddFileNode: PropTypes.func.isRequired,
|
|
|
|
onAddFolderNode: PropTypes.func.isRequired,
|
2019-02-27 09:34:39 +00:00
|
|
|
repoID: PropTypes.string.isRequired,
|
2019-02-28 06:33:21 +00:00
|
|
|
navRate: PropTypes.number,
|
|
|
|
inResizing: PropTypes.bool.isRequired,
|
2019-03-15 07:48:16 +00:00
|
|
|
currentRepoInfo: PropTypes.object.isRequired,
|
2019-02-20 03:54:25 +00:00
|
|
|
};
|
|
|
|
|
2019-02-21 09:37:04 +00:00
|
|
|
class DirColumnNav extends React.Component {
|
2019-02-20 03:54:25 +00:00
|
|
|
|
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
|
|
|
this.state = {
|
|
|
|
opNode: null,
|
|
|
|
isDeleteDialogShow: false,
|
|
|
|
isAddFileDialogShow: false,
|
|
|
|
isAddFolderDialogShow: false,
|
|
|
|
isRenameDialogShow: false,
|
2019-03-15 07:48:16 +00:00
|
|
|
isNodeImagePopupOpen: false,
|
|
|
|
imageNodeItems: [],
|
|
|
|
imageIndex: 0,
|
2019-03-20 03:04:36 +00:00
|
|
|
isCopyDialogShow: false,
|
|
|
|
isMoveDialogShow: false,
|
|
|
|
isMutipleOperation:false,
|
2019-02-20 03:54:25 +00:00
|
|
|
};
|
|
|
|
this.isNodeMenuShow = true;
|
|
|
|
}
|
|
|
|
|
2019-02-21 09:37:04 +00:00
|
|
|
componentWillReceiveProps(nextProps) {
|
|
|
|
this.setState({opNode: nextProps.currentNode});
|
2019-02-20 03:54:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
onNodeClick = (node) => {
|
|
|
|
this.setState({opNode: node});
|
2019-03-15 07:48:16 +00:00
|
|
|
if (Utils.imageCheck(node.object.name)) {
|
|
|
|
this.showNodeImagePopup(node);
|
|
|
|
return;
|
|
|
|
}
|
2019-02-20 03:54:25 +00:00
|
|
|
this.props.onNodeClick(node);
|
|
|
|
}
|
|
|
|
|
|
|
|
onMenuItemClick = (operation, node) => {
|
|
|
|
this.setState({opNode: node});
|
|
|
|
switch (operation) {
|
|
|
|
case 'New Folder':
|
|
|
|
this.onAddFolderToggle();
|
|
|
|
break;
|
|
|
|
case 'New File':
|
|
|
|
this.onAddFileToggle();
|
|
|
|
break;
|
|
|
|
case 'Rename':
|
|
|
|
this.onRenameToggle();
|
|
|
|
break;
|
|
|
|
case 'Delete':
|
|
|
|
this.onDeleteToggle();
|
|
|
|
break;
|
2019-03-20 03:04:36 +00:00
|
|
|
case 'Copy':
|
|
|
|
this.onCopyToggle();
|
|
|
|
break;
|
|
|
|
case 'Move':
|
|
|
|
this.onMoveToggle();
|
|
|
|
break;
|
2019-02-27 09:34:39 +00:00
|
|
|
case 'Open in New Tab':
|
|
|
|
this.onOpenFile(node);
|
|
|
|
break;
|
2019-02-20 03:54:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
onAddFileToggle = (type) => {
|
|
|
|
if (type === 'root') {
|
|
|
|
let root = this.props.treeData.root;
|
|
|
|
this.setState({
|
|
|
|
isAddFileDialogShow: !this.state.isAddFileDialogShow,
|
|
|
|
opNode: root,
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
this.setState({isAddFileDialogShow: !this.state.isAddFileDialogShow});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
onAddFolderToggle = (type) => {
|
|
|
|
if (type === 'root') {
|
|
|
|
let root = this.props.treeData.root;
|
|
|
|
this.setState({
|
|
|
|
isAddFolderDialogShow: !this.state.isAddFolderDialogShow,
|
|
|
|
opNode: root,
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
this.setState({isAddFolderDialogShow: !this.state.isAddFolderDialogShow});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
onRenameToggle = () => {
|
|
|
|
this.setState({isRenameDialogShow: !this.state.isRenameDialogShow});
|
|
|
|
}
|
|
|
|
|
|
|
|
onDeleteToggle = () => {
|
|
|
|
this.setState({isDeleteDialogShow: !this.state.isDeleteDialogShow});
|
|
|
|
}
|
|
|
|
|
2019-03-20 03:04:36 +00:00
|
|
|
onCopyToggle =() => {
|
|
|
|
this.setState({isCopyDialogShow: !this.state.isCopyDialogShow})
|
|
|
|
}
|
|
|
|
|
|
|
|
onMoveToggle = () => {
|
|
|
|
this.setState({isMoveDialogShow: !this.state.isMoveDialogShow})
|
|
|
|
}
|
|
|
|
|
2019-02-20 03:54:25 +00:00
|
|
|
onAddFolderNode = (dirPath) => {
|
|
|
|
this.setState({isAddFolderDialogShow: !this.state.isAddFolderDialogShow});
|
|
|
|
this.props.onAddFolderNode(dirPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
onAddFileNode = (filePath, isDraft) => {
|
|
|
|
this.setState({isAddFileDialogShow: !this.state.isAddFileDialogShow});
|
|
|
|
this.props.onAddFileNode(filePath, isDraft);
|
|
|
|
}
|
|
|
|
|
|
|
|
onRenameNode = (newName) => {
|
|
|
|
this.setState({isRenameDialogShow: !this.state.isRenameDialogShow});
|
|
|
|
let node = this.state.opNode;
|
|
|
|
this.props.onRenameNode(node, newName);
|
|
|
|
}
|
|
|
|
|
|
|
|
onDeleteNode = () => {
|
|
|
|
this.setState({isDeleteDialogShow: !this.state.isDeleteDialogShow});
|
|
|
|
let node = this.state.opNode;
|
|
|
|
this.props.onDeleteNode(node);
|
|
|
|
}
|
|
|
|
|
2019-02-27 09:34:39 +00:00
|
|
|
onOpenFile = (node) => {
|
|
|
|
let newUrl = siteRoot + 'lib/' + this.props.repoID + '/file' + Utils.encodePath(node.path);
|
|
|
|
window.open(newUrl, '_blank');
|
|
|
|
}
|
|
|
|
|
2019-02-20 03:54:25 +00:00
|
|
|
checkDuplicatedName = (newName) => {
|
|
|
|
let node = this.state.opNode;
|
|
|
|
// root node to new node conditions: parentNode is null,
|
|
|
|
let parentNode = node.parentNode ? node.parentNode : node;
|
|
|
|
let childrenObject = parentNode.children.map(item => {
|
|
|
|
return item.object;
|
|
|
|
});
|
|
|
|
let isDuplicated = childrenObject.some(object => {
|
|
|
|
return object.name === newName;
|
|
|
|
});
|
|
|
|
return isDuplicated;
|
|
|
|
}
|
|
|
|
|
2019-03-15 07:48:16 +00:00
|
|
|
showNodeImagePopup = (node) => {
|
|
|
|
let childrenNode = node.parentNode.children;
|
|
|
|
let items = childrenNode.filter((item) => {
|
|
|
|
return Utils.imageCheck(item.object.name);
|
|
|
|
});
|
|
|
|
let imageNames = items.map((item) => {
|
|
|
|
return item.object.name;
|
|
|
|
})
|
|
|
|
this.setState({
|
|
|
|
isNodeImagePopupOpen: true,
|
|
|
|
imageNodeItems: this.prepareImageItems(node),
|
|
|
|
imageIndex: imageNames.indexOf(node.object.name)
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
prepareImageItems = (node) => {
|
|
|
|
let childrenNode = node.parentNode.children;
|
|
|
|
let items = childrenNode.filter((item) => {
|
|
|
|
return Utils.imageCheck(item.object.name);
|
|
|
|
});
|
|
|
|
|
|
|
|
const useThumbnail = !this.props.currentRepoInfo.encrypted;
|
|
|
|
let prepareItem = (item) => {
|
|
|
|
const name = item.object.name;
|
|
|
|
|
|
|
|
const path = Utils.encodePath(Utils.joinPath(node.parentNode.path, name));
|
|
|
|
const fileExt = name.substr(name.lastIndexOf('.') + 1).toLowerCase();
|
|
|
|
const isGIF = fileExt === 'gif';
|
|
|
|
|
|
|
|
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
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
return items.map((item) => { return prepareItem(item); });
|
|
|
|
}
|
|
|
|
|
|
|
|
closeNodeImagePopup = () => {
|
|
|
|
this.setState({
|
|
|
|
isNodeImagePopupOpen: false
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
moveToPrevImage = () => {
|
|
|
|
const imageItemsLength = this.state.imageNodeItems.length;
|
|
|
|
this.setState((prevState) => ({
|
|
|
|
imageIndex: (prevState.imageIndex + imageItemsLength - 1) % imageItemsLength
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
|
|
|
moveToNextImage = () => {
|
|
|
|
const imageItemsLength = this.state.imageNodeItems.length;
|
|
|
|
this.setState((prevState) => ({
|
|
|
|
imageIndex: (prevState.imageIndex + 1) % imageItemsLength
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
2019-02-20 03:54:25 +00:00
|
|
|
render() {
|
2019-02-28 06:33:21 +00:00
|
|
|
let flex = this.props.navRate ? '0 0 ' + this.props.navRate * 100 + '%' : '0 0 25%';
|
|
|
|
const select = this.props.inResizing ? 'none' : '';
|
2019-03-15 07:48:16 +00:00
|
|
|
|
|
|
|
const imageNodeItems = this.state.imageNodeItems;
|
|
|
|
const imageIndex = this.state.imageIndex;
|
|
|
|
const imageItemsLength = imageNodeItems.length;
|
|
|
|
const imageCaption = imageItemsLength && (
|
|
|
|
<Fragment>
|
|
|
|
<span>{gettext('%curr% of %total%').replace('%curr%', imageIndex + 1).replace('%total%', imageItemsLength)}</span>
|
|
|
|
<br />
|
|
|
|
<a href={imageNodeItems[imageIndex].url} target="_blank">{gettext('Open in New Tab')}</a>
|
|
|
|
</Fragment>
|
|
|
|
);
|
|
|
|
|
2019-02-20 03:54:25 +00:00
|
|
|
return (
|
|
|
|
<Fragment>
|
2019-02-28 06:33:21 +00:00
|
|
|
<div className="dir-content-nav" role="navigation" style={{flex: (flex), userSelect: select}}>
|
2019-02-21 09:37:04 +00:00
|
|
|
{this.props.isTreeDataLoading ?
|
|
|
|
(<Loading/>) :
|
|
|
|
(<TreeView
|
|
|
|
repoPermission={this.props.repoPermission}
|
|
|
|
isNodeMenuShow={this.isNodeMenuShow}
|
|
|
|
treeData={this.props.treeData}
|
|
|
|
currentPath={this.props.currentPath}
|
|
|
|
onNodeClick={this.onNodeClick}
|
|
|
|
onNodeExpanded={this.props.onNodeExpanded}
|
|
|
|
onNodeCollapse={this.props.onNodeCollapse}
|
|
|
|
onMenuItemClick={this.onMenuItemClick}
|
|
|
|
onFreezedItem={this.onFreezedItem}
|
|
|
|
onUnFreezedItem={this.onUnFreezedItem}
|
|
|
|
/>)
|
|
|
|
}
|
2019-02-20 03:54:25 +00:00
|
|
|
</div>
|
|
|
|
{this.state.isAddFolderDialogShow && (
|
|
|
|
<ModalPortal>
|
|
|
|
<CreateFolder
|
|
|
|
parentPath={this.state.opNode.path}
|
|
|
|
onAddFolder={this.onAddFolderNode}
|
|
|
|
checkDuplicatedName={this.checkDuplicatedName}
|
|
|
|
addFolderCancel={this.onAddFolderToggle}
|
|
|
|
/>
|
|
|
|
</ModalPortal>
|
|
|
|
)}
|
|
|
|
{this.state.isAddFileDialogShow && (
|
|
|
|
<ModalPortal>
|
|
|
|
<CreateFile
|
|
|
|
parentPath={this.state.opNode.path}
|
|
|
|
onAddFile={this.onAddFileNode}
|
|
|
|
checkDuplicatedName={this.checkDuplicatedName}
|
|
|
|
addFileCancel={this.onAddFileToggle}
|
|
|
|
/>
|
|
|
|
</ModalPortal>
|
|
|
|
)}
|
|
|
|
{this.state.isRenameDialogShow && (
|
|
|
|
<ModalPortal>
|
|
|
|
<Rename
|
|
|
|
currentNode={this.state.opNode}
|
|
|
|
onRename={this.onRenameNode}
|
|
|
|
checkDuplicatedName={this.checkDuplicatedName}
|
|
|
|
toggleCancel={this.onRenameToggle}
|
|
|
|
/>
|
|
|
|
</ModalPortal>
|
|
|
|
)}
|
|
|
|
{this.state.isDeleteDialogShow && (
|
|
|
|
<ModalPortal>
|
|
|
|
<Delete
|
|
|
|
currentNode={this.state.opNode}
|
|
|
|
handleSubmit={this.onDeleteNode}
|
|
|
|
toggleCancel={this.onDeleteToggle}
|
|
|
|
/>
|
|
|
|
</ModalPortal>
|
|
|
|
)}
|
2019-03-20 03:04:36 +00:00
|
|
|
{this.state.isCopyDialogShow && (
|
|
|
|
<ModalPortal>
|
|
|
|
<Copy
|
|
|
|
path={this.state.opNode.parentNode.path}
|
|
|
|
repoID={this.props.repoID}
|
|
|
|
dirent={this.state.opNode.object}
|
|
|
|
onItemCopy={this.props.onItemCopy}
|
|
|
|
repoEncrypted={this.props.currentRepoInfo.encrypted}
|
|
|
|
onCancelCopy={this.onCopyToggle}
|
|
|
|
isMutipleOperation={this.state.isMutipleOperation}
|
|
|
|
/>
|
|
|
|
</ModalPortal>
|
|
|
|
)}
|
|
|
|
{this.state.isMoveDialogShow && (
|
|
|
|
<ModalPortal>
|
|
|
|
<Move
|
|
|
|
path={this.state.opNode.parentNode.path}
|
|
|
|
repoID={this.props.repoID}
|
|
|
|
dirent={this.state.opNode.object}
|
|
|
|
onItemMove={this.props.onItemMove}
|
|
|
|
repoEncrypted={this.props.currentRepoInfo.encrypted}
|
|
|
|
onCancelMove={this.onMoveToggle}
|
|
|
|
isMutipleOperation={this.state.isMutipleOperation}
|
|
|
|
/>
|
|
|
|
</ModalPortal>
|
|
|
|
)}
|
2019-03-15 07:48:16 +00:00
|
|
|
{this.state.isNodeImagePopupOpen && (
|
|
|
|
<Lightbox
|
|
|
|
mainSrc={imageNodeItems[imageIndex].src}
|
|
|
|
imageCaption={imageCaption}
|
|
|
|
imageTitle={imageNodeItems[imageIndex].name}
|
|
|
|
nextSrc={imageNodeItems[(imageIndex + 1) % imageItemsLength].src}
|
|
|
|
prevSrc={imageNodeItems[(imageIndex + imageItemsLength - 1) % imageItemsLength].src}
|
|
|
|
onCloseRequest={this.closeNodeImagePopup}
|
|
|
|
onMovePrevRequest={this.moveToPrevImage}
|
|
|
|
onMoveNextRequest={this.moveToNextImage}
|
|
|
|
imagePadding={70}
|
|
|
|
imageLoadErrorMessage={gettext('The image could not be loaded.')}
|
|
|
|
prevLabel={gettext('Previous (Left arrow key)')}
|
|
|
|
nextLabel={gettext('Next (Right arrow key)')}
|
|
|
|
closeLabel={gettext('Close (Esc)')}
|
|
|
|
zoomInLabel={gettext('Zoom in')}
|
|
|
|
zoomOutLabel={gettext('Zoom out')}
|
|
|
|
/>
|
|
|
|
)}
|
2019-02-20 03:54:25 +00:00
|
|
|
</Fragment>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-28 06:33:21 +00:00
|
|
|
DirColumnNav.defaultProps={
|
|
|
|
navRate: 0.25
|
|
|
|
};
|
|
|
|
|
2019-02-21 09:37:04 +00:00
|
|
|
DirColumnNav.propTypes = propTypes;
|
2019-02-20 03:54:25 +00:00
|
|
|
|
2019-02-21 09:37:04 +00:00
|
|
|
export default DirColumnNav;
|