Skip to content

Commit 48c2a02

Browse files
committed
Refactor asset conversion and deduplication logic
1 parent 04b6310 commit 48c2a02

3 files changed

Lines changed: 538 additions & 43 deletions

File tree

src/lib/components/assets/browser/select-assets-dialog.svelte

Lines changed: 13 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,11 @@
2222
import InternalAssetsPanel from '$lib/components/assets/browser/internal-assets-panel.svelte';
2323
import ViewSwitcher from '$lib/components/common/page-toolbar/view-switcher.svelte';
2424
import { allAssets } from '$lib/services/assets';
25-
import { getAssetKind } from '$lib/services/assets/kinds';
2625
import { selectAssetsView, showContentOverlay } from '$lib/services/contents/editor';
26+
import {
27+
convertFileItemToAsset,
28+
getUnsavedAssets,
29+
} from '$lib/services/contents/widgets/file/process';
2730
import { allCloudStorageServices } from '$lib/services/integrations/media-libraries/cloud';
2831
import {
2932
allStockAssetProviders,
@@ -32,7 +35,6 @@
3235
import { normalize } from '$lib/services/search/util';
3336
import { isSmallScreen } from '$lib/services/user/env';
3437
import { prefs } from '$lib/services/user/prefs';
35-
import { getGitHash } from '$lib/services/utils/file';
3638
import { SUPPORTED_IMAGE_TYPES } from '$lib/services/utils/media/image';
3739
3840
/**
@@ -164,42 +166,6 @@
164166
);
165167
const Selector = $derived($isSmallScreen ? Select : Listbox);
166168
167-
/**
168-
* Convert unsaved files to the `Asset` format so these can be browsed just like other assets.
169-
* @param {object} args Arguments.
170-
* @param {File} args.file Raw file.
171-
* @param {string} [args.blobURL] Blob URL of the file.
172-
* @param {AssetFolderInfo | undefined} args.folder Asset folder.
173-
* @returns {Promise<Asset>} Asset.
174-
*/
175-
const convertFileItemToAsset = async ({ file, blobURL, folder }) => {
176-
const { name, size } = file;
177-
178-
return /** @type {Asset} */ ({
179-
unsaved: true,
180-
file,
181-
blobURL: blobURL ?? URL.createObjectURL(file),
182-
name,
183-
path: `${targetFolderPath}/${name}`,
184-
sha: await getGitHash(file),
185-
size,
186-
kind: getAssetKind(name),
187-
folder,
188-
});
189-
};
190-
191-
/**
192-
* Get all the unsaved assets, including already cached for the draft and dropped ones.
193-
* @returns {Promise<Asset[]>} Assets.
194-
*/
195-
const getUnsavedAssets = async () =>
196-
Promise.all([
197-
...Object.entries($entryDraft?.files ?? {}).map(async ([blobURL, { file, folder }]) =>
198-
convertFileItemToAsset({ file, blobURL, folder }),
199-
),
200-
...Object.values(droppedAssets),
201-
]);
202-
203169
/**
204170
* Check if an asset with the same hash and folder already exists in the unsaved assets.
205171
* @param {object} args Arguments.
@@ -232,7 +198,7 @@
232198
return undefined;
233199
}
234200
235-
const asset = await convertFileItemToAsset({ file, folder });
201+
const asset = await convertFileItemToAsset({ file, folder, targetFolderPath });
236202
237203
droppedAssets.push(asset);
238204
@@ -291,10 +257,16 @@
291257
292258
$effect(() => {
293259
void $entryDraft?.files;
294-
void droppedAssets;
260+
// Somehow we need to snapshot `droppedAssets` here to make Svelte aware of its changes
261+
void $state.snapshot(droppedAssets);
295262
296263
(async () => {
297-
unsavedAssets = await getUnsavedAssets();
264+
unsavedAssets = [
265+
...($entryDraft?.files
266+
? await getUnsavedAssets({ draft: $entryDraft, targetFolderPath })
267+
: []),
268+
...Object.values(droppedAssets),
269+
];
298270
})();
299271
});
300272

src/lib/services/contents/widgets/file/process.js

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ import { get } from 'svelte/store';
55

66
import { allAssets } from '$lib/services/assets';
77
import { getAssetPublicURL } from '$lib/services/assets/info';
8+
import { getAssetKind } from '$lib/services/assets/kinds';
89
import { transformFile } from '$lib/services/integrations/media-libraries/default';
910
import { getGitHash } from '$lib/services/utils/file';
1011

1112
/**
12-
* @import { EntryDraft, SelectedResource } from '$lib/types/private';
13+
* @import { Asset, AssetFolderInfo, EntryDraft, SelectedResource } from '$lib/types/private';
1314
* @import { DefaultMediaLibraryConfig } from '$lib/types/public';
1415
*/
1516

@@ -37,6 +38,45 @@ export const getExistingBlobURL = async ({ draft, file }) => {
3738
return foundURL;
3839
};
3940

41+
/**
42+
* Convert unsaved files to the `Asset` format so these can be browsed just like other assets.
43+
* @param {object} args Arguments.
44+
* @param {File} args.file Raw file.
45+
* @param {string} [args.blobURL] Blob URL of the file.
46+
* @param {AssetFolderInfo | undefined} args.folder Asset folder.
47+
* @param {string} [args.targetFolderPath] Target folder path.
48+
* @returns {Promise<Asset>} Asset.
49+
*/
50+
export const convertFileItemToAsset = async ({ file, blobURL, folder, targetFolderPath }) => {
51+
const { name, size } = file;
52+
53+
return /** @type {Asset} */ ({
54+
unsaved: true,
55+
file,
56+
blobURL: blobURL ?? URL.createObjectURL(file),
57+
name,
58+
path: targetFolderPath ? `${targetFolderPath}/${name}` : name,
59+
sha: await getGitHash(file),
60+
size,
61+
kind: getAssetKind(name),
62+
folder,
63+
});
64+
};
65+
66+
/**
67+
* Get all the unsaved assets, including already cached for the draft and dropped ones.
68+
* @param {object} args Arguments.
69+
* @param {EntryDraft} args.draft Entry draft containing the resource.
70+
* @param {string} [args.targetFolderPath] Target folder path.
71+
* @returns {Promise<Asset[]>} Assets.
72+
*/
73+
export const getUnsavedAssets = async ({ draft, targetFolderPath }) =>
74+
Promise.all(
75+
Object.entries(draft.files).map(async ([blobURL, { file, folder }]) =>
76+
convertFileItemToAsset({ file, blobURL, folder, targetFolderPath }),
77+
),
78+
);
79+
4080
/**
4181
* Process a selected resource.
4282
* @param {object} args Arguments.
@@ -67,7 +107,12 @@ export const processResource = async ({ draft, resource, libraryConfig }) => {
67107

68108
const sha = await getGitHash(file);
69109
const { folder } = resource;
70-
const existingAsset = get(allAssets).find((a) => a.sha === sha && equal(a.folder, folder));
110+
111+
// Check if the selected file has already been uploaded or is pending upload, otherwise
112+
// duplicate files lead to an `each_key_duplicate` error in Svelte
113+
const existingAsset = [...get(allAssets), ...(await getUnsavedAssets({ draft }))].find(
114+
(a) => a.sha === sha && equal(a.folder, folder),
115+
);
71116

72117
if (existingAsset) {
73118
// If the selected file has already been uploaded, use the existing asset instead of

0 commit comments

Comments
 (0)