-
-
diff --git a/UI/Web/src/app/book-reader/book-reader/book-reader.component.scss b/UI/Web/src/app/book-reader/book-reader/book-reader.component.scss
index 568ce5210..be2b9c444 100644
--- a/UI/Web/src/app/book-reader/book-reader/book-reader.component.scss
+++ b/UI/Web/src/app/book-reader/book-reader/book-reader.component.scss
@@ -130,6 +130,7 @@ $action-bar-height: 38px;
overflow: auto;
height: calc(var(--vh, 1vh) * 100);
position: relative;
+ // This is completely invisible, everything else renders over it
&.column-layout-1 {
height: calc(var(--vh) * 100);
@@ -145,8 +146,11 @@ $action-bar-height: 38px;
//overflow: auto; // This will break progress reporting
height: 100vh;
padding-top: $action-bar-height;
+ padding-bottom: $action-bar-height;
position: relative;
+ //background-color: green !important;
+
&.column-layout-1 {
height: calc((var(--vh, 1vh) * 100) - $action-bar-height);
}
@@ -154,12 +158,20 @@ $action-bar-height: 38px;
&.column-layout-2 {
height: calc((var(--vh, 1vh) * 100) - $action-bar-height);
}
+
+ &.immersive {
+ height: calc((var(--vh, 1vh) * 100));
+ //padding-top: 0px;
+ //padding-bottom: 0px;
+ }
}
.book-container {
position: relative;
height: 100%;
+ //background-color: purple !important;
+
&.column-layout-1 {
height: calc((var(--vh, 1vh) * 100) - $action-bar-height);
}
@@ -173,13 +185,18 @@ $action-bar-height: 38px;
position: relative;
padding: 20px 0;
margin: 0px 0px;
+ //background-color: red !important;
&.column-layout-1 {
- height: calc((var(--vh) * 100) - calc($action-bar-height * 2));
+ height: calc((var(--vh) * 100) - calc($action-bar-height)); // * 2
}
&.column-layout-2 {
- height: calc((var(--vh) * 100) - calc($action-bar-height * 2));
+ height: calc((var(--vh) * 100) - calc($action-bar-height)); // * 2
+ }
+
+ &.immersive {
+ height: calc((var(--vh, 1vh) * 100) - $action-bar-height);
}
a, :link {
@@ -203,6 +220,12 @@ $action-bar-height: 38px;
box-shadow: var(--drawer-pagination-horizontal-rule);
}
+.bottom-bar {
+ position: fixed;
+ width: 100%;
+ bottom: 0px;
+}
+
// This is essentially fitting the text to height and when you press next you are scrolling over by page width
@@ -213,10 +236,6 @@ $action-bar-height: 38px;
overflow: hidden;
word-break: break-word;
overflow-wrap: break-word;
-
- &.debug {
- column-rule: 20px solid rebeccapurple;
- }
}
@@ -229,10 +248,6 @@ $action-bar-height: 38px;
overflow: hidden;
word-break: break-word;
overflow-wrap: break-word;
-
- &.debug {
- column-rule: 20px solid rebeccapurple;
- }
}
@@ -249,7 +264,8 @@ $action-bar-height: 38px;
// This is applied to images in the backend
::ng-deep .kavita-scale-width-container {
width: auto;
- max-height: calc((var(--vh)*100) - 116px) !important;
+ // * 4 is just for extra buffer which is needed based on testing. --book-reader-content-max-height is set by us on calculation of columnHeight
+ max-height: calc(var(--book-reader-content-max-height) - ($action-bar-height * 4)), calc((var(--vh)*100) - ($action-bar-height * 4)) !important;
}
// This is applied to images in the backend
@@ -272,13 +288,21 @@ $action-bar-height: 38px;
.right {
position: absolute;
- right: 0px; // with scrollbar: 17px
+ right: 0px;
top: $action-bar-height;
- width: 20%; // with scrollbar: 18%
-
- z-index: 2;
+ width: 20%;
+ z-index: 3;
cursor: pointer;
background: transparent;
+ border-color: transparent;
+ border: none !important;
+ opacity: 0;
+ outline: none;
+ //background-color: aqua;
+
+ &.immersive {
+ top: 0px;
+ }
}
// This class pushes the click area to the left a bit to let users click the scrollbar
@@ -287,9 +311,18 @@ $action-bar-height: 38px;
right: 17px;
top: $action-bar-height;
width: 18%;
- z-index: 2;
+ z-index: 3;
cursor: pointer;
background: transparent;
+ border-color: transparent;
+ border: none !important;
+ opacity: 0;
+ outline: none;
+ //background-color: aqua;
+
+ &.immersive {
+ top: 0px;
+ }
}
.left {
@@ -298,16 +331,24 @@ $action-bar-height: 38px;
top: $action-bar-height;
width: 20%;
background: transparent;
-
- z-index: 2;
+ border-color: transparent;
+ border: none !important;
+ z-index: 3;
cursor: pointer;
+ opacity: 0;
+ outline: none;
+ //background-color: aqua;
+
+ &.immersive {
+ top: 0px;
+ }
}
.highlight {
- background-color: rgba(65, 225, 100, 0.5) !important;
- animation: fadein .5s both;
+ background-color: rgba(65, 225, 100, 0.5) !important;
+ animation: fadein .5s both;
}
.highlight-2 {
background-color: rgba(65, 105, 225, 0.5) !important;
@@ -333,13 +374,13 @@ $action-bar-height: 38px;
background-color: unset;
&:hover, &:focus {
- border-color: var(--br-actionbar-button-hover-border-color); // #545b62;
+ border-color: var(--br-actionbar-button-hover-border-color);
}
}
span {
background-color: unset;
- color: var(--br-actionbar-button-text-color); // #6c757d;
+ color: var(--br-actionbar-button-text-color);
}
i {
diff --git a/UI/Web/src/app/book-reader/book-reader/book-reader.component.ts b/UI/Web/src/app/book-reader/book-reader/book-reader.component.ts
index ca438e477..c38fbcabb 100644
--- a/UI/Web/src/app/book-reader/book-reader/book-reader.component.ts
+++ b/UI/Web/src/app/book-reader/book-reader/book-reader.component.ts
@@ -352,7 +352,9 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
get ColumnHeight() {
if (this.layoutMode !== BookPageLayoutMode.Default) {
// Take the height after page loads, subtract the top/bottom bar
- return this.windowHeight - (this.topOffset *2) + 'px';
+ const height = this.windowHeight - (this.topOffset * 2);
+ this.document.documentElement.style.setProperty('--book-reader-content-max-height', `${height}px`);
+ return height + 'px';
}
return 'unset';
}
@@ -370,10 +372,11 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
get PageHeightForPagination() {
if (this.layoutMode === BookPageLayoutMode.Default) {
- return (this.readingSectionElemRef?.nativeElement?.scrollHeight || 0) - (this.topOffset * 2) + 'px';
+ return (this.readingSectionElemRef?.nativeElement?.scrollHeight || 0) - ((this.topOffset * (this.immersiveMode ? 0 : 1)) * 2) + 'px';
}
- return this.ColumnHeight;
+ if (this.immersiveMode) return this.windowHeight + 'px';
+ return (this.windowHeight) - (this.topOffset * 2) + 'px';
}
@@ -808,7 +811,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
const images = this.readingSectionElemRef?.nativeElement.querySelectorAll('img') || [];
if (this.layoutMode !== BookPageLayoutMode.Default) {
- const height = this.ColumnHeight;
+ const height = (parseInt(this.ColumnHeight.replace('px', ''), 10) - (this.topOffset * 2)) + 'px';
Array.from(images).forEach(img => {
this.renderer.setStyle(img, 'max-height', height);
});
@@ -1209,6 +1212,12 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
return;
}
setTimeout(() => {this.scrollbarNeeded = this.readingHtml.nativeElement.clientHeight > this.reader.nativeElement.clientHeight;});
+
+ // When I switch layout, I might need to resume the progress point.
+ if (mode === BookPageLayoutMode.Default) {
+ const lastSelector = this.lastSeenScrollPartPath;
+ setTimeout(() => this.scrollTo(lastSelector));
+ }
}
updateReadingDirection(readingDirection: ReadingDirection) {
@@ -1220,6 +1229,19 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
if (this.immersiveMode && !this.drawerOpen) {
this.actionBarVisible = false;
}
+
+ this.updateReadingSectionHeight();
+ }
+
+ updateReadingSectionHeight() {
+ setTimeout(() => {
+ console.log('setting height on ', this.readingSectionElemRef)
+ if (this.immersiveMode) {
+ this.renderer.setStyle(this.readingSectionElemRef, 'height', 'calc(var(--vh, 1vh) * 100)', RendererStyleFlags2.Important);
+ } else {
+ this.renderer.setStyle(this.readingSectionElemRef, 'height', 'calc(var(--vh, 1vh) * 100 - ' + this.topOffset + 'px)', RendererStyleFlags2.Important);
+ }
+ });
}
// Table of Contents
diff --git a/UI/Web/src/app/cards/card-detail-drawer/card-detail-drawer.component.html b/UI/Web/src/app/cards/card-detail-drawer/card-detail-drawer.component.html
index 819a09a3e..18b32fef3 100644
--- a/UI/Web/src/app/cards/card-detail-drawer/card-detail-drawer.component.html
+++ b/UI/Web/src/app/cards/card-detail-drawer/card-detail-drawer.component.html
@@ -43,24 +43,29 @@
-
-
+
+
+
+
+
+ No Summary available.
+
-
+ 0">
-
- {{chapter.pages | number:''}} Pages
+
+ {{totalPages | number:''}} Pages
-
+
-
+
{{chapterMetadata.releaseDate | date:'shortDate'}}
@@ -69,7 +74,7 @@
0 || chapter.files[0].format !== MangaFormat.EPUB">
-
+
{{readingTime.minHours}}{{readingTime.maxHours !== readingTime.minHours ? ('-' + readingTime.maxHours) : ''}} Hour{{readingTime.minHours > 1 ? 's' : ''}}
@@ -78,7 +83,7 @@
0">
-
+
{{chapterMetadata.wordCount | compactNumber}} Words
@@ -88,12 +93,30 @@
+
+
+
+
+
+ {{chapter.created | date:'short' || '-'}}
+
+
+
+
+
+
+
+
@@ -182,24 +205,6 @@
{{tabs[TabID.Files].title}}
-
-
-
-
- Created: {{chapter.created | date:'short' || '-'}}
-
-
-
-
-
-
-
-
-
{{utilityService.formatChapterName(libraryType) + 's'}}
-
diff --git a/UI/Web/src/app/cards/card-detail-drawer/card-detail-drawer.component.scss b/UI/Web/src/app/cards/card-detail-drawer/card-detail-drawer.component.scss
index 2db0d16b3..39570d23f 100644
--- a/UI/Web/src/app/cards/card-detail-drawer/card-detail-drawer.component.scss
+++ b/UI/Web/src/app/cards/card-detail-drawer/card-detail-drawer.component.scss
@@ -12,5 +12,5 @@
.tab-content {
overflow: auto;
- height: calc(45vh - 63px); // drawer height - offcanvas heading height
+ height: calc(40vh - 63px); // drawer height - offcanvas heading height
}
diff --git a/UI/Web/src/app/cards/card-detail-drawer/card-detail-drawer.component.ts b/UI/Web/src/app/cards/card-detail-drawer/card-detail-drawer.component.ts
index ba43deba8..ecb9a38b7 100644
--- a/UI/Web/src/app/cards/card-detail-drawer/card-detail-drawer.component.ts
+++ b/UI/Web/src/app/cards/card-detail-drawer/card-detail-drawer.component.ts
@@ -2,7 +2,9 @@ import { Component, Input, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { NgbActiveOffcanvas } from '@ng-bootstrap/ng-bootstrap';
import { ToastrService } from 'ngx-toastr';
-import { Observable, of, take } from 'rxjs';
+import { finalize, Observable, of, take, takeWhile, tap } from 'rxjs';
+import { Download } from 'src/app/shared/_models/download';
+import { DownloadService } from 'src/app/shared/_services/download.service';
import { Breakpoint, UtilityService } from 'src/app/shared/_services/utility.service';
import { Chapter } from 'src/app/_models/chapter';
import { ChapterMetadata } from 'src/app/_models/chapter-metadata';
@@ -18,7 +20,7 @@ import { ActionService } from 'src/app/_services/action.service';
import { ImageService } from 'src/app/_services/image.service';
import { LibraryService } from 'src/app/_services/library.service';
import { MetadataService } from 'src/app/_services/metadata.service';
-import { MAX_PAGES_PER_MINUTE, MAX_WORDS_PER_HOUR, MIN_PAGES_PER_MINUTE, MIN_WORDS_PER_HOUR, ReaderService } from 'src/app/_services/reader.service';
+import { ReaderService } from 'src/app/_services/reader.service';
import { SeriesService } from 'src/app/_services/series.service';
import { UploadService } from 'src/app/_services/upload.service';
@@ -81,6 +83,13 @@ export class CardDetailDrawerComponent implements OnInit {
readingTime: HourEstimateRange = {maxHours: 1, minHours: 1, avgHours: 1, hasProgress: false};
minHoursToRead: number = 1;
maxHoursToRead: number = 1;
+ /**
+ * We use a separate variable because if this is a volume, we need a sum of all chapters
+ */
+ totalPages: number = 0;
+
+ download$: Observable | null = null;
+ downloadInProgress: boolean = false;
@@ -109,7 +118,7 @@ export class CardDetailDrawerComponent implements OnInit {
private accountService: AccountService, private actionFactoryService: ActionFactoryService,
private actionService: ActionService, private router: Router, private libraryService: LibraryService,
private seriesService: SeriesService, private readerService: ReaderService, public metadataService: MetadataService,
- public activeOffcanvas: NgbActiveOffcanvas) { }
+ public activeOffcanvas: NgbActiveOffcanvas, private downloadService: DownloadService) { }
ngOnInit(): void {
this.isChapter = this.utilityService.isChapter(this.data);
@@ -123,13 +132,13 @@ export class CardDetailDrawerComponent implements OnInit {
this.metadataService.getAgeRating(this.chapterMetadata.ageRating).subscribe(ageRating => this.ageRating = ageRating);
- let totalPages = this.chapter.pages;
+ this.totalPages = this.chapter.pages;
if (!this.isChapter) {
// Need to account for multiple chapters if this is a volume
- totalPages = this.utilityService.asVolume(this.data).chapters.map(c => c.pages).reduce((sum, d) => sum + d);
+ this.totalPages = this.utilityService.asVolume(this.data).chapters.map(c => c.pages).reduce((sum, d) => sum + d);
}
- this.readerService.getManualTimeToRead(this.chapterMetadata.wordCount, totalPages, this.chapter.files[0].format === MangaFormat.EPUB).subscribe((time) => this.readingTime = time);
+ this.readerService.getManualTimeToRead(this.chapterMetadata.wordCount, this.totalPages, this.chapter.files[0].format === MangaFormat.EPUB).subscribe((time) => this.readingTime = time);
});
@@ -153,6 +162,7 @@ export class CardDetailDrawerComponent implements OnInit {
});
this.chapterActions = this.actionFactoryService.getChapterActions(this.handleChapterActionCallback.bind(this)).filter(item => item.action !== Action.Edit);
+ this.chapterActions.push({title: 'Read', action: Action.Read, callback: this.handleChapterActionCallback.bind(this), requiresAdmin: false});
if (this.isChapter) {
this.chapters.push(this.data as Chapter);
@@ -245,22 +255,59 @@ export class CardDetailDrawerComponent implements OnInit {
case(Action.AddToReadingList):
this.actionService.addChapterToReadingList(chapter, this.seriesId);
break;
+ case (Action.IncognitoRead):
+ this.readChapter(chapter, true);
+ break;
+ case (Action.Download):
+ this.download(chapter);
+ break;
+ case (Action.Read):
+ this.readChapter(chapter, false);
+ break;
default:
break;
}
}
- readChapter(chapter: Chapter) {
+ readChapter(chapter: Chapter, incognito: boolean = false) {
if (chapter.pages === 0) {
this.toastr.error('There are no pages. Kavita was not able to read this archive.');
return;
}
+ const params = this.readerService.getQueryParamsObject(incognito, false);
if (chapter.files.length > 0 && chapter.files[0].format === MangaFormat.EPUB) {
- this.router.navigate(['library', this.libraryId, 'series', this.seriesId, 'book', chapter.id]);
+ this.router.navigate(['library', this.libraryId, 'series', this.seriesId, 'book', chapter.id], {queryParams: params});
} else {
- this.router.navigate(['library', this.libraryId, 'series', this.seriesId, 'manga', chapter.id]);
+ this.router.navigate(['library', this.libraryId, 'series', this.seriesId, 'manga', chapter.id], {queryParams: params});
}
+ this.close();
+ }
+
+ download(chapter: Chapter) {
+ if (this.downloadInProgress === true) {
+ this.toastr.info('Download is already in progress. Please wait.');
+ return;
+ }
+
+ this.downloadService.downloadChapterSize(chapter.id).pipe(take(1)).subscribe(async (size) => {
+ const wantToDownload = await this.downloadService.confirmSize(size, 'chapter');
+ console.log('want to download: ', wantToDownload);
+ if (!wantToDownload) { return; }
+ this.downloadInProgress = true;
+ this.download$ = this.downloadService.downloadChapter(chapter).pipe(
+ tap(val => {
+ console.log(val);
+ }),
+ takeWhile(val => {
+ return val.state != 'DONE';
+ }),
+ finalize(() => {
+ this.download$ = null;
+ this.downloadInProgress = false;
+ }));
+ this.download$.subscribe(() => {});
+ });
}
}
diff --git a/UI/Web/src/app/cards/card-detail-layout/card-detail-layout.component.html b/UI/Web/src/app/cards/card-detail-layout/card-detail-layout.component.html
index a06f80af6..66b4c51d0 100644
--- a/UI/Web/src/app/cards/card-detail-layout/card-detail-layout.component.html
+++ b/UI/Web/src/app/cards/card-detail-layout/card-detail-layout.component.html
@@ -12,18 +12,23 @@