mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-01 15:09:14 +00:00
[shared dir view] redesigned the 'file upload' dialog (#6010)
This commit is contained in:
@@ -16,6 +16,7 @@ const propTypes = {
|
|||||||
onUploadCancel: PropTypes.func.isRequired,
|
onUploadCancel: PropTypes.func.isRequired,
|
||||||
onUploadRetry: PropTypes.func.isRequired,
|
onUploadRetry: PropTypes.func.isRequired,
|
||||||
onUploadRetryAll: PropTypes.func.isRequired,
|
onUploadRetryAll: PropTypes.func.isRequired,
|
||||||
|
isUploading : PropTypes.bool.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
class UploadProgressDialog extends React.Component {
|
class UploadProgressDialog extends React.Component {
|
||||||
|
@@ -44,7 +44,6 @@ class FileUploader extends React.Component {
|
|||||||
isUploadRemindDialogShow: false,
|
isUploadRemindDialogShow: false,
|
||||||
currentResumableFile: null,
|
currentResumableFile: null,
|
||||||
uploadBitrate: 0,
|
uploadBitrate: 0,
|
||||||
allFilesUploaded: false,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.uploadInput = React.createRef();
|
this.uploadInput = React.createRef();
|
||||||
@@ -157,11 +156,6 @@ 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;
|
||||||
@@ -436,7 +430,6 @@ class FileUploader extends React.Component {
|
|||||||
this.notifiedFolders = [];
|
this.notifiedFolders = [];
|
||||||
// reset upload link loaded
|
// reset upload link loaded
|
||||||
this.isUploadLinkLoaded = false;
|
this.isUploadLinkLoaded = false;
|
||||||
this.setState({allFilesUploaded: true});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
onPause = () => {
|
onPause = () => {
|
||||||
@@ -533,8 +526,7 @@ class FileUploader extends React.Component {
|
|||||||
|
|
||||||
if (!this.resumable.isUploading()) {
|
if (!this.resumable.isUploading()) {
|
||||||
this.setState({
|
this.setState({
|
||||||
totalProgress: '100',
|
totalProgress: 100
|
||||||
allFilesUploaded: true,
|
|
||||||
});
|
});
|
||||||
this.loaded = 0;
|
this.loaded = 0;
|
||||||
}
|
}
|
||||||
@@ -554,8 +546,7 @@ class FileUploader extends React.Component {
|
|||||||
this.loaded = 0;
|
this.loaded = 0;
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
allFilesUploaded: true,
|
totalProgress: 100,
|
||||||
totalProgress: '100',
|
|
||||||
uploadFileList: uploadFileList
|
uploadFileList: uploadFileList
|
||||||
});
|
});
|
||||||
// reset upload link loaded
|
// reset upload link loaded
|
||||||
@@ -683,12 +674,12 @@ class FileUploader extends React.Component {
|
|||||||
forbidUploadFileList={this.state.forbidUploadFileList}
|
forbidUploadFileList={this.state.forbidUploadFileList}
|
||||||
totalProgress={this.state.totalProgress}
|
totalProgress={this.state.totalProgress}
|
||||||
uploadBitrate={this.state.uploadBitrate}
|
uploadBitrate={this.state.uploadBitrate}
|
||||||
allFilesUploaded={this.state.allFilesUploaded}
|
|
||||||
onCloseUploadDialog={this.onCloseUploadDialog}
|
onCloseUploadDialog={this.onCloseUploadDialog}
|
||||||
onCancelAllUploading={this.onCancelAllUploading}
|
onCancelAllUploading={this.onCancelAllUploading}
|
||||||
onUploadCancel={this.onUploadCancel}
|
onUploadCancel={this.onUploadCancel}
|
||||||
onUploadRetry={this.onUploadRetry}
|
onUploadRetry={this.onUploadRetry}
|
||||||
onUploadRetryAll={this.onUploadRetryAll}
|
onUploadRetryAll={this.onUploadRetryAll}
|
||||||
|
isUploading={this.resumable.isUploading()}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { gettext, maxUploadFileSize } from '../../utils/constants';
|
import { Utils } from '../../utils/utils';
|
||||||
|
import { gettext } from '../../utils/constants';
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
file: PropTypes.object,
|
file: PropTypes.object,
|
||||||
@@ -9,15 +10,20 @@ const propTypes = {
|
|||||||
class ForbidUploadListItem extends React.Component {
|
class ForbidUploadListItem extends React.Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let { file } = this.props;
|
const { file } = this.props;
|
||||||
let msg = gettext('Please upload files less than {placeholder}M').replace('{placeholder}', maxUploadFileSize);
|
|
||||||
return (
|
return (
|
||||||
<tr className="file-upload-item">
|
<tr className="file-upload-item">
|
||||||
<td className="upload-name">
|
<td className="upload-name">
|
||||||
<div className="ellipsis" title={file.name}>{file.name}</div>
|
<div className="ellipsis" title={file.name}>{file.name}</div>
|
||||||
</td>
|
</td>
|
||||||
|
<td>{Utils.bytesToSize(file.size)}</td>
|
||||||
<td colSpan={3} className="error">{msg}</td>
|
<td>
|
||||||
|
<div className="d-flex align-items-center">
|
||||||
|
<span className="upload-failure-icon fas fa-exclamation mr-2"></span>
|
||||||
|
<span className="upload-failure-msg">{gettext('File too large')}</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -73,18 +73,15 @@ class UploadListItem extends React.Component {
|
|||||||
let progress = Math.round(resumableFile.progress() * 100);
|
let progress = Math.round(resumableFile.progress() * 100);
|
||||||
let error = resumableFile.error;
|
let error = resumableFile.error;
|
||||||
|
|
||||||
const fileName = resumableFile.newFileName;
|
|
||||||
const size = this.formatFileSize(resumableFile.size);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr className="file-upload-item">
|
<tr className="file-upload-item">
|
||||||
<td className="upload-name">
|
<td className="upload-name">
|
||||||
<div className="ellipsis" title={fileName}>{fileName}</div>
|
<div className="ellipsis">{resumableFile.newFileName}</div>
|
||||||
</td>
|
</td>
|
||||||
<td className="ellipsis">
|
<td>
|
||||||
<span className="file-size" title={size}>{size}</span>
|
<span className="file-size">{this.formatFileSize(resumableFile.size)}</span>
|
||||||
</td>
|
</td>
|
||||||
<td className="upload-progress ellipsis">
|
<td className="upload-progress">
|
||||||
{(this.state.uploadState === UPLOAD_UPLOADING || this.state.uploadState === UPLOAD_ISSAVING) &&
|
{(this.state.uploadState === UPLOAD_UPLOADING || this.state.uploadState === UPLOAD_ISSAVING) &&
|
||||||
<Fragment>
|
<Fragment>
|
||||||
{resumableFile.size >= (100 * 1000 * 1000) &&
|
{resumableFile.size >= (100 * 1000 * 1000) &&
|
||||||
@@ -96,7 +93,7 @@ class UploadListItem extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
{(resumableFile.remainingTime === -1) && <div className="progress-text">{gettext('Preparing to upload...')}</div>}
|
{(resumableFile.remainingTime === -1) && <div className="progress-text">{gettext('Preparing to upload...')}</div>}
|
||||||
{(resumableFile.remainingTime > 0) && <div className="progress-text">{gettext('Remaining')}{' '}{Utils.formatTime(resumableFile.remainingTime)}</div>}
|
{(resumableFile.remainingTime > 0) && <div className="progress-text">{gettext('Remaining')}{' '}{Utils.formatTime(resumableFile.remainingTime)}</div>}
|
||||||
{(resumableFile.remainingTime === 0) && <div className="progress-text">{gettext('Indexing...')}</div>}
|
{(resumableFile.remainingTime === 0) && <div className="progress-text">{gettext('Saving...')}</div>}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{!resumableFile.isUploading() && (
|
{!resumableFile.isUploading() && (
|
||||||
@@ -108,32 +105,46 @@ class UploadListItem extends React.Component {
|
|||||||
)}
|
)}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
}
|
}
|
||||||
{(resumableFile.size < (100 * 1000 * 1000)) &&
|
{(resumableFile.size < (100 * 1000 * 1000)) && (
|
||||||
<div className="progress-container d-flex align-items-center">
|
<>
|
||||||
<div className="progress">
|
<div className="progress-container d-flex align-items-center">
|
||||||
<div className="progress-bar" role="progressbar" style={{width: `${progress}%`}} aria-valuenow={progress} aria-valuemin="0" aria-valuemax="100"></div>
|
<div className="progress">
|
||||||
|
<div className="progress-bar" role="progressbar" style={{width: `${progress}%`}} aria-valuenow={progress} aria-valuemin="0" aria-valuemax="100"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{this.state.uploadState === UPLOAD_UPLOADING && (
|
||||||
}
|
<>
|
||||||
|
{progress == 0 && <p className="progress-text mb-0">{gettext('Waiting...')}</p>}
|
||||||
|
{progress > 0 && <p className="progress-text mb-0">{`${gettext('Uploading...')} ${progress}%`}</p>}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{this.state.uploadState === UPLOAD_ISSAVING && (
|
||||||
|
<p className="progress-text mb-0">{gettext('Saving...')}</p>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
}
|
}
|
||||||
|
{this.state.uploadState === UPLOAD_UPLOADED && (
|
||||||
|
<div className="d-flex align-items-center">
|
||||||
|
<span className="upload-success-icon sf2-icon-tick mr-2"></span>
|
||||||
|
<span className="upload-success-msg">{gettext('Uploaded')}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{this.state.uploadState === UPLOAD_ERROR && (
|
{this.state.uploadState === UPLOAD_ERROR && (
|
||||||
<div className="message err-message ml-0" dangerouslySetInnerHTML={{__html: error}}></div>
|
<div className="d-flex align-items-center">
|
||||||
|
<span className="upload-failure-icon fas fa-exclamation mr-2"></span>
|
||||||
|
<span className="upload-failure-msg" dangerouslySetInnerHTML={{__html: error}}></span>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
<td className="upload-operation ellipsis">
|
<td className="upload-operation">
|
||||||
<Fragment>
|
<Fragment>
|
||||||
{this.state.uploadState === UPLOAD_UPLOADING && (
|
{this.state.uploadState === UPLOAD_UPLOADING && (
|
||||||
<a href="#" onClick={this.onUploadCancel}>{gettext('Cancel')}</a>
|
<a href="#" onClick={this.onUploadCancel} role="button">{gettext('Cancel')}</a>
|
||||||
)}
|
)}
|
||||||
{this.state.uploadState === UPLOAD_ERROR && (
|
{this.state.uploadState === UPLOAD_ERROR && (
|
||||||
<a href="#" onClick={this.onUploadRetry}>{gettext('Retry')}</a>
|
<a href="#" onClick={this.onUploadRetry} role="button">{gettext('Retry')}</a>
|
||||||
)}
|
|
||||||
{this.state.uploadState === UPLOAD_ISSAVING && (
|
|
||||||
<span className="saving">{gettext('Saving...')}</span>
|
|
||||||
)}
|
|
||||||
{this.state.uploadState === UPLOAD_UPLOADED && (
|
|
||||||
<span className="uploaded">{gettext('Uploaded')}</span>
|
|
||||||
)}
|
)}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
</td>
|
</td>
|
||||||
|
@@ -16,7 +16,7 @@ const propTypes = {
|
|||||||
onUploadCancel: PropTypes.func.isRequired,
|
onUploadCancel: PropTypes.func.isRequired,
|
||||||
onUploadRetry: PropTypes.func.isRequired,
|
onUploadRetry: PropTypes.func.isRequired,
|
||||||
onUploadRetryAll: PropTypes.func.isRequired,
|
onUploadRetryAll: PropTypes.func.isRequired,
|
||||||
allFilesUploaded: PropTypes.bool.isRequired,
|
isUploading : PropTypes.bool.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
class UploadProgressDialog extends React.Component {
|
class UploadProgressDialog extends React.Component {
|
||||||
@@ -43,52 +43,74 @@ class UploadProgressDialog extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const { totalProgress, retryFileList, uploadBitrate, uploadFileList, forbidUploadFileList, isUploading } = this.props;
|
||||||
|
|
||||||
let uploadBitrate = Utils.formatBitRate(this.props.uploadBitrate);
|
const filesUploadedMsg = gettext('{uploaded_files_num}/{all_files_num} Files')
|
||||||
let uploadedMessage = gettext('File Upload');
|
.replace('{uploaded_files_num}', uploadFileList.filter(file => file.isSaved).length)
|
||||||
let uploadingMessage = gettext('File Uploading...') + ' ' + this.props.totalProgress + '% (' + uploadBitrate + ')';
|
.replace('{all_files_num}', uploadFileList.length);
|
||||||
|
|
||||||
let uploadingOptions = (<span className="sf2-icon-minus" onClick={this.onMinimizeUpload}></span>);
|
let filesFailedMsg;
|
||||||
|
if (!isUploading) {
|
||||||
let uploadedOptions = (
|
const failedNum = uploadFileList.filter(file => file.error).length + forbidUploadFileList.length;
|
||||||
<Fragment>
|
if (failedNum > 0) {
|
||||||
<span className="sf2-icon-minus" onClick={this.onMinimizeUpload}></span>
|
filesFailedMsg = gettext('{failed_files_num} file(s) failed to upload')
|
||||||
<span className="sf2-icon-x1" onClick={this.onCloseUpload}></span>
|
.replace('{failed_files_num}', failedNum);
|
||||||
</Fragment>
|
}
|
||||||
);
|
}
|
||||||
|
|
||||||
let { totalProgress, allFilesUploaded, retryFileList } = this.props;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="uploader-list-view mw-100" style={{height: this.state.isMinimized ? '2.25rem' : '20rem'}}>
|
<div className="uploader-list-view mw-100" style={{height: this.state.isMinimized ? document.querySelector('.uploader-list-header').offsetHeight : '20rem'}}>
|
||||||
<div className="uploader-list-header">
|
<div className="uploader-list-header flex-shrink-0">
|
||||||
<div className="title">
|
<div>
|
||||||
{totalProgress === 100 ? uploadedMessage : uploadingMessage}
|
{isUploading ? (
|
||||||
|
<>
|
||||||
|
<span>{gettext('File Uploading...')}</span>
|
||||||
|
<span className="ml-2">{`${totalProgress}% (${Utils.formatBitRate(uploadBitrate)})`}</span>
|
||||||
|
<div className="progress">
|
||||||
|
<div className="progress-bar" role="progressbar" style={{width: `${totalProgress}%`}} aria-valuenow={totalProgress} aria-valuemin="0" aria-valuemax="100"></div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{filesFailedMsg ?
|
||||||
|
<p className="m-0 error">{filesFailedMsg}</p> :
|
||||||
|
<p className="m-0">{gettext('All files uploaded')}</p>
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="uploader-options">
|
<div className="upload-dialog-op-container">
|
||||||
{totalProgress === 100 || allFilesUploaded ? uploadedOptions : uploadingOptions}
|
<span className="sf2-icon-minus upload-dialog-op" onClick={this.onMinimizeUpload}></span>
|
||||||
|
{!isUploading && <span className="sf2-icon-x1 upload-dialog-op" onClick={this.onCloseUpload}></span>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="uploader-list-content">
|
<div className="uploader-list-content">
|
||||||
<div className="text-right mt-2">
|
<div className="d-flex justify-content-between align-items-center border-bottom">
|
||||||
{retryFileList.length > 0 ?
|
{uploadFileList.length > 0 && <span>{filesUploadedMsg}</span>}
|
||||||
<span className="cursor-pointer" onClick={this.props.onUploadRetryAll}>{gettext('Retry All')}</span>
|
<div className="ml-auto">
|
||||||
:
|
<button
|
||||||
<span className="cursor-pointer disabled-link">{gettext('Retry All')}</span>
|
className="btn btn-lg border-0 background-transparent px-0"
|
||||||
}
|
onClick={this.props.onUploadRetryAll}
|
||||||
{!allFilesUploaded ?
|
disabled={retryFileList.length == 0}
|
||||||
<span className="cursor-pointer ml-3" onClick={this.onCancelAllUploading}>{gettext('Cancel All')}</span>
|
>
|
||||||
:
|
{gettext('Retry All')}
|
||||||
<span className="cursor-pointer ml-3 disabled-link" >{gettext('Cancel All')}</span>
|
</button>
|
||||||
}
|
<button
|
||||||
|
className="btn btn-lg border-0 background-transparent px-0 ml-3"
|
||||||
|
onClick={this.props.onCancelAllUploading}
|
||||||
|
disabled={!isUploading}
|
||||||
|
>
|
||||||
|
{gettext('Cancel All')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<table className="table-thead-hidden">
|
<table className="table-thead-hidden">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th width="30%">{gettext('name')}</th>
|
<th width="40%">{gettext('name')}</th>
|
||||||
<th width="20%">{gettext('size')}</th>
|
<th width="15%">{gettext('size')}</th>
|
||||||
<th width="30%">{gettext('progress')}</th>
|
<th width="30%">{gettext('progress')}</th>
|
||||||
<th width="20%">{gettext('state')}</th>
|
<th width="15%">{gettext('state')}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@@ -84,10 +84,6 @@
|
|||||||
color: #666666;
|
color: #666666;
|
||||||
}
|
}
|
||||||
|
|
||||||
.disabled-link {
|
|
||||||
color: #999999;
|
|
||||||
}
|
|
||||||
|
|
||||||
#upload-link-total-progress-container {
|
#upload-link-total-progress-container {
|
||||||
margin: 20px -32px 0;
|
margin: 20px -32px 0;
|
||||||
border: 1px solid #eee;
|
border: 1px solid #eee;
|
||||||
|
Reference in New Issue
Block a user