index.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. const { Plugin } = require('@uppy/core')
  2. const Translator = require('@uppy/utils/lib/Translator')
  3. const StatusBarUI = require('./StatusBar')
  4. const statusBarStates = require('./StatusBarStates')
  5. const getSpeed = require('@uppy/utils/lib/getSpeed')
  6. const getBytesRemaining = require('@uppy/utils/lib/getBytesRemaining')
  7. const prettyETA = require('@uppy/utils/lib/prettyETA')
  8. const prettyBytes = require('prettier-bytes')
  9. /**
  10. * StatusBar: renders a status bar with upload/pause/resume/cancel/retry buttons,
  11. * progress percentage and time remaining.
  12. */
  13. module.exports = class StatusBar extends Plugin {
  14. constructor (uppy, opts) {
  15. super(uppy, opts)
  16. this.id = this.opts.id || 'StatusBar'
  17. this.title = 'StatusBar'
  18. this.type = 'progressindicator'
  19. const defaultLocale = {
  20. strings: {
  21. uploading: 'Uploading',
  22. complete: 'Complete',
  23. uploadFailed: 'Upload failed',
  24. pleasePressRetry: 'Please press Retry to upload again',
  25. paused: 'Paused',
  26. error: 'Error',
  27. retry: 'Retry',
  28. cancel: 'Cancel',
  29. pressToRetry: 'Press to retry',
  30. retryUpload: 'Retry upload',
  31. resumeUpload: 'Resume upload',
  32. cancelUpload: 'Cancel upload',
  33. pauseUpload: 'Pause upload',
  34. filesUploadedOfTotal: {
  35. 0: '%{complete} of %{smart_count} file uploaded',
  36. 1: '%{complete} of %{smart_count} files uploaded'
  37. },
  38. dataUploadedOfTotal: '%{complete} of %{total}',
  39. xTimeLeft: '%{time} left',
  40. uploadXFiles: {
  41. 0: 'Upload %{smart_count} file',
  42. 1: 'Upload %{smart_count} files'
  43. },
  44. uploadXNewFiles: {
  45. 0: 'Upload +%{smart_count} file',
  46. 1: 'Upload +%{smart_count} files'
  47. }
  48. }
  49. }
  50. // set default options
  51. const defaultOptions = {
  52. target: 'body',
  53. hideUploadButton: false,
  54. hideRetryButton: false,
  55. hidePauseResumeCancelButtons: false,
  56. showProgressDetails: false,
  57. locale: defaultLocale,
  58. hideAfterFinish: true
  59. }
  60. // merge default options with the ones set by user
  61. this.opts = Object.assign({}, defaultOptions, opts)
  62. this.locale = Object.assign({}, defaultLocale, this.opts.locale)
  63. this.locale.strings = Object.assign({}, defaultLocale.strings, this.opts.locale.strings)
  64. this.translator = new Translator({locale: this.locale})
  65. this.i18n = this.translator.translate.bind(this.translator)
  66. this.startUpload = this.startUpload.bind(this)
  67. this.render = this.render.bind(this)
  68. this.install = this.install.bind(this)
  69. }
  70. getTotalSpeed (files) {
  71. let totalSpeed = 0
  72. files.forEach((file) => {
  73. totalSpeed = totalSpeed + getSpeed(file.progress)
  74. })
  75. return totalSpeed
  76. }
  77. getTotalETA (files) {
  78. const totalSpeed = this.getTotalSpeed(files)
  79. if (totalSpeed === 0) {
  80. return 0
  81. }
  82. const totalBytesRemaining = files.reduce((total, file) => {
  83. return total + getBytesRemaining(file.progress)
  84. }, 0)
  85. return Math.round(totalBytesRemaining / totalSpeed * 10) / 10
  86. }
  87. startUpload () {
  88. return this.uppy.upload().catch((err) => {
  89. this.uppy.log(err.stack || err.message || err)
  90. // Ignore
  91. })
  92. }
  93. getUploadingState (isAllErrored, isAllComplete, files) {
  94. if (isAllErrored) {
  95. return statusBarStates.STATE_ERROR
  96. }
  97. if (isAllComplete) {
  98. return statusBarStates.STATE_COMPLETE
  99. }
  100. let state = statusBarStates.STATE_WAITING
  101. const fileIDs = Object.keys(files)
  102. for (let i = 0; i < fileIDs.length; i++) {
  103. const progress = files[fileIDs[i]].progress
  104. // If ANY files are being uploaded right now, show the uploading state.
  105. if (progress.uploadStarted && !progress.uploadComplete) {
  106. return statusBarStates.STATE_UPLOADING
  107. }
  108. // If files are being preprocessed AND postprocessed at this time, we show the
  109. // preprocess state. If any files are being uploaded we show uploading.
  110. if (progress.preprocess && state !== statusBarStates.STATE_UPLOADING) {
  111. state = statusBarStates.STATE_PREPROCESSING
  112. }
  113. // If NO files are being preprocessed or uploaded right now, but some files are
  114. // being postprocessed, show the postprocess state.
  115. if (progress.postprocess && state !== statusBarStates.STATE_UPLOADING && state !== statusBarStates.STATE_PREPROCESSING) {
  116. state = statusBarStates.STATE_POSTPROCESSING
  117. }
  118. }
  119. return state
  120. }
  121. render (state) {
  122. const files = state.files
  123. const uploadStartedFiles = Object.keys(files).filter((file) => {
  124. return files[file].progress.uploadStarted
  125. })
  126. const newFiles = Object.keys(files).filter((file) => {
  127. return !files[file].progress.uploadStarted &&
  128. !files[file].progress.preprocess &&
  129. !files[file].progress.postprocess
  130. })
  131. const completeFiles = Object.keys(files).filter((file) => {
  132. return files[file].progress.uploadComplete
  133. })
  134. const erroredFiles = Object.keys(files).filter((file) => {
  135. return files[file].error
  136. })
  137. const inProgressFiles = Object.keys(files).filter((file) => {
  138. return !files[file].progress.uploadComplete &&
  139. files[file].progress.uploadStarted &&
  140. !files[file].isPaused
  141. })
  142. const startedFiles = Object.keys(files).filter((file) => {
  143. return files[file].progress.uploadStarted ||
  144. files[file].progress.preprocess ||
  145. files[file].progress.postprocess
  146. })
  147. const processingFiles = Object.keys(files).filter((file) => {
  148. return files[file].progress.preprocess || files[file].progress.postprocess
  149. })
  150. let inProgressFilesArray = inProgressFiles.map((file) => {
  151. return files[file]
  152. })
  153. const totalSpeed = prettyBytes(this.getTotalSpeed(inProgressFilesArray))
  154. const totalETA = prettyETA(this.getTotalETA(inProgressFilesArray))
  155. // total size and uploaded size
  156. let totalSize = 0
  157. let totalUploadedSize = 0
  158. inProgressFilesArray.forEach((file) => {
  159. totalSize = totalSize + (file.progress.bytesTotal || 0)
  160. totalUploadedSize = totalUploadedSize + (file.progress.bytesUploaded || 0)
  161. })
  162. totalSize = prettyBytes(totalSize)
  163. totalUploadedSize = prettyBytes(totalUploadedSize)
  164. const isUploadStarted = uploadStartedFiles.length > 0
  165. const isAllComplete = state.totalProgress === 100 &&
  166. completeFiles.length === Object.keys(files).length &&
  167. processingFiles.length === 0
  168. const isAllErrored = isUploadStarted &&
  169. erroredFiles.length === uploadStartedFiles.length
  170. const isAllPaused = inProgressFiles.length === 0 &&
  171. !isAllComplete &&
  172. !isAllErrored &&
  173. uploadStartedFiles.length > 0
  174. const resumableUploads = state.capabilities.resumableUploads || false
  175. return StatusBarUI({
  176. error: state.error,
  177. uploadState: this.getUploadingState(isAllErrored, isAllComplete, state.files || {}),
  178. totalProgress: state.totalProgress,
  179. totalSize: totalSize,
  180. totalUploadedSize: totalUploadedSize,
  181. uploadStarted: uploadStartedFiles.length,
  182. isAllComplete: isAllComplete,
  183. isAllPaused: isAllPaused,
  184. isAllErrored: isAllErrored,
  185. isUploadStarted: isUploadStarted,
  186. complete: completeFiles.length,
  187. newFiles: newFiles.length,
  188. numUploads: startedFiles.length,
  189. totalSpeed: totalSpeed,
  190. totalETA: totalETA,
  191. files: state.files,
  192. i18n: this.i18n,
  193. pauseAll: this.uppy.pauseAll,
  194. resumeAll: this.uppy.resumeAll,
  195. retryAll: this.uppy.retryAll,
  196. cancelAll: this.uppy.cancelAll,
  197. startUpload: this.startUpload,
  198. resumableUploads: resumableUploads,
  199. showProgressDetails: this.opts.showProgressDetails,
  200. hideUploadButton: this.opts.hideUploadButton,
  201. hideRetryButton: this.opts.hideRetryButton,
  202. hidePauseResumeCancelButtons: this.opts.hidePauseResumeCancelButtons,
  203. hideAfterFinish: this.opts.hideAfterFinish
  204. })
  205. }
  206. install () {
  207. const target = this.opts.target
  208. if (target) {
  209. this.mount(target, this)
  210. }
  211. }
  212. uninstall () {
  213. this.unmount()
  214. }
  215. }