Bookmarking Pages within the Reader (#469)
# Added - Added: Added the ability to bookmark certain pages within the manga (image) reader and later download them from the series context menu. # Fixed - Fixed: Fixed an issue where after adding a new folder to an existing library, a scan wouldn't be kicked off - Fixed: In some cases, after clicking the background of a modal, the modal would close, but state wouldn't be handled as if cancel was pushed # Changed - Changed: Admin contextual actions on cards will now be separated by a line to help differentiate. - Changed: Performance enhancement on an API used before reading # Dev - Bumped dependencies to latest versions ============================================= * Bumped versions of dependencies and refactored bookmark to progress. * Refactored method names in UI from bookmark to progress to prepare for new bookmark entity * Basic code is done, user can now bookmark a page (currently image reader only). * Comments and pipes * Some accessibility for new bookmark button * Fixed up the APIs to work correctly, added a new modal to quickly explore bookmarks (not implemented, not final). * Cleanup on the UI side to get the modal to look decent * Added dismissed handlers for modals where appropriate * Refactored UI to only show number of bookmarks across files to simplify delivery. Admin actionables are now separated by hr vs non-admin actions. * Basic API implemented, now to implement the ability to actually extract files. * Implemented the ability to download bookmarks. * Fixed a bug where adding a new folder to an existing library would not trigger a scan library task. * Fixed an issue that could cause bookmarked pages to get copied out of order. * Added handler from series-card component
This commit is contained in:
parent
d1d7df9291
commit
e9ec6671d5
49 changed files with 1860 additions and 241 deletions
|
|
@ -7,6 +7,8 @@ import { saveAs } from 'file-saver';
|
|||
import { Chapter } from 'src/app/_models/chapter';
|
||||
import { Volume } from 'src/app/_models/volume';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
import { PageBookmark } from 'src/app/_models/page-bookmark';
|
||||
import { map, take } from 'rxjs/operators';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
|
|
@ -85,6 +87,12 @@ export class DownloadService {
|
|||
});
|
||||
}
|
||||
|
||||
downloadBookmarks(bookmarks: PageBookmark[], seriesName: string) {
|
||||
return this.httpClient.post(this.baseUrl + 'download/bookmarks', {bookmarks}, {observe: 'response', responseType: 'blob' as 'text'}).pipe(take(1), map(resp => {
|
||||
this.preformSave(resp.body || '', this.getFilenameFromHeader(resp.headers, seriesName));
|
||||
}));
|
||||
}
|
||||
|
||||
private preformSave(res: string, filename: string) {
|
||||
const blob = new Blob([res], {type: 'text/plain;charset=utf-8'});
|
||||
saveAs(blob, filename);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@
|
|||
<div ngbDropdown container="body" class="d-inline-block">
|
||||
<button [disabled]="disabled" class="btn {{btnClass}}" id="actions-{{labelBy}}" ngbDropdownToggle (click)="preventClick($event)"><i class="fa {{iconClass}}" aria-hidden="true"></i></button>
|
||||
<div ngbDropdownMenu attr.aria-labelledby="actions-{{labelBy}}">
|
||||
<button ngbDropdownItem *ngFor="let action of actions" (click)="performAction($event, action)">{{action.title}}</button>
|
||||
<button ngbDropdownItem *ngFor="let action of nonAdminActions" (click)="performAction($event, action)">{{action.title}}</button>
|
||||
<div class="dropdown-divider" *ngIf="nonAdminActions.length > 1 && adminActions.length > 1"></div>
|
||||
<button ngbDropdownItem *ngFor="let action of adminActions" (click)="performAction($event, action)">{{action.title}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
|
@ -15,9 +15,15 @@ export class CardActionablesComponent implements OnInit {
|
|||
@Input() disabled: boolean = false;
|
||||
@Output() actionHandler = new EventEmitter<ActionItem<any>>();
|
||||
|
||||
adminActions: ActionItem<any>[] = [];
|
||||
nonAdminActions: ActionItem<any>[] = [];
|
||||
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.nonAdminActions = this.actions.filter(item => !item.requiresAdmin);
|
||||
this.adminActions = this.actions.filter(item => item.requiresAdmin);
|
||||
}
|
||||
|
||||
preventClick(event: any) {
|
||||
|
|
@ -33,4 +39,7 @@ export class CardActionablesComponent implements OnInit {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Insert hr to separate admin actions
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { ConfirmDialogComponent } from './confirm-dialog/confirm-dialog.component';
|
||||
import { ConfirmConfig } from './confirm-dialog/_models/confirm-config';
|
||||
|
||||
|
|
@ -36,9 +37,12 @@ export class ConfirmService {
|
|||
|
||||
const modalRef = this.modalService.open(ConfirmDialogComponent);
|
||||
modalRef.componentInstance.config = config;
|
||||
modalRef.closed.subscribe(result => {
|
||||
modalRef.closed.pipe(take(1)).subscribe(result => {
|
||||
return resolve(result);
|
||||
});
|
||||
modalRef.dismissed.pipe(take(1)).subscribe(() => {
|
||||
return reject(false);
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
|
@ -57,9 +61,12 @@ export class ConfirmService {
|
|||
|
||||
const modalRef = this.modalService.open(ConfirmDialogComponent);
|
||||
modalRef.componentInstance.config = config;
|
||||
modalRef.closed.subscribe(result => {
|
||||
modalRef.closed.pipe(take(1)).subscribe(result => {
|
||||
return resolve(result);
|
||||
});
|
||||
modalRef.dismissed.pipe(take(1)).subscribe(() => {
|
||||
return reject(false);
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue