mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-08-11 09:13:42 -04:00
Incognito Reader (#560)
* Hooked in incognito mode into the manga reader
This commit is contained in:
parent
c728b79616
commit
2eaddbdfac
@ -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>
|
||||
|
@ -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
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -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');
|
||||
|
@ -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 + '¤tChapterId=' + currentChapterId);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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>
|
||||
|
@ -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 */});
|
||||
}
|
||||
|
||||
|
@ -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}});
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user