1
0
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:
llj
2024-04-07 22:16:08 +08:00
committed by GitHub
parent 4d33c2aca3
commit 48c0d96df8
6 changed files with 106 additions and 79 deletions

View File

@@ -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 {

View File

@@ -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>

View File

@@ -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>
); );
} }

View File

@@ -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>

View File

@@ -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>

View File

@@ -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;