1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-01 23:20:51 +00:00

improve upload ui (#2780)

This commit is contained in:
杨顺强
2019-01-08 11:26:20 +08:00
committed by Daniel Pan
parent db2d0e01e7
commit 93a2be4ada
5 changed files with 136 additions and 39 deletions

View File

@@ -4,6 +4,7 @@ import Resumablejs from '@seafile/resumablejs';
import MD5 from 'MD5'; import MD5 from 'MD5';
import { enableResumableFileUpload } from '../../utils/constants'; import { enableResumableFileUpload } from '../../utils/constants';
import { seafileAPI } from '../../utils/seafile-api'; import { seafileAPI } from '../../utils/seafile-api';
import { Utils } from '../../utils/utils';
import UploadProgressDialog from './upload-progress-dialog'; import UploadProgressDialog from './upload-progress-dialog';
import UploadRemindDialog from '../dialog/upload-remind-dialog'; import UploadRemindDialog from '../dialog/upload-remind-dialog';
import '../../css/file-uploader.css'; import '../../css/file-uploader.css';
@@ -39,9 +40,14 @@ class FileUploader extends React.Component {
isUploadProgressDialogShow: false, isUploadProgressDialogShow: false,
isUploadRemindDialogShow: false, isUploadRemindDialogShow: false,
currentResumableFile: null, currentResumableFile: null,
uploadBitrate: 0
}; };
this.notifiedFolders = []; this.notifiedFolders = [];
this.timestamp = null;
this.loaded = 0;
this.bitrateInterval = 500; // Interval in milliseconds to calculate the bitrate
} }
componentDidMount() { componentDidMount() {
@@ -95,20 +101,20 @@ class FileUploader extends React.Component {
} }
bindEventHandler = () => { bindEventHandler = () => {
this.resumable.on('chunkingComplete', this.onChunkingComplete); this.resumable.on('chunkingComplete', this.onChunkingComplete.bind(this));
this.resumable.on('fileAdded', this.onFileAdded); this.resumable.on('fileAdded', this.onFileAdded.bind(this));
this.resumable.on('filesAddedComplete', this.filesAddedComplete); this.resumable.on('filesAddedComplete', this.filesAddedComplete.bind(this));
this.resumable.on('fileProgress', this.onFileProgress); this.resumable.on('fileProgress', this.onFileProgress.bind(this));
this.resumable.on('fileSuccess', this.onFileUploadSuccess); this.resumable.on('fileSuccess', this.onFileUploadSuccess.bind(this));
this.resumable.on('progress', this.onProgress); this.resumable.on('progress', this.onProgress.bind(this));
this.resumable.on('complete', this.onComplete); this.resumable.on('complete', this.onComplete.bind(this));
this.resumable.on('pause', this.onPause); this.resumable.on('pause', this.onPause.bind(this));
this.resumable.on('fileRetry', this.onFileRetry); this.resumable.on('fileRetry', this.onFileRetry.bind(this));
this.resumable.on('fileError', this.onFileError); this.resumable.on('fileError', this.onFileError.bind(this));
this.resumable.on('error', this.onError); this.resumable.on('error', this.onError.bind(this));
this.resumable.on('beforeCancel', this.onBeforeCancel); this.resumable.on('beforeCancel', this.onBeforeCancel.bind(this));
this.resumable.on('cancel', this.onCancel); this.resumable.on('cancel', this.onCancel.bind(this));
this.resumable.on('dragstart', this.onDragStart); this.resumable.on('dragstart', this.onDragStart.bind(this));
} }
onChunkingComplete = (file) => { onChunkingComplete = (file) => {
@@ -164,11 +170,11 @@ class FileUploader extends React.Component {
}); });
} else { } else {
this.setUploadFileList(this.resumable.files); this.setUploadFileList(this.resumable.files);
resumableFile.upload(); this.resumable.upload();
} }
} else { } else {
this.setUploadFileList(this.resumable.files); this.setUploadFileList(this.resumable.files);
resumableFile.upload(); this.resumable.upload();
} }
} }
@@ -206,9 +212,37 @@ class FileUploader extends React.Component {
this.setState({uploadFileList: uploadFileList}); this.setState({uploadFileList: uploadFileList});
} }
getBitrate = () => {
let loaded = 0;
let uploadBitrate = 0;
let now = new Date().getTime();
this.resumable.files.forEach(file => {
loaded += file.progress() * file.size;
});
if (this.timestamp) {
let timeDiff = (now - this.timestamp);
if (timeDiff < this.bitrateInterval) {
return this.state.uploadBitrate;
}
uploadBitrate = (loaded - this.loaded) * (1000 / timeDiff) * 8;
}
this.timestamp = now;
this.loaded = loaded;
uploadBitrate = Utils.formatBitRate(uploadBitrate);
return uploadBitrate;
}
onProgress = () => { onProgress = () => {
let progress = Math.round(this.resumable.progress() * 100); let progress = Math.round(this.resumable.progress() * 100);
this.setState({totalProgress: progress}); let uploadBitrate = this.getBitrate();
this.setState({
totalProgress: progress,
uploadBitrate: uploadBitrate
});
} }
onFileUploadSuccess = (resumableFile, message) => { onFileUploadSuccess = (resumableFile, message) => {
@@ -370,17 +404,18 @@ class FileUploader extends React.Component {
}); });
} }
onMinimizeUploadDialog = () => {
this.setState({isUploadProgressDialogShow: false});
}
onCloseUploadDialog = () => { onCloseUploadDialog = () => {
this.setState({isUploadProgressDialogShow: false, uploadFileList: []}); this.setState({isUploadProgressDialogShow: false, uploadFileList: []});
} }
onUploadCancel = (resumableFile) => { onUploadCancel = (uploadingItem) => {
let uploadFileList = this.state.uploadFileList.filter(item => { let uploadFileList = this.state.uploadFileList.filter(item => {
return item.uniqueIdentifier !== resumableFile.uniqueIdentifier; if (item.uniqueIdentifier === uploadingItem.uniqueIdentifier) {
uploadingItem.resumableFile.cancel();
this.resumable.removeFile(uploadingItem.resumableFile.file);
} else {
return item;
}
}); });
let newUploaderFileList = uploadFileList.map(item => { let newUploaderFileList = uploadFileList.map(item => {
let progress = Math.round(item.resumableFile.progress() * 100); let progress = Math.round(item.resumableFile.progress() * 100);
@@ -390,6 +425,19 @@ class FileUploader extends React.Component {
this.setState({uploadFileList: newUploaderFileList}); this.setState({uploadFileList: newUploaderFileList});
} }
onCancelAllUploading = () => {
let uploadFileList = this.state.uploadFileList.filter(item => {
let resumableFile = item.resumableFile;
if (Math.round(resumableFile.progress() !== 1)) {
resumableFile.cancel();
this.resumable.removeFile(resumableFile.file);
} else {
return item;
}
});
this.setState({uploadFileList: uploadFileList});
}
onUploaderRetry = () => { onUploaderRetry = () => {
} }
@@ -447,9 +495,10 @@ class FileUploader extends React.Component {
<UploadProgressDialog <UploadProgressDialog
uploadFileList={this.state.uploadFileList} uploadFileList={this.state.uploadFileList}
totalProgress={this.state.totalProgress} totalProgress={this.state.totalProgress}
onMinimizeUploadDialog={this.onMinimizeUploadDialog}
onCloseUploadDialog={this.onCloseUploadDialog} onCloseUploadDialog={this.onCloseUploadDialog}
onCancelAllUploading={this.onCancelAllUploading}
onUploadCancel={this.onUploadCancel} onUploadCancel={this.onUploadCancel}
uploadBitrate={this.state.uploadBitrate}
/> />
} }
</Fragment> </Fragment>

View File

@@ -11,9 +11,7 @@ class UploadListItem extends React.Component {
onUploadCancel = (e) => { onUploadCancel = (e) => {
e.preventDefault(); e.preventDefault();
let item = this.props.item; this.props.onUploadCancel(this.props.item);
item.resumableFile.cancel();
this.props.onUploadCancel(item);
} }
formatFileSize = (size) => { formatFileSize = (size) => {
@@ -37,13 +35,16 @@ class UploadListItem extends React.Component {
let progress = Math.round(item.resumableFile.progress() * 100); let progress = Math.round(item.resumableFile.progress() * 100);
return ( return (
<tr className="file-upload-item"> <tr className="file-upload-item">
<td width="50%" className="upload-name ellipsis">{item.resumableFile.relativePath}</td> <td className="upload-name ellipsis">{item.resumableFile.relativePath}</td>
<td width="30%" className="upload-progress upload-size"> <td className="upload-progress">
{ <span className="file-size">{this.formatFileSize(item.resumableFile.size)}</span>
progress === 100 ? this.formatFileSize(item.resumableFile.size) : progress + '%' {progress !== 100 &&
<div className="progress">
<div className="progress-bar" role="progressbar" style={{width: `${progress}%`}} aria-valuenow={`${progress}`} aria-valuemin="0" aria-valuemax="100"></div>
</div>
} }
</td> </td>
<td width="20%" className="upload-operation"> <td className="upload-operation">
{ progress !== 100 ? { progress !== 100 ?
<a href="#" onClick={this.onUploadCancel}>{gettext(('cancel'))}</a> : <a href="#" onClick={this.onUploadCancel}>{gettext(('cancel'))}</a> :
<span>{gettext('uploaded')}</span> <span>{gettext('uploaded')}</span>

View File

@@ -4,18 +4,30 @@ import { gettext } from '../../utils/constants';
import UploadListItem from './upload-list-item'; import UploadListItem from './upload-list-item';
const propTypes = { const propTypes = {
uploadBitrate: PropTypes.number.isRequired,
totalProgress: PropTypes.number.isRequired, totalProgress: PropTypes.number.isRequired,
uploadFileList: PropTypes.array.isRequired, uploadFileList: PropTypes.array.isRequired,
onMinimizeUploadDialog: PropTypes.func.isRequired,
onCloseUploadDialog: PropTypes.func.isRequired, onCloseUploadDialog: PropTypes.func.isRequired,
onCancelAllUploading: PropTypes.func.isRequired,
onUploadCancel: PropTypes.func.isRequired, onUploadCancel: PropTypes.func.isRequired,
}; };
class UploadProgressDialog extends React.Component { class UploadProgressDialog extends React.Component {
constructor(props) {
super(props);
this.state = {
isMinimized: false
};
}
onCancelAllUploading = () => {
this.props.onCancelAllUploading();
}
onMinimizeUpload = (e) => { onMinimizeUpload = (e) => {
e.nativeEvent.stopImmediatePropagation(); e.nativeEvent.stopImmediatePropagation();
this.props.onMinimizeUploadDialog(); this.setState({isMinimized: !this.state.isMinimized});
} }
onCloseUpload = (e) => { onCloseUpload = (e) => {
@@ -25,7 +37,7 @@ class UploadProgressDialog extends React.Component {
render() { render() {
let uploadedMessage = gettext('File Upload'); let uploadedMessage = gettext('File Upload');
let uploadingMessage = gettext('File Uploading...') + this.props.totalProgress + '%'; let uploadingMessage = gettext('File Uploading...') + ' ' + this.props.totalProgress + '%' + ' (' + this.props.uploadBitrate + ')';
let uploadingOptions = (<span className="sf2-icon-minus" onClick={this.onMinimizeUpload}></span>); let uploadingOptions = (<span className="sf2-icon-minus" onClick={this.onMinimizeUpload}></span>);
@@ -39,7 +51,7 @@ class UploadProgressDialog extends React.Component {
let totalProgress = this.props.totalProgress; let totalProgress = this.props.totalProgress;
return ( return (
<div className="uploader-list-view"> <div className="uploader-list-view" style={{height: this.state.isMinimized ? '2.25rem' : '20rem'}}>
<div className="uploader-list-header"> <div className="uploader-list-header">
<div className="title"> <div className="title">
{totalProgress === 100 ? uploadedMessage : uploadingMessage} {totalProgress === 100 ? uploadedMessage : uploadingMessage}
@@ -49,8 +61,18 @@ class UploadProgressDialog extends React.Component {
</div> </div>
</div> </div>
<div className="uploader-list-content"> <div className="uploader-list-content">
<table> <table className="table-thead-hidden">
<thead>
<tr>
<th width="50%">{gettext('name')}</th>
<th width="40%">{gettext('progress')}</th>
<th width="10%">{gettext('state')}</th>
</tr>
</thead>
<tbody> <tbody>
{(this.props.totalProgress !== 100) &&
<tr><td className="text-right" colSpan={3}><span className="cursor-pointer" onClick={this.onCancelAllUploading}>{gettext('Cancel All')}</span></td></tr>
}
{ {
this.props.uploadFileList.map((item, index) => { this.props.uploadFileList.map((item, index) => {
return ( return (

View File

@@ -15,8 +15,7 @@
right: 1px; right: 1px;
bottom: 1px; bottom: 1px;
width: 35rem; width: 35rem;
min-height: 15rem; height: 20rem;
max-height: 20rem;
border: 1px solid #ddd; border: 1px solid #ddd;
border-radius: 3px; border-radius: 3px;
box-shadow: 0 0 6px #ddd; box-shadow: 0 0 6px #ddd;
@@ -47,3 +46,11 @@
background-color: #fff; background-color: #fff;
overflow: auto; overflow: auto;
} }
.file-upload-item .progress {
width: 80%;
}
.file-upload-item .progress .progress-bar {
color: #e83;
}

View File

@@ -363,6 +363,24 @@ export const Utils = {
} }
}, },
formatBitRate: function(bits) {
var Bs;
if (typeof bits !== 'number') {
return '';
}
Bs = bits / 8;
if (Bs >= 1000000000) {
return (Bs / 1000000000).toFixed(2) + ' GB/s';
}
if (Bs >= 1000000) {
return (Bs / 1000000).toFixed(2) + ' MB/s';
}
if (Bs >= 1000) {
return (Bs / 1000).toFixed(2) + ' kB/s';
}
return Bs.toFixed(2) + ' B/s';
},
isMarkdownFile: function(filePath) { isMarkdownFile: function(filePath) {
let index = filePath.lastIndexOf('.'); let index = filePath.lastIndexOf('.');
if (index === -1) { if (index === -1) {