Incognito Reader (#560)

* Hooked in incognito mode into the manga reader
This commit is contained in:
Joseph Milazzo 2021-09-07 05:45:17 -07:00 committed by GitHub
parent c728b79616
commit 2eaddbdfac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 103 additions and 13 deletions

View File

@ -172,7 +172,7 @@ namespace API.Controllers
}
/// <summary>
/// Marks a Chapter as Unread (progress)
/// Marks a Series as Unread (progress)
/// </summary>
/// <param name="markReadDto"></param>
/// <returns></returns>
@ -206,6 +206,50 @@ namespace API.Controllers
return BadRequest("There was an issue saving progress");
}
/// <summary>
/// Marks all chapters within a volume as unread
/// </summary>
/// <param name="markVolumeReadDto"></param>
/// <returns></returns>
[HttpPost("mark-volume-unread")]
public async Task<ActionResult> MarkVolumeAsUnread(MarkVolumeReadDto markVolumeReadDto)
{
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername());
var chapters = await _unitOfWork.VolumeRepository.GetChaptersAsync(markVolumeReadDto.VolumeId);
foreach (var chapter in chapters)
{
user.Progresses ??= new List<AppUserProgress>();
var userProgress = user.Progresses.FirstOrDefault(x => x.ChapterId == chapter.Id && x.AppUserId == user.Id);
if (userProgress == null)
{
user.Progresses.Add(new AppUserProgress
{
PagesRead = 0,
VolumeId = markVolumeReadDto.VolumeId,
SeriesId = markVolumeReadDto.SeriesId,
ChapterId = chapter.Id
});
}
else
{
userProgress.PagesRead = 0;
userProgress.SeriesId = markVolumeReadDto.SeriesId;
userProgress.VolumeId = markVolumeReadDto.VolumeId;
}
}
_unitOfWork.UserRepository.Update(user);
if (await _unitOfWork.CommitAsync())
{
return Ok();
}
return BadRequest("Could not save progress");
}
/// <summary>
/// Marks all chapters within a volume as Read
/// </summary>

View File

@ -16,7 +16,8 @@ export enum Action {
Info = 5,
RefreshMetadata = 6,
Download = 7,
Bookmarks = 8
Bookmarks = 8,
IncognitoRead = 9
}
export interface ActionItem<T> {
@ -203,6 +204,12 @@ export class ActionFactoryService {
callback: this.dummyCallback,
requiresAdmin: false
},
{
action: Action.IncognitoRead,
title: 'Read in Incognito',
callback: this.dummyCallback,
requiresAdmin: false
},
{
action: Action.Edit,
title: 'Info',
@ -223,7 +230,13 @@ export class ActionFactoryService {
title: 'Mark as Unread',
callback: this.dummyCallback,
requiresAdmin: false
}
},
{
action: Action.IncognitoRead,
title: 'Read in Incognito',
callback: this.dummyCallback,
requiresAdmin: false
},
];
}
}

View File

