Manga Reader Fixes (#1925)
* Fixed up an issue where image might be cut off in fit to height * Removed some calls to backend for translating age rating to a string * Fixed an issue with sizing on page splitting right to left. * Ensure all image access requires apikey * Removed a TODO
This commit is contained in:
parent
ff18389954
commit
5c1e9c0521
11 changed files with 169 additions and 48 deletions
|
@ -1,3 +1,5 @@
|
|||
$scrollbarHeight: 34px;
|
||||
|
||||
img {
|
||||
user-select: none;
|
||||
}
|
||||
|
@ -12,8 +14,9 @@ img {
|
|||
}
|
||||
|
||||
&.full-height {
|
||||
height: calc(100vh - 34px); // 34px is the height of the horizontal scrollbar that will appear
|
||||
display: flex; // changed from inline-block to fix the centering on tablets not working
|
||||
height: calc(100vh - $scrollbarHeight);
|
||||
display: flex;
|
||||
align-content: center;
|
||||
}
|
||||
|
||||
&.original {
|
||||
|
|
|
@ -13,6 +13,7 @@ export class ImageService implements OnDestroy {
|
|||
|
||||
baseUrl = environment.apiUrl;
|
||||
apiKey: string = '';
|
||||
encodedKey: string = '';
|
||||
public placeholderImage = 'assets/images/image-placeholder-min.png';
|
||||
public errorImage = 'assets/images/error-placeholder2-min.png';
|
||||
public resetCoverImage = 'assets/images/image-reset-cover-min.png';
|
||||
|
@ -33,6 +34,7 @@ export class ImageService implements OnDestroy {
|
|||
this.accountService.currentUser$.pipe(takeUntil(this.onDestroy)).subscribe(user => {
|
||||
if (user) {
|
||||
this.apiKey = user.apiKey;
|
||||
this.encodedKey = encodeURIComponent(this.apiKey);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -62,35 +64,35 @@ export class ImageService implements OnDestroy {
|
|||
}
|
||||
|
||||
getLibraryCoverImage(libraryId: number) {
|
||||
return this.baseUrl + 'image/library-cover?libraryId=' + libraryId;
|
||||
return `${this.baseUrl}image/library-cover?libraryId=${libraryId}&apiKey=${this.encodedKey}`;
|
||||
}
|
||||
|
||||
getVolumeCoverImage(volumeId: number) {
|
||||
return this.baseUrl + 'image/volume-cover?volumeId=' + volumeId;
|
||||
return `${this.baseUrl}image/volume-cover?volumeId=${volumeId}&apiKey=${this.encodedKey}`;
|
||||
}
|
||||
|
||||
getSeriesCoverImage(seriesId: number) {
|
||||
return this.baseUrl + 'image/series-cover?seriesId=' + seriesId;
|
||||
return `${this.baseUrl}image/series-cover?seriesId=${seriesId}&apiKey=${this.encodedKey}`;
|
||||
}
|
||||
|
||||
getCollectionCoverImage(collectionTagId: number) {
|
||||
return this.baseUrl + 'image/collection-cover?collectionTagId=' + collectionTagId;
|
||||
return `${this.baseUrl}image/collection-cover?collectionTagId=${collectionTagId}&apiKey=${this.encodedKey}`;
|
||||
}
|
||||
|
||||
getReadingListCoverImage(readingListId: number) {
|
||||
return this.baseUrl + 'image/readinglist-cover?readingListId=' + readingListId;
|
||||
return `${this.baseUrl}image/readinglist-cover?readingListId=${readingListId}&apiKey=${this.encodedKey}`;
|
||||
}
|
||||
|
||||
getChapterCoverImage(chapterId: number) {
|
||||
return this.baseUrl + 'image/chapter-cover?chapterId=' + chapterId;
|
||||
return `${this.baseUrl}image/chapter-cover?chapterId=${chapterId}&apiKey=${this.encodedKey}`;
|
||||
}
|
||||
|
||||
getBookmarkedImage(chapterId: number, pageNum: number) {
|
||||
return this.baseUrl + 'image/bookmark?chapterId=' + chapterId + '&pageNum=' + pageNum + '&apiKey=' + encodeURIComponent(this.apiKey);
|
||||
return `${this.baseUrl}image/bookmark?chapterId=${chapterId}&apiKey=${this.encodedKey}&pageNum=${pageNum}`;
|
||||
}
|
||||
|
||||
getCoverUploadImage(filename: string) {
|
||||
return this.baseUrl + 'image/cover-upload?filename=' + encodeURIComponent(filename);
|
||||
return `${this.baseUrl}image/cover-upload?filename=${encodeURIComponent(filename)}&apiKey=${this.encodedKey}`;
|
||||
}
|
||||
|
||||
updateErroredImage(event: any) {
|
||||
|
|
|
@ -16,6 +16,9 @@ import { FilterUtilitiesService } from '../shared/_services/filter-utilities.ser
|
|||
import { FileDimension } from '../manga-reader/_models/file-dimension';
|
||||
import screenfull from 'screenfull';
|
||||
import { TextResonse } from '../_types/text-response';
|
||||
import { AccountService } from './account.service';
|
||||
import { Subject, takeUntil } from 'rxjs';
|
||||
import { OnDestroy } from '@angular/core';
|
||||
|
||||
export const CHAPTER_ID_DOESNT_EXIST = -1;
|
||||
export const CHAPTER_ID_NOT_FETCHED = -2;
|
||||
|
@ -23,16 +26,29 @@ export const CHAPTER_ID_NOT_FETCHED = -2;
|
|||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ReaderService {
|
||||
export class ReaderService implements OnDestroy {
|
||||
|
||||
baseUrl = environment.apiUrl;
|
||||
encodedKey: string = '';
|
||||
private onDestroy: Subject<void> = new Subject();
|
||||
|
||||
// Override background color for reader and restore it onDestroy
|
||||
private originalBodyColor!: string;
|
||||
|
||||
constructor(private httpClient: HttpClient, private router: Router,
|
||||
private location: Location, private utilityService: UtilityService,
|
||||
private filterUtilitySerivce: FilterUtilitiesService) { }
|
||||
private filterUtilitySerivce: FilterUtilitiesService, private accountService: AccountService) {
|
||||
this.accountService.currentUser$.pipe(takeUntil(this.onDestroy)).subscribe(user => {
|
||||
if (user) {
|
||||
this.encodedKey = encodeURIComponent(user.apiKey);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.onDestroy.next();
|
||||
this.onDestroy.complete();
|
||||
}
|
||||
|
||||
getNavigationArray(libraryId: number, seriesId: number, chapterId: number, format: MangaFormat) {
|
||||
if (format === undefined) format = MangaFormat.ARCHIVE;
|
||||
|
@ -47,7 +63,7 @@ export class ReaderService {
|
|||
}
|
||||
|
||||
downloadPdf(chapterId: number) {
|
||||
return this.baseUrl + 'reader/pdf?chapterId=' + chapterId;
|
||||
return `${this.baseUrl}reader/pdf?chapterId=${chapterId}&apiKey=${this.encodedKey}`;
|
||||
}
|
||||
|
||||
bookmark(seriesId: number, volumeId: number, chapterId: number, page: number) {
|
||||
|
@ -98,11 +114,11 @@ export class ReaderService {
|
|||
}
|
||||
|
||||
getPageUrl(chapterId: number, page: number) {
|
||||
return this.baseUrl + 'reader/image?chapterId=' + chapterId + '&page=' + page;
|
||||
return `${this.baseUrl}reader/image?chapterId=${chapterId}&apiKey=${this.encodedKey}&page=${page}`;
|
||||
}
|
||||
|
||||
getThumbnailUrl(chapterId: number, page: number) {
|
||||
return this.baseUrl + 'reader/thumbnail?chapterId=' + chapterId + '&page=' + page;
|
||||
return `${this.baseUrl}reader/thumbnail?chapterId=${chapterId}&apiKey=${this.encodedKey}&page=${page}`;
|
||||
}
|
||||
|
||||
getBookmarkPageUrl(seriesId: number, apiKey: string, page: number) {
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
<ng-container *ngIf="seriesMetadata.ageRating">
|
||||
<div class="col-lg-1 col-md-4 col-sm-4 col-4 mb-3">
|
||||
<app-icon-and-title label="Age Rating" [clickable]="true" fontClasses="fas fa-eye" (click)="handleGoTo(FilterQueryParam.AgeRating, seriesMetadata.ageRating)" title="Age Rating">
|
||||
{{metadataService.getAgeRating(this.seriesMetadata.ageRating) | async}}
|
||||
{{this.seriesMetadata.ageRating | ageRating | async}}
|
||||
</app-icon-and-title>
|
||||
</div>
|
||||
<div class="vr d-none d-lg-block m-2"></div>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<div class="image-container {{imageFitClass$ | async}}"
|
||||
[ngClass]="{'d-none': !renderWithCanvas }"
|
||||
[style.filter]="(darkenss$ | async) ?? '' | safeStyle">
|
||||
<canvas #content ondragstart="return false;" onselectstart="return false;"></canvas>
|
||||
<canvas #content ondragstart="return false;" onselectstart="return false;" class="{{imageFitClass$ | async}}"></canvas>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ export class CanvasRendererComponent implements OnInit, AfterViewInit, OnDestroy
|
|||
constructor(private readonly cdRef: ChangeDetectorRef, private mangaReaderService: ManagaReaderService, private readerService: ReaderService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.readerSettings$.pipe(takeUntil(this.onDestroy), tap(value => {
|
||||
this.readerSettings$.pipe(takeUntil(this.onDestroy), tap((value: ReaderSetting) => {
|
||||
this.fit = value.fitting;
|
||||
this.pageSplit = value.pageSplit;
|
||||
this.layoutMode = value.layoutMode;
|
||||
|
@ -70,9 +70,9 @@ export class CanvasRendererComponent implements OnInit, AfterViewInit, OnDestroy
|
|||
|
||||
this.imageFitClass$ = this.readerSettings$.pipe(
|
||||
takeUntil(this.onDestroy),
|
||||
map(values => values.fitting),
|
||||
map((values: ReaderSetting) => values.fitting),
|
||||
map(fit => {
|
||||
if (fit === FITTING_OPTION.WIDTH || this.layoutMode === LayoutMode.Single) return fit;
|
||||
if (fit === FITTING_OPTION.WIDTH) return fit; // || this.layoutMode === LayoutMode.Single (so that we can check the wide stuff)
|
||||
if (this.canvasImage === null) return fit;
|
||||
|
||||
// Would this ever execute given that we perform splitting only in this renderer?
|
||||
|
@ -181,9 +181,10 @@ export class CanvasRendererComponent implements OnInit, AfterViewInit, OnDestroy
|
|||
|
||||
const needsSplitting = this.updateSplitPage();
|
||||
if (!needsSplitting) return;
|
||||
if (this.currentImageSplitPart === SPLIT_PAGE_PART.NO_SPLIT) return;
|
||||
|
||||
this.renderWithCanvas = true;
|
||||
if (this.currentImageSplitPart === SPLIT_PAGE_PART.NO_SPLIT) return;
|
||||
|
||||
this.setCanvasSize();
|
||||
|
||||
if (needsSplitting && this.currentImageSplitPart === SPLIT_PAGE_PART.LEFT_PART) {
|
||||
|
|
|
@ -392,7 +392,6 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
}
|
||||
|
||||
get SplitIconClass() {
|
||||
// TODO: make this a pipe
|
||||
if (this.mangaReaderService.isSplitLeftToRight(this.pageSplitOption)) {
|
||||
return 'left-side';
|
||||
} else if (this.mangaReaderService.isNoSplit(this.pageSplitOption)) {
|
||||
|
|
|
@ -2,23 +2,38 @@ import { Pipe, PipeTransform } from '@angular/core';
|
|||
import { Observable, of } from 'rxjs';
|
||||
import { AgeRating } from '../_models/metadata/age-rating';
|
||||
import { AgeRatingDto } from '../_models/metadata/age-rating-dto';
|
||||
import { MetadataService } from '../_services/metadata.service';
|
||||
|
||||
@Pipe({
|
||||
name: 'ageRating'
|
||||
})
|
||||
export class AgeRatingPipe implements PipeTransform {
|
||||
|
||||
constructor(private metadataService: MetadataService) {}
|
||||
constructor() {}
|
||||
|
||||
transform(value: AgeRating | AgeRatingDto | undefined): Observable<string> {
|
||||
if (value === undefined || value === null) return of('undefined');
|
||||
if (value === undefined || value === null) return of('Unknown');
|
||||
|
||||
if (value.hasOwnProperty('title')) {
|
||||
return of((value as AgeRatingDto).title);
|
||||
}
|
||||
|
||||
return this.metadataService.getAgeRating((value as AgeRating));
|
||||
switch(value) {
|
||||
case AgeRating.Unknown: return of('Unknown');
|
||||
case AgeRating.EarlyChildhood: return of('Early Childhood');
|
||||
case AgeRating.AdultsOnly: return of('Adults Only 18+');
|
||||
case AgeRating.Everyone: return of('Everyone');
|
||||
case AgeRating.Everyone10Plus: return of('Everyone 10+');
|
||||
case AgeRating.G: return of('G');
|
||||
case AgeRating.KidsToAdults: return of('Kids to Adults');
|
||||
case AgeRating.Mature: return of('Mature');
|
||||
case AgeRating.Mature17Plus: return of('M');
|
||||
case AgeRating.RatingPending: return of('Rating Pending');
|
||||
case AgeRating.Teen: return of('Teen');
|
||||
case AgeRating.X18Plus: return of('X18+');
|
||||
case AgeRating.NotApplicable: return of('Not Applicable');
|
||||
}
|
||||
|
||||
return of('Unknown');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue