Metadata Tags (#947)

* Implemented the ability to click a metadata tag (in series detail) and load a pre-filtered view. Apply still needs to be implemented (preset load is out of sync with external filter)

* Refactored people to properly use typeahead so duplicates don't happen and use an observable chain so we can update the screen correctly

* Many refactoring to ensure that the timings for filtering always works
This commit is contained in:
Joseph Milazzo 2022-01-16 13:17:29 -08:00 committed by GitHub
parent 06be7de6b2
commit 80e9738f67
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 374 additions and 293 deletions

View file

@ -61,10 +61,10 @@
<label for="libraries">Libraries</label>
<app-typeahead (selectedData)="updateLibraryFilters($event)" [settings]="librarySettings" [reset]="resetTypeaheads">
<ng-template #badgeItem let-item let-position="idx">
{{item.title}}
{{item.name}}
</ng-template>
<ng-template #optionItem let-item let-position="idx">
{{item.title}}
{{item.name}}
</ng-template>
</app-typeahead>
</div>
@ -117,13 +117,13 @@
<!-- The People row -->
<div class="col-md-2 mr-3" *ngIf="peopleSettings.hasOwnProperty(PersonRole.CoverArtist)">
<div class="form-group">
<label for="cover-artist">(Cover) Artists</label>
<label for="cover-artist">Cover Artists</label>
<app-typeahead (selectedData)="updatePersonFilters($event, PersonRole.CoverArtist)" [settings]="getPersonsSettings(PersonRole.CoverArtist)" [reset]="resetTypeaheads">
<ng-template #badgeItem let-item let-position="idx">
{{item.title}}
{{item.name}}
</ng-template>
<ng-template #optionItem let-item let-position="idx">
{{item.title}}
{{item.name}}
</ng-template>
</app-typeahead>
</div>
@ -134,10 +134,10 @@
<label for="writers">Writers</label>
<app-typeahead (selectedData)="updatePersonFilters($event, PersonRole.Writer)" [settings]="getPersonsSettings(PersonRole.Writer)" [reset]="resetTypeaheads">
<ng-template #badgeItem let-item let-position="idx">
{{item.title}}
{{item.name}}
</ng-template>
<ng-template #optionItem let-item let-position="idx">
{{item.title}}
{{item.name}}
</ng-template>
</app-typeahead>
</div>
@ -148,10 +148,10 @@
<label for="publisher">Publisher</label>
<app-typeahead (selectedData)="updatePersonFilters($event, PersonRole.Publisher)" [settings]="getPersonsSettings(PersonRole.Publisher)" [reset]="resetTypeaheads">
<ng-template #badgeItem let-item let-position="idx">
{{item.title}}
{{item.name}}
</ng-template>
<ng-template #optionItem let-item let-position="idx">
{{item.title}}
{{item.name}}
</ng-template>
</app-typeahead>
</div>
@ -162,10 +162,10 @@
<label for="penciller">Penciller</label>
<app-typeahead (selectedData)="updatePersonFilters($event, PersonRole.Penciller)" [settings]="getPersonsSettings(PersonRole.Penciller)" [reset]="resetTypeaheads">
<ng-template #badgeItem let-item let-position="idx">
{{item.title}}
{{item.name}}
</ng-template>
<ng-template #optionItem let-item let-position="idx">
{{item.title}}
{{item.name}}
</ng-template>
</app-typeahead>
</div>
@ -176,10 +176,10 @@
<label for="letterer">Letterer</label>
<app-typeahead (selectedData)="updatePersonFilters($event, PersonRole.Letterer)" [settings]="getPersonsSettings(PersonRole.Letterer)" [reset]="resetTypeaheads">
<ng-template #badgeItem let-item let-position="idx">
{{item.title}}
{{item.name}}
</ng-template>
<ng-template #optionItem let-item let-position="idx">
{{item.title}}
{{item.name}}
</ng-template>
</app-typeahead>
</div>
@ -190,10 +190,10 @@
<label for="inker">Inker</label>
<app-typeahead (selectedData)="updatePersonFilters($event, PersonRole.Inker)" [settings]="getPersonsSettings(PersonRole.Inker)" [reset]="resetTypeaheads">
<ng-template #badgeItem let-item let-position="idx">
{{item.title}}
{{item.name}}
</ng-template>
<ng-template #optionItem let-item let-position="idx">
{{item.title}}
{{item.name}}
</ng-template>
</app-typeahead>
</div>
@ -204,10 +204,10 @@
<label for="editor">Editor</label>
<app-typeahead (selectedData)="updatePersonFilters($event, PersonRole.Editor)" [settings]="getPersonsSettings(PersonRole.Editor)" [reset]="resetTypeaheads">
<ng-template #badgeItem let-item let-position="idx">
{{item.title}}
{{item.name}}
</ng-template>
<ng-template #optionItem let-item let-position="idx">
{{item.title}}
{{item.name}}
</ng-template>
</app-typeahead>
</div>
@ -218,10 +218,10 @@
<label for="colorist">Colorist</label>
<app-typeahead (selectedData)="updatePersonFilters($event, PersonRole.Colorist)" [settings]="getPersonsSettings(PersonRole.Colorist)" [reset]="resetTypeaheads">
<ng-template #badgeItem let-item let-position="idx">
{{item.title}}
{{item.name}}
</ng-template>
<ng-template #optionItem let-item let-position="idx">
{{item.title}}
{{item.name}}
</ng-template>
</app-typeahead>
</div>
@ -232,10 +232,10 @@
<label for="character">Character</label>
<app-typeahead (selectedData)="updatePersonFilters($event, PersonRole.Character)" [settings]="getPersonsSettings(PersonRole.Character)" [reset]="resetTypeaheads">
<ng-template #badgeItem let-item let-position="idx">
{{item.title}}
{{item.name}}
</ng-template>
<ng-template #optionItem let-item let-position="idx">
{{item.title}}
{{item.name}}
</ng-template>
</app-typeahead>
</div>
@ -246,10 +246,10 @@
<label for="translators">Translators</label>
<app-typeahead (selectedData)="updatePersonFilters($event, PersonRole.Translator)" [settings]="getPersonsSettings(PersonRole.Translator)" [reset]="resetTypeaheads">
<ng-template #badgeItem let-item let-position="idx">
{{item.title}}
{{item.name}}
</ng-template>
<ng-template #optionItem let-item let-position="idx">
{{item.title}}
{{item.name}}
</ng-template>
</app-typeahead>
</div>
@ -328,9 +328,9 @@
<div class="form-group">
<label for="sort-options">Sort By</label>
<button class="btn btn-sm btn-secondary-outline" (click)="updateSortOrder()" style="height: 25px; padding-bottom: 0px;">
<i class="fa fa-arrow-down" title="Ascending" *ngIf="isAscendingSort; else descSort"></i>
<i class="fa fa-arrow-up" title="Ascending" *ngIf="isAscendingSort; else descSort"></i>
<ng-template #descSort>
<i class="fa fa-arrow-up" title="Descending"></i>
<i class="fa fa-arrow-down" title="Descending"></i>
</ng-template>
</button>
<select id="sort-options" class="form-control" formControlName="sortField" style="height: 38px;">

View file

@ -1,6 +1,6 @@
import { Component, ContentChild, EventEmitter, Input, OnDestroy, OnInit, Output, TemplateRef } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import { Observable, of, ReplaySubject, Subject } from 'rxjs';
import { forkJoin, Observable, of, ReplaySubject, Subject } from 'rxjs';
import { map, takeUntil } from 'rxjs/operators';
import { UtilityService } from 'src/app/shared/_services/utility.service';
import { TypeaheadSettings } from 'src/app/typeahead/typeahead-settings';
@ -34,13 +34,16 @@ export class FilterSettings {
peopleDisabled = false;
readProgressDisabled = false;
ratingDisabled = false;
presetLibraryId = 0;
presetCollectionId = 0;
sortDisabled = false;
ageRatingDisabled = false;
tagsDisabled = false;
languageDisabled = false;
publicationStatusDisabled = false;
presets: SeriesFilter | undefined;
/**
* Should the filter section be open by default
*/
openByDefault = false;
}
@Component({
@ -65,17 +68,16 @@ export class CardDetailLayoutComponent implements OnInit, OnDestroy {
@Output() applyFilter: EventEmitter<SeriesFilter> = new EventEmitter();
@ContentChild('cardItem') itemTemplate!: TemplateRef<any>;
formatSettings: TypeaheadSettings<FilterItem<MangaFormat>> = new TypeaheadSettings();
librarySettings: TypeaheadSettings<FilterItem<Library>> = new TypeaheadSettings();
genreSettings: TypeaheadSettings<FilterItem<Genre>> = new TypeaheadSettings();
collectionSettings: TypeaheadSettings<FilterItem<CollectionTag>> = new TypeaheadSettings();
ageRatingSettings: TypeaheadSettings<FilterItem<AgeRatingDto>> = new TypeaheadSettings();
publicationStatusSettings: TypeaheadSettings<FilterItem<PublicationStatusDto>> = new TypeaheadSettings();
tagsSettings: TypeaheadSettings<FilterItem<Tag>> = new TypeaheadSettings();
languageSettings: TypeaheadSettings<FilterItem<Language>> = new TypeaheadSettings();
peopleSettings: {[PersonRole: string]: TypeaheadSettings<FilterItem<Person>>} = {};
librarySettings: TypeaheadSettings<Library> = new TypeaheadSettings();
genreSettings: TypeaheadSettings<Genre> = new TypeaheadSettings();
collectionSettings: TypeaheadSettings<CollectionTag> = new TypeaheadSettings();
ageRatingSettings: TypeaheadSettings<AgeRatingDto> = new TypeaheadSettings();
publicationStatusSettings: TypeaheadSettings<PublicationStatusDto> = new TypeaheadSettings();
tagsSettings: TypeaheadSettings<Tag> = new TypeaheadSettings();
languageSettings: TypeaheadSettings<Language> = new TypeaheadSettings();
peopleSettings: {[PersonRole: string]: TypeaheadSettings<Person>} = {};
resetTypeaheads: Subject<boolean> = new ReplaySubject(1);
/**
@ -85,8 +87,6 @@ export class CardDetailLayoutComponent implements OnInit, OnDestroy {
filter!: SeriesFilter;
libraries: Array<FilterItem<Library>> = [];
genres: Array<FilterItem<Genre>> = [];
persons: Array<FilterItem<Person>> = [];
readProgressGroup!: FormGroup;
@ -152,24 +152,12 @@ export class CardDetailLayoutComponent implements OnInit, OnDestroy {
ngOnInit(): void {
this.trackByIdentity = (index: number, item: any) => `${this.header}_${this.pagination?.currentPage}_${this.updateApplied}`;
this.setupFormatTypeahead();
if (this.filterSettings === undefined) {
this.filterSettings = new FilterSettings();
}
this.libraryService.getLibrariesForMember().subscribe(libs => {
this.libraries = libs.map(lib => {
return {
title: lib.name,
value: lib,
selected: true,
}
});
this.setupTypeaheads();
});
this.setupTypeaheads();
}
ngOnDestroy() {
@ -178,14 +166,25 @@ export class CardDetailLayoutComponent implements OnInit, OnDestroy {
}
setupTypeaheads() {
this.setupLibraryTypeahead();
this.setupCollectionTagTypeahead();
this.setupPersonTypeahead();
this.setupAgeRatingSettings();
this.setupPublicationStatusSettings();
this.setupTagSettings();
this.setupLanguageSettings();
this.setupGenreTypeahead();
this.setupFormatTypeahead();
forkJoin([
this.setupLibraryTypeahead(),
this.setupCollectionTagTypeahead(),
this.setupAgeRatingSettings(),
this.setupPublicationStatusSettings(),
this.setupTagSettings(),
this.setupLanguageSettings(),
this.setupGenreTypeahead(),
this.setupPersonTypeahead(),
]).subscribe(results => {
this.resetTypeaheads.next(true);
if (this.filterSettings.openByDefault) {
this.filteringCollapsed = false;
}
this.apply();
});
}
@ -200,6 +199,12 @@ export class CardDetailLayoutComponent implements OnInit, OnDestroy {
const f = filter.toLowerCase();
return options.filter(m => m.title.toLowerCase() === f);
}
if (this.filterSettings.presets?.formats && this.filterSettings.presets?.formats.length > 0) {
this.formatSettings.savedData = mangaFormatFilters.filter(item => this.filterSettings.presets?.formats.includes(item.value));
this.filter.formats = this.formatSettings.savedData.map(item => item.value);
this.resetTypeaheads.next(true);
}
}
setupLibraryTypeahead() {
@ -209,18 +214,21 @@ export class CardDetailLayoutComponent implements OnInit, OnDestroy {
this.librarySettings.unique = true;
this.librarySettings.addIfNonExisting = false;
this.librarySettings.fetchFn = (filter: string) => {
return of (this.libraries)
return this.libraryService.getLibrariesForMember();
};
this.librarySettings.compareFn = (options: FilterItem<Library>[], filter: string) => {
this.librarySettings.compareFn = (options: Library[], filter: string) => {
const f = filter.toLowerCase();
return options.filter(m => m.title.toLowerCase() === f);
return options.filter(m => m.name.toLowerCase() === f);
}
if (this.filterSettings.presetLibraryId > 0) {
this.librarySettings.savedData = this.libraries.filter(item => item.value.id === this.filterSettings.presetLibraryId);
this.filter.libraries = this.librarySettings.savedData.map(item => item.value.id);
this.resetTypeaheads.next(true); // For some reason library just doesn't update properly with savedData
if (this.filterSettings.presets?.libraries && this.filterSettings.presets?.libraries.length > 0) {
return this.librarySettings.fetchFn('').pipe(map(libraries => {
this.librarySettings.savedData = libraries.filter(item => this.filterSettings.presets?.libraries.includes(item.id));
this.filter.libraries = this.librarySettings.savedData.map(item => item.id);
return of(true);
}));
}
return of(true);
}
setupGenreTypeahead() {
@ -230,20 +238,21 @@ export class CardDetailLayoutComponent implements OnInit, OnDestroy {
this.genreSettings.unique = true;
this.genreSettings.addIfNonExisting = false;
this.genreSettings.fetchFn = (filter: string) => {
return this.metadataService.getAllGenres(this.filter.libraries).pipe(map(genres => {
return genres.map(genre => {
return {
title: genre.title,
value: genre,
selected: false,
}
})
}));
return this.metadataService.getAllGenres(this.filter.libraries);
};
this.genreSettings.compareFn = (options: FilterItem<Genre>[], filter: string) => {
this.genreSettings.compareFn = (options: Genre[], filter: string) => {
const f = filter.toLowerCase();
return options.filter(m => m.title.toLowerCase() === f);
}
if (this.filterSettings.presets?.genres && this.filterSettings.presets?.genres.length > 0) {
return this.genreSettings.fetchFn('').pipe(map(genres => {
this.genreSettings.savedData = genres.filter(item => this.filterSettings.presets?.genres.includes(item.id));
this.filter.genres = this.genreSettings.savedData.map(item => item.id);
return of(true);
}));
}
return of(true);
}
setupAgeRatingSettings() {
@ -253,20 +262,21 @@ export class CardDetailLayoutComponent implements OnInit, OnDestroy {
this.ageRatingSettings.unique = true;
this.ageRatingSettings.addIfNonExisting = false;
this.ageRatingSettings.fetchFn = (filter: string) => {
return this.metadataService.getAllAgeRatings(this.filter.libraries).pipe(map(ratings => {
return ratings.map(rating => {
return {
title: rating.title,
value: rating,
selected: false,
}
})
}));
return this.metadataService.getAllAgeRatings(this.filter.libraries);
};
this.ageRatingSettings.compareFn = (options: FilterItem<AgeRatingDto>[], filter: string) => {
this.ageRatingSettings.compareFn = (options: AgeRatingDto[], filter: string) => {
const f = filter.toLowerCase();
return options.filter(m => m.title.toLowerCase() === f && this.utilityService.filter(m.title, filter));
}
if (this.filterSettings.presets?.ageRating && this.filterSettings.presets?.ageRating.length > 0) {
return this.ageRatingSettings.fetchFn('').pipe(map(rating => {
this.ageRatingSettings.savedData = rating.filter(item => this.filterSettings.presets?.ageRating.includes(item.value));
this.filter.ageRating = this.ageRatingSettings.savedData.map(item => item.value);
return of(true);
}));
}
return of(true);
}
setupPublicationStatusSettings() {
@ -276,20 +286,21 @@ export class CardDetailLayoutComponent implements OnInit, OnDestroy {
this.publicationStatusSettings.unique = true;
this.publicationStatusSettings.addIfNonExisting = false;
this.publicationStatusSettings.fetchFn = (filter: string) => {
return this.metadataService.getAllPublicationStatus(this.filter.libraries).pipe(map(statuses => {
return statuses.map(status => {
return {
title: status.title,
value: status,
selected: false,
}
})
}));
return this.metadataService.getAllPublicationStatus(this.filter.libraries);
};
this.publicationStatusSettings.compareFn = (options: FilterItem<PublicationStatusDto>[], filter: string) => {
this.publicationStatusSettings.compareFn = (options: PublicationStatusDto[], filter: string) => {
const f = filter.toLowerCase();
return options.filter(m => m.title.toLowerCase() === f && this.utilityService.filter(m.title, filter));
}
if (this.filterSettings.presets?.publicationStatus && this.filterSettings.presets?.publicationStatus.length > 0) {
return this.publicationStatusSettings.fetchFn('').pipe(map(statuses => {
this.publicationStatusSettings.savedData = statuses.filter(item => this.filterSettings.presets?.publicationStatus.includes(item.value));
this.filter.publicationStatus = this.publicationStatusSettings.savedData.map(item => item.value);
return of(true);
}));
}
return of(true);
}
setupTagSettings() {
@ -299,20 +310,21 @@ export class CardDetailLayoutComponent implements OnInit, OnDestroy {
this.tagsSettings.unique = true;
this.tagsSettings.addIfNonExisting = false;
this.tagsSettings.fetchFn = (filter: string) => {
return this.metadataService.getAllTags(this.filter.libraries).pipe(map(tags => {
return tags.map(tag => {
return {
title: tag.title,
value: tag,
selected: false,
}
})
}));
return this.metadataService.getAllTags(this.filter.libraries);
};
this.tagsSettings.compareFn = (options: FilterItem<Tag>[], filter: string) => {
this.tagsSettings.compareFn = (options: Tag[], filter: string) => {
const f = filter.toLowerCase();
return options.filter(m => m.title.toLowerCase() === f && this.utilityService.filter(m.title, filter));
}
if (this.filterSettings.presets?.tags && this.filterSettings.presets?.tags.length > 0) {
return this.tagsSettings.fetchFn('').pipe(map(tags => {
this.tagsSettings.savedData = tags.filter(item => this.filterSettings.presets?.tags.includes(item.id));
this.filter.tags = this.tagsSettings.savedData.map(item => item.id);
return of(true);
}));
}
return of(true);
}
setupLanguageSettings() {
@ -322,20 +334,21 @@ export class CardDetailLayoutComponent implements OnInit, OnDestroy {
this.languageSettings.unique = true;
this.languageSettings.addIfNonExisting = false;
this.languageSettings.fetchFn = (filter: string) => {
return this.metadataService.getAllLanguages(this.filter.libraries).pipe(map(tags => {
return tags.map(tag => {
return {
title: tag.title,
value: tag,
selected: false,
}
})
}));
return this.metadataService.getAllLanguages(this.filter.libraries);
};
this.languageSettings.compareFn = (options: FilterItem<Language>[], filter: string) => {
this.languageSettings.compareFn = (options: Language[], filter: string) => {
const f = filter.toLowerCase();
return options.filter(m => m.title.toLowerCase() === f && this.utilityService.filter(m.title, filter));
}
if (this.filterSettings.presets?.languages && this.filterSettings.presets?.languages.length > 0) {
return this.languageSettings.fetchFn('').pipe(map(languages => {
this.languageSettings.savedData = languages.filter(item => this.filterSettings.presets?.languages.includes(item.isoCode));
this.filter.languages = this.languageSettings.savedData.map(item => item.isoCode);
return of(true);
}));
}
return of(true);
}
setupCollectionTagTypeahead() {
@ -345,128 +358,81 @@ export class CardDetailLayoutComponent implements OnInit, OnDestroy {
this.collectionSettings.unique = true;
this.collectionSettings.addIfNonExisting = false;
this.collectionSettings.fetchFn = (filter: string) => {
return this.collectionTagService.allTags().pipe(map(tags => {
return tags.map(lib => {
return {
title: lib.title,
value: lib,
selected: false,
}
});
}));
return this.collectionTagService.allTags();
};
this.collectionSettings.compareFn = (options: FilterItem<CollectionTag>[], filter: string) => {
this.collectionSettings.compareFn = (options: CollectionTag[], filter: string) => {
const f = filter.toLowerCase();
return options.filter(m => m.title.toLowerCase() === f);
}
if (this.filterSettings.presetCollectionId > 0) {
this.collectionSettings.fetchFn('').subscribe(tags => {
this.collectionSettings.savedData = tags.filter(item => item.value.id === this.filterSettings.presetCollectionId);
this.filter.collectionTags = this.collectionSettings.savedData.map(item => item.value.id);
this.resetTypeaheads.next(true);
});
if (this.filterSettings.presets?.collectionTags && this.filterSettings.presets?.collectionTags.length > 0) {
return this.collectionSettings.fetchFn('').pipe(map(tags => {
this.collectionSettings.savedData = tags.filter(item => this.filterSettings.presets?.collectionTags.includes(item.id));
this.filter.collectionTags = this.collectionSettings.savedData.map(item => item.id);
return of(true);
}));
}
return of(true);
}
applyPresets() {
// if (this.filterSettings.presetCollectionId > 0) {
// this.collectionSettings.fetchFn('').subscribe(tags => {
// this.collectionSettings.savedData = tags.filter(item => item.value.id === this.filterSettings.presetCollectionId);
// this.filter.collectionTags = this.collectionSettings.savedData.map(item => item.value.id);
// this.resetTypeaheads.next(true);
// });
// }
updateFromPreset(id: string, peopleFilterField: Array<any>, presetField: Array<any> | undefined, role: PersonRole) {
const personSettings = this.createBlankPersonSettings(id, role)
if (presetField && presetField.length > 0) {
const fetch = personSettings.fetchFn as ((filter: string) => Observable<Person[]>);
return fetch('').pipe(map(people => {
personSettings.savedData = people.filter(item => presetField.includes(item.id));
peopleFilterField = personSettings.savedData.map(item => item.id);
this.resetTypeaheads.next(true);
this.peopleSettings[role] = personSettings;
this.updatePersonFilters(personSettings.savedData as Person[], role);
return true;
}));
} else {
this.peopleSettings[role] = personSettings;
return of(true);
}
}
setupPersonTypeahead() {
this.peopleSettings = {};
var personSettings = this.createBlankPersonSettings('writers');
personSettings.fetchFn = (filter: string) => {
return this.fetchPeople(PersonRole.Writer, filter);
};
this.peopleSettings[PersonRole.Writer] = personSettings;
personSettings = this.createBlankPersonSettings('character');
personSettings.fetchFn = (filter: string) => {
return this.fetchPeople(PersonRole.Character, filter);
};
this.peopleSettings[PersonRole.Character] = personSettings;
personSettings = this.createBlankPersonSettings('colorist');
personSettings.fetchFn = (filter: string) => {
return this.fetchPeople(PersonRole.Colorist, filter);
};
this.peopleSettings[PersonRole.Colorist] = personSettings;
personSettings = this.createBlankPersonSettings('cover-artist');
personSettings.fetchFn = (filter: string) => {
return this.fetchPeople(PersonRole.CoverArtist, filter);
};
this.peopleSettings[PersonRole.CoverArtist] = personSettings;
personSettings = this.createBlankPersonSettings('editor');
personSettings.fetchFn = (filter: string) => {
return this.fetchPeople(PersonRole.Editor, filter);
};
this.peopleSettings[PersonRole.Editor] = personSettings;
personSettings = this.createBlankPersonSettings('inker');
personSettings.fetchFn = (filter: string) => {
return this.fetchPeople(PersonRole.Inker, filter);
};
this.peopleSettings[PersonRole.Inker] = personSettings;
personSettings = this.createBlankPersonSettings('letterer');
personSettings.fetchFn = (filter: string) => {
return this.fetchPeople(PersonRole.Letterer, filter);
};
this.peopleSettings[PersonRole.Letterer] = personSettings;
personSettings = this.createBlankPersonSettings('penciller');
personSettings.fetchFn = (filter: string) => {
return this.fetchPeople(PersonRole.Penciller, filter);
};
this.peopleSettings[PersonRole.Penciller] = personSettings;
personSettings = this.createBlankPersonSettings('publisher');
personSettings.fetchFn = (filter: string) => {
return this.fetchPeople(PersonRole.Publisher, filter);
};
this.peopleSettings[PersonRole.Publisher] = personSettings;
personSettings = this.createBlankPersonSettings('translators');
personSettings.fetchFn = (filter: string) => {
return this.fetchPeople(PersonRole.Translator, filter);
};
this.peopleSettings[PersonRole.Translator] = personSettings;
}
fetchPeople(role: PersonRole, filter: string): Observable<FilterItem<Person>[]> {
return this.metadataService.getAllPeople(this.filter.libraries).pipe(map(people => {
return people.filter(p => p.role == role && this.utilityService.filter(p.name, filter)).map((p: Person) => {
return {
title: p.name,
value: p,
selected: false,
}
});
return forkJoin([
this.updateFromPreset('writers', this.filter.writers, this.filterSettings.presets?.writers, PersonRole.Writer),
this.updateFromPreset('character', this.filter.character, this.filterSettings.presets?.character, PersonRole.Character),
this.updateFromPreset('colorist', this.filter.colorist, this.filterSettings.presets?.colorist, PersonRole.Colorist),
this.updateFromPreset('cover-artist', this.filter.coverArtist, this.filterSettings.presets?.coverArtist, PersonRole.CoverArtist),
this.updateFromPreset('editor', this.filter.editor, this.filterSettings.presets?.editor, PersonRole.Editor),
this.updateFromPreset('inker', this.filter.inker, this.filterSettings.presets?.inker, PersonRole.Inker),
this.updateFromPreset('letterer', this.filter.letterer, this.filterSettings.presets?.letterer, PersonRole.Letterer),
this.updateFromPreset('penciller', this.filter.penciller, this.filterSettings.presets?.penciller, PersonRole.Penciller),
this.updateFromPreset('publisher', this.filter.publisher, this.filterSettings.presets?.publisher, PersonRole.Publisher),
this.updateFromPreset('translators', this.filter.translators, this.filterSettings.presets?.translators, PersonRole.Translator)
]).pipe(map(results => {
this.resetTypeaheads.next(true);
return of(true);
}));
}
createBlankPersonSettings(id: string) {
var personSettings = new TypeaheadSettings<FilterItem<Person>>();
fetchPeople(role: PersonRole, filter: string) {
return this.metadataService.getAllPeople(this.filter.libraries).pipe(map(people => {
return people.filter(p => p.role == role && this.utilityService.filter(p.name, filter));
}));
}
createBlankPersonSettings(id: string, role: PersonRole) {
var personSettings = new TypeaheadSettings<Person>();
personSettings.minCharacters = 0;
personSettings.multiple = true;
personSettings.unique = true;
personSettings.addIfNonExisting = false;
personSettings.id = id;
personSettings.compareFn = (options: FilterItem<Person>[], filter: string) => {
personSettings.compareFn = (options: Person[], filter: string) => {
const f = filter.toLowerCase();
return options.filter(m => m.title.toLowerCase() === f);
return options.filter(m => m.name.toLowerCase() === f);
}
personSettings.fetchFn = (filter: string) => {
return this.fetchPeople(role, filter);
};
return personSettings;
}
@ -491,75 +457,75 @@ export class CardDetailLayoutComponent implements OnInit, OnDestroy {
}
updateFormatFilters(formats: FilterItem<MangaFormat>[]) {
this.filter.formats = formats.map(item => item.value) || [];
updateFormatFilters(formats: MangaFormat[]) {
this.filter.formats = formats.map(item => item) || [];
}
updateLibraryFilters(libraries: FilterItem<Library>[]) {
this.filter.libraries = libraries.map(item => item.value.id) || [];
updateLibraryFilters(libraries: Library[]) {
this.filter.libraries = libraries.map(item => item.id) || [];
}
updateGenreFilters(genres: FilterItem<Genre>[]) {
this.filter.genres = genres.map(item => item.value.id) || [];
updateGenreFilters(genres: Genre[]) {
this.filter.genres = genres.map(item => item.id) || [];
}
updateTagFilters(tags: FilterItem<Tag>[]) {
this.filter.tags = tags.map(item => item.value.id) || [];
updateTagFilters(tags: Tag[]) {
this.filter.tags = tags.map(item => item.id) || [];
}
updatePersonFilters(persons: FilterItem<Person>[], role: PersonRole) {
updatePersonFilters(persons: Person[], role: PersonRole) {
switch (role) {
case PersonRole.CoverArtist:
this.filter.coverArtist = persons.map(p => p.value.id);
this.filter.coverArtist = persons.map(p => p.id);
break;
case PersonRole.Character:
this.filter.character = persons.map(p => p.value.id);
this.filter.character = persons.map(p => p.id);
break;
case PersonRole.Colorist:
this.filter.colorist = persons.map(p => p.value.id);
this.filter.colorist = persons.map(p => p.id);
break;
case PersonRole.Editor:
this.filter.editor = persons.map(p => p.value.id);
this.filter.editor = persons.map(p => p.id);
break;
case PersonRole.Inker:
this.filter.inker = persons.map(p => p.value.id);
this.filter.inker = persons.map(p => p.id);
break;
case PersonRole.Letterer:
this.filter.letterer = persons.map(p => p.value.id);
this.filter.letterer = persons.map(p => p.id);
break;
case PersonRole.Penciller:
this.filter.penciller = persons.map(p => p.value.id);
this.filter.penciller = persons.map(p => p.id);
break;
case PersonRole.Publisher:
this.filter.publisher = persons.map(p => p.value.id);
this.filter.publisher = persons.map(p => p.id);
break;
case PersonRole.Writer:
this.filter.writers = persons.map(p => p.value.id);
this.filter.writers = persons.map(p => p.id);
break;
case PersonRole.Translator:
this.filter.translators = persons.map(p => p.value.id);
this.filter.translators = persons.map(p => p.id);
}
}
updateCollectionFilters(tags: FilterItem<CollectionTag>[]) {
this.filter.collectionTags = tags.map(item => item.value.id) || [];
updateCollectionFilters(tags: CollectionTag[]) {
this.filter.collectionTags = tags.map(item => item.id) || [];
}
updateRating(rating: any) {
this.filter.rating = rating;
}
updateAgeRating(ratingDtos: FilterItem<AgeRatingDto>[]) {
this.filter.ageRating = ratingDtos.map(item => item.value.value) || [];
updateAgeRating(ratingDtos: AgeRatingDto[]) {
this.filter.ageRating = ratingDtos.map(item => item.value) || [];
}
updatePublicationStatus(dtos: FilterItem<PublicationStatusDto>[]) {
this.filter.publicationStatus = dtos.map(item => item.value.value) || [];
updatePublicationStatus(dtos: PublicationStatusDto[]) {
this.filter.publicationStatus = dtos.map(item => item.value) || [];
}
updateLanguageRating(languages: FilterItem<Language>[]) {
this.filter.languages = languages.map(item => item.value.isoCode) || [];
updateLanguageRating(languages: Language[]) {
this.filter.languages = languages.map(item => item.isoCode) || [];
}
updateReadStatus(status: string) {
@ -595,12 +561,8 @@ export class CardDetailLayoutComponent implements OnInit, OnDestroy {
this.readProgressGroup.get('inProgress')?.setValue(true);
this.sortGroup.get('sortField')?.setValue(SortField.SortName);
this.isAscendingSort = true;
// Apply any presets
// Apply any presets which will trigger the apply
this.setupTypeaheads();
this.resetTypeaheads.next(true);
this.applyFilter.emit(this.filter);
this.updateApplied++;
}
apply() {