@wordpress/media-utils 5.41.1-next.v.202603161435.0 → 5.43.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.
- package/CHANGELOG.md +4 -0
- package/build/components/media-upload-modal/index.cjs +133 -87
- package/build/components/media-upload-modal/index.cjs.map +3 -3
- package/build/components/media-upload-modal/upload-status-popover.cjs +156 -0
- package/build/components/media-upload-modal/upload-status-popover.cjs.map +7 -0
- package/build/components/media-upload-modal/use-invalidate-attachment-resolutions.cjs +45 -0
- package/build/components/media-upload-modal/use-invalidate-attachment-resolutions.cjs.map +7 -0
- package/build/components/media-upload-modal/use-upload-status.cjs +127 -0
- package/build/components/media-upload-modal/use-upload-status.cjs.map +7 -0
- package/build-module/components/media-upload-modal/index.mjs +126 -88
- package/build-module/components/media-upload-modal/index.mjs.map +2 -2
- package/build-module/components/media-upload-modal/upload-status-popover.mjs +131 -0
- package/build-module/components/media-upload-modal/upload-status-popover.mjs.map +7 -0
- package/build-module/components/media-upload-modal/use-invalidate-attachment-resolutions.mjs +20 -0
- package/build-module/components/media-upload-modal/use-invalidate-attachment-resolutions.mjs.map +7 -0
- package/build-module/components/media-upload-modal/use-upload-status.mjs +102 -0
- package/build-module/components/media-upload-modal/use-upload-status.mjs.map +7 -0
- package/build-style/style-rtl.css +73 -3
- package/build-style/style.css +73 -3
- package/build-types/components/media-upload-modal/index.d.ts.map +1 -1
- package/build-types/components/media-upload-modal/upload-status-popover.d.ts +15 -0
- package/build-types/components/media-upload-modal/upload-status-popover.d.ts.map +1 -0
- package/build-types/components/media-upload-modal/use-invalidate-attachment-resolutions.d.ts +22 -0
- package/build-types/components/media-upload-modal/use-invalidate-attachment-resolutions.d.ts.map +1 -0
- package/build-types/components/media-upload-modal/use-upload-status.d.ts +41 -0
- package/build-types/components/media-upload-modal/use-upload-status.d.ts.map +1 -0
- package/package.json +17 -15
- package/src/components/media-upload-modal/index.tsx +129 -107
- package/src/components/media-upload-modal/style.scss +88 -3
- package/src/components/media-upload-modal/test/use-upload-status.test.ts +501 -0
- package/src/components/media-upload-modal/upload-status-popover.tsx +155 -0
- package/src/components/media-upload-modal/use-invalidate-attachment-resolutions.ts +50 -0
- package/src/components/media-upload-modal/use-upload-status.ts +203 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook for tracking media upload status with batch-scoped callbacks.
|
|
3
|
+
*
|
|
4
|
+
* This is a transitional layer that manually tracks upload progress using
|
|
5
|
+
* local state. The @wordpress/upload-media package provides a Redux-based
|
|
6
|
+
* store with richer capabilities (per-file progress, pause/resume, retry,
|
|
7
|
+
* concurrency control, client-side processing). When the media upload modal
|
|
8
|
+
* adopts @wordpress/upload-media, this hook can be replaced by selectors
|
|
9
|
+
* from that store (getItems, isBatchUploaded, getItemProgress, etc.) while
|
|
10
|
+
* keeping the same return interface.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* WordPress dependencies
|
|
15
|
+
*/
|
|
16
|
+
import { useState, useCallback } from '@wordpress/element';
|
|
17
|
+
import { isBlobURL } from '@wordpress/blob';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Internal dependencies
|
|
21
|
+
*/
|
|
22
|
+
import type { Attachment } from '../../utils/types';
|
|
23
|
+
import { UploadError } from '../../utils/upload-error';
|
|
24
|
+
import type { UploadingFile } from './upload-status-popover';
|
|
25
|
+
|
|
26
|
+
let idCounter = 0;
|
|
27
|
+
let batchIdCounter = 0;
|
|
28
|
+
|
|
29
|
+
interface UseUploadStatusOptions {
|
|
30
|
+
onBatchComplete?: ( attachments: Partial< Attachment >[] ) => void;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface RegisterBatchResult {
|
|
34
|
+
onFileChange: ( attachments: Partial< Attachment >[] ) => void;
|
|
35
|
+
onError: ( error: Error ) => void;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface UseUploadStatusReturn {
|
|
39
|
+
/** Current list of all tracked files. */
|
|
40
|
+
uploadingFiles: UploadingFile[];
|
|
41
|
+
/**
|
|
42
|
+
* Register a new batch of files for tracking.
|
|
43
|
+
* Returns batch-scoped onFileChange and onError callbacks.
|
|
44
|
+
*/
|
|
45
|
+
registerBatch: ( files: File[] ) => RegisterBatchResult;
|
|
46
|
+
/** Remove a single error entry by file id. */
|
|
47
|
+
dismissError: ( fileId: string ) => void;
|
|
48
|
+
/** Remove all uploaded (completed) entries from the list. */
|
|
49
|
+
clearCompleted: () => void;
|
|
50
|
+
/** True when tracked entries exist but none are still uploading. */
|
|
51
|
+
allComplete: boolean;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function useUploadStatus( {
|
|
55
|
+
onBatchComplete,
|
|
56
|
+
}: UseUploadStatusOptions = {} ): UseUploadStatusReturn {
|
|
57
|
+
const [ uploadingFiles, setUploadingFiles ] = useState< UploadingFile[] >(
|
|
58
|
+
[]
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
const clearCompleted = useCallback( () => {
|
|
62
|
+
setUploadingFiles( ( prev ) =>
|
|
63
|
+
prev.filter( ( item ) => item.status !== 'uploaded' )
|
|
64
|
+
);
|
|
65
|
+
}, [] );
|
|
66
|
+
|
|
67
|
+
const dismissError = useCallback( ( fileId: string ) => {
|
|
68
|
+
setUploadingFiles( ( prev ) =>
|
|
69
|
+
prev.filter( ( item ) => item.id !== fileId )
|
|
70
|
+
);
|
|
71
|
+
}, [] );
|
|
72
|
+
|
|
73
|
+
const registerBatch = useCallback(
|
|
74
|
+
( files: File[] ): RegisterBatchResult => {
|
|
75
|
+
const batchId = String( ++batchIdCounter );
|
|
76
|
+
const batchSize = files.length;
|
|
77
|
+
|
|
78
|
+
const newEntries: UploadingFile[] = files.map( ( file ) => ( {
|
|
79
|
+
id: String( ++idCounter ),
|
|
80
|
+
batchId,
|
|
81
|
+
name: file.name,
|
|
82
|
+
status: 'uploading' as const,
|
|
83
|
+
} ) );
|
|
84
|
+
|
|
85
|
+
setUploadingFiles( ( prev ) => [ ...prev, ...newEntries ] );
|
|
86
|
+
|
|
87
|
+
// Track successes and errors separately. onFileChange receives
|
|
88
|
+
// the full current array each time (not incremental), so we
|
|
89
|
+
// store the latest count rather than accumulating. The batch
|
|
90
|
+
// is complete when successCount + errorCount >= batchSize.
|
|
91
|
+
let successCount = 0;
|
|
92
|
+
let errorCount = 0;
|
|
93
|
+
let batchDone = false;
|
|
94
|
+
let successAttachments: Partial< Attachment >[] = [];
|
|
95
|
+
|
|
96
|
+
const completeBatchIfDone = () => {
|
|
97
|
+
if ( batchDone || successCount + errorCount < batchSize ) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
batchDone = true;
|
|
102
|
+
|
|
103
|
+
// Mark any remaining 'uploading' entries in this batch
|
|
104
|
+
// as 'uploaded' (these are the successful ones).
|
|
105
|
+
setUploadingFiles( ( prev ) =>
|
|
106
|
+
prev.map( ( item ) =>
|
|
107
|
+
item.batchId === batchId && item.status === 'uploading'
|
|
108
|
+
? {
|
|
109
|
+
...item,
|
|
110
|
+
status: 'uploaded' as const,
|
|
111
|
+
}
|
|
112
|
+
: item
|
|
113
|
+
)
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
onBatchComplete?.( successAttachments );
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const onFileChange = ( attachments: Partial< Attachment >[] ) => {
|
|
120
|
+
if ( batchDone ) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Ignore intermediate calls where some files still have
|
|
125
|
+
// blob URLs (not yet uploaded to the server).
|
|
126
|
+
const allReal = attachments.every(
|
|
127
|
+
( attachment ) =>
|
|
128
|
+
attachment.id &&
|
|
129
|
+
attachment.url &&
|
|
130
|
+
! isBlobURL( attachment.url )
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
if ( ! allReal ) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Store the latest success count and attachments.
|
|
138
|
+
// onFileChange receives the full array each time, so
|
|
139
|
+
// we replace rather than accumulate.
|
|
140
|
+
successCount = attachments.length;
|
|
141
|
+
successAttachments = attachments;
|
|
142
|
+
|
|
143
|
+
completeBatchIfDone();
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
const onError = ( error: Error ) => {
|
|
147
|
+
// uploadMedia always wraps errors in UploadError, which
|
|
148
|
+
// carries the originating File so we can match it back to
|
|
149
|
+
// the correct item. A custom onUpload prop could pass a
|
|
150
|
+
// plain Error instead — in that case fileName is undefined
|
|
151
|
+
// and no entry will be visually marked as errored, but the
|
|
152
|
+
// batch will still complete correctly via errorCount.
|
|
153
|
+
const fileName =
|
|
154
|
+
error instanceof UploadError ? error.file.name : undefined;
|
|
155
|
+
|
|
156
|
+
// Find the first still-uploading entry in this batch whose
|
|
157
|
+
// name matches the failed file and mark it as errored.
|
|
158
|
+
// Falls back to the first uploading entry in the batch
|
|
159
|
+
// when no filename is available. The `matched` flag
|
|
160
|
+
// ensures only one entry is updated even when duplicate
|
|
161
|
+
// filenames exist in the same batch.
|
|
162
|
+
setUploadingFiles( ( prev ) => {
|
|
163
|
+
let matched = false;
|
|
164
|
+
return prev.map( ( item ) => {
|
|
165
|
+
if (
|
|
166
|
+
! matched &&
|
|
167
|
+
item.batchId === batchId &&
|
|
168
|
+
item.status === 'uploading' &&
|
|
169
|
+
( ! fileName || item.name === fileName )
|
|
170
|
+
) {
|
|
171
|
+
matched = true;
|
|
172
|
+
return {
|
|
173
|
+
...item,
|
|
174
|
+
status: 'error' as const,
|
|
175
|
+
error: error.message,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
return item;
|
|
179
|
+
} );
|
|
180
|
+
} );
|
|
181
|
+
|
|
182
|
+
errorCount++;
|
|
183
|
+
|
|
184
|
+
completeBatchIfDone();
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
return { onFileChange, onError };
|
|
188
|
+
},
|
|
189
|
+
[ onBatchComplete ]
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
const allComplete =
|
|
193
|
+
uploadingFiles.length > 0 &&
|
|
194
|
+
uploadingFiles.every( ( item ) => item.status !== 'uploading' );
|
|
195
|
+
|
|
196
|
+
return {
|
|
197
|
+
uploadingFiles,
|
|
198
|
+
registerBatch,
|
|
199
|
+
dismissError,
|
|
200
|
+
clearCompleted,
|
|
201
|
+
allComplete,
|
|
202
|
+
};
|
|
203
|
+
}
|