mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-01 15:09:14 +00:00
Upload improve (#2753)
This commit is contained in:
@@ -12,10 +12,21 @@ const propTypes = {
|
|||||||
|
|
||||||
class UploadRemindDialog extends React.Component {
|
class UploadRemindDialog extends React.Component {
|
||||||
|
|
||||||
toggle = () => {
|
toggle = (e) => {
|
||||||
|
e.nativeEvent.stopImmediatePropagation();
|
||||||
this.props.cancelFileUpload();
|
this.props.cancelFileUpload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
replaceRepetitionFile = (e) => {
|
||||||
|
e.nativeEvent.stopImmediatePropagation();
|
||||||
|
this.props.replaceRepetitionFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
uploadFile = (e) => {
|
||||||
|
e.nativeEvent.stopImmediatePropagation();
|
||||||
|
this.props.uploadFile();
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
||||||
let title = gettext('Replace file {filename}?');
|
let title = gettext('Replace file {filename}?');
|
||||||
@@ -28,8 +39,8 @@ class UploadRemindDialog extends React.Component {
|
|||||||
<p>{gettext('Replacing it will overwrite its content.')}</p>
|
<p>{gettext('Replacing it will overwrite its content.')}</p>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<Button outline color="primary" onClick={this.props.replaceRepetitionFile}>{gettext('Replace')}</Button>
|
<Button outline color="primary" onClick={this.replaceRepetitionFile}>{gettext('Replace')}</Button>
|
||||||
<Button outline color="info" onClick={this.props.uploadFile}>{gettext("Don't replace")}</Button>
|
<Button outline color="info" onClick={this.uploadFile}>{gettext("Don't replace")}</Button>
|
||||||
<Button outline color="danger" onClick={this.toggle}>{gettext('Cancel')}</Button>
|
<Button outline color="danger" onClick={this.toggle}>{gettext('Cancel')}</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import moment from 'moment';
|
||||||
import { siteRoot } from '../../utils/constants';
|
import { siteRoot } from '../../utils/constants';
|
||||||
import { seafileAPI } from '../../utils/seafile-api';
|
import { seafileAPI } from '../../utils/seafile-api';
|
||||||
import { Utils } from '../../utils/utils';
|
import { Utils } from '../../utils/utils';
|
||||||
@@ -421,8 +422,29 @@ class DirView extends React.Component {
|
|||||||
this.setState({direntList: newDirentList});
|
this.setState({direntList: newDirentList});
|
||||||
}
|
}
|
||||||
|
|
||||||
onFileUploadSuccess = () => {
|
onFileUploadSuccess = (direntObject) => {
|
||||||
// todo update upload file to direntList
|
let isExist = this.state.direntList.some(item => {
|
||||||
|
return item.name === direntObject.name && item.type === direntObject.type;
|
||||||
|
});
|
||||||
|
if (isExist) {
|
||||||
|
let direntList = this.state.direntList;
|
||||||
|
for (let i = 0; i < direntList.length; i++) {
|
||||||
|
let dirent = direntList[i];
|
||||||
|
if (dirent.name === direntObject.name && dirent.type === direntObject.type) {
|
||||||
|
let mtime = moment.unix(direntObject.mtime).fromNow();
|
||||||
|
this.updateDirent(dirent, 'mtime', mtime); // todo file size is need update too, api is not return;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
direntObject.permission = 'rw';
|
||||||
|
let dirent = new Dirent(direntObject);
|
||||||
|
if (direntObject.type === 'dir') {
|
||||||
|
this.setState({direntList: [dirent, ...this.state.direntList]});
|
||||||
|
} else {
|
||||||
|
this.setState({direntList: [...this.state.direntList, dirent]});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onSearchedClick = (selectedItem) => {
|
onSearchedClick = (selectedItem) => {
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import Resumablejs from '@seafile/resumablejs';
|
import Resumablejs from '@seafile/resumablejs';
|
||||||
import MD5 from 'MD5';
|
import MD5 from 'MD5';
|
||||||
@@ -40,6 +40,8 @@ class FileUploader extends React.Component {
|
|||||||
isUploadRemindDialogShow: false,
|
isUploadRemindDialogShow: false,
|
||||||
currentResumableFile: null,
|
currentResumableFile: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.uploadedFolders = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
@@ -174,14 +176,13 @@ class FileUploader extends React.Component {
|
|||||||
// single file uploading can check repetition, because custom dialog conn't prevent program execution;
|
// single file uploading can check repetition, because custom dialog conn't prevent program execution;
|
||||||
}
|
}
|
||||||
|
|
||||||
setUploadFileList = (files) => {
|
setUploadFileList = () => {
|
||||||
let uploadFileList = files.map(resumableFile => {
|
let uploadFileList = this.resumable.files.map(resumableFile => {
|
||||||
return this.buildCustomFileObj(resumableFile);
|
return this.buildCustomFileObj(resumableFile);
|
||||||
});
|
});
|
||||||
this.setState({
|
this.setState({
|
||||||
isUploadRemindDialogShow: false,
|
|
||||||
isUploadProgressDialogShow: true,
|
isUploadProgressDialogShow: true,
|
||||||
uploadFileList: uploadFileList
|
uploadFileList: uploadFileList,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,27 +205,90 @@ class FileUploader extends React.Component {
|
|||||||
|
|
||||||
this.setState({uploadFileList: uploadFileList});
|
this.setState({uploadFileList: uploadFileList});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onProgress = () => {
|
||||||
|
let progress = Math.round(this.resumable.progress() * 100);
|
||||||
|
this.setState({totalProgress: progress});
|
||||||
|
}
|
||||||
|
|
||||||
onFileUploadSuccess = (file) => {
|
onFileUploadSuccess = (resumableFile, message) => {
|
||||||
// todos, update uploadList or updateList;
|
let formData = resumableFile.formData;
|
||||||
|
let currentTime = new Date().getTime()/1000;
|
||||||
|
message = formData.replace ? message : JSON.parse(message)[0];
|
||||||
|
if (formData.relative_path) { // upload folder
|
||||||
|
let relative_path = formData.relative_path;
|
||||||
|
let dir_name = relative_path.slice(0, relative_path.indexOf('/'));
|
||||||
|
let dirent = {
|
||||||
|
id: message.id,
|
||||||
|
name: dir_name,
|
||||||
|
type: 'dir',
|
||||||
|
mtime: currentTime,
|
||||||
|
};
|
||||||
|
|
||||||
|
// update folders cache
|
||||||
|
let isExist = this.uploadedFolders.some(item => {return item.name === dirent.name;});
|
||||||
|
if (!isExist) {
|
||||||
|
this.uploadedFolders.push(dirent);
|
||||||
|
this.props.onFileUploadSuccess(dirent);
|
||||||
|
}
|
||||||
|
|
||||||
|
// update uploadFileList
|
||||||
|
let uploadFileList = this.state.uploadFileList.map(item => {
|
||||||
|
if (item.resumableFile.uniqueIdentifier === resumableFile.uniqueIdentifier) {
|
||||||
|
item.resumableFile.fileName = message.name;
|
||||||
|
item.resumableFile.relativePath = relative_path + message.name;
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
this.setState({uploadFileList: uploadFileList});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formData.replace) { // upload file -- replace exist file
|
||||||
|
let fileName = resumableFile.fileName;
|
||||||
|
let dirent = {
|
||||||
|
id: message,
|
||||||
|
name: fileName,
|
||||||
|
type: 'file',
|
||||||
|
mtime: currentTime
|
||||||
|
};
|
||||||
|
this.props.onFileUploadSuccess(dirent); // this contance: just one file
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// upload file -- add files
|
||||||
|
let dirent = {
|
||||||
|
id: message.id,
|
||||||
|
type: 'file',
|
||||||
|
name: message.name,
|
||||||
|
size: message.size,
|
||||||
|
mtime: currentTime,
|
||||||
|
};
|
||||||
|
this.props.onFileUploadSuccess(dirent); // this contance: no repetition file
|
||||||
|
|
||||||
|
let uploadFileList = this.state.uploadFileList.map(item => {
|
||||||
|
if (item.resumableFile.uniqueIdentifier === resumableFile.uniqueIdentifier) {
|
||||||
|
item.resumableFile.fileName = message.name;
|
||||||
|
item.resumableFile.relativePath = message.name;
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
this.setState({uploadFileList: uploadFileList});
|
||||||
}
|
}
|
||||||
|
|
||||||
onFileError = (file) => {
|
onFileError = (file) => {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onProgress = () => {
|
|
||||||
let progress = Math.round(this.resumable.progress() * 100);
|
|
||||||
this.setState({totalProgress: progress});
|
|
||||||
}
|
|
||||||
|
|
||||||
onComplete = () => {
|
onComplete = () => {
|
||||||
|
this.uploadedFolders = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
onPause = () => [
|
onPause = () => {
|
||||||
|
|
||||||
]
|
}
|
||||||
|
|
||||||
onError = () => {
|
onError = () => {
|
||||||
|
|
||||||
@@ -243,6 +307,9 @@ class FileUploader extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setHeaders = (resumableFile, resumable) => {
|
setHeaders = (resumableFile, resumable) => {
|
||||||
|
if (resumableFile.formData.replace) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
let offset = resumable.offset;
|
let offset = resumable.offset;
|
||||||
let chunkSize = resumable.getOpt('chunkSize');
|
let chunkSize = resumable.getOpt('chunkSize');
|
||||||
let fileSize = resumableFile.size;
|
let fileSize = resumableFile.size;
|
||||||
@@ -328,21 +395,29 @@ class FileUploader extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
replaceRepetitionFile = () => {
|
replaceRepetitionFile = () => {
|
||||||
let resumableFile = this.resumable.files[this.resumable.files.length - 1];
|
let { repoID, path } = this.props;
|
||||||
resumableFile.formData['replace'] = 1;
|
seafileAPI.getUpdateLink(repoID, path).then(res => {
|
||||||
|
this.resumable.opts.target = res.data;
|
||||||
|
|
||||||
// this.setState({isUploadRemindDialogShow: false});
|
let resumableFile = this.resumable.files[this.resumable.files.length - 1];
|
||||||
|
resumableFile.formData['replace'] = 1;
|
||||||
this.setUploadFileList(this.resumable.files);
|
resumableFile.formData['target_file'] = resumableFile.formData.parent_dir + resumableFile.fileName;
|
||||||
|
this.setState({isUploadRemindDialogShow: false});
|
||||||
this.resumable.upload();
|
this.setUploadFileList(this.resumable.files);
|
||||||
|
this.resumable.upload();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
uploadFile = () => {
|
uploadFile = () => {
|
||||||
// this.setState({isUploadRemindDialogShow: false});
|
let resumableFile = this.resumable.files[this.resumable.files.length - 1];
|
||||||
|
let fileObject = this.buildCustomFileObj(resumableFile);
|
||||||
this.setUploadFileList(this.resumable.files);
|
this.setState({
|
||||||
this.resumable.upload();
|
isUploadRemindDialogShow: false,
|
||||||
|
isUploadProgressDialogShow: true,
|
||||||
|
uploadFileList: [...this.state.uploadFileList, fileObject]
|
||||||
|
}, () => {
|
||||||
|
this.resumable.upload();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelFileUpload = () => {
|
cancelFileUpload = () => {
|
||||||
@@ -352,30 +427,32 @@ class FileUploader extends React.Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className="file-uploader-container">
|
<Fragment>
|
||||||
<div className="file-uploader">
|
<div className="file-uploader-container">
|
||||||
<input className="upload-input" type="file" ref={node => this.uploadInput = node} onClick={this.onClick}/>
|
<div className="file-uploader">
|
||||||
|
<input className="upload-input" type="file" ref={node => this.uploadInput = node} onClick={this.onClick}/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{
|
{
|
||||||
this.state.isUploadProgressDialogShow &&
|
this.state.isUploadRemindDialogShow &&
|
||||||
<UploadProgressDialog
|
<UploadRemindDialog
|
||||||
uploadFileList={this.state.uploadFileList}
|
currentResumableFile={this.state.currentResumableFile}
|
||||||
totalProgress={this.state.totalProgress}
|
replaceRepetitionFile={this.replaceRepetitionFile}
|
||||||
onMinimizeUploadDialog={this.onMinimizeUploadDialog}
|
uploadFile={this.uploadFile}
|
||||||
onCloseUploadDialog={this.onCloseUploadDialog}
|
cancelFileUpload={this.cancelFileUpload}
|
||||||
onUploadCancel={this.onUploadCancel}
|
/>
|
||||||
/>
|
}
|
||||||
}
|
{
|
||||||
{
|
this.state.isUploadProgressDialogShow &&
|
||||||
this.state.isUploadRemindDialogShow &&
|
<UploadProgressDialog
|
||||||
<UploadRemindDialog
|
uploadFileList={this.state.uploadFileList}
|
||||||
currentResumableFile={this.state.currentResumableFile}
|
totalProgress={this.state.totalProgress}
|
||||||
replaceRepetitionFile={this.replaceRepetitionFile}
|
onMinimizeUploadDialog={this.onMinimizeUploadDialog}
|
||||||
uploadFile={this.uploadFile}
|
onCloseUploadDialog={this.onCloseUploadDialog}
|
||||||
cancelFileUpload={this.cancelFileUpload}
|
onUploadCancel={this.onUploadCancel}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</div>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -13,8 +13,8 @@ class Dirent {
|
|||||||
this.isSelected = false; // is check or not
|
this.isSelected = false; // is check or not
|
||||||
if (json.type === 'file') {
|
if (json.type === 'file') {
|
||||||
this.size = Utils.bytesToSize(json.size);
|
this.size = Utils.bytesToSize(json.size);
|
||||||
this.starred = json.starred;
|
this.starred = json.starred || false;
|
||||||
this.is_locked = json.is_locked;
|
this.is_locked = json.is_locked || false;
|
||||||
this.lock_time = moment.unix(json.lock_time).fromNow();
|
this.lock_time = moment.unix(json.lock_time).fromNow();
|
||||||
this.lock_owner= json.lock_owner;
|
this.lock_owner= json.lock_owner;
|
||||||
this.locked_by_me = json.locked_by_me;
|
this.locked_by_me = json.locked_by_me;
|
||||||
|
@@ -53,6 +53,7 @@ const propTypes = {
|
|||||||
onItemsCopy: PropTypes.func.isRequired,
|
onItemsCopy: PropTypes.func.isRequired,
|
||||||
onItemsDelete: PropTypes.func.isRequired,
|
onItemsDelete: PropTypes.func.isRequired,
|
||||||
onLinkClick: PropTypes.func.isRequired,
|
onLinkClick: PropTypes.func.isRequired,
|
||||||
|
onFileUploadSuccess: PropTypes.func.isRequired,
|
||||||
isDraft: PropTypes.bool,
|
isDraft: PropTypes.bool,
|
||||||
hasDraft: PropTypes.bool,
|
hasDraft: PropTypes.bool,
|
||||||
reviewStatus: PropTypes.any,
|
reviewStatus: PropTypes.any,
|
||||||
@@ -146,8 +147,8 @@ class MainPanel extends Component {
|
|||||||
this.uploader.onFolderUpload();
|
this.uploader.onFolderUpload();
|
||||||
}
|
}
|
||||||
|
|
||||||
onFileUploadSuccess = (file) => {
|
onFileUploadSuccess = (direntObject) => {
|
||||||
// todo
|
this.props.onFileUploadSuccess(direntObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePageScroll = () => {
|
handlePageScroll = () => {
|
||||||
@@ -291,8 +292,8 @@ class MainPanel extends Component {
|
|||||||
dragAndDrop={true}
|
dragAndDrop={true}
|
||||||
path={this.props.path}
|
path={this.props.path}
|
||||||
repoID={repoID}
|
repoID={repoID}
|
||||||
onFileUploadSuccess={this.onFileUploadSuccess}
|
|
||||||
direntList={this.props.direntList}
|
direntList={this.props.direntList}
|
||||||
|
onFileUploadSuccess={this.onFileUploadSuccess}
|
||||||
/>
|
/>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
}
|
}
|
||||||
|
@@ -556,6 +556,32 @@ class Wiki extends Component {
|
|||||||
this.setState({direntList: direntList});
|
this.setState({direntList: direntList});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onFileUploadSuccess = (direntObject) => {
|
||||||
|
let isExist = this.state.direntList.some(item => {
|
||||||
|
return item.name === direntObject.name && item.type === direntObject.type;
|
||||||
|
});
|
||||||
|
if (isExist) {
|
||||||
|
let direntList = this.state.direntList;
|
||||||
|
for (let i = 0; i < direntList.length; i++) {
|
||||||
|
let dirent = direntList[i];
|
||||||
|
if (dirent.name === direntObject.name && dirent.type === direntObject.type) {
|
||||||
|
let mtime = moment.unix(direntObject.mtime).fromNow();
|
||||||
|
this.updateDirent(dirent, 'mtime', mtime); // todo file size is need update too, api is not return;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
direntObject.permission = 'rw';
|
||||||
|
let dirent = new Dirent(direntObject);
|
||||||
|
this.addNodeToTree(dirent.name, this.state.path, dirent.type);
|
||||||
|
if (direntObject.type === 'dir') {
|
||||||
|
this.setState({direntList: [dirent, ...this.state.direntList]});
|
||||||
|
} else {
|
||||||
|
this.setState({direntList: [...this.state.direntList, dirent]});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onMoveItems = (destRepo, destDirentPath) => {
|
onMoveItems = (destRepo, destDirentPath) => {
|
||||||
let direntPaths = this.getSelectedDirentPaths();
|
let direntPaths = this.getSelectedDirentPaths();
|
||||||
let dirNames = this.getSelectedDirentNames();
|
let dirNames = this.getSelectedDirentNames();
|
||||||
@@ -937,6 +963,7 @@ class Wiki extends Component {
|
|||||||
onItemsMove={this.onMoveItems}
|
onItemsMove={this.onMoveItems}
|
||||||
onItemsCopy={this.onCopyItems}
|
onItemsCopy={this.onCopyItems}
|
||||||
onItemsDelete={this.onDeleteItems}
|
onItemsDelete={this.onDeleteItems}
|
||||||
|
onFileUploadSuccess={this.onFileUploadSuccess}
|
||||||
hash={this.hash}
|
hash={this.hash}
|
||||||
isDraft={this.state.isDraft}
|
isDraft={this.state.isDraft}
|
||||||
hasDraft={this.state.hasDraft}
|
hasDraft={this.state.hasDraft}
|
||||||
|
Reference in New Issue
Block a user