Lots of Bugfixes (#2977)
This commit is contained in:
parent
8c629695ef
commit
616ed7a75d
26 changed files with 427 additions and 244 deletions
|
|
@ -14,11 +14,11 @@ export class SearchService {
|
|||
|
||||
constructor(private httpClient: HttpClient) { }
|
||||
|
||||
search(term: string) {
|
||||
search(term: string, includeChapterAndFiles: boolean = false) {
|
||||
if (term === '') {
|
||||
return of(new SearchResultGroup());
|
||||
}
|
||||
return this.httpClient.get<SearchResultGroup>(this.baseUrl + 'search/search?queryString=' + encodeURIComponent(term));
|
||||
return this.httpClient.get<SearchResultGroup>(this.baseUrl + `search/search?includeChapterAndFiles=${includeChapterAndFiles}&queryString=${encodeURIComponent(term)}`);
|
||||
}
|
||||
|
||||
getSeriesForMangaFile(mangaFileId: number) {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import {forkJoin} from "rxjs";
|
|||
import {ToastrService} from "ngx-toastr";
|
||||
import {DecimalPipe} from "@angular/common";
|
||||
import {LoadingComponent} from "../../../shared/loading/loading.component";
|
||||
import {AccountService} from "../../../_services/account.service";
|
||||
import {ConfirmService} from "../../../shared/confirm.service";
|
||||
|
||||
@Component({
|
||||
|
|
|
|||
|
|
@ -5,126 +5,166 @@
|
|||
<input #input [id]="id" type="text" inputmode="search" autocomplete="off" formControlName="typeahead" [placeholder]="placeholder"
|
||||
aria-haspopup="listbox" aria-owns="dropdown"
|
||||
[attr.aria-expanded]="hasFocus && hasData"
|
||||
aria-autocomplete="list" (focusout)="close($event)" (focus)="open($event)" role="search"
|
||||
aria-autocomplete="list" (focusout)="close($event)" (focus)="open($event)" role="searchbox"
|
||||
>
|
||||
<div class="spinner-border spinner-border-sm" role="status" *ngIf="isLoading">
|
||||
<span class="visually-hidden">{{t('loading')}}</span>
|
||||
</div>
|
||||
<button type="button" class="btn-close" [attr.aria-label]="t('close')" (click)="resetField()" *ngIf="typeaheadForm.get('typeahead')?.value.length > 0"></button>
|
||||
@if (searchTerm.length > 0) {
|
||||
@if (isLoading) {
|
||||
<div class="spinner-border spinner-border-sm" role="status">
|
||||
<span class="visually-hidden">{{t('loading')}}</span>
|
||||
</div>
|
||||
} @else {
|
||||
<button type="button" class="btn-close" [attr.aria-label]="t('close')" (click)="resetField()"></button>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="dropdown" *ngIf="hasFocus">
|
||||
<ul class="list-group" role="listbox" id="dropdown">
|
||||
<ng-container *ngIf="seriesTemplate !== undefined && groupedData.series.length > 0">
|
||||
<li class="list-group-item section-header"><h5 id="series-group">Series</h5></li>
|
||||
<ul class="list-group results" role="group" aria-describedby="series-group">
|
||||
<li *ngFor="let option of groupedData.series; let index = index;" (click)="handleResultlick(option)" tabindex="0"
|
||||
class="list-group-item" aria-labelledby="series-group" role="option">
|
||||
<ng-container [ngTemplateOutlet]="seriesTemplate" [ngTemplateOutletContext]="{ $implicit: option, idx: index }"></ng-container>
|
||||
</li>
|
||||
</ul>
|
||||
</ng-container>
|
||||
@if (hasFocus) {
|
||||
<div class="dropdown">
|
||||
<ul class="list-group" role="listbox" id="dropdown">
|
||||
|
||||
<ng-container *ngIf="collectionTemplate !== undefined && groupedData.collections.length > 0">
|
||||
<li class="list-group-item section-header"><h5>{{t('collections')}}</h5></li>
|
||||
<ul class="list-group results">
|
||||
<li *ngFor="let option of groupedData.collections; let index = index;" (click)="handleResultlick(option)" tabindex="0"
|
||||
class="list-group-item" role="option">
|
||||
<ng-container [ngTemplateOutlet]="collectionTemplate" [ngTemplateOutletContext]="{ $implicit: option, idx: index }"></ng-container>
|
||||
</li>
|
||||
</ul>
|
||||
</ng-container>
|
||||
@if (seriesTemplate !== undefined && groupedData.series.length > 0) {
|
||||
<li class="list-group-item section-header"><h5 id="series-group">Series</h5></li>
|
||||
<ul class="list-group results" role="group" aria-describedby="series-group">
|
||||
@for(option of groupedData.series; track option; let index = $index) {
|
||||
<li (click)="handleResultClick(option)" tabindex="0"
|
||||
class="list-group-item" aria-labelledby="series-group" role="option">
|
||||
<ng-container [ngTemplateOutlet]="seriesTemplate" [ngTemplateOutletContext]="{ $implicit: option, idx: index }"></ng-container>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
|
||||
<ng-container *ngIf="readingListTemplate !== undefined && groupedData.readingLists.length > 0">
|
||||
<li class="list-group-item section-header"><h5>{{t('reading-lists')}}</h5></li>
|
||||
<ul class="list-group results">
|
||||
<li *ngFor="let option of groupedData.readingLists; let index = index;" (click)="handleResultlick(option)" tabindex="0"
|
||||
class="list-group-item" role="option">
|
||||
<ng-container [ngTemplateOutlet]="readingListTemplate" [ngTemplateOutletContext]="{ $implicit: option, idx: index }"></ng-container>
|
||||
</li>
|
||||
</ul>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="bookmarkTemplate !== undefined && groupedData.bookmarks.length > 0">
|
||||
<li class="list-group-item section-header"><h5>{{t('bookmarks')}}</h5></li>
|
||||
<ul class="list-group results">
|
||||
<li *ngFor="let option of groupedData.bookmarks; let index = index;" (click)="handleResultlick(option)" tabindex="0"
|
||||
class="list-group-item" role="option">
|
||||
<ng-container [ngTemplateOutlet]="bookmarkTemplate" [ngTemplateOutletContext]="{ $implicit: option, idx: index }"></ng-container>
|
||||
</li>
|
||||
</ul>
|
||||
</ng-container>
|
||||
@if (collectionTemplate !== undefined && groupedData.collections.length > 0) {
|
||||
<li class="list-group-item section-header"><h5>{{t('collections')}}</h5></li>
|
||||
<ul class="list-group results">
|
||||
@for(option of groupedData.collections; track option; let index = $index) {
|
||||
<li (click)="handleResultClick(option)" tabindex="0"
|
||||
class="list-group-item" role="option">
|
||||
<ng-container [ngTemplateOutlet]="collectionTemplate" [ngTemplateOutletContext]="{ $implicit: option, idx: index }"></ng-container>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
|
||||
<ng-container *ngIf="libraryTemplate !== undefined && groupedData.libraries.length > 0">
|
||||
<li class="list-group-item section-header"><h5 id="libraries-group">{{t('libraries')}}</h5></li>
|
||||
<ul class="list-group results" role="group" aria-describedby="libraries-group">
|
||||
<li *ngFor="let option of groupedData.libraries; let index = index;" (click)="handleResultlick(option)" tabindex="0"
|
||||
class="list-group-item" aria-labelledby="libraries-group" role="option">
|
||||
<ng-container [ngTemplateOutlet]="libraryTemplate" [ngTemplateOutletContext]="{ $implicit: option, idx: index }"></ng-container>
|
||||
</li>
|
||||
</ul>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="genreTemplate !== undefined && groupedData.genres.length > 0">
|
||||
<li class="list-group-item section-header"><h5>{{t('genres')}}</h5></li>
|
||||
<ul class="list-group results">
|
||||
<li *ngFor="let option of groupedData.genres; let index = index;" (click)="handleResultlick(option)" tabindex="0"
|
||||
class="list-group-item" role="option">
|
||||
<ng-container [ngTemplateOutlet]="genreTemplate" [ngTemplateOutletContext]="{ $implicit: option, idx: index }"></ng-container>
|
||||
</li>
|
||||
</ul>
|
||||
</ng-container>
|
||||
@if (readingListTemplate !== undefined && groupedData.readingLists.length > 0) {
|
||||
<li class="list-group-item section-header"><h5>{{t('reading-lists')}}</h5></li>
|
||||
<ul class="list-group results">
|
||||
@for(option of groupedData.readingLists; track option; let index = $index) {
|
||||
<li (click)="handleResultClick(option)" tabindex="0"
|
||||
class="list-group-item" role="option">
|
||||
<ng-container [ngTemplateOutlet]="readingListTemplate" [ngTemplateOutletContext]="{ $implicit: option, idx: index }"></ng-container>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
|
||||
<ng-container *ngIf="tagTemplate !== undefined && groupedData.tags.length > 0">
|
||||
<li class="list-group-item section-header"><h5>{{t('tags')}}</h5></li>
|
||||
<ul class="list-group results">
|
||||
<li *ngFor="let option of groupedData.tags; let index = index;" (click)="handleResultlick(option)" tabindex="0"
|
||||
class="list-group-item" role="option">
|
||||
<ng-container [ngTemplateOutlet]="tagTemplate" [ngTemplateOutletContext]="{ $implicit: option, idx: index }"></ng-container>
|
||||
</li>
|
||||
</ul>
|
||||
</ng-container>
|
||||
@if (bookmarkTemplate !== undefined && groupedData.bookmarks.length > 0) {
|
||||
<li class="list-group-item section-header"><h5>{{t('bookmarks')}}</h5></li>
|
||||
<ul class="list-group results">
|
||||
@for(option of groupedData.bookmarks; track option; let index = $index) {
|
||||
<li (click)="handleResultClick(option)" tabindex="0"
|
||||
class="list-group-item" role="option">
|
||||
<ng-container [ngTemplateOutlet]="bookmarkTemplate" [ngTemplateOutletContext]="{ $implicit: option, idx: index }"></ng-container>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
|
||||
<ng-container *ngIf="personTemplate !== undefined && groupedData.persons.length > 0">
|
||||
<li class="list-group-item section-header"><h5>{{t('people')}}</h5></li>
|
||||
<ul class="list-group results">
|
||||
<li *ngFor="let option of groupedData.persons; let index = index;" (click)="handleResultlick(option)" tabindex="0"
|
||||
class="list-group-item" role="option">
|
||||
<ng-container [ngTemplateOutlet]="personTemplate" [ngTemplateOutletContext]="{ $implicit: option, idx: index }"></ng-container>
|
||||
</li>
|
||||
</ul>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="chapterTemplate !== undefined && groupedData.chapters.length > 0">
|
||||
<li class="list-group-item section-header"><h5>{{t('chapters')}}</h5></li>
|
||||
<ul class="list-group results">
|
||||
<li *ngFor="let option of groupedData.chapters; let index = index;" (click)="handleResultlick(option)" tabindex="0"
|
||||
class="list-group-item" role="option">
|
||||
<ng-container [ngTemplateOutlet]="chapterTemplate" [ngTemplateOutletContext]="{ $implicit: option, idx: index }"></ng-container>
|
||||
</li>
|
||||
</ul>
|
||||
</ng-container>
|
||||
@if (libraryTemplate !== undefined && groupedData.libraries.length > 0) {
|
||||
<li class="list-group-item section-header"><h5 id="libraries-group">{{t('libraries')}}</h5></li>
|
||||
<ul class="list-group results" role="group" aria-describedby="libraries-group">
|
||||
@for(option of groupedData.libraries; track option; let index = $index) {
|
||||
<li (click)="handleResultClick(option)" tabindex="0"
|
||||
class="list-group-item" aria-labelledby="libraries-group" role="option">
|
||||
<ng-container [ngTemplateOutlet]="libraryTemplate" [ngTemplateOutletContext]="{ $implicit: option, idx: index }"></ng-container>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
|
||||
<ng-container *ngIf="fileTemplate !== undefined && groupedData.files.length > 0">
|
||||
<li class="list-group-item section-header"><h5>{{t('files')}}</h5></li>
|
||||
<ul class="list-group results">
|
||||
<li *ngFor="let option of groupedData.files; let index = index;" (click)="handleResultlick(option)" tabindex="0"
|
||||
class="list-group-item" role="option">
|
||||
<ng-container [ngTemplateOutlet]="fileTemplate" [ngTemplateOutletContext]="{ $implicit: option, idx: index }"></ng-container>
|
||||
</li>
|
||||
</ul>
|
||||
</ng-container>
|
||||
@if (genreTemplate !== undefined && groupedData.genres.length > 0) {
|
||||
<li class="list-group-item section-header"><h5>{{t('genres')}}</h5></li>
|
||||
<ul class="list-group results">
|
||||
@for(option of groupedData.genres; track option; let index = $index) {
|
||||
<li (click)="handleResultClick(option)" tabindex="0"
|
||||
class="list-group-item" role="option">
|
||||
<ng-container [ngTemplateOutlet]="genreTemplate" [ngTemplateOutletContext]="{ $implicit: option, idx: index }"></ng-container>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
|
||||
<ng-container *ngIf="!hasData && searchTerm.length > 0">
|
||||
<ul class="list-group results">
|
||||
<li class="list-group-item">
|
||||
<ng-container [ngTemplateOutlet]="noResultsTemplate"></ng-container>
|
||||
</li>
|
||||
</ul>
|
||||
@if (tagTemplate !== undefined && groupedData.tags.length > 0) {
|
||||
<li class="list-group-item section-header"><h5>{{t('tags')}}</h5></li>
|
||||
<ul class="list-group results">
|
||||
@for(option of groupedData.tags; track option; let index = $index) {
|
||||
<li (click)="handleResultClick(option)" tabindex="0"
|
||||
class="list-group-item" role="option">
|
||||
<ng-container [ngTemplateOutlet]="tagTemplate" [ngTemplateOutletContext]="{ $implicit: option, idx: index }"></ng-container>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
|
||||
</ng-container>
|
||||
</ul>
|
||||
</div>
|
||||
@if (personTemplate !== undefined && groupedData.persons.length > 0) {
|
||||
<li class="list-group-item section-header"><h5>{{t('people')}}</h5></li>
|
||||
<ul class="list-group results">
|
||||
@for(option of groupedData.persons; track option; let index = $index) {
|
||||
<li (click)="handleResultClick(option)" tabindex="0"
|
||||
class="list-group-item" role="option">
|
||||
<ng-container [ngTemplateOutlet]="personTemplate" [ngTemplateOutletContext]="{ $implicit: option, idx: index }"></ng-container>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
|
||||
@if (chapterTemplate !== undefined && groupedData.chapters.length > 0) {
|
||||
<li class="list-group-item section-header"><h5>{{t('chapters')}}</h5></li>
|
||||
<ul class="list-group results">
|
||||
@for(option of groupedData.chapters; track option; let index = $index) {
|
||||
<li (click)="handleResultClick(option)" tabindex="0"
|
||||
class="list-group-item" role="option">
|
||||
<ng-container [ngTemplateOutlet]="chapterTemplate" [ngTemplateOutletContext]="{ $implicit: option, idx: index }"></ng-container>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
|
||||
@if (fileTemplate !== undefined && groupedData.files.length > 0) {
|
||||
<li class="list-group-item section-header"><h5>{{t('files')}}</h5></li>
|
||||
<ul class="list-group results">
|
||||
@for(option of groupedData.files; track option; let index = $index) {
|
||||
<li (click)="handleResultClick(option)" tabindex="0"
|
||||
class="list-group-item" role="option">
|
||||
<ng-container [ngTemplateOutlet]="fileTemplate" [ngTemplateOutletContext]="{ $implicit: option, idx: index }"></ng-container>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
|
||||
@if (!hasData && searchTerm.length > 0 && !isLoading) {
|
||||
|
||||
<ul class="list-group results">
|
||||
<li class="list-group-item">
|
||||
<ng-container [ngTemplateOutlet]="noResultsTemplate"></ng-container>
|
||||
</li>
|
||||
</ul>
|
||||
}
|
||||
|
||||
@if (hasData && !includeChapterAndFiles) {
|
||||
<li class="list-group-item" style="min-height: 34px">
|
||||
<ng-container [ngTemplateOutlet]="extraTemplate"></ng-container>
|
||||
<a href="javascript:void(0)" (click)="toggleIncludeFiles()" class="float-end">
|
||||
{{t('include-extras')}}
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
|
||||
</form>
|
||||
|
||||
|
|
|
|||
|
|
@ -18,8 +18,14 @@ import { debounceTime } from 'rxjs/operators';
|
|||
import { KEY_CODES } from 'src/app/shared/_services/utility.service';
|
||||
import { SearchResultGroup } from 'src/app/_models/search/search-result-group';
|
||||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||
import { NgClass, NgIf, NgFor, NgTemplateOutlet } from '@angular/common';
|
||||
import { NgClass, NgTemplateOutlet } from '@angular/common';
|
||||
import {TranslocoDirective} from "@ngneat/transloco";
|
||||
import {LoadingComponent} from "../../../shared/loading/loading.component";
|
||||
|
||||
export interface SearchEvent {
|
||||
value: string;
|
||||
includeFiles: boolean;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-grouped-typeahead',
|
||||
|
|
@ -27,9 +33,12 @@ import {TranslocoDirective} from "@ngneat/transloco";
|
|||
styleUrls: ['./grouped-typeahead.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
imports: [ReactiveFormsModule, NgClass, NgIf, NgFor, NgTemplateOutlet, TranslocoDirective]
|
||||
imports: [ReactiveFormsModule, NgClass, NgTemplateOutlet, TranslocoDirective, LoadingComponent]
|
||||
})
|
||||
export class GroupedTypeaheadComponent implements OnInit {
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
private readonly cdRef = inject(ChangeDetectorRef);
|
||||
|
||||
/**
|
||||
* Unique id to tie with a label element
|
||||
*/
|
||||
|
|
@ -47,6 +56,10 @@ export class GroupedTypeaheadComponent implements OnInit {
|
|||
* Placeholder for the input
|
||||
*/
|
||||
@Input() placeholder: string = '';
|
||||
/**
|
||||
* When the search is active
|
||||
*/
|
||||
@Input() isLoading: boolean = false;
|
||||
/**
|
||||
* Number of milliseconds after typing before triggering inputChanged for data fetching
|
||||
*/
|
||||
|
|
@ -54,7 +67,7 @@ export class GroupedTypeaheadComponent implements OnInit {
|
|||
/**
|
||||
* Emits when the input changes from user interaction
|
||||
*/
|
||||
@Output() inputChanged: EventEmitter<string> = new EventEmitter();
|
||||
@Output() inputChanged: EventEmitter<SearchEvent> = new EventEmitter();
|
||||
/**
|
||||
* Emits when something is clicked/selected
|
||||
*/
|
||||
|
|
@ -76,17 +89,18 @@ export class GroupedTypeaheadComponent implements OnInit {
|
|||
@ContentChild('personTemplate') personTemplate: TemplateRef<any> | undefined;
|
||||
@ContentChild('genreTemplate') genreTemplate!: TemplateRef<any>;
|
||||
@ContentChild('noResultsTemplate') noResultsTemplate!: TemplateRef<any>;
|
||||
@ContentChild('extraTemplate') extraTemplate!: TemplateRef<any>;
|
||||
@ContentChild('libraryTemplate') libraryTemplate!: TemplateRef<any>;
|
||||
@ContentChild('readingListTemplate') readingListTemplate!: TemplateRef<any>;
|
||||
@ContentChild('fileTemplate') fileTemplate!: TemplateRef<any>;
|
||||
@ContentChild('chapterTemplate') chapterTemplate!: TemplateRef<any>;
|
||||
@ContentChild('bookmarkTemplate') bookmarkTemplate!: TemplateRef<any>;
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
|
||||
|
||||
|
||||
hasFocus: boolean = false;
|
||||
isLoading: boolean = false;
|
||||
typeaheadForm: FormGroup = new FormGroup({});
|
||||
includeChapterAndFiles: boolean = false;
|
||||
|
||||
prevSearchTerm: string = '';
|
||||
|
||||
|
|
@ -101,8 +115,6 @@ export class GroupedTypeaheadComponent implements OnInit {
|
|||
}
|
||||
|
||||
|
||||
constructor(private readonly cdRef: ChangeDetectorRef) { }
|
||||
|
||||
@HostListener('window:click', ['$event'])
|
||||
handleDocumentClick(event: any) {
|
||||
this.close();
|
||||
|
|
@ -127,7 +139,10 @@ export class GroupedTypeaheadComponent implements OnInit {
|
|||
this.typeaheadForm.addControl('typeahead', new FormControl(this.initialValue, []));
|
||||
this.cdRef.markForCheck();
|
||||
|
||||
this.typeaheadForm.valueChanges.pipe(debounceTime(this.debounceTime), takeUntilDestroyed(this.destroyRef)).subscribe(change => {
|
||||
this.typeaheadForm.valueChanges.pipe(
|
||||
debounceTime(this.debounceTime),
|
||||
takeUntilDestroyed(this.destroyRef)
|
||||
).subscribe(change => {
|
||||
const value = this.typeaheadForm.get('typeahead')?.value;
|
||||
|
||||
if (value != undefined && value != '' && !this.hasFocus) {
|
||||
|
|
@ -138,7 +153,7 @@ export class GroupedTypeaheadComponent implements OnInit {
|
|||
if (value != undefined && value.length >= this.minQueryLength) {
|
||||
|
||||
if (this.prevSearchTerm === value) return;
|
||||
this.inputChanged.emit(value);
|
||||
this.inputChanged.emit({value, includeFiles: this.includeChapterAndFiles});
|
||||
this.prevSearchTerm = value;
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
|
|
@ -164,10 +179,20 @@ export class GroupedTypeaheadComponent implements OnInit {
|
|||
});
|
||||
}
|
||||
|
||||
handleResultlick(item: any) {
|
||||
handleResultClick(item: any) {
|
||||
this.selected.emit(item);
|
||||
}
|
||||
|
||||
toggleIncludeFiles() {
|
||||
this.includeChapterAndFiles = true;
|
||||
this.inputChanged.emit({value: this.searchTerm, includeFiles: this.includeChapterAndFiles});
|
||||
|
||||
this.hasFocus = true;
|
||||
this.inputElem.nativeElement.focus();
|
||||
this.openDropdown();
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
|
||||
resetField() {
|
||||
this.prevSearchTerm = '';
|
||||
this.typeaheadForm.get('typeahead')?.setValue(this.initialValue);
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#search
|
||||
id="nav-search"
|
||||
[minQueryLength]="2"
|
||||
[isLoading]="isLoading"
|
||||
initialValue=""
|
||||
[placeholder]="t('search-alt')"
|
||||
[groupedData]="searchResults"
|
||||
|
|
@ -147,7 +148,6 @@
|
|||
<ng-template #noResultsTemplate let-notFound>
|
||||
{{t('no-data')}}
|
||||
</ng-template>
|
||||
|
||||
</app-grouped-typeahead>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ import {NgbDropdown, NgbDropdownItem, NgbDropdownMenu, NgbDropdownToggle} from '
|
|||
import {EventsWidgetComponent} from '../events-widget/events-widget.component';
|
||||
import {SeriesFormatComponent} from '../../../shared/series-format/series-format.component';
|
||||
import {ImageComponent} from '../../../shared/image/image.component';
|
||||
import {GroupedTypeaheadComponent} from '../grouped-typeahead/grouped-typeahead.component';
|
||||
import {GroupedTypeaheadComponent, SearchEvent} from '../grouped-typeahead/grouped-typeahead.component';
|
||||
import {TranslocoDirective} from "@ngneat/transloco";
|
||||
import {FilterUtilitiesService} from "../../../shared/_services/filter-utilities.service";
|
||||
import {FilterStatement} from "../../../_models/metadata/v2/filter-statement";
|
||||
|
|
@ -66,7 +66,6 @@ export class NavHeaderComponent implements OnInit {
|
|||
searchResults: SearchResultGroup = new SearchResultGroup();
|
||||
searchTerm = '';
|
||||
|
||||
|
||||
backToTopNeeded = false;
|
||||
searchFocused: boolean = false;
|
||||
scrollElem: HTMLElement;
|
||||
|
|
@ -121,12 +120,14 @@ export class NavHeaderComponent implements OnInit {
|
|||
|
||||
|
||||
|
||||
onChangeSearch(val: string) {
|
||||
|
||||
|
||||
onChangeSearch(evt: SearchEvent) {
|
||||
this.isLoading = true;
|
||||
this.searchTerm = val.trim();
|
||||
this.searchTerm = evt.value.trim();
|
||||
this.cdRef.markForCheck();
|
||||
|
||||
this.searchService.search(val.trim()).pipe(takeUntilDestroyed(this.destroyRef)).subscribe(results => {
|
||||
this.searchService.search(this.searchTerm, evt.includeFiles).pipe(takeUntilDestroyed(this.destroyRef)).subscribe(results => {
|
||||
this.searchResults = results;
|
||||
this.isLoading = false;
|
||||
this.cdRef.markForCheck();
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ import {SpreadType} from "ngx-extended-pdf-viewer/lib/options/spread-type";
|
|||
import {PdfLayoutModePipe} from "../../_pipe/pdf-layout-mode.pipe";
|
||||
import {PdfScrollModePipe} from "../../_pipe/pdf-scroll-mode.pipe";
|
||||
import {PdfSpreadModePipe} from "../../_pipe/pdf-spread-mode.pipe";
|
||||
import {HandtoolChanged} from "ngx-extended-pdf-viewer/lib/events/handtool-changed";
|
||||
|
||||
@Component({
|
||||
selector: 'app-pdf-reader',
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ export class SeriesMetadataDetailComponent implements OnChanges, OnInit {
|
|||
|
||||
ngOnInit() {
|
||||
// If on desktop, we can just have all the data expanded by default:
|
||||
this.isCollapsed = this.utilityService.getActiveBreakpoint() < Breakpoint.Desktop;
|
||||
this.isCollapsed = true; // this.utilityService.getActiveBreakpoint() < Breakpoint.Desktop;
|
||||
// Check if there is a lot of extended data, if so, re-collapse
|
||||
const sum = (this.seriesMetadata.colorists.length + this.seriesMetadata.editors.length
|
||||
+ this.seriesMetadata.coverArtists.length + this.seriesMetadata.inkers.length
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ export class UtilityService {
|
|||
);
|
||||
}
|
||||
|
||||
deepEqual(object1: any, object2: any) {
|
||||
deepEqual(object1: any | undefined | null, object2: any | undefined | null) {
|
||||
if ((object1 === null || object1 === undefined) && (object2 !== null || object2 !== undefined)) return false;
|
||||
if ((object2 === null || object2 === undefined) && (object1 !== null || object1 !== undefined)) return false;
|
||||
if (object1 === null && object2 === null) return true;
|
||||
|
|
|
|||
|
|
@ -65,7 +65,6 @@ export class SideNavComponent implements OnInit {
|
|||
homeActions = [
|
||||
{action: Action.Edit, title: 'customize', children: [], requiresAdmin: false, callback: this.openCustomize.bind(this)},
|
||||
{action: Action.Import, title: 'import-cbl', children: [], requiresAdmin: true, callback: this.importCbl.bind(this)},
|
||||
{action: Action.Import, title: 'import-mal-stack', children: [], requiresAdmin: true, callback: this.importMalCollection.bind(this)}, // This requires the Collection Rework (https://github.com/Kareadita/Kavita/issues/2810)
|
||||
];
|
||||
|
||||
filterQuery: string = '';
|
||||
|
|
@ -144,6 +143,13 @@ export class SideNavComponent implements OnInit {
|
|||
this.navService.toggleSideNav();
|
||||
this.cdRef.markForCheck();
|
||||
});
|
||||
|
||||
this.accountService.hasValidLicense$.subscribe(res =>{
|
||||
if (!res) return;
|
||||
|
||||
this.homeActions.push({action: Action.Import, title: 'import-mal-stack', children: [], requiresAdmin: true, callback: this.importMalCollection.bind(this)});
|
||||
this.cdRef.markForCheck();
|
||||
})
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
|
|
|||
|
|
@ -1537,8 +1537,8 @@
|
|||
"reading-lists": "Reading Lists",
|
||||
"collections": "Collections",
|
||||
"close": "{{common.close}}",
|
||||
"loading": "{{common.loading}}"
|
||||
|
||||
"loading": "{{common.loading}}",
|
||||
"include-extras": "Include Chapters & Files"
|
||||
},
|
||||
|
||||
"nav-header": {
|
||||
|
|
@ -1605,7 +1605,7 @@
|
|||
"description": "Import your MAL Interest Stacks and create Collections within Kavita",
|
||||
"series-count": "{{common.series-count}}",
|
||||
"restack-count": "{{num}} Restacks",
|
||||
"nothing-found": ""
|
||||
"nothing-found": "Nothing found"
|
||||
},
|
||||
|
||||
"edit-chapter-progress": {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue