@strapi/upload 5.52.3 → 5.54.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (220) hide show
  1. package/dist/admin/future/components/Drawer.js +6 -3
  2. package/dist/admin/future/components/Drawer.js.map +1 -1
  3. package/dist/admin/future/components/Drawer.mjs +6 -3
  4. package/dist/admin/future/components/Drawer.mjs.map +1 -1
  5. package/dist/admin/future/components/UploadProgressDialog.js +9 -8
  6. package/dist/admin/future/components/UploadProgressDialog.js.map +1 -1
  7. package/dist/admin/future/components/UploadProgressDialog.mjs +9 -8
  8. package/dist/admin/future/components/UploadProgressDialog.mjs.map +1 -1
  9. package/dist/admin/future/pages/Assets/AssetsPage.js +16 -4
  10. package/dist/admin/future/pages/Assets/AssetsPage.js.map +1 -1
  11. package/dist/admin/future/pages/Assets/AssetsPage.mjs +16 -4
  12. package/dist/admin/future/pages/Assets/AssetsPage.mjs.map +1 -1
  13. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.js +5 -0
  14. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.js.map +1 -1
  15. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.mjs +5 -0
  16. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.mjs.map +1 -1
  17. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.js +19 -4
  18. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.js.map +1 -1
  19. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.mjs +19 -4
  20. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.mjs.map +1 -1
  21. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js +35 -1
  22. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js.map +1 -1
  23. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs +35 -1
  24. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs.map +1 -1
  25. package/dist/admin/future/pages/Assets/components/AssetsGrid.js +10 -0
  26. package/dist/admin/future/pages/Assets/components/AssetsGrid.js.map +1 -1
  27. package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs +10 -0
  28. package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs.map +1 -1
  29. package/dist/admin/future/pages/Assets/components/AssetsTable.js +9 -0
  30. package/dist/admin/future/pages/Assets/components/AssetsTable.js.map +1 -1
  31. package/dist/admin/future/pages/Assets/components/AssetsTable.mjs +10 -1
  32. package/dist/admin/future/pages/Assets/components/AssetsTable.mjs.map +1 -1
  33. package/dist/admin/future/pages/Assets/components/BulkActionsBar.js +132 -88
  34. package/dist/admin/future/pages/Assets/components/BulkActionsBar.js.map +1 -1
  35. package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs +135 -91
  36. package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs.map +1 -1
  37. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js +7 -0
  38. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js.map +1 -1
  39. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs +7 -0
  40. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs.map +1 -1
  41. package/dist/admin/future/pages/Assets/components/Dnd/DragOverlayChip.js +4 -1
  42. package/dist/admin/future/pages/Assets/components/Dnd/DragOverlayChip.js.map +1 -1
  43. package/dist/admin/future/pages/Assets/components/Dnd/DragOverlayChip.mjs +4 -1
  44. package/dist/admin/future/pages/Assets/components/Dnd/DragOverlayChip.mjs.map +1 -1
  45. package/dist/admin/future/pages/Assets/components/MainAreaContextMenu.js +181 -0
  46. package/dist/admin/future/pages/Assets/components/MainAreaContextMenu.js.map +1 -0
  47. package/dist/admin/future/pages/Assets/components/MainAreaContextMenu.mjs +179 -0
  48. package/dist/admin/future/pages/Assets/components/MainAreaContextMenu.mjs.map +1 -0
  49. package/dist/admin/future/pages/Assets/constants.js +22 -0
  50. package/dist/admin/future/pages/Assets/constants.js.map +1 -1
  51. package/dist/admin/future/pages/Assets/constants.mjs +19 -1
  52. package/dist/admin/future/pages/Assets/constants.mjs.map +1 -1
  53. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js +65 -10
  54. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js.map +1 -1
  55. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs +66 -11
  56. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs.map +1 -1
  57. package/dist/admin/future/pages/Assets/hooks/useListScrollRestoration.js +111 -0
  58. package/dist/admin/future/pages/Assets/hooks/useListScrollRestoration.js.map +1 -0
  59. package/dist/admin/future/pages/Assets/hooks/useListScrollRestoration.mjs +109 -0
  60. package/dist/admin/future/pages/Assets/hooks/useListScrollRestoration.mjs.map +1 -0
  61. package/dist/admin/future/services/api.js +17 -0
  62. package/dist/admin/future/services/api.js.map +1 -1
  63. package/dist/admin/future/services/api.mjs +18 -1
  64. package/dist/admin/future/services/api.mjs.map +1 -1
  65. package/dist/admin/future/store/uploadProgress.js +31 -22
  66. package/dist/admin/future/store/uploadProgress.js.map +1 -1
  67. package/dist/admin/future/store/uploadProgress.mjs +31 -22
  68. package/dist/admin/future/store/uploadProgress.mjs.map +1 -1
  69. package/dist/admin/index.js +11 -10
  70. package/dist/admin/index.js.map +1 -1
  71. package/dist/admin/index.mjs +11 -10
  72. package/dist/admin/index.mjs.map +1 -1
  73. package/dist/admin/src/future/components/Drawer.d.ts +10 -0
  74. package/dist/admin/src/future/pages/Assets/components/MainAreaContextMenu.d.ts +28 -0
  75. package/dist/admin/src/future/pages/Assets/constants.d.ts +20 -0
  76. package/dist/admin/src/future/pages/Assets/hooks/useInfiniteAssets.d.ts +4 -0
  77. package/dist/admin/src/future/pages/Assets/hooks/useListScrollRestoration.d.ts +15 -0
  78. package/dist/admin/src/future/store/uploadProgress.d.ts +14 -20
  79. package/dist/admin/translations/en.json.js +1 -3
  80. package/dist/admin/translations/en.json.js.map +1 -1
  81. package/dist/admin/translations/en.json.mjs +1 -3
  82. package/dist/admin/translations/en.json.mjs.map +1 -1
  83. package/dist/server/bootstrap.js +11 -0
  84. package/dist/server/bootstrap.js.map +1 -1
  85. package/dist/server/bootstrap.mjs +11 -0
  86. package/dist/server/bootstrap.mjs.map +1 -1
  87. package/dist/server/controllers/admin-upload.js +31 -1
  88. package/dist/server/controllers/admin-upload.js.map +1 -1
  89. package/dist/server/controllers/admin-upload.mjs +31 -1
  90. package/dist/server/controllers/admin-upload.mjs.map +1 -1
  91. package/dist/server/controllers/utils/find-entity-and-check-permissions.js +15 -4
  92. package/dist/server/controllers/utils/find-entity-and-check-permissions.js.map +1 -1
  93. package/dist/server/controllers/utils/find-entity-and-check-permissions.mjs +15 -4
  94. package/dist/server/controllers/utils/find-entity-and-check-permissions.mjs.map +1 -1
  95. package/dist/server/controllers/utils/folders.js +1 -1
  96. package/dist/server/controllers/utils/folders.js.map +1 -1
  97. package/dist/server/controllers/utils/folders.mjs +1 -1
  98. package/dist/server/controllers/utils/folders.mjs.map +1 -1
  99. package/dist/server/mcp/ambient-instance.js +46 -0
  100. package/dist/server/mcp/ambient-instance.js.map +1 -0
  101. package/dist/server/mcp/ambient-instance.mjs +43 -0
  102. package/dist/server/mcp/ambient-instance.mjs.map +1 -0
  103. package/dist/server/mcp/handlers/constants.js +60 -0
  104. package/dist/server/mcp/handlers/constants.js.map +1 -0
  105. package/dist/server/mcp/handlers/constants.mjs +45 -0
  106. package/dist/server/mcp/handlers/constants.mjs.map +1 -0
  107. package/dist/server/mcp/handlers/folder-handlers.js +308 -0
  108. package/dist/server/mcp/handlers/folder-handlers.js.map +1 -0
  109. package/dist/server/mcp/handlers/folder-handlers.mjs +303 -0
  110. package/dist/server/mcp/handlers/folder-handlers.mjs.map +1 -0
  111. package/dist/server/mcp/handlers/read-handlers.js +134 -0
  112. package/dist/server/mcp/handlers/read-handlers.js.map +1 -0
  113. package/dist/server/mcp/handlers/read-handlers.mjs +130 -0
  114. package/dist/server/mcp/handlers/read-handlers.mjs.map +1 -0
  115. package/dist/server/mcp/handlers/write-handlers.js +315 -0
  116. package/dist/server/mcp/handlers/write-handlers.js.map +1 -0
  117. package/dist/server/mcp/handlers/write-handlers.mjs +311 -0
  118. package/dist/server/mcp/handlers/write-handlers.mjs.map +1 -0
  119. package/dist/server/mcp/permissions.js +36 -0
  120. package/dist/server/mcp/permissions.js.map +1 -0
  121. package/dist/server/mcp/permissions.mjs +33 -0
  122. package/dist/server/mcp/permissions.mjs.map +1 -0
  123. package/dist/server/mcp/register-upload-mcp-tools.js +235 -0
  124. package/dist/server/mcp/register-upload-mcp-tools.js.map +1 -0
  125. package/dist/server/mcp/register-upload-mcp-tools.mjs +232 -0
  126. package/dist/server/mcp/register-upload-mcp-tools.mjs.map +1 -0
  127. package/dist/server/mcp/sanitizers/sanitize-media.js +97 -0
  128. package/dist/server/mcp/sanitizers/sanitize-media.js.map +1 -0
  129. package/dist/server/mcp/sanitizers/sanitize-media.mjs +93 -0
  130. package/dist/server/mcp/sanitizers/sanitize-media.mjs.map +1 -0
  131. package/dist/server/mcp/schemas/input-schemas.js +206 -0
  132. package/dist/server/mcp/schemas/input-schemas.js.map +1 -0
  133. package/dist/server/mcp/schemas/input-schemas.mjs +190 -0
  134. package/dist/server/mcp/schemas/input-schemas.mjs.map +1 -0
  135. package/dist/server/mcp/schemas/output-schemas.js +158 -0
  136. package/dist/server/mcp/schemas/output-schemas.js.map +1 -0
  137. package/dist/server/mcp/schemas/output-schemas.mjs +142 -0
  138. package/dist/server/mcp/schemas/output-schemas.mjs.map +1 -0
  139. package/dist/server/mcp/utils.js +14 -0
  140. package/dist/server/mcp/utils.js.map +1 -0
  141. package/dist/server/mcp/utils.mjs +12 -0
  142. package/dist/server/mcp/utils.mjs.map +1 -0
  143. package/dist/server/media-library-default-notice.js +42 -0
  144. package/dist/server/media-library-default-notice.js.map +1 -0
  145. package/dist/server/media-library-default-notice.mjs +40 -0
  146. package/dist/server/media-library-default-notice.mjs.map +1 -0
  147. package/dist/server/register.js +5 -0
  148. package/dist/server/register.js.map +1 -1
  149. package/dist/server/register.mjs +5 -0
  150. package/dist/server/register.mjs.map +1 -1
  151. package/dist/server/services/folder.js.map +1 -1
  152. package/dist/server/services/folder.mjs.map +1 -1
  153. package/dist/server/services/upload.js +34 -10
  154. package/dist/server/services/upload.js.map +1 -1
  155. package/dist/server/services/upload.mjs +34 -10
  156. package/dist/server/services/upload.mjs.map +1 -1
  157. package/dist/server/src/bootstrap.d.ts.map +1 -1
  158. package/dist/server/src/controllers/admin-upload.d.ts +1 -0
  159. package/dist/server/src/controllers/admin-upload.d.ts.map +1 -1
  160. package/dist/server/src/controllers/utils/find-entity-and-check-permissions.d.ts +14 -1
  161. package/dist/server/src/controllers/utils/find-entity-and-check-permissions.d.ts.map +1 -1
  162. package/dist/server/src/controllers/utils/folders.d.ts +2 -1
  163. package/dist/server/src/controllers/utils/folders.d.ts.map +1 -1
  164. package/dist/server/src/index.d.ts +10 -2
  165. package/dist/server/src/index.d.ts.map +1 -1
  166. package/dist/server/src/mcp/ambient-instance.d.ts +65 -0
  167. package/dist/server/src/mcp/ambient-instance.d.ts.map +1 -0
  168. package/dist/server/src/mcp/handlers/constants.d.ts +48 -0
  169. package/dist/server/src/mcp/handlers/constants.d.ts.map +1 -0
  170. package/dist/server/src/mcp/handlers/folder-handlers.d.ts +49 -0
  171. package/dist/server/src/mcp/handlers/folder-handlers.d.ts.map +1 -0
  172. package/dist/server/src/mcp/handlers/index.d.ts +5 -0
  173. package/dist/server/src/mcp/handlers/index.d.ts.map +1 -0
  174. package/dist/server/src/mcp/handlers/read-handlers.d.ts +35 -0
  175. package/dist/server/src/mcp/handlers/read-handlers.d.ts.map +1 -0
  176. package/dist/server/src/mcp/handlers/write-handlers.d.ts +66 -0
  177. package/dist/server/src/mcp/handlers/write-handlers.d.ts.map +1 -0
  178. package/dist/server/src/mcp/index.d.ts +3 -0
  179. package/dist/server/src/mcp/index.d.ts.map +1 -0
  180. package/dist/server/src/mcp/permissions.d.ts +22 -0
  181. package/dist/server/src/mcp/permissions.d.ts.map +1 -0
  182. package/dist/server/src/mcp/register-upload-mcp-tools.d.ts +29 -0
  183. package/dist/server/src/mcp/register-upload-mcp-tools.d.ts.map +1 -0
  184. package/dist/server/src/mcp/sanitizers/sanitize-media.d.ts +63 -0
  185. package/dist/server/src/mcp/sanitizers/sanitize-media.d.ts.map +1 -0
  186. package/dist/server/src/mcp/schemas/index.d.ts +3 -0
  187. package/dist/server/src/mcp/schemas/index.d.ts.map +1 -0
  188. package/dist/server/src/mcp/schemas/input-schemas.d.ts +148 -0
  189. package/dist/server/src/mcp/schemas/input-schemas.d.ts.map +1 -0
  190. package/dist/server/src/mcp/schemas/output-schemas.d.ts +276 -0
  191. package/dist/server/src/mcp/schemas/output-schemas.d.ts.map +1 -0
  192. package/dist/server/src/mcp/types.d.ts +19 -0
  193. package/dist/server/src/mcp/types.d.ts.map +1 -0
  194. package/dist/server/src/mcp/utils.d.ts +4 -0
  195. package/dist/server/src/mcp/utils.d.ts.map +1 -0
  196. package/dist/server/src/media-library-default-notice.d.ts +19 -0
  197. package/dist/server/src/media-library-default-notice.d.ts.map +1 -0
  198. package/dist/server/src/register.d.ts.map +1 -1
  199. package/dist/server/src/services/folder.d.ts +6 -2
  200. package/dist/server/src/services/folder.d.ts.map +1 -1
  201. package/dist/server/src/services/index.d.ts +10 -2
  202. package/dist/server/src/services/index.d.ts.map +1 -1
  203. package/dist/server/src/services/upload.d.ts +10 -1
  204. package/dist/server/src/services/upload.d.ts.map +1 -1
  205. package/dist/server/src/types.d.ts +2 -2
  206. package/dist/server/src/types.d.ts.map +1 -1
  207. package/dist/server/src/utils/index.d.ts +2 -1
  208. package/dist/server/src/utils/index.d.ts.map +1 -1
  209. package/dist/server/utils/index.js +2 -2
  210. package/dist/server/utils/index.js.map +1 -1
  211. package/dist/server/utils/index.mjs +2 -2
  212. package/dist/server/utils/index.mjs.map +1 -1
  213. package/dist/shared/contracts/files.d.ts +22 -0
  214. package/dist/shared/contracts/files.d.ts.map +1 -1
  215. package/package.json +7 -6
  216. package/dist/admin/future/components/BetaNotice.js +0 -72
  217. package/dist/admin/future/components/BetaNotice.js.map +0 -1
  218. package/dist/admin/future/components/BetaNotice.mjs +0 -70
  219. package/dist/admin/future/components/BetaNotice.mjs.map +0 -1
  220. package/dist/admin/src/future/components/BetaNotice.d.ts +0 -11
