UX Overhaul Part 2 (#3112)
Co-authored-by: Robbie Davis <robbie@therobbiedavis.com>
This commit is contained in:
parent
0247bc5012
commit
3d8aa2ad24
192 changed files with 14808 additions and 1874 deletions
|
|
@ -1,15 +1,15 @@
|
|||
import {
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
EventEmitter, inject,
|
||||
Component, DestroyRef,
|
||||
EventEmitter, HostListener, inject,
|
||||
Input,
|
||||
OnChanges,
|
||||
OnInit,
|
||||
Output
|
||||
} from '@angular/core';
|
||||
import {Router} from '@angular/router';
|
||||
import {NgbModal, NgbOffcanvas} from '@ng-bootstrap/ng-bootstrap';
|
||||
import {Router, RouterLink} from '@angular/router';
|
||||
import {NgbModal, NgbOffcanvas, NgbProgressbar, NgbTooltip} from '@ng-bootstrap/ng-bootstrap';
|
||||
import {ToastrService} from 'ngx-toastr';
|
||||
import {Series} from 'src/app/_models/series';
|
||||
import {ImageService} from 'src/app/_services/image.service';
|
||||
|
|
@ -22,8 +22,23 @@ import {CommonModule} from "@angular/common";
|
|||
import {CardItemComponent} from "../card-item/card-item.component";
|
||||
import {RelationshipPipe} from "../../_pipes/relationship.pipe";
|
||||
import {Device} from "../../_models/device/device";
|
||||
import {translate, TranslocoService} from "@jsverse/transloco";
|
||||
import {translate, TranslocoDirective} from "@jsverse/transloco";
|
||||
import {SeriesPreviewDrawerComponent} from "../../_single-module/series-preview-drawer/series-preview-drawer.component";
|
||||
import {CardActionablesComponent} from "../../_single-module/card-actionables/card-actionables.component";
|
||||
import {DefaultValuePipe} from "../../_pipes/default-value.pipe";
|
||||
import {DownloadIndicatorComponent} from "../download-indicator/download-indicator.component";
|
||||
import {EntityTitleComponent} from "../entity-title/entity-title.component";
|
||||
import {FormsModule} from "@angular/forms";
|
||||
import {ImageComponent} from "../../shared/image/image.component";
|
||||
import {DownloadEvent, DownloadService} from "../../shared/_services/download.service";
|
||||
import {Observable} from "rxjs";
|
||||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||
import {map} from "rxjs/operators";
|
||||
import {AccountService} from "../../_services/account.service";
|
||||
import {BulkSelectionService} from "../bulk-selection.service";
|
||||
import {User} from "../../_models/user";
|
||||
import {ScrollService} from "../../_services/scroll.service";
|
||||
import {ReaderService} from "../../_services/reader.service";
|
||||
|
||||
function deepClone(obj: any): any {
|
||||
if (obj === null || typeof obj !== 'object') {
|
||||
|
|
@ -52,13 +67,30 @@ function deepClone(obj: any): any {
|
|||
@Component({
|
||||
selector: 'app-series-card',
|
||||
standalone: true,
|
||||
imports: [CommonModule, CardItemComponent, RelationshipPipe],
|
||||
imports: [CommonModule, CardItemComponent, RelationshipPipe, CardActionablesComponent, DefaultValuePipe, DownloadIndicatorComponent, EntityTitleComponent, FormsModule, ImageComponent, NgbProgressbar, NgbTooltip, RouterLink, TranslocoDirective],
|
||||
templateUrl: './series-card.component.html',
|
||||
styleUrls: ['./series-card.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class SeriesCardComponent implements OnInit, OnChanges {
|
||||
@Input({required: true}) data!: Series;
|
||||
|
||||
private readonly offcanvasService = inject(NgbOffcanvas);
|
||||
private readonly router = inject(Router);
|
||||
private readonly cdRef = inject(ChangeDetectorRef);
|
||||
private readonly seriesService = inject(SeriesService);
|
||||
private readonly toastr = inject(ToastrService);
|
||||
private readonly modalService = inject(NgbModal);
|
||||
protected readonly imageService = inject(ImageService);
|
||||
private readonly actionFactoryService = inject(ActionFactoryService);
|
||||
private readonly actionService = inject(ActionService);
|
||||
private readonly accountService = inject(AccountService);
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
protected readonly bulkSelectionService = inject(BulkSelectionService);
|
||||
private readonly downloadService = inject(DownloadService);
|
||||
private readonly scrollService = inject(ScrollService);
|
||||
private readonly readerService = inject(ReaderService);
|
||||
|
||||
@Input({required: true}) series!: Series;
|
||||
@Input() libraryId = 0;
|
||||
@Input() suppressLibraryLink = false;
|
||||
/**
|
||||
|
|
@ -93,27 +125,75 @@ export class SeriesCardComponent implements OnInit, OnChanges {
|
|||
*/
|
||||
@Output() selection = new EventEmitter<boolean>();
|
||||
|
||||
count: number = 0;
|
||||
actions: ActionItem<Series>[] = [];
|
||||
imageUrl: string = '';
|
||||
/**
|
||||
* This is the download we get from download service.
|
||||
*/
|
||||
download$: Observable<DownloadEvent | null> | null = null;
|
||||
/**
|
||||
* Handles touch events for selection on mobile devices
|
||||
*/
|
||||
prevTouchTime: number = 0;
|
||||
/**
|
||||
* Handles touch events for selection on mobile devices to ensure you aren't touch scrolling
|
||||
*/
|
||||
prevOffset: number = 0;
|
||||
selectionInProgress: boolean = false;
|
||||
private user: User | undefined;
|
||||
|
||||
private readonly offcanvasService = inject(NgbOffcanvas);
|
||||
|
||||
constructor(private router: Router, private cdRef: ChangeDetectorRef,
|
||||
private seriesService: SeriesService, private toastr: ToastrService,
|
||||
private modalService: NgbModal, private imageService: ImageService,
|
||||
private actionFactoryService: ActionFactoryService,
|
||||
private actionService: ActionService) {}
|
||||
@HostListener('touchmove', ['$event'])
|
||||
onTouchMove(event: TouchEvent) {
|
||||
if (!this.allowSelection) return;
|
||||
|
||||
this.selectionInProgress = false;
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
|
||||
@HostListener('touchstart', ['$event'])
|
||||
onTouchStart(event: TouchEvent) {
|
||||
if (!this.allowSelection) return;
|
||||
|
||||
this.prevTouchTime = event.timeStamp;
|
||||
this.prevOffset = this.scrollService.scrollPosition;
|
||||
this.selectionInProgress = true;
|
||||
}
|
||||
|
||||
@HostListener('touchend', ['$event'])
|
||||
onTouchEnd(event: TouchEvent) {
|
||||
if (!this.allowSelection) return;
|
||||
const delta = event.timeStamp - this.prevTouchTime;
|
||||
const verticalOffset = this.scrollService.scrollPosition;
|
||||
|
||||
if (delta >= 300 && delta <= 1000 && (verticalOffset === this.prevOffset) && this.selectionInProgress) {
|
||||
this.handleSelection();
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
}
|
||||
this.prevTouchTime = 0;
|
||||
this.selectionInProgress = false;
|
||||
}
|
||||
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.data) {
|
||||
this.imageUrl = this.imageService.getSeriesCoverImage(this.data.id);
|
||||
if (this.series) {
|
||||
this.imageUrl = this.imageService.getSeriesCoverImage(this.series.id);
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges(changes: any) {
|
||||
if (this.data) {
|
||||
if (this.series) {
|
||||
this.accountService.currentUser$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(user => {
|
||||
this.user = user;
|
||||
});
|
||||
|
||||
this.download$ = this.downloadService.activeDownloads$.pipe(takeUntilDestroyed(this.destroyRef), map((events) => {
|
||||
return this.downloadService.mapToEntityType(events, this.series);
|
||||
}));
|
||||
|
||||
this.actions = [...this.actionFactoryService.getSeriesActions((action: ActionItem<Series>, series: Series) => this.handleSeriesActionCallback(action, series))];
|
||||
if (this.isOnDeck) {
|
||||
const othersIndex = this.actions.findIndex(obj => obj.title === 'others');
|
||||
|
|
@ -194,7 +274,7 @@ export class SeriesCardComponent implements OnInit, OnChanges {
|
|||
modalRef.closed.subscribe((closeResult: {success: boolean, series: Series, coverImageUpdate: boolean}) => {
|
||||
if (closeResult.success) {
|
||||
this.seriesService.getSeries(data.id).subscribe(series => {
|
||||
this.data = series;
|
||||
this.series = series;
|
||||
this.cdRef.markForCheck();
|
||||
this.reload.emit(series.id);
|
||||
this.dataChanged.emit(series);
|
||||
|
|
@ -223,8 +303,8 @@ export class SeriesCardComponent implements OnInit, OnChanges {
|
|||
|
||||
markAsUnread(series: Series) {
|
||||
this.actionService.markSeriesAsUnread(series, () => {
|
||||
if (this.data) {
|
||||
this.data.pagesRead = 0;
|
||||
if (this.series) {
|
||||
this.series.pagesRead = 0;
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
|
||||
|
|
@ -234,8 +314,8 @@ export class SeriesCardComponent implements OnInit, OnChanges {
|
|||
|
||||
markAsRead(series: Series) {
|
||||
this.actionService.markSeriesAsRead(series, () => {
|
||||
if (this.data) {
|
||||
this.data.pagesRead = series.pages;
|
||||
if (this.series) {
|
||||
this.series.pagesRead = series.pages;
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
this.dataChanged.emit(series);
|
||||
|
|
@ -244,15 +324,34 @@ export class SeriesCardComponent implements OnInit, OnChanges {
|
|||
|
||||
handleClick() {
|
||||
if (this.previewOnClick) {
|
||||
const ref = this.offcanvasService.open(SeriesPreviewDrawerComponent, {position: 'end', panelClass: 'navbar-offset'});
|
||||
const ref = this.offcanvasService.open(SeriesPreviewDrawerComponent, {position: 'end', panelClass: ''});
|
||||
ref.componentInstance.isExternalSeries = false;
|
||||
ref.componentInstance.seriesId = this.data.id;
|
||||
ref.componentInstance.libraryId = this.data.libraryId;
|
||||
ref.componentInstance.name = this.data.name;
|
||||
ref.componentInstance.seriesId = this.series.id;
|
||||
ref.componentInstance.libraryId = this.series.libraryId;
|
||||
ref.componentInstance.name = this.series.name;
|
||||
return;
|
||||
}
|
||||
this.clicked.emit(this.data);
|
||||
this.router.navigate(['library', this.libraryId, 'series', this.data?.id]);
|
||||
this.clicked.emit(this.series);
|
||||
this.router.navigate(['library', this.libraryId, 'series', this.series?.id]);
|
||||
}
|
||||
|
||||
handleSelection(event?: any) {
|
||||
if (event) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
this.selection.emit(this.selected);
|
||||
this.cdRef.detectChanges();
|
||||
}
|
||||
|
||||
read(event: any) {
|
||||
|
||||
event.stopPropagation();
|
||||
if (this.bulkSelectionService.hasSelections()) return;
|
||||
|
||||
// Get Continue Reading point and open directly
|
||||
this.readerService.getCurrentChapter(this.series.id).subscribe(chapter => {
|
||||
this.readerService.readChapter(this.libraryId, this.series.id, chapter, false);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue