mirror of https://github.com/Squidex/squidex.git
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.
271 lines
7.3 KiB
271 lines
7.3 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 { tap } from 'rxjs/operators';
|
|
|
|
import {
|
|
compareStrings,
|
|
DialogService,
|
|
Pager,
|
|
shareSubscribed,
|
|
State
|
|
} from '@app/framework';
|
|
|
|
import { AssetDto, AssetsService} from './../services/assets.service';
|
|
import { AppsState } from './apps.state';
|
|
import { SavedQuery } from './queries';
|
|
import { encodeQuery, Query } from './query';
|
|
|
|
interface Snapshot {
|
|
// All assets tags.
|
|
tags: { [name: string]: number };
|
|
|
|
// The selected asset tags.
|
|
tagsSelected: { [name: string]: boolean };
|
|
|
|
// The current assets.
|
|
assets: ReadonlyArray<AssetDto>;
|
|
|
|
// The pagination information.
|
|
assetsPager: Pager;
|
|
|
|
// The query to filter assets.
|
|
assetsQuery?: Query;
|
|
|
|
// The json of the assets query.
|
|
assetsQueryJson: string;
|
|
|
|
// Indicates if the assets are loaded.
|
|
isLoaded?: boolean;
|
|
|
|
// Indicates if the user can create assets.
|
|
canCreate?: boolean;
|
|
}
|
|
|
|
@Injectable()
|
|
export class AssetsState extends State<Snapshot> {
|
|
public tagsUnsorted =
|
|
this.project(x => x.tags);
|
|
|
|
public tagsSelected =
|
|
this.project(x => x.tagsSelected);
|
|
|
|
public tags =
|
|
this.projectFrom(this.tagsUnsorted, x => sort(x));
|
|
|
|
public tagsNames =
|
|
this.projectFrom(this.tagsUnsorted, x => Object.keys(x));
|
|
|
|
public selectedTagNames =
|
|
this.projectFrom(this.tagsSelected, x => Object.keys(x));
|
|
|
|
public assets =
|
|
this.project(x => x.assets);
|
|
|
|
public assetsQuery =
|
|
this.project(x => x.assetsQuery);
|
|
|
|
public assetsPager =
|
|
this.project(x => x.assetsPager);
|
|
|
|
public isLoaded =
|
|
this.project(x => x.isLoaded === true);
|
|
|
|
public canCreate =
|
|
this.project(x => x.canCreate === true);
|
|
|
|
constructor(
|
|
private readonly appsState: AppsState,
|
|
private readonly assetsService: AssetsService,
|
|
private readonly dialogs: DialogService
|
|
) {
|
|
super({ assets: [], assetsPager: new Pager(0, 0, 30), assetsQueryJson: '', 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(([ { items: assets, total, canCreate }, tags ]) => {
|
|
if (isReload) {
|
|
this.dialogs.notifyInfo('Assets reloaded.');
|
|
}
|
|
|
|
this.next(s => {
|
|
const assetsPager = s.assetsPager.setCount(total);
|
|
|
|
return { ...s, assets, assetsPager, isLoaded: true, tags, canCreate };
|
|
});
|
|
}),
|
|
shareSubscribed(this.dialogs));
|
|
}
|
|
|
|
public add(asset: AssetDto) {
|
|
this.next(s => {
|
|
const assets = [asset, ...s.assets];
|
|
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, 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 };
|
|
});
|
|
}),
|
|
shareSubscribed(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: ReadonlyArray<string>): Observable<any> {
|
|
this.next(s => {
|
|
const tagsSelected = {};
|
|
|
|
for (const 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?: Query): Observable<any> {
|
|
this.next(s => ({ ...s, assetsPager: new Pager(0, 0, 30), assetsQuery: query, assetsQueryJson: encodeQuery(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 isQueryUsed(saved: SavedQuery) {
|
|
return this.snapshot.assetsQueryJson === saved.queryJson;
|
|
}
|
|
|
|
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 (const 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 (const 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(compareStrings).map(name => ({ name, count: tags[name] }));
|
|
}
|
|
|
|
@Injectable()
|
|
export class AssetsDialogState extends AssetsState { }
|