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

@ -22,7 +22,7 @@ namespace API.DTOs
/// </summary>
public ICollection<TagDto> Tags { get; set; }
public ICollection<PersonDto> Writers { get; set; } = new List<PersonDto>();
public ICollection<PersonDto> Artists { get; set; } = new List<PersonDto>();
public ICollection<PersonDto> CoverArtists { get; set; } = new List<PersonDto>();
public ICollection<PersonDto> Publishers { get; set; } = new List<PersonDto>();
public ICollection<PersonDto> Characters { get; set; } = new List<PersonDto>();
public ICollection<PersonDto> Pencillers { get; set; } = new List<PersonDto>();

View File

@ -452,6 +452,7 @@ public class SeriesRepository : ISeriesRepository
allPeopleIds.AddRange(filter.Publisher);
allPeopleIds.AddRange(filter.CoverArtist);
allPeopleIds.AddRange(filter.Translators);
//allPeopleIds.AddRange(filter.Artist);
hasPeopleFilter = allPeopleIds.Count > 0;
hasGenresFilter = filter.Genres.Count > 0;

View File

@ -63,7 +63,7 @@ namespace API.Helpers
.ForMember(dest => dest.Writers,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.Writer)))
.ForMember(dest => dest.Artists,
.ForMember(dest => dest.CoverArtists,
opt =>
opt.MapFrom(src => src.People.Where(p => p.Role == PersonRole.CoverArtist)))
.ForMember(dest => dest.Characters,

View File

@ -12,7 +12,7 @@ export interface SeriesMetadata {
tags: Array<Tag>;
collectionTags: Array<CollectionTag>;
writers: Array<Person>;
artists: Array<Person>;
coverArtists: Array<Person>;
publishers: Array<Person>;
characters: Array<Person>;
pencillers: Array<Person>;

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() {

View File

@ -8,7 +8,7 @@ import { debounceTime, take, takeUntil, takeWhile } from 'rxjs/operators';
import { BulkSelectionService } from 'src/app/cards/bulk-selection.service';
import { FilterSettings } from 'src/app/cards/card-detail-layout/card-detail-layout.component';
import { EditCollectionTagsComponent } from 'src/app/cards/_modals/edit-collection-tags/edit-collection-tags.component';
import { KEY_CODES } from 'src/app/shared/_services/utility.service';
import { KEY_CODES, UtilityService } from 'src/app/shared/_services/utility.service';
import { CollectionTag } from 'src/app/_models/collection-tag';
import { SeriesAddedToCollectionEvent } from 'src/app/_models/events/series-added-to-collection-event';
import { Pagination } from 'src/app/_models/pagination';
@ -82,7 +82,8 @@ export class CollectionDetailComponent implements OnInit, OnDestroy {
constructor(public imageService: ImageService, private collectionService: CollectionTagService, private router: Router, private route: ActivatedRoute,
private seriesService: SeriesService, private toastr: ToastrService, private actionFactoryService: ActionFactoryService,
private modalService: NgbModal, private titleService: Title, private accountService: AccountService,
public bulkSelectionService: BulkSelectionService, private actionService: ActionService, private messageHub: MessageHubService) {
public bulkSelectionService: BulkSelectionService, private actionService: ActionService, private messageHub: MessageHubService,
private utilityService: UtilityService) {
this.router.routeReuseStrategy.shouldReuseRoute = () => false;
this.accountService.currentUser$.pipe(take(1)).subscribe(user => {
@ -98,7 +99,8 @@ export class CollectionDetailComponent implements OnInit, OnDestroy {
}
const tagId = parseInt(routeId, 10);
this.filterSettings.presetCollectionId = tagId;
[this.filterSettings.presets, this.filterSettings.openByDefault] = this.utilityService.filterPresetsFromUrl(this.route.snapshot, this.seriesService.createSeriesFilter());
this.filterSettings.presets.collectionTags = [tagId];
this.updateTag(tagId);
}

View File

@ -5,7 +5,7 @@ import { Subject } from 'rxjs';
import { debounceTime, take, takeUntil, takeWhile } from 'rxjs/operators';
import { BulkSelectionService } from '../cards/bulk-selection.service';
import { FilterSettings } from '../cards/card-detail-layout/card-detail-layout.component';
import { KEY_CODES } from '../shared/_services/utility.service';
import { KEY_CODES, UtilityService } from '../shared/_services/utility.service';
import { SeriesAddedEvent } from '../_models/events/series-added-event';
import { Library } from '../_models/library';
import { Pagination } from '../_models/pagination';
@ -73,12 +73,14 @@ export class LibraryDetailComponent implements OnInit, OnDestroy {
constructor(private route: ActivatedRoute, private router: Router, private seriesService: SeriesService,
private libraryService: LibraryService, private titleService: Title, private actionFactoryService: ActionFactoryService,
private actionService: ActionService, public bulkSelectionService: BulkSelectionService, private hubService: MessageHubService) {
private actionService: ActionService, public bulkSelectionService: BulkSelectionService, private hubService: MessageHubService,
private utilityService: UtilityService) {
const routeId = this.route.snapshot.paramMap.get('id');
if (routeId === null) {
this.router.navigateByUrl('/libraries');
return;
}
this.router.routeReuseStrategy.shouldReuseRoute = () => false;
this.libraryId = parseInt(routeId, 10);
this.libraryService.getLibraryNames().pipe(take(1)).subscribe(names => {
@ -87,7 +89,9 @@ export class LibraryDetailComponent implements OnInit, OnDestroy {
});
this.actions = this.actionFactoryService.getLibraryActions(this.handleAction.bind(this));
this.pagination = {currentPage: 0, itemsPerPage: 30, totalItems: 0, totalPages: 1};
this.filterSettings.presetLibraryId = this.libraryId;
[this.filterSettings.presets, this.filterSettings.openByDefault] = this.utilityService.filterPresetsFromUrl(this.route.snapshot, this.seriesService.createSeriesFilter());
this.filterSettings.presets.libraries = [this.libraryId];
this.loadPage();
}
@ -136,6 +140,7 @@ export class LibraryDetailComponent implements OnInit, OnDestroy {
updateFilter(data: SeriesFilter) {
this.filter = data;
console.log('filter: ', this.filter);
if (this.pagination !== undefined && this.pagination !== null) {
this.pagination.currentPage = 1;
this.onPageChange(this.pagination);
@ -151,6 +156,7 @@ export class LibraryDetailComponent implements OnInit, OnDestroy {
}
this.loadingSeries = true;
// The filter is out of sync with the presets from typeaheads on first load but syncs afterwards
if (this.filter == undefined) {
this.filter = this.seriesService.createSeriesFilter();
this.filter.libraries.push(this.libraryId);

View File

@ -4,14 +4,14 @@
<!-- This first row will have random information about the series-->
<div class="row no-gutters mb-2">
<app-tag-badge title="Age Rating" *ngIf="seriesMetadata.ageRating">{{metadataService.getAgeRating(this.seriesMetadata.ageRating) | async}}</app-tag-badge>
<app-tag-badge title="Age Rating" *ngIf="seriesMetadata.ageRating" a11y-click="13,32" class="clickable" (click)="goTo('ageRating', seriesMetadata.ageRating)" [selectionMode]="TagBadgeCursor.Clickable">{{metadataService.getAgeRating(this.seriesMetadata.ageRating) | async}}</app-tag-badge>
<ng-container *ngIf="series">
<!-- Maybe we can put the library this resides in to make it easier to get back -->
<!-- tooltip here explaining how this is year of first issue -->
<app-tag-badge *ngIf="seriesMetadata.releaseYear > 0" title="Release date">{{seriesMetadata.releaseYear}}</app-tag-badge>
<app-tag-badge *ngIf="seriesMetadata.language !== null && seriesMetadata.language !== ''" title="Language">{{seriesMetadata.language}}</app-tag-badge>
<app-tag-badge title="Publication Status">{{seriesMetadata.publicationStatus | publicationStatus}}</app-tag-badge>
<app-tag-badge [selectionMode]="TagBadgeCursor.NotAllowed">
<app-tag-badge *ngIf="seriesMetadata.language !== null && seriesMetadata.language !== ''" title="Language" a11y-click="13,32" class="clickable" (click)="goTo('languages', seriesMetadata.language)" [selectionMode]="TagBadgeCursor.Clickable">{{seriesMetadata.language}}</app-tag-badge>
<app-tag-badge title="Publication Status" a11y-click="13,32" class="clickable" (click)="goTo('publicationStatus', seriesMetadata.publicationStatus)" [selectionMode]="TagBadgeCursor.Clickable">{{seriesMetadata.publicationStatus | publicationStatus}}</app-tag-badge>
<app-tag-badge a11y-click="13,32" class="clickable" (click)="goTo('format', series.format)" [selectionMode]="TagBadgeCursor.Clickable">
<app-series-format [format]="series.format">{{utilityService.mangaFormat(series.format)}}</app-series-format>
</app-tag-badge>
</ng-container>
@ -24,7 +24,7 @@
<div class="col-md-8">
<app-badge-expander [items]="seriesMetadata.genres">
<ng-template #badgeExpanderItem let-item let-position="idx">
<app-tag-badge [selectionMode]="TagBadgeCursor.Clickable">{{item.title}}</app-tag-badge>
<app-tag-badge a11y-click="13,32" class="clickable" (click)="goTo('genres', item.id)" [selectionMode]="TagBadgeCursor.Clickable">{{item.title}}</app-tag-badge>
</ng-template>
</app-badge-expander>
</div>
@ -36,21 +36,21 @@
<div class="col-md-8">
<app-badge-expander [items]="seriesMetadata.collectionTags">
<ng-template #badgeExpanderItem let-item let-position="idx">
<app-tag-badge a11y-click="13,32" class="clickable" routerLink="/collections/{{item.id}}" [selectionMode]="TagBadgeCursor.Clickable">
<app-tag-badge a11y-click="13,32" class="clickable" routerLink="/collections/{{item.id}}" [selectionMode]="TagBadgeCursor.Clickable">
{{item.title}}
</app-tag-badge>
</ng-template>
</app-badge-expander>
</div>
</div>
<div class="row no-gutters mt-1" *ngIf="seriesMetadata.writers && seriesMetadata.writers.length > 0">
<div class="row no-gutters mt-1" *ngIf="seriesMetadata.writers && seriesMetadata.writers.length > 0">
<div class="col-md-4">
<h5>Authors</h5>
<h5>Writers/Authors</h5>
</div>
<div class="col-md-8">
<app-badge-expander [items]="seriesMetadata.writers">
<ng-template #badgeExpanderItem let-item let-position="idx">
<app-person-badge [person]="item"></app-person-badge>
<app-person-badge a11y-click="13,32" class="clickable" (click)="goTo('writers', item.id)" [person]="item"></app-person-badge>
</ng-template>
</app-badge-expander>
</div>
@ -62,14 +62,14 @@
</div>
<div #collapse="ngbCollapse" [(ngbCollapse)]="isCollapsed" id="extended-series-metadata">
<div class="row no-gutters mt-1" *ngIf="seriesMetadata.artists && seriesMetadata.artists.length > 0">
<div class="row no-gutters mt-1" *ngIf="seriesMetadata.coverArtists && seriesMetadata.coverArtists.length > 0">
<div class="col-md-4">
<h5>Artists</h5>
<h5>Cover Artists</h5>
</div>
<div class="col-md-8">
<app-badge-expander [items]="seriesMetadata.artists">
<app-badge-expander [items]="seriesMetadata.coverArtists">
<ng-template #badgeExpanderItem let-item let-position="idx">
<app-person-badge [person]="item"></app-person-badge>
<app-person-badge a11y-click="13,32" class="clickable" (click)="goTo('coverArtists', item.id)" [person]="item"></app-person-badge>
</ng-template>
</app-badge-expander>
</div>
@ -82,7 +82,7 @@
<div class="col-md-8">
<app-badge-expander [items]="seriesMetadata.characters">
<ng-template #badgeExpanderItem let-item let-position="idx">
<app-person-badge [person]="item"></app-person-badge>
<app-person-badge a11y-click="13,32" class="clickable" (click)="goTo('character', item.id)" [person]="item"></app-person-badge>
</ng-template>
</app-badge-expander>
</div>
@ -95,7 +95,7 @@
<div class="col-md-8">
<app-badge-expander [items]="seriesMetadata.colorists">
<ng-template #badgeExpanderItem let-item let-position="idx">
<app-person-badge [person]="item"></app-person-badge>
<app-person-badge a11y-click="13,32" class="clickable" (click)="goTo('colorist', item.id)" [person]="item"></app-person-badge>
</ng-template>
</app-badge-expander>
</div>
@ -108,7 +108,7 @@
<div class="col-md-8">
<app-badge-expander [items]="seriesMetadata.editors">
<ng-template #badgeExpanderItem let-item let-position="idx">
<app-person-badge [person]="item"></app-person-badge>
<app-person-badge a11y-click="13,32" class="clickable" (click)="goTo('editor', item.id)" [person]="item"></app-person-badge>
</ng-template>
</app-badge-expander>
</div>
@ -121,7 +121,7 @@
<div class="col-md-8">
<app-badge-expander [items]="seriesMetadata.inkers">
<ng-template #badgeExpanderItem let-item let-position="idx">
<app-person-badge [person]="item"></app-person-badge>
<app-person-badge a11y-click="13,32" class="clickable" (click)="goTo('inker', item.id)" [person]="item"></app-person-badge>
</ng-template>
</app-badge-expander>
</div>
@ -134,7 +134,7 @@
<div class="col-md-8">
<app-badge-expander [items]="seriesMetadata.letterers">
<ng-template #badgeExpanderItem let-item let-position="idx">
<app-person-badge [person]="item"></app-person-badge>
<app-person-badge a11y-click="13,32" class="clickable" (click)="goTo('letterer', item.id)" [person]="item"></app-person-badge>
</ng-template>
</app-badge-expander>
</div>
@ -146,7 +146,7 @@
<div class="col-md-8">
<app-badge-expander [items]="seriesMetadata.tags">
<ng-template #badgeExpanderItem let-item let-position="idx">
<app-tag-badge [selectionMode]="TagBadgeCursor.Clickable">{{item.title}}</app-tag-badge>
<app-tag-badge a11y-click="13,32" class="clickable" (click)="goTo('tags', item.id)" [selectionMode]="TagBadgeCursor.Clickable">{{item.title}}</app-tag-badge>
</ng-template>
</app-badge-expander>
</div>
@ -158,7 +158,7 @@
<div class="col-md-8">
<app-badge-expander [items]="seriesMetadata.translators">
<ng-template #badgeExpanderItem let-item let-position="idx">
<app-person-badge [person]="item"></app-person-badge>
<app-person-badge a11y-click="13,32" class="clickable" (click)="goTo('translators', item.id)" [person]="item"></app-person-badge>
</ng-template>
</app-badge-expander>
</div>
@ -171,7 +171,7 @@
<div class="col-md-8">
<app-badge-expander [items]="seriesMetadata.pencillers">
<ng-template #badgeExpanderItem let-item let-position="idx">
<app-person-badge [person]="item"></app-person-badge>
<app-person-badge a11y-click="13,32" class="clickable" (click)="goTo('penciller', item.id)" [person]="item"></app-person-badge>
</ng-template>
</app-badge-expander>
</div>
@ -184,7 +184,7 @@
<div class="col-md-8">
<app-badge-expander [items]="seriesMetadata.publishers">
<ng-template #badgeExpanderItem let-item let-position="idx">
<app-person-badge [person]="item"></app-person-badge>
<app-person-badge a11y-click="13,32" class="clickable" (click)="goTo('publisher', item.id)" [person]="item"></app-person-badge>
</ng-template>
</app-badge-expander>
</div>

View File

@ -1,4 +1,5 @@
import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
import { Router } from '@angular/router';
import { TagBadgeCursor } from '../shared/tag-badge/tag-badge.component';
import { UtilityService } from '../shared/_services/utility.service';
import { MangaFormat } from '../_models/manga-format';
@ -32,12 +33,12 @@ export class SeriesMetadataDetailComponent implements OnInit, OnChanges {
return TagBadgeCursor;
}
constructor(public utilityService: UtilityService, public metadataService: MetadataService) { }
constructor(public utilityService: UtilityService, public metadataService: MetadataService, private router: Router) { }
ngOnChanges(changes: SimpleChanges): void {
this.hasExtendedProperites = this.seriesMetadata.colorists.length > 0 ||
this.seriesMetadata.editors.length > 0 ||
this.seriesMetadata.artists.length > 0 ||
this.seriesMetadata.coverArtists.length > 0 ||
this.seriesMetadata.inkers.length > 0 ||
this.seriesMetadata.letterers.length > 0 ||
this.seriesMetadata.pencillers.length > 0 ||
@ -58,5 +59,11 @@ export class SeriesMetadataDetailComponent implements OnInit, OnChanges {
this.isCollapsed = !this.isCollapsed;
}
goTo(queryParamName: string, filter: any) {
let params: any = {};
params[queryParamName] = filter;
params['page'] = 1;
this.router.navigate(['library', this.series.libraryId], {queryParams: params});
}
}

View File

@ -1,9 +1,12 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot } from '@angular/router';
import { FilterSettings } from 'src/app/cards/card-detail-layout/card-detail-layout.component';
import { Chapter } from 'src/app/_models/chapter';
import { LibraryType } from 'src/app/_models/library';
import { MangaFormat } from 'src/app/_models/manga-format';
import { AgeRating } from 'src/app/_models/metadata/age-rating';
import { Series } from 'src/app/_models/series';
import { SeriesFilter } from 'src/app/_models/series-filter';
import { Volume } from 'src/app/_models/volume';
export enum KEY_CODES {
@ -94,6 +97,116 @@ export class UtilityService {
return input.toUpperCase().replace(reg, '').includes(filter.toUpperCase().replace(reg, ''));
}
/**
* Returns a new instance of a filterSettings that is populated with filter presets from URL
* @param snapshot
* @param blankFilter Filter to start with
* @returns The Preset filter and if something was set within
*/
filterPresetsFromUrl(snapshot: ActivatedRouteSnapshot, blankFilter: SeriesFilter): [SeriesFilter, boolean] {
const filter = Object.assign({}, blankFilter);
let anyChanged = false;
const format = snapshot.queryParamMap.get('format');
if (format !== undefined && format !== null) {
filter.formats = [...filter.formats, ...format.split(',').map(item => parseInt(item, 10))];
anyChanged = true;
}
const genres = snapshot.queryParamMap.get('genres');
if (genres !== undefined && genres !== null) {
filter.genres = [...filter.genres, ...genres.split(',').map(item => parseInt(item, 10))];
anyChanged = true;
}
const ageRating = snapshot.queryParamMap.get('ageRating');
if (ageRating !== undefined && ageRating !== null) {
filter.ageRating = [...filter.ageRating, ...ageRating.split(',').map(item => parseInt(item, 10))];
anyChanged = true;
}
const publicationStatus = snapshot.queryParamMap.get('publicationStatus');
if (publicationStatus !== undefined && publicationStatus !== null) {
filter.publicationStatus = [...filter.publicationStatus, ...publicationStatus.split(',').map(item => parseInt(item, 10))];
anyChanged = true;
}
const tags = snapshot.queryParamMap.get('tags');
if (tags !== undefined && tags !== null) {
filter.tags = [...filter.tags, ...tags.split(',').map(item => parseInt(item, 10))];
anyChanged = true;
}
const languages = snapshot.queryParamMap.get('languages');
if (languages !== undefined && languages !== null) {
filter.languages = [...filter.languages, ...languages.split(',')];
anyChanged = true;
}
const writers = snapshot.queryParamMap.get('writers');
if (writers !== undefined && writers !== null) {
filter.writers = [...filter.writers, ...writers.split(',').map(item => parseInt(item, 10))];
anyChanged = true;
}
const character = snapshot.queryParamMap.get('character');
if (character !== undefined && character !== null) {
filter.character = [...filter.character, ...character.split(',').map(item => parseInt(item, 10))];
anyChanged = true;
}
const colorist = snapshot.queryParamMap.get('colorist');
if (colorist !== undefined && colorist !== null) {
filter.colorist = [...filter.colorist, ...colorist.split(',').map(item => parseInt(item, 10))];
anyChanged = true;
}
const coverArtists = snapshot.queryParamMap.get('coverArtists');
if (coverArtists !== undefined && coverArtists !== null) {
filter.coverArtist = [...filter.coverArtist, ...coverArtists.split(',').map(item => parseInt(item, 10))];
anyChanged = true;
}
const editor = snapshot.queryParamMap.get('editor');
if (editor !== undefined && editor !== null) {
filter.editor = [...filter.editor, ...editor.split(',').map(item => parseInt(item, 10))];
anyChanged = true;
}
const inker = snapshot.queryParamMap.get('inker');
if (inker !== undefined && inker !== null) {
filter.inker = [...filter.inker, ...inker.split(',').map(item => parseInt(item, 10))];
anyChanged = true;
}
const letterer = snapshot.queryParamMap.get('letterer');
if (letterer !== undefined && letterer !== null) {
filter.letterer = [...filter.letterer, ...letterer.split(',').map(item => parseInt(item, 10))];
anyChanged = true;
}
const penciller = snapshot.queryParamMap.get('penciller');
if (penciller !== undefined && penciller !== null) {
filter.penciller = [...filter.penciller, ...penciller.split(',').map(item => parseInt(item, 10))];
anyChanged = true;
}
const publisher = snapshot.queryParamMap.get('publisher');
if (publisher !== undefined && publisher !== null) {
filter.publisher = [...filter.publisher, ...publisher.split(',').map(item => parseInt(item, 10))];
anyChanged = true;
}
const translators = snapshot.queryParamMap.get('translators');
if (translators !== undefined && translators !== null) {
filter.translators = [...filter.translators, ...translators.split(',').map(item => parseInt(item, 10))];
anyChanged = true;
}
return [filter, anyChanged];
}
mangaFormat(format: MangaFormat): string {
switch (format) {
case MangaFormat.EPUB:

View File

@ -1,15 +1,4 @@
<!-- <div class="badge">
<div class="col-4 img">
<i class="fa fa-user-circle" aria-hidden="true"></i>
</div>
<div class="col-8">
<span style="font-size: 12px;">{{person.name}}</span>
</div>
</div> -->
<div class="tagbadge cursor">
<div class="tagbadge cursor clickable" style="cursor: pointer;">
<div class="media">
<!-- <img src="..." class="align-self-center mr-3" alt="..."> -->
<i class="fa fa-user-circle align-self-center mr-2" aria-hidden="true"></i>

View File

@ -9,6 +9,7 @@ import { Person } from '../../_models/person';
export class PersonBadgeComponent implements OnInit {
@Input() person!: Person;
constructor() { }