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:
parent
62715a9977
commit
9d6843614d
48 changed files with 648 additions and 634 deletions
|
|
@ -1,42 +0,0 @@
|
|||
<p *ngIf="series.length === 0 && !loadingBookmarks">
|
||||
There are no bookmarks. Try creating <a href="https://wiki.kavitareader.com/en/guides/get-started-using-your-library/bookmarks" target="_blank">one <i class="fa fa-external-link-alt" aria-hidden="true"></i></a>.
|
||||
</p>
|
||||
<ul class="list-group">
|
||||
<li *ngFor="let series of series" class="list-group-item">
|
||||
<div>
|
||||
<h4>
|
||||
<a id="series--{{series.name}}" href="javascript:void(0);" (click)="viewBookmarks(series)">{{series.name | titlecase}}</a>
|
||||
<span class="badge bg-secondary rounded-pill">{{getBookmarkPages(series.id)}}</span>
|
||||
<div class="float-end">
|
||||
<button attr.aria-labelledby="series--{{series.name}}" class="btn btn-danger me-2 btn-sm" (click)="clearBookmarks(series)" [disabled]="clearingSeries[series.id]" placement="top" ngbTooltip="Clear Bookmarks" attr.aria-label="Clear Bookmarks">
|
||||
<ng-container *ngIf="clearingSeries[series.id]; else notClearing">
|
||||
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
</ng-container>
|
||||
<ng-template #notClearing>
|
||||
<i class="fa fa-trash-alt" aria-hidden="true"></i>
|
||||
</ng-template>
|
||||
</button>
|
||||
<button attr.aria-labelledby="series--{{series.name}}" class="btn btn-secondary me-2 btn-sm" (click)="downloadBookmarks(series)" [disabled]="downloadingSeries[series.id]" placement="top" ngbTooltip="Download Bookmarks" attr.aria-label="Download Bookmarks">
|
||||
<ng-container *ngIf="downloadingSeries[series.id]; else notDownloading">
|
||||
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||
<span class="visually-hidden">Downloading...</span>
|
||||
</ng-container>
|
||||
<ng-template #notDownloading>
|
||||
<i class="fa fa-arrow-alt-circle-down" aria-hidden="true"></i>
|
||||
</ng-template>
|
||||
</button>
|
||||
<button attr.aria-labelledby="series--{{series.name}}" class="btn btn-primary me-2 btn-sm" routerLink="/library/{{series.libraryId}}/series/{{series.id}}" placement="top" ngbTooltip="Open Series" attr.aria-label="Open Series">
|
||||
<i class="fa fa-eye" title="Open Series"></i>
|
||||
</button>
|
||||
</div>
|
||||
</h4>
|
||||
</div>
|
||||
</li>
|
||||
<li *ngIf="loadingBookmarks" class="list-group-item">
|
||||
<div class="spinner-border text-secondary" role="status">
|
||||
<span class="invisible">Loading...</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
import { take, takeWhile, finalize } from 'rxjs/operators';
|
||||
import { BookmarksModalComponent } from 'src/app/cards/_modals/bookmarks-modal/bookmarks-modal.component';
|
||||
import { ConfirmService } from 'src/app/shared/confirm.service';
|
||||
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 { ReaderService } from 'src/app/_services/reader.service';
|
||||
import { SeriesService } from 'src/app/_services/series.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-series-bookmarks',
|
||||
templateUrl: './series-bookmarks.component.html',
|
||||
styleUrls: ['./series-bookmarks.component.scss']
|
||||
})
|
||||
export class SeriesBookmarksComponent implements OnInit {
|
||||
|
||||
bookmarks: Array<PageBookmark> = [];
|
||||
series: Array<Series> = [];
|
||||
loadingBookmarks: boolean = false;
|
||||
seriesIds: {[id: number]: number} = {};
|
||||
downloadingSeries: {[id: number]: boolean} = {};
|
||||
clearingSeries: {[id: number]: boolean} = {};
|
||||
|
||||
constructor(private readerService: ReaderService, private seriesService: SeriesService,
|
||||
private modalService: NgbModal, private downloadService: DownloadService, private toastr: ToastrService,
|
||||
private confirmService: ConfirmService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loadBookmarks();
|
||||
}
|
||||
|
||||
loadBookmarks() {
|
||||
this.loadingBookmarks = true;
|
||||
this.readerService.getAllBookmarks().pipe(take(1)).subscribe(bookmarks => {
|
||||
this.bookmarks = bookmarks;
|
||||
this.seriesIds = {};
|
||||
this.bookmarks.forEach(bmk => {
|
||||
if (!this.seriesIds.hasOwnProperty(bmk.seriesId)) {
|
||||
this.seriesIds[bmk.seriesId] = 1;
|
||||
} else {
|
||||
this.seriesIds[bmk.seriesId] += 1;
|
||||
}
|
||||
this.downloadingSeries[bmk.seriesId] = false;
|
||||
this.clearingSeries[bmk.seriesId] = false;
|
||||
});
|
||||
|
||||
const ids = Object.keys(this.seriesIds).map(k => parseInt(k, 10));
|
||||
this.seriesService.getAllSeriesByIds(ids).subscribe(series => {
|
||||
this.series = series;
|
||||
this.loadingBookmarks = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
viewBookmarks(series: Series) {
|
||||
const bookmarkModalRef = this.modalService.open(BookmarksModalComponent, { scrollable: true, size: 'lg' });
|
||||
bookmarkModalRef.componentInstance.series = series;
|
||||
bookmarkModalRef.closed.pipe(take(1)).subscribe(() => {
|
||||
this.loadBookmarks();
|
||||
});
|
||||
}
|
||||
|
||||
async clearBookmarks(series: Series) {
|
||||
if (!await this.confirmService.confirm('Are you sure you want to clear all bookmarks for ' + series.name + '? This cannot be undone.')) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.clearingSeries[series.id] = true;
|
||||
this.readerService.clearBookmarks(series.id).subscribe(() => {
|
||||
const index = this.series.indexOf(series);
|
||||
if (index > -1) {
|
||||
this.series.splice(index, 1);
|
||||
}
|
||||
this.clearingSeries[series.id] = false;
|
||||
this.toastr.success(series.name + '\'s bookmarks have been removed');
|
||||
});
|
||||
}
|
||||
|
||||
getBookmarkPages(seriesId: number) {
|
||||
return this.seriesIds[seriesId];
|
||||
}
|
||||
|
||||
downloadBookmarks(series: Series) {
|
||||
this.downloadingSeries[series.id] = true;
|
||||
this.downloadService.downloadBookmarks(this.bookmarks.filter(bmk => bmk.seriesId === series.id)).pipe(
|
||||
takeWhile(val => {
|
||||
return val.state != 'DONE';
|
||||
}),
|
||||
finalize(() => {
|
||||
this.downloadingSeries[series.id] = false;
|
||||
})).subscribe(() => {/* No Operation */});
|
||||
}
|
||||
}
|
||||
|
|
@ -192,9 +192,6 @@
|
|||
</ngb-panel>
|
||||
</ngb-accordion>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="tab.fragment === 'bookmarks'">
|
||||
<app-series-bookmarks></app-series-bookmarks>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="tab.fragment === 'password'">
|
||||
<ng-container *ngIf="(isAdmin || hasChangePasswordRole); else noPermission">
|
||||
<p>Change your Password</p>
|
||||
|
|
|
|||
|
|
@ -56,7 +56,6 @@ export class UserPreferencesComponent implements OnInit, OnDestroy {
|
|||
|
||||
tabs: Array<{title: string, fragment: string}> = [
|
||||
{title: 'Preferences', fragment: ''},
|
||||
{title: 'Bookmarks', fragment: 'bookmarks'},
|
||||
{title: 'Password', fragment: 'password'},
|
||||
{title: '3rd Party Clients', fragment: 'clients'},
|
||||
{title: 'Theme', fragment: 'theme'},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { SeriesBookmarksComponent } from './series-bookmarks/series-bookmarks.component';
|
||||
import { UserPreferencesComponent } from './user-preferences/user-preferences.component';
|
||||
import { NgbAccordionModule, NgbNavModule, NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
|
|
@ -17,7 +16,6 @@ import { ColorPickerModule } from 'ngx-color-picker';
|
|||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
SeriesBookmarksComponent,
|
||||
UserPreferencesComponent,
|
||||
ApiKeyComponent,
|
||||
ThemeManagerComponent,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue