uppy/src/plugins/StatusBar/index.js
2018-05-11 02:06:49 +02:00

234 lines
7.5 KiB
JavaScript

const Plugin = require('../../core/Plugin')
const Translator = require('../../core/Translator')
const StatusBarUI = require('./StatusBar')
const statusBarStates = require('./StatusBarStates')
const { getSpeed } = require('../../core/Utils')
const { getBytesRemaining } = require('../../core/Utils')
const { prettyETA } = require('../../core/Utils')
const prettyBytes = require('prettier-bytes')
/**
* StatusBar: renders a status bar with upload/pause/resume/cancel/retry buttons,
* progress percentage and time remaining.
*/
module.exports = class StatusBar extends Plugin {
constructor (uppy, opts) {
super(uppy, opts)
this.id = this.opts.id || 'StatusBar'
this.title = 'StatusBar'
this.type = 'progressindicator'
const defaultLocale = {
strings: {
uploading: 'Uploading',
complete: 'Complete',
uploadFailed: 'Upload failed',
pleasePressRetry: 'Please press Retry to upload again',
paused: 'Paused',
error: 'Error',
retry: 'Retry',
cancel: 'Cancel',
pressToRetry: 'Press to retry',
retryUpload: 'Retry upload',
resumeUpload: 'Resume upload',
cancelUpload: 'Cancel upload',
pauseUpload: 'Pause upload',
filesUploadedOfTotal: {
0: '%{complete} of %{smart_count} file uploaded',
1: '%{complete} of %{smart_count} files uploaded'
},
dataUploadedOfTotal: '%{complete} of %{total}',
xTimeLeft: '%{time} left',
uploadXFiles: {
0: 'Upload %{smart_count} file',
1: 'Upload %{smart_count} files'
},
uploadXNewFiles: {
0: 'Upload +%{smart_count} file',
1: 'Upload +%{smart_count} files'
}
}
}
// set default options
const defaultOptions = {
target: 'body',
hideUploadButton: false,
showProgressDetails: false,
locale: defaultLocale,
hideAfterFinish: true
}
// merge default options with the ones set by user
this.opts = Object.assign({}, defaultOptions, opts)
this.locale = Object.assign({}, defaultLocale, this.opts.locale)
this.locale.strings = Object.assign({}, defaultLocale.strings, this.opts.locale.strings)
this.translator = new Translator({locale: this.locale})
this.i18n = this.translator.translate.bind(this.translator)
this.startUpload = this.startUpload.bind(this)
this.render = this.render.bind(this)
this.install = this.install.bind(this)
}
getTotalSpeed (files) {
let totalSpeed = 0
files.forEach((file) => {
totalSpeed = totalSpeed + getSpeed(file.progress)
})
return totalSpeed
}
getTotalETA (files) {
const totalSpeed = this.getTotalSpeed(files)
if (totalSpeed === 0) {
return 0
}
const totalBytesRemaining = files.reduce((total, file) => {
return total + getBytesRemaining(file.progress)
}, 0)
return Math.round(totalBytesRemaining / totalSpeed * 10) / 10
}
startUpload () {
return this.uppy.upload().catch((err) => {
this.uppy.log(err.stack || err.message || err)
// Ignore
})
}
getUploadingState (isAllErrored, isAllComplete, files) {
if (isAllErrored) {
return statusBarStates.STATE_ERROR
}
if (isAllComplete) {
return statusBarStates.STATE_COMPLETE
}
let state = statusBarStates.STATE_WAITING
const fileIDs = Object.keys(files)
for (let i = 0; i < fileIDs.length; i++) {
const progress = files[fileIDs[i]].progress
// If ANY files are being uploaded right now, show the uploading state.
if (progress.uploadStarted && !progress.uploadComplete) {
return statusBarStates.STATE_UPLOADING
}
// If files are being preprocessed AND postprocessed at this time, we show the
// preprocess state. If any files are being uploaded we show uploading.
if (progress.preprocess && state !== statusBarStates.STATE_UPLOADING) {
state = statusBarStates.STATE_PREPROCESSING
}
// If NO files are being preprocessed or uploaded right now, but some files are
// being postprocessed, show the postprocess state.
if (progress.postprocess && state !== statusBarStates.STATE_UPLOADING && state !== statusBarStates.STATE_PREPROCESSING) {
state = statusBarStates.STATE_POSTPROCESSING
}
}
return state
}
render (state) {
const files = state.files
const uploadStartedFiles = Object.keys(files).filter((file) => {
return files[file].progress.uploadStarted
})
const newFiles = Object.keys(files).filter((file) => {
return !files[file].progress.uploadStarted &&
!files[file].progress.preprocess &&
!files[file].progress.postprocess
})
const completeFiles = Object.keys(files).filter((file) => {
return files[file].progress.uploadComplete
})
const erroredFiles = Object.keys(files).filter((file) => {
return files[file].error
})
const inProgressFiles = Object.keys(files).filter((file) => {
return !files[file].progress.uploadComplete &&
files[file].progress.uploadStarted &&
!files[file].isPaused
})
const processingFiles = Object.keys(files).filter((file) => {
return files[file].progress.preprocess || files[file].progress.postprocess
})
let inProgressFilesArray = inProgressFiles.map((file) => {
return files[file]
})
const totalSpeed = prettyBytes(this.getTotalSpeed(inProgressFilesArray))
const totalETA = prettyETA(this.getTotalETA(inProgressFilesArray))
// total size and uploaded size
let totalSize = 0
let totalUploadedSize = 0
inProgressFilesArray.forEach((file) => {
totalSize = totalSize + (file.progress.bytesTotal || 0)
totalUploadedSize = totalUploadedSize + (file.progress.bytesUploaded || 0)
})
totalSize = prettyBytes(totalSize)
totalUploadedSize = prettyBytes(totalUploadedSize)
const isUploadStarted = uploadStartedFiles.length > 0
const isAllComplete = state.totalProgress === 100 &&
completeFiles.length === Object.keys(files).length &&
processingFiles.length === 0
const isAllErrored = isUploadStarted &&
erroredFiles.length === uploadStartedFiles.length
const isAllPaused = inProgressFiles.length === 0 &&
!isAllComplete &&
!isAllErrored &&
uploadStartedFiles.length > 0
const resumableUploads = state.capabilities.resumableUploads || false
return StatusBarUI({
error: state.error,
uploadState: this.getUploadingState(isAllErrored, isAllComplete, state.files || {}),
totalProgress: state.totalProgress,
totalSize: totalSize,
totalUploadedSize: totalUploadedSize,
uploadStarted: uploadStartedFiles.length,
isAllComplete: isAllComplete,
isAllPaused: isAllPaused,
isAllErrored: isAllErrored,
isUploadStarted: isUploadStarted,
complete: completeFiles.length,
newFiles: newFiles.length,
inProgress: inProgressFiles.length,
totalSpeed: totalSpeed,
totalETA: totalETA,
files: state.files,
i18n: this.i18n,
pauseAll: this.uppy.pauseAll,
resumeAll: this.uppy.resumeAll,
retryAll: this.uppy.retryAll,
cancelAll: this.uppy.cancelAll,
startUpload: this.startUpload,
resumableUploads: resumableUploads,
showProgressDetails: this.opts.showProgressDetails,
hideUploadButton: this.opts.hideUploadButton,
hideAfterFinish: this.opts.hideAfterFinish
})
}
install () {
const target = this.opts.target
if (target) {
this.mount(target, this)
}
}
uninstall () {
this.unmount()
}
}