@@ -64,13 +64,21 @@ const uploadProgressSlice = toolkit.createSlice({
64
64
  state.files[index].size = size;
65
65
  }
66
66
  },
67
- setFileProgress (state, action) {
68
- const { index, bytes, uploadId } = action.payload;
67
+ /**
68
+ * `size` is optional and only sent by the URL flow, which learns the size from the
69
+ * same server event that carries the first byte count — the row was created with
70
+ * `size: 0`, and bytes clamped against a zero size would all read as zero. The
71
+ * direct-file flow already knows the size up front and omits it.
72
+ */ setFileProgress (state, action) {
73
+ const { index, bytes, size, uploadId } = action.payload;
69
74
  if (uploadId !== state.uploadId) {
70
75
  return;
71
76
  }
72
77
  const file = state.files[index];
73
78
  if (file) {
79
+ if (size !== undefined && file.size === 0) {
80
+ file.size = size;
81
+ }
74
82
  // Clamp to the known file size so the aggregate can never exceed 100%.
75
83
  file.uploadedBytes = Math.min(bytes, file.size);
76
84
  }
@@ -234,30 +242,31 @@ const selectCompletedUploads = toolkit.createSelector(// The plugin registers th
234
242
  return Math.round(uploadedBytes / totalSize * 100);
235
243
  });
236
244
  /**
237
- * Whether to show byte-weighted `selectAggregateProgress` rather than count-based progress.
238
- * A batch that never reports bytes stays pinned at 0%, so the header falls back to
239
- * `selectCountBasedProgress`.
245
+ * Whether to show byte-weighted `selectAggregateProgress` rather than `selectCountBasedProgress`.
240
246
  *
241
- * Checks the rows too, not just the flag: an in-flight row reporting bytes proves the batch
242
- * can, so byte-weighting takes over once the URL flow starts emitting progress. Only
243
- * `uploading` rows count — completion backfills `uploadedBytes` to the file size, so letting
244
- * settled rows qualify would switch a multi-URL batch to byte-weighting mid-upload over only
245
- * the sizes known so far (transiently 100% after the first file).
246
- */ const selectReportsByteProgress = toolkit.createSelector((state)=>state.uploadProgress.reportsByteProgress, (state)=>state.uploadProgress.files, (reportsByteProgress, files)=>reportsByteProgress || files.some((f)=>f.status === 'uploading' && f.uploadedBytes > 0));
247
+ * Only the flag: byte-weighting needs every size up front. A URL batch learns sizes one row
248
+ * at a time, so weighting over the sizes known so far makes the header jump backwards at
249
+ * every file boundary.
250
+ */ const selectReportsByteProgress = toolkit.createSelector((state)=>state.uploadProgress.reportsByteProgress, (reportsByteProgress)=>reportsByteProgress);
247
251
  /**
248
- * Count-based progress (settled / total rows) for a batch that reports no transferred bytes
249
- * (the URL flow). Used by the header whenever `selectReportsByteProgress` is false.
250
- *
251
- * Never byte-weights, so a multi-URL batch climbs 33 → 67 → 100 as each file lands instead
252
- * of jumping to a transient 100% after the first.
252
+ * Row-weighted progress for a batch whose sizes are not known up front (the URL flow).
253
+ * Each row is worth `1 / total`: settled rows count fully and an in-flight row contributes
254
+ * `uploadedBytes / size` once it reports bytes, so a multi-URL batch climbs smoothly to
255
+ * 33 → 67 → 100 and never moves backwards.
253
256
  *
254
- * Returns `null` until a row settles — 0% is the frozen signal this flow exists to remove
255
- * (a lone URL upload would otherwise read "Uploading 1 item (0%)" throughout), so the header
256
- * stays indeterminate until there's real progress.
257
+ * Returns `null` until something has moved — 0% is the frozen signal this flow exists to
258
+ * remove, so the header stays indeterminate until there's real progress.
257
259
  */ const selectCountBasedProgress = toolkit.createSelector((state)=>state.uploadProgress.files, (files)=>{
258
- const settled = files.filter((f)=>f.status === 'complete' || f.status === 'error' || f.status === 'cancelled').length;
259
- if (settled === 0) return null;
260
- return Math.round(settled / files.length * 100);
260
+ const done = files.reduce((sum, f)=>{
261
+ if (f.status === 'complete' || f.status === 'error' || f.status === 'cancelled') {
262
+ return sum + 1;
263
+ }
264
+ return f.size > 0 ? sum + Math.min(f.uploadedBytes / f.size, 1) : sum;
265
+ }, 0);
266
+ if (done === 0) {
267
+ return null;
268
+ }
269
+ return Math.round(done / files.length * 100);
261
270
  });
262
271
  /**
263
272
  * Count-based metadata progress across the whole batch: `settled / expected`.
@@ -1 +1 @@
1
- {"version":3,"file":"uploadProgress.js","sources":["../../../../admin/src/future/store/uploadProgress.ts"],"sourcesContent":["import { createSelector, createSlice, type PayloadAction } from '@reduxjs/toolkit';\n\nimport type { File } from '../../../../shared/contracts/files';\n\nexport interface FileUploadError {\n name: string;\n message: string;\n}\n\nexport type FileProgressStatus = 'pending' | 'uploading' | 'complete' | 'error' | 'cancelled';\n\n/**\n * AI metadata generation phase for a single row, tracked independently from the\n * upload status: a metadata failure never turns a successful upload into an error.\n * `undefined` means the row has no metadata phase at all, i.e. AI metadata is\n * disabled — non-images do get a phase and come back `skipped` from the server.\n */\nexport type FileMetadataStatus = 'generating' | 'generated' | 'skipped' | 'failed';\n\nexport type FileMetadataResultStatus = Exclude<FileMetadataStatus, 'generating'>;\n\nexport interface FileProgress {\n name: string;\n index: number;\n status: FileProgressStatus;\n size: number;\n uploadedBytes: number;\n file?: File;\n /** When the server confirmed the upload. Set with the row's asset. */\n completedAt?: number;\n error?: string;\n metadataStatus?: FileMetadataStatus;\n}\n\nexport interface UploadProgressState {\n isVisible: boolean;\n isMinimized: boolean;\n totalFiles: number;\n files: FileProgress[];\n errors: FileUploadError[];\n uploadId: number;\n /**\n * Whether this batch reports transferred bytes at all. The direct-file flow streams\n * byte counts from XHR; the URL flow leaves the fetch to the server and gets none back\n * so its byte-weighted aggregate is stuck at 0%.\n */\n reportsByteProgress: boolean;\n}\n\nexport interface RootState {\n uploadProgress: UploadProgressState;\n}\n\nconst initialState: UploadProgressState = {\n isVisible: false,\n isMinimized: false,\n totalFiles: 0,\n files: [],\n errors: [],\n uploadId: 0,\n reportsByteProgress: false,\n};\n\nconst uploadProgressSlice = createSlice({\n name: 'uploadProgress',\n initialState,\n reducers: {\n openUploadProgress(\n state,\n action: PayloadAction<{\n totalFiles: number;\n fileNames: string[];\n fileSizes?: number[];\n }>\n ) {\n state.isVisible = true;\n state.isMinimized = false;\n\n // Create pending files for upload\n const pendingFiles: FileProgress[] = action.payload.fileNames.map((name, index) => ({\n name,\n index,\n status: 'pending' as FileProgressStatus,\n size: action.payload.fileSizes?.[index] ?? 0,\n uploadedBytes: 0,\n }));\n\n state.files = pendingFiles;\n state.totalFiles = action.payload.totalFiles;\n state.errors = [];\n state.uploadId += 1;\n // Known sizes == the direct-file flow == the flow that streams bytes.\n state.reportsByteProgress = action.payload.fileSizes !== undefined;\n },\n /**\n * Rows for a second drop, joined to the batch already uploading. Indices\n * continue from the current length so the earlier drop's in-flight uploads\n * keep addressing their own rows.\n */\n appendUploadFiles(\n state,\n action: PayloadAction<{\n uploadId: number;\n fileNames: string[];\n fileSizes?: number[];\n }>\n ) {\n if (action.payload.uploadId !== state.uploadId) {\n return;\n }\n\n const offset = state.files.length;\n\n state.files.push(\n ...action.payload.fileNames.map((name, i) => ({\n name,\n index: offset + i,\n status: 'pending' as FileProgressStatus,\n size: action.payload.fileSizes?.[i] ?? 0,\n uploadedBytes: 0,\n }))\n );\n\n state.totalFiles += action.payload.fileNames.length;\n // The dialog may have been dismissed while the first drop was running.\n state.isVisible = true;\n state.isMinimized = false;\n },\n setFileUploading(\n state,\n action: PayloadAction<{ name: string; index: number; size: number; uploadId: number }>\n ) {\n const { index, size, uploadId } = action.payload;\n if (uploadId !== state.uploadId) {\n return;\n }\n if (state.files[index]) {\n state.files[index].status = 'uploading';\n state.files[index].size = size;\n }\n },\n setFileProgress(\n state,\n action: PayloadAction<{ index: number; bytes: number; uploadId: number }>\n ) {\n const { index, bytes, uploadId } = action.payload;\n if (uploadId !== state.uploadId) {\n return;\n }\n const file = state.files[index];\n if (file) {\n // Clamp to the known file size so the aggregate can never exceed 100%.\n file.uploadedBytes = Math.min(bytes, file.size);\n }\n },\n setFileComplete(\n state,\n action: PayloadAction<{ index: number; file: File; uploadId: number; completedAt: number }>\n ) {\n const { index, file, uploadId, completedAt } = action.payload;\n if (uploadId !== state.uploadId) {\n return;\n }\n if (state.files[index]) {\n state.files[index].status = 'complete';\n state.files[index].file = file;\n state.files[index].completedAt = completedAt;\n // Reflect completion in the aggregate even if the final progress event was throttled.\n state.files[index].uploadedBytes = state.files[index].size;\n }\n },\n setFileError(\n state,\n action: PayloadAction<{ index: number; name: string; message: string; uploadId: number }>\n ) {\n const { index, name, message, uploadId } = action.payload;\n if (uploadId !== state.uploadId) {\n return;\n }\n if (state.files[index]) {\n state.files[index].status = 'error';\n state.files[index].error = message;\n }\n state.errors = [...state.errors, { name, message }];\n },\n /**\n * Metadata generation started for a row.\n *\n * Metadata requests are fired-and-forgotten, so their callbacks can land after\n * a new batch has replaced `files` and reused the same row indices. Every\n * metadata payload therefore carries the `uploadId` it was fired for, and stale\n * ones are dropped instead of writing onto another batch's row.\n */\n setFileMetadataGenerating(state, action: PayloadAction<{ index: number; uploadId: number }>) {\n const { index, uploadId } = action.payload;\n if (uploadId !== state.uploadId) {\n return;\n }\n if (state.files[index]) {\n state.files[index].metadataStatus = 'generating';\n }\n },\n /**\n * Terminal metadata outcome for a row. Never touches `status`/`error`/`errors`:\n * the upload itself already succeeded regardless of how generation went.\n */\n setFileMetadataResult(\n state,\n action: PayloadAction<{\n index: number;\n uploadId: number;\n status: FileMetadataResultStatus;\n }>\n ) {\n const { index, uploadId, status } = action.payload;\n if (uploadId !== state.uploadId) {\n return;\n }\n if (state.files[index]) {\n state.files[index].metadataStatus = status;\n }\n },\n addUploadErrors(state, action: PayloadAction<FileUploadError[]>) {\n state.errors = [...state.errors, ...action.payload];\n },\n closeUploadProgress(state) {\n state.isVisible = false;\n state.isMinimized = false;\n state.totalFiles = 0;\n state.files = [];\n state.errors = [];\n state.reportsByteProgress = false;\n },\n toggleMinimize(state) {\n state.isMinimized = !state.isMinimized;\n },\n cancelUpload(state) {\n // Mark all pending and uploading files as cancelled\n state.files = state.files.map((file) => {\n if (file.status === 'pending' || file.status === 'uploading') {\n return { ...file, status: 'cancelled' as FileProgressStatus };\n }\n return file;\n });\n },\n setUploadFailed(state, action: PayloadAction<{ message: string }>) {\n // Mark all pending and uploading files as errored when a catastrophic failure occurs\n state.files = state.files.map((file) => {\n if (file.status === 'pending' || file.status === 'uploading') {\n return {\n ...file,\n status: 'error' as FileProgressStatus,\n error: action.payload.message,\n };\n }\n return file;\n });\n state.errors = [...state.errors, { name: 'Upload Error', message: action.payload.message }];\n },\n retryCancelledFiles(state) {\n // Reset all cancelled files back to pending for retry\n state.files = state.files.map((file) => {\n if (file.status === 'cancelled') {\n return {\n ...file,\n status: 'pending' as FileProgressStatus,\n uploadedBytes: 0,\n // A retried row goes through the metadata phase again from scratch.\n metadataStatus: undefined,\n };\n }\n return file;\n });\n },\n },\n});\n\n/**\n * Assets whose own upload has already finished, newest batch only.\n *\n * The list behind the upload dialog uses these to show each asset the moment it\n * lands, instead of waiting for the batch mutation to invalidate the cache. Rows\n * only carry a `file` once the server has created it, so every entry here is a\n * real, persisted asset.\n */\nconst NO_FILES: FileProgress[] = [];\n\n/** An uploaded asset, with the moment the server confirmed it. */\nexport interface CompletedUpload {\n asset: File;\n completedAt: number;\n}\n\nexport const selectCompletedUploads = createSelector(\n // The plugin registers this slice in `register()`, so a host that has not\n // reached that point has no upload state — and therefore nothing to place.\n (state: Partial<RootState>) => state.uploadProgress?.files ?? NO_FILES,\n (files): CompletedUpload[] =>\n files.reduce<CompletedUpload[]>((completed, row) => {\n if (row.status === 'complete' && row.file && row.completedAt !== undefined) {\n completed.push({ asset: row.file, completedAt: row.completedAt });\n }\n\n return completed;\n }, [])\n);\n\n/**\n * Byte-weighted aggregate progress across the whole batch: `sum(uploadedBytes) / sum(size)`.\n *\n * Falls back to count-based progress (settled files / total files) when all sizes are\n * zero — e.g. URL-flow rows where the file size is unknown up front.\n */\nexport const selectAggregateProgress = createSelector(\n (state: RootState) => state.uploadProgress.files,\n (files): number => {\n if (files.length === 0) return 0;\n\n const totalSize = files.reduce((sum, f) => sum + f.size, 0);\n\n if (totalSize === 0) {\n const settled = files.filter(\n (f) => f.status === 'complete' || f.status === 'error' || f.status === 'cancelled'\n ).length;\n return Math.round((settled / files.length) * 100);\n }\n\n const uploadedBytes = files.reduce((sum, f) => sum + f.uploadedBytes, 0);\n return Math.round((uploadedBytes / totalSize) * 100);\n }\n);\n\n/**\n * Whether to show byte-weighted `selectAggregateProgress` rather than count-based progress.\n * A batch that never reports bytes stays pinned at 0%, so the header falls back to\n * `selectCountBasedProgress`.\n *\n * Checks the rows too, not just the flag: an in-flight row reporting bytes proves the batch\n * can, so byte-weighting takes over once the URL flow starts emitting progress. Only\n * `uploading` rows count — completion backfills `uploadedBytes` to the file size, so letting\n * settled rows qualify would switch a multi-URL batch to byte-weighting mid-upload over only\n * the sizes known so far (transiently 100% after the first file).\n */\nexport const selectReportsByteProgress = createSelector(\n (state: RootState) => state.uploadProgress.reportsByteProgress,\n (state: RootState) => state.uploadProgress.files,\n (reportsByteProgress, files): boolean =>\n reportsByteProgress || files.some((f) => f.status === 'uploading' && f.uploadedBytes > 0)\n);\n\n/**\n * Count-based progress (settled / total rows) for a batch that reports no transferred bytes\n * (the URL flow). Used by the header whenever `selectReportsByteProgress` is false.\n *\n * Never byte-weights, so a multi-URL batch climbs 33 → 67 → 100 as each file lands instead\n * of jumping to a transient 100% after the first.\n *\n * Returns `null` until a row settles — 0% is the frozen signal this flow exists to remove\n * (a lone URL upload would otherwise read \"Uploading 1 item (0%)\" throughout), so the header\n * stays indeterminate until there's real progress.\n */\nexport const selectCountBasedProgress = createSelector(\n (state: RootState) => state.uploadProgress.files,\n (files): number | null => {\n const settled = files.filter(\n (f) => f.status === 'complete' || f.status === 'error' || f.status === 'cancelled'\n ).length;\n\n if (settled === 0) return null;\n\n return Math.round((settled / files.length) * 100);\n }\n);\n\n/**\n * Count-based metadata progress across the whole batch: `settled / expected`.\n *\n * Count-based rather than byte-weighted because the generation endpoint answers in one\n * go — there is no intermediate progress to weight.\n *\n * The denominator is every row *expected* to enter the phase, not just those that\n * already have. Generation is fired per file as each upload finishes, so counting only\n * started rows made the denominator grow mid-batch and the percentage jump backwards\n * (1/1 = 100%, then a second row starts and it drops to 1/2 = 50%), which flickered the\n * header subtitle on and off once per file.\n *\n * \"Expected\" is derived from upload outcome rather than from the AI-metadata flag:\n * - a row still uploading may yet enter the phase, so it counts toward the total;\n * - an errored or cancelled row never will, so it must not — otherwise a batch with\n * one failure could never reach 100%.\n *\n * Returns `null` when nothing has entered the phase and nothing is pending — i.e. AI\n * metadata is off, or the batch is done — so the header can hide the subtitle.\n */\nexport const selectMetadataProgress = createSelector(\n (state: RootState) => state.uploadProgress.files,\n (files): number | null => {\n const started = files.filter((f) => f.metadataStatus !== undefined);\n\n // Nothing has entered the phase yet: with no started row there is no evidence AI\n // metadata is even enabled, so stay silent rather than showing a premature 0%.\n if (started.length === 0) return null;\n\n // Rows whose upload is still in flight are counted in the denominator so the\n // percentage only ever climbs as the batch progresses.\n const pending = files.filter(\n (f) => f.metadataStatus === undefined && (f.status === 'pending' || f.status === 'uploading')\n ).length;\n\n const expected = started.length + pending;\n const settled = started.filter((f) => f.metadataStatus !== 'generating').length;\n\n return Math.round((settled / expected) * 100);\n }\n);\n\n/**\n * Whether the metadata phase is still ongoing for the batch as a whole.\n *\n * The header uses this — not `selectMetadataProgress === 100` — to decide when to drop\n * the subtitle. Two distinct reasons the phase can be unfinished:\n * - a row is generating right now;\n * - no row is generating this instant, but rows are still uploading and will enter the\n * phase when they finish.\n *\n * The second clause is what keeps the subtitle stable. Generation for file N typically\n * finishes before file N+1 finishes uploading, so a naive \"is anything generating?\"\n * check goes false between every pair of files and blinks the subtitle out and back in.\n */\nexport const selectIsGeneratingMetadata = createSelector(\n (state: RootState) => state.uploadProgress.files,\n (files): boolean => {\n if (files.some((f) => f.metadataStatus === 'generating')) return true;\n\n // Only bridge the gap if some row has already entered the phase — otherwise a batch\n // with AI metadata disabled would report the phase as ongoing for its whole upload.\n const hasStarted = files.some((f) => f.metadataStatus !== undefined);\n\n return hasStarted && files.some((f) => f.status === 'pending' || f.status === 'uploading');\n }\n);\n\n/**\n * How many rows reached each terminal metadata outcome, for the header's completion\n * message. `null` while the phase is unfinished — or was never entered at all — so the\n * caller has a single check for \"there is no settled outcome to report yet\".\n *\n * Counts are reported separately rather than collapsed into one total because they are\n * not interchangeable: only `generated` rows actually had metadata written, so only that\n * count can be claimed as a success. A batch of non-images settles entirely on `skipped`,\n * where \"generated on 0 files\" would be worse than saying nothing.\n */\nexport const selectMetadataOutcome = createSelector(\n (state: RootState) => state.uploadProgress.files,\n (files): { generated: number; skipped: number; failed: number } | null => {\n const started = files.filter((f) => f.metadataStatus !== undefined);\n\n if (started.length === 0) return null;\n\n // Mirrors `selectIsGeneratingMetadata`: the phase isn't over while a row is\n // generating, nor while a row is still uploading and has yet to enter it.\n const isOngoing =\n started.some((f) => f.metadataStatus === 'generating') ||\n files.some((f) => f.status === 'pending' || f.status === 'uploading');\n\n if (isOngoing) return null;\n\n return {\n generated: started.filter((f) => f.metadataStatus === 'generated').length,\n skipped: started.filter((f) => f.metadataStatus === 'skipped').length,\n failed: started.filter((f) => f.metadataStatus === 'failed').length,\n };\n }\n);\n\nexport const {\n openUploadProgress,\n appendUploadFiles,\n setFileUploading,\n setFileProgress,\n setFileComplete,\n setFileError,\n setFileMetadataGenerating,\n setFileMetadataResult,\n addUploadErrors,\n closeUploadProgress,\n toggleMinimize,\n cancelUpload,\n setUploadFailed,\n retryCancelledFiles,\n} = uploadProgressSlice.actions;\n\n/**\n * The merge/reset switch. Errored and cancelled rows count as settled, so a\n * batch that finished badly is still replaced by the next drop.\n */\nexport const isUploadInFlight = (files: ReadonlyArray<{ status: FileProgressStatus }>): boolean =>\n files.some((f) => f.status === 'pending' || f.status === 'uploading');\n\nexport const uploadProgressReducer = uploadProgressSlice.reducer;\n"],"names":["initialState","isVisible","isMinimized","totalFiles","files","errors","uploadId","reportsByteProgress","uploadProgressSlice","createSlice","name","reducers","openUploadProgress","state","action","pendingFiles","payload","fileNames","map","index","status","size","fileSizes","uploadedBytes","undefined","appendUploadFiles","offset","length","push","i","setFileUploading","setFileProgress","bytes","file","Math","min","setFileComplete","completedAt","setFileError","message","error","setFileMetadataGenerating","metadataStatus","setFileMetadataResult","addUploadErrors","closeUploadProgress","toggleMinimize","cancelUpload","setUploadFailed","retryCancelledFiles","NO_FILES","selectCompletedUploads","createSelector","uploadProgress","reduce","completed","row","asset","selectAggregateProgress","totalSize","sum","f","settled","filter","round","selectReportsByteProgress","some","selectCountBasedProgress","selectMetadataProgress","started","pending","expected","selectIsGeneratingMetadata","hasStarted","selectMetadataOutcome","isOngoing","generated","skipped","failed","actions","isUploadInFlight","uploadProgressReducer","reducer"],"mappings":"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"}
1
+ {"version":3,"file":"uploadProgress.js","sources":["../../../../admin/src/future/store/uploadProgress.ts"],"sourcesContent":["import { createSelector, createSlice, type PayloadAction } from '@reduxjs/toolkit';\n\nimport type { File } from '../../../../shared/contracts/files';\n\nexport interface FileUploadError {\n name: string;\n message: string;\n}\n\nexport type FileProgressStatus = 'pending' | 'uploading' | 'complete' | 'error' | 'cancelled';\n\n/**\n * AI metadata generation phase for a single row, tracked independently from the\n * upload status: a metadata failure never turns a successful upload into an error.\n * `undefined` means the row has no metadata phase at all, i.e. AI metadata is\n * disabled — non-images do get a phase and come back `skipped` from the server.\n */\nexport type FileMetadataStatus = 'generating' | 'generated' | 'skipped' | 'failed';\n\nexport type FileMetadataResultStatus = Exclude<FileMetadataStatus, 'generating'>;\n\nexport interface FileProgress {\n name: string;\n index: number;\n status: FileProgressStatus;\n size: number;\n uploadedBytes: number;\n file?: File;\n /** When the server confirmed the upload. Set with the row's asset. */\n completedAt?: number;\n error?: string;\n metadataStatus?: FileMetadataStatus;\n}\n\nexport interface UploadProgressState {\n isVisible: boolean;\n isMinimized: boolean;\n totalFiles: number;\n files: FileProgress[];\n errors: FileUploadError[];\n uploadId: number;\n /**\n * Whether every row's size was known when the batch opened (the direct-file flow), so\n * the byte-weighted aggregate is meaningful. The URL flow learns sizes one row at a time.\n */\n reportsByteProgress: boolean;\n}\n\nexport interface RootState {\n uploadProgress: UploadProgressState;\n}\n\nconst initialState: UploadProgressState = {\n isVisible: false,\n isMinimized: false,\n totalFiles: 0,\n files: [],\n errors: [],\n uploadId: 0,\n reportsByteProgress: false,\n};\n\nconst uploadProgressSlice = createSlice({\n name: 'uploadProgress',\n initialState,\n reducers: {\n openUploadProgress(\n state,\n action: PayloadAction<{\n totalFiles: number;\n fileNames: string[];\n fileSizes?: number[];\n }>\n ) {\n state.isVisible = true;\n state.isMinimized = false;\n\n // Create pending files for upload\n const pendingFiles: FileProgress[] = action.payload.fileNames.map((name, index) => ({\n name,\n index,\n status: 'pending' as FileProgressStatus,\n size: action.payload.fileSizes?.[index] ?? 0,\n uploadedBytes: 0,\n }));\n\n state.files = pendingFiles;\n state.totalFiles = action.payload.totalFiles;\n state.errors = [];\n state.uploadId += 1;\n // Known sizes == the direct-file flow == the flow that streams bytes.\n state.reportsByteProgress = action.payload.fileSizes !== undefined;\n },\n /**\n * Rows for a second drop, joined to the batch already uploading. Indices\n * continue from the current length so the earlier drop's in-flight uploads\n * keep addressing their own rows.\n */\n appendUploadFiles(\n state,\n action: PayloadAction<{\n uploadId: number;\n fileNames: string[];\n fileSizes?: number[];\n }>\n ) {\n if (action.payload.uploadId !== state.uploadId) {\n return;\n }\n\n const offset = state.files.length;\n\n state.files.push(\n ...action.payload.fileNames.map((name, i) => ({\n name,\n index: offset + i,\n status: 'pending' as FileProgressStatus,\n size: action.payload.fileSizes?.[i] ?? 0,\n uploadedBytes: 0,\n }))\n );\n\n state.totalFiles += action.payload.fileNames.length;\n // The dialog may have been dismissed while the first drop was running.\n state.isVisible = true;\n state.isMinimized = false;\n },\n setFileUploading(\n state,\n action: PayloadAction<{ name: string; index: number; size: number; uploadId: number }>\n ) {\n const { index, size, uploadId } = action.payload;\n if (uploadId !== state.uploadId) {\n return;\n }\n if (state.files[index]) {\n state.files[index].status = 'uploading';\n state.files[index].size = size;\n }\n },\n /**\n * `size` is optional and only sent by the URL flow, which learns the size from the\n * same server event that carries the first byte count — the row was created with\n * `size: 0`, and bytes clamped against a zero size would all read as zero. The\n * direct-file flow already knows the size up front and omits it.\n */\n setFileProgress(\n state,\n action: PayloadAction<{ index: number; bytes: number; uploadId: number; size?: number }>\n ) {\n const { index, bytes, size, uploadId } = action.payload;\n if (uploadId !== state.uploadId) {\n return;\n }\n const file = state.files[index];\n if (file) {\n if (size !== undefined && file.size === 0) {\n file.size = size;\n }\n // Clamp to the known file size so the aggregate can never exceed 100%.\n file.uploadedBytes = Math.min(bytes, file.size);\n }\n },\n setFileComplete(\n state,\n action: PayloadAction<{ index: number; file: File; uploadId: number; completedAt: number }>\n ) {\n const { index, file, uploadId, completedAt } = action.payload;\n if (uploadId !== state.uploadId) {\n return;\n }\n if (state.files[index]) {\n state.files[index].status = 'complete';\n state.files[index].file = file;\n state.files[index].completedAt = completedAt;\n // Reflect completion in the aggregate even if the final progress event was throttled.\n state.files[index].uploadedBytes = state.files[index].size;\n }\n },\n setFileError(\n state,\n action: PayloadAction<{ index: number; name: string; message: string; uploadId: number }>\n ) {\n const { index, name, message, uploadId } = action.payload;\n if (uploadId !== state.uploadId) {\n return;\n }\n if (state.files[index]) {\n state.files[index].status = 'error';\n state.files[index].error = message;\n }\n state.errors = [...state.errors, { name, message }];\n },\n /**\n * Metadata generation started for a row.\n *\n * Metadata requests are fired-and-forgotten, so their callbacks can land after\n * a new batch has replaced `files` and reused the same row indices. Every\n * metadata payload therefore carries the `uploadId` it was fired for, and stale\n * ones are dropped instead of writing onto another batch's row.\n */\n setFileMetadataGenerating(state, action: PayloadAction<{ index: number; uploadId: number }>) {\n const { index, uploadId } = action.payload;\n if (uploadId !== state.uploadId) {\n return;\n }\n if (state.files[index]) {\n state.files[index].metadataStatus = 'generating';\n }\n },\n /**\n * Terminal metadata outcome for a row. Never touches `status`/`error`/`errors`:\n * the upload itself already succeeded regardless of how generation went.\n */\n setFileMetadataResult(\n state,\n action: PayloadAction<{\n index: number;\n uploadId: number;\n status: FileMetadataResultStatus;\n }>\n ) {\n const { index, uploadId, status } = action.payload;\n if (uploadId !== state.uploadId) {\n return;\n }\n if (state.files[index]) {\n state.files[index].metadataStatus = status;\n }\n },\n addUploadErrors(state, action: PayloadAction<FileUploadError[]>) {\n state.errors = [...state.errors, ...action.payload];\n },\n closeUploadProgress(state) {\n state.isVisible = false;\n state.isMinimized = false;\n state.totalFiles = 0;\n state.files = [];\n state.errors = [];\n state.reportsByteProgress = false;\n },\n toggleMinimize(state) {\n state.isMinimized = !state.isMinimized;\n },\n cancelUpload(state) {\n // Mark all pending and uploading files as cancelled\n state.files = state.files.map((file) => {\n if (file.status === 'pending' || file.status === 'uploading') {\n return { ...file, status: 'cancelled' as FileProgressStatus };\n }\n return file;\n });\n },\n setUploadFailed(state, action: PayloadAction<{ message: string }>) {\n // Mark all pending and uploading files as errored when a catastrophic failure occurs\n state.files = state.files.map((file) => {\n if (file.status === 'pending' || file.status === 'uploading') {\n return {\n ...file,\n status: 'error' as FileProgressStatus,\n error: action.payload.message,\n };\n }\n return file;\n });\n state.errors = [...state.errors, { name: 'Upload Error', message: action.payload.message }];\n },\n retryCancelledFiles(state) {\n // Reset all cancelled files back to pending for retry\n state.files = state.files.map((file) => {\n if (file.status === 'cancelled') {\n return {\n ...file,\n status: 'pending' as FileProgressStatus,\n uploadedBytes: 0,\n // A retried row goes through the metadata phase again from scratch.\n metadataStatus: undefined,\n };\n }\n return file;\n });\n },\n },\n});\n\n/**\n * Assets whose own upload has already finished, newest batch only.\n *\n * The list behind the upload dialog uses these to show each asset the moment it\n * lands, instead of waiting for the batch mutation to invalidate the cache. Rows\n * only carry a `file` once the server has created it, so every entry here is a\n * real, persisted asset.\n */\nconst NO_FILES: FileProgress[] = [];\n\n/** An uploaded asset, with the moment the server confirmed it. */\nexport interface CompletedUpload {\n asset: File;\n completedAt: number;\n}\n\nexport const selectCompletedUploads = createSelector(\n // The plugin registers this slice in `register()`, so a host that has not\n // reached that point has no upload state — and therefore nothing to place.\n (state: Partial<RootState>) => state.uploadProgress?.files ?? NO_FILES,\n (files): CompletedUpload[] =>\n files.reduce<CompletedUpload[]>((completed, row) => {\n if (row.status === 'complete' && row.file && row.completedAt !== undefined) {\n completed.push({ asset: row.file, completedAt: row.completedAt });\n }\n\n return completed;\n }, [])\n);\n\n/**\n * Byte-weighted aggregate progress across the whole batch: `sum(uploadedBytes) / sum(size)`.\n *\n * Falls back to count-based progress (settled files / total files) when all sizes are\n * zero — e.g. URL-flow rows where the file size is unknown up front.\n */\nexport const selectAggregateProgress = createSelector(\n (state: RootState) => state.uploadProgress.files,\n (files): number => {\n if (files.length === 0) return 0;\n\n const totalSize = files.reduce((sum, f) => sum + f.size, 0);\n\n if (totalSize === 0) {\n const settled = files.filter(\n (f) => f.status === 'complete' || f.status === 'error' || f.status === 'cancelled'\n ).length;\n return Math.round((settled / files.length) * 100);\n }\n\n const uploadedBytes = files.reduce((sum, f) => sum + f.uploadedBytes, 0);\n return Math.round((uploadedBytes / totalSize) * 100);\n }\n);\n\n/**\n * Whether to show byte-weighted `selectAggregateProgress` rather than `selectCountBasedProgress`.\n *\n * Only the flag: byte-weighting needs every size up front. A URL batch learns sizes one row\n * at a time, so weighting over the sizes known so far makes the header jump backwards at\n * every file boundary.\n */\nexport const selectReportsByteProgress = createSelector(\n (state: RootState) => state.uploadProgress.reportsByteProgress,\n (reportsByteProgress): boolean => reportsByteProgress\n);\n\n/**\n * Row-weighted progress for a batch whose sizes are not known up front (the URL flow).\n * Each row is worth `1 / total`: settled rows count fully and an in-flight row contributes\n * `uploadedBytes / size` once it reports bytes, so a multi-URL batch climbs smoothly to\n * 33 → 67 → 100 and never moves backwards.\n *\n * Returns `null` until something has moved — 0% is the frozen signal this flow exists to\n * remove, so the header stays indeterminate until there's real progress.\n */\nexport const selectCountBasedProgress = createSelector(\n (state: RootState) => state.uploadProgress.files,\n (files): number | null => {\n const done = files.reduce((sum, f) => {\n if (f.status === 'complete' || f.status === 'error' || f.status === 'cancelled') {\n return sum + 1;\n }\n return f.size > 0 ? sum + Math.min(f.uploadedBytes / f.size, 1) : sum;\n }, 0);\n\n if (done === 0) {\n return null;\n }\n\n return Math.round((done / files.length) * 100);\n }\n);\n\n/**\n * Count-based metadata progress across the whole batch: `settled / expected`.\n *\n * Count-based rather than byte-weighted because the generation endpoint answers in one\n * go — there is no intermediate progress to weight.\n *\n * The denominator is every row *expected* to enter the phase, not just those that\n * already have. Generation is fired per file as each upload finishes, so counting only\n * started rows made the denominator grow mid-batch and the percentage jump backwards\n * (1/1 = 100%, then a second row starts and it drops to 1/2 = 50%), which flickered the\n * header subtitle on and off once per file.\n *\n * \"Expected\" is derived from upload outcome rather than from the AI-metadata flag:\n * - a row still uploading may yet enter the phase, so it counts toward the total;\n * - an errored or cancelled row never will, so it must not — otherwise a batch with\n * one failure could never reach 100%.\n *\n * Returns `null` when nothing has entered the phase and nothing is pending — i.e. AI\n * metadata is off, or the batch is done — so the header can hide the subtitle.\n */\nexport const selectMetadataProgress = createSelector(\n (state: RootState) => state.uploadProgress.files,\n (files): number | null => {\n const started = files.filter((f) => f.metadataStatus !== undefined);\n\n // Nothing has entered the phase yet: with no started row there is no evidence AI\n // metadata is even enabled, so stay silent rather than showing a premature 0%.\n if (started.length === 0) return null;\n\n // Rows whose upload is still in flight are counted in the denominator so the\n // percentage only ever climbs as the batch progresses.\n const pending = files.filter(\n (f) => f.metadataStatus === undefined && (f.status === 'pending' || f.status === 'uploading')\n ).length;\n\n const expected = started.length + pending;\n const settled = started.filter((f) => f.metadataStatus !== 'generating').length;\n\n return Math.round((settled / expected) * 100);\n }\n);\n\n/**\n * Whether the metadata phase is still ongoing for the batch as a whole.\n *\n * The header uses this — not `selectMetadataProgress === 100` — to decide when to drop\n * the subtitle. Two distinct reasons the phase can be unfinished:\n * - a row is generating right now;\n * - no row is generating this instant, but rows are still uploading and will enter the\n * phase when they finish.\n *\n * The second clause is what keeps the subtitle stable. Generation for file N typically\n * finishes before file N+1 finishes uploading, so a naive \"is anything generating?\"\n * check goes false between every pair of files and blinks the subtitle out and back in.\n */\nexport const selectIsGeneratingMetadata = createSelector(\n (state: RootState) => state.uploadProgress.files,\n (files): boolean => {\n if (files.some((f) => f.metadataStatus === 'generating')) return true;\n\n // Only bridge the gap if some row has already entered the phase — otherwise a batch\n // with AI metadata disabled would report the phase as ongoing for its whole upload.\n const hasStarted = files.some((f) => f.metadataStatus !== undefined);\n\n return hasStarted && files.some((f) => f.status === 'pending' || f.status === 'uploading');\n }\n);\n\n/**\n * How many rows reached each terminal metadata outcome, for the header's completion\n * message. `null` while the phase is unfinished — or was never entered at all — so the\n * caller has a single check for \"there is no settled outcome to report yet\".\n *\n * Counts are reported separately rather than collapsed into one total because they are\n * not interchangeable: only `generated` rows actually had metadata written, so only that\n * count can be claimed as a success. A batch of non-images settles entirely on `skipped`,\n * where \"generated on 0 files\" would be worse than saying nothing.\n */\nexport const selectMetadataOutcome = createSelector(\n (state: RootState) => state.uploadProgress.files,\n (files): { generated: number; skipped: number; failed: number } | null => {\n const started = files.filter((f) => f.metadataStatus !== undefined);\n\n if (started.length === 0) return null;\n\n // Mirrors `selectIsGeneratingMetadata`: the phase isn't over while a row is\n // generating, nor while a row is still uploading and has yet to enter it.\n const isOngoing =\n started.some((f) => f.metadataStatus === 'generating') ||\n files.some((f) => f.status === 'pending' || f.status === 'uploading');\n\n if (isOngoing) return null;\n\n return {\n generated: started.filter((f) => f.metadataStatus === 'generated').length,\n skipped: started.filter((f) => f.metadataStatus === 'skipped').length,\n failed: started.filter((f) => f.metadataStatus === 'failed').length,\n };\n }\n);\n\nexport const {\n openUploadProgress,\n appendUploadFiles,\n setFileUploading,\n setFileProgress,\n setFileComplete,\n setFileError,\n setFileMetadataGenerating,\n setFileMetadataResult,\n addUploadErrors,\n closeUploadProgress,\n toggleMinimize,\n cancelUpload,\n setUploadFailed,\n retryCancelledFiles,\n} = uploadProgressSlice.actions;\n\n/**\n * The merge/reset switch. Errored and cancelled rows count as settled, so a\n * batch that finished badly is still replaced by the next drop.\n */\nexport const isUploadInFlight = (files: ReadonlyArray<{ status: FileProgressStatus }>): boolean =>\n files.some((f) => f.status === 'pending' || f.status === 'uploading');\n\nexport const uploadProgressReducer = uploadProgressSlice.reducer;\n"],"names":["initialState","isVisible","isMinimized","totalFiles","files","errors","uploadId","reportsByteProgress","uploadProgressSlice","createSlice","name","reducers","openUploadProgress","state","action","pendingFiles","payload","fileNames","map","index","status","size","fileSizes","uploadedBytes","undefined","appendUploadFiles","offset","length","push","i","setFileUploading","setFileProgress","bytes","file","Math","min","setFileComplete","completedAt","setFileError","message","error","setFileMetadataGenerating","metadataStatus","setFileMetadataResult","addUploadErrors","closeUploadProgress","toggleMinimize","cancelUpload","setUploadFailed","retryCancelledFiles","NO_FILES","selectCompletedUploads","createSelector","uploadProgress","reduce","completed","row","asset","selectAggregateProgress","totalSize","sum","f","settled","filter","round","selectReportsByteProgress","selectCountBasedProgress","done","selectMetadataProgress","started","pending","expected","selectIsGeneratingMetadata","some","hasStarted","selectMetadataOutcome","isOngoing","generated","skipped","failed","actions","isUploadInFlight","uploadProgressReducer","reducer"],"mappings":"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"}
@@ -62,13 +62,21 @@ const uploadProgressSlice = createSlice({
62
62
  state.files[index].size = size;
63
63
  }
64
64
  },
65
- setFileProgress (state, action) {
66
- const { index, bytes, uploadId } = action.payload;
65
+ /**
66
+ * `size` is optional and only sent by the URL flow, which learns the size from the
67
+ * same server event that carries the first byte count — the row was created with
68
+ * `size: 0`, and bytes clamped against a zero size would all read as zero. The
69
+ * direct-file flow already knows the size up front and omits it.
70
+ */ setFileProgress (state, action) {
71
+ const { index, bytes, size, uploadId } = action.payload;
67
72
  if (uploadId !== state.uploadId) {
68
73
  return;
69
74
  }
70
75
  const file = state.files[index];
71
76
  if (file) {
77
+ if (size !== undefined && file.size === 0) {
78
+ file.size = size;
79
+ }
72
80
  // Clamp to the known file size so the aggregate can never exceed 100%.
73
81
  file.uploadedBytes = Math.min(bytes, file.size);
74
82
  }
@@ -232,30 +240,31 @@ const selectCompletedUploads = createSelector(// The plugin registers this slice
232
240
  return Math.round(uploadedBytes / totalSize * 100);
233
241
  });
234
242
  /**
235
- * Whether to show byte-weighted `selectAggregateProgress` rather than count-based progress.
236
- * A batch that never reports bytes stays pinned at 0%, so the header falls back to
237
- * `selectCountBasedProgress`.
243
+ * Whether to show byte-weighted `selectAggregateProgress` rather than `selectCountBasedProgress`.
238
244
  *
239
- * Checks the rows too, not just the flag: an in-flight row reporting bytes proves the batch
240
- * can, so byte-weighting takes over once the URL flow starts emitting progress. Only
241
- * `uploading` rows count — completion backfills `uploadedBytes` to the file size, so letting
242
- * settled rows qualify would switch a multi-URL batch to byte-weighting mid-upload over only
243
- * the sizes known so far (transiently 100% after the first file).
244
- */ const selectReportsByteProgress = createSelector((state)=>state.uploadProgress.reportsByteProgress, (state)=>state.uploadProgress.files, (reportsByteProgress, files)=>reportsByteProgress || files.some((f)=>f.status === 'uploading' && f.uploadedBytes > 0));
245
+ * Only the flag: byte-weighting needs every size up front. A URL batch learns sizes one row
246
+ * at a time, so weighting over the sizes known so far makes the header jump backwards at
247
+ * every file boundary.
248
+ */ const selectReportsByteProgress = createSelector((state)=>state.uploadProgress.reportsByteProgress, (reportsByteProgress)=>reportsByteProgress);
245
249
  /**
246
- * Count-based progress (settled / total rows) for a batch that reports no transferred bytes
247
- * (the URL flow). Used by the header whenever `selectReportsByteProgress` is false.
248
- *
249
- * Never byte-weights, so a multi-URL batch climbs 33 → 67 → 100 as each file lands instead
250
- * of jumping to a transient 100% after the first.
250
+ * Row-weighted progress for a batch whose sizes are not known up front (the URL flow).
251
+ * Each row is worth `1 / total`: settled rows count fully and an in-flight row contributes
252
+ * `uploadedBytes / size` once it reports bytes, so a multi-URL batch climbs smoothly to
253
+ * 33 → 67 → 100 and never moves backwards.
251
254
  *
252
- * Returns `null` until a row settles — 0% is the frozen signal this flow exists to remove
253
- * (a lone URL upload would otherwise read "Uploading 1 item (0%)" throughout), so the header
254
- * stays indeterminate until there's real progress.
255
+ * Returns `null` until something has moved — 0% is the frozen signal this flow exists to
256
+ * remove, so the header stays indeterminate until there's real progress.
255
257
  */ const selectCountBasedProgress = createSelector((state)=>state.uploadProgress.files, (files)=>{
256
- const settled = files.filter((f)=>f.status === 'complete' || f.status === 'error' || f.status === 'cancelled').length;
257
- if (settled === 0) return null;
258
- return Math.round(settled / files.length * 100);
258
+ const done = files.reduce((sum, f)=>{
259
+ if (f.status === 'complete' || f.status === 'error' || f.status === 'cancelled') {
260
+ return sum + 1;
261
+ }
262
+ return f.size > 0 ? sum + Math.min(f.uploadedBytes / f.size, 1) : sum;
263
+ }, 0);
264
+ if (done === 0) {
265
+ return null;
266
+ }
267
+ return Math.round(done / files.length * 100);
259
268
  });
260
269
  /**
261
270
  * Count-based metadata progress across the whole batch: `settled / expected`.
@@ -1 +1 @@
1
- {"version":3,"file":"uploadProgress.mjs","sources":["../../../../admin/src/future/store/uploadProgress.ts"],"sourcesContent":["import { createSelector, createSlice, type PayloadAction } from '@reduxjs/toolkit';\n\nimport type { File } from '../../../../shared/contracts/files';\n\nexport interface FileUploadError {\n name: string;\n message: string;\n}\n\nexport type FileProgressStatus = 'pending' | 'uploading' | 'complete' | 'error' | 'cancelled';\n\n/**\n * AI metadata generation phase for a single row, tracked independently from the\n * upload status: a metadata failure never turns a successful upload into an error.\n * `undefined` means the row has no metadata phase at all, i.e. AI metadata is\n * disabled — non-images do get a phase and come back `skipped` from the server.\n */\nexport type FileMetadataStatus = 'generating' | 'generated' | 'skipped' | 'failed';\n\nexport type FileMetadataResultStatus = Exclude<FileMetadataStatus, 'generating'>;\n\nexport interface FileProgress {\n name: string;\n index: number;\n status: FileProgressStatus;\n size: number;\n uploadedBytes: number;\n file?: File;\n /** When the server confirmed the upload. Set with the row's asset. */\n completedAt?: number;\n error?: string;\n metadataStatus?: FileMetadataStatus;\n}\n\nexport interface UploadProgressState {\n isVisible: boolean;\n isMinimized: boolean;\n totalFiles: number;\n files: FileProgress[];\n errors: FileUploadError[];\n uploadId: number;\n /**\n * Whether this batch reports transferred bytes at all. The direct-file flow streams\n * byte counts from XHR; the URL flow leaves the fetch to the server and gets none back\n * so its byte-weighted aggregate is stuck at 0%.\n */\n reportsByteProgress: boolean;\n}\n\nexport interface RootState {\n uploadProgress: UploadProgressState;\n}\n\nconst initialState: UploadProgressState = {\n isVisible: false,\n isMinimized: false,\n totalFiles: 0,\n files: [],\n errors: [],\n uploadId: 0,\n reportsByteProgress: false,\n};\n\nconst uploadProgressSlice = createSlice({\n name: 'uploadProgress',\n initialState,\n reducers: {\n openUploadProgress(\n state,\n action: PayloadAction<{\n totalFiles: number;\n fileNames: string[];\n fileSizes?: number[];\n }>\n ) {\n state.isVisible = true;\n state.isMinimized = false;\n\n // Create pending files for upload\n const pendingFiles: FileProgress[] = action.payload.fileNames.map((name, index) => ({\n name,\n index,\n status: 'pending' as FileProgressStatus,\n size: action.payload.fileSizes?.[index] ?? 0,\n uploadedBytes: 0,\n }));\n\n state.files = pendingFiles;\n state.totalFiles = action.payload.totalFiles;\n state.errors = [];\n state.uploadId += 1;\n // Known sizes == the direct-file flow == the flow that streams bytes.\n state.reportsByteProgress = action.payload.fileSizes !== undefined;\n },\n /**\n * Rows for a second drop, joined to the batch already uploading. Indices\n * continue from the current length so the earlier drop's in-flight uploads\n * keep addressing their own rows.\n */\n appendUploadFiles(\n state,\n action: PayloadAction<{\n uploadId: number;\n fileNames: string[];\n fileSizes?: number[];\n }>\n ) {\n if (action.payload.uploadId !== state.uploadId) {\n return;\n }\n\n const offset = state.files.length;\n\n state.files.push(\n ...action.payload.fileNames.map((name, i) => ({\n name,\n index: offset + i,\n status: 'pending' as FileProgressStatus,\n size: action.payload.fileSizes?.[i] ?? 0,\n uploadedBytes: 0,\n }))\n );\n\n state.totalFiles += action.payload.fileNames.length;\n // The dialog may have been dismissed while the first drop was running.\n state.isVisible = true;\n state.isMinimized = false;\n },\n setFileUploading(\n state,\n action: PayloadAction<{ name: string; index: number; size: number; uploadId: number }>\n ) {\n const { index, size, uploadId } = action.payload;\n if (uploadId !== state.uploadId) {\n return;\n }\n if (state.files[index]) {\n state.files[index].status = 'uploading';\n state.files[index].size = size;\n }\n },\n setFileProgress(\n state,\n action: PayloadAction<{ index: number; bytes: number; uploadId: number }>\n ) {\n const { index, bytes, uploadId } = action.payload;\n if (uploadId !== state.uploadId) {\n return;\n }\n const file = state.files[index];\n if (file) {\n // Clamp to the known file size so the aggregate can never exceed 100%.\n file.uploadedBytes = Math.min(bytes, file.size);\n }\n },\n setFileComplete(\n state,\n action: PayloadAction<{ index: number; file: File; uploadId: number; completedAt: number }>\n ) {\n const { index, file, uploadId, completedAt } = action.payload;\n if (uploadId !== state.uploadId) {\n return;\n }\n if (state.files[index]) {\n state.files[index].status = 'complete';\n state.files[index].file = file;\n state.files[index].completedAt = completedAt;\n // Reflect completion in the aggregate even if the final progress event was throttled.\n state.files[index].uploadedBytes = state.files[index].size;\n }\n },\n setFileError(\n state,\n action: PayloadAction<{ index: number; name: string; message: string; uploadId: number }>\n ) {\n const { index, name, message, uploadId } = action.payload;\n if (uploadId !== state.uploadId) {\n return;\n }\n if (state.files[index]) {\n state.files[index].status = 'error';\n state.files[index].error = message;\n }\n state.errors = [...state.errors, { name, message }];\n },\n /**\n * Metadata generation started for a row.\n *\n * Metadata requests are fired-and-forgotten, so their callbacks can land after\n * a new batch has replaced `files` and reused the same row indices. Every\n * metadata payload therefore carries the `uploadId` it was fired for, and stale\n * ones are dropped instead of writing onto another batch's row.\n */\n setFileMetadataGenerating(state, action: PayloadAction<{ index: number; uploadId: number }>) {\n const { index, uploadId } = action.payload;\n if (uploadId !== state.uploadId) {\n return;\n }\n if (state.files[index]) {\n state.files[index].metadataStatus = 'generating';\n }\n },\n /**\n * Terminal metadata outcome for a row. Never touches `status`/`error`/`errors`:\n * the upload itself already succeeded regardless of how generation went.\n */\n setFileMetadataResult(\n state,\n action: PayloadAction<{\n index: number;\n uploadId: number;\n status: FileMetadataResultStatus;\n }>\n ) {\n const { index, uploadId, status } = action.payload;\n if (uploadId !== state.uploadId) {\n return;\n }\n if (state.files[index]) {\n state.files[index].metadataStatus = status;\n }\n },\n addUploadErrors(state, action: PayloadAction<FileUploadError[]>) {\n state.errors = [...state.errors, ...action.payload];\n },\n closeUploadProgress(state) {\n state.isVisible = false;\n state.isMinimized = false;\n state.totalFiles = 0;\n state.files = [];\n state.errors = [];\n state.reportsByteProgress = false;\n },\n toggleMinimize(state) {\n state.isMinimized = !state.isMinimized;\n },\n cancelUpload(state) {\n // Mark all pending and uploading files as cancelled\n state.files = state.files.map((file) => {\n if (file.status === 'pending' || file.status === 'uploading') {\n return { ...file, status: 'cancelled' as FileProgressStatus };\n }\n return file;\n });\n },\n setUploadFailed(state, action: PayloadAction<{ message: string }>) {\n // Mark all pending and uploading files as errored when a catastrophic failure occurs\n state.files = state.files.map((file) => {\n if (file.status === 'pending' || file.status === 'uploading') {\n return {\n ...file,\n status: 'error' as FileProgressStatus,\n error: action.payload.message,\n };\n }\n return file;\n });\n state.errors = [...state.errors, { name: 'Upload Error', message: action.payload.message }];\n },\n retryCancelledFiles(state) {\n // Reset all cancelled files back to pending for retry\n state.files = state.files.map((file) => {\n if (file.status === 'cancelled') {\n return {\n ...file,\n status: 'pending' as FileProgressStatus,\n uploadedBytes: 0,\n // A retried row goes through the metadata phase again from scratch.\n metadataStatus: undefined,\n };\n }\n return file;\n });\n },\n },\n});\n\n/**\n * Assets whose own upload has already finished, newest batch only.\n *\n * The list behind the upload dialog uses these to show each asset the moment it\n * lands, instead of waiting for the batch mutation to invalidate the cache. Rows\n * only carry a `file` once the server has created it, so every entry here is a\n * real, persisted asset.\n */\nconst NO_FILES: FileProgress[] = [];\n\n/** An uploaded asset, with the moment the server confirmed it. */\nexport interface CompletedUpload {\n asset: File;\n completedAt: number;\n}\n\nexport const selectCompletedUploads = createSelector(\n // The plugin registers this slice in `register()`, so a host that has not\n // reached that point has no upload state — and therefore nothing to place.\n (state: Partial<RootState>) => state.uploadProgress?.files ?? NO_FILES,\n (files): CompletedUpload[] =>\n files.reduce<CompletedUpload[]>((completed, row) => {\n if (row.status === 'complete' && row.file && row.completedAt !== undefined) {\n completed.push({ asset: row.file, completedAt: row.completedAt });\n }\n\n return completed;\n }, [])\n);\n\n/**\n * Byte-weighted aggregate progress across the whole batch: `sum(uploadedBytes) / sum(size)`.\n *\n * Falls back to count-based progress (settled files / total files) when all sizes are\n * zero — e.g. URL-flow rows where the file size is unknown up front.\n */\nexport const selectAggregateProgress = createSelector(\n (state: RootState) => state.uploadProgress.files,\n (files): number => {\n if (files.length === 0) return 0;\n\n const totalSize = files.reduce((sum, f) => sum + f.size, 0);\n\n if (totalSize === 0) {\n const settled = files.filter(\n (f) => f.status === 'complete' || f.status === 'error' || f.status === 'cancelled'\n ).length;\n return Math.round((settled / files.length) * 100);\n }\n\n const uploadedBytes = files.reduce((sum, f) => sum + f.uploadedBytes, 0);\n return Math.round((uploadedBytes / totalSize) * 100);\n }\n);\n\n/**\n * Whether to show byte-weighted `selectAggregateProgress` rather than count-based progress.\n * A batch that never reports bytes stays pinned at 0%, so the header falls back to\n * `selectCountBasedProgress`.\n *\n * Checks the rows too, not just the flag: an in-flight row reporting bytes proves the batch\n * can, so byte-weighting takes over once the URL flow starts emitting progress. Only\n * `uploading` rows count — completion backfills `uploadedBytes` to the file size, so letting\n * settled rows qualify would switch a multi-URL batch to byte-weighting mid-upload over only\n * the sizes known so far (transiently 100% after the first file).\n */\nexport const selectReportsByteProgress = createSelector(\n (state: RootState) => state.uploadProgress.reportsByteProgress,\n (state: RootState) => state.uploadProgress.files,\n (reportsByteProgress, files): boolean =>\n reportsByteProgress || files.some((f) => f.status === 'uploading' && f.uploadedBytes > 0)\n);\n\n/**\n * Count-based progress (settled / total rows) for a batch that reports no transferred bytes\n * (the URL flow). Used by the header whenever `selectReportsByteProgress` is false.\n *\n * Never byte-weights, so a multi-URL batch climbs 33 → 67 → 100 as each file lands instead\n * of jumping to a transient 100% after the first.\n *\n * Returns `null` until a row settles — 0% is the frozen signal this flow exists to remove\n * (a lone URL upload would otherwise read \"Uploading 1 item (0%)\" throughout), so the header\n * stays indeterminate until there's real progress.\n */\nexport const selectCountBasedProgress = createSelector(\n (state: RootState) => state.uploadProgress.files,\n (files): number | null => {\n const settled = files.filter(\n (f) => f.status === 'complete' || f.status === 'error' || f.status === 'cancelled'\n ).length;\n\n if (settled === 0) return null;\n\n return Math.round((settled / files.length) * 100);\n }\n);\n\n/**\n * Count-based metadata progress across the whole batch: `settled / expected`.\n *\n * Count-based rather than byte-weighted because the generation endpoint answers in one\n * go — there is no intermediate progress to weight.\n *\n * The denominator is every row *expected* to enter the phase, not just those that\n * already have. Generation is fired per file as each upload finishes, so counting only\n * started rows made the denominator grow mid-batch and the percentage jump backwards\n * (1/1 = 100%, then a second row starts and it drops to 1/2 = 50%), which flickered the\n * header subtitle on and off once per file.\n *\n * \"Expected\" is derived from upload outcome rather than from the AI-metadata flag:\n * - a row still uploading may yet enter the phase, so it counts toward the total;\n * - an errored or cancelled row never will, so it must not — otherwise a batch with\n * one failure could never reach 100%.\n *\n * Returns `null` when nothing has entered the phase and nothing is pending — i.e. AI\n * metadata is off, or the batch is done — so the header can hide the subtitle.\n */\nexport const selectMetadataProgress = createSelector(\n (state: RootState) => state.uploadProgress.files,\n (files): number | null => {\n const started = files.filter((f) => f.metadataStatus !== undefined);\n\n // Nothing has entered the phase yet: with no started row there is no evidence AI\n // metadata is even enabled, so stay silent rather than showing a premature 0%.\n if (started.length === 0) return null;\n\n // Rows whose upload is still in flight are counted in the denominator so the\n // percentage only ever climbs as the batch progresses.\n const pending = files.filter(\n (f) => f.metadataStatus === undefined && (f.status === 'pending' || f.status === 'uploading')\n ).length;\n\n const expected = started.length + pending;\n const settled = started.filter((f) => f.metadataStatus !== 'generating').length;\n\n return Math.round((settled / expected) * 100);\n }\n);\n\n/**\n * Whether the metadata phase is still ongoing for the batch as a whole.\n *\n * The header uses this — not `selectMetadataProgress === 100` — to decide when to drop\n * the subtitle. Two distinct reasons the phase can be unfinished:\n * - a row is generating right now;\n * - no row is generating this instant, but rows are still uploading and will enter the\n * phase when they finish.\n *\n * The second clause is what keeps the subtitle stable. Generation for file N typically\n * finishes before file N+1 finishes uploading, so a naive \"is anything generating?\"\n * check goes false between every pair of files and blinks the subtitle out and back in.\n */\nexport const selectIsGeneratingMetadata = createSelector(\n (state: RootState) => state.uploadProgress.files,\n (files): boolean => {\n if (files.some((f) => f.metadataStatus === 'generating')) return true;\n\n // Only bridge the gap if some row has already entered the phase — otherwise a batch\n // with AI metadata disabled would report the phase as ongoing for its whole upload.\n const hasStarted = files.some((f) => f.metadataStatus !== undefined);\n\n return hasStarted && files.some((f) => f.status === 'pending' || f.status === 'uploading');\n }\n);\n\n/**\n * How many rows reached each terminal metadata outcome, for the header's completion\n * message. `null` while the phase is unfinished — or was never entered at all — so the\n * caller has a single check for \"there is no settled outcome to report yet\".\n *\n * Counts are reported separately rather than collapsed into one total because they are\n * not interchangeable: only `generated` rows actually had metadata written, so only that\n * count can be claimed as a success. A batch of non-images settles entirely on `skipped`,\n * where \"generated on 0 files\" would be worse than saying nothing.\n */\nexport const selectMetadataOutcome = createSelector(\n (state: RootState) => state.uploadProgress.files,\n (files): { generated: number; skipped: number; failed: number } | null => {\n const started = files.filter((f) => f.metadataStatus !== undefined);\n\n if (started.length === 0) return null;\n\n // Mirrors `selectIsGeneratingMetadata`: the phase isn't over while a row is\n // generating, nor while a row is still uploading and has yet to enter it.\n const isOngoing =\n started.some((f) => f.metadataStatus === 'generating') ||\n files.some((f) => f.status === 'pending' || f.status === 'uploading');\n\n if (isOngoing) return null;\n\n return {\n generated: started.filter((f) => f.metadataStatus === 'generated').length,\n skipped: started.filter((f) => f.metadataStatus === 'skipped').length,\n failed: started.filter((f) => f.metadataStatus === 'failed').length,\n };\n }\n);\n\nexport const {\n openUploadProgress,\n appendUploadFiles,\n setFileUploading,\n setFileProgress,\n setFileComplete,\n setFileError,\n setFileMetadataGenerating,\n setFileMetadataResult,\n addUploadErrors,\n closeUploadProgress,\n toggleMinimize,\n cancelUpload,\n setUploadFailed,\n retryCancelledFiles,\n} = uploadProgressSlice.actions;\n\n/**\n * The merge/reset switch. Errored and cancelled rows count as settled, so a\n * batch that finished badly is still replaced by the next drop.\n */\nexport const isUploadInFlight = (files: ReadonlyArray<{ status: FileProgressStatus }>): boolean =>\n files.some((f) => f.status === 'pending' || f.status === 'uploading');\n\nexport const uploadProgressReducer = uploadProgressSlice.reducer;\n"],"names":["initialState","isVisible","isMinimized","totalFiles","files","errors","uploadId","reportsByteProgress","uploadProgressSlice","createSlice","name","reducers","openUploadProgress","state","action","pendingFiles","payload","fileNames","map","index","status","size","fileSizes","uploadedBytes","undefined","appendUploadFiles","offset","length","push","i","setFileUploading","setFileProgress","bytes","file","Math","min","setFileComplete","completedAt","setFileError","message","error","setFileMetadataGenerating","metadataStatus","setFileMetadataResult","addUploadErrors","closeUploadProgress","toggleMinimize","cancelUpload","setUploadFailed","retryCancelledFiles","NO_FILES","selectCompletedUploads","createSelector","uploadProgress","reduce","completed","row","asset","selectAggregateProgress","totalSize","sum","f","settled","filter","round","selectReportsByteProgress","some","selectCountBasedProgress","selectMetadataProgress","started","pending","expected","selectIsGeneratingMetadata","hasStarted","selectMetadataOutcome","isOngoing","generated","skipped","failed","actions","isUploadInFlight","uploadProgressReducer","reducer"],"mappings":"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"}
1
+ {"version":3,"file":"uploadProgress.mjs","sources":["../../../../admin/src/future/store/uploadProgress.ts"],"sourcesContent":["import { createSelector, createSlice, type PayloadAction } from '@reduxjs/toolkit';\n\nimport type { File } from '../../../../shared/contracts/files';\n\nexport interface FileUploadError {\n name: string;\n message: string;\n}\n\nexport type FileProgressStatus = 'pending' | 'uploading' | 'complete' | 'error' | 'cancelled';\n\n/**\n * AI metadata generation phase for a single row, tracked independently from the\n * upload status: a metadata failure never turns a successful upload into an error.\n * `undefined` means the row has no metadata phase at all, i.e. AI metadata is\n * disabled — non-images do get a phase and come back `skipped` from the server.\n */\nexport type FileMetadataStatus = 'generating' | 'generated' | 'skipped' | 'failed';\n\nexport type FileMetadataResultStatus = Exclude<FileMetadataStatus, 'generating'>;\n\nexport interface FileProgress {\n name: string;\n index: number;\n status: FileProgressStatus;\n size: number;\n uploadedBytes: number;\n file?: File;\n /** When the server confirmed the upload. Set with the row's asset. */\n completedAt?: number;\n error?: string;\n metadataStatus?: FileMetadataStatus;\n}\n\nexport interface UploadProgressState {\n isVisible: boolean;\n isMinimized: boolean;\n totalFiles: number;\n files: FileProgress[];\n errors: FileUploadError[];\n uploadId: number;\n /**\n * Whether every row's size was known when the batch opened (the direct-file flow), so\n * the byte-weighted aggregate is meaningful. The URL flow learns sizes one row at a time.\n */\n reportsByteProgress: boolean;\n}\n\nexport interface RootState {\n uploadProgress: UploadProgressState;\n}\n\nconst initialState: UploadProgressState = {\n isVisible: false,\n isMinimized: false,\n totalFiles: 0,\n files: [],\n errors: [],\n uploadId: 0,\n reportsByteProgress: false,\n};\n\nconst uploadProgressSlice = createSlice({\n name: 'uploadProgress',\n initialState,\n reducers: {\n openUploadProgress(\n state,\n action: PayloadAction<{\n totalFiles: number;\n fileNames: string[];\n fileSizes?: number[];\n }>\n ) {\n state.isVisible = true;\n state.isMinimized = false;\n\n // Create pending files for upload\n const pendingFiles: FileProgress[] = action.payload.fileNames.map((name, index) => ({\n name,\n index,\n status: 'pending' as FileProgressStatus,\n size: action.payload.fileSizes?.[index] ?? 0,\n uploadedBytes: 0,\n }));\n\n state.files = pendingFiles;\n state.totalFiles = action.payload.totalFiles;\n state.errors = [];\n state.uploadId += 1;\n // Known sizes == the direct-file flow == the flow that streams bytes.\n state.reportsByteProgress = action.payload.fileSizes !== undefined;\n },\n /**\n * Rows for a second drop, joined to the batch already uploading. Indices\n * continue from the current length so the earlier drop's in-flight uploads\n * keep addressing their own rows.\n */\n appendUploadFiles(\n state,\n action: PayloadAction<{\n uploadId: number;\n fileNames: string[];\n fileSizes?: number[];\n }>\n ) {\n if (action.payload.uploadId !== state.uploadId) {\n return;\n }\n\n const offset = state.files.length;\n\n state.files.push(\n ...action.payload.fileNames.map((name, i) => ({\n name,\n index: offset + i,\n status: 'pending' as FileProgressStatus,\n size: action.payload.fileSizes?.[i] ?? 0,\n uploadedBytes: 0,\n }))\n );\n\n state.totalFiles += action.payload.fileNames.length;\n // The dialog may have been dismissed while the first drop was running.\n state.isVisible = true;\n state.isMinimized = false;\n },\n setFileUploading(\n state,\n action: PayloadAction<{ name: string; index: number; size: number; uploadId: number }>\n ) {\n const { index, size, uploadId } = action.payload;\n if (uploadId !== state.uploadId) {\n return;\n }\n if (state.files[index]) {\n state.files[index].status = 'uploading';\n state.files[index].size = size;\n }\n },\n /**\n * `size` is optional and only sent by the URL flow, which learns the size from the\n * same server event that carries the first byte count — the row was created with\n * `size: 0`, and bytes clamped against a zero size would all read as zero. The\n * direct-file flow already knows the size up front and omits it.\n */\n setFileProgress(\n state,\n action: PayloadAction<{ index: number; bytes: number; uploadId: number; size?: number }>\n ) {\n const { index, bytes, size, uploadId } = action.payload;\n if (uploadId !== state.uploadId) {\n return;\n }\n const file = state.files[index];\n if (file) {\n if (size !== undefined && file.size === 0) {\n file.size = size;\n }\n // Clamp to the known file size so the aggregate can never exceed 100%.\n file.uploadedBytes = Math.min(bytes, file.size);\n }\n },\n setFileComplete(\n state,\n action: PayloadAction<{ index: number; file: File; uploadId: number; completedAt: number }>\n ) {\n const { index, file, uploadId, completedAt } = action.payload;\n if (uploadId !== state.uploadId) {\n return;\n }\n if (state.files[index]) {\n state.files[index].status = 'complete';\n state.files[index].file = file;\n state.files[index].completedAt = completedAt;\n // Reflect completion in the aggregate even if the final progress event was throttled.\n state.files[index].uploadedBytes = state.files[index].size;\n }\n },\n setFileError(\n state,\n action: PayloadAction<{ index: number; name: string; message: string; uploadId: number }>\n ) {\n const { index, name, message, uploadId } = action.payload;\n if (uploadId !== state.uploadId) {\n return;\n }\n if (state.files[index]) {\n state.files[index].status = 'error';\n state.files[index].error = message;\n }\n state.errors = [...state.errors, { name, message }];\n },\n /**\n * Metadata generation started for a row.\n *\n * Metadata requests are fired-and-forgotten, so their callbacks can land after\n * a new batch has replaced `files` and reused the same row indices. Every\n * metadata payload therefore carries the `uploadId` it was fired for, and stale\n * ones are dropped instead of writing onto another batch's row.\n */\n setFileMetadataGenerating(state, action: PayloadAction<{ index: number; uploadId: number }>) {\n const { index, uploadId } = action.payload;\n if (uploadId !== state.uploadId) {\n return;\n }\n if (state.files[index]) {\n state.files[index].metadataStatus = 'generating';\n }\n },\n /**\n * Terminal metadata outcome for a row. Never touches `status`/`error`/`errors`:\n * the upload itself already succeeded regardless of how generation went.\n */\n setFileMetadataResult(\n state,\n action: PayloadAction<{\n index: number;\n uploadId: number;\n status: FileMetadataResultStatus;\n }>\n ) {\n const { index, uploadId, status } = action.payload;\n if (uploadId !== state.uploadId) {\n return;\n }\n if (state.files[index]) {\n state.files[index].metadataStatus = status;\n }\n },\n addUploadErrors(state, action: PayloadAction<FileUploadError[]>) {\n state.errors = [...state.errors, ...action.payload];\n },\n closeUploadProgress(state) {\n state.isVisible = false;\n state.isMinimized = false;\n state.totalFiles = 0;\n state.files = [];\n state.errors = [];\n state.reportsByteProgress = false;\n },\n toggleMinimize(state) {\n state.isMinimized = !state.isMinimized;\n },\n cancelUpload(state) {\n // Mark all pending and uploading files as cancelled\n state.files = state.files.map((file) => {\n if (file.status === 'pending' || file.status === 'uploading') {\n return { ...file, status: 'cancelled' as FileProgressStatus };\n }\n return file;\n });\n },\n setUploadFailed(state, action: PayloadAction<{ message: string }>) {\n // Mark all pending and uploading files as errored when a catastrophic failure occurs\n state.files = state.files.map((file) => {\n if (file.status === 'pending' || file.status === 'uploading') {\n return {\n ...file,\n status: 'error' as FileProgressStatus,\n error: action.payload.message,\n };\n }\n return file;\n });\n state.errors = [...state.errors, { name: 'Upload Error', message: action.payload.message }];\n },\n retryCancelledFiles(state) {\n // Reset all cancelled files back to pending for retry\n state.files = state.files.map((file) => {\n if (file.status === 'cancelled') {\n return {\n ...file,\n status: 'pending' as FileProgressStatus,\n uploadedBytes: 0,\n // A retried row goes through the metadata phase again from scratch.\n metadataStatus: undefined,\n };\n }\n return file;\n });\n },\n },\n});\n\n/**\n * Assets whose own upload has already finished, newest batch only.\n *\n * The list behind the upload dialog uses these to show each asset the moment it\n * lands, instead of waiting for the batch mutation to invalidate the cache. Rows\n * only carry a `file` once the server has created it, so every entry here is a\n * real, persisted asset.\n */\nconst NO_FILES: FileProgress[] = [];\n\n/** An uploaded asset, with the moment the server confirmed it. */\nexport interface CompletedUpload {\n asset: File;\n completedAt: number;\n}\n\nexport const selectCompletedUploads = createSelector(\n // The plugin registers this slice in `register()`, so a host that has not\n // reached that point has no upload state — and therefore nothing to place.\n (state: Partial<RootState>) => state.uploadProgress?.files ?? NO_FILES,\n (files): CompletedUpload[] =>\n files.reduce<CompletedUpload[]>((completed, row) => {\n if (row.status === 'complete' && row.file && row.completedAt !== undefined) {\n completed.push({ asset: row.file, completedAt: row.completedAt });\n }\n\n return completed;\n }, [])\n);\n\n/**\n * Byte-weighted aggregate progress across the whole batch: `sum(uploadedBytes) / sum(size)`.\n *\n * Falls back to count-based progress (settled files / total files) when all sizes are\n * zero — e.g. URL-flow rows where the file size is unknown up front.\n */\nexport const selectAggregateProgress = createSelector(\n (state: RootState) => state.uploadProgress.files,\n (files): number => {\n if (files.length === 0) return 0;\n\n const totalSize = files.reduce((sum, f) => sum + f.size, 0);\n\n if (totalSize === 0) {\n const settled = files.filter(\n (f) => f.status === 'complete' || f.status === 'error' || f.status === 'cancelled'\n ).length;\n return Math.round((settled / files.length) * 100);\n }\n\n const uploadedBytes = files.reduce((sum, f) => sum + f.uploadedBytes, 0);\n return Math.round((uploadedBytes / totalSize) * 100);\n }\n);\n\n/**\n * Whether to show byte-weighted `selectAggregateProgress` rather than `selectCountBasedProgress`.\n *\n * Only the flag: byte-weighting needs every size up front. A URL batch learns sizes one row\n * at a time, so weighting over the sizes known so far makes the header jump backwards at\n * every file boundary.\n */\nexport const selectReportsByteProgress = createSelector(\n (state: RootState) => state.uploadProgress.reportsByteProgress,\n (reportsByteProgress): boolean => reportsByteProgress\n);\n\n/**\n * Row-weighted progress for a batch whose sizes are not known up front (the URL flow).\n * Each row is worth `1 / total`: settled rows count fully and an in-flight row contributes\n * `uploadedBytes / size` once it reports bytes, so a multi-URL batch climbs smoothly to\n * 33 → 67 → 100 and never moves backwards.\n *\n * Returns `null` until something has moved — 0% is the frozen signal this flow exists to\n * remove, so the header stays indeterminate until there's real progress.\n */\nexport const selectCountBasedProgress = createSelector(\n (state: RootState) => state.uploadProgress.files,\n (files): number | null => {\n const done = files.reduce((sum, f) => {\n if (f.status === 'complete' || f.status === 'error' || f.status === 'cancelled') {\n return sum + 1;\n }\n return f.size > 0 ? sum + Math.min(f.uploadedBytes / f.size, 1) : sum;\n }, 0);\n\n if (done === 0) {\n return null;\n }\n\n return Math.round((done / files.length) * 100);\n }\n);\n\n/**\n * Count-based metadata progress across the whole batch: `settled / expected`.\n *\n * Count-based rather than byte-weighted because the generation endpoint answers in one\n * go — there is no intermediate progress to weight.\n *\n * The denominator is every row *expected* to enter the phase, not just those that\n * already have. Generation is fired per file as each upload finishes, so counting only\n * started rows made the denominator grow mid-batch and the percentage jump backwards\n * (1/1 = 100%, then a second row starts and it drops to 1/2 = 50%), which flickered the\n * header subtitle on and off once per file.\n *\n * \"Expected\" is derived from upload outcome rather than from the AI-metadata flag:\n * - a row still uploading may yet enter the phase, so it counts toward the total;\n * - an errored or cancelled row never will, so it must not — otherwise a batch with\n * one failure could never reach 100%.\n *\n * Returns `null` when nothing has entered the phase and nothing is pending — i.e. AI\n * metadata is off, or the batch is done — so the header can hide the subtitle.\n */\nexport const selectMetadataProgress = createSelector(\n (state: RootState) => state.uploadProgress.files,\n (files): number | null => {\n const started = files.filter((f) => f.metadataStatus !== undefined);\n\n // Nothing has entered the phase yet: with no started row there is no evidence AI\n // metadata is even enabled, so stay silent rather than showing a premature 0%.\n if (started.length === 0) return null;\n\n // Rows whose upload is still in flight are counted in the denominator so the\n // percentage only ever climbs as the batch progresses.\n const pending = files.filter(\n (f) => f.metadataStatus === undefined && (f.status === 'pending' || f.status === 'uploading')\n ).length;\n\n const expected = started.length + pending;\n const settled = started.filter((f) => f.metadataStatus !== 'generating').length;\n\n return Math.round((settled / expected) * 100);\n }\n);\n\n/**\n * Whether the metadata phase is still ongoing for the batch as a whole.\n *\n * The header uses this — not `selectMetadataProgress === 100` — to decide when to drop\n * the subtitle. Two distinct reasons the phase can be unfinished:\n * - a row is generating right now;\n * - no row is generating this instant, but rows are still uploading and will enter the\n * phase when they finish.\n *\n * The second clause is what keeps the subtitle stable. Generation for file N typically\n * finishes before file N+1 finishes uploading, so a naive \"is anything generating?\"\n * check goes false between every pair of files and blinks the subtitle out and back in.\n */\nexport const selectIsGeneratingMetadata = createSelector(\n (state: RootState) => state.uploadProgress.files,\n (files): boolean => {\n if (files.some((f) => f.metadataStatus === 'generating')) return true;\n\n // Only bridge the gap if some row has already entered the phase — otherwise a batch\n // with AI metadata disabled would report the phase as ongoing for its whole upload.\n const hasStarted = files.some((f) => f.metadataStatus !== undefined);\n\n return hasStarted && files.some((f) => f.status === 'pending' || f.status === 'uploading');\n }\n);\n\n/**\n * How many rows reached each terminal metadata outcome, for the header's completion\n * message. `null` while the phase is unfinished — or was never entered at all — so the\n * caller has a single check for \"there is no settled outcome to report yet\".\n *\n * Counts are reported separately rather than collapsed into one total because they are\n * not interchangeable: only `generated` rows actually had metadata written, so only that\n * count can be claimed as a success. A batch of non-images settles entirely on `skipped`,\n * where \"generated on 0 files\" would be worse than saying nothing.\n */\nexport const selectMetadataOutcome = createSelector(\n (state: RootState) => state.uploadProgress.files,\n (files): { generated: number; skipped: number; failed: number } | null => {\n const started = files.filter((f) => f.metadataStatus !== undefined);\n\n if (started.length === 0) return null;\n\n // Mirrors `selectIsGeneratingMetadata`: the phase isn't over while a row is\n // generating, nor while a row is still uploading and has yet to enter it.\n const isOngoing =\n started.some((f) => f.metadataStatus === 'generating') ||\n files.some((f) => f.status === 'pending' || f.status === 'uploading');\n\n if (isOngoing) return null;\n\n return {\n generated: started.filter((f) => f.metadataStatus === 'generated').length,\n skipped: started.filter((f) => f.metadataStatus === 'skipped').length,\n failed: started.filter((f) => f.metadataStatus === 'failed').length,\n };\n }\n);\n\nexport const {\n openUploadProgress,\n appendUploadFiles,\n setFileUploading,\n setFileProgress,\n setFileComplete,\n setFileError,\n setFileMetadataGenerating,\n setFileMetadataResult,\n addUploadErrors,\n closeUploadProgress,\n toggleMinimize,\n cancelUpload,\n setUploadFailed,\n retryCancelledFiles,\n} = uploadProgressSlice.actions;\n\n/**\n * The merge/reset switch. Errored and cancelled rows count as settled, so a\n * batch that finished badly is still replaced by the next drop.\n */\nexport const isUploadInFlight = (files: ReadonlyArray<{ status: FileProgressStatus }>): boolean =>\n files.some((f) => f.status === 'pending' || f.status === 'uploading');\n\nexport const uploadProgressReducer = uploadProgressSlice.reducer;\n"],"names":["initialState","isVisible","isMinimized","totalFiles","files","errors","uploadId","reportsByteProgress","uploadProgressSlice","createSlice","name","reducers","openUploadProgress","state","action","pendingFiles","payload","fileNames","map","index","status","size","fileSizes","uploadedBytes","undefined","appendUploadFiles","offset","length","push","i","setFileUploading","setFileProgress","bytes","file","Math","min","setFileComplete","completedAt","setFileError","message","error","setFileMetadataGenerating","metadataStatus","setFileMetadataResult","addUploadErrors","closeUploadProgress","toggleMinimize","cancelUpload","setUploadFailed","retryCancelledFiles","NO_FILES","selectCompletedUploads","createSelector","uploadProgress","reduce","completed","row","asset","selectAggregateProgress","totalSize","sum","f","settled","filter","round","selectReportsByteProgress","selectCountBasedProgress","done","selectMetadataProgress","started","pending","expected","selectIsGeneratingMetadata","some","hasStarted","selectMetadataOutcome","isOngoing","generated","skipped","failed","actions","isUploadInFlight","uploadProgressReducer","reducer"],"mappings":"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"}
@@ -53,10 +53,11 @@ const name = _package.default.strapi.name;
53
53
  const admin = {
54
54
  register (app) {
55
55
  /**
56
- * The beta Media Library owns `plugins/upload` outright when the flag is on:
57
- * the legacy app is not registered at all, so there is exactly one Media
58
- * Library entry in the menu and no route to rename at GA.
59
- */ const isBetaMediaLibrary = window.strapi.future.isEnabled('betaMediaLibrary');
56
+ * Whichever Media Library is selected owns `plugins/upload` outright: the other is
57
+ * not registered at all, so there is exactly one Media Library entry in the menu.
58
+ *
59
+ * The new one is the default; `useLegacyMediaLibrary` opts back out.
60
+ */ const isLegacyMediaLibrary = window.strapi.featureFlags.isEnabled('useLegacyMediaLibrary');
60
61
  app.addMenuLink({
61
62
  to: `plugins/${pluginId.pluginId}`,
62
63
  icon: icons.Images,
@@ -65,18 +66,18 @@ const admin = {
65
66
  defaultMessage: 'Media Library'
66
67
  },
67
68
  permissions: constants.PERMISSIONS.main,
68
- Component: isBetaMediaLibrary ? ()=>{
69
- return Promise.resolve().then(function () { return require('./future/App.js'); }).then((mod)=>({
70
- default: mod.BetaMediaLibrary
71
- }));
72
- } : ()=>{
69
+ Component: isLegacyMediaLibrary ? ()=>{
73
70
  return Promise.resolve().then(function () { return require('./pages/App/App.js'); }).then((mod)=>({
74
71
  default: mod.Upload
75
72
  }));
73
+ } : ()=>{
74
+ return Promise.resolve().then(function () { return require('./future/App.js'); }).then((mod)=>({
75
+ default: mod.BetaMediaLibrary
76
+ }));
76
77
  },
77
78
  position: 4
78
79
  });
79
- if (isBetaMediaLibrary) {
80
+ if (!isLegacyMediaLibrary) {
80
81
  app.addReducers({
81
82
  uploadProgress: uploadProgress.uploadProgressReducer
82
83
  });
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../admin/src/index.ts"],"sourcesContent":["import { Images } from '@strapi/icons';\n\nimport pluginPkg from '../../package.json';\n\nimport { MediaLibraryDialog } from './components/MediaLibraryDialog/MediaLibraryDialog';\nimport { MediaLibraryInput } from './components/MediaLibraryInput/MediaLibraryInput';\nimport { PERMISSIONS } from './constants';\nimport { UploadProgressDialog } from './future/components/UploadProgressDialog';\nimport { uploadProgressReducer } from './future/store/uploadProgress';\nimport { pluginId } from './pluginId';\nimport { getTrad, prefixPluginTranslations } from './utils';\n\nimport type { MediaLibraryDialogProps } from './components/MediaLibraryDialog/MediaLibraryDialog';\nimport type { MediaLibraryInputProps } from './components/MediaLibraryInput/MediaLibraryInput';\nimport type { StrapiApp } from '@strapi/admin/strapi-admin';\nimport type { Plugin } from '@strapi/types';\n\nconst name = pluginPkg.strapi.name;\n\nconst admin: Plugin.Config.AdminInput = {\n register(app: StrapiApp) {\n /**\n * The beta Media Library owns `plugins/upload` outright when the flag is on:\n * the legacy app is not registered at all, so there is exactly one Media\n * Library entry in the menu and no route to rename at GA.\n */\n const isBetaMediaLibrary = window.strapi.future.isEnabled('betaMediaLibrary');\n\n app.addMenuLink({\n to: `plugins/${pluginId}`,\n icon: Images,\n intlLabel: {\n id: `${pluginId}.plugin.name`,\n defaultMessage: 'Media Library',\n },\n permissions: PERMISSIONS.main,\n Component: isBetaMediaLibrary\n ? () => {\n return import('./future/App').then((mod) => ({\n default: mod.BetaMediaLibrary,\n }));\n }\n : () => {\n return import('./pages/App/App').then((mod) => ({ default: mod.Upload }));\n },\n position: 4,\n });\n\n if (isBetaMediaLibrary) {\n app.addReducers({ uploadProgress: uploadProgressReducer });\n\n app.addComponents([\n {\n name: 'future-global::upload-progress',\n Component: UploadProgressDialog,\n },\n ]);\n }\n\n app.addSettingsLink('global', {\n id: 'media-library-settings',\n to: 'media-library',\n intlLabel: {\n id: getTrad('plugin.name'),\n defaultMessage: 'Media Library',\n },\n Component() {\n return import('./pages/SettingsPage/SettingsPage').then((mod) => ({\n default: mod.ProtectedSettingsPage,\n }));\n },\n permissions: PERMISSIONS.settings,\n });\n\n app.addFields({\n type: 'media',\n Component: MediaLibraryInput as React.FC<Partial<MediaLibraryInputProps>>,\n });\n app.addComponents([\n {\n name: 'media-library',\n Component: MediaLibraryDialog as React.FC<Partial<MediaLibraryDialogProps>>,\n },\n ]);\n\n app.registerPlugin({\n id: pluginId,\n name,\n });\n },\n async registerTrads({ locales }: { locales: string[] }) {\n const importedTrads = await Promise.all(\n locales.map((locale) => {\n return import(`./translations/${locale}.json`)\n .then(({ default: data }) => {\n return {\n data: prefixPluginTranslations(data, pluginId),\n locale,\n };\n })\n .catch(() => {\n return {\n data: {},\n locale,\n };\n });\n })\n );\n\n return Promise.resolve(importedTrads);\n },\n};\n\n// eslint-disable-next-line import/no-default-export\nexport default admin;\n"],"names":["name","pluginPkg","strapi","admin","register","app","isBetaMediaLibrary","window","future","isEnabled","addMenuLink","to","pluginId","icon","Images","intlLabel","id","defaultMessage","permissions","PERMISSIONS","main","Component","then","mod","default","BetaMediaLibrary","Upload","position","addReducers","uploadProgress","uploadProgressReducer","addComponents","UploadProgressDialog","addSettingsLink","getTrad","ProtectedSettingsPage","settings","addFields","type","MediaLibraryInput","MediaLibraryDialog","registerPlugin","registerTrads","locales","importedTrads","Promise","all","map","locale","data","prefixPluginTranslations","catch","resolve"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../admin/src/index.ts"],"sourcesContent":["import { Images } from '@strapi/icons';\n\nimport pluginPkg from '../../package.json';\n\nimport { MediaLibraryDialog } from './components/MediaLibraryDialog/MediaLibraryDialog';\nimport { MediaLibraryInput } from './components/MediaLibraryInput/MediaLibraryInput';\nimport { PERMISSIONS } from './constants';\nimport { UploadProgressDialog } from './future/components/UploadProgressDialog';\nimport { uploadProgressReducer } from './future/store/uploadProgress';\nimport { pluginId } from './pluginId';\nimport { getTrad, prefixPluginTranslations } from './utils';\n\nimport type { MediaLibraryDialogProps } from './components/MediaLibraryDialog/MediaLibraryDialog';\nimport type { MediaLibraryInputProps } from './components/MediaLibraryInput/MediaLibraryInput';\nimport type { StrapiApp } from '@strapi/admin/strapi-admin';\nimport type { Plugin } from '@strapi/types';\n\nconst name = pluginPkg.strapi.name;\n\nconst admin: Plugin.Config.AdminInput = {\n register(app: StrapiApp) {\n /**\n * Whichever Media Library is selected owns `plugins/upload` outright: the other is\n * not registered at all, so there is exactly one Media Library entry in the menu.\n *\n * The new one is the default; `useLegacyMediaLibrary` opts back out.\n */\n const isLegacyMediaLibrary = window.strapi.featureFlags.isEnabled('useLegacyMediaLibrary');\n\n app.addMenuLink({\n to: `plugins/${pluginId}`,\n icon: Images,\n intlLabel: {\n id: `${pluginId}.plugin.name`,\n defaultMessage: 'Media Library',\n },\n permissions: PERMISSIONS.main,\n Component: isLegacyMediaLibrary\n ? () => {\n return import('./pages/App/App').then((mod) => ({ default: mod.Upload }));\n }\n : () => {\n return import('./future/App').then((mod) => ({\n default: mod.BetaMediaLibrary,\n }));\n },\n position: 4,\n });\n\n if (!isLegacyMediaLibrary) {\n app.addReducers({ uploadProgress: uploadProgressReducer });\n\n app.addComponents([\n {\n name: 'future-global::upload-progress',\n Component: UploadProgressDialog,\n },\n ]);\n }\n\n app.addSettingsLink('global', {\n id: 'media-library-settings',\n to: 'media-library',\n intlLabel: {\n id: getTrad('plugin.name'),\n defaultMessage: 'Media Library',\n },\n Component() {\n return import('./pages/SettingsPage/SettingsPage').then((mod) => ({\n default: mod.ProtectedSettingsPage,\n }));\n },\n permissions: PERMISSIONS.settings,\n });\n\n app.addFields({\n type: 'media',\n Component: MediaLibraryInput as React.FC<Partial<MediaLibraryInputProps>>,\n });\n app.addComponents([\n {\n name: 'media-library',\n Component: MediaLibraryDialog as React.FC<Partial<MediaLibraryDialogProps>>,\n },\n ]);\n\n app.registerPlugin({\n id: pluginId,\n name,\n });\n },\n async registerTrads({ locales }: { locales: string[] }) {\n const importedTrads = await Promise.all(\n locales.map((locale) => {\n return import(`./translations/${locale}.json`)\n .then(({ default: data }) => {\n return {\n data: prefixPluginTranslations(data, pluginId),\n locale,\n };\n })\n .catch(() => {\n return {\n data: {},\n locale,\n };\n });\n })\n );\n\n return Promise.resolve(importedTrads);\n },\n};\n\n// eslint-disable-next-line import/no-default-export\nexport default admin;\n"],"names":["name","pluginPkg","strapi","admin","register","app","isLegacyMediaLibrary","window","featureFlags","isEnabled","addMenuLink","to","pluginId","icon","Images","intlLabel","id","defaultMessage","permissions","PERMISSIONS","main","Component","then","mod","default","Upload","BetaMediaLibrary","position","addReducers","uploadProgress","uploadProgressReducer","addComponents","UploadProgressDialog","addSettingsLink","getTrad","ProtectedSettingsPage","settings","addFields","type","MediaLibraryInput","MediaLibraryDialog","registerPlugin","registerTrads","locales","importedTrads","Promise","all","map","locale","data","prefixPluginTranslations","catch","resolve"],"mappings":"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"}
@@ -49,10 +49,11 @@ const name = pluginPkg.strapi.name;
49
49
  const admin = {
50
50
  register (app) {
51
51
  /**
52
- * The beta Media Library owns `plugins/upload` outright when the flag is on:
53
- * the legacy app is not registered at all, so there is exactly one Media
54
- * Library entry in the menu and no route to rename at GA.
55
- */ const isBetaMediaLibrary = window.strapi.future.isEnabled('betaMediaLibrary');
52
+ * Whichever Media Library is selected owns `plugins/upload` outright: the other is
53
+ * not registered at all, so there is exactly one Media Library entry in the menu.
54
+ *
55
+ * The new one is the default; `useLegacyMediaLibrary` opts back out.
56
+ */ const isLegacyMediaLibrary = window.strapi.featureFlags.isEnabled('useLegacyMediaLibrary');
56
57
  app.addMenuLink({
57
58
  to: `plugins/${pluginId}`,
58
59
  icon: Images,
@@ -61,18 +62,18 @@ const admin = {
61
62
  defaultMessage: 'Media Library'
62
63
  },
63
64
  permissions: PERMISSIONS.main,
64
- Component: isBetaMediaLibrary ? ()=>{
65
- return import('./future/App.mjs').then((mod)=>({
66
- default: mod.BetaMediaLibrary
67
- }));
68
- } : ()=>{
65
+ Component: isLegacyMediaLibrary ? ()=>{
69
66
  return import('./pages/App/App.mjs').then((mod)=>({
70
67
  default: mod.Upload
71
68
  }));
69
+ } : ()=>{
70
+ return import('./future/App.mjs').then((mod)=>({
71
+ default: mod.BetaMediaLibrary
72
+ }));
72
73
  },
73
74
  position: 4
74
75
  });
75
- if (isBetaMediaLibrary) {
76
+ if (!isLegacyMediaLibrary) {
76
77
  app.addReducers({
77
78
  uploadProgress: uploadProgressReducer
78
79
  });