Readable Bookmarks (#1228)

* Moved bookmarks to it's own page on side nav and integrated actions.

* Implemented the ability to read bookmarks in the manga reader.

* Removed old bookmark components that aren't needed any longer.

* Removed recently added component as we use all-series instead now

* Removed bookmark tab from card detail

* Fixed scroll to top not working and being missing

* When opening the side nav on mobile with metadata filter already open, collapse the filter.

* When on mobile viewports, when clicking an item from side nav, collapse it afterwards

* Converted most of series detail to use the card detail layout, except storyline which has custom logic

* Fixed unit test
This commit is contained in:
Joseph Milazzo 2022-04-23 13:58:14 -05:00 committed by GitHub
parent 62715a9977
commit 9d6843614d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
48 changed files with 648 additions and 634 deletions

View file

@ -1,29 +0,0 @@
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{title}} Bookmarks</h4>
<button type="button" class="btn-close" aria-label="Close" (click)="close()">
</button>
</div>
<div class="modal-body">
<p *ngIf="bookmarks.length > 0; else noBookmarks">
There are {{bookmarks.length}} pages bookmarked over {{uniqueChapters}} files.
</p>
<ng-template #noBookmarks>No bookmarks yet</ng-template>
<div class="row g-0">
<div *ngFor="let bookmark of bookmarks; let idx = index">
<app-bookmark [bookmark]="bookmark" (bookmarkRemoved)="removeBookmark(bookmark, idx)" class="col-auto"></app-bookmark>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" (click)="clearBookmarks()" [disabled]="(isDownloading || isClearing) || bookmarks.length === 0">
<span *ngIf="isClearing" class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
<span>Clear{{isClearing ? 'ing...' : ''}}</span>
</button>
<button type="button" class="btn btn-secondary" (click)="downloadBookmarks()" [disabled]="(isDownloading || isClearing) || bookmarks.length === 0">
<span *ngIf="isDownloading" class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
<span>Download{{isDownloading ? 'ing...' : ''}}</span>
</button>
<button type="button" class="btn btn-primary" (click)="close()">Close</button>
</div>

View file

@ -1,77 +0,0 @@
import { Component, Input, OnInit } from '@angular/core';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { ToastrService } from 'ngx-toastr';
import { finalize, take, takeWhile } from 'rxjs/operators';
import { DownloadService } from 'src/app/shared/_services/download.service';
import { PageBookmark } from 'src/app/_models/page-bookmark';
import { Series } from 'src/app/_models/series';
import { ImageService } from 'src/app/_services/image.service';
import { ReaderService } from 'src/app/_services/reader.service';
@Component({
selector: 'app-bookmarks-modal',
templateUrl: './bookmarks-modal.component.html',
styleUrls: ['./bookmarks-modal.component.scss']
})
export class BookmarksModalComponent implements OnInit {
@Input() series!: Series;
bookmarks: Array<PageBookmark> = [];
title: string = '';
subtitle: string = '';
isDownloading: boolean = false;
isClearing: boolean = false;
uniqueChapters: number = 0;
constructor(public imageService: ImageService, private readerService: ReaderService,
public modal: NgbActiveModal, private downloadService: DownloadService,
private toastr: ToastrService) { }
ngOnInit(): void {
this.init();
}
init() {
this.readerService.getBookmarksForSeries(this.series.id).pipe(take(1)).subscribe(bookmarks => {
this.bookmarks = bookmarks;
const chapters: {[id: number]: string} = {};
this.bookmarks.forEach(bmk => {
if (!chapters.hasOwnProperty(bmk.chapterId)) {
chapters[bmk.chapterId] = '';
}
});
this.uniqueChapters = Object.keys(chapters).length;
});
}
close() {
this.modal.close();
}
removeBookmark(bookmark: PageBookmark, index: number) {
this.bookmarks.splice(index, 1);
}
downloadBookmarks() {
this.isDownloading = true;
this.downloadService.downloadBookmarks(this.bookmarks).pipe(
takeWhile(val => {
return val.state != 'DONE';
}),
finalize(() => {
this.isDownloading = false;
})).subscribe(() => {/* No Operation */});
}
clearBookmarks() {
this.isClearing = true;
this.readerService.clearBookmarks(this.series.id).subscribe(() => {
this.isClearing = false;
this.init();
this.toastr.success(this.series.name + '\'s bookmarks have been removed');
});
}
}

View file

@ -89,20 +89,6 @@
</ng-template>
</li>
<li [ngbNavItem]="tabs[3]" *ngIf="!tabs[3].disabled">
<a ngbNavLink>{{tabs[3].title}}</a>
<ng-template ngbNavContent>
<div class="row g-0">
<ng-container *ngFor="let bookmark of bookmarks; let idx = index">
<app-bookmark [bookmark]="bookmark" class="col-auto" (bookmarkRemoved)="removeBookmark(bookmark, idx)"></app-bookmark>
</ng-container>
<ng-container *ngIf="bookmarks.length === 0">
No bookmarks yet
</ng-container>
</div>
</ng-template>
</li>
<li [ngbNavItem]="tabs[4]" *ngIf="!tabs[4].disabled">
<a ngbNavLink>{{tabs[4].title}}</a>
<ng-template ngbNavContent>

View file

@ -66,9 +66,8 @@ export class CardDetailsModalComponent implements OnInit {
chapterActions: ActionItem<Chapter>[] = [];
libraryType: LibraryType = LibraryType.Manga;
bookmarks: PageBookmark[] = [];
tabs = [{title: 'General', disabled: false}, {title: 'Metadata', disabled: false}, {title: 'Cover', disabled: false}, {title: 'Bookmarks', disabled: false}, {title: 'Info', disabled: false}];
tabs = [{title: 'General', disabled: false}, {title: 'Metadata', disabled: false}, {title: 'Cover', disabled: false}, {title: 'Info', disabled: false}];
active = this.tabs[0];
chapterMetadata!: ChapterMetadata;
@ -100,17 +99,6 @@ export class CardDetailsModalComponent implements OnInit {
this.imageUrls.push(this.imageService.getChapterCoverImage(this.chapter.id));
let bookmarkApi;
if (this.isChapter) {
bookmarkApi = this.readerService.getBookmarks(this.chapter.id);
} else {
bookmarkApi = this.readerService.getBookmarksForVolume(this.data.id);
}
bookmarkApi.pipe(take(1)).subscribe(bookmarks => {
this.bookmarks = bookmarks;
});
this.seriesService.getChapterMetadata(this.chapter.id).subscribe(metadata => {
this.chapterMetadata = metadata;
@ -240,10 +228,4 @@ export class CardDetailsModalComponent implements OnInit {
this.router.navigate(['library', this.libraryId, 'series', this.seriesId, 'manga', chapter.id]);
}
}
removeBookmark(bookmark: PageBookmark, index: number) {
this.readerService.unbookmark(bookmark.seriesId, bookmark.volumeId, bookmark.chapterId, bookmark.page).subscribe(() => {
this.bookmarks.splice(index, 1);
});
}
}