Smart Filter Polish & New Filters (#2283)
This commit is contained in:
parent
0d8c081093
commit
45f6fb67d4
23 changed files with 375 additions and 181 deletions
|
@ -20,6 +20,7 @@ export enum SortField {
|
|||
LastChapterAdded = 4,
|
||||
TimeToRead = 5,
|
||||
ReleaseYear = 6,
|
||||
ReadProgress = 7,
|
||||
}
|
||||
|
||||
export const allSortFields = Object.keys(SortField)
|
||||
|
|
|
@ -27,7 +27,8 @@ export enum FilterField
|
|||
ReadTime = 23,
|
||||
Path = 24,
|
||||
FilePath = 25,
|
||||
WantToRead = 26
|
||||
WantToRead = 26,
|
||||
ReadingDate = 27
|
||||
}
|
||||
|
||||
export const allFields = Object.keys(FilterField)
|
||||
|
|
|
@ -205,7 +205,7 @@ export class ActionFactoryService {
|
|||
action: Action.Scan,
|
||||
title: 'scan-library',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
requiresAdmin: true,
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
|
|
|
@ -1,47 +1,73 @@
|
|||
<ng-container *transloco="let t; read: 'metadata-filter-row'">
|
||||
<form [formGroup]="formGroup">
|
||||
<div class="row g-0">
|
||||
<div class="col-md-3 me-2 col-10 mb-2">
|
||||
<select class="form-select me-2" formControlName="input">
|
||||
<option *ngFor="let field of availableFields" [value]="field">{{field | filterField}}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<form [formGroup]="formGroup">
|
||||
<div class="row g-0">
|
||||
<div class="col-md-3 me-2 col-10 mb-2">
|
||||
<select class="form-select me-2" formControlName="input">
|
||||
<option *ngFor="let field of availableFields" [value]="field">{{field | filterField}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2 me-2 col-10 mb-2">
|
||||
<select class="col-auto form-select" formControlName="comparison">
|
||||
<option *ngFor="let comparison of validComparisons$ | async" [value]="comparison">{{comparison | filterComparison}}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-2 me-2 col-10 mb-2">
|
||||
<select class="col-auto form-select" formControlName="comparison">
|
||||
<option *ngFor="let comparison of validComparisons$ | async" [value]="comparison">{{comparison | filterComparison}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4 col-10 mb-2">
|
||||
<ng-container *ngIf="predicateType$ | async as predicateType">
|
||||
<ng-container [ngSwitch]="predicateType">
|
||||
<ng-container *ngSwitchCase="PredicateType.Text">
|
||||
<input type="text" class="form-control me-2" autocomplete="true" formControlName="filterValue">
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="PredicateType.Number">
|
||||
<input type="number" inputmode="numeric" class="form-control me-2" formControlName="filterValue" min="0">
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="PredicateType.Boolean">
|
||||
<input type="checkbox" class="form-check-input mt-2 me-2" style="font-size: 1.5rem" formControlName="filterValue">
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="PredicateType.Date">
|
||||
<div class="input-group">
|
||||
<input
|
||||
class="form-control"
|
||||
placeholder="yyyy-mm-dd"
|
||||
name="dp"
|
||||
formControlName="filterValue"
|
||||
(dateSelect)="onDateSelect($event)"
|
||||
(blur)="updateIfDateFilled()"
|
||||
ngbDatepicker
|
||||
#d="ngbDatepicker"
|
||||
/>
|
||||
<button class="btn btn-outline-secondary fa-solid fa-calendar-days" (click)="d.toggle()" type="button"></button>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4 col-10 mb-2">
|
||||
<ng-container *ngIf="predicateType$ | async as predicateType">
|
||||
<ng-container [ngSwitch]="predicateType">
|
||||
<ng-container *ngSwitchCase="PredicateType.Text">
|
||||
<input type="text" class="form-control me-2" autocomplete="true" formControlName="filterValue">
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="PredicateType.Number">
|
||||
<input type="number" inputmode="numeric" class="form-control me-2" formControlName="filterValue" min="0">
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="PredicateType.Boolean">
|
||||
<input type="checkbox" class="form-check-input mt-2 me-2" style="font-size: 1.5rem" formControlName="filterValue">
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="PredicateType.Dropdown">
|
||||
<ng-container *ngIf="dropdownOptions$ | async as opts">
|
||||
<ng-container *ngTemplateOutlet="dropdown; context: { options: opts, multipleAllowed: MultipleDropdownAllowed }"></ng-container>
|
||||
<ng-template #dropdown let-options="options" let-multipleAllowed="multipleAllowed">
|
||||
<select2 [data]="options"
|
||||
formControlName="filterValue"
|
||||
[multiple]="multipleAllowed"
|
||||
[infiniteScroll]="true"
|
||||
[resettable]="true">
|
||||
</select2>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="PredicateType.Dropdown">
|
||||
<ng-container *ngIf="dropdownOptions$ | async as opts">
|
||||
<ng-container *ngTemplateOutlet="dropdown; context: { options: opts, multipleAllowed: MultipleDropdownAllowed }"></ng-container>
|
||||
<ng-template #dropdown let-options="options" let-multipleAllowed="multipleAllowed">
|
||||
<select2 [data]="options"
|
||||
formControlName="filterValue"
|
||||
[hideSelectedItems]="true"
|
||||
[multiple]="multipleAllowed"
|
||||
[infiniteScroll]="true"
|
||||
[resettable]="true">
|
||||
</select2>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-content #removeBtn></ng-content>
|
||||
</div>
|
||||
</form>
|
||||
<div class="col pt-2 ms-2">
|
||||
<ng-container *ngIf="UiLabel !== null">
|
||||
<span class="text-muted">{{t(UiLabel.unit)}}</span>
|
||||
<i *ngIf="UiLabel.tooltip" class="fa fa-info-circle ms-1" aria-hidden="true" [ngbTooltip]="t(UiLabel.tooltip)"></i>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<ng-content #removeBtn></ng-content>
|
||||
</div>
|
||||
</form>
|
||||
</ng-container>
|
||||
|
|
|
@ -1,3 +1,23 @@
|
|||
::ng-deep .select2-selection__rendered {
|
||||
padding-top: 4px !important;
|
||||
}
|
||||
|
||||
|
||||
::ng-deep .ngb-dp-content, ::ng-deep .ngb-dp-header, ::ng-deep .dropdown-menu{
|
||||
background: var(--bs-body-bg);
|
||||
color: var(--body-text-color);
|
||||
}
|
||||
|
||||
::ng-deep .ngb-dp-header, ::ng-deep .ngb-dp-weekdays {
|
||||
background-color: var(--bs-body-bg) !important;
|
||||
}
|
||||
|
||||
::ng-deep .ngb-dp-day .btn-light, ::ng-deep .ngb-dp-weekday {
|
||||
background: var(--bs-body-bg);
|
||||
color: var(--body-text-color);
|
||||
}
|
||||
|
||||
::ng-deep [ngbDatepickerDayView]:hover:not(.bg-primary), [ngbDatepickerDayView].active:not(.bg-primary) {
|
||||
background: var(--primary-color-dark-shade) !important;
|
||||
outline: 1px solid var(--primary-color-dark-shade) !important;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import {
|
|||
inject,
|
||||
Input,
|
||||
OnInit,
|
||||
Output,
|
||||
Output, ViewChild,
|
||||
} from '@angular/core';
|
||||
import {FormControl, FormGroup, ReactiveFormsModule} from '@angular/forms';
|
||||
import {FilterStatement} from '../../../_models/metadata/v2/filter-statement';
|
||||
|
@ -25,14 +25,38 @@ import {FilterComparisonPipe} from "../../_pipes/filter-comparison.pipe";
|
|||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||
import {Select2Module, Select2Option} from "ng-select2-component";
|
||||
import {TagBadgeComponent} from "../../../shared/tag-badge/tag-badge.component";
|
||||
import {
|
||||
NgbDate,
|
||||
NgbDateParserFormatter,
|
||||
NgbDatepicker,
|
||||
NgbDateStruct,
|
||||
NgbInputDatepicker,
|
||||
NgbTooltip
|
||||
} from "@ng-bootstrap/ng-bootstrap";
|
||||
import {TranslocoDirective} from "@ngneat/transloco";
|
||||
|
||||
enum PredicateType {
|
||||
Text = 1,
|
||||
Number = 2,
|
||||
Dropdown = 3,
|
||||
Boolean = 4
|
||||
Boolean = 4,
|
||||
Date = 5
|
||||
}
|
||||
|
||||
class FilterRowUi {
|
||||
unit = '';
|
||||
tooltip = ''
|
||||
constructor(unit: string = '', tooltip: string = '') {
|
||||
this.unit = unit;
|
||||
this.tooltip = tooltip;
|
||||
}
|
||||
}
|
||||
|
||||
const unitLabels: Map<FilterField, FilterRowUi> = new Map([
|
||||
[FilterField.ReadingDate, new FilterRowUi('unit-reading-date')],
|
||||
[FilterField.ReadProgress, new FilterRowUi('unit-reading-progress')],
|
||||
]);
|
||||
|
||||
const StringFields = [FilterField.SeriesName, FilterField.Summary, FilterField.Path, FilterField.FilePath];
|
||||
const NumberFields = [FilterField.ReadTime, FilterField.ReleaseYear, FilterField.ReadProgress, FilterField.UserRating];
|
||||
const DropdownFields = [FilterField.PublicationStatus, FilterField.Languages, FilterField.AgeRating,
|
||||
|
@ -42,7 +66,8 @@ const DropdownFields = [FilterField.PublicationStatus, FilterField.Languages, Fi
|
|||
FilterField.Writers, FilterField.Genres, FilterField.Libraries,
|
||||
FilterField.Formats, FilterField.CollectionTags, FilterField.Tags
|
||||
];
|
||||
const BooleanFields = [FilterField.WantToRead]
|
||||
const BooleanFields = [FilterField.WantToRead];
|
||||
const DateFields = [FilterField.ReadingDate];
|
||||
|
||||
const DropdownFieldsWithoutMustContains = [
|
||||
FilterField.Libraries, FilterField.Formats, FilterField.AgeRating, FilterField.PublicationStatus
|
||||
|
@ -59,7 +84,8 @@ const StringComparisons = [FilterComparison.Equal,
|
|||
FilterComparison.BeginsWith,
|
||||
FilterComparison.EndsWith,
|
||||
FilterComparison.Matches];
|
||||
const DateComparisons = [FilterComparison.IsBefore, FilterComparison.IsAfter, FilterComparison.IsInLast, FilterComparison.IsNotInLast];
|
||||
const DateComparisons = [FilterComparison.IsBefore, FilterComparison.IsAfter, FilterComparison.Equal,
|
||||
FilterComparison.NotEqual,];
|
||||
const NumberComparisons = [FilterComparison.Equal,
|
||||
FilterComparison.NotEqual,
|
||||
FilterComparison.LessThan,
|
||||
|
@ -91,7 +117,11 @@ const BooleanComparisons = [
|
|||
NgIf,
|
||||
Select2Module,
|
||||
NgTemplateOutlet,
|
||||
TagBadgeComponent
|
||||
TagBadgeComponent,
|
||||
NgbTooltip,
|
||||
TranslocoDirective,
|
||||
NgbDatepicker,
|
||||
NgbInputDatepicker
|
||||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
|
@ -105,8 +135,10 @@ export class MetadataFilterRowComponent implements OnInit {
|
|||
@Input() availableFields: Array<FilterField> = allFields;
|
||||
@Output() filterStatement = new EventEmitter<FilterStatement>();
|
||||
|
||||
|
||||
private readonly cdRef = inject(ChangeDetectorRef);
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
private readonly dateParser = inject(NgbDateParserFormatter);
|
||||
|
||||
formGroup: FormGroup = new FormGroup({
|
||||
'comparison': new FormControl<FilterComparison>(FilterComparison.Equal, []),
|
||||
|
@ -119,6 +151,12 @@ export class MetadataFilterRowComponent implements OnInit {
|
|||
loaded: boolean = false;
|
||||
protected readonly PredicateType = PredicateType;
|
||||
|
||||
get UiLabel(): FilterRowUi | null {
|
||||
const field = parseInt(this.formGroup.get('input')!.value, 10) as FilterField;
|
||||
if (!unitLabels.has(field)) return null;
|
||||
return unitLabels.get(field) as FilterRowUi;
|
||||
}
|
||||
|
||||
get MultipleDropdownAllowed() {
|
||||
const comp = parseInt(this.formGroup.get('comparison')?.value, 10) as FilterComparison;
|
||||
return comp === FilterComparison.Contains || comp === FilterComparison.NotContains || comp === FilterComparison.MustContains;
|
||||
|
@ -149,30 +187,36 @@ export class MetadataFilterRowComponent implements OnInit {
|
|||
|
||||
|
||||
this.formGroup!.valueChanges.pipe(distinctUntilChanged(), takeUntilDestroyed(this.destroyRef)).subscribe(_ => {
|
||||
const stmt = {
|
||||
comparison: parseInt(this.formGroup.get('comparison')?.value, 10) as FilterComparison,
|
||||
field: parseInt(this.formGroup.get('input')?.value, 10) as FilterField,
|
||||
value: this.formGroup.get('filterValue')?.value!
|
||||
};
|
||||
|
||||
// Some ids can get through and be numbers, convert them to strings for the backend
|
||||
if (typeof stmt.value === 'number' && !Number.isNaN(stmt.value)) {
|
||||
stmt.value = stmt.value + '';
|
||||
}
|
||||
|
||||
if (typeof stmt.value === 'boolean') {
|
||||
stmt.value = stmt.value + '';
|
||||
}
|
||||
|
||||
if (!stmt.value && (stmt.field !== FilterField.SeriesName && !BooleanFields.includes(stmt.field))) return;
|
||||
this.filterStatement.emit(stmt);
|
||||
this.propagateFilterUpdate();
|
||||
});
|
||||
|
||||
this.loaded = true;
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
|
||||
propagateFilterUpdate() {
|
||||
const stmt = {
|
||||
comparison: parseInt(this.formGroup.get('comparison')?.value, 10) as FilterComparison,
|
||||
field: parseInt(this.formGroup.get('input')?.value, 10) as FilterField,
|
||||
value: this.formGroup.get('filterValue')?.value!
|
||||
};
|
||||
|
||||
if (typeof stmt.value === 'object' && DateFields.includes(stmt.field)) {
|
||||
stmt.value = this.dateParser.format(stmt.value);
|
||||
}
|
||||
|
||||
// Some ids can get through and be numbers, convert them to strings for the backend
|
||||
if (typeof stmt.value === 'number' && !Number.isNaN(stmt.value)) {
|
||||
stmt.value = stmt.value + '';
|
||||
}
|
||||
|
||||
if (typeof stmt.value === 'boolean') {
|
||||
stmt.value = stmt.value + '';
|
||||
}
|
||||
|
||||
if (!stmt.value && (![FilterField.SeriesName, FilterField.Summary].includes(stmt.field) && !BooleanFields.includes(stmt.field))) return;
|
||||
this.filterStatement.emit(stmt);
|
||||
}
|
||||
|
||||
populateFromPreset() {
|
||||
const val = this.preset.value === "undefined" || !this.preset.value ? '' : this.preset.value;
|
||||
|
@ -183,7 +227,10 @@ export class MetadataFilterRowComponent implements OnInit {
|
|||
this.formGroup.get('filterValue')?.patchValue(val);
|
||||
} else if (BooleanFields.includes(this.preset.field)) {
|
||||
this.formGroup.get('filterValue')?.patchValue(val);
|
||||
} else if (DropdownFields.includes(this.preset.field)) {
|
||||
} else if (DateFields.includes(this.preset.field)) {
|
||||
this.formGroup.get('filterValue')?.patchValue(this.dateParser.parse(val)); // TODO: Figure out how this works
|
||||
}
|
||||
else if (DropdownFields.includes(this.preset.field)) {
|
||||
if (this.MultipleDropdownAllowed || val.includes(',')) {
|
||||
this.formGroup.get('filterValue')?.patchValue(val.split(',').map(d => parseInt(d, 10)));
|
||||
} else {
|
||||
|
@ -281,6 +328,16 @@ export class MetadataFilterRowComponent implements OnInit {
|
|||
return;
|
||||
}
|
||||
|
||||
if (DateFields.includes(inputVal)) {
|
||||
this.validComparisons$.next(DateComparisons);
|
||||
this.predicateType$.next(PredicateType.Date);
|
||||
|
||||
if (this.loaded) {
|
||||
this.formGroup.get('filterValue')?.patchValue(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (BooleanFields.includes(inputVal)) {
|
||||
this.validComparisons$.next(BooleanComparisons);
|
||||
this.predicateType$.next(PredicateType.Boolean);
|
||||
|
@ -306,4 +363,15 @@ export class MetadataFilterRowComponent implements OnInit {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
onDateSelect(event: NgbDate) {
|
||||
console.log('date selected: ', event);
|
||||
this.propagateFilterUpdate();
|
||||
}
|
||||
updateIfDateFilled() {
|
||||
console.log('date inputted: ', this.formGroup.get('filterValue')?.value);
|
||||
this.propagateFilterUpdate();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -64,6 +64,8 @@ export class FilterFieldPipe implements PipeTransform {
|
|||
return translate('filter-field-pipe.file-path');
|
||||
case FilterField.WantToRead:
|
||||
return translate('filter-field-pipe.want-to-read');
|
||||
case FilterField.ReadingDate:
|
||||
return translate('filter-field-pipe.read-date');
|
||||
default:
|
||||
throw new Error(`Invalid FilterField value: ${value}`);
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
<ng-container *transloco="let t; read: 'metadata-filter'">
|
||||
<ng-container *ngIf="toggleService.toggleState$ | async as isOpen">
|
||||
<div class="phone-hidden" *ngIf="utilityService.getActiveBreakpoint() > Breakpoint.Tablet">
|
||||
<div *ngIf="utilityService.getActiveBreakpoint() >= Breakpoint.Tablet">
|
||||
<div #collapse="ngbCollapse" [ngbCollapse]="!isOpen" (ngbCollapseChange)="setToggle($event)">
|
||||
<ng-container [ngTemplateOutlet]="filterSection"></ng-container>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="not-phone-hidden" *ngIf="utilityService.getActiveBreakpoint() < Breakpoint.Desktop">
|
||||
<div *ngIf="utilityService.getActiveBreakpoint() < Breakpoint.Desktop">
|
||||
<app-drawer #commentDrawer="drawer" [isOpen]="isOpen" [options]="{topOffset: 56}" (drawerClosed)="toggleService.set(false)">
|
||||
<h5 header>
|
||||
{{t('filter-title')}}
|
||||
|
@ -51,6 +51,16 @@
|
|||
<div class="col-md-3 col-sm-10">
|
||||
<label for="filter-name" class="form-label">{{t('filter-name-label')}}</label>
|
||||
<input id="filter-name" type="text" class="form-control" formControlName="name">
|
||||
<!-- <select2 [data]="smartFilters"-->
|
||||
<!-- id="filter-name"-->
|
||||
<!-- formControlName="name"-->
|
||||
<!-- (update)="updateFilterValue($event)"-->
|
||||
<!-- [autoCreate]="true"-->
|
||||
<!-- [multiple]="false"-->
|
||||
<!-- [infiniteScroll]="false"-->
|
||||
<!-- [hideSelectedItems]="true"-->
|
||||
<!-- [resettable]="true">-->
|
||||
<!-- </select2>-->
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="utilityService.getActiveBreakpoint() > Breakpoint.Tablet" [ngTemplateOutlet]="buttons"></ng-container>
|
||||
|
@ -63,15 +73,13 @@
|
|||
</ng-template>
|
||||
<ng-template #buttons>
|
||||
<!-- TODO: I might want to put a Clear button which blanks out the whole filter -->
|
||||
<div class="col-md-1 col-sm-6 mt-4 pt-1">
|
||||
<button class="btn btn-secondary col-12" (click)="clear()">{{t('reset')}}</button>
|
||||
<div class="col-md-2 col-sm-6 mt-4 pt-2 d-flex justify-content-between">
|
||||
<button class="btn btn-secondary col-6 me-1" (click)="clear()"><i class="fa-solid fa-arrow-rotate-left me-1" aria-hidden="true"></i>{{t('reset')}}</button>
|
||||
<button class="btn btn-primary col-6" (click)="apply()"><i class="fa-solid fa-play me-1" aria-hidden="true"></i>{{t('apply')}}</button>
|
||||
</div>
|
||||
<div class="col-md-1 col-sm-6 mt-4 pt-1">
|
||||
<button class="btn btn-primary col-12" (click)="apply()">{{t('apply')}}</button>
|
||||
</div>
|
||||
<div class="col-md-1 col-sm-6 mt-4 pt-1">
|
||||
<div class="col-md-1 col-sm-6 mt-4 pt-2">
|
||||
<button class="btn btn-primary col-12" (click)="save()" [disabled]="filterSettings.saveDisabled || !this.sortGroup.get('name')?.value">
|
||||
<!-- TODO: Icon here -->
|
||||
<i class="fa-solid fa-floppy-disk" aria-hidden="true"></i>
|
||||
{{t('save')}}
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -30,6 +30,8 @@ import {MetadataService} from "../_services/metadata.service";
|
|||
import {FilterUtilitiesService} from "../shared/_services/filter-utilities.service";
|
||||
import {FilterService} from "../_services/filter.service";
|
||||
import {ToastrService} from "ngx-toastr";
|
||||
import {Select2Module, Select2Option, Select2UpdateEvent} from "ng-select2-component";
|
||||
import {SmartFilter} from "../_models/metadata/v2/smart-filter";
|
||||
|
||||
@Component({
|
||||
selector: 'app-metadata-filter',
|
||||
|
@ -38,7 +40,7 @@ import {ToastrService} from "ngx-toastr";
|
|||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
imports: [NgIf, NgbCollapse, NgTemplateOutlet, DrawerComponent, NgbTooltip, TypeaheadComponent,
|
||||
ReactiveFormsModule, FormsModule, NgbRating, AsyncPipe, TranslocoModule, SortFieldPipe, MetadataBuilderComponent, NgForOf]
|
||||
ReactiveFormsModule, FormsModule, NgbRating, AsyncPipe, TranslocoModule, SortFieldPipe, MetadataBuilderComponent, NgForOf, Select2Module]
|
||||
})
|
||||
export class MetadataFilterComponent implements OnInit {
|
||||
|
||||
|
@ -78,16 +80,22 @@ export class MetadataFilterComponent implements OnInit {
|
|||
allSortFields = allSortFields;
|
||||
allFilterFields = allFields;
|
||||
|
||||
handleFilters(filter: SeriesFilterV2) {
|
||||
this.filterV2 = filter;
|
||||
}
|
||||
|
||||
smartFilters!: Array<Select2Option>;
|
||||
|
||||
private readonly cdRef = inject(ChangeDetectorRef);
|
||||
private readonly toastr = inject(ToastrService);
|
||||
|
||||
|
||||
constructor(public toggleService: ToggleService, private filterService: FilterService) {}
|
||||
constructor(public toggleService: ToggleService, private filterService: FilterService) {
|
||||
this.filterService.getAllFilters().subscribe(res => {
|
||||
this.smartFilters = res.map(r => {
|
||||
return {
|
||||
value: r,
|
||||
label: r.name,
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.filterSettings === undefined) {
|
||||
|
@ -106,6 +114,11 @@ export class MetadataFilterComponent implements OnInit {
|
|||
this.loadFromPresetsAndSetup();
|
||||
}
|
||||
|
||||
updateFilterValue(event: Select2UpdateEvent<any>) {
|
||||
console.log('event: ', event);
|
||||
}
|
||||
|
||||
|
||||
close() {
|
||||
this.filterOpen.emit(false);
|
||||
this.filteringCollapsed = true;
|
||||
|
@ -137,6 +150,10 @@ export class MetadataFilterComponent implements OnInit {
|
|||
return clonedObj;
|
||||
}
|
||||
|
||||
handleFilters(filter: SeriesFilterV2) {
|
||||
this.filterV2 = filter;
|
||||
}
|
||||
|
||||
|
||||
loadFromPresetsAndSetup() {
|
||||
this.fullyLoaded = false;
|
||||
|
@ -187,7 +204,7 @@ export class MetadataFilterComponent implements OnInit {
|
|||
|
||||
apply() {
|
||||
this.applyFilter.emit({isFirst: this.updateApplied === 0, filterV2: this.filterV2!});
|
||||
|
||||
|
||||
if (this.utilityService.getActiveBreakpoint() === Breakpoint.Mobile && this.updateApplied !== 0) {
|
||||
this.toggleSelected();
|
||||
}
|
||||
|
|
|
@ -14,17 +14,19 @@ export class SortFieldPipe implements PipeTransform {
|
|||
transform(value: SortField): string {
|
||||
switch (value) {
|
||||
case SortField.SortName:
|
||||
return this.translocoService.translate('sort-field-pipe.sort-name')
|
||||
return this.translocoService.translate('sort-field-pipe.sort-name');
|
||||
case SortField.Created:
|
||||
return this.translocoService.translate('sort-field-pipe.created')
|
||||
return this.translocoService.translate('sort-field-pipe.created');
|
||||
case SortField.LastModified:
|
||||
return this.translocoService.translate('sort-field-pipe.last-modified')
|
||||
return this.translocoService.translate('sort-field-pipe.last-modified');
|
||||
case SortField.LastChapterAdded:
|
||||
return this.translocoService.translate('sort-field-pipe.last-chapter-added')
|
||||
return this.translocoService.translate('sort-field-pipe.last-chapter-added');
|
||||
case SortField.TimeToRead:
|
||||
return this.translocoService.translate('sort-field-pipe.time-to-read')
|
||||
return this.translocoService.translate('sort-field-pipe.time-to-read');
|
||||
case SortField.ReleaseYear:
|
||||
return this.translocoService.translate('sort-field-pipe.release-year')
|
||||
return this.translocoService.translate('sort-field-pipe.release-year');
|
||||
case SortField.ReadProgress:
|
||||
return this.translocoService.translate('sort-field-pipe.read-progress');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@
|
|||
<button ngbDropdownItem (click)="read(true)">
|
||||
<span>
|
||||
<i class="fa fa-glasses" aria-hidden="true"></i>
|
||||
<span class="read-btn--text"> {{(hasReadingProgress) ? t('continue') : t('read')}} Incognito</span>
|
||||
<span class="read-btn--text"> {{(hasReadingProgress) ? t('continue-incognito') : t('read-incognito')}}</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
@ -101,7 +101,7 @@
|
|||
</div>
|
||||
|
||||
<div class="col-auto ms-2 d-none d-md-block" *ngIf="isAdmin || hasDownloadingRole">
|
||||
<button class="btn btn-secondary" (click)="downloadSeries()" title="Download Series" [disabled]="downloadInProgress">
|
||||
<button class="btn btn-secondary" (click)="downloadSeries()" [title]="t('download-series--tooltip')" [disabled]="downloadInProgress">
|
||||
<ng-container *ngIf="downloadInProgress; else notDownloading">
|
||||
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||
<span class="visually-hidden">{{t('downloading-status')}}</span>
|
||||
|
|
|
@ -681,6 +681,8 @@
|
|||
"continue-from": "Continue {{title}}",
|
||||
"read": "{{common.read}}",
|
||||
"continue": "Continue",
|
||||
"read-incognito": "Read Incognito",
|
||||
"continue-incognito": "Continue Incognito",
|
||||
"read-options-alt": "Read options",
|
||||
"incognito": "Incognito",
|
||||
"remove-from-want-to-read": "Remove from Want to Read",
|
||||
|
@ -1509,7 +1511,7 @@
|
|||
"reset": "{{common.reset}}",
|
||||
"apply": "{{common.apply}}",
|
||||
"save": "{{common.save}}",
|
||||
"limit-label": "Limit To",
|
||||
"limit-label": "Limit",
|
||||
|
||||
"format-label": "Format",
|
||||
"libraries-label": "Libraries",
|
||||
|
@ -1541,13 +1543,19 @@
|
|||
"max": "Max"
|
||||
},
|
||||
|
||||
"metadata-filter-row": {
|
||||
"unit-reading-date": "Date",
|
||||
"unit-reading-progress": "Percent"
|
||||
},
|
||||
|
||||
"sort-field-pipe": {
|
||||
"sort-name": "Sort Name",
|
||||
"created": "Created",
|
||||
"last-modified": "Last Modified",
|
||||
"last-chapter-added": "Item Added",
|
||||
"time-to-read": "Time to Read",
|
||||
"release-year": "Release Year"
|
||||
"release-year": "Release Year",
|
||||
"read-progress": "Read Progress"
|
||||
},
|
||||
|
||||
"edit-series-modal": {
|
||||
|
@ -1751,7 +1759,8 @@
|
|||
"writers": "Writers",
|
||||
"path": "Path",
|
||||
"file-path": "File Path",
|
||||
"want-to-read": "Want to Read"
|
||||
"want-to-read": "Want to Read",
|
||||
"read-date": "Reading Date"
|
||||
},
|
||||
|
||||
"filter-comparison-pipe": {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue