Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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