@orthacms/media-admin 0.0.0-reserve.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/LICENSE +21 -0
- package/README.md +7 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/dist/lib/components/AssetActionsMenu/index.d.ts +37 -0
- package/dist/lib/components/AssetActionsMenu/index.d.ts.map +1 -0
- package/dist/lib/components/AssetActionsMenu/index.js +57 -0
- package/dist/lib/components/AssetDetailDrawer/MetaRow/index.d.ts +11 -0
- package/dist/lib/components/AssetDetailDrawer/MetaRow/index.d.ts.map +1 -0
- package/dist/lib/components/AssetDetailDrawer/MetaRow/index.js +9 -0
- package/dist/lib/components/AssetDetailDrawer/index.d.ts +28 -0
- package/dist/lib/components/AssetDetailDrawer/index.d.ts.map +1 -0
- package/dist/lib/components/AssetDetailDrawer/index.js +93 -0
- package/dist/lib/components/EntryMediaTab/MediaFieldSection/index.d.ts +28 -0
- package/dist/lib/components/EntryMediaTab/MediaFieldSection/index.d.ts.map +1 -0
- package/dist/lib/components/EntryMediaTab/MediaFieldSection/index.js +52 -0
- package/dist/lib/components/EntryMediaTab/index.d.ts +11 -0
- package/dist/lib/components/EntryMediaTab/index.d.ts.map +1 -0
- package/dist/lib/components/EntryMediaTab/index.js +31 -0
- package/dist/lib/components/FolderActionsMenu/index.d.ts +21 -0
- package/dist/lib/components/FolderActionsMenu/index.d.ts.map +1 -0
- package/dist/lib/components/FolderActionsMenu/index.js +25 -0
- package/dist/lib/components/MediaAltTextWidget/index.d.ts +9 -0
- package/dist/lib/components/MediaAltTextWidget/index.d.ts.map +1 -0
- package/dist/lib/components/MediaAltTextWidget/index.js +66 -0
- package/dist/lib/components/MediaBreadcrumbs/index.d.ts +11 -0
- package/dist/lib/components/MediaBreadcrumbs/index.d.ts.map +1 -0
- package/dist/lib/components/MediaEmptyState/index.d.ts +13 -0
- package/dist/lib/components/MediaEmptyState/index.d.ts.map +1 -0
- package/dist/lib/components/MediaEmptyState/index.js +38 -0
- package/dist/lib/components/MediaFieldControl/MediaFieldItem/index.d.ts +34 -0
- package/dist/lib/components/MediaFieldControl/MediaFieldItem/index.d.ts.map +1 -0
- package/dist/lib/components/MediaFieldControl/MediaFieldItem/index.js +117 -0
- package/dist/lib/components/MediaFieldControl/index.d.ts +61 -0
- package/dist/lib/components/MediaFieldControl/index.d.ts.map +1 -0
- package/dist/lib/components/MediaFieldControl/index.js +422 -0
- package/dist/lib/components/MediaFoldersNav/MediaFolderTreeItem/index.d.ts +17 -0
- package/dist/lib/components/MediaFoldersNav/MediaFolderTreeItem/index.d.ts.map +1 -0
- package/dist/lib/components/MediaFoldersNav/MediaFolderTreeItem/index.js +37 -0
- package/dist/lib/components/MediaFoldersNav/index.d.ts +22 -0
- package/dist/lib/components/MediaFoldersNav/index.d.ts.map +1 -0
- package/dist/lib/components/MediaFoldersNav/index.js +40 -0
- package/dist/lib/components/MediaGrid/MediaAssetCard/index.d.ts +18 -0
- package/dist/lib/components/MediaGrid/MediaAssetCard/index.d.ts.map +1 -0
- package/dist/lib/components/MediaGrid/MediaAssetCard/index.js +29 -0
- package/dist/lib/components/MediaGrid/MediaFolderCard/index.d.ts +16 -0
- package/dist/lib/components/MediaGrid/MediaFolderCard/index.d.ts.map +1 -0
- package/dist/lib/components/MediaGrid/MediaFolderCard/index.js +25 -0
- package/dist/lib/components/MediaGrid/index.d.ts +24 -0
- package/dist/lib/components/MediaGrid/index.d.ts.map +1 -0
- package/dist/lib/components/MediaGrid/index.js +20 -0
- package/dist/lib/components/MediaKindBadge/index.d.ts +10 -0
- package/dist/lib/components/MediaKindBadge/index.d.ts.map +1 -0
- package/dist/lib/components/MediaKindBadge/index.js +30 -0
- package/dist/lib/components/MediaKindIcon/index.d.ts +11 -0
- package/dist/lib/components/MediaKindIcon/index.d.ts.map +1 -0
- package/dist/lib/components/MediaKindIcon/index.js +20 -0
- package/dist/lib/components/MediaLibrarySkeleton/index.d.ts +18 -0
- package/dist/lib/components/MediaLibrarySkeleton/index.d.ts.map +1 -0
- package/dist/lib/components/MediaLibrarySkeleton/index.js +43 -0
- package/dist/lib/components/MediaPagination/index.d.ts +26 -0
- package/dist/lib/components/MediaPagination/index.d.ts.map +1 -0
- package/dist/lib/components/MediaPagination/index.js +52 -0
- package/dist/lib/components/MediaPickerDialog/MediaPickerTile/index.d.ts +17 -0
- package/dist/lib/components/MediaPickerDialog/MediaPickerTile/index.d.ts.map +1 -0
- package/dist/lib/components/MediaPickerDialog/MediaPickerTile/index.js +43 -0
- package/dist/lib/components/MediaPickerDialog/index.d.ts +21 -0
- package/dist/lib/components/MediaPickerDialog/index.d.ts.map +1 -0
- package/dist/lib/components/MediaPickerDialog/index.js +208 -0
- package/dist/lib/components/MediaSelectionBar/index.d.ts +18 -0
- package/dist/lib/components/MediaSelectionBar/index.d.ts.map +1 -0
- package/dist/lib/components/MediaSelectionBar/index.js +26 -0
- package/dist/lib/components/MediaStorageStat/index.d.ts +9 -0
- package/dist/lib/components/MediaStorageStat/index.d.ts.map +1 -0
- package/dist/lib/components/MediaStorageStat/index.js +31 -0
- package/dist/lib/components/MediaStorageWidget/index.d.ts +10 -0
- package/dist/lib/components/MediaStorageWidget/index.d.ts.map +1 -0
- package/dist/lib/components/MediaStorageWidget/index.js +93 -0
- package/dist/lib/components/MediaTable/MediaAssetRow/index.d.ts +17 -0
- package/dist/lib/components/MediaTable/MediaAssetRow/index.d.ts.map +1 -0
- package/dist/lib/components/MediaTable/MediaFolderRow/index.d.ts +16 -0
- package/dist/lib/components/MediaTable/MediaFolderRow/index.d.ts.map +1 -0
- package/dist/lib/components/MediaTable/index.d.ts +26 -0
- package/dist/lib/components/MediaTable/index.d.ts.map +1 -0
- package/dist/lib/components/MediaThumbnail/index.d.ts +19 -0
- package/dist/lib/components/MediaThumbnail/index.d.ts.map +1 -0
- package/dist/lib/components/MediaThumbnail/index.js +26 -0
- package/dist/lib/components/MediaToolbar/index.d.ts +19 -0
- package/dist/lib/components/MediaToolbar/index.d.ts.map +1 -0
- package/dist/lib/components/MediaToolbar/index.js +62 -0
- package/dist/lib/components/MediaTopBar/index.d.ts +20 -0
- package/dist/lib/components/MediaTopBar/index.d.ts.map +1 -0
- package/dist/lib/components/MediaTopBar/index.js +35 -0
- package/dist/lib/components/MediaUploadBanner/MediaUploadRow/index.d.ts +14 -0
- package/dist/lib/components/MediaUploadBanner/MediaUploadRow/index.d.ts.map +1 -0
- package/dist/lib/components/MediaUploadBanner/MediaUploadRow/index.js +51 -0
- package/dist/lib/components/MediaUploadBanner/index.d.ts +24 -0
- package/dist/lib/components/MediaUploadBanner/index.d.ts.map +1 -0
- package/dist/lib/components/MediaUploadBanner/index.js +99 -0
- package/dist/lib/components/MediaUploadsWidget/index.d.ts +7 -0
- package/dist/lib/components/MediaUploadsWidget/index.d.ts.map +1 -0
- package/dist/lib/components/MediaUploadsWidget/index.js +70 -0
- package/dist/lib/components/MoveAssetsDialog/DestinationOption/index.d.ts +19 -0
- package/dist/lib/components/MoveAssetsDialog/DestinationOption/index.d.ts.map +1 -0
- package/dist/lib/components/MoveAssetsDialog/DestinationOption/index.js +10 -0
- package/dist/lib/components/MoveAssetsDialog/index.d.ts +18 -0
- package/dist/lib/components/MoveAssetsDialog/index.d.ts.map +1 -0
- package/dist/lib/components/MoveAssetsDialog/index.js +41 -0
- package/dist/lib/components/NewFolderDialog/index.d.ts +13 -0
- package/dist/lib/components/NewFolderDialog/index.d.ts.map +1 -0
- package/dist/lib/components/NewFolderDialog/index.js +45 -0
- package/dist/lib/components/RenameDialog/index.d.ts +13 -0
- package/dist/lib/components/RenameDialog/index.d.ts.map +1 -0
- package/dist/lib/components/RenameDialog/index.js +37 -0
- package/dist/lib/components/UploadDialog/StagedFileRow/index.d.ts +25 -0
- package/dist/lib/components/UploadDialog/StagedFileRow/index.d.ts.map +1 -0
- package/dist/lib/components/UploadDialog/StagedFileRow/index.js +82 -0
- package/dist/lib/components/UploadDialog/index.d.ts +50 -0
- package/dist/lib/components/UploadDialog/index.d.ts.map +1 -0
- package/dist/lib/components/UploadDialog/index.js +120 -0
- package/dist/lib/components/WysiwygLibrarySource/index.d.ts +14 -0
- package/dist/lib/components/WysiwygLibrarySource/index.d.ts.map +1 -0
- package/dist/lib/components/WysiwygLibrarySource/index.js +28 -0
- package/dist/lib/components/WysiwygUploadSource/index.d.ts +25 -0
- package/dist/lib/components/WysiwygUploadSource/index.d.ts.map +1 -0
- package/dist/lib/components/WysiwygUploadSource/index.js +102 -0
- package/dist/lib/constants/index.d.ts +82 -0
- package/dist/lib/constants/index.d.ts.map +1 -0
- package/dist/lib/constants/index.js +75 -0
- package/dist/lib/hooks/useMediaInsights/index.d.ts +32 -0
- package/dist/lib/hooks/useMediaInsights/index.d.ts.map +1 -0
- package/dist/lib/hooks/useMediaInsights/index.js +85 -0
- package/dist/lib/hooks/useMediaLibrary/index.d.ts +83 -0
- package/dist/lib/hooks/useMediaLibrary/index.d.ts.map +1 -0
- package/dist/lib/hooks/useMediaLibrary/index.js +341 -0
- package/dist/lib/hooks/usePendingMediaUploads/index.d.ts +26 -0
- package/dist/lib/hooks/usePendingMediaUploads/index.d.ts.map +1 -0
- package/dist/lib/hooks/usePendingMediaUploads/index.js +217 -0
- package/dist/lib/hooks/useUploadQueue/index.d.ts +33 -0
- package/dist/lib/hooks/useUploadQueue/index.d.ts.map +1 -0
- package/dist/lib/hooks/useUploadQueue/index.js +218 -0
- package/dist/lib/hooks/useWysiwygAssetPicker/index.d.ts +23 -0
- package/dist/lib/hooks/useWysiwygAssetPicker/index.d.ts.map +1 -0
- package/dist/lib/infrastructure/apiMessage/index.d.ts +17 -0
- package/dist/lib/infrastructure/apiMessage/index.d.ts.map +1 -0
- package/dist/lib/infrastructure/apiMessage/index.js +30 -0
- package/dist/lib/infrastructure/httpMediaGateway/index.d.ts +9 -0
- package/dist/lib/infrastructure/httpMediaGateway/index.d.ts.map +1 -0
- package/dist/lib/infrastructure/httpMediaGateway/index.js +157 -0
- package/dist/lib/infrastructure/httpMediaInsightsGateway/index.d.ts +8 -0
- package/dist/lib/infrastructure/httpMediaInsightsGateway/index.d.ts.map +1 -0
- package/dist/lib/infrastructure/httpMediaInsightsGateway/index.js +35 -0
- package/dist/lib/infrastructure/mediaGateway/index.d.ts +120 -0
- package/dist/lib/infrastructure/mediaGateway/index.d.ts.map +1 -0
- package/dist/lib/infrastructure/mediaGateway/index.js +1 -0
- package/dist/lib/infrastructure/mediaInsightsGateway/index.d.ts +46 -0
- package/dist/lib/infrastructure/mediaInsightsGateway/index.d.ts.map +1 -0
- package/dist/lib/infrastructure/mediaInsightsGateway/index.js +6 -0
- package/dist/lib/infrastructure/mediaKeys/index.d.ts +23 -0
- package/dist/lib/infrastructure/mediaKeys/index.d.ts.map +1 -0
- package/dist/lib/infrastructure/mediaKeys/index.js +11 -0
- package/dist/lib/infrastructure/mediaMapper/index.d.ts +50 -0
- package/dist/lib/infrastructure/mediaMapper/index.d.ts.map +1 -0
- package/dist/lib/infrastructure/mediaMapper/index.js +39 -0
- package/dist/lib/pages/MediaLibraryPage/index.d.ts +23 -0
- package/dist/lib/pages/MediaLibraryPage/index.d.ts.map +1 -0
- package/dist/lib/pages/MediaLibraryPage/index.js +425 -0
- package/dist/lib/types/mediaAsset/index.d.ts +44 -0
- package/dist/lib/types/mediaAsset/index.d.ts.map +1 -0
- package/dist/lib/types/mediaAsset/index.js +1 -0
- package/dist/lib/types/mediaFieldDisplay/index.d.ts +48 -0
- package/dist/lib/types/mediaFieldDisplay/index.d.ts.map +1 -0
- package/dist/lib/types/mediaFieldDisplay/index.js +1 -0
- package/dist/lib/types/mediaFolder/index.d.ts +16 -0
- package/dist/lib/types/mediaFolder/index.d.ts.map +1 -0
- package/dist/lib/types/mediaFolder/index.js +1 -0
- package/dist/lib/types/pendingUpload/index.d.ts +38 -0
- package/dist/lib/types/pendingUpload/index.d.ts.map +1 -0
- package/dist/lib/types/pendingUpload/index.js +1 -0
- package/dist/lib/types/upload/index.d.ts +37 -0
- package/dist/lib/types/upload/index.d.ts.map +1 -0
- package/dist/lib/types/upload/index.js +1 -0
- package/dist/lib/utils/assetGradient/index.d.ts +8 -0
- package/dist/lib/utils/assetGradient/index.d.ts.map +1 -0
- package/dist/lib/utils/assetGradient/index.js +16 -0
- package/dist/lib/utils/assetHandlers/index.d.ts +11 -0
- package/dist/lib/utils/assetHandlers/index.d.ts.map +1 -0
- package/dist/lib/utils/assetHandlers/index.js +17 -0
- package/dist/lib/utils/folderContents/index.d.ts +17 -0
- package/dist/lib/utils/folderContents/index.d.ts.map +1 -0
- package/dist/lib/utils/folderContents/index.js +35 -0
- package/dist/lib/utils/folderTree/index.d.ts +13 -0
- package/dist/lib/utils/folderTree/index.d.ts.map +1 -0
- package/dist/lib/utils/folderTree/index.js +15 -0
- package/dist/lib/utils/formatBytes/index.d.ts +6 -0
- package/dist/lib/utils/formatBytes/index.d.ts.map +1 -0
- package/dist/lib/utils/formatBytes/index.js +17 -0
- package/dist/lib/utils/formatDuration/index.d.ts +6 -0
- package/dist/lib/utils/formatDuration/index.d.ts.map +1 -0
- package/dist/lib/utils/formatDuration/index.js +13 -0
- package/dist/lib/utils/kindFromMime/index.d.ts +10 -0
- package/dist/lib/utils/kindFromMime/index.d.ts.map +1 -0
- package/dist/lib/utils/kindFromMime/index.js +27 -0
- package/dist/lib/utils/mediaAccept/index.d.ts +30 -0
- package/dist/lib/utils/mediaAccept/index.d.ts.map +1 -0
- package/dist/lib/utils/mediaAccept/index.js +63 -0
- package/dist/lib/utils/mediaPlugin/index.d.ts +15 -0
- package/dist/lib/utils/mediaPlugin/index.d.ts.map +1 -0
- package/dist/lib/utils/mediaPlugin/index.js +177 -0
- package/dist/lib/utils/toWysiwygEmbed/index.d.ts +23 -0
- package/dist/lib/utils/toWysiwygEmbed/index.d.ts.map +1 -0
- package/dist/lib/utils/toWysiwygEmbed/index.js +44 -0
- package/package.json +50 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { UploadItem, UploadSummary } from '../../types/upload';
|
|
2
|
+
import type { StagedUpload } from '../../infrastructure/mediaGateway';
|
|
3
|
+
/**
|
|
4
|
+
* The Media Library's upload queue: turns a set of picked files into a list of
|
|
5
|
+
* progress-tracked items, uploading at most {@link UPLOAD_CONCURRENCY} at once.
|
|
6
|
+
*
|
|
7
|
+
* Each file is **its own request** (the gateway is one-file-per-call), so one
|
|
8
|
+
* rejection fails exactly one row and leaves the rest of the batch running —
|
|
9
|
+
* the old `Promise.all` lost the whole batch to a single bad file. Failed rows
|
|
10
|
+
* are individually retryable; in-flight ones are cancellable through an
|
|
11
|
+
* `AbortController`.
|
|
12
|
+
*
|
|
13
|
+
* The `File` objects and the runner's bookkeeping live in refs, not state: a
|
|
14
|
+
* progress event fires many times a second per file, and only the small
|
|
15
|
+
* {@link UploadItem} rows belong in a re-render.
|
|
16
|
+
*
|
|
17
|
+
* @param folderId - destination folder, captured **per file at enqueue time**
|
|
18
|
+
* so navigating away mid-upload can't redirect in-flight files to a folder
|
|
19
|
+
* the user merely happens to be looking at.
|
|
20
|
+
* @param onUploaded - called once per settled batch that may have changed the
|
|
21
|
+
* library (the caller invalidates its queries there — once, not per file).
|
|
22
|
+
* That includes a **cancelled** file: an abort only stops the client, so a
|
|
23
|
+
* body the server already finished reading is committed either way.
|
|
24
|
+
*/
|
|
25
|
+
export declare function useUploadQueue(folderId: string, onUploaded: () => void): {
|
|
26
|
+
items: UploadItem[];
|
|
27
|
+
summary: UploadSummary;
|
|
28
|
+
enqueue: (uploads: StagedUpload[]) => void;
|
|
29
|
+
retry: (id: string) => void;
|
|
30
|
+
cancel: (id: string) => void;
|
|
31
|
+
dismissSettled: () => void;
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/hooks/useUploadQueue/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AAiBtE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,IAAI;;;uBAiHrD,YAAY,EAAE;gBAsBnB,MAAM;iBAeN,MAAM;;EAuElB"}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import { defineMessages, useIntl } from 'react-intl';
|
|
3
|
+
import { UPLOAD_CONCURRENCY, UPLOAD_STATUS } from '../../constants';
|
|
4
|
+
import { httpMediaGateway } from '../../infrastructure/httpMediaGateway';
|
|
5
|
+
import { apiMessage } from '../../infrastructure/apiMessage';
|
|
6
|
+
/** Intl descriptor for an upload failure with no server-supplied reason. */
|
|
7
|
+
const messages = defineMessages({
|
|
8
|
+
failed: {
|
|
9
|
+
id: 'media.upload.failed',
|
|
10
|
+
defaultMessage: 'Upload failed'
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
/** Statuses that are over — the ones "Dismiss" clears. */
|
|
14
|
+
const SETTLED = [
|
|
15
|
+
UPLOAD_STATUS.Done,
|
|
16
|
+
UPLOAD_STATUS.Failed,
|
|
17
|
+
UPLOAD_STATUS.Cancelled
|
|
18
|
+
];
|
|
19
|
+
/**
|
|
20
|
+
* The Media Library's upload queue: turns a set of picked files into a list of
|
|
21
|
+
* progress-tracked items, uploading at most {@link UPLOAD_CONCURRENCY} at once.
|
|
22
|
+
*
|
|
23
|
+
* Each file is **its own request** (the gateway is one-file-per-call), so one
|
|
24
|
+
* rejection fails exactly one row and leaves the rest of the batch running —
|
|
25
|
+
* the old `Promise.all` lost the whole batch to a single bad file. Failed rows
|
|
26
|
+
* are individually retryable; in-flight ones are cancellable through an
|
|
27
|
+
* `AbortController`.
|
|
28
|
+
*
|
|
29
|
+
* The `File` objects and the runner's bookkeeping live in refs, not state: a
|
|
30
|
+
* progress event fires many times a second per file, and only the small
|
|
31
|
+
* {@link UploadItem} rows belong in a re-render.
|
|
32
|
+
*
|
|
33
|
+
* @param folderId - destination folder, captured **per file at enqueue time**
|
|
34
|
+
* so navigating away mid-upload can't redirect in-flight files to a folder
|
|
35
|
+
* the user merely happens to be looking at.
|
|
36
|
+
* @param onUploaded - called once per settled batch that may have changed the
|
|
37
|
+
* library (the caller invalidates its queries there — once, not per file).
|
|
38
|
+
* That includes a **cancelled** file: an abort only stops the client, so a
|
|
39
|
+
* body the server already finished reading is committed either way.
|
|
40
|
+
*/
|
|
41
|
+
export function useUploadQueue(folderId, onUploaded) {
|
|
42
|
+
const intl = useIntl();
|
|
43
|
+
const [items, setItems] = useState([]);
|
|
44
|
+
// --- Runner bookkeeping (refs: mutated mid-flight, never rendered) --------
|
|
45
|
+
/** id → the file to send, its description, and the folder it was queued for. */
|
|
46
|
+
const filesRef = useRef(new Map());
|
|
47
|
+
/** id → abort handle, present only while that file is on the wire. */
|
|
48
|
+
const abortsRef = useRef(new Map());
|
|
49
|
+
/** Ids waiting for a slot, oldest first. */
|
|
50
|
+
const waitingRef = useRef([]);
|
|
51
|
+
const activeRef = useRef(0);
|
|
52
|
+
/** Whether the current batch has produced a success worth invalidating for. */
|
|
53
|
+
const successRef = useRef(false);
|
|
54
|
+
const nextIdRef = useRef(0);
|
|
55
|
+
// `pump` and `runOne` call each other; the ref breaks the cycle without
|
|
56
|
+
// rebuilding either callback on every render.
|
|
57
|
+
const runOneRef = useRef(() => undefined);
|
|
58
|
+
const onUploadedRef = useRef(onUploaded);
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
onUploadedRef.current = onUploaded;
|
|
61
|
+
}, [onUploaded]);
|
|
62
|
+
const patch = useCallback((id, next) => {
|
|
63
|
+
setItems((prev) => prev.map((item) => (item.id === id ? { ...item, ...next } : item)));
|
|
64
|
+
}, []);
|
|
65
|
+
/** Fills every free slot, then settles the batch if nothing is left. */
|
|
66
|
+
const pump = useCallback(() => {
|
|
67
|
+
while (activeRef.current < UPLOAD_CONCURRENCY &&
|
|
68
|
+
waitingRef.current.length > 0) {
|
|
69
|
+
const id = waitingRef.current.shift();
|
|
70
|
+
if (id)
|
|
71
|
+
runOneRef.current(id);
|
|
72
|
+
}
|
|
73
|
+
if (activeRef.current === 0 && waitingRef.current.length === 0) {
|
|
74
|
+
if (successRef.current) {
|
|
75
|
+
successRef.current = false;
|
|
76
|
+
onUploadedRef.current();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}, []);
|
|
80
|
+
const runOne = useCallback((id) => {
|
|
81
|
+
const entry = filesRef.current.get(id);
|
|
82
|
+
if (!entry)
|
|
83
|
+
return;
|
|
84
|
+
const controller = new AbortController();
|
|
85
|
+
abortsRef.current.set(id, controller);
|
|
86
|
+
// Claim the slot synchronously — `pump`'s loop checks this counter
|
|
87
|
+
// again on its very next iteration.
|
|
88
|
+
activeRef.current += 1;
|
|
89
|
+
patch(id, { status: UPLOAD_STATUS.Uploading, progress: 0 });
|
|
90
|
+
void httpMediaGateway
|
|
91
|
+
.uploadFile(entry.folderId, entry.file, {
|
|
92
|
+
alt: entry.alt,
|
|
93
|
+
signal: controller.signal,
|
|
94
|
+
onProgress: (percent) => patch(id, { progress: percent })
|
|
95
|
+
})
|
|
96
|
+
.then(() => {
|
|
97
|
+
successRef.current = true;
|
|
98
|
+
patch(id, {
|
|
99
|
+
status: UPLOAD_STATUS.Done,
|
|
100
|
+
progress: 100,
|
|
101
|
+
error: undefined
|
|
102
|
+
});
|
|
103
|
+
})
|
|
104
|
+
.catch((error) => {
|
|
105
|
+
if (controller.signal.aborted) {
|
|
106
|
+
patch(id, { status: UPLOAD_STATUS.Cancelled });
|
|
107
|
+
// Cancelling aborts the *client*. If the server had
|
|
108
|
+
// already received the whole body it commits anyway, so
|
|
109
|
+
// the asset exists — it was simply never fetched, and
|
|
110
|
+
// stayed invisible until a manual reload. Refetch on a
|
|
111
|
+
// cancel too, so the grid tells the truth. (Deleting the
|
|
112
|
+
// committed asset isn't possible from here: the response
|
|
113
|
+
// that carries its id is what the abort threw away.)
|
|
114
|
+
successRef.current = true;
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
patch(id, {
|
|
118
|
+
status: UPLOAD_STATUS.Failed,
|
|
119
|
+
progress: 0,
|
|
120
|
+
// The API's own sentence ("File exceeds the maximum
|
|
121
|
+
// upload size.") rather than the transport's status
|
|
122
|
+
// line, which told the author nothing actionable.
|
|
123
|
+
error: apiMessage(error) ??
|
|
124
|
+
intl.formatMessage(messages.failed)
|
|
125
|
+
});
|
|
126
|
+
})
|
|
127
|
+
.finally(() => {
|
|
128
|
+
abortsRef.current.delete(id);
|
|
129
|
+
activeRef.current -= 1;
|
|
130
|
+
pump();
|
|
131
|
+
});
|
|
132
|
+
}, [intl, patch, pump]);
|
|
133
|
+
useEffect(() => {
|
|
134
|
+
runOneRef.current = runOne;
|
|
135
|
+
}, [runOne]);
|
|
136
|
+
/** Queues files for the folder open **now** and starts uploading. */
|
|
137
|
+
const enqueue = useCallback((uploads) => {
|
|
138
|
+
if (uploads.length === 0)
|
|
139
|
+
return;
|
|
140
|
+
const queued = uploads.map(({ file, alt }) => {
|
|
141
|
+
const id = `upload-${nextIdRef.current++}`;
|
|
142
|
+
filesRef.current.set(id, { file, alt, folderId });
|
|
143
|
+
waitingRef.current.push(id);
|
|
144
|
+
return {
|
|
145
|
+
id,
|
|
146
|
+
name: file.name,
|
|
147
|
+
size: file.size,
|
|
148
|
+
progress: 0,
|
|
149
|
+
status: UPLOAD_STATUS.Pending
|
|
150
|
+
};
|
|
151
|
+
});
|
|
152
|
+
setItems((prev) => [...prev, ...queued]);
|
|
153
|
+
pump();
|
|
154
|
+
}, [folderId, pump]);
|
|
155
|
+
/** Re-queues one failed or cancelled file. */
|
|
156
|
+
const retry = useCallback((id) => {
|
|
157
|
+
if (!filesRef.current.has(id))
|
|
158
|
+
return;
|
|
159
|
+
patch(id, {
|
|
160
|
+
status: UPLOAD_STATUS.Pending,
|
|
161
|
+
progress: 0,
|
|
162
|
+
error: undefined
|
|
163
|
+
});
|
|
164
|
+
waitingRef.current.push(id);
|
|
165
|
+
pump();
|
|
166
|
+
}, [patch, pump]);
|
|
167
|
+
/** Aborts an in-flight file, or drops one that hasn't started. */
|
|
168
|
+
const cancel = useCallback((id) => {
|
|
169
|
+
const controller = abortsRef.current.get(id);
|
|
170
|
+
if (controller) {
|
|
171
|
+
// The abort rejects the request; `runOne`'s catch marks it.
|
|
172
|
+
controller.abort();
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
waitingRef.current = waitingRef.current.filter((queued) => queued !== id);
|
|
176
|
+
patch(id, { status: UPLOAD_STATUS.Cancelled });
|
|
177
|
+
}, [patch]);
|
|
178
|
+
/** Clears every finished row, leaving anything still uploading. */
|
|
179
|
+
const dismissSettled = useCallback(() => {
|
|
180
|
+
setItems((prev) => {
|
|
181
|
+
for (const item of prev) {
|
|
182
|
+
if (SETTLED.includes(item.status)) {
|
|
183
|
+
filesRef.current.delete(item.id);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return prev.filter((item) => !SETTLED.includes(item.status));
|
|
187
|
+
});
|
|
188
|
+
}, []);
|
|
189
|
+
// Abort anything still in flight if the library unmounts, so a navigation
|
|
190
|
+
// away doesn't leave orphan requests writing to a dead component.
|
|
191
|
+
useEffect(() => {
|
|
192
|
+
const aborts = abortsRef.current;
|
|
193
|
+
return () => {
|
|
194
|
+
for (const controller of aborts.values())
|
|
195
|
+
controller.abort();
|
|
196
|
+
};
|
|
197
|
+
}, []);
|
|
198
|
+
const summary = useMemo(() => {
|
|
199
|
+
const counted = items.filter((item) => item.status !== UPLOAD_STATUS.Cancelled);
|
|
200
|
+
const totalBytes = counted.reduce((sum, item) => sum + item.size, 0);
|
|
201
|
+
const sentBytes = counted.reduce((sum, item) => sum + (item.size * item.progress) / 100, 0);
|
|
202
|
+
return {
|
|
203
|
+
inFlight: counted.filter((item) => item.status === UPLOAD_STATUS.Pending ||
|
|
204
|
+
item.status === UPLOAD_STATUS.Uploading).length,
|
|
205
|
+
done: counted.filter((item) => item.status === UPLOAD_STATUS.Done)
|
|
206
|
+
.length,
|
|
207
|
+
failed: counted.filter((item) => item.status === UPLOAD_STATUS.Failed).length,
|
|
208
|
+
total: counted.length,
|
|
209
|
+
// A zero-byte file would make this 0/0; treat an empty batch as done.
|
|
210
|
+
percent: totalBytes === 0
|
|
211
|
+
? 100
|
|
212
|
+
: Math.round((sentBytes / totalBytes) * 100),
|
|
213
|
+
active: counted.some((item) => item.status === UPLOAD_STATUS.Pending ||
|
|
214
|
+
item.status === UPLOAD_STATUS.Uploading)
|
|
215
|
+
};
|
|
216
|
+
}, [items]);
|
|
217
|
+
return { items, summary, enqueue, retry, cancel, dismissSettled };
|
|
218
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { AssetPicker } from '@ortha-cms/content-admin';
|
|
2
|
+
/** Stable id of this contribution, so the slot registration reads once. */
|
|
3
|
+
export declare const WYSIWYG_ASSET_PICKER_ID = "media.assetPicker";
|
|
4
|
+
/**
|
|
5
|
+
* The Media Library, offered to content-admin's rich-text field as an
|
|
6
|
+
* {@link AssetPicker} — how an image block in a `wysiwyg` field gets a URL from
|
|
7
|
+
* the library instead of only from a pasted link.
|
|
8
|
+
*
|
|
9
|
+
* The editor's port is a **promise**, and the library's picker is a **dialog**,
|
|
10
|
+
* so this adapts one to the other: `pick()` opens the dialog and parks the
|
|
11
|
+
* resolver until the author confirms or dismisses. Everything a dialog needs to
|
|
12
|
+
* exist — the open flag, the mounted element — stays here; the editor sees one
|
|
13
|
+
* async function and knows nothing about React trees.
|
|
14
|
+
*
|
|
15
|
+
* The picked asset's URL is stored **as the image's `src`**, not as an asset id.
|
|
16
|
+
* That is the whole contract of the field: its value is HTML any consumer can
|
|
17
|
+
* render, with nothing to resolve. It also means an asset deleted from the
|
|
18
|
+
* library leaves a dead image in a document — worth stating plainly, because the
|
|
19
|
+
* alternative (an id the delivery layer resolves) would make the stored value
|
|
20
|
+
* unusable without this CMS.
|
|
21
|
+
*/
|
|
22
|
+
export declare function useWysiwygAssetPicker(): AssetPicker;
|
|
23
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/hooks/useWysiwygAssetPicker/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAqB,MAAM,0BAA0B,CAAC;AAK/E,2EAA2E;AAC3E,eAAO,MAAM,uBAAuB,sBAAsB,CAAC;AAK3D;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,qBAAqB,IAAI,WAAW,CA4CnD"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The server's own explanation of a failure, or `undefined` when there isn't
|
|
3
|
+
* one.
|
|
4
|
+
*
|
|
5
|
+
* `toApiError` puts the **transport's** description in `ApiError.message`
|
|
6
|
+
* ("Request failed with status code 413") and the parsed **body** in
|
|
7
|
+
* `details` — so a call site that reads `error.message` shows the user a status
|
|
8
|
+
* code where the API sent a sentence ("File exceeds the maximum upload size.",
|
|
9
|
+
* "Invalid file name: a/b.txt"). Everything media renders to a human goes
|
|
10
|
+
* through here instead, falling back to its own copy when the body carries
|
|
11
|
+
* nothing usable.
|
|
12
|
+
*
|
|
13
|
+
* A `ValidationPipe` rejection puts several strings in `message`; the first is
|
|
14
|
+
* the one that names the offending field, so that is what is shown.
|
|
15
|
+
*/
|
|
16
|
+
export declare function apiMessage(error: unknown): string | undefined;
|
|
17
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/infrastructure/apiMessage/index.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAY7D"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { ApiError } from '@orthacms/utils-admin';
|
|
2
|
+
/**
|
|
3
|
+
* The server's own explanation of a failure, or `undefined` when there isn't
|
|
4
|
+
* one.
|
|
5
|
+
*
|
|
6
|
+
* `toApiError` puts the **transport's** description in `ApiError.message`
|
|
7
|
+
* ("Request failed with status code 413") and the parsed **body** in
|
|
8
|
+
* `details` — so a call site that reads `error.message` shows the user a status
|
|
9
|
+
* code where the API sent a sentence ("File exceeds the maximum upload size.",
|
|
10
|
+
* "Invalid file name: a/b.txt"). Everything media renders to a human goes
|
|
11
|
+
* through here instead, falling back to its own copy when the body carries
|
|
12
|
+
* nothing usable.
|
|
13
|
+
*
|
|
14
|
+
* A `ValidationPipe` rejection puts several strings in `message`; the first is
|
|
15
|
+
* the one that names the offending field, so that is what is shown.
|
|
16
|
+
*/
|
|
17
|
+
export function apiMessage(error) {
|
|
18
|
+
if (!(error instanceof ApiError))
|
|
19
|
+
return undefined;
|
|
20
|
+
const body = error.details;
|
|
21
|
+
const message = body?.message;
|
|
22
|
+
if (typeof message === 'string' && message.trim() !== '')
|
|
23
|
+
return message;
|
|
24
|
+
if (Array.isArray(message)) {
|
|
25
|
+
const first = message.find((entry) => typeof entry === 'string' && entry.trim() !== '');
|
|
26
|
+
if (typeof first === 'string')
|
|
27
|
+
return first;
|
|
28
|
+
}
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { MediaGateway } from '../mediaGateway';
|
|
2
|
+
/**
|
|
3
|
+
* The media {@link MediaGateway} over `apiClient`. The one place the plugin
|
|
4
|
+
* touches HTTP: it maps responses through the ACL, normalizes failures to
|
|
5
|
+
* `ApiError`, and translates the root sentinel to/from the wire's `null`.
|
|
6
|
+
* `apiClient` attaches `X-Workspace-Id` automatically inside the workspace shell.
|
|
7
|
+
*/
|
|
8
|
+
export declare const httpMediaGateway: MediaGateway;
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/infrastructure/httpMediaGateway/index.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAKR,YAAY,EAKf,MAAM,iBAAiB,CAAC;AAQzB;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,EAAE,YAuK9B,CAAC"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { apiClient, toApiError } from '@orthacms/utils-admin';
|
|
2
|
+
import { KIND_FILTER_ALL, ROOT_FOLDER_ID } from '../../constants';
|
|
3
|
+
import { toMediaAsset, toMediaFolder } from '../mediaMapper';
|
|
4
|
+
/** Translates the admin root sentinel to `undefined` (backend root = no id). */
|
|
5
|
+
function folderParam(folderId) {
|
|
6
|
+
return folderId === ROOT_FOLDER_ID ? undefined : folderId;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* The media {@link MediaGateway} over `apiClient`. The one place the plugin
|
|
10
|
+
* touches HTTP: it maps responses through the ACL, normalizes failures to
|
|
11
|
+
* `ApiError`, and translates the root sentinel to/from the wire's `null`.
|
|
12
|
+
* `apiClient` attaches `X-Workspace-Id` automatically inside the workspace shell.
|
|
13
|
+
*/
|
|
14
|
+
export const httpMediaGateway = {
|
|
15
|
+
async listFolders() {
|
|
16
|
+
try {
|
|
17
|
+
const { data } = await apiClient.get('/media/folders');
|
|
18
|
+
const folderCounts = new Map();
|
|
19
|
+
for (const folder of data.folders) {
|
|
20
|
+
folderCounts.set(folder.id, folder.assetCount);
|
|
21
|
+
}
|
|
22
|
+
folderCounts.set(ROOT_FOLDER_ID, data.rootAssetCount);
|
|
23
|
+
return { folders: data.folders.map(toMediaFolder), folderCounts };
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
throw toApiError(error);
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
async listAssets({ folderId, search, kind, sort, page, pageSize }) {
|
|
30
|
+
try {
|
|
31
|
+
const term = search?.trim();
|
|
32
|
+
const { data } = await apiClient.get('/media/assets', {
|
|
33
|
+
// Omitted rather than sent empty: `axios` drops an
|
|
34
|
+
// `undefined` param, and the server reads a missing `search`
|
|
35
|
+
// as "no search" while `search=` would be a blank term it
|
|
36
|
+
// has to decide about. `kind` is omitted on the `all`
|
|
37
|
+
// sentinel for the same reason — the wire says nothing
|
|
38
|
+
// rather than saying "every kind".
|
|
39
|
+
params: {
|
|
40
|
+
folderId: folderParam(folderId),
|
|
41
|
+
search: term || undefined,
|
|
42
|
+
kind: kind === KIND_FILTER_ALL ? undefined : kind,
|
|
43
|
+
sort,
|
|
44
|
+
page,
|
|
45
|
+
pageSize
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
return {
|
|
49
|
+
items: data.items.map(toMediaAsset),
|
|
50
|
+
total: data.total,
|
|
51
|
+
page: data.page,
|
|
52
|
+
pageSize: data.pageSize
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
throw toApiError(error);
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
async createFolder({ name, parentId }) {
|
|
60
|
+
try {
|
|
61
|
+
await apiClient.post('/media/folders', {
|
|
62
|
+
name,
|
|
63
|
+
parentId: folderParam(parentId)
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
throw toApiError(error);
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
async renameFolder({ id, name }) {
|
|
71
|
+
try {
|
|
72
|
+
await apiClient.patch(`/media/folders/${id}`, { name });
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
throw toApiError(error);
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
async deleteFolder(id) {
|
|
79
|
+
try {
|
|
80
|
+
await apiClient.delete(`/media/folders/${id}`);
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
throw toApiError(error);
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
async uploadFile(folderId, file, options) {
|
|
87
|
+
const target = folderParam(folderId);
|
|
88
|
+
const form = new FormData();
|
|
89
|
+
form.append('file', file);
|
|
90
|
+
if (target)
|
|
91
|
+
form.append('folderId', target);
|
|
92
|
+
// Only sent when the author wrote one — an empty part would be a blank
|
|
93
|
+
// description, which the server normalizes away but which would still
|
|
94
|
+
// read as "described" to anyone auditing the request.
|
|
95
|
+
if (options?.alt && options.alt.trim() !== '') {
|
|
96
|
+
form.append('alt', options.alt);
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
const { data } = await apiClient.post('/media/assets', form, {
|
|
100
|
+
signal: options?.signal,
|
|
101
|
+
onUploadProgress: (event) => {
|
|
102
|
+
// `total` is absent on some proxies/streams; without it a
|
|
103
|
+
// percentage would be a lie, so report nothing and let the
|
|
104
|
+
// caller keep showing indeterminate progress.
|
|
105
|
+
if (!event.total)
|
|
106
|
+
return;
|
|
107
|
+
options?.onProgress?.(Math.round((event.loaded / event.total) * 100));
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
return toMediaAsset(data);
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
throw toApiError(error);
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
async renameAsset({ id, name }) {
|
|
117
|
+
try {
|
|
118
|
+
await apiClient.patch(`/media/assets/${id}`, { name });
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
throw toApiError(error);
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
async updateAsset({ id, alt }) {
|
|
125
|
+
try {
|
|
126
|
+
await apiClient.patch(`/media/assets/${id}`, { alt });
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
throw toApiError(error);
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
async moveAssets({ ids, folderId }) {
|
|
133
|
+
const target = folderId === ROOT_FOLDER_ID ? null : folderId;
|
|
134
|
+
try {
|
|
135
|
+
await Promise.all(ids.map((id) => apiClient.patch(`/media/assets/${id}`, { folderId: target })));
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
throw toApiError(error);
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
async duplicateAssets(ids) {
|
|
142
|
+
try {
|
|
143
|
+
await Promise.all(ids.map((id) => apiClient.post(`/media/assets/${id}/duplicate`)));
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
throw toApiError(error);
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
async deleteAssets(ids) {
|
|
150
|
+
try {
|
|
151
|
+
await apiClient.delete('/media/assets', { data: { ids } });
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
throw toApiError(error);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { MediaInsightsGateway } from '../mediaInsightsGateway';
|
|
2
|
+
/**
|
|
3
|
+
* HTTP implementation of {@link MediaInsightsGateway} over the shared
|
|
4
|
+
* `apiClient` — same-origin, cookie-authed, implicitly workspace-scoped via the
|
|
5
|
+
* ambient `X-Workspace-Id` header. Failures normalise to `ApiError`.
|
|
6
|
+
*/
|
|
7
|
+
export declare const httpMediaInsightsGateway: MediaInsightsGateway;
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/infrastructure/httpMediaInsightsGateway/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAER,oBAAoB,EAGvB,MAAM,yBAAyB,CAAC;AAEjC;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,EAAE,oBAkCtC,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { apiClient, toApiError } from '@orthacms/utils-admin';
|
|
2
|
+
/**
|
|
3
|
+
* HTTP implementation of {@link MediaInsightsGateway} over the shared
|
|
4
|
+
* `apiClient` — same-origin, cookie-authed, implicitly workspace-scoped via the
|
|
5
|
+
* ambient `X-Workspace-Id` header. Failures normalise to `ApiError`.
|
|
6
|
+
*/
|
|
7
|
+
export const httpMediaInsightsGateway = {
|
|
8
|
+
async storage() {
|
|
9
|
+
try {
|
|
10
|
+
const { data } = await apiClient.get('/insights/media/storage');
|
|
11
|
+
return data;
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
throw toApiError(error);
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
async uploads(days) {
|
|
18
|
+
try {
|
|
19
|
+
const { data } = await apiClient.get('/insights/media/uploads', { params: { days } });
|
|
20
|
+
return data;
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
throw toApiError(error);
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
async altCoverage() {
|
|
27
|
+
try {
|
|
28
|
+
const { data } = await apiClient.get('/insights/media/alt');
|
|
29
|
+
return data;
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
throw toApiError(error);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
};
|