Skip to content

Commit 1bf0a73

Browse files
authored
Merge pull request #2107 from umbraco/v14/fix/badge-thumbnails
Bugfix: Resized media urls result in the request path being too long
2 parents ce85f3c + 5622376 commit 1bf0a73

File tree

7 files changed

+113
-21
lines changed

7 files changed

+113
-21
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export const UMB_IMAGING_REPOSITORY_ALIAS = 'Umb.Repository.Imaging';
2+
export const UMB_IMAGING_STORE_ALIAS = 'Umb.Store.Imaging';

src/packages/media/imaging/imaging.repository.ts

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,76 @@
11
import type { UmbImagingModel } from './types.js';
22
import { UmbImagingServerDataSource } from './imaging.server.data.js';
3-
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
3+
import { UMB_IMAGING_STORE_CONTEXT } from './imaging.store.token.js';
44
import { ImageCropModeModel } from '@umbraco-cms/backoffice/external/backend-api';
5+
import { UmbRepositoryBase } from '@umbraco-cms/backoffice/repository';
56
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
67
import type { UmbApi } from '@umbraco-cms/backoffice/extension-api';
8+
import type { UmbMediaUrlModel } from '@umbraco-cms/backoffice/media';
79

8-
export class UmbImagingRepository extends UmbControllerBase implements UmbApi {
10+
export class UmbImagingRepository extends UmbRepositoryBase implements UmbApi {
11+
#dataStore?: typeof UMB_IMAGING_STORE_CONTEXT.TYPE;
912
#itemSource: UmbImagingServerDataSource;
1013

1114
constructor(host: UmbControllerHost) {
1215
super(host);
1316
this.#itemSource = new UmbImagingServerDataSource(host);
17+
18+
this.consumeContext(UMB_IMAGING_STORE_CONTEXT, (instance) => {
19+
this.#dataStore = instance;
20+
});
1421
}
1522

1623
/**
1724
* Requests the items for the given uniques
1825
* @param {Array<string>} uniques
19-
* @return {*}
2026
* @memberof UmbImagingRepository
2127
*/
22-
async requestResizedItems(uniques: Array<string>, imagingModel?: UmbImagingModel) {
28+
async requestResizedItems(
29+
uniques: Array<string>,
30+
imagingModel?: UmbImagingModel,
31+
): Promise<{ data: UmbMediaUrlModel[] }> {
2332
if (!uniques.length) throw new Error('Uniques are missing');
33+
if (!this.#dataStore) throw new Error('Data store is missing');
34+
35+
const urls = new Map<string, string>();
36+
37+
for (const unique of uniques) {
38+
const existingCrop = this.#dataStore.getCrop(unique, imagingModel);
39+
if (existingCrop !== undefined) {
40+
urls.set(unique, existingCrop);
41+
continue;
42+
}
43+
44+
const { data: urlModels, error } = await this.#itemSource.getItems([unique], imagingModel);
45+
46+
if (error) {
47+
console.error('[UmbImagingRepository] Error fetching items', error);
48+
continue;
49+
}
50+
51+
const url = urlModels?.[0].url;
52+
53+
this.#dataStore.addCrop(unique, url ?? '', imagingModel);
54+
55+
if (url) {
56+
urls.set(unique, url);
57+
}
58+
}
2459

25-
const { data, error: _error } = await this.#itemSource.getItems(uniques, imagingModel);
26-
const error: any = _error;
27-
return { data, error };
60+
return { data: Array.from(urls).map(([unique, url]) => ({ unique, url })) };
2861
}
2962

3063
/**
3164
* Requests the thumbnail URLs for the given uniques
3265
* @param {Array<string>} uniques
3366
* @param {number} height
3467
* @param {number} width
35-
* @returns {*}
68+
* @param {ImageCropModeModel} mode - The crop mode
3669
* @memberof UmbImagingRepository
3770
*/
38-
async requestThumbnailUrls(uniques: Array<string>, height: number, width: number) {
39-
const imagingModel = { height: height, width: width, mode: ImageCropModeModel.MIN };
40-
return await this.requestResizedItems(uniques, imagingModel);
71+
async requestThumbnailUrls(uniques: Array<string>, height: number, width: number, mode = ImageCropModeModel.MIN) {
72+
const imagingModel: UmbImagingModel = { height, width, mode };
73+
return this.requestResizedItems(uniques, imagingModel);
4174
}
4275
}
4376

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import type { UmbImagingStore } from './imaging.store.js';
2+
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
3+
4+
export const UMB_IMAGING_STORE_CONTEXT = new UmbContextToken<UmbImagingStore>('UmbImagingStore');
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { UMB_IMAGING_STORE_CONTEXT } from './imaging.store.token.js';
2+
import type { UmbImagingModel } from './types.js';
3+
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
4+
import type { UmbApi } from '@umbraco-cms/backoffice/extension-api';
5+
import { UmbContextBase } from '@umbraco-cms/backoffice/class-api';
6+
7+
export class UmbImagingStore extends UmbContextBase<never> implements UmbApi {
8+
#data;
9+
10+
constructor(host: UmbControllerHost) {
11+
super(host, UMB_IMAGING_STORE_CONTEXT.toString());
12+
this.#data = new Map<string, Map<string, string>>();
13+
}
14+
15+
/**
16+
* Gets the data from the store.
17+
*/
18+
getData(unique: string) {
19+
return this.#data.get(unique);
20+
}
21+
22+
/**
23+
* Gets a specific crop if it exists.
24+
*/
25+
getCrop(unique: string, data?: UmbImagingModel) {
26+
return this.#data.get(unique)?.get(this.#generateCropKey(data));
27+
}
28+
29+
/**
30+
* Adds a new crop to the store.
31+
*/
32+
addCrop(unique: string, urlInfo: string, data?: UmbImagingModel) {
33+
if (!this.#data.has(unique)) {
34+
this.#data.set(unique, new Map());
35+
}
36+
this.#data.get(unique)?.set(this.#generateCropKey(data), urlInfo);
37+
}
38+
39+
/**
40+
* Generates a unique key for the crop based on the width, height and mode.
41+
*/
42+
#generateCropKey(data?: UmbImagingModel) {
43+
return data ? `${data.width}x${data.height};${data.mode}` : 'generic';
44+
}
45+
}
46+
47+
export default UmbImagingStore;
Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { UMB_IMAGING_REPOSITORY_ALIAS } from './constants.js';
2-
import type { ManifestRepository, ManifestTypes } from '@umbraco-cms/backoffice/extension-registry';
1+
import { UMB_IMAGING_REPOSITORY_ALIAS, UMB_IMAGING_STORE_ALIAS } from './constants.js';
2+
import type { ManifestRepository, ManifestStore, ManifestTypes } from '@umbraco-cms/backoffice/extension-registry';
33

44
const repository: ManifestRepository = {
55
type: 'repository',
@@ -8,4 +8,11 @@ const repository: ManifestRepository = {
88
api: () => import('./imaging.repository.js'),
99
};
1010

11-
export const manifests: Array<ManifestTypes> = [repository];
11+
const store: ManifestStore = {
12+
type: 'store',
13+
alias: UMB_IMAGING_STORE_ALIAS,
14+
name: 'Imaging Store',
15+
api: () => import('./imaging.store.js'),
16+
};
17+
18+
export const manifests: Array<ManifestTypes> = [repository, store];

src/packages/media/media/collection/media-collection.context.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,14 @@ import { UmbImagingRepository } from '@umbraco-cms/backoffice/imaging';
44
import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
55
import { UmbDefaultCollectionContext } from '@umbraco-cms/backoffice/collection';
66
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
7-
import { ImageCropModeModel } from '@umbraco-cms/backoffice/external/backend-api';
87

98
export class UmbMediaCollectionContext extends UmbDefaultCollectionContext<
109
UmbMediaCollectionItemModel,
1110
UmbMediaCollectionFilterModel
1211
> {
1312
#imagingRepository: UmbImagingRepository;
1413

15-
#thumbnailItems = new UmbArrayState<UmbMediaCollectionItemModel>([], (x) => x);
14+
#thumbnailItems = new UmbArrayState<UmbMediaCollectionItemModel>([], (x) => x.unique);
1615
public readonly thumbnailItems = this.#thumbnailItems.asObservable();
1716

1817
constructor(host: UmbControllerHost) {
@@ -22,9 +21,10 @@ export class UmbMediaCollectionContext extends UmbDefaultCollectionContext<
2221
this.observe(this.items, async (items) => {
2322
if (!items?.length) return;
2423

25-
const { data } = await this.#imagingRepository.requestResizedItems(
24+
const { data } = await this.#imagingRepository.requestThumbnailUrls(
2625
items.map((m) => m.unique),
27-
{ height: 400, width: 400, mode: ImageCropModeModel.MIN },
26+
400,
27+
400,
2828
);
2929

3030
this.#thumbnailItems.setValue(

src/packages/media/media/modals/media-picker/media-picker-modal.element.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import type { UmbMediaPickerFolderPathElement } from './components/media-picker-
88
import type { UmbMediaPickerModalData, UmbMediaPickerModalValue } from './media-picker-modal.token.js';
99
import { css, html, customElement, state, repeat, ifDefined, query } from '@umbraco-cms/backoffice/external/lit';
1010
import { debounce } from '@umbraco-cms/backoffice/utils';
11-
import { ImageCropModeModel } from '@umbraco-cms/backoffice/external/backend-api';
1211
import { UmbImagingRepository } from '@umbraco-cms/backoffice/imaging';
1312
import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal';
1413
import { UMB_CONTENT_PROPERTY_CONTEXT } from '@umbraco-cms/backoffice/content';
@@ -95,9 +94,10 @@ export class UmbMediaPickerModalElement extends UmbModalBaseElement<
9594
async #mapMediaUrls(items: Array<UmbMediaItemModel>): Promise<Array<UmbMediaCardItemModel>> {
9695
if (!items.length) return [];
9796

98-
const { data } = await this.#imagingRepository.requestResizedItems(
97+
const { data } = await this.#imagingRepository.requestThumbnailUrls(
9998
items.map((item) => item.unique),
100-
{ height: 400, width: 400, mode: ImageCropModeModel.MIN },
99+
400,
100+
400,
101101
);
102102

103103
return items

0 commit comments

Comments
 (0)