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
|
|
@ -12,6 +12,9 @@
|
|||
{{subtitle}}
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-left: auto; padding-right: 3%;">
|
||||
<button class="btn btn-icon btn-small" role="checkbox" [attr.aria-checked]="pageBookmarked" title="{{pageBookmarked ? 'Unbookmark Page' : 'Bookmark Page'}}" (click)="bookmarkPage()"><i class="{{pageBookmarked ? 'fa' : 'far'}} fa-bookmark" aria-hidden="true"></i><span class="sr-only">{{pageBookmarked ? 'Unbookmark Page' : 'Bookmark Page'}}</span></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ng-container *ngIf="isLoading">
|
||||
|
|
|
|||
|
|
@ -183,6 +183,10 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
* If extended settings area is visible. Blocks auto-closing of menu.
|
||||
*/
|
||||
settingsOpen: boolean = false;
|
||||
/**
|
||||
* A map of bookmarked pages to anything. Used for O(1) lookup time if a page is bookmarked or not.
|
||||
*/
|
||||
bookmarks: {[key: string]: number} = {};
|
||||
|
||||
private readonly onDestroy = new Subject<void>();
|
||||
|
||||
|
|
@ -191,6 +195,9 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
|
||||
|
||||
|
||||
get pageBookmarked() {
|
||||
return this.bookmarks.hasOwnProperty(this.pageNum);
|
||||
}
|
||||
|
||||
|
||||
get splitIconClass() {
|
||||
|
|
@ -348,13 +355,14 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
this.pageNum = 1;
|
||||
|
||||
forkJoin({
|
||||
bookmark: this.readerService.getBookmark(this.chapterId),
|
||||
chapterInfo: this.readerService.getChapterInfo(this.chapterId)
|
||||
progress: this.readerService.getProgress(this.chapterId),
|
||||
chapterInfo: this.readerService.getChapterInfo(this.seriesId, this.chapterId),
|
||||
bookmarks: this.readerService.getBookmarks(this.chapterId)
|
||||
}).pipe(take(1)).subscribe(results => {
|
||||
this.volumeId = results.chapterInfo.volumeId;
|
||||
this.maxPages = results.chapterInfo.pages;
|
||||
|
||||
let page = results.bookmark.pageNum;
|
||||
let page = results.progress.pageNum;
|
||||
if (page >= this.maxPages) {
|
||||
page = this.maxPages - 1;
|
||||
}
|
||||
|
|
@ -367,6 +375,12 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
|
||||
this.updateTitle(results.chapterInfo);
|
||||
|
||||
// From bookmarks, create map of pages to make lookup time O(1)
|
||||
this.bookmarks = {};
|
||||
results.bookmarks.forEach(bookmark => {
|
||||
this.bookmarks[bookmark.page] = 1;
|
||||
});
|
||||
|
||||
this.readerService.getNextChapter(this.seriesId, this.volumeId, this.chapterId).pipe(take(1)).subscribe(chapterId => {
|
||||
this.nextChapterId = chapterId;
|
||||
if (chapterId === CHAPTER_ID_DOESNT_EXIST || chapterId === this.chapterId) {
|
||||
|
|
@ -747,14 +761,14 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
loadPage() {
|
||||
if (!this.canvas || !this.ctx) { return; }
|
||||
|
||||
// Due to the fact that we start at image 0, but page 1, we need the last page to be bookmarked as page + 1 to be completed
|
||||
// Due to the fact that we start at image 0, but page 1, we need the last page to have progress as page + 1 to be completed
|
||||
let pageNum = this.pageNum;
|
||||
if (this.pageNum == this.maxPages - 1) {
|
||||
pageNum = this.pageNum + 1;
|
||||
}
|
||||
|
||||
|
||||
this.readerService.bookmark(this.seriesId, this.volumeId, this.chapterId, pageNum).pipe(take(1)).subscribe(() => {/* No operation */});
|
||||
this.readerService.saveProgress(this.seriesId, this.volumeId, this.chapterId, pageNum).pipe(take(1)).subscribe(() => {/* No operation */});
|
||||
|
||||
this.isLoading = true;
|
||||
this.canvasImage = this.cachedImages.current();
|
||||
|
|
@ -814,13 +828,13 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
if (this.pageNum >= this.maxPages - 10) {
|
||||
// Tell server to cache the next chapter
|
||||
if (this.nextChapterId > 0 && !this.nextChapterPrefetched) {
|
||||
this.readerService.getChapterInfo(this.nextChapterId).pipe(take(1)).subscribe(res => {
|
||||
this.readerService.getChapterInfo(this.seriesId, this.nextChapterId).pipe(take(1)).subscribe(res => {
|
||||
this.nextChapterPrefetched = true;
|
||||
});
|
||||
}
|
||||
} else if (this.pageNum <= 10) {
|
||||
if (this.prevChapterId > 0 && !this.prevChapterPrefetched) {
|
||||
this.readerService.getChapterInfo(this.prevChapterId).pipe(take(1)).subscribe(res => {
|
||||
this.readerService.getChapterInfo(this.seriesId, this.prevChapterId).pipe(take(1)).subscribe(res => {
|
||||
this.prevChapterPrefetched = true;
|
||||
});
|
||||
}
|
||||
|
|
@ -905,7 +919,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
|
||||
handleWebtoonPageChange(updatedPageNum: number) {
|
||||
this.setPageNum(updatedPageNum);
|
||||
this.readerService.bookmark(this.seriesId, this.volumeId, this.chapterId, this.pageNum).pipe(take(1)).subscribe(() => {/* No operation */});
|
||||
this.readerService.saveProgress(this.seriesId, this.volumeId, this.chapterId, this.pageNum).pipe(take(1)).subscribe(() => {/* No operation */});
|
||||
}
|
||||
|
||||
saveSettings() {
|
||||
|
|
@ -945,4 +959,22 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
|
||||
this.updateForm();
|
||||
}
|
||||
|
||||
/**
|
||||
* Bookmarks the current page for the chapter
|
||||
*/
|
||||
bookmarkPage() {
|
||||
const pageNum = this.pageNum;
|
||||
if (this.pageBookmarked) {
|
||||
// Remove bookmark
|
||||
this.readerService.unbookmark(this.seriesId, this.volumeId, this.chapterId, pageNum).pipe(take(1)).subscribe(() => {
|
||||
delete this.bookmarks[pageNum];
|
||||
});
|
||||
} else {
|
||||
this.readerService.bookmark(this.seriesId, this.volumeId, this.chapterId, pageNum).pipe(take(1)).subscribe(() => {
|
||||
this.bookmarks[pageNum] = 1;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue