Skip to content

Commit

Permalink
Merge pull request #2107 from umbraco/v14/fix/badge-thumbnails
Browse files Browse the repository at this point in the history
Bugfix: Resized media urls result in the request path being too long
  • Loading branch information
leekelleher authored Jul 10, 2024
2 parents ce85f3c + 5622376 commit 1bf0a73
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 21 deletions.
1 change: 1 addition & 0 deletions src/packages/media/imaging/constants.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export const UMB_IMAGING_REPOSITORY_ALIAS = 'Umb.Repository.Imaging';
export const UMB_IMAGING_STORE_ALIAS = 'Umb.Store.Imaging';
55 changes: 44 additions & 11 deletions src/packages/media/imaging/imaging.repository.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,76 @@
import type { UmbImagingModel } from './types.js';
import { UmbImagingServerDataSource } from './imaging.server.data.js';
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
import { UMB_IMAGING_STORE_CONTEXT } from './imaging.store.token.js';
import { ImageCropModeModel } from '@umbraco-cms/backoffice/external/backend-api';
import { UmbRepositoryBase } from '@umbraco-cms/backoffice/repository';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import type { UmbApi } from '@umbraco-cms/backoffice/extension-api';
import type { UmbMediaUrlModel } from '@umbraco-cms/backoffice/media';

