AssemblyWatcher.ts 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. import type { Uppy } from '@uppy/core'
  2. import type { Body, Meta } from '@uppy/utils/lib/UppyFile'
  3. import Emitter from 'component-emitter'
  4. import type { AssemblyResponse } from '.'
  5. /**
  6. * Track completion of multiple assemblies.
  7. *
  8. * Emits 'assembly-complete' when an assembly completes.
  9. * Emits 'assembly-error' when an assembly fails.
  10. * Exposes a `.promise` property that resolves when all assemblies have
  11. * completed (or failed).
  12. */
  13. class TransloaditAssemblyWatcher<
  14. M extends Meta,
  15. B extends Body,
  16. > extends Emitter {
  17. #assemblyIDs
  18. #remaining: number
  19. promise: Promise<void>
  20. #resolve: () => void
  21. #reject: (reason?: string) => void
  22. #uppy
  23. constructor(uppy: Uppy<M, B>, assemblyIDs: string[]) {
  24. super()
  25. this.#uppy = uppy
  26. this.#assemblyIDs = assemblyIDs
  27. this.#remaining = assemblyIDs.length
  28. this.promise = new Promise<void>((resolve, reject) => {
  29. this.#resolve = resolve
  30. this.#reject = reject
  31. })
  32. this.#addListeners()
  33. }
  34. /**
  35. * Are we watching this assembly ID?
  36. */
  37. #watching(id: string) {
  38. return this.#assemblyIDs.indexOf(id) !== -1
  39. }
  40. #onAssemblyComplete = (assembly: AssemblyResponse) => {
  41. if (!this.#watching(assembly.assembly_id)) {
  42. return
  43. }
  44. this.#uppy.log(
  45. `[Transloadit] AssemblyWatcher: Got Assembly finish ${assembly.assembly_id}`,
  46. )
  47. this.emit('assembly-complete', assembly.assembly_id)
  48. this.#checkAllComplete()
  49. }
  50. #onAssemblyCancel = (assembly: AssemblyResponse) => {
  51. if (!this.#watching(assembly.assembly_id)) {
  52. return
  53. }
  54. this.#checkAllComplete()
  55. }
  56. #onAssemblyError = (assembly: AssemblyResponse, error: Error) => {
  57. if (!this.#watching(assembly.assembly_id)) {
  58. return
  59. }
  60. this.#uppy.log(
  61. `[Transloadit] AssemblyWatcher: Got Assembly error ${assembly.assembly_id}`,
  62. )
  63. this.#uppy.log(error)
  64. this.emit('assembly-error', assembly.assembly_id, error)
  65. this.#checkAllComplete()
  66. }
  67. #onImportError = (
  68. assembly: AssemblyResponse,
  69. fileID: string,
  70. error: Error,
  71. ) => {
  72. if (!this.#watching(assembly.assembly_id)) {
  73. return
  74. }
  75. // Not sure if we should be doing something when it's just one file failing.
  76. // ATM, the only options are 1) ignoring or 2) failing the entire upload.
  77. // I think failing the upload is better than silently ignoring.
  78. // In the future we should maybe have a way to resolve uploads with some failures,
  79. // like returning an object with `{ successful, failed }` uploads.
  80. this.#onAssemblyError(assembly, error)
  81. }
  82. #checkAllComplete() {
  83. this.#remaining -= 1
  84. if (this.#remaining === 0) {
  85. // We're done, these listeners can be removed
  86. this.#removeListeners()
  87. this.#resolve()
  88. }
  89. }
  90. #removeListeners() {
  91. this.#uppy.off('transloadit:complete', this.#onAssemblyComplete)
  92. this.#uppy.off('transloadit:assembly-cancel', this.#onAssemblyCancel)
  93. this.#uppy.off('transloadit:assembly-error', this.#onAssemblyError)
  94. this.#uppy.off('transloadit:import-error', this.#onImportError)
  95. }
  96. #addListeners() {
  97. this.#uppy.on('transloadit:complete', this.#onAssemblyComplete)
  98. this.#uppy.on('transloadit:assembly-cancel', this.#onAssemblyCancel)
  99. this.#uppy.on('transloadit:assembly-error', this.#onAssemblyError)
  100. this.#uppy.on('transloadit:import-error', this.#onImportError)
  101. }
  102. }
  103. export default TransloaditAssemblyWatcher