Headless CMS and Content Managment Hub
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

259 lines
7.0 KiB

/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Injectable } from '@angular/core';
import { combineLatest, Observable } from 'rxjs';
import { distinctUntilChanged, map, tap } from 'rxjs/operators';
import {
DialogService,
ImmutableArray,
notify,
Pager,
State
} from '@app/framework';
import { AssetDto, AssetsService} from './../services/assets.service';
import { AppsState } from './apps.state';
interface Snapshot {
tags: { [name: string]: number };
tagsSelected: { [name: string]: boolean };
assets: ImmutableArray<AssetDto>;
assetsPager: Pager;
assetsQuery?: string;
isLoaded?: boolean;
}
@Injectable()
export class AssetsState extends State<Snapshot> {
public tags =
this.changes.pipe(map(x => x.tags),
distinctUntilChanged(), map(x => sort(x)));
public tagsNames =
this.tags.pipe(
distinctUntilChanged(), map(x => x.map(t => t.name)));
public selectedTagNames =
this.changes.pipe(
distinctUntilChanged(), map(x => Object.keys(x.tagsSelected)));
public assets =
this.changes.pipe(map(x => x.assets),
distinctUntilChanged());
public assetsQuery =
this.changes.pipe(map(x => x.assetsQuery),
distinctUntilChanged());
public assetsPager =
this.changes.pipe(map(x => x.assetsPager),
distinctUntilChanged());
public isLoaded =
this.changes.pipe(map(x => !!x.isLoaded),
distinctUntilChanged());
constructor(
private readonly appsState: AppsState,
private readonly assetsService: AssetsService,
private readonly dialogs: DialogService
) {
super({ assets: ImmutableArray.empty(), assetsPager: new Pager(0, 0, 30), tags: {}, tagsSelected: {} });
}
public load(isReload = false): Observable<any> {
if (!isReload) {
this.resetState();
}
return this.loadInternal(isReload);
}
private loadInternal(isReload = false): Observable<any> {
return combineLatest(
this.assetsService.getAssets(this.appName,
this.snapshot.assetsPager.pageSize,
this.snapshot.assetsPager.skip,
this.snapshot.assetsQuery,
Object.keys(this.snapshot.tagsSelected)),
this.assetsService.getTags(this.appName)
).pipe(
tap(dtos => {
if (isReload) {
this.dialogs.notifyInfo('Assets reloaded.');
}
this.next(s => {
const assets = ImmutableArray.of(dtos[0].items);
const assetsPager = s.assetsPager.setCount(dtos[0].total);
return { ...s, assets, assetsPager, isLoaded: true, tags: dtos[1] };
});
}),
notify(this.dialogs));
}
public add(asset: AssetDto) {
this.next(s => {
const assets = s.assets.pushFront(asset);
const assetsPager = s.assetsPager.incrementCount();
const tags = { ...s.tags };
addTags(asset, tags);
return { ...s, assets, assetsPager, tags };
});
}
public delete(asset: AssetDto): Observable<any> {
return this.assetsService.deleteAsset(this.appName, asset.id, asset.version).pipe(
tap(() => {
return this.next(s => {
const assets = s.assets.filter(x => x.id !== asset.id);
const assetsPager = s.assetsPager.decrementCount();
const tags = { ...s.tags };
const tagsSelected = { ...s.tagsSelected };
removeTags(asset, tags, tagsSelected);
return { ...s, assets, assetsPager, tags, tagsSelected };
});
}),
notify(this.dialogs));
}
public update(asset: AssetDto) {
this.next(s => {
const previous = s.assets.find(x => x.id === asset.id);
const tags = { ...s.tags };
const tagsSelected = { ...s.tagsSelected };
if (previous) {
removeTags(previous, tags, tagsSelected);
}
if (asset) {
addTags(asset, tags);
}
const assets = s.assets.replaceBy('id', asset);
return { ...s, assets, tags, tagsSelected };
});
}
public toggleTag(tag: string): Observable<any> {
this.next(s => {
const tagsSelected = { ...s.tagsSelected };
if (tagsSelected[tag]) {
delete tagsSelected[tag];
} else {
tagsSelected[tag] = true;
}
return { ...s, assetsPager: new Pager(0, 0, 30), tagsSelected };
});
return this.loadInternal();
}
public selectTags(tags: string[]): Observable<any> {
this.next(s => {
const tagsSelected = {};
for (let tag of tags) {
tagsSelected[tag] = true;
}
return { ...s, assetsPager: new Pager(0, 0, 30), tagsSelected };
});
return this.loadInternal();
}
public resetTags(): Observable<any> {
this.next(s => ({ ...s, assetsPager: new Pager(0, 0, 30), tagsSelected: {} }));
return this.loadInternal();
}
public search(query: string): Observable<any> {
this.next(s => ({ ...s, assetsPager: new Pager(0, 0, 30), assetsQuery: query }));
return this.loadInternal();
}
public goNext(): Observable<any> {
this.next(s => ({ ...s, assetsPager: s.assetsPager.goNext() }));
return this.loadInternal();
}
public goPrev(): Observable<any> {
this.next(s => ({ ...s, assetsPager: s.assetsPager.goPrev() }));
return this.loadInternal();
}
public isTagSelected(tag: string) {
return this.snapshot.tagsSelected[tag];
}
public isTagSelectionEmpty() {
return Object.keys(this.snapshot.tagsSelected).length === 0;
}
private get appName() {
return this.appsState.appName;
}
}
function addTags(asset: AssetDto, tags: { [x: string]: number; }) {
for (let tag of asset.tags) {
if (tags[tag]) {
tags[tag]++;
} else {
tags[tag] = 1;
}
}
}
function removeTags(previous: AssetDto, tags: { [x: string]: number; }, tagsSelected: { [x: string]: boolean; }) {
for (let tag of previous.tags) {
if (tags[tag] === 1) {
delete tags[tag];
delete tagsSelected[tag];
} else {
tags[tag]--;
}
}
}
function sort(tags: { [name: string]: number }) {
return Object.keys(tags).sort((a, b) => {
if (a < b) {
return -1;
}
if (a > b) {
return 1;
}
return 0;
}).map(key => {
return { name: key, count: tags[key] };
});
}
@Injectable()
export class AssetsDialogState extends AssetsState { }