
# 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
131 lines
5 KiB
TypeScript
131 lines
5 KiB
TypeScript
import { HttpClient } from '@angular/common/http';
|
|
import { Injectable } from '@angular/core';
|
|
import { environment } from 'src/environments/environment';
|
|
import { ChapterInfo } from '../manga-reader/_models/chapter-info';
|
|
import { UtilityService } from '../shared/_services/utility.service';
|
|
import { Chapter } from '../_models/chapter';
|
|
import { PageBookmark } from '../_models/page-bookmark';
|
|
import { ProgressBookmark } from '../_models/progress-bookmark';
|
|
import { Volume } from '../_models/volume';
|
|
|
|
@Injectable({
|
|
providedIn: 'root'
|
|
})
|
|
export class ReaderService {
|
|
|
|
baseUrl = environment.apiUrl;
|
|
|
|
// Override background color for reader and restore it onDestroy
|
|
private originalBodyColor!: string;
|
|
|
|
constructor(private httpClient: HttpClient, private utilityService: UtilityService) { }
|
|
|
|
bookmark(seriesId: number, volumeId: number, chapterId: number, page: number) {
|
|
return this.httpClient.post(this.baseUrl + 'reader/bookmark', {seriesId, volumeId, chapterId, page});
|
|
}
|
|
|
|
unbookmark(seriesId: number, volumeId: number, chapterId: number, page: number) {
|
|
return this.httpClient.post(this.baseUrl + 'reader/unbookmark', {seriesId, volumeId, chapterId, page});
|
|
}
|
|
|
|
getBookmarks(chapterId: number) {
|
|
return this.httpClient.get<PageBookmark[]>(this.baseUrl + 'reader/get-bookmarks?chapterId=' + chapterId);
|
|
}
|
|
|
|
getBookmarksForVolume(volumeId: number) {
|
|
return this.httpClient.get<PageBookmark[]>(this.baseUrl + 'reader/get-volume-bookmarks?volumeId=' + volumeId);
|
|
}
|
|
|
|
getBookmarksForSeries(seriesId: number) {
|
|
return this.httpClient.get<PageBookmark[]>(this.baseUrl + 'reader/get-series-bookmarks?seriesId=' + seriesId);
|
|
}
|
|
|
|
clearBookmarks(seriesId: number) {
|
|
return this.httpClient.post(this.baseUrl + 'reader/remove-bookmarks', {seriesId});
|
|
}
|
|
|
|
getProgress(chapterId: number) {
|
|
return this.httpClient.get<ProgressBookmark>(this.baseUrl + 'reader/get-progress?chapterId=' + chapterId);
|
|
}
|
|
|
|
getPageUrl(chapterId: number, page: number) {
|
|
return this.baseUrl + 'reader/image?chapterId=' + chapterId + '&page=' + page;
|
|
}
|
|
|
|
getChapterInfo(seriesId: number, chapterId: number) {
|
|
return this.httpClient.get<ChapterInfo>(this.baseUrl + 'reader/chapter-info?chapterId=' + chapterId + '&seriesId=' + seriesId);
|
|
}
|
|
|
|
saveProgress(seriesId: number, volumeId: number, chapterId: number, page: number, bookScrollId: string | null = null) {
|
|
return this.httpClient.post(this.baseUrl + 'reader/progress', {seriesId, volumeId, chapterId, pageNum: page, bookScrollId});
|
|
}
|
|
|
|
markVolumeRead(seriesId: number, volumeId: number) {
|
|
return this.httpClient.post(this.baseUrl + 'reader/mark-volume-read', {seriesId, volumeId});
|
|
}
|
|
|
|
getNextChapter(seriesId: number, volumeId: number, currentChapterId: number) {
|
|
return this.httpClient.get<number>(this.baseUrl + 'reader/next-chapter?seriesId=' + seriesId + '&volumeId=' + volumeId + '¤tChapterId=' + currentChapterId);
|
|
}
|
|
|
|
getPrevChapter(seriesId: number, volumeId: number, currentChapterId: number) {
|
|
return this.httpClient.get<number>(this.baseUrl + 'reader/prev-chapter?seriesId=' + seriesId + '&volumeId=' + volumeId + '¤tChapterId=' + currentChapterId);
|
|
}
|
|
|
|
getCurrentChapter(volumes: Array<Volume>): Chapter {
|
|
let currentlyReadingChapter: Chapter | undefined = undefined;
|
|
const chapters = volumes.filter(v => v.number !== 0).map(v => v.chapters || []).flat().sort(this.utilityService.sortChapters); // changed from === 0 to != 0
|
|
|
|
for (const c of chapters) {
|
|
if (c.pagesRead < c.pages) {
|
|
currentlyReadingChapter = c;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (currentlyReadingChapter === undefined) {
|
|
// Check if there are specials we can load:
|
|
const specials = volumes.filter(v => v.number === 0).map(v => v.chapters || []).flat().sort(this.utilityService.sortChapters);
|
|
for (const c of specials) {
|
|
if (c.pagesRead < c.pages) {
|
|
currentlyReadingChapter = c;
|
|
break;
|
|
}
|
|
}
|
|
if (currentlyReadingChapter === undefined) {
|
|
// Default to first chapter
|
|
currentlyReadingChapter = chapters[0];
|
|
}
|
|
}
|
|
|
|
return currentlyReadingChapter;
|
|
}
|
|
|
|
/**
|
|
* Captures current body color and forces background color to be black. Call @see resetOverrideStyles() on destroy of component to revert changes
|
|
*/
|
|
setOverrideStyles() {
|
|
const bodyNode = document.querySelector('body');
|
|
if (bodyNode !== undefined && bodyNode !== null) {
|
|
this.originalBodyColor = bodyNode.style.background;
|
|
bodyNode.setAttribute('style', 'background-color: black !important');
|
|
}
|
|
}
|
|
|
|
resetOverrideStyles() {
|
|
const bodyNode = document.querySelector('body');
|
|
if (bodyNode !== undefined && bodyNode !== null && this.originalBodyColor !== undefined) {
|
|
bodyNode.style.background = this.originalBodyColor;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Parses out the page number from a Image src url
|
|
* @param imageSrc Src attribute of Image
|
|
* @returns
|
|
*/
|
|
imageUrlToPageNum(imageSrc: string) {
|
|
if (imageSrc === undefined || imageSrc === '') { return -1; }
|
|
return parseInt(imageSrc.split('&page=')[1], 10);
|
|
}
|
|
}
|