mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-04 08:28:11 +00:00
Improve upload module 2 (#3984)
* optimized upload file * improve upload * revert code * optimized code * optimized code * optimized code * optimized code * update resumablejs version
This commit is contained in:
18
frontend/package-lock.json
generated
18
frontend/package-lock.json
generated
@@ -118,9 +118,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@seafile/resumablejs": {
|
"@seafile/resumablejs": {
|
||||||
"version": "1.1.9",
|
"version": "1.1.12",
|
||||||
"resolved": "https://registry.npmjs.org/@seafile/resumablejs/-/resumablejs-1.1.9.tgz",
|
"resolved": "https://registry.npmjs.org/@seafile/resumablejs/-/resumablejs-1.1.12.tgz",
|
||||||
"integrity": "sha512-YsAX+gnnf1ytv7asZgJP7T56DALQniKtRVtlz0f11PljLV19I1Av+Oz3QcYaRiKhCCB+EMnVKI9Yc14sYKp6lA=="
|
"integrity": "sha512-IK3POb3mdqFOJwQRerzpamQf5/3LdKFFgxe81M6X/ZQwjusINZKJwTZmqawKU1EnW3ghX7d3HW0nmcIrZayfLw=="
|
||||||
},
|
},
|
||||||
"@seafile/seafile-editor": {
|
"@seafile/seafile-editor": {
|
||||||
"version": "0.2.57",
|
"version": "0.2.57",
|
||||||
@@ -2216,7 +2216,7 @@
|
|||||||
},
|
},
|
||||||
"browserify-rsa": {
|
"browserify-rsa": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
|
"resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
|
||||||
"integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
|
"integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
@@ -2267,7 +2267,7 @@
|
|||||||
},
|
},
|
||||||
"buffer": {
|
"buffer": {
|
||||||
"version": "4.9.1",
|
"version": "4.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
|
"resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
|
||||||
"integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
|
"integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
@@ -4296,7 +4296,7 @@
|
|||||||
},
|
},
|
||||||
"events": {
|
"events": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
|
"resolved": "http://registry.npmjs.org/events/-/events-1.1.1.tgz",
|
||||||
"integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=",
|
"integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
@@ -12394,7 +12394,7 @@
|
|||||||
},
|
},
|
||||||
"yargs": {
|
"yargs": {
|
||||||
"version": "3.10.0",
|
"version": "3.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
|
"resolved": "http://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
|
||||||
"integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=",
|
"integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
@@ -12929,7 +12929,7 @@
|
|||||||
},
|
},
|
||||||
"load-json-file": {
|
"load-json-file": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
|
"resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
|
||||||
"integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
|
"integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
@@ -13219,7 +13219,7 @@
|
|||||||
},
|
},
|
||||||
"yargs": {
|
"yargs": {
|
||||||
"version": "6.6.0",
|
"version": "6.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz",
|
"resolved": "http://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz",
|
||||||
"integrity": "sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=",
|
"integrity": "sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@reach/router": "^1.2.0",
|
"@reach/router": "^1.2.0",
|
||||||
"@seafile/resumablejs": "^1.1.9",
|
"@seafile/resumablejs": "^1.1.12",
|
||||||
"@seafile/seafile-editor": "^0.2.57",
|
"@seafile/seafile-editor": "^0.2.57",
|
||||||
"MD5": "^1.3.0",
|
"MD5": "^1.3.0",
|
||||||
"autoprefixer": "7.1.6",
|
"autoprefixer": "7.1.6",
|
||||||
|
@@ -37,6 +37,7 @@ class FileUploader extends React.Component {
|
|||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
|
retryFileList: [],
|
||||||
uploadFileList: [],
|
uploadFileList: [],
|
||||||
totalProgress: 0,
|
totalProgress: 0,
|
||||||
isUploadProgressDialogShow: false,
|
isUploadProgressDialogShow: false,
|
||||||
@@ -140,6 +141,12 @@ class FileUploader extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onChunkingComplete = (resumableFile) => {
|
onChunkingComplete = (resumableFile) => {
|
||||||
|
|
||||||
|
let allFilesUploaded = this.state.allFilesUploaded;
|
||||||
|
if (allFilesUploaded === true) {
|
||||||
|
this.setState({allFilesUploaded: false});
|
||||||
|
}
|
||||||
|
|
||||||
//get parent_dir relative_path
|
//get parent_dir relative_path
|
||||||
let path = this.props.path === '/' ? '/' : this.props.path + '/';
|
let path = this.props.path === '/' ? '/' : this.props.path + '/';
|
||||||
let fileName = resumableFile.fileName;
|
let fileName = resumableFile.fileName;
|
||||||
@@ -210,42 +217,38 @@ class FileUploader extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setUploadFileList = () => {
|
setUploadFileList = () => {
|
||||||
let uploadFileList = this.resumable.files.map(resumableFile => {
|
let uploadFileList = this.resumable.files;
|
||||||
return this.buildCustomFileObj(resumableFile);
|
|
||||||
});
|
|
||||||
this.setState({
|
this.setState({
|
||||||
isUploadProgressDialogShow: true,
|
|
||||||
uploadFileList: uploadFileList,
|
uploadFileList: uploadFileList,
|
||||||
|
isUploadProgressDialogShow: true,
|
||||||
});
|
});
|
||||||
Utils.registerGlobalVariable('uploader', 'isUploadProgressDialogShow', true);
|
Utils.registerGlobalVariable('uploader', 'isUploadProgressDialogShow', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
buildCustomFileObj = (resumableFile) => {
|
onFileProgress = (resumableFile) => {
|
||||||
return {
|
let uploadBitrate = this.getBitrate();
|
||||||
uniqueIdentifier: resumableFile.uniqueIdentifier,
|
|
||||||
resumableFile: resumableFile,
|
|
||||||
progress: resumableFile.progress(),
|
|
||||||
isSaved: resumableFile.progress() === 1 ? true : false, // The 'isSaved' property is not saved in resumableFile.
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
onFileProgress = (file) => {
|
|
||||||
let uniqueIdentifier = file.uniqueIdentifier;
|
|
||||||
let uploadFileList = this.state.uploadFileList.map(item => {
|
let uploadFileList = this.state.uploadFileList.map(item => {
|
||||||
if (item.uniqueIdentifier === uniqueIdentifier) {
|
if (item.uniqueIdentifier === resumableFile.uniqueIdentifier) {
|
||||||
item.progress = Math.round(file.progress() * 100);
|
if (uploadBitrate) {
|
||||||
|
let lastSize = (item.size - (item.size * item.progress())) * 8;
|
||||||
|
let time = Math.ceil(lastSize / uploadBitrate);
|
||||||
|
item.remainingTime = time;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return item;
|
return item;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.setState({uploadFileList: uploadFileList});
|
this.setState({
|
||||||
|
uploadBitrate: uploadBitrate,
|
||||||
|
uploadFileList: uploadFileList
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getBitrate = () => {
|
getBitrate = () => {
|
||||||
let loaded = 0;
|
let loaded = 0;
|
||||||
let uploadBitrate = 0;
|
let uploadBitrate = 0;
|
||||||
let now = new Date().getTime();
|
let now = new Date().getTime();
|
||||||
|
|
||||||
this.resumable.files.forEach(file => {
|
this.resumable.files.forEach(file => {
|
||||||
loaded += file.progress() * file.size;
|
loaded += file.progress() * file.size;
|
||||||
});
|
});
|
||||||
@@ -255,23 +258,25 @@ class FileUploader extends React.Component {
|
|||||||
if (timeDiff < this.bitrateInterval) {
|
if (timeDiff < this.bitrateInterval) {
|
||||||
return this.state.uploadBitrate;
|
return this.state.uploadBitrate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 1. Cancel will produce loaded greater than this.loaded
|
||||||
|
// 2. reset can make this.loaded to be 0
|
||||||
|
if (loaded < this.loaded || this.loaded === 0) {
|
||||||
|
this.loaded = loaded; //
|
||||||
|
}
|
||||||
|
|
||||||
uploadBitrate = (loaded - this.loaded) * (1000 / timeDiff) * 8;
|
uploadBitrate = (loaded - this.loaded) * (1000 / timeDiff) * 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.timestamp = now;
|
this.timestamp = now;
|
||||||
this.loaded = loaded;
|
this.loaded = loaded;
|
||||||
|
|
||||||
uploadBitrate = Utils.formatBitRate(uploadBitrate);
|
|
||||||
return uploadBitrate;
|
return uploadBitrate;
|
||||||
}
|
}
|
||||||
|
|
||||||
onProgress = () => {
|
onProgress = () => {
|
||||||
let progress = Math.round(this.resumable.progress() * 100);
|
let progress = Math.round(this.resumable.progress() * 100);
|
||||||
let uploadBitrate = this.getBitrate();
|
this.setState({totalProgress: progress});
|
||||||
this.setState({
|
|
||||||
totalProgress: progress,
|
|
||||||
uploadBitrate: uploadBitrate
|
|
||||||
});
|
|
||||||
Utils.registerGlobalVariable('uploader', 'totalProgress', progress);
|
Utils.registerGlobalVariable('uploader', 'totalProgress', progress);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,10 +303,10 @@ class FileUploader extends React.Component {
|
|||||||
|
|
||||||
// update uploadFileList
|
// update uploadFileList
|
||||||
let uploadFileList = this.state.uploadFileList.map(item => {
|
let uploadFileList = this.state.uploadFileList.map(item => {
|
||||||
if (item.resumableFile.uniqueIdentifier === resumableFile.uniqueIdentifier) {
|
if (item.uniqueIdentifier === resumableFile.uniqueIdentifier) {
|
||||||
item.resumableFile.fileName = message.name;
|
item.newFileName = relative_path + message.name;
|
||||||
item.resumableFile.relativePath = relative_path + message.name;
|
|
||||||
item.isSaved = true;
|
item.isSaved = true;
|
||||||
|
item.remainingTime = 0;
|
||||||
}
|
}
|
||||||
return item;
|
return item;
|
||||||
});
|
});
|
||||||
@@ -321,8 +326,10 @@ class FileUploader extends React.Component {
|
|||||||
this.props.onFileUploadSuccess(dirent); // this contance: just one file
|
this.props.onFileUploadSuccess(dirent); // this contance: just one file
|
||||||
|
|
||||||
let uploadFileList = this.state.uploadFileList.map(item => {
|
let uploadFileList = this.state.uploadFileList.map(item => {
|
||||||
if (item.resumableFile.uniqueIdentifier === resumableFile.uniqueIdentifier) {
|
if (item.uniqueIdentifier === resumableFile.uniqueIdentifier) {
|
||||||
|
item.newFileName = fileName;
|
||||||
item.isSaved = true;
|
item.isSaved = true;
|
||||||
|
item.remainingTime = 0;
|
||||||
}
|
}
|
||||||
return item;
|
return item;
|
||||||
});
|
});
|
||||||
@@ -342,10 +349,10 @@ class FileUploader extends React.Component {
|
|||||||
this.props.onFileUploadSuccess(dirent); // this contance: no repetition file
|
this.props.onFileUploadSuccess(dirent); // this contance: no repetition file
|
||||||
|
|
||||||
let uploadFileList = this.state.uploadFileList.map(item => {
|
let uploadFileList = this.state.uploadFileList.map(item => {
|
||||||
if (item.resumableFile.uniqueIdentifier === resumableFile.uniqueIdentifier) {
|
if (item.uniqueIdentifier === resumableFile.uniqueIdentifier) {
|
||||||
item.resumableFile.fileName = message.name;
|
item.newFileName = message.name;
|
||||||
item.resumableFile.relativePath = message.name;
|
|
||||||
item.isSaved = true;
|
item.isSaved = true;
|
||||||
|
item.remainingTime = 0;
|
||||||
}
|
}
|
||||||
return item;
|
return item;
|
||||||
});
|
});
|
||||||
@@ -361,12 +368,18 @@ class FileUploader extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let uploadFileList = this.state.uploadFileList.map(item => {
|
let uploadFileList = this.state.uploadFileList.map(item => {
|
||||||
if (item.resumableFile.uniqueIdentifier === resumableFile.uniqueIdentifier) {
|
if (item.uniqueIdentifier === resumableFile.uniqueIdentifier) {
|
||||||
item.resumableFile.error = error;
|
this.state.retryFileList.push(item);
|
||||||
|
item.error = error;
|
||||||
}
|
}
|
||||||
return item;
|
return item;
|
||||||
});
|
});
|
||||||
this.setState({uploadFileList: uploadFileList});
|
|
||||||
|
this.loaded = 0; // reset loaded data;
|
||||||
|
this.setState({
|
||||||
|
retryFileList: this.state.retryFileList,
|
||||||
|
uploadFileList: uploadFileList
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -384,11 +397,11 @@ class FileUploader extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onFileRetry = () => {
|
onFileRetry = () => {
|
||||||
//todos, cancel upload file, uploded again;
|
// todo, cancel upload file, uploded again;
|
||||||
}
|
}
|
||||||
|
|
||||||
onBeforeCancel = () => {
|
onBeforeCancel = () => {
|
||||||
//todos, giving a pop message ?
|
// todo, giving a pop message ?
|
||||||
}
|
}
|
||||||
|
|
||||||
onCancel = () => {
|
onCancel = () => {
|
||||||
@@ -476,43 +489,96 @@ class FileUploader extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onCloseUploadDialog = () => {
|
onCloseUploadDialog = () => {
|
||||||
|
this.loaded = 0;
|
||||||
this.resumable.files = [];
|
this.resumable.files = [];
|
||||||
this.setState({isUploadProgressDialogShow: false, uploadFileList: []});
|
this.setState({isUploadProgressDialogShow: false, uploadFileList: []});
|
||||||
Utils.registerGlobalVariable('uploader', 'isUploadProgressDialogShow', false);
|
Utils.registerGlobalVariable('uploader', 'isUploadProgressDialogShow', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
onUploadCancel = (uploadingItem) => {
|
onUploadCancel = (uploadingItem) => {
|
||||||
|
|
||||||
let uploadFileList = this.state.uploadFileList.filter(item => {
|
let uploadFileList = this.state.uploadFileList.filter(item => {
|
||||||
if (item.uniqueIdentifier === uploadingItem.uniqueIdentifier) {
|
if (item.uniqueIdentifier === uploadingItem.uniqueIdentifier) {
|
||||||
uploadingItem.resumableFile.cancel();
|
item.cancel(); // execute cancel function will delete the file at the same time
|
||||||
this.resumable.removeFile(uploadingItem.resumableFile.file);
|
return false;
|
||||||
} else {
|
|
||||||
return item;
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
let newUploaderFileList = uploadFileList.map(item => {
|
|
||||||
let progress = Math.round(item.resumableFile.progress() * 100);
|
if (!this.resumable.isUploading()) {
|
||||||
item.progress = progress;
|
this.setState({
|
||||||
return item;
|
totalProgress: '100',
|
||||||
});
|
allFilesUploaded: true,
|
||||||
this.setState({uploadFileList: newUploaderFileList});
|
});
|
||||||
|
this.loaded = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({uploadFileList: uploadFileList});
|
||||||
}
|
}
|
||||||
|
|
||||||
onCancelAllUploading = () => {
|
onCancelAllUploading = () => {
|
||||||
let uploadFileList = this.state.uploadFileList.filter(item => {
|
let uploadFileList = this.state.uploadFileList.filter(item => {
|
||||||
let resumableFile = item.resumableFile;
|
if (Math.round(item.progress() !== 1)) {
|
||||||
if (Math.round(resumableFile.progress() !== 1)) {
|
item.cancel();
|
||||||
resumableFile.cancel();
|
return false;
|
||||||
this.resumable.removeFile(resumableFile.file);
|
|
||||||
} else {
|
|
||||||
return item;
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.loaded = 0;
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
allFilesUploaded: true,
|
||||||
|
totalProgress: '100',
|
||||||
|
uploadFileList: uploadFileList
|
||||||
});
|
});
|
||||||
this.setState({uploadFileList: uploadFileList});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onUploaderRetry = () => {
|
onUploadRetry = (resumableFile) => {
|
||||||
|
|
||||||
|
seafileAPI.getUploadLink(this.props.repoID, this.props.path).then(res => {
|
||||||
|
this.resumable.opts.target = res.data;
|
||||||
|
|
||||||
|
let retryFileList = this.state.retryFileList.filter(item => {
|
||||||
|
return item.uniqueIdentifier !== resumableFile.uniqueIdentifier;
|
||||||
|
});
|
||||||
|
let uploadFileList = this.state.uploadFileList.map(item => {
|
||||||
|
if (item.uniqueIdentifier === resumableFile.uniqueIdentifier) {
|
||||||
|
item.error = null;
|
||||||
|
item.retry();
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
retryFileList: retryFileList,
|
||||||
|
uploadFileList: uploadFileList
|
||||||
|
});
|
||||||
|
}).catch(error => {
|
||||||
|
let errMessage = Utils.getErrorMsg(error);
|
||||||
|
toaster.danger(errMessage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onUploadRetryAll = () => {
|
||||||
|
|
||||||
|
seafileAPI.getUploadLink(this.props.repoID, this.props.path).then(res => {
|
||||||
|
this.resumable.opts.target = res.data;
|
||||||
|
this.state.retryFileList.forEach(item => {
|
||||||
|
item.retry();
|
||||||
|
item.error = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
let uploadFileList = this.state.uploadFileList.slice(0);
|
||||||
|
this.setState({
|
||||||
|
retryFileList: [],
|
||||||
|
uploadFileList: uploadFileList
|
||||||
|
});
|
||||||
|
|
||||||
|
}).catch(error => {
|
||||||
|
let errMessage = Utils.getErrorMsg(error);
|
||||||
|
toaster.danger(errMessage);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
replaceRepetitionFile = () => {
|
replaceRepetitionFile = () => {
|
||||||
@@ -534,11 +600,10 @@ class FileUploader extends React.Component {
|
|||||||
|
|
||||||
uploadFile = () => {
|
uploadFile = () => {
|
||||||
let resumableFile = this.resumable.files[this.resumable.files.length - 1];
|
let resumableFile = this.resumable.files[this.resumable.files.length - 1];
|
||||||
let fileObject = this.buildCustomFileObj(resumableFile);
|
|
||||||
this.setState({
|
this.setState({
|
||||||
isUploadRemindDialogShow: false,
|
isUploadRemindDialogShow: false,
|
||||||
isUploadProgressDialogShow: true,
|
isUploadProgressDialogShow: true,
|
||||||
uploadFileList: [...this.state.uploadFileList, fileObject]
|
uploadFileList: [...this.state.uploadFileList, resumableFile]
|
||||||
}, () => {
|
}, () => {
|
||||||
this.resumable.upload();
|
this.resumable.upload();
|
||||||
});
|
});
|
||||||
@@ -568,6 +633,7 @@ class FileUploader extends React.Component {
|
|||||||
}
|
}
|
||||||
{this.state.isUploadProgressDialogShow &&
|
{this.state.isUploadProgressDialogShow &&
|
||||||
<UploadProgressDialog
|
<UploadProgressDialog
|
||||||
|
retryFileList={this.state.retryFileList}
|
||||||
uploadFileList={this.state.uploadFileList}
|
uploadFileList={this.state.uploadFileList}
|
||||||
totalProgress={this.state.totalProgress}
|
totalProgress={this.state.totalProgress}
|
||||||
uploadBitrate={this.state.uploadBitrate}
|
uploadBitrate={this.state.uploadBitrate}
|
||||||
@@ -575,6 +641,8 @@ class FileUploader extends React.Component {
|
|||||||
onCloseUploadDialog={this.onCloseUploadDialog}
|
onCloseUploadDialog={this.onCloseUploadDialog}
|
||||||
onCancelAllUploading={this.onCancelAllUploading}
|
onCancelAllUploading={this.onCancelAllUploading}
|
||||||
onUploadCancel={this.onUploadCancel}
|
onUploadCancel={this.onUploadCancel}
|
||||||
|
onUploadRetry={this.onUploadRetry}
|
||||||
|
onUploadRetryAll={this.onUploadRetryAll}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
@@ -1,17 +1,55 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { gettext } from '../../utils/constants';
|
import { gettext } from '../../utils/constants';
|
||||||
|
import { Utils } from '../../utils/utils';
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
item: PropTypes.object.isRequired,
|
resumableFile: PropTypes.object.isRequired,
|
||||||
onUploadCancel: PropTypes.func.isRequired,
|
onUploadCancel: PropTypes.func.isRequired,
|
||||||
|
onUploadRetry: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const UPLOAD_UPLOADING = 'uploading';
|
||||||
|
const UPLOAD_ERROR = 'error';
|
||||||
|
const UPLOAD_ISSAVING = 'isSaving';
|
||||||
|
const UPLOAD_UPLOADED = 'uploaded';
|
||||||
|
|
||||||
class UploadListItem extends React.Component {
|
class UploadListItem extends React.Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
uploadState: UPLOAD_UPLOADING
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
let { resumableFile } = nextProps;
|
||||||
|
let uploadState = UPLOAD_UPLOADING;
|
||||||
|
|
||||||
|
if (resumableFile.error) {
|
||||||
|
uploadState = UPLOAD_ERROR;
|
||||||
|
} else {
|
||||||
|
if (resumableFile.progress() === 1 && !resumableFile.isSaved) {
|
||||||
|
uploadState = UPLOAD_ISSAVING;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resumableFile.isSaved) {
|
||||||
|
uploadState = UPLOAD_UPLOADED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({uploadState: uploadState});
|
||||||
|
}
|
||||||
|
|
||||||
onUploadCancel = (e) => {
|
onUploadCancel = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.props.onUploadCancel(this.props.item);
|
this.props.onUploadCancel(this.props.resumableFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
onUploadRetry = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
this.props.onUploadRetry(this.props.resumableFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
formatFileSize = (size) => {
|
formatFileSize = (size) => {
|
||||||
@@ -31,38 +69,69 @@ class UploadListItem extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let { item } = this.props;
|
let { resumableFile } = this.props;
|
||||||
let progress = Math.round(item.resumableFile.progress() * 100);
|
let progress = Math.round(resumableFile.progress() * 100);
|
||||||
let error = item.resumableFile.error;
|
let error = resumableFile.error;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr className="file-upload-item">
|
<tr className="file-upload-item">
|
||||||
<td className="upload-name">
|
<td className="upload-name">
|
||||||
<div className="ellipsis">{item.resumableFile.relativePath}</div>
|
<div className="ellipsis">{resumableFile.newFileName}</div>
|
||||||
<div className="message err-message ml-0" dangerouslySetInnerHTML={{__html: error}}></div>
|
</td>
|
||||||
|
<td>
|
||||||
|
<span className="file-size">{this.formatFileSize(resumableFile.size)}</span>
|
||||||
</td>
|
</td>
|
||||||
<td className="upload-progress">
|
<td className="upload-progress">
|
||||||
<span className="file-size">{this.formatFileSize(item.resumableFile.size)}</span>
|
{this.state.uploadState === UPLOAD_UPLOADING &&
|
||||||
{!item.resumableFile.error && progress !== 100 &&
|
<Fragment>
|
||||||
<div className="progress">
|
{resumableFile.size >= (100 * 1000 * 1000) &&
|
||||||
<div className="progress-bar" role="progressbar" style={{width: `${progress}%`}} aria-valuenow={progress} aria-valuemin="0" aria-valuemax="100"></div>
|
<Fragment>
|
||||||
</div>
|
{resumableFile.isUploading() && (
|
||||||
|
<div className="progress-container">
|
||||||
|
<div className="progress">
|
||||||
|
<div className="progress-bar" role="progressbar" style={{width: `${progress}%`}} aria-valuenow={progress} aria-valuemin="0" aria-valuemax="100"></div>
|
||||||
|
</div>
|
||||||
|
{resumableFile.remainingTime === 0 && <div className="progress-text">{gettext('Preparing to upload...')}</div>}
|
||||||
|
{resumableFile.remainingTime !== 0 && <div className="progress-text">{gettext('Remaining')}{' '}{Utils.formatTime(resumableFile.remainingTime)}</div>}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{!resumableFile.isUploading() && (
|
||||||
|
<div className="progress-container d-flex align-items-center">
|
||||||
|
<div className="progress">
|
||||||
|
<div className="progress-bar" role="progressbar" style={{width: `${progress}%`}} aria-valuenow={progress} aria-valuemin="0" aria-valuemax="100"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Fragment>
|
||||||
|
}
|
||||||
|
{(resumableFile.size < (100 * 1000 * 1000)) &&
|
||||||
|
<div className="progress-container d-flex align-items-center">
|
||||||
|
<div className="progress">
|
||||||
|
<div className="progress-bar" role="progressbar" style={{width: `${progress}%`}} aria-valuenow={progress} aria-valuemin="0" aria-valuemax="100"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</Fragment>
|
||||||
}
|
}
|
||||||
|
{this.state.uploadState === UPLOAD_ERROR && (
|
||||||
|
<div className="message err-message ml-0" dangerouslySetInnerHTML={{__html: error}}></div>
|
||||||
|
)}
|
||||||
</td>
|
</td>
|
||||||
<td className="upload-operation">
|
<td className="upload-operation">
|
||||||
{!item.resumableFile.error && (
|
<Fragment>
|
||||||
<Fragment>
|
{this.state.uploadState === UPLOAD_UPLOADING && (
|
||||||
{(!item.isSaved && progress !== 100) && (
|
<a href="#" onClick={this.onUploadCancel}>{gettext('Cancel')}</a>
|
||||||
<a href="#" onClick={this.onUploadCancel}>{gettext('cancel')}</a>
|
)}
|
||||||
)}
|
{this.state.uploadState === UPLOAD_ERROR && (
|
||||||
{(!item.isSaved && progress === 100) && (
|
<a href="#" onClick={this.onUploadRetry}>{gettext('Retry')}</a>
|
||||||
<span className="saving">{gettext('saving...')}</span>
|
)}
|
||||||
)}
|
{this.state.uploadState === UPLOAD_ISSAVING && (
|
||||||
{item.isSaved && (
|
<span className="saving">{gettext('Saving...')}</span>
|
||||||
<span className="uploaded">{gettext('uploaded')}</span>
|
)}
|
||||||
)}
|
{this.state.uploadState === UPLOAD_UPLOADED && (
|
||||||
</Fragment>
|
<span className="uploaded">{gettext('Uploaded')}</span>
|
||||||
)}
|
)}
|
||||||
|
</Fragment>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
|
@@ -2,14 +2,18 @@ import React, { Fragment } from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { gettext } from '../../utils/constants';
|
import { gettext } from '../../utils/constants';
|
||||||
import UploadListItem from './upload-list-item';
|
import UploadListItem from './upload-list-item';
|
||||||
|
import { Utils } from '../../utils/utils';
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
uploadBitrate: PropTypes.string.isRequired,
|
uploadBitrate: PropTypes.string.isRequired,
|
||||||
totalProgress: PropTypes.number.isRequired,
|
totalProgress: PropTypes.number.isRequired,
|
||||||
|
retryFileList: PropTypes.array.isRequired,
|
||||||
uploadFileList: PropTypes.array.isRequired,
|
uploadFileList: PropTypes.array.isRequired,
|
||||||
onCloseUploadDialog: PropTypes.func.isRequired,
|
onCloseUploadDialog: PropTypes.func.isRequired,
|
||||||
onCancelAllUploading: PropTypes.func.isRequired,
|
onCancelAllUploading: PropTypes.func.isRequired,
|
||||||
onUploadCancel: PropTypes.func.isRequired,
|
onUploadCancel: PropTypes.func.isRequired,
|
||||||
|
onUploadRetry: PropTypes.func.isRequired,
|
||||||
|
onUploadRetryAll: PropTypes.func.isRequired,
|
||||||
allFilesUploaded: PropTypes.bool.isRequired,
|
allFilesUploaded: PropTypes.bool.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -37,8 +41,10 @@ class UploadProgressDialog extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
||||||
|
let uploadBitrate = Utils.formatBitRate(this.props.uploadBitrate)
|
||||||
let uploadedMessage = gettext('File Upload');
|
let uploadedMessage = gettext('File Upload');
|
||||||
let uploadingMessage = gettext('File Uploading...') + ' ' + this.props.totalProgress + '%' + ' (' + this.props.uploadBitrate + ')';
|
let uploadingMessage = gettext('File Uploading...') + ' ' + this.props.totalProgress + '%' + ' (' + uploadBitrate + ')';
|
||||||
|
|
||||||
let uploadingOptions = (<span className="sf2-icon-minus" onClick={this.onMinimizeUpload}></span>);
|
let uploadingOptions = (<span className="sf2-icon-minus" onClick={this.onMinimizeUpload}></span>);
|
||||||
|
|
||||||
@@ -49,7 +55,7 @@ class UploadProgressDialog extends React.Component {
|
|||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
|
|
||||||
let { totalProgress, allFilesUploaded } = this.props;
|
let { totalProgress, allFilesUploaded, retryFileList } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="uploader-list-view" style={{height: this.state.isMinimized ? '2.25rem' : '20rem'}}>
|
<div className="uploader-list-view" style={{height: this.state.isMinimized ? '2.25rem' : '20rem'}}>
|
||||||
@@ -65,19 +71,38 @@ class UploadProgressDialog extends React.Component {
|
|||||||
<table className="table-thead-hidden">
|
<table className="table-thead-hidden">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th width="45%">{gettext('name')}</th>
|
<th width="35%">{gettext('name')}</th>
|
||||||
<th width="40%">{gettext('progress')}</th>
|
<th width="15%">{gettext('size')}</th>
|
||||||
|
<th width="35%">{gettext('progress')}</th>
|
||||||
<th width="15%">{gettext('state')}</th>
|
<th width="15%">{gettext('state')}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{(!allFilesUploaded) &&
|
<tr>
|
||||||
<tr><td className="text-right" colSpan={3}><span className="cursor-pointer" onClick={this.onCancelAllUploading}>{gettext('Cancel All')}</span></td></tr>
|
<td className="text-right" colSpan={3}>
|
||||||
}
|
{retryFileList.length > 0 ?
|
||||||
|
<span className="cursor-pointer" onClick={this.props.onUploadRetryAll}>{gettext('Retry All')}</span>
|
||||||
|
:
|
||||||
|
<span className="cursor-pointer disabled-link">{gettext('Retry All')}</span>
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
<td className="text-right" colSpan={1}>
|
||||||
|
{!allFilesUploaded ?
|
||||||
|
<span className="cursor-pointer" onClick={this.onCancelAllUploading}>{gettext('Cancel All')}</span>
|
||||||
|
:
|
||||||
|
<span className="cursor-pointer disabled-link" >{gettext('Cancel All')}</span>
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
{
|
{
|
||||||
this.props.uploadFileList.map((item, index) => {
|
this.props.uploadFileList.map((resumableFile, index) => {
|
||||||
return (
|
return (
|
||||||
<UploadListItem key={index} item={item} onUploadCancel={this.props.onUploadCancel}/>
|
<UploadListItem
|
||||||
|
key={index}
|
||||||
|
resumableFile={resumableFile}
|
||||||
|
onUploadCancel={this.props.onUploadCancel}
|
||||||
|
onUploadRetry={this.props.onUploadRetry}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@@ -43,20 +43,41 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.uploader-list-content {
|
.uploader-list-content {
|
||||||
padding: 0.625rem 1rem 1.25rem;
|
padding: 0rem 1rem 1.25rem;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-upload-item .progress {
|
.file-upload-item {
|
||||||
|
height: 44px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-progress .progress-container {
|
||||||
|
height: 24px;
|
||||||
|
padding: 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-progress .progress {
|
||||||
|
height: 5px;
|
||||||
width: 80%;
|
width: 80%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-upload-item .progress .progress-bar {
|
.upload-progress .progress .progress-bar {
|
||||||
color: #e83;
|
color: #e83;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-upload-item .saving {
|
.upload-progress .progress-text {
|
||||||
|
margin-top: 2px;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 12px;
|
||||||
|
color: #666666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-operation .saving {
|
||||||
color: #ee8204;
|
color: #ee8204;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.disabled-link {
|
||||||
|
color: #999999;
|
||||||
}
|
}
|
@@ -989,11 +989,38 @@ export const Utils = {
|
|||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
|
||||||
registerGlobalVariable(namespace, key, value) {
|
registerGlobalVariable: function(namespace, key, value) {
|
||||||
if (!window[namespace]) {
|
if (!window[namespace]) {
|
||||||
window[namespace] = {};
|
window[namespace] = {};
|
||||||
}
|
}
|
||||||
window[namespace][key] = value;
|
window[namespace][key] = value;
|
||||||
|
},
|
||||||
|
|
||||||
|
formatTime: function(seconds) {
|
||||||
|
var ss = parseInt(seconds);
|
||||||
|
var mm = 0;
|
||||||
|
var hh = 0;
|
||||||
|
if (ss > 60) {
|
||||||
|
mm = parseInt(ss / 60);
|
||||||
|
ss = parseInt(ss % 60);
|
||||||
|
}
|
||||||
|
if (mm > 60) {
|
||||||
|
hh = parseInt(mm / 60);
|
||||||
|
mm = parseInt(mm % 60);
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = ('00' + parseInt(ss)).slice(-2);
|
||||||
|
if (mm > 0) {
|
||||||
|
result = ('00' + parseInt(mm)).slice(-2) + ':' + result;
|
||||||
|
} else {
|
||||||
|
result = '00:' + result;
|
||||||
|
}
|
||||||
|
if (hh > 0) {
|
||||||
|
result = ('00' + parseInt(hh)).slice(-2) + ':' + result;
|
||||||
|
} else {
|
||||||
|
result = '00:' + result;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user