@ -156,7 +156,7 @@ export class ActionService implements OnDestroy {
* @param callback Optional callback to perform actions after API completes
*/
markVolumeAsUnread(seriesId: number, volume: Volume, callback?: VolumeActionCallback) {
this.readerService.markVolumeRead(seriesId, volume.id).subscribe(() => {
this.readerService.markVolumeUnread(seriesId, volume.id).subscribe(() => {
volume.pagesRead = 0;
volume.chapters?.forEach(c => c.pagesRead = 0);
this.toastr.success('Marked as Unread');

View File

@ -1,5 +1,6 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { environment } from 'src/environments/environment';
import { ChapterInfo } from '../manga-reader/_models/chapter-info';
import { UtilityService } from '../shared/_services/utility.service';
@ -68,6 +69,10 @@ export class ReaderService {
return this.httpClient.post(this.baseUrl + 'reader/mark-volume-read', {seriesId, volumeId});
}
markVolumeUnread(seriesId: number, volumeId: number) {
return this.httpClient.post(this.baseUrl + 'reader/mark-volume-unread', {seriesId, volumeId});
}
getNextChapter(seriesId: number, volumeId: number, currentChapterId: number) {
return this.httpClient.get<number>(this.baseUrl + 'reader/next-chapter?seriesId=' + seriesId + '&volumeId=' + volumeId + '&currentChapterId=' + currentChapterId);
}

View File

@ -64,6 +64,10 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
volumeId!: number;
chapterId!: number;
chapter!: Chapter;
/**
* If we should save progress or not
*/
incognitoMode: boolean = false;
chapters: Array<BookChapterItem> = [];
@ -268,6 +272,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
this.libraryId = parseInt(libraryId, 10);
this.seriesId = parseInt(seriesId, 10);
this.chapterId = parseInt(chapterId, 10);
this.incognitoMode = this.route.snapshot.queryParamMap.get('incognitoMode') === 'true';
this.memberService.hasReadingProgress(this.libraryId).pipe(take(1)).subscribe(hasProgress => {
if (!hasProgress) {
@ -292,7 +297,9 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
if (this.pageNum >= this.maxPages) {
this.pageNum = this.maxPages - 1;
this.readerService.saveProgress(this.seriesId, this.volumeId, this.chapterId, this.pageNum).pipe(take(1)).subscribe(() => {/* No operation */});
if (!this.incognitoMode) {
this.readerService.saveProgress(this.seriesId, this.volumeId, this.chapterId, this.pageNum).pipe(take(1)).subscribe(() => {/* No operation */});
}
}
// Check if user progress has part, if so load it so we scroll to it
@ -483,7 +490,9 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
loadPage(part?: string | undefined, scrollTop?: number | undefined) {
this.isLoading = true;
this.readerService.saveProgress(this.seriesId, this.volumeId, this.chapterId, this.pageNum).pipe(take(1)).subscribe(() => {/* No operation */});
if (!this.incognitoMode) {
this.readerService.saveProgress(this.seriesId, this.volumeId, this.chapterId, this.pageNum).pipe(take(1)).subscribe(() => {/* No operation */});
}
this.bookService.getBookPage(this.chapterId, this.pageNum).pipe(take(1)).subscribe(content => {
this.page = this.domSanitizer.bypassSecurityTrustHtml(content);

View File

@ -7,7 +7,7 @@
</button>
<div>
<div style="font-weight: bold;">{{title}}</div>
<div style="font-weight: bold;">{{title}} <span *ngIf="incognitoMode">(<i class="fa fa-glasses" aria-hidden="true"></i><span class="sr-only">Incognito Mode</span>)</span></div>
<div class="subtitle">
{{subtitle}}
</div>

View File

@ -66,6 +66,11 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
volumeId!: number;
chapterId!: number;
/**
* If this is true, no progress will be saved.
*/
incognitoMode: boolean = false;
/**
* The current page. UI will show this number + 1.
*/
@ -259,6 +264,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
this.libraryId = parseInt(libraryId, 10);
this.seriesId = parseInt(seriesId, 10);
this.chapterId = parseInt(chapterId, 10);
this.incognitoMode = this.route.snapshot.queryParamMap.get('incognitoMode') === 'true';
this.continuousChaptersStack.push(this.chapterId);
@ -706,7 +712,10 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
this.continuousChaptersStack.push(chapterId);
// Load chapter Id onto route but don't reload
const lastSlashIndex = this.router.url.lastIndexOf('/');
const newRoute = this.router.url.substring(0, lastSlashIndex + 1) + this.chapterId + '';
let newRoute = this.router.url.substring(0, lastSlashIndex + 1) + this.chapterId + '';
if (this.incognitoMode) {
newRoute += '?incognitoMode=true';
}
window.history.replaceState({}, '', newRoute);
this.init();
} else {
@ -777,8 +786,9 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
pageNum = this.pageNum + 1;
}
this.readerService.saveProgress(this.seriesId, this.volumeId, this.chapterId, pageNum).pipe(take(1)).subscribe(() => {/* No operation */});
if (!this.incognitoMode) {
this.readerService.saveProgress(this.seriesId, this.volumeId, this.chapterId, pageNum).pipe(take(1)).subscribe(() => {/* No operation */});
}
this.isLoading = true;
this.canvasImage = this.cachedImages.current();
@ -929,6 +939,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
handleWebtoonPageChange(updatedPageNum: number) {
this.setPageNum(updatedPageNum);
if (this.incognitoMode) return;
this.readerService.saveProgress(this.seriesId, this.volumeId, this.chapterId, this.pageNum).pipe(take(1)).subscribe(() => {/* No operation */});
}

View File

@ -175,6 +175,11 @@ export class SeriesDetailComponent implements OnInit {
case(Action.Edit):
this.openViewInfo(volume);
break;
case(Action.IncognitoRead):
if (volume.chapters != undefined && volume.chapters?.length >= 1) {
this.openChapter(volume.chapters[0], true);
}
break;
default:
break;
}
@ -191,6 +196,9 @@ export class SeriesDetailComponent implements OnInit {
case(Action.Edit):
this.openViewInfo(chapter);
break;
case(Action.IncognitoRead):
this.openChapter(chapter, true);
break;
default:
break;
}
@ -348,16 +356,16 @@ export class SeriesDetailComponent implements OnInit {
});
}
openChapter(chapter: Chapter) {
openChapter(chapter: Chapter, incognitoMode = false) {
if (chapter.pages === 0) {
this.toastr.error('There are no pages. Kavita was not able to read this archive.');
return;
}
if (chapter.files.length > 0 && chapter.files[0].format === MangaFormat.EPUB) {
this.router.navigate(['library', this.libraryId, 'series', this.series?.id, 'book', chapter.id]);
this.router.navigate(['library', this.libraryId, 'series', this.series?.id, 'book', chapter.id], {queryParams: {incognitoMode}});
} else {
this.router.navigate(['library', this.libraryId, 'series', this.series?.id, 'manga', chapter.id]);
this.router.navigate(['library', this.libraryId, 'series', this.series?.id, 'manga', chapter.id], {queryParams: {incognitoMode}});
}
}