Applied new _components layout structure to Kavita. All except manga as there is an open PR that drastically changes that module. (#1666)
This commit is contained in:
parent
e2470cba88
commit
ab9021cb32
198 changed files with 263 additions and 270 deletions
|
|
@ -0,0 +1,231 @@
|
|||
import { DOCUMENT } from '@angular/common';
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Inject, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||
import { NavigationEnd, Router } from '@angular/router';
|
||||
import { fromEvent, Subject } from 'rxjs';
|
||||
import { debounceTime, distinctUntilChanged, filter, takeUntil, tap } from 'rxjs/operators';
|
||||
import { FilterQueryParam } from 'src/app/shared/_services/filter-utilities.service';
|
||||
import { Chapter } from 'src/app/_models/chapter';
|
||||
import { CollectionTag } from 'src/app/_models/collection-tag';
|
||||
import { Library } from 'src/app/_models/library';
|
||||
import { MangaFile } from 'src/app/_models/manga-file';
|
||||
import { PersonRole } from 'src/app/_models/metadata/person';
|
||||
import { ReadingList } from 'src/app/_models/reading-list';
|
||||
import { SearchResult } from 'src/app/_models/search/search-result';
|
||||
import { SearchResultGroup } from 'src/app/_models/search/search-result-group';
|
||||
import { AccountService } from 'src/app/_services/account.service';
|
||||
import { ImageService } from 'src/app/_services/image.service';
|
||||
import { NavService } from 'src/app/_services/nav.service';
|
||||
import { ScrollService } from 'src/app/_services/scroll.service';
|
||||
import { SearchService } from 'src/app/_services/search.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-nav-header',
|
||||
templateUrl: './nav-header.component.html',
|
||||
styleUrls: ['./nav-header.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class NavHeaderComponent implements OnInit, OnDestroy {
|
||||
|
||||
@ViewChild('search') searchViewRef!: any;
|
||||
|
||||
isLoading = false;
|
||||
debounceTime = 300;
|
||||
imageStyles = {width: '24px', 'margin-top': '5px'};
|
||||
searchResults: SearchResultGroup = new SearchResultGroup();
|
||||
searchTerm = '';
|
||||
customFilter: (items: SearchResult[], query: string) => SearchResult[] = (items: SearchResult[], query: string) => {
|
||||
const normalizedQuery = query.trim().toLowerCase();
|
||||
const matches = items.filter(item => {
|
||||
const normalizedSeriesName = item.name.toLowerCase().trim();
|
||||
const normalizedOriginalName = item.originalName.toLowerCase().trim();
|
||||
const normalizedLocalizedName = item.localizedName.toLowerCase().trim();
|
||||
return normalizedSeriesName.indexOf(normalizedQuery) >= 0 || normalizedOriginalName.indexOf(normalizedQuery) >= 0 || normalizedLocalizedName.indexOf(normalizedQuery) >= 0;
|
||||
});
|
||||
return matches;
|
||||
};
|
||||
|
||||
|
||||
backToTopNeeded = false;
|
||||
searchFocused: boolean = false;
|
||||
scrollElem: HTMLElement;
|
||||
private readonly onDestroy = new Subject<void>();
|
||||
|
||||
constructor(public accountService: AccountService, private router: Router, public navService: NavService,
|
||||
public imageService: ImageService, @Inject(DOCUMENT) private document: Document,
|
||||
private scrollService: ScrollService, private searchService: SearchService, private readonly cdRef: ChangeDetectorRef) {
|
||||
this.scrollElem = this.document.body;
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.scrollService.scrollContainer$.pipe(distinctUntilChanged(), takeUntil(this.onDestroy), tap((scrollContainer) => {
|
||||
if (scrollContainer === 'body' || scrollContainer === undefined) {
|
||||
this.scrollElem = this.document.body;
|
||||
fromEvent(this.document.body, 'scroll').pipe(debounceTime(20)).subscribe(() => this.checkBackToTopNeeded(this.document.body));
|
||||
} else {
|
||||
const elem = scrollContainer as ElementRef<HTMLDivElement>;
|
||||
this.scrollElem = elem.nativeElement;
|
||||
fromEvent(elem.nativeElement, 'scroll').pipe(debounceTime(20)).subscribe(() => this.checkBackToTopNeeded(elem.nativeElement));
|
||||
}
|
||||
})).subscribe();
|
||||
|
||||
// Sometimes the top event emitter can be slow, so let's also check when a navigation occurs and recalculate
|
||||
this.router.events
|
||||
.pipe(filter(event => event instanceof NavigationEnd))
|
||||
.subscribe(() => {
|
||||
this.checkBackToTopNeeded(this.scrollElem);
|
||||
});
|
||||
}
|
||||
|
||||
checkBackToTopNeeded(elem: HTMLElement) {
|
||||
const offset = elem.scrollTop || 0;
|
||||
if (offset > 100) {
|
||||
this.backToTopNeeded = true;
|
||||
} else if (offset < 40) {
|
||||
this.backToTopNeeded = false;
|
||||
}
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.onDestroy.next();
|
||||
this.onDestroy.complete();
|
||||
}
|
||||
|
||||
logout() {
|
||||
this.accountService.logout();
|
||||
this.navService.hideNavBar();
|
||||
this.navService.hideSideNav();
|
||||
this.router.navigateByUrl('/login');
|
||||
}
|
||||
|
||||
moveFocus() {
|
||||
this.document.getElementById('content')?.focus();
|
||||
}
|
||||
|
||||
|
||||
|
||||
onChangeSearch(val: string) {
|
||||
this.isLoading = true;
|
||||
this.searchTerm = val.trim();
|
||||
this.cdRef.markForCheck();
|
||||
|
||||
this.searchService.search(val.trim()).pipe(takeUntil(this.onDestroy)).subscribe(results => {
|
||||
this.searchResults = results;
|
||||
this.isLoading = false;
|
||||
this.cdRef.markForCheck();
|
||||
}, err => {
|
||||
this.searchResults.reset();
|
||||
this.isLoading = false;
|
||||
this.searchTerm = '';
|
||||
this.cdRef.markForCheck();
|
||||
});
|
||||
}
|
||||
|
||||
goTo(queryParamName: string, filter: any) {
|
||||
let params: any = {};
|
||||
params[queryParamName] = filter;
|
||||
params[FilterQueryParam.Page] = 1;
|
||||
this.clearSearch();
|
||||
this.router.navigate(['all-series'], {queryParams: params});
|
||||
}
|
||||
|
||||
goToPerson(role: PersonRole, filter: any) {
|
||||
this.clearSearch();
|
||||
switch(role) {
|
||||
case PersonRole.Writer:
|
||||
this.goTo(FilterQueryParam.Writers, filter);
|
||||
break;
|
||||
case PersonRole.Artist:
|
||||
this.goTo(FilterQueryParam.Artists, filter);
|
||||
break;
|
||||
case PersonRole.Character:
|
||||
this.goTo(FilterQueryParam.Character, filter);
|
||||
break;
|
||||
case PersonRole.Colorist:
|
||||
this.goTo(FilterQueryParam.Colorist, filter);
|
||||
break;
|
||||
case PersonRole.Editor:
|
||||
this.goTo(FilterQueryParam.Editor, filter);
|
||||
break;
|
||||
case PersonRole.Inker:
|
||||
this.goTo(FilterQueryParam.Inker, filter);
|
||||
break;
|
||||
case PersonRole.CoverArtist:
|
||||
this.goTo(FilterQueryParam.CoverArtists, filter);
|
||||
break;
|
||||
case PersonRole.Letterer:
|
||||
this.goTo(FilterQueryParam.Letterer, filter);
|
||||
break;
|
||||
case PersonRole.Penciller:
|
||||
this.goTo(FilterQueryParam.Penciller, filter);
|
||||
break;
|
||||
case PersonRole.Publisher:
|
||||
this.goTo(FilterQueryParam.Publisher, filter);
|
||||
break;
|
||||
case PersonRole.Translator:
|
||||
this.goTo(FilterQueryParam.Translator, filter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
clearSearch() {
|
||||
this.searchViewRef.clear();
|
||||
this.searchTerm = '';
|
||||
this.searchResults = new SearchResultGroup();
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
|
||||
clickSeriesSearchResult(item: SearchResult) {
|
||||
this.clearSearch();
|
||||
const libraryId = item.libraryId;
|
||||
const seriesId = item.seriesId;
|
||||
this.router.navigate(['library', libraryId, 'series', seriesId]);
|
||||
}
|
||||
|
||||
clickFileSearchResult(item: MangaFile) {
|
||||
this.clearSearch();
|
||||
this.searchService.getSeriesForMangaFile(item.id).subscribe(series => {
|
||||
if (series !== undefined && series !== null) {
|
||||
this.router.navigate(['library', series.libraryId, 'series', series.id]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
clickChapterSearchResult(item: Chapter) {
|
||||
this.clearSearch();
|
||||
this.searchService.getSeriesForChapter(item.id).subscribe(series => {
|
||||
if (series !== undefined && series !== null) {
|
||||
this.router.navigate(['library', series.libraryId, 'series', series.id]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
clickLibraryResult(item: Library) {
|
||||
this.clearSearch();
|
||||
this.router.navigate(['library', item.id]);
|
||||
}
|
||||
|
||||
clickCollectionSearchResult(item: CollectionTag) {
|
||||
this.clearSearch();
|
||||
this.router.navigate(['collections', item.id]);
|
||||
}
|
||||
|
||||
clickReadingListSearchResult(item: ReadingList) {
|
||||
this.clearSearch();
|
||||
this.router.navigate(['lists', item.id]);
|
||||
}
|
||||
|
||||
|
||||
scrollToTop() {
|
||||
this.scrollService.scrollTo(0, this.scrollElem);
|
||||
}
|
||||
|
||||
focusUpdate(searchFocused: boolean) {
|
||||
this.searchFocused = searchFocused;
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
|
||||
hideSideNav() {
|
||||
this.navService.toggleSideNav();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue