index.js 7.7 KB

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