mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-01 15:09:14 +00:00
Double name bug repair (#2879)
This commit is contained in:
@@ -1,12 +1,14 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Button, Modal, ModalHeader, Input, ModalBody, ModalFooter, Form, FormGroup, Label } from 'reactstrap';
|
import { Button, Modal, ModalHeader, Input, ModalBody, ModalFooter, Form, FormGroup, Label, Alert } from 'reactstrap';
|
||||||
import { gettext } from '../../utils/constants';
|
import { gettext } from '../../utils/constants';
|
||||||
|
import { Utils } from '../../utils/utils';
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
fileType: PropTypes.string,
|
fileType: PropTypes.string,
|
||||||
parentPath: PropTypes.string.isRequired,
|
parentPath: PropTypes.string.isRequired,
|
||||||
onAddFile: PropTypes.func.isRequired,
|
onAddFile: PropTypes.func.isRequired,
|
||||||
|
checkDuplicatedName: PropTypes.func.isRequired,
|
||||||
addFileCancel: PropTypes.func.isRequired,
|
addFileCancel: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -17,10 +19,22 @@ class CreateFile extends React.Component {
|
|||||||
parentPath: '',
|
parentPath: '',
|
||||||
childName: props.fileType,
|
childName: props.fileType,
|
||||||
isDraft: false,
|
isDraft: false,
|
||||||
|
errMessage: '',
|
||||||
};
|
};
|
||||||
this.newInput = React.createRef();
|
this.newInput = React.createRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
let parentPath = this.props.parentPath;
|
||||||
|
if (parentPath[parentPath.length - 1] === '/') { // mainPanel
|
||||||
|
this.setState({parentPath: parentPath});
|
||||||
|
} else {
|
||||||
|
this.setState({parentPath: parentPath + '/'}); // sidePanel
|
||||||
|
}
|
||||||
|
this.newInput.focus();
|
||||||
|
this.newInput.setSelectionRange(0,0);
|
||||||
|
}
|
||||||
|
|
||||||
handleChange = (e) => {
|
handleChange = (e) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
childName: e.target.value,
|
childName: e.target.value,
|
||||||
@@ -28,14 +42,23 @@ class CreateFile extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleSubmit = () => {
|
handleSubmit = () => {
|
||||||
let path = this.state.parentPath + this.state.childName;
|
let isDuplicated = this.checkDuplicatedName();
|
||||||
let isDraft = this.state.isDraft;
|
let newName = this.state.childName
|
||||||
this.props.onAddFile(path, isDraft);
|
if (isDuplicated) {
|
||||||
|
let errMessage = gettext('The name \'{name}\' is already occupied, please choose another name.');
|
||||||
|
errMessage = errMessage.replace('{name}', Utils.HTMLescape(newName));
|
||||||
|
this.setState({errMessage: errMessage});
|
||||||
|
} else {
|
||||||
|
let path = this.state.parentPath + newName;
|
||||||
|
let isDraft = this.state.isDraft;
|
||||||
|
this.props.onAddFile(path, isDraft);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleKeyPress = (e) => {
|
handleKeyPress = (e) => {
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
this.handleSubmit();
|
this.handleSubmit();
|
||||||
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,15 +115,9 @@ class CreateFile extends React.Component {
|
|||||||
this.props.addFileCancel();
|
this.props.addFileCancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
checkDuplicatedName = () => {
|
||||||
let parentPath = this.props.parentPath;
|
let isDuplicated = this.props.checkDuplicatedName(this.state.childName);
|
||||||
if (parentPath[parentPath.length - 1] === '/') { // mainPanel
|
return isDuplicated;
|
||||||
this.setState({parentPath: parentPath});
|
|
||||||
} else {
|
|
||||||
this.setState({parentPath: parentPath + '/'}); // sidePanel
|
|
||||||
}
|
|
||||||
this.newInput.focus();
|
|
||||||
this.newInput.setSelectionRange(0,0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@@ -127,6 +144,7 @@ class CreateFile extends React.Component {
|
|||||||
</FormGroup>
|
</FormGroup>
|
||||||
)}
|
)}
|
||||||
</Form>
|
</Form>
|
||||||
|
{this.state.errMessage && <Alert color="danger" className="mt-2">{this.state.errMessage}</Alert>}
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<Button color="secondary" onClick={this.toggle}>{gettext('Cancel')}</Button>
|
<Button color="secondary" onClick={this.toggle}>{gettext('Cancel')}</Button>
|
||||||
|
@@ -1,12 +1,14 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Button, Modal, ModalHeader, Input, ModalBody, ModalFooter, Form, FormGroup, Label } from 'reactstrap';
|
import { Button, Modal, ModalHeader, Input, ModalBody, ModalFooter, Form, FormGroup, Label, Alert } from 'reactstrap';
|
||||||
import { gettext } from '../../utils/constants';
|
import { gettext } from '../../utils/constants';
|
||||||
|
import { Utils } from '../../utils/utils';
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
fileType: PropTypes.string,
|
fileType: PropTypes.string,
|
||||||
parentPath: PropTypes.string.isRequired,
|
parentPath: PropTypes.string.isRequired,
|
||||||
onAddFolder: PropTypes.func.isRequired,
|
onAddFolder: PropTypes.func.isRequired,
|
||||||
|
checkDuplicatedName: PropTypes.func.isRequired,
|
||||||
addFolderCancel: PropTypes.func.isRequired,
|
addFolderCancel: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -15,32 +17,12 @@ class CreateForder extends React.Component {
|
|||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
parentPath: '',
|
parentPath: '',
|
||||||
childName: ''
|
childName: '',
|
||||||
|
errMessage: ''
|
||||||
};
|
};
|
||||||
this.newInput = React.createRef();
|
this.newInput = React.createRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChange = (e) => {
|
|
||||||
this.setState({
|
|
||||||
childName: e.target.value,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
handleSubmit = () => {
|
|
||||||
let path = this.state.parentPath + this.state.childName;
|
|
||||||
this.props.onAddFolder(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleKeyPress = (e) => {
|
|
||||||
if (e.key === 'Enter') {
|
|
||||||
this.handleSubmit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
toggle = () => {
|
|
||||||
this.props.addFolderCancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
let parentPath = this.props.parentPath;
|
let parentPath = this.props.parentPath;
|
||||||
if (parentPath[parentPath.length - 1] === '/') { // mainPanel
|
if (parentPath[parentPath.length - 1] === '/') { // mainPanel
|
||||||
@@ -52,6 +34,39 @@ class CreateForder extends React.Component {
|
|||||||
this.newInput.setSelectionRange(0,0);
|
this.newInput.setSelectionRange(0,0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleChange = (e) => {
|
||||||
|
this.setState({childName: e.target.value});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSubmit = () => {
|
||||||
|
let newName = this.state.childName;
|
||||||
|
let isDuplicated = this.checkDuplicatedName();
|
||||||
|
if (isDuplicated) {
|
||||||
|
let errMessage = gettext('The name \'{name}\' is already occupied, please choose another name.');
|
||||||
|
errMessage = errMessage.replace('{name}', Utils.HTMLescape(newName));
|
||||||
|
this.setState({errMessage: errMessage});
|
||||||
|
} else {
|
||||||
|
let path = this.state.parentPath + newName;
|
||||||
|
this.props.onAddFolder(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleKeyPress = (e) => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
this.handleSubmit();
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle = () => {
|
||||||
|
this.props.addFolderCancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
checkDuplicatedName = () => {
|
||||||
|
let isDuplicated = this.props.checkDuplicatedName(this.state.childName);
|
||||||
|
return isDuplicated;
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Modal isOpen={true} toggle={this.toggle}>
|
<Modal isOpen={true} toggle={this.toggle}>
|
||||||
@@ -69,6 +84,7 @@ class CreateForder extends React.Component {
|
|||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
</Form>
|
</Form>
|
||||||
|
{this.state.errMessage && <Alert color="danger" className="mt-2">{this.state.errMessage}</Alert>}
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<Button color="secondary" onClick={this.toggle}>{gettext('Cancel')}</Button>
|
<Button color="secondary" onClick={this.toggle}>{gettext('Cancel')}</Button>
|
||||||
|
@@ -1,12 +1,14 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { gettext } from '../../utils/constants';
|
import { gettext } from '../../utils/constants';
|
||||||
import { Button, Modal, ModalHeader, Input, ModalBody, ModalFooter } from 'reactstrap';
|
import { Utils } from '../../utils/utils';
|
||||||
|
import { Button, Modal, ModalHeader, Input, ModalBody, ModalFooter, Alert } from 'reactstrap';
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
currentNode: PropTypes.object,
|
currentNode: PropTypes.object,
|
||||||
onRename: PropTypes.func.isRequired,
|
onRename: PropTypes.func.isRequired,
|
||||||
toggleCancel: PropTypes.func.isRequired,
|
toggleCancel: PropTypes.func.isRequired,
|
||||||
|
checkDuplicatedName: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
class Rename extends React.Component {
|
class Rename extends React.Component {
|
||||||
@@ -14,34 +16,13 @@ class Rename extends React.Component {
|
|||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
newName: '',
|
newName: '',
|
||||||
|
errMessage: '',
|
||||||
};
|
};
|
||||||
this.newInput = React.createRef();
|
this.newInput = React.createRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChange = (e) => {
|
|
||||||
this.setState({
|
|
||||||
newName: e.target.value,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
handleSubmit = () => {
|
|
||||||
this.props.onRename(this.state.newName);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleKeyPress = (e) => {
|
|
||||||
if (e.key === 'Enter') {
|
|
||||||
this.handleSubmit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
toggle = () => {
|
|
||||||
this.props.toggleCancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
this.setState({
|
this.setState({newName: this.props.currentNode.object.name});
|
||||||
newName: this.props.currentNode.object.name
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
@@ -60,12 +41,66 @@ class Rename extends React.Component {
|
|||||||
componentWillReceiveProps(nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
this.changeState(nextProps.currentNode);
|
this.changeState(nextProps.currentNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleChange = (e) => {
|
||||||
|
this.setState({newName: e.target.value});
|
||||||
|
}
|
||||||
|
|
||||||
changeState(currentNode) {
|
handleSubmit = () => {
|
||||||
|
let { isValid, errMessage } = this.validateInput();
|
||||||
|
if (!isValid) {
|
||||||
|
this.setState({errMessage : errMessage});
|
||||||
|
} else {
|
||||||
|
let isDuplicated = this.checkDuplicatedName();
|
||||||
|
if (isDuplicated) {
|
||||||
|
let errMessage = gettext('The name \'{name}\' is already occupied, please choose another name.');
|
||||||
|
errMessage = errMessage.replace('{name}', Utils.HTMLescape(this.state.newName));
|
||||||
|
this.setState({errMessage: errMessage});
|
||||||
|
} else {
|
||||||
|
this.props.onRename(this.state.newName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleKeyPress = (e) => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
this.handleSubmit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle = () => {
|
||||||
|
this.props.toggleCancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
changeState = (currentNode) => {
|
||||||
let name = currentNode.object.name;
|
let name = currentNode.object.name;
|
||||||
this.setState({newName: name});
|
this.setState({newName: name});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validateInput = () => {
|
||||||
|
let newName = this.state.newName.trim();
|
||||||
|
let isValid = true;
|
||||||
|
let errMessage = '';
|
||||||
|
if (!newName) {
|
||||||
|
isValid = false;
|
||||||
|
errMessage = gettext('Name is required.');
|
||||||
|
return { isValid, errMessage };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newName.indexOf('/') > -1) {
|
||||||
|
isValid = false;
|
||||||
|
errMessage = gettext('Name should not include ' + '\'/\'' + '.');
|
||||||
|
return { isValid, errMessage };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { isValid, errMessage };
|
||||||
|
}
|
||||||
|
|
||||||
|
checkDuplicatedName = () => {
|
||||||
|
let isDuplicated = this.props.checkDuplicatedName(this.state.newName);
|
||||||
|
return isDuplicated;
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let type = this.props.currentNode.object.type;
|
let type = this.props.currentNode.object.type;
|
||||||
return (
|
return (
|
||||||
@@ -74,6 +109,7 @@ class Rename extends React.Component {
|
|||||||
<ModalBody>
|
<ModalBody>
|
||||||
<p>{type === 'file' ? gettext('Enter the new file name:'): gettext('Enter the new folder name:')}</p>
|
<p>{type === 'file' ? gettext('Enter the new file name:'): gettext('Enter the new folder name:')}</p>
|
||||||
<Input onKeyPress={this.handleKeyPress} innerRef={input => {this.newInput = input;}} placeholder="newName" value={this.state.newName} onChange={this.handleChange} />
|
<Input onKeyPress={this.handleKeyPress} innerRef={input => {this.newInput = input;}} placeholder="newName" value={this.state.newName} onChange={this.handleChange} />
|
||||||
|
{this.state.errMessage && <Alert color="danger" className="mt-2">{this.state.errMessage}</Alert>}
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<Button color="primary" onClick={this.handleSubmit}>{gettext('Submit')}</Button>
|
<Button color="primary" onClick={this.handleSubmit}>{gettext('Submit')}</Button>
|
||||||
|
@@ -157,6 +157,7 @@ class DirPanel extends React.Component {
|
|||||||
path={this.props.path}
|
path={this.props.path}
|
||||||
repoID={this.props.repoID}
|
repoID={this.props.repoID}
|
||||||
showShareBtn={this.props.showShareBtn}
|
showShareBtn={this.props.showShareBtn}
|
||||||
|
direntList={this.props.direntList}
|
||||||
onAddFile={this.props.onAddFile}
|
onAddFile={this.props.onAddFile}
|
||||||
onAddFolder={this.props.onAddFolder}
|
onAddFolder={this.props.onAddFolder}
|
||||||
onUploadFile={this.onUploadFile}
|
onUploadFile={this.onUploadFile}
|
||||||
|
@@ -11,6 +11,7 @@ import Lightbox from 'react-image-lightbox';
|
|||||||
import 'react-image-lightbox/style.css';
|
import 'react-image-lightbox/style.css';
|
||||||
|
|
||||||
import '../../css/tip-for-new-md.css';
|
import '../../css/tip-for-new-md.css';
|
||||||
|
import toaster from '../toast';
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
path: PropTypes.string.isRequired,
|
path: PropTypes.string.isRequired,
|
||||||
@@ -61,6 +62,19 @@ class DirentListView extends React.Component {
|
|||||||
this.setState({isItemFreezed: false});
|
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 occupied, please choose another name.');
|
||||||
|
errMessage = errMessage.replace('{name}', Utils.HTMLescape(newName));
|
||||||
|
toaster.danger(errMessage);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.props.onItemRename(dirent, newName);
|
||||||
|
}
|
||||||
|
|
||||||
onItemRenameToggle = () => {
|
onItemRenameToggle = () => {
|
||||||
this.onFreezedItem();
|
this.onFreezedItem();
|
||||||
}
|
}
|
||||||
@@ -238,11 +252,12 @@ class DirentListView extends React.Component {
|
|||||||
repoID={this.props.repoID}
|
repoID={this.props.repoID}
|
||||||
currentRepoInfo={this.props.currentRepoInfo}
|
currentRepoInfo={this.props.currentRepoInfo}
|
||||||
isRepoOwner={this.props.isRepoOwner}
|
isRepoOwner={this.props.isRepoOwner}
|
||||||
|
direntList={this.props.direntList}
|
||||||
onItemClick={this.props.onItemClick}
|
onItemClick={this.props.onItemClick}
|
||||||
onItemRenameToggle={this.onItemRenameToggle}
|
onItemRenameToggle={this.onItemRenameToggle}
|
||||||
onItemSelected={this.props.onItemSelected}
|
onItemSelected={this.props.onItemSelected}
|
||||||
onItemDelete={this.props.onItemDelete}
|
onItemDelete={this.props.onItemDelete}
|
||||||
onItemRename={this.props.onItemRename}
|
onItemRename={this.onItemRename}
|
||||||
onItemMove={this.props.onItemMove}
|
onItemMove={this.props.onItemMove}
|
||||||
onItemCopy={this.props.onItemCopy}
|
onItemCopy={this.props.onItemCopy}
|
||||||
updateDirent={this.props.updateDirent}
|
updateDirent={this.props.updateDirent}
|
||||||
|
@@ -20,6 +20,7 @@ const propTypes = {
|
|||||||
onUploadFolder: PropTypes.func.isRequired,
|
onUploadFolder: PropTypes.func.isRequired,
|
||||||
isDraft: PropTypes.bool,
|
isDraft: PropTypes.bool,
|
||||||
hasDraft: PropTypes.bool,
|
hasDraft: PropTypes.bool,
|
||||||
|
direntList: PropTypes.array.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
class DirOperationToolbar extends React.Component {
|
class DirOperationToolbar extends React.Component {
|
||||||
@@ -167,6 +168,14 @@ class DirOperationToolbar extends React.Component {
|
|||||||
this.props.goDraftPage();
|
this.props.goDraftPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkDuplicatedName = (newName) => {
|
||||||
|
let direntList = this.props.direntList;
|
||||||
|
let isDuplicated = direntList.some(object => {
|
||||||
|
return object.name === newName;
|
||||||
|
});
|
||||||
|
return isDuplicated;
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let { path, isViewFile } = this.props;
|
let { path, isViewFile } = this.props;
|
||||||
let itemType = isViewFile ? 'file' : 'dir';
|
let itemType = isViewFile ? 'file' : 'dir';
|
||||||
@@ -220,8 +229,9 @@ class DirOperationToolbar extends React.Component {
|
|||||||
parentPath={this.props.path}
|
parentPath={this.props.path}
|
||||||
fileType={this.state.fileType}
|
fileType={this.state.fileType}
|
||||||
onAddFile={this.onAddFile}
|
onAddFile={this.onAddFile}
|
||||||
|
checkDuplicatedName={this.checkDuplicatedName}
|
||||||
addFileCancel={this.onCreateFileToggle}
|
addFileCancel={this.onCreateFileToggle}
|
||||||
/>
|
/>
|
||||||
</ModalPortal>
|
</ModalPortal>
|
||||||
)}
|
)}
|
||||||
{this.state.isCreateFolderDialogShow && (
|
{this.state.isCreateFolderDialogShow && (
|
||||||
@@ -229,6 +239,7 @@ class DirOperationToolbar extends React.Component {
|
|||||||
<CreateFolder
|
<CreateFolder
|
||||||
parentPath={this.props.path}
|
parentPath={this.props.path}
|
||||||
onAddFolder={this.onAddFolder}
|
onAddFolder={this.onAddFolder}
|
||||||
|
checkDuplicatedName={this.checkDuplicatedName}
|
||||||
addFolderCancel={this.onCreateFolderToggle}
|
addFolderCancel={this.onCreateFolderToggle}
|
||||||
/>
|
/>
|
||||||
</ModalPortal>
|
</ModalPortal>
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import { Utils } from '../../utils/utils';
|
import { Utils } from '../../utils/utils';
|
||||||
import Tree from './tree';
|
import Tree from './tree';
|
||||||
import TreeNode from './tree-node';
|
import TreeNode from './tree-node';
|
||||||
|
import Dirent from '../../models/dirent';
|
||||||
|
|
||||||
class TreeHelper {
|
class TreeHelper {
|
||||||
|
|
||||||
@@ -74,11 +75,12 @@ class TreeHelper {
|
|||||||
return treeCopy;
|
return treeCopy;
|
||||||
}
|
}
|
||||||
|
|
||||||
moveNodeByPath(tree, nodePath, destPath) {
|
moveNodeByPath(tree, nodePath, destPath, nodeName) {
|
||||||
let treeCopy = tree.clone();
|
let treeCopy = tree.clone();
|
||||||
let node = treeCopy.getNodeByPath(nodePath);
|
let node = treeCopy.getNodeByPath(nodePath);
|
||||||
let destNode = treeCopy.getNodeByPath(destPath);
|
let destNode = treeCopy.getNodeByPath(destPath);
|
||||||
if (destNode && node) { // node has loaded
|
if (destNode && node) { // node has loaded
|
||||||
|
node.object.name = nodeName; // need not update path
|
||||||
treeCopy.moveNode(node, destNode);
|
treeCopy.moveNode(node, destNode);
|
||||||
}
|
}
|
||||||
if (!destNode && node){
|
if (!destNode && node){
|
||||||
@@ -94,7 +96,7 @@ class TreeHelper {
|
|||||||
nodePaths.forEach(nodePath => {
|
nodePaths.forEach(nodePath => {
|
||||||
let node = treeCopy.getNodeByPath(nodePath);
|
let node = treeCopy.getNodeByPath(nodePath);
|
||||||
treeCopy.moveNode(node, destNode);
|
treeCopy.moveNode(node, destNode);
|
||||||
})
|
});
|
||||||
} else {
|
} else {
|
||||||
nodePaths.forEach(nodePath=> {
|
nodePaths.forEach(nodePath=> {
|
||||||
let node = treeCopy.getNodeByPath(nodePath);
|
let node = treeCopy.getNodeByPath(nodePath);
|
||||||
@@ -104,12 +106,13 @@ class TreeHelper {
|
|||||||
return treeCopy;
|
return treeCopy;
|
||||||
}
|
}
|
||||||
|
|
||||||
copyNodeByPath(tree, nodePath, destPath) {
|
copyNodeByPath(tree, nodePath, destPath, nodeName) {
|
||||||
let treeCopy = tree.clone();
|
let treeCopy = tree.clone();
|
||||||
let node = treeCopy.getNodeByPath(nodePath);
|
|
||||||
node = node.clone(); // need a dup
|
|
||||||
let destNode = treeCopy.getNodeByPath(destPath);
|
let destNode = treeCopy.getNodeByPath(destPath);
|
||||||
|
let treeNode = treeCopy.getNodeByPath(nodePath);
|
||||||
if (destNode) {
|
if (destNode) {
|
||||||
|
let node = treeNode.clone(); // need a dup
|
||||||
|
node.object.name = nodeName; // need not update path
|
||||||
treeCopy.copyNode(node, destNode);
|
treeCopy.copyNode(node, destNode);
|
||||||
}
|
}
|
||||||
return treeCopy;
|
return treeCopy;
|
||||||
@@ -129,7 +132,7 @@ class TreeHelper {
|
|||||||
|
|
||||||
buildTree() {
|
buildTree() {
|
||||||
let tree = new Tree();
|
let tree = new Tree();
|
||||||
let object = {name: '/'};
|
let object = new Dirent({name: '/'});
|
||||||
let root = new TreeNode({object, isLoaded: false, isExpanded: true});
|
let root = new TreeNode({object, isLoaded: false, isExpanded: true});
|
||||||
tree.setRoot(root);
|
tree.setRoot(root);
|
||||||
return tree;
|
return tree;
|
||||||
|
@@ -2,7 +2,7 @@ class TreeNode {
|
|||||||
|
|
||||||
constructor({ path, object, isLoaded, isPreload, isExpanded, parentNode }) {
|
constructor({ path, object, isLoaded, isPreload, isExpanded, parentNode }) {
|
||||||
this.path = path || object.name, // The default setting is the object name, which is set to a relative path when the father is set.
|
this.path = path || object.name, // The default setting is the object name, which is set to a relative path when the father is set.
|
||||||
this.object = object;
|
this.object = object.clone();
|
||||||
this.isLoaded = isLoaded || false;
|
this.isLoaded = isLoaded || false;
|
||||||
this.isPreload = isPreload || false;
|
this.isPreload = isPreload || false;
|
||||||
this.isExpanded = isExpanded || false;
|
this.isExpanded = isExpanded || false;
|
||||||
@@ -13,7 +13,7 @@ class TreeNode {
|
|||||||
clone() {
|
clone() {
|
||||||
let treeNode = new TreeNode({
|
let treeNode = new TreeNode({
|
||||||
path: this.path,
|
path: this.path,
|
||||||
object: this.object,
|
object: this.object.clone(),
|
||||||
isLoaded: this.isLoaded,
|
isLoaded: this.isLoaded,
|
||||||
isPreload: this.isPreload,
|
isPreload: this.isPreload,
|
||||||
isExpanded: this.isExpanded,
|
isExpanded: this.isExpanded,
|
||||||
@@ -102,7 +102,7 @@ class TreeNode {
|
|||||||
|
|
||||||
const treeNode = {
|
const treeNode = {
|
||||||
path: this.path,
|
path: this.path,
|
||||||
object: this.object,
|
object: this.object.clone(),
|
||||||
isLoaded: this.isLoaded,
|
isLoaded: this.isLoaded,
|
||||||
isPreload: this.isPreload,
|
isPreload: this.isPreload,
|
||||||
isExpanded: this.isExpanded,
|
isExpanded: this.isExpanded,
|
||||||
@@ -115,6 +115,7 @@ class TreeNode {
|
|||||||
|
|
||||||
static deserializefromJson(json) {
|
static deserializefromJson(json) {
|
||||||
let { path, object, isLoaded, isPreload, isExpanded, parentNode, children = [] } = json;
|
let { path, object, isLoaded, isPreload, isExpanded, parentNode, children = [] } = json;
|
||||||
|
object = object.clone();
|
||||||
|
|
||||||
const treeNode = new TreeNode({
|
const treeNode = new TreeNode({
|
||||||
path,
|
path,
|
||||||
|
@@ -34,6 +34,10 @@ class Dirent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clone() {
|
||||||
|
return new Dirent(this);
|
||||||
|
}
|
||||||
|
|
||||||
isDir() {
|
isDir() {
|
||||||
return this.type !== 'file';
|
return this.type !== 'file';
|
||||||
}
|
}
|
||||||
|
@@ -187,6 +187,7 @@ class MainPanel extends Component {
|
|||||||
repoID={repoID}
|
repoID={repoID}
|
||||||
isDraft={this.props.isDraft}
|
isDraft={this.props.isDraft}
|
||||||
hasDraft={this.props.hasDraft}
|
hasDraft={this.props.hasDraft}
|
||||||
|
direntList={this.props.direntList}
|
||||||
permission={this.props.permission}
|
permission={this.props.permission}
|
||||||
isViewFile={this.props.isViewFile}
|
isViewFile={this.props.isViewFile}
|
||||||
showShareBtn={this.props.showShareBtn}
|
showShareBtn={this.props.showShareBtn}
|
||||||
|
@@ -1,10 +1,12 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { gettext } from '../../utils/constants';
|
|
||||||
import { Dropdown, DropdownMenu, DropdownToggle, DropdownItem } from 'reactstrap';
|
import { Dropdown, DropdownMenu, DropdownToggle, DropdownItem } from 'reactstrap';
|
||||||
|
import { gettext } from '../../utils/constants';
|
||||||
|
import { Utils } from '../../utils/utils';
|
||||||
import TreeView from '../../components/tree-view-2/tree-view';
|
import TreeView from '../../components/tree-view-2/tree-view';
|
||||||
import Logo from '../../components/logo';
|
import Logo from '../../components/logo';
|
||||||
import Loading from '../../components/loading';
|
import Loading from '../../components/loading';
|
||||||
|
import toaster from '../../components/toast';
|
||||||
import ModalPortal from '../../components/modal-portal';
|
import ModalPortal from '../../components/modal-portal';
|
||||||
import Delete from '../../components/dialog/delete-dialog';
|
import Delete from '../../components/dialog/delete-dialog';
|
||||||
import Rename from '../../components/dialog/rename-dialog';
|
import Rename from '../../components/dialog/rename-dialog';
|
||||||
@@ -141,6 +143,19 @@ class SidePanel extends Component {
|
|||||||
this.props.onDeleteNode(node);
|
this.props.onDeleteNode(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className={`side-panel wiki-side-panel ${this.props.closeSideBar ? '': 'left-zero'}`}>
|
<div className={`side-panel wiki-side-panel ${this.props.closeSideBar ? '': 'left-zero'}`}>
|
||||||
@@ -190,8 +205,9 @@ class SidePanel extends Component {
|
|||||||
<CreateFolder
|
<CreateFolder
|
||||||
parentPath={this.state.opNode.path}
|
parentPath={this.state.opNode.path}
|
||||||
onAddFolder={this.onAddFolderNode}
|
onAddFolder={this.onAddFolderNode}
|
||||||
|
checkDuplicatedName={this.checkDuplicatedName}
|
||||||
addFolderCancel={this.onAddFolderToggle}
|
addFolderCancel={this.onAddFolderToggle}
|
||||||
/>
|
/>
|
||||||
</ModalPortal>
|
</ModalPortal>
|
||||||
)}
|
)}
|
||||||
{this.state.isAddFileDialogShow && (
|
{this.state.isAddFileDialogShow && (
|
||||||
@@ -200,8 +216,9 @@ class SidePanel extends Component {
|
|||||||
fileType={'.md'}
|
fileType={'.md'}
|
||||||
parentPath={this.state.opNode.path}
|
parentPath={this.state.opNode.path}
|
||||||
onAddFile={this.onAddFileNode}
|
onAddFile={this.onAddFileNode}
|
||||||
|
checkDuplicatedName={this.checkDuplicatedName}
|
||||||
addFileCancel={this.onAddFileToggle}
|
addFileCancel={this.onAddFileToggle}
|
||||||
/>
|
/>
|
||||||
</ModalPortal>
|
</ModalPortal>
|
||||||
)}
|
)}
|
||||||
{this.state.isRenameDialogShow && (
|
{this.state.isRenameDialogShow && (
|
||||||
@@ -209,6 +226,7 @@ class SidePanel extends Component {
|
|||||||
<Rename
|
<Rename
|
||||||
currentNode={this.state.opNode}
|
currentNode={this.state.opNode}
|
||||||
onRename={this.onRenameNode}
|
onRename={this.onRenameNode}
|
||||||
|
checkDuplicatedName={this.checkDuplicatedName}
|
||||||
toggleCancel={this.onRenameToggle}
|
toggleCancel={this.onRenameToggle}
|
||||||
/>
|
/>
|
||||||
</ModalPortal>
|
</ModalPortal>
|
||||||
|
@@ -473,9 +473,9 @@ class Wiki extends Component {
|
|||||||
//just for view list state
|
//just for view list state
|
||||||
let dirName = dirent.name
|
let dirName = dirent.name
|
||||||
let direntPath = Utils.joinPath(this.state.path, dirName);
|
let direntPath = Utils.joinPath(this.state.path, dirName);
|
||||||
seafileAPI.moveDir(repoID, destRepo.repo_id,moveToDirentPath, this.state.path, dirName).then(() => {
|
seafileAPI.moveDir(repoID, destRepo.repo_id,moveToDirentPath, this.state.path, dirName).then(res => {
|
||||||
|
let nodeName = res.data[0].obj_name;
|
||||||
this.moveTreeNode(direntPath, moveToDirentPath, destRepo);
|
this.moveTreeNode(direntPath, moveToDirentPath, destRepo, nodeName);
|
||||||
this.moveDirent(direntPath);
|
this.moveDirent(direntPath);
|
||||||
|
|
||||||
let message = gettext('Successfully moved %(name)s.');
|
let message = gettext('Successfully moved %(name)s.');
|
||||||
@@ -492,8 +492,9 @@ class Wiki extends Component {
|
|||||||
//just for view list state
|
//just for view list state
|
||||||
let dirName = dirent.name;
|
let dirName = dirent.name;
|
||||||
let direntPath = Utils.joinPath(this.state.path, dirName);
|
let direntPath = Utils.joinPath(this.state.path, dirName);
|
||||||
seafileAPI.copyDir(repoID, destRepo.repo_id, copyToDirentPath, this.state.path, dirName).then(() => {
|
seafileAPI.copyDir(repoID, destRepo.repo_id, copyToDirentPath, this.state.path, dirName).then(res => {
|
||||||
this.copyTreeNode(direntPath, copyToDirentPath, destRepo);
|
let nodeName = res.data[0].obj_name;
|
||||||
|
this.copyTreeNode(direntPath, copyToDirentPath, destRepo, nodeName);
|
||||||
let message = gettext('Successfully copied %(name)s.');
|
let message = gettext('Successfully copied %(name)s.');
|
||||||
message = message.replace('%(name)s', dirName);
|
message = message.replace('%(name)s', dirName);
|
||||||
toaster.success(message);
|
toaster.success(message);
|
||||||
@@ -508,9 +509,12 @@ class Wiki extends Component {
|
|||||||
let direntPaths = this.getSelectedDirentPaths();
|
let direntPaths = this.getSelectedDirentPaths();
|
||||||
let dirNames = this.getSelectedDirentNames();
|
let dirNames = this.getSelectedDirentNames();
|
||||||
|
|
||||||
seafileAPI.moveDir(repoID, destRepo.repo_id, destDirentPath, this.state.path, dirNames).then(() => {
|
seafileAPI.moveDir(repoID, destRepo.repo_id, destDirentPath, this.state.path, dirNames).then(res => {
|
||||||
direntPaths.forEach(direntPath => {
|
let names = res.data.map(item => {
|
||||||
this.moveTreeNode(direntPath, destDirentPath, destRepo);
|
return item.obj_name;
|
||||||
|
});
|
||||||
|
direntPaths.forEach((direntPath, index) => {
|
||||||
|
this.moveTreeNode(direntPath, destDirentPath, destRepo, names[index]);
|
||||||
this.moveDirent(direntPath);
|
this.moveDirent(direntPath);
|
||||||
});
|
});
|
||||||
let message = gettext('Successfully moved %(name)s.');
|
let message = gettext('Successfully moved %(name)s.');
|
||||||
@@ -527,9 +531,12 @@ class Wiki extends Component {
|
|||||||
let direntPaths = this.getSelectedDirentPaths();
|
let direntPaths = this.getSelectedDirentPaths();
|
||||||
let dirNames = this.getSelectedDirentNames();
|
let dirNames = this.getSelectedDirentNames();
|
||||||
|
|
||||||
seafileAPI.copyDir(repoID, destRepo.repo_id, destDirentPath, this.state.path, dirNames).then(() => {
|
seafileAPI.copyDir(repoID, destRepo.repo_id, destDirentPath, this.state.path, dirNames).then(res => {
|
||||||
direntPaths.forEach(direntPath => {
|
let names = res.data.map(item => {
|
||||||
this.copyTreeNode(direntPath, destDirentPath, destRepo);
|
return item.obj_name;
|
||||||
|
});
|
||||||
|
direntPaths.forEach((direntPath, index) => {
|
||||||
|
this.copyTreeNode(direntPath, destDirentPath, destRepo, names[index]);
|
||||||
});
|
});
|
||||||
let message = gettext('Successfully copied %(name)s.');
|
let message = gettext('Successfully copied %(name)s.');
|
||||||
message = message.replace('%(name)s', dirNames);
|
message = message.replace('%(name)s', dirNames);
|
||||||
@@ -911,21 +918,21 @@ class Wiki extends Component {
|
|||||||
this.setState({treeData: tree});
|
this.setState({treeData: tree});
|
||||||
}
|
}
|
||||||
|
|
||||||
moveTreeNode = (nodePath, moveToPath, moveToRepo) => {
|
moveTreeNode = (nodePath, moveToPath, moveToRepo, nodeName) => {
|
||||||
if (repoID !== moveToRepo.repo_id) {
|
if (repoID !== moveToRepo.repo_id) {
|
||||||
let tree = treeHelper.deleteNodeByPath(this.state.treeData, nodePath);
|
let tree = treeHelper.deleteNodeByPath(this.state.treeData, nodePath);
|
||||||
this.setState({treeData: tree});
|
this.setState({treeData: tree});
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let tree = treeHelper.moveNodeByPath(this.state.treeData, nodePath, moveToPath);
|
let tree = treeHelper.moveNodeByPath(this.state.treeData, nodePath, moveToPath, nodeName);
|
||||||
this.setState({treeData: tree});
|
this.setState({treeData: tree});
|
||||||
}
|
}
|
||||||
|
|
||||||
copyTreeNode = (nodePath, copyToPath, destRepo) => {
|
copyTreeNode = (nodePath, copyToPath, destRepo, nodeName) => {
|
||||||
if (repoID !== destRepo.repo_id) {
|
if (repoID !== destRepo.repo_id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let tree = treeHelper.copyNodeByPath(this.state.treeData, nodePath, copyToPath);
|
let tree = treeHelper.copyNodeByPath(this.state.treeData, nodePath, copyToPath, nodeName);
|
||||||
this.setState({treeData: tree});
|
this.setState({treeData: tree});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user