export class UmbImagingRepository extends UmbControllerBase implements UmbApi {
export class UmbImagingRepository extends UmbRepositoryBase implements UmbApi {
#dataStore?: typeof UMB_IMAGING_STORE_CONTEXT.TYPE;
#itemSource: UmbImagingServerDataSource;

constructor(host: UmbControllerHost) {
super(host);
this.#itemSource = new UmbImagingServerDataSource(host);

this.consumeContext(UMB_IMAGING_STORE_CONTEXT, (instance) => {
this.#dataStore = instance;
});
}

/**
* Requests the items for the given uniques
* @param {Array<string>} uniques
* @return {*}
* @memberof UmbImagingRepository
*/
async requestResizedItems(uniques: Array<string>, imagingModel?: UmbImagingModel) {
async requestResizedItems(
uniques: Array<string>,
imagingModel?: UmbImagingModel,
): Promise<{ data: UmbMediaUrlModel[] }> {
if (!uniques.length) throw new Error('Uniques are missing');
if (!this.#dataStore) throw new Error('Data store is missing');

const urls = new Map<string, string>();

for (const unique of uniques) {
const existingCrop = this.#dataStore.getCrop(unique, imagingModel);
if (existingCrop !== undefined) {
urls.set(unique, existingCrop);
continue;
}

const { data: urlModels, error } = await this.#itemSource.getItems([unique], imagingModel);

if (error) {
console.error('[UmbImagingRepository] Error fetching items', error);
continue;
}

const url = urlModels?.[0].url;

this.#dataStore.addCrop(unique, url ?? '', imagingModel);

if (url) {
urls.set(unique, url);
}
}

const { data, error: _error } = await this.#itemSource.getItems(uniques, imagingModel);
const error: any = _error;
return { data, error };
return { data: Array.from(urls).map(([unique, url]) => ({ unique, url })) };
}

/**
* Requests the thumbnail URLs for the given uniques
* @param {Array<string>} uniques
* @param {number} height
* @param {number} width
* @returns {*}
* @param {ImageCropModeModel} mode - The crop mode
* @memberof UmbImagingRepository
*/
async requestThumbnailUrls(uniques: Array<string>, height: number, width: number) {
const imagingModel = { height: height, width: width, mode: ImageCropModeModel.MIN };
return await this.requestResizedItems(uniques, imagingModel);
async requestThumbnailUrls(uniques: Array<string>, height: number, width: number, mode = ImageCropModeModel.MIN) {
const imagingModel: UmbImagingModel = { height, width, mode };
return this.requestResizedItems(uniques, imagingModel);
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/packages/media/imaging/imaging.store.token.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import type { UmbImagingStore } from './imaging.store.js';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';

export const UMB_IMAGING_STORE_CONTEXT = new UmbContextToken<UmbImagingStore>('UmbImagingStore');
47 changes: 47 additions & 0 deletions src/packages/media/imaging/imaging.store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { UMB_IMAGING_STORE_CONTEXT } from './imaging.store.token.js';
import type { UmbImagingModel } from './types.js';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import type { UmbApi } from '@umbraco-cms/backoffice/extension-api';
import { UmbContextBase } from '@umbraco-cms/backoffice/class-api';

export class UmbImagingStore extends UmbContextBase<never> implements UmbApi {
#data;

constructor(host: UmbControllerHost) {
super(host, UMB_IMAGING_STORE_CONTEXT.toString());
this.#data = new Map<string, Map<string, string>>();
}

/**
* Gets the data from the store.
*/
getData(unique: string) {
return this.#data.get(unique);
}

/**
* Gets a specific crop if it exists.
*/
getCrop(unique: string, data?: UmbImagingModel) {
return this.#data.get(unique)?.get(this.#generateCropKey(data));
}

/**
* Adds a new crop to the store.
*/
addCrop(unique: string, urlInfo: string, data?: UmbImagingModel) {
if (!this.#data.has(unique)) {
this.#data.set(unique, new Map());
}
this.#data.get(unique)?.set(this.#generateCropKey(data), urlInfo);
}

/**
* Generates a unique key for the crop based on the width, height and mode.
*/
#generateCropKey(data?: UmbImagingModel) {
return data ? `${data.width}x${data.height};${data.mode}` : 'generic';
}
}

export default UmbImagingStore;
13 changes: 10 additions & 3 deletions src/packages/media/imaging/manifests.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { UMB_IMAGING_REPOSITORY_ALIAS } from './constants.js';
import type { ManifestRepository, ManifestTypes } from '@umbraco-cms/backoffice/extension-registry';
import { UMB_IMAGING_REPOSITORY_ALIAS, UMB_IMAGING_STORE_ALIAS } from './constants.js';
import type { ManifestRepository, ManifestStore, ManifestTypes } from '@umbraco-cms/backoffice/extension-registry';

const repository: ManifestRepository = {
type: 'repository',
Expand All @@ -8,4 +8,11 @@ const repository: ManifestRepository = {
api: () => import('./imaging.repository.js'),
};

export const manifests: Array<ManifestTypes> = [repository];
const store: ManifestStore = {
type: 'store',
alias: UMB_IMAGING_STORE_ALIAS,
name: 'Imaging Store',
api: () => import('./imaging.store.js'),
};

export const manifests: Array<ManifestTypes> = [repository, store];
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@ import { UmbImagingRepository } from '@umbraco-cms/backoffice/imaging';
import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
import { UmbDefaultCollectionContext } from '@umbraco-cms/backoffice/collection';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import { ImageCropModeModel } from '@umbraco-cms/backoffice/external/backend-api';

export class UmbMediaCollectionContext extends UmbDefaultCollectionContext<
UmbMediaCollectionItemModel,
UmbMediaCollectionFilterModel
> {
#imagingRepository: UmbImagingRepository;

#thumbnailItems = new UmbArrayState<UmbMediaCollectionItemModel>([], (x) => x);
#thumbnailItems = new UmbArrayState<UmbMediaCollectionItemModel>([], (x) => x.unique);
public readonly thumbnailItems = this.#thumbnailItems.asObservable();

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

const { data } = await this.#imagingRepository.requestResizedItems(
const { data } = await this.#imagingRepository.requestThumbnailUrls(
items.map((m) => m.unique),
{ height: 400, width: 400, mode: ImageCropModeModel.MIN },
400,
400,
);

this.#thumbnailItems.setValue(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import type { UmbMediaPickerFolderPathElement } from './components/media-picker-
import type { UmbMediaPickerModalData, UmbMediaPickerModalValue } from './media-picker-modal.token.js';
import { css, html, customElement, state, repeat, ifDefined, query } from '@umbraco-cms/backoffice/external/lit';
import { debounce } from '@umbraco-cms/backoffice/utils';
import { ImageCropModeModel } from '@umbraco-cms/backoffice/external/backend-api';
import { UmbImagingRepository } from '@umbraco-cms/backoffice/imaging';
import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal';
import { UMB_CONTENT_PROPERTY_CONTEXT } from '@umbraco-cms/backoffice/content';
Expand Down Expand Up @@ -95,9 +94,10 @@ export class UmbMediaPickerModalElement extends UmbModalBaseElement<
async #mapMediaUrls(items: Array<UmbMediaItemModel>): Promise<Array<UmbMediaCardItemModel>> {
if (!items.length) return [];

const { data } = await this.#imagingRepository.requestResizedItems(
const { data } = await this.#imagingRepository.requestThumbnailUrls(
items.map((item) => item.unique),
{ height: 400, width: 400, mode: ImageCropModeModel.MIN },
400,
400,
);

return items
Expand Down

0 comments on commit 1bf0a73

Please sign in to comment.