Refactored to make the FilterSettings genetic and type safe. Now I need to refactor the metadata component to respect the generics.
This commit is contained in:
parent
2654ea2965
commit
a4c31debd0
20 changed files with 117 additions and 65 deletions
|
|
@ -23,7 +23,7 @@ export enum SortField {
|
||||||
Random = 9
|
Random = 9
|
||||||
}
|
}
|
||||||
|
|
||||||
export const allSortFields = Object.keys(SortField)
|
export const allSeriesSortFields = Object.keys(SortField)
|
||||||
.filter(key => !isNaN(Number(key)) && parseInt(key, 10) >= 0)
|
.filter(key => !isNaN(Number(key)) && parseInt(key, 10) >= 0)
|
||||||
.map(key => parseInt(key, 10)) as SortField[];
|
.map(key => parseInt(key, 10)) as SortField[];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ const enumArray = Object.keys(FilterField)
|
||||||
|
|
||||||
enumArray.sort((a, b) => a.value.localeCompare(b.value));
|
enumArray.sort((a, b) => a.value.localeCompare(b.value));
|
||||||
|
|
||||||
export const allFields = enumArray
|
export const allSeriesFilterFields = enumArray
|
||||||
.map(key => parseInt(key.key, 10))as FilterField[];
|
.map(key => parseInt(key.key, 10))as FilterField[];
|
||||||
|
|
||||||
export const allPeople = [
|
export const allPeople = [
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,10 @@ import {FilterStatement} from "./filter-statement";
|
||||||
import {FilterCombination} from "./filter-combination";
|
import {FilterCombination} from "./filter-combination";
|
||||||
import {SortOptions} from "./sort-options";
|
import {SortOptions} from "./sort-options";
|
||||||
|
|
||||||
export interface FilterV2<T> {
|
export interface FilterV2<TFilter, TSort extends number = number> {
|
||||||
name?: string;
|
name?: string;
|
||||||
statements: Array<FilterStatement<T>>;
|
statements: Array<FilterStatement<TFilter>>;
|
||||||
combination: FilterCombination;
|
combination: FilterCombination;
|
||||||
sortOptions?: SortOptions;
|
sortOptions?: SortOptions<TSort>;
|
||||||
limitTo: number;
|
limitTo: number;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,3 +3,9 @@ export enum PersonFilterField {
|
||||||
Role = 1,
|
Role = 1,
|
||||||
Name = 2
|
Name = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const allPersonFilterFields = Object.keys(PersonFilterField)
|
||||||
|
.filter(key => !isNaN(Number(key)) && parseInt(key, 10) >= 0)
|
||||||
|
.map(key => parseInt(key, 10)) as PersonFilterField[];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,3 +3,7 @@ export enum PersonSortField {
|
||||||
SeriesCount = 2,
|
SeriesCount = 2,
|
||||||
ChapterCount = 3
|
ChapterCount = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const allPersonSortFields = Object.keys(PersonSortField)
|
||||||
|
.filter(key => !isNaN(Number(key)) && parseInt(key, 10) >= 0)
|
||||||
|
.map(key => parseInt(key, 10)) as PersonSortField[];
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
import {SortField} from "../series-filter";
|
|
||||||
import {PersonSortField} from "./person-sort-field";
|
import {PersonSortField} from "./person-sort-field";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Series-based Sort options
|
* Series-based Sort options
|
||||||
*/
|
*/
|
||||||
export interface SortOptions {
|
export interface SortOptions<TSort extends number = number> {
|
||||||
sortField: SortField;
|
sortField: TSort;
|
||||||
isAscending: boolean;
|
isAscending: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,5 +25,4 @@ export class FilterService {
|
||||||
renameSmartFilter(filter: SmartFilter) {
|
renameSmartFilter(filter: SmartFilter) {
|
||||||
return this.httpClient.post(this.baseUrl + `filter/rename?filterId=${filter.id}&name=${filter.name.trim()}`, {});
|
return this.httpClient.post(this.baseUrl + `filter/rename?filterId=${filter.id}&name=${filter.name.trim()}`, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -227,5 +227,4 @@ export class SeriesService {
|
||||||
updateDontMatch(seriesId: number, dontMatch: boolean) {
|
updateDontMatch(seriesId: number, dontMatch: boolean) {
|
||||||
return this.httpClient.post<string>(this.baseUrl + `series/dont-match?seriesId=${seriesId}&dontMatch=${dontMatch}`, {}, TextResonse);
|
return this.httpClient.post<string>(this.baseUrl + `series/dont-match?seriesId=${seriesId}&dontMatch=${dontMatch}`, {}, TextResonse);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,13 +11,12 @@ import {Title} from '@angular/platform-browser';
|
||||||
import {ActivatedRoute, Router} from '@angular/router';
|
import {ActivatedRoute, Router} from '@angular/router';
|
||||||
import {debounceTime, take} from 'rxjs/operators';
|
import {debounceTime, take} from 'rxjs/operators';
|
||||||
import {BulkSelectionService} from 'src/app/cards/bulk-selection.service';
|
import {BulkSelectionService} from 'src/app/cards/bulk-selection.service';
|
||||||
import {FilterSettings} from 'src/app/metadata-filter/filter-settings';
|
|
||||||
import {FilterUtilitiesService} from 'src/app/shared/_services/filter-utilities.service';
|
import {FilterUtilitiesService} from 'src/app/shared/_services/filter-utilities.service';
|
||||||
import {UtilityService} from 'src/app/shared/_services/utility.service';
|
import {UtilityService} from 'src/app/shared/_services/utility.service';
|
||||||
import {JumpKey} from 'src/app/_models/jumpbar/jump-key';
|
import {JumpKey} from 'src/app/_models/jumpbar/jump-key';
|
||||||
import {Pagination} from 'src/app/_models/pagination';
|
import {Pagination} from 'src/app/_models/pagination';
|
||||||
import {Series} from 'src/app/_models/series';
|
import {Series} from 'src/app/_models/series';
|
||||||
import {FilterEvent} from 'src/app/_models/metadata/series-filter';
|
import {FilterEvent, SortField} from 'src/app/_models/metadata/series-filter';
|
||||||
import {Action, ActionItem} from 'src/app/_services/action-factory.service';
|
import {Action, ActionItem} from 'src/app/_services/action-factory.service';
|
||||||
import {ActionService} from 'src/app/_services/action.service';
|
import {ActionService} from 'src/app/_services/action.service';
|
||||||
import {JumpbarService} from 'src/app/_services/jumpbar.service';
|
import {JumpbarService} from 'src/app/_services/jumpbar.service';
|
||||||
|
|
@ -38,6 +37,7 @@ import {BrowseTitlePipe} from "../../../_pipes/browse-title.pipe";
|
||||||
import {MetadataService} from "../../../_services/metadata.service";
|
import {MetadataService} from "../../../_services/metadata.service";
|
||||||
import {Observable} from "rxjs";
|
import {Observable} from "rxjs";
|
||||||
import {FilterField} from "../../../_models/metadata/v2/filter-field";
|
import {FilterField} from "../../../_models/metadata/v2/filter-field";
|
||||||
|
import {SeriesFilterSettings} from "../../../metadata-filter/filter-settings";
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
|
@ -68,8 +68,8 @@ export class AllSeriesComponent implements OnInit {
|
||||||
series: Series[] = [];
|
series: Series[] = [];
|
||||||
loadingSeries = false;
|
loadingSeries = false;
|
||||||
pagination: Pagination = new Pagination();
|
pagination: Pagination = new Pagination();
|
||||||
filter: FilterV2<FilterField> | undefined = undefined;
|
filter: FilterV2<FilterField, SortField> | undefined = undefined;
|
||||||
filterSettings: FilterSettings<FilterField> = new FilterSettings();
|
filterSettings: SeriesFilterSettings = new SeriesFilterSettings();
|
||||||
filterOpen: EventEmitter<boolean> = new EventEmitter();
|
filterOpen: EventEmitter<boolean> = new EventEmitter();
|
||||||
filterActiveCheck!: FilterV2<FilterField>;
|
filterActiveCheck!: FilterV2<FilterField>;
|
||||||
filterActive: boolean = false;
|
filterActive: boolean = false;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ import {ActivatedRoute, Router} from '@angular/router';
|
||||||
import {ToastrService} from 'ngx-toastr';
|
import {ToastrService} from 'ngx-toastr';
|
||||||
import {take} from 'rxjs';
|
import {take} from 'rxjs';
|
||||||
import {BulkSelectionService} from 'src/app/cards/bulk-selection.service';
|
import {BulkSelectionService} from 'src/app/cards/bulk-selection.service';
|
||||||
import {FilterSettings} from 'src/app/metadata-filter/filter-settings';
|
|
||||||
import {ConfirmService} from 'src/app/shared/confirm.service';
|
import {ConfirmService} from 'src/app/shared/confirm.service';
|
||||||
import {DownloadService} from 'src/app/shared/_services/download.service';
|
import {DownloadService} from 'src/app/shared/_services/download.service';
|
||||||
import {FilterUtilitiesService} from 'src/app/shared/_services/filter-utilities.service';
|
import {FilterUtilitiesService} from 'src/app/shared/_services/filter-utilities.service';
|
||||||
|
|
@ -28,6 +27,7 @@ import {FilterV2} from "../../../_models/metadata/v2/filter-v2";
|
||||||
import {Title} from "@angular/platform-browser";
|
import {Title} from "@angular/platform-browser";
|
||||||
import {WikiLink} from "../../../_models/wiki";
|
import {WikiLink} from "../../../_models/wiki";
|
||||||
import {FilterField} from "../../../_models/metadata/v2/filter-field";
|
import {FilterField} from "../../../_models/metadata/v2/filter-field";
|
||||||
|
import {SeriesFilterSettings} from "../../../metadata-filter/filter-settings";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-bookmarks',
|
selector: 'app-bookmarks',
|
||||||
|
|
@ -65,7 +65,7 @@ export class BookmarksComponent implements OnInit {
|
||||||
|
|
||||||
pagination: Pagination = new Pagination();
|
pagination: Pagination = new Pagination();
|
||||||
filter: FilterV2<FilterField> | undefined = undefined;
|
filter: FilterV2<FilterField> | undefined = undefined;
|
||||||
filterSettings: FilterSettings<FilterField> = new FilterSettings();
|
filterSettings: SeriesFilterSettings = new SeriesFilterSettings();
|
||||||
filterOpen: EventEmitter<boolean> = new EventEmitter();
|
filterOpen: EventEmitter<boolean> = new EventEmitter();
|
||||||
filterActive: boolean = false;
|
filterActive: boolean = false;
|
||||||
filterActiveCheck!: FilterV2<FilterField>;
|
filterActiveCheck!: FilterV2<FilterField>;
|
||||||
|
|
|
||||||
|
|
@ -32,10 +32,10 @@ import {debounceTime, tap} from "rxjs/operators";
|
||||||
import {SortButtonComponent} from "../../_single-module/sort-button/sort-button.component";
|
import {SortButtonComponent} from "../../_single-module/sort-button/sort-button.component";
|
||||||
import {PersonSortField} from "../../_models/metadata/v2/person-sort-field";
|
import {PersonSortField} from "../../_models/metadata/v2/person-sort-field";
|
||||||
import {PersonSortOptions} from "../../_models/metadata/v2/sort-options";
|
import {PersonSortOptions} from "../../_models/metadata/v2/sort-options";
|
||||||
import {FilterSettings} from "../../metadata-filter/filter-settings";
|
|
||||||
import {PersonFilterField} from "../../_models/metadata/v2/person-filter-field";
|
import {PersonFilterField} from "../../_models/metadata/v2/person-filter-field";
|
||||||
import {FilterUtilitiesService} from "../../shared/_services/filter-utilities.service";
|
import {FilterUtilitiesService} from "../../shared/_services/filter-utilities.service";
|
||||||
import {FilterV2} from "../../_models/metadata/v2/filter-v2";
|
import {FilterV2} from "../../_models/metadata/v2/filter-v2";
|
||||||
|
import {PersonFilterSettings} from "../../metadata-filter/filter-settings";
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
|
@ -83,7 +83,7 @@ export class BrowseAuthorsComponent implements OnInit {
|
||||||
query: new FormControl('', []),
|
query: new FormControl('', []),
|
||||||
});
|
});
|
||||||
isAscending: boolean = true;
|
isAscending: boolean = true;
|
||||||
filterSettings: FilterSettings<PersonFilterField> = new FilterSettings<PersonFilterField>();
|
filterSettings: PersonFilterSettings = new PersonFilterSettings();
|
||||||
filterActive: boolean = false;
|
filterActive: boolean = false;
|
||||||
filterOpen: EventEmitter<boolean> = new EventEmitter();
|
filterOpen: EventEmitter<boolean> = new EventEmitter();
|
||||||
filter: FilterV2<PersonFilterField> | undefined = undefined;
|
filter: FilterV2<PersonFilterField> | undefined = undefined;
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ import {
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import {NavigationStart, Router} from '@angular/router';
|
import {NavigationStart, Router} from '@angular/router';
|
||||||
import {VirtualScrollerComponent, VirtualScrollerModule} from '@iharbeck/ngx-virtual-scroller';
|
import {VirtualScrollerComponent, VirtualScrollerModule} from '@iharbeck/ngx-virtual-scroller';
|
||||||
import {FilterSettings} from 'src/app/metadata-filter/filter-settings';
|
|
||||||
import {FilterUtilitiesService} from 'src/app/shared/_services/filter-utilities.service';
|
import {FilterUtilitiesService} from 'src/app/shared/_services/filter-utilities.service';
|
||||||
import {Breakpoint, UtilityService} from 'src/app/shared/_services/utility.service';
|
import {Breakpoint, UtilityService} from 'src/app/shared/_services/utility.service';
|
||||||
import {JumpKey} from 'src/app/_models/jumpbar/jump-key';
|
import {JumpKey} from 'src/app/_models/jumpbar/jump-key';
|
||||||
|
|
@ -39,6 +38,7 @@ import {filter, map} from "rxjs/operators";
|
||||||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||||
import {tap} from "rxjs";
|
import {tap} from "rxjs";
|
||||||
import {FilterV2} from "../../_models/metadata/v2/filter-v2";
|
import {FilterV2} from "../../_models/metadata/v2/filter-v2";
|
||||||
|
import {FilterSettingsBase, SeriesFilterSettings} from "../../metadata-filter/filter-settings";
|
||||||
|
|
||||||
|
|
||||||
const ANIMATION_TIME_MS = 0;
|
const ANIMATION_TIME_MS = 0;
|
||||||
|
|
@ -49,7 +49,7 @@ const ANIMATION_TIME_MS = 0;
|
||||||
* How to use:
|
* How to use:
|
||||||
* - For filtering:
|
* - For filtering:
|
||||||
* - pass a filterSettings which will bootstrap the filtering bar
|
* - pass a filterSettings which will bootstrap the filtering bar
|
||||||
* - pass a jumpbar method binding to calc the count for the entity
|
* - pass a jumpbar method binding to calc the count for the entity (not implemented yet)
|
||||||
* - For card layout
|
* - For card layout
|
||||||
* - Pass an identity function for trackby
|
* - Pass an identity function for trackby
|
||||||
* - Pass a pagination object for the total count
|
* - Pass a pagination object for the total count
|
||||||
|
|
@ -84,7 +84,7 @@ export class CardDetailLayoutComponent implements OnInit, OnChanges {
|
||||||
*/
|
*/
|
||||||
@Input() parentScroll!: Element | Window;
|
@Input() parentScroll!: Element | Window;
|
||||||
|
|
||||||
// Filter Code
|
// We need to pass filterOpen from the grandfather to the metadata filter due to the filter button being in a separate component
|
||||||
@Input() filterOpen!: EventEmitter<boolean>;
|
@Input() filterOpen!: EventEmitter<boolean>;
|
||||||
/**
|
/**
|
||||||
* Should filtering be shown on the page
|
* Should filtering be shown on the page
|
||||||
|
|
@ -98,7 +98,7 @@ export class CardDetailLayoutComponent implements OnInit, OnChanges {
|
||||||
* A trackBy to help with rendering. This is required as without it there are issues when scrolling
|
* A trackBy to help with rendering. This is required as without it there are issues when scrolling
|
||||||
*/
|
*/
|
||||||
@Input({required: true}) trackByIdentity!: TrackByFunction<any>;
|
@Input({required: true}) trackByIdentity!: TrackByFunction<any>;
|
||||||
@Input() filterSettings!: FilterSettings<number>;
|
@Input() filterSettings!: FilterSettingsBase;
|
||||||
@Input() refresh!: EventEmitter<void>;
|
@Input() refresh!: EventEmitter<void>;
|
||||||
/**
|
/**
|
||||||
* Pass the filter object optionally. If not passed, will create a SeriesFilter by default
|
* Pass the filter object optionally. If not passed, will create a SeriesFilter by default
|
||||||
|
|
@ -150,7 +150,7 @@ export class CardDetailLayoutComponent implements OnInit, OnChanges {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
if (this.filterSettings === undefined) {
|
if (this.filterSettings === undefined) {
|
||||||
this.filterSettings = new FilterSettings();
|
this.filterSettings = new SeriesFilterSettings();
|
||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ import {ToastrService} from 'ngx-toastr';
|
||||||
import {debounceTime, take} from 'rxjs/operators';
|
import {debounceTime, take} from 'rxjs/operators';
|
||||||
import {BulkSelectionService} from 'src/app/cards/bulk-selection.service';
|
import {BulkSelectionService} from 'src/app/cards/bulk-selection.service';
|
||||||
import {EditCollectionTagsComponent} from 'src/app/cards/_modals/edit-collection-tags/edit-collection-tags.component';
|
import {EditCollectionTagsComponent} from 'src/app/cards/_modals/edit-collection-tags/edit-collection-tags.component';
|
||||||
import {FilterSettings} from 'src/app/metadata-filter/filter-settings';
|
|
||||||
import {FilterUtilitiesService} from 'src/app/shared/_services/filter-utilities.service';
|
import {FilterUtilitiesService} from 'src/app/shared/_services/filter-utilities.service';
|
||||||
import {Breakpoint, UtilityService} from 'src/app/shared/_services/utility.service';
|
import {Breakpoint, UtilityService} from 'src/app/shared/_services/utility.service';
|
||||||
import {UserCollection} from 'src/app/_models/collection-tag';
|
import {UserCollection} from 'src/app/_models/collection-tag';
|
||||||
|
|
@ -63,6 +62,7 @@ import {DefaultModalOptions} from "../../../_models/default-modal-options";
|
||||||
import {ScrobbleProviderNamePipe} from "../../../_pipes/scrobble-provider-name.pipe";
|
import {ScrobbleProviderNamePipe} from "../../../_pipes/scrobble-provider-name.pipe";
|
||||||
import {PromotedIconComponent} from "../../../shared/_components/promoted-icon/promoted-icon.component";
|
import {PromotedIconComponent} from "../../../shared/_components/promoted-icon/promoted-icon.component";
|
||||||
import {FilterStatement} from "../../../_models/metadata/v2/filter-statement";
|
import {FilterStatement} from "../../../_models/metadata/v2/filter-statement";
|
||||||
|
import {SeriesFilterSettings} from "../../../metadata-filter/filter-settings";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-collection-detail',
|
selector: 'app-collection-detail',
|
||||||
|
|
@ -111,7 +111,7 @@ export class CollectionDetailComponent implements OnInit, AfterContentChecked {
|
||||||
pagination: Pagination = new Pagination();
|
pagination: Pagination = new Pagination();
|
||||||
collectionTagActions: ActionItem<UserCollection>[] = [];
|
collectionTagActions: ActionItem<UserCollection>[] = [];
|
||||||
filter: FilterV2<FilterField> | undefined = undefined;
|
filter: FilterV2<FilterField> | undefined = undefined;
|
||||||
filterSettings: FilterSettings<FilterField> = new FilterSettings();
|
filterSettings: SeriesFilterSettings = new SeriesFilterSettings();
|
||||||
summary: string = '';
|
summary: string = '';
|
||||||
user!: User;
|
user!: User;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@ import {EVENTS, MessageHubService} from '../_services/message-hub.service';
|
||||||
import {SeriesService} from '../_services/series.service';
|
import {SeriesService} from '../_services/series.service';
|
||||||
import {NavService} from '../_services/nav.service';
|
import {NavService} from '../_services/nav.service';
|
||||||
import {FilterUtilitiesService} from '../shared/_services/filter-utilities.service';
|
import {FilterUtilitiesService} from '../shared/_services/filter-utilities.service';
|
||||||
import {FilterSettings} from '../metadata-filter/filter-settings';
|
|
||||||
import {JumpKey} from '../_models/jumpbar/jump-key';
|
import {JumpKey} from '../_models/jumpbar/jump-key';
|
||||||
import {SeriesRemovedEvent} from '../_models/events/series-removed-event';
|
import {SeriesRemovedEvent} from '../_models/events/series-removed-event';
|
||||||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||||
|
|
@ -43,6 +42,7 @@ import {FilterField} from "../_models/metadata/v2/filter-field";
|
||||||
import {CardActionablesComponent} from "../_single-module/card-actionables/card-actionables.component";
|
import {CardActionablesComponent} from "../_single-module/card-actionables/card-actionables.component";
|
||||||
import {LoadingComponent} from "../shared/loading/loading.component";
|
import {LoadingComponent} from "../shared/loading/loading.component";
|
||||||
import {debounceTime, ReplaySubject, tap} from "rxjs";
|
import {debounceTime, ReplaySubject, tap} from "rxjs";
|
||||||
|
import {SeriesFilterSettings} from "../metadata-filter/filter-settings";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-library-detail',
|
selector: 'app-library-detail',
|
||||||
|
|
@ -76,7 +76,7 @@ export class LibraryDetailComponent implements OnInit {
|
||||||
pagination: Pagination = {currentPage: 0, totalPages: 0, totalItems: 0, itemsPerPage: 0};
|
pagination: Pagination = {currentPage: 0, totalPages: 0, totalItems: 0, itemsPerPage: 0};
|
||||||
actions: ActionItem<Library>[] = [];
|
actions: ActionItem<Library>[] = [];
|
||||||
filter: FilterV2<FilterField> | undefined = undefined;
|
filter: FilterV2<FilterField> | undefined = undefined;
|
||||||
filterSettings: FilterSettings<FilterField> = new FilterSettings();
|
filterSettings: SeriesFilterSettings = new SeriesFilterSettings();
|
||||||
filterOpen: EventEmitter<boolean> = new EventEmitter();
|
filterOpen: EventEmitter<boolean> = new EventEmitter();
|
||||||
filterActive: boolean = false;
|
filterActive: boolean = false;
|
||||||
filterActiveCheck!: FilterV2<FilterField>;
|
filterActiveCheck!: FilterV2<FilterField>;
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ import {FormControl, FormGroup, FormsModule, ReactiveFormsModule} from "@angular
|
||||||
import {NgbTooltip} from "@ng-bootstrap/ng-bootstrap";
|
import {NgbTooltip} from "@ng-bootstrap/ng-bootstrap";
|
||||||
import {FilterCombination} from "../../../_models/metadata/v2/filter-combination";
|
import {FilterCombination} from "../../../_models/metadata/v2/filter-combination";
|
||||||
import {FilterUtilitiesService} from "../../../shared/_services/filter-utilities.service";
|
import {FilterUtilitiesService} from "../../../shared/_services/filter-utilities.service";
|
||||||
import {allFields} from "../../../_models/metadata/v2/filter-field";
|
import {allSeriesFilterFields} from "../../../_models/metadata/v2/filter-field";
|
||||||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||||
import {distinctUntilChanged, tap} from "rxjs/operators";
|
import {distinctUntilChanged, tap} from "rxjs/operators";
|
||||||
import {translate, TranslocoDirective} from "@jsverse/transloco";
|
import {translate, TranslocoDirective} from "@jsverse/transloco";
|
||||||
|
|
@ -43,7 +43,7 @@ export class MetadataBuilderComponent implements OnInit {
|
||||||
* The number of statements that can be. 0 means unlimited. -1 means none.
|
* The number of statements that can be. 0 means unlimited. -1 means none.
|
||||||
*/
|
*/
|
||||||
@Input() statementLimit = 0;
|
@Input() statementLimit = 0;
|
||||||
@Input() availableFilterFields = allFields;
|
@Input() availableFilterFields = allSeriesFilterFields;
|
||||||
@Output() update: EventEmitter<FilterV2<number>> = new EventEmitter<FilterV2<number>>();
|
@Output() update: EventEmitter<FilterV2<number>> = new EventEmitter<FilterV2<number>>();
|
||||||
@Output() apply: EventEmitter<void> = new EventEmitter<void>();
|
@Output() apply: EventEmitter<void> = new EventEmitter<void>();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import {FilterStatement} from '../../../_models/metadata/v2/filter-statement';
|
||||||
import {BehaviorSubject, distinctUntilChanged, filter, Observable, of, startWith, switchMap, tap} from 'rxjs';
|
import {BehaviorSubject, distinctUntilChanged, filter, Observable, of, startWith, switchMap, tap} from 'rxjs';
|
||||||
import {MetadataService} from 'src/app/_services/metadata.service';
|
import {MetadataService} from 'src/app/_services/metadata.service';
|
||||||
import {FilterComparison} from 'src/app/_models/metadata/v2/filter-comparison';
|
import {FilterComparison} from 'src/app/_models/metadata/v2/filter-comparison';
|
||||||
import {allFields, FilterField} from 'src/app/_models/metadata/v2/filter-field';
|
import {allSeriesFilterFields, FilterField} from 'src/app/_models/metadata/v2/filter-field';
|
||||||
import {AsyncPipe} from "@angular/common";
|
import {AsyncPipe} from "@angular/common";
|
||||||
import {FilterFieldPipe} from "../../../_pipes/filter-field.pipe";
|
import {FilterFieldPipe} from "../../../_pipes/filter-field.pipe";
|
||||||
import {FilterComparisonPipe} from "../../../_pipes/filter-comparison.pipe";
|
import {FilterComparisonPipe} from "../../../_pipes/filter-comparison.pipe";
|
||||||
|
|
@ -148,7 +148,7 @@ export class MetadataFilterRowComponent implements OnInit {
|
||||||
* Slightly misleading as this is the initial state and will be updated on the filterStatement event emitter
|
* Slightly misleading as this is the initial state and will be updated on the filterStatement event emitter
|
||||||
*/
|
*/
|
||||||
@Input() preset!: FilterStatement<number>;
|
@Input() preset!: FilterStatement<number>;
|
||||||
@Input() availableFields: Array<FilterField> = allFields;
|
@Input() availableFields: Array<FilterField> = allSeriesFilterFields;
|
||||||
@Output() filterStatement = new EventEmitter<FilterStatement<number>>();
|
@Output() filterStatement = new EventEmitter<FilterStatement<number>>();
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
import {FilterV2} from "../_models/metadata/v2/filter-v2";
|
import {FilterV2} from "../_models/metadata/v2/filter-v2";
|
||||||
|
import {SortField} from "../_models/metadata/series-filter";
|
||||||
|
import {PersonSortField} from "../_models/metadata/v2/person-sort-field";
|
||||||
|
import {PersonFilterField} from "../_models/metadata/v2/person-filter-field";
|
||||||
|
import {FilterField} from "../_models/metadata/v2/filter-field";
|
||||||
|
|
||||||
export class FilterSettings<T> {
|
export class FilterSettingsBase<TFilter extends number = number, TSort extends number = number> {
|
||||||
presetsV2: FilterV2<T> | undefined;
|
presetsV2: FilterV2<TFilter, TSort> | undefined;
|
||||||
sortDisabled = false;
|
sortDisabled = false;
|
||||||
/**
|
/**
|
||||||
* The number of statements that can be on the filter. Set to 1 to disable adding more.
|
* The number of statements that can be on the filter. Set to 1 to disable adding more.
|
||||||
|
|
@ -9,3 +13,19 @@ export class FilterSettings<T> {
|
||||||
statementLimit: number = 0;
|
statementLimit: number = 0;
|
||||||
saveDisabled: boolean = false;
|
saveDisabled: boolean = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter Settings for Series entity
|
||||||
|
*/
|
||||||
|
export class SeriesFilterSettings extends FilterSettingsBase<FilterField, SortField> {
|
||||||
|
type = 'sortField';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter Settings for People entity
|
||||||
|
*/
|
||||||
|
export class PersonFilterSettings extends FilterSettingsBase<PersonFilterField, PersonSortField> {
|
||||||
|
type = 'personSortField';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,12 @@ import {
|
||||||
ChangeDetectionStrategy,
|
ChangeDetectionStrategy,
|
||||||
ChangeDetectorRef,
|
ChangeDetectorRef,
|
||||||
Component,
|
Component,
|
||||||
|
computed,
|
||||||
ContentChild,
|
ContentChild,
|
||||||
DestroyRef,
|
DestroyRef,
|
||||||
EventEmitter,
|
EventEmitter,
|
||||||
inject,
|
inject,
|
||||||
|
input,
|
||||||
Input,
|
Input,
|
||||||
OnInit,
|
OnInit,
|
||||||
Output
|
Output
|
||||||
|
|
@ -14,9 +16,8 @@ import {FormControl, FormGroup, FormsModule, ReactiveFormsModule} from '@angular
|
||||||
import {NgbCollapse} from '@ng-bootstrap/ng-bootstrap';
|
import {NgbCollapse} from '@ng-bootstrap/ng-bootstrap';
|
||||||
import {Breakpoint, UtilityService} from '../shared/_services/utility.service';
|
import {Breakpoint, UtilityService} from '../shared/_services/utility.service';
|
||||||
import {Library} from '../_models/library/library';
|
import {Library} from '../_models/library/library';
|
||||||
import {allSortFields, FilterEvent, FilterItem, SortField} from '../_models/metadata/series-filter';
|
import {allSeriesSortFields, FilterEvent, FilterItem} from '../_models/metadata/series-filter';
|
||||||
import {ToggleService} from '../_services/toggle.service';
|
import {ToggleService} from '../_services/toggle.service';
|
||||||
import {FilterSettings} from './filter-settings';
|
|
||||||
import {FilterV2} from '../_models/metadata/v2/filter-v2';
|
import {FilterV2} from '../_models/metadata/v2/filter-v2';
|
||||||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||||
import {DrawerComponent} from '../shared/drawer/drawer.component';
|
import {DrawerComponent} from '../shared/drawer/drawer.component';
|
||||||
|
|
@ -24,11 +25,15 @@ import {AsyncPipe, NgClass, NgTemplateOutlet} from '@angular/common';
|
||||||
import {translate, TranslocoModule, TranslocoService} from "@jsverse/transloco";
|
import {translate, TranslocoModule, TranslocoService} from "@jsverse/transloco";
|
||||||
import {SortFieldPipe} from "../_pipes/sort-field.pipe";
|
import {SortFieldPipe} from "../_pipes/sort-field.pipe";
|
||||||
import {MetadataBuilderComponent} from "./_components/metadata-builder/metadata-builder.component";
|
import {MetadataBuilderComponent} from "./_components/metadata-builder/metadata-builder.component";
|
||||||
import {allFields, FilterField} from "../_models/metadata/v2/filter-field";
|
import {allSeriesFilterFields, FilterField} from "../_models/metadata/v2/filter-field";
|
||||||
import {FilterService} from "../_services/filter.service";
|
import {FilterService} from "../_services/filter.service";
|
||||||
import {ToastrService} from "ngx-toastr";
|
import {ToastrService} from "ngx-toastr";
|
||||||
import {animate, style, transition, trigger} from "@angular/animations";
|
import {animate, style, transition, trigger} from "@angular/animations";
|
||||||
import {SortButtonComponent} from "../_single-module/sort-button/sort-button.component";
|
import {SortButtonComponent} from "../_single-module/sort-button/sort-button.component";
|
||||||
|
import {FilterUtilitiesService} from "../shared/_services/filter-utilities.service";
|
||||||
|
import {FilterSettingsBase} from "./filter-settings";
|
||||||
|
import {allPersonSortFields} from "../_models/metadata/v2/person-sort-field";
|
||||||
|
import {allPersonFilterFields} from "../_models/metadata/v2/person-filter-field";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-metadata-filter',
|
selector: 'app-metadata-filter',
|
||||||
|
|
@ -51,10 +56,11 @@ import {SortButtonComponent} from "../_single-module/sort-button/sort-button.com
|
||||||
ReactiveFormsModule, FormsModule, AsyncPipe, TranslocoModule,
|
ReactiveFormsModule, FormsModule, AsyncPipe, TranslocoModule,
|
||||||
MetadataBuilderComponent, NgClass, SortButtonComponent]
|
MetadataBuilderComponent, NgClass, SortButtonComponent]
|
||||||
})
|
})
|
||||||
export class MetadataFilterComponent implements OnInit {
|
export class MetadataFilterComponent<TFilter extends number = number, TSort extends number = number> implements OnInit {
|
||||||
protected readonly allFilterFields = allFields;
|
protected readonly allFilterFields = allSeriesFilterFields;
|
||||||
|
|
||||||
private readonly destroyRef = inject(DestroyRef);
|
private readonly destroyRef = inject(DestroyRef);
|
||||||
|
private readonly filterUtilityService = inject(FilterUtilitiesService);
|
||||||
public readonly utilityService = inject(UtilityService);
|
public readonly utilityService = inject(UtilityService);
|
||||||
private readonly cdRef = inject(ChangeDetectorRef);
|
private readonly cdRef = inject(ChangeDetectorRef);
|
||||||
private readonly toastr = inject(ToastrService);
|
private readonly toastr = inject(ToastrService);
|
||||||
|
|
@ -63,7 +69,7 @@ export class MetadataFilterComponent implements OnInit {
|
||||||
protected readonly translocoService = inject(TranslocoService);
|
protected readonly translocoService = inject(TranslocoService);
|
||||||
private readonly sortFieldPipe = new SortFieldPipe(this.translocoService);
|
private readonly sortFieldPipe = new SortFieldPipe(this.translocoService);
|
||||||
|
|
||||||
protected readonly allSortFields = allSortFields.map(f => {
|
protected readonly allSortFields = allSeriesSortFields.map(f => {
|
||||||
return {title: this.sortFieldPipe.transform(f), value: f};
|
return {title: this.sortFieldPipe.transform(f), value: f};
|
||||||
}).sort((a, b) => a.title.localeCompare(b.title));
|
}).sort((a, b) => a.title.localeCompare(b.title));
|
||||||
|
|
||||||
|
|
@ -76,7 +82,23 @@ export class MetadataFilterComponent implements OnInit {
|
||||||
* Should filtering be shown on the page
|
* Should filtering be shown on the page
|
||||||
*/
|
*/
|
||||||
@Input() filteringDisabled: boolean = false;
|
@Input() filteringDisabled: boolean = false;
|
||||||
@Input({required: true}) filterSettings!: FilterSettings<FilterField>;
|
//@Input({required: true}) filterSettings!: FilterSettings<T>;
|
||||||
|
|
||||||
|
@Input({required: true}) filterSettings!: FilterSettingsBase<TFilter, TSort>;
|
||||||
|
/**
|
||||||
|
* Entity type derives the Sort and Filter fields
|
||||||
|
*/
|
||||||
|
entityType = input<'series' | 'person'>('series');
|
||||||
|
|
||||||
|
sortFieldOptions = computed(() => {
|
||||||
|
if (this.entityType() === 'series') return allSeriesSortFields;
|
||||||
|
return allPersonSortFields;
|
||||||
|
});
|
||||||
|
filterFieldOptions = computed(() => {
|
||||||
|
if (this.entityType() === 'series') return allSeriesFilterFields;
|
||||||
|
return allPersonFilterFields;
|
||||||
|
});
|
||||||
|
|
||||||
@Output() applyFilter: EventEmitter<FilterEvent> = new EventEmitter();
|
@Output() applyFilter: EventEmitter<FilterEvent> = new EventEmitter();
|
||||||
@ContentChild('[ngbCollapse]') collapse!: NgbCollapse;
|
@ContentChild('[ngbCollapse]') collapse!: NgbCollapse;
|
||||||
|
|
||||||
|
|
@ -100,7 +122,7 @@ export class MetadataFilterComponent implements OnInit {
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
if (this.filterSettings === undefined) {
|
if (this.filterSettings === undefined) {
|
||||||
this.filterSettings = new FilterSettings();
|
this.filterSettings = new FilterSettingsBase<TFilter, TSort>();
|
||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -159,8 +181,10 @@ export class MetadataFilterComponent implements OnInit {
|
||||||
|
|
||||||
this.filterV2 = this.deepClone(this.filterSettings.presetsV2);
|
this.filterV2 = this.deepClone(this.filterSettings.presetsV2);
|
||||||
|
|
||||||
|
const defaultSortField = this.sortFieldOptions()[0];
|
||||||
|
|
||||||
this.sortGroup = new FormGroup({
|
this.sortGroup = new FormGroup({
|
||||||
sortField: new FormControl({value: this.filterV2?.sortOptions?.sortField || SortField.SortName, disabled: this.filterSettings.sortDisabled}, []),
|
sortField: new FormControl({value: this.filterV2?.sortOptions?.sortField || defaultSortField, disabled: this.filterSettings.sortDisabled}, []),
|
||||||
limitTo: new FormControl(this.filterV2?.limitTo || 0, []),
|
limitTo: new FormControl(this.filterV2?.limitTo || 0, []),
|
||||||
name: new FormControl(this.filterV2?.name || '', [])
|
name: new FormControl(this.filterV2?.name || '', [])
|
||||||
});
|
});
|
||||||
|
|
@ -192,9 +216,11 @@ export class MetadataFilterComponent implements OnInit {
|
||||||
this.isAscendingSort = isAscending;
|
this.isAscendingSort = isAscending;
|
||||||
|
|
||||||
if (this.filterV2?.sortOptions === null) {
|
if (this.filterV2?.sortOptions === null) {
|
||||||
|
const defaultSortField = this.sortFieldOptions()[0];
|
||||||
|
|
||||||
this.filterV2.sortOptions = {
|
this.filterV2.sortOptions = {
|
||||||
isAscending: this.isAscendingSort,
|
isAscending: this.isAscendingSort,
|
||||||
sortField: SortField.SortName
|
sortField: defaultSortField
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -236,5 +262,6 @@ export class MetadataFilterComponent implements OnInit {
|
||||||
this.toggleService.set(!this.filteringCollapsed);
|
this.toggleService.set(!this.filteringCollapsed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected readonly Breakpoint = Breakpoint;
|
protected readonly Breakpoint = Breakpoint;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ import {TextResonse} from "../../_types/text-response";
|
||||||
import {environment} from "../../../environments/environment";
|
import {environment} from "../../../environments/environment";
|
||||||
import {map, tap} from "rxjs/operators";
|
import {map, tap} from "rxjs/operators";
|
||||||
import {Observable, of, switchMap} from "rxjs";
|
import {Observable, of, switchMap} from "rxjs";
|
||||||
import {Location} from "@angular/common";
|
|
||||||
import {PersonFilterField} from "../../_models/metadata/v2/person-filter-field";
|
import {PersonFilterField} from "../../_models/metadata/v2/person-filter-field";
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -21,12 +20,11 @@ import {PersonFilterField} from "../../_models/metadata/v2/person-filter-field";
|
||||||
})
|
})
|
||||||
export class FilterUtilitiesService {
|
export class FilterUtilitiesService {
|
||||||
|
|
||||||
private readonly location = inject(Location);
|
|
||||||
private readonly router = inject(Router);
|
private readonly router = inject(Router);
|
||||||
private readonly metadataService = inject(MetadataService);
|
private readonly metadataService = inject(MetadataService);
|
||||||
private readonly http = inject(HttpClient);
|
private readonly http = inject(HttpClient);
|
||||||
|
|
||||||
private apiUrl = environment.apiUrl;
|
private readonly apiUrl = environment.apiUrl;
|
||||||
|
|
||||||
encodeFilter(filter: FilterV2<number> | undefined) {
|
encodeFilter(filter: FilterV2<number> | undefined) {
|
||||||
return this.http.post<string>(this.apiUrl + 'filter/encode', filter, TextResonse);
|
return this.http.post<string>(this.apiUrl + 'filter/encode', filter, TextResonse);
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ import {Title} from '@angular/platform-browser';
|
||||||
import {ActivatedRoute, Router} from '@angular/router';
|
import {ActivatedRoute, Router} from '@angular/router';
|
||||||
import {debounceTime, take} from 'rxjs';
|
import {debounceTime, take} from 'rxjs';
|
||||||
import {BulkSelectionService} from 'src/app/cards/bulk-selection.service';
|
import {BulkSelectionService} from 'src/app/cards/bulk-selection.service';
|
||||||
import {FilterSettings} from 'src/app/metadata-filter/filter-settings';
|
|
||||||
import {FilterUtilitiesService} from 'src/app/shared/_services/filter-utilities.service';
|
import {FilterUtilitiesService} from 'src/app/shared/_services/filter-utilities.service';
|
||||||
import {UtilityService} from 'src/app/shared/_services/utility.service';
|
import {UtilityService} from 'src/app/shared/_services/utility.service';
|
||||||
import {SeriesRemovedEvent} from 'src/app/_models/events/series-removed-event';
|
import {SeriesRemovedEvent} from 'src/app/_models/events/series-removed-event';
|
||||||
|
|
@ -41,6 +40,7 @@ import {
|
||||||
import {translate, TranslocoDirective} from "@jsverse/transloco";
|
import {translate, TranslocoDirective} from "@jsverse/transloco";
|
||||||
import {FilterV2} from "../../../_models/metadata/v2/filter-v2";
|
import {FilterV2} from "../../../_models/metadata/v2/filter-v2";
|
||||||
import {FilterField} from "../../../_models/metadata/v2/filter-field";
|
import {FilterField} from "../../../_models/metadata/v2/filter-field";
|
||||||
|
import {SeriesFilterSettings} from "../../../metadata-filter/filter-settings";
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
|
@ -60,7 +60,7 @@ export class WantToReadComponent implements OnInit, AfterContentChecked {
|
||||||
series: Array<Series> = [];
|
series: Array<Series> = [];
|
||||||
pagination: Pagination = new Pagination();
|
pagination: Pagination = new Pagination();
|
||||||
filter: FilterV2<FilterField> | undefined = undefined;
|
filter: FilterV2<FilterField> | undefined = undefined;
|
||||||
filterSettings: FilterSettings<FilterField> = new FilterSettings();
|
filterSettings: SeriesFilterSettings = new SeriesFilterSettings();
|
||||||
refresh: EventEmitter<void> = new EventEmitter();
|
refresh: EventEmitter<void> = new EventEmitter();
|
||||||
|
|
||||||
filterActiveCheck!: FilterV2<FilterField>;
|
filterActiveCheck!: FilterV2<FilterField>;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue