mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-05-24 00:52:23 -04:00
Reader Polish (#2465)
Co-authored-by: Andre Smith <Hobogrammer@users.noreply.github.com>
This commit is contained in:
parent
9fdaf5f99f
commit
e489d2404a
@ -308,7 +308,7 @@ public class OpdsController : BaseApiController
|
||||
var userId = await GetUser(apiKey);
|
||||
if (!(await _unitOfWork.SettingsRepository.GetSettingsDtoAsync()).EnableOpds)
|
||||
return BadRequest(await _localizationService.Translate(userId, "opds-disabled"));
|
||||
var (baseUrl, prefix) = await GetPrefix();
|
||||
var (_, prefix) = await GetPrefix();
|
||||
|
||||
var filters = _unitOfWork.AppUserSmartFilterRepository.GetAllDtosByUserId(userId);
|
||||
var feed = CreateFeed(await _localizationService.Translate(userId, "smartFilters"), $"{prefix}{apiKey}/smart-filters", apiKey, prefix);
|
||||
@ -337,7 +337,7 @@ public class OpdsController : BaseApiController
|
||||
var userId = await GetUser(apiKey);
|
||||
if (!(await _unitOfWork.SettingsRepository.GetSettingsDtoAsync()).EnableOpds)
|
||||
return BadRequest(await _localizationService.Translate(userId, "opds-disabled"));
|
||||
var (baseUrl, prefix) = await GetPrefix();
|
||||
var (_, prefix) = await GetPrefix();
|
||||
|
||||
var externalSources = await _unitOfWork.AppUserExternalSourceRepository.GetExternalSources(userId);
|
||||
var feed = CreateFeed(await _localizationService.Translate(userId, "external-sources"), $"{prefix}{apiKey}/external-sources", apiKey, prefix);
|
||||
@ -370,15 +370,13 @@ public class OpdsController : BaseApiController
|
||||
if (!(await _unitOfWork.SettingsRepository.GetSettingsDtoAsync()).EnableOpds)
|
||||
return BadRequest(await _localizationService.Translate(userId, "opds-disabled"));
|
||||
var (baseUrl, prefix) = await GetPrefix();
|
||||
var libraries = await _unitOfWork.LibraryRepository.GetLibrariesForUserIdAsync(userId);
|
||||
var feed = CreateFeed(await _localizationService.Translate(userId, "libraries"), $"{prefix}{apiKey}/libraries", apiKey, prefix);
|
||||
SetFeedId(feed, "libraries");
|
||||
|
||||
// Ensure libraries follow SideNav order
|
||||
var userSideNavStreams = await _unitOfWork.UserRepository.GetSideNavStreams(userId, false);
|
||||
foreach (var sideNavStream in userSideNavStreams.Where(s => s.StreamType == SideNavStreamType.Library))
|
||||
foreach (var library in userSideNavStreams.Where(s => s.StreamType == SideNavStreamType.Library).Select(sideNavStream => sideNavStream.Library))
|
||||
{
|
||||
var library = sideNavStream.Library;
|
||||
feed.Entries.Add(new FeedEntry()
|
||||
{
|
||||
Id = library!.Id.ToString(),
|
||||
@ -779,13 +777,13 @@ public class OpdsController : BaseApiController
|
||||
var chapters = (await _unitOfWork.ChapterRepository.GetChaptersAsync(volume.Id)).OrderBy(x => double.Parse(x.Number, CultureInfo.InvariantCulture),
|
||||
_chapterSortComparer);
|
||||
|
||||
foreach (var chapter in chapters)
|
||||
foreach (var chapterId in chapters.Select(c => c.Id))
|
||||
{
|
||||
var files = await _unitOfWork.ChapterRepository.GetFilesForChapterAsync(chapter.Id);
|
||||
var chapterTest = await _unitOfWork.ChapterRepository.GetChapterDtoAsync(chapter.Id);
|
||||
var files = await _unitOfWork.ChapterRepository.GetFilesForChapterAsync(chapterId);
|
||||
var chapterTest = await _unitOfWork.ChapterRepository.GetChapterDtoAsync(chapterId);
|
||||
foreach (var mangaFile in files)
|
||||
{
|
||||
feed.Entries.Add(await CreateChapterWithFile(userId, seriesId, volume.Id, chapter.Id, mangaFile, series, chapterTest, apiKey, prefix, baseUrl));
|
||||
feed.Entries.Add(await CreateChapterWithFile(userId, seriesId, volume.Id, chapterId, mangaFile, series, chapterTest, apiKey, prefix, baseUrl));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,18 +105,23 @@ public class ReaderController : BaseApiController
|
||||
/// <param name="extractPdf">Should Kavita extract pdf into images. Defaults to false.</param>
|
||||
/// <returns></returns>
|
||||
[HttpGet("image")]
|
||||
[ResponseCache(CacheProfileName = ResponseCacheProfiles.Hour, VaryByQueryKeys = new []{"chapterId","page", "extractPdf", "apiKey"})]
|
||||
[ResponseCache(CacheProfileName = ResponseCacheProfiles.Hour, VaryByQueryKeys = new []{"chapterId", "page", "extractPdf", "apiKey"})]
|
||||
[AllowAnonymous]
|
||||
public async Task<ActionResult> GetImage(int chapterId, int page, string apiKey, bool extractPdf = false)
|
||||
{
|
||||
if (page < 0) page = 0;
|
||||
var userId = await _unitOfWork.UserRepository.GetUserIdByApiKeyAsync(apiKey);
|
||||
if (userId == 0) return BadRequest();
|
||||
var chapter = await _cacheService.Ensure(chapterId, extractPdf);
|
||||
if (chapter == null) return NoContent();
|
||||
|
||||
try
|
||||
{
|
||||
if (new Random().Next(1, 10) > 5)
|
||||
{
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
var chapter = await _cacheService.Ensure(chapterId, extractPdf);
|
||||
if (chapter == null) return NoContent();
|
||||
_logger.LogInformation("Fetching Page {PageNum} on Chapter {ChapterId}", page, chapterId);
|
||||
var path = _cacheService.GetCachedPagePath(chapter.Id, page);
|
||||
if (string.IsNullOrEmpty(path) || !System.IO.File.Exists(path))
|
||||
return BadRequest(await _localizationService.Translate(userId, "no-image-for-page", page));
|
||||
@ -245,8 +250,8 @@ public class ReaderController : BaseApiController
|
||||
LibraryId = dto.LibraryId,
|
||||
IsSpecial = dto.IsSpecial,
|
||||
Pages = dto.Pages,
|
||||
SeriesTotalPages = series?.Pages ?? 0,
|
||||
SeriesTotalPagesRead = series?.PagesRead ?? 0,
|
||||
SeriesTotalPages = series.Pages,
|
||||
SeriesTotalPagesRead = series.PagesRead,
|
||||
ChapterTitle = dto.ChapterTitle ?? string.Empty,
|
||||
Subtitle = string.Empty,
|
||||
Title = dto.SeriesName,
|
||||
|
@ -280,7 +280,7 @@ public class LibraryRepository : ILibraryRepository
|
||||
{
|
||||
Title = s,
|
||||
IsoCode = s
|
||||
};;
|
||||
};
|
||||
}
|
||||
|
||||
public IEnumerable<PublicationStatusDto> GetAllPublicationStatusesDtosForLibrariesAsync(List<int> libraryIds)
|
||||
|
@ -644,6 +644,7 @@ public class DirectoryService : IDirectoryService
|
||||
/// Scans a directory by utilizing a recursive folder search. If a .kavitaignore file is found, will ignore matching patterns
|
||||
/// </summary>
|
||||
/// <param name="folderPath"></param>
|
||||
/// <param name="supportedExtensions"></param>
|
||||
/// <param name="matcher"></param>
|
||||
/// <returns></returns>
|
||||
public IList<string> ScanFiles(string folderPath, string supportedExtensions, GlobMatcher? matcher = null)
|
||||
|
@ -398,7 +398,7 @@ public class ReaderService : IReaderService
|
||||
// Handle Chapters within next Volume
|
||||
// ! When selecting the chapter for the next volume, we need to make sure a c0 comes before a c1+
|
||||
var chapters = volume.Chapters.OrderBy(x => x.Number.AsDouble(), _chapterSortComparer).ToList();
|
||||
if (currentChapter.Number.Equals(Parser.DefaultChapter) && chapters.Last().Number.Equals(Parser.DefaultChapter))
|
||||
if (currentChapter.Number.Equals(Parser.DefaultChapter) && chapters[^1].Number.Equals(Parser.DefaultChapter))
|
||||
{
|
||||
// We need to handle an extra check if the current chapter is the last special, as we should return -1
|
||||
if (currentChapter.IsSpecial) return -1;
|
||||
|
@ -813,7 +813,7 @@ public class SeriesService : ISeriesService
|
||||
|
||||
private static double ExponentialSmoothing(IList<double> data, double alpha)
|
||||
{
|
||||
var forecast = data.First();
|
||||
var forecast = data[0];
|
||||
|
||||
foreach (var value in data)
|
||||
{
|
||||
|
@ -71,7 +71,7 @@ public class StreamService : IStreamService
|
||||
var smartFilter = await _unitOfWork.AppUserSmartFilterRepository.GetById(smartFilterId);
|
||||
if (smartFilter == null) throw new KavitaException(await _localizationService.Translate(userId, "smart-filter-doesnt-exist"));
|
||||
|
||||
var stream = user?.DashboardStreams.FirstOrDefault(d => d.SmartFilter?.Id == smartFilterId);
|
||||
var stream = user.DashboardStreams.FirstOrDefault(d => d.SmartFilter?.Id == smartFilterId);
|
||||
if (stream != null) throw new KavitaException(await _localizationService.Translate(userId, "smart-filter-already-in-use"));
|
||||
|
||||
var maxOrder = user!.DashboardStreams.Max(d => d.Order);
|
||||
@ -159,7 +159,7 @@ public class StreamService : IStreamService
|
||||
var smartFilter = await _unitOfWork.AppUserSmartFilterRepository.GetById(smartFilterId);
|
||||
if (smartFilter == null) throw new KavitaException(await _localizationService.Translate(userId, "smart-filter-doesnt-exist"));
|
||||
|
||||
var stream = user?.SideNavStreams.FirstOrDefault(d => d.SmartFilter?.Id == smartFilterId);
|
||||
var stream = user.SideNavStreams.FirstOrDefault(d => d.SmartFilter?.Id == smartFilterId);
|
||||
if (stream != null) throw new KavitaException(await _localizationService.Translate(userId, "smart-filter-already-in-use"));
|
||||
|
||||
var maxOrder = user!.SideNavStreams.Max(d => d.Order);
|
||||
|
@ -72,7 +72,7 @@ public class TachiyomiService : ITachiyomiService
|
||||
if (looseLeafChapterVolume == null)
|
||||
{
|
||||
var volumeChapter = _mapper.Map<ChapterDto>(volumes
|
||||
.Last().Chapters
|
||||
[^1].Chapters
|
||||
.OrderBy(c => c.Number.AsFloat(), ChapterSortComparerZeroFirst.Default)
|
||||
.Last());
|
||||
if (volumeChapter.Number == "0")
|
||||
|
@ -179,7 +179,7 @@ public class LibraryWatcher : ILibraryWatcher
|
||||
/// <param name="e"></param>
|
||||
private void OnError(object sender, ErrorEventArgs e)
|
||||
{
|
||||
_logger.LogError(e.GetException(), "[LibraryWatcher] An error occured, likely too many changes occured at once or the folder being watched was deleted. Restarting Watchers");
|
||||
_logger.LogError(e.GetException(), "[LibraryWatcher] An error occured, likely too many changes occured at once or the folder being watched was deleted. Restarting Watchers {Current}/{Total}", _bufferFullCounter, 3);
|
||||
bool condition;
|
||||
lock (Lock)
|
||||
{
|
||||
|
@ -312,7 +312,6 @@ public class ParseScannedFiles
|
||||
}
|
||||
|
||||
await _eventHub.SendMessageAsync(MessageFactory.NotificationProgress, MessageFactory.FileScanProgressEvent("File Scan Done", library.Name, ProgressEventType.Ended));
|
||||
return;
|
||||
|
||||
async Task ProcessFolder(IList<string> files, string folder)
|
||||
{
|
||||
|
@ -295,7 +295,7 @@ public class ProcessSeries : IProcessSeries
|
||||
if (series.Format == MangaFormat.Epub || series.Format == MangaFormat.Pdf && chapters.Count == 1)
|
||||
{
|
||||
series.Metadata.MaxCount = 1;
|
||||
} else if (series.Metadata.TotalCount == 1 && chapters.Count == 1 && chapters.First().IsSpecial)
|
||||
} else if (series.Metadata.TotalCount == 1 && chapters.Count == 1 && chapters[0].IsSpecial)
|
||||
{
|
||||
// If a series has a TotalCount of 1 and there is only a Special, mark it as Complete
|
||||
series.Metadata.MaxCount = series.Metadata.TotalCount;
|
||||
|
@ -347,6 +347,7 @@ public class Startup
|
||||
=>
|
||||
{
|
||||
opts.EnrichDiagnosticContext = LogEnricher.EnrichFromRequest;
|
||||
opts.IncludeQueryInRequestPath = true;
|
||||
});
|
||||
|
||||
app.Use(async (context, next) =>
|
||||
|
@ -17,7 +17,7 @@ export enum RelationKind {
|
||||
Edition = 13
|
||||
}
|
||||
|
||||
export const RelationKinds = [
|
||||
const RelationKindsUnsorted = [
|
||||
{text: 'Prequel', value: RelationKind.Prequel},
|
||||
{text: 'Sequel', value: RelationKind.Sequel},
|
||||
{text: 'Spin Off', value: RelationKind.SpinOff},
|
||||
@ -31,3 +31,5 @@ export const RelationKinds = [
|
||||
{text: 'Doujinshi', value: RelationKind.Doujinshi},
|
||||
{text: 'Other', value: RelationKind.Other},
|
||||
];
|
||||
|
||||
export const RelationKinds = RelationKindsUnsorted.slice().sort((a, b) => a.text.localeCompare(b.text));
|
||||
|
@ -38,6 +38,7 @@ export class BookLineOverlayComponent implements OnInit {
|
||||
@Input({required: true}) pageNumber: number = 0;
|
||||
@Input({required: true}) parent: ElementRef | undefined;
|
||||
@Output() refreshToC: EventEmitter<void> = new EventEmitter();
|
||||
@Output() isOpen: EventEmitter<boolean> = new EventEmitter(false);
|
||||
|
||||
xPath: string = '';
|
||||
selectedText: string = '';
|
||||
@ -84,6 +85,8 @@ export class BookLineOverlayComponent implements OnInit {
|
||||
if (!event.target) return;
|
||||
|
||||
if ((!selection || selection.toString().trim() === '' || selection.toString().trim() === this.selectedText)) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.reset();
|
||||
return;
|
||||
}
|
||||
@ -96,6 +99,7 @@ export class BookLineOverlayComponent implements OnInit {
|
||||
this.xPath = '//' + this.xPath;
|
||||
}
|
||||
|
||||
this.isOpen.emit(true);
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
@ -137,6 +141,7 @@ export class BookLineOverlayComponent implements OnInit {
|
||||
if (selection) {
|
||||
selection.removeAllRanges();
|
||||
}
|
||||
this.isOpen.emit(false);
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
|
||||
|
@ -3,13 +3,14 @@
|
||||
<ng-container *transloco="let t; read: 'book-reader'">
|
||||
<div class="fixed-top" #stickyTop>
|
||||
<a class="visually-hidden-focusable focus-visible" href="javascript:void(0);" (click)="moveFocus()">{{t('skip-header')}}</a>
|
||||
<ng-container [ngTemplateOutlet]="actionBar"></ng-container>
|
||||
<ng-container [ngTemplateOutlet]="actionBar" [ngTemplateOutletContext]="{isTop: true}"></ng-container>
|
||||
<app-book-line-overlay [parent]="bookContainerElemRef" *ngIf="page !== undefined"
|
||||
[libraryId]="libraryId"
|
||||
[volumeId]="volumeId"
|
||||
[chapterId]="chapterId"
|
||||
[seriesId]="seriesId"
|
||||
[pageNumber]="pageNum"
|
||||
(isOpen)="updateLineOverlayOpen($event)"
|
||||
(refreshToC)="refreshPersonalToC()">
|
||||
</app-book-line-overlay>
|
||||
<app-drawer #commentDrawer="drawer" [(isOpen)]="drawerOpen" [options]="{topOffset: topOffset}">
|
||||
@ -18,11 +19,6 @@
|
||||
<span style="font-size: 14px; color: var(--primary-color)" tabindex="0" role="button" (click)="closeReader()">{{t('close-reader')}}</span>
|
||||
</div>
|
||||
<div subheader>
|
||||
<!-- <div class="g-0 text-center" *ngIf="!isLoading">-->
|
||||
<!-- <span *ngIf="incognitoMode" (click)="turnOffIncognito()" role="button" [attr.aria-label]="t('incognito-mode-alt')">-->
|
||||
<!-- (<i class="fa fa-glasses" aria-hidden="true"></i><span class="visually-hidden">{{t('incognito-mode-label')}}</span>)</span>-->
|
||||
<!-- <span class="book-title-text ms-1" [ngbTooltip]="bookTitle">{{bookTitle}}</span>-->
|
||||
<!-- </div>-->
|
||||
<div class="pagination-cont">
|
||||
<ng-container *ngIf="layoutMode !== BookPageLayoutMode.Default">
|
||||
<div class="virt-pagination-cont">
|
||||
@ -129,13 +125,14 @@
|
||||
<div *ngIf="page !== undefined && (scrollbarNeeded || layoutMode !== BookPageLayoutMode.Default) && !(writingStyle === WritingStyle.Vertical && layoutMode === BookPageLayoutMode.Default)"
|
||||
(click)="$event.stopPropagation();"
|
||||
[ngClass]="{'bottom-bar': layoutMode !== BookPageLayoutMode.Default}">
|
||||
<ng-container [ngTemplateOutlet]="actionBar"></ng-container>
|
||||
<ng-container [ngTemplateOutlet]="actionBar" [ngTemplateOutletContext]="{isTop: false}"></ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ng-template #actionBar>
|
||||
<ng-template #actionBar let-isTop>
|
||||
<div class="action-bar row g-0 justify-content-between" *ngIf="!immersiveMode || drawerOpen || actionBarVisible">
|
||||
<button class="btn btn-outline-secondary btn-icon col-2 col-xs-1" (click)="movePage(readingDirection === ReadingDirection.LeftToRight ? PAGING_DIRECTION.BACKWARDS : PAGING_DIRECTION.FORWARD)"
|
||||
<button class="btn btn-outline-secondary btn-icon col-2 col-xs-1"
|
||||
(click)="!isTop && movePage(readingDirection === ReadingDirection.LeftToRight ? PAGING_DIRECTION.BACKWARDS : PAGING_DIRECTION.FORWARD)"
|
||||
[disabled]="readingDirection === ReadingDirection.LeftToRight ? IsPrevDisabled : IsNextDisabled"
|
||||
title="{{readingDirection === ReadingDirection.LeftToRight ? t('previous') : t('next')}} Page">
|
||||
<i class="fa {{(readingDirection === ReadingDirection.LeftToRight ? IsPrevChapter : IsNextChapter) ? 'fa-angle-double-left' : 'fa-angle-left'}} {{readingDirection === ReadingDirection.RightToLeft ? 'next-page-highlight' : ''}}" aria-hidden="true"></i>
|
||||
@ -146,20 +143,20 @@
|
||||
<button class="btn btn-secondary col-2 col-xs-1" (click)="toggleDrawer()">
|
||||
<i class="fa fa-bars" aria-hidden="true"></i></button>
|
||||
<div class="book-title col-2 d-none d-sm-block">
|
||||
<ng-container *ngIf="isLoading; else showTitle">
|
||||
@if(isLoading) {
|
||||
<div class="spinner-border spinner-border-sm text-primary" style="border-radius: 50%;" role="status">
|
||||
<span class="visually-hidden">{{t('loading-book')}}</span>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-template #showTitle>
|
||||
<span *ngIf="incognitoMode" (click)="turnOffIncognito()" role="button" [attr.aria-label]="t('incognito-mode-alt')">
|
||||
} @else {
|
||||
<span *ngIf="incognitoMode" (click)="turnOffIncognito()" role="button" [attr.aria-label]="t('incognito-mode-alt')">
|
||||
(<i class="fa fa-glasses" aria-hidden="true"></i><span class="visually-hidden">{{t('incognito-mode-label')}}</span>)</span>
|
||||
<span class="book-title-text ms-1" [ngbTooltip]="bookTitle">{{bookTitle}}</span>
|
||||
</ng-template>
|
||||
<span class="book-title-text ms-1" [ngbTooltip]="bookTitle">{{bookTitle}}</span>
|
||||
}
|
||||
</div>
|
||||
<button class="btn btn-secondary col-2 col-xs-1" (click)="closeReader()"><i class="fa fa-times-circle" aria-hidden="true"></i></button>
|
||||
<button class="btn btn-outline-secondary btn-icon col-2 col-xs-1"
|
||||
[disabled]="readingDirection === ReadingDirection.LeftToRight ? IsNextDisabled : IsPrevDisabled"
|
||||
(click)="!isTop && movePage(readingDirection === ReadingDirection.LeftToRight ? PAGING_DIRECTION.FORWARD : PAGING_DIRECTION.BACKWARDS)"
|
||||
title="{{readingDirection === ReadingDirection.LeftToRight ? t('next') : t('previous')}} Page">
|
||||
<i class="fa {{(readingDirection === ReadingDirection.LeftToRight ? IsNextChapter : IsPrevChapter) ? 'fa-angle-double-right' : 'fa-angle-right'}} {{readingDirection === ReadingDirection.LeftToRight ? 'next-page-highlight' : ''}}" aria-hidden="true"></i>
|
||||
</button>
|
||||
|
@ -185,6 +185,10 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
* Belongs to drawer component
|
||||
*/
|
||||
drawerOpen = false;
|
||||
/**
|
||||
* If the word/line overlay is open
|
||||
*/
|
||||
isLineOverlayOpen = false;
|
||||
/**
|
||||
* If the action bar is visible
|
||||
*/
|
||||
@ -1630,20 +1634,21 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
|
||||
// Responsible for handling pagination only
|
||||
handleContainerClick(event: MouseEvent) {
|
||||
|
||||
if (this.drawerOpen || ['action-bar', 'offcanvas-backdrop'].some(className => (event.target as Element).classList.contains(className))) {
|
||||
if (this.drawerOpen || this.isLineOverlayOpen || ['action-bar', 'offcanvas-backdrop'].some(className => (event.target as Element).classList.contains(className))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isCursorOverLeftPaginationArea(event)) {
|
||||
this.movePage(this.readingDirection === ReadingDirection.LeftToRight ? PAGING_DIRECTION.BACKWARDS : PAGING_DIRECTION.FORWARD);
|
||||
} else if (this.isCursorOverRightPaginationArea(event)) {
|
||||
this.movePage(this.readingDirection === ReadingDirection.LeftToRight ? PAGING_DIRECTION.FORWARD : PAGING_DIRECTION.BACKWARDS)
|
||||
} else {
|
||||
this.toggleMenu(event);
|
||||
if (this.clickToPaginate) {
|
||||
if (this.isCursorOverLeftPaginationArea(event)) {
|
||||
this.movePage(this.readingDirection === ReadingDirection.LeftToRight ? PAGING_DIRECTION.BACKWARDS : PAGING_DIRECTION.FORWARD);
|
||||
} else if (this.isCursorOverRightPaginationArea(event)) {
|
||||
this.movePage(this.readingDirection === ReadingDirection.LeftToRight ? PAGING_DIRECTION.FORWARD : PAGING_DIRECTION.BACKWARDS)
|
||||
}
|
||||
}
|
||||
|
||||
this.toggleMenu(event);
|
||||
}
|
||||
|
||||
handleReaderClick(event: MouseEvent) {
|
||||
@ -1706,4 +1711,13 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
refreshPersonalToC() {
|
||||
this.refreshPToC.emit();
|
||||
}
|
||||
|
||||
updateLineOverlayOpen(isOpen: boolean) {
|
||||
// HACK: This hack allows the boolean to be changed to false so that the pagination doesn't trigger and move us to the next page when
|
||||
// the book overlay is just closing
|
||||
setTimeout(() => {
|
||||
this.isLineOverlayOpen = isOpen;
|
||||
this.cdRef.markForCheck();
|
||||
}, 10);
|
||||
}
|
||||
}
|
||||
|
@ -106,9 +106,9 @@
|
||||
<div class="col-md-12">
|
||||
<div class="mb-3">
|
||||
<label for="genres" class="form-label">{{t('genres-label')}}</label>
|
||||
<app-typeahead (selectedData)="updateGenres($event)" [settings]="genreSettings"
|
||||
<app-typeahead (selectedData)="updateGenres($event);metadata.genresLocked = true" [settings]="genreSettings"
|
||||
[(locked)]="metadata.genresLocked" (onUnlock)="metadata.genresLocked = false"
|
||||
(newItemAdded)="metadata.genresLocked = true" (selectedData)="metadata.genresLocked = true">
|
||||
(newItemAdded)="metadata.genresLocked = true">
|
||||
<ng-template #badgeItem let-item let-position="idx">
|
||||
{{item.title}}
|
||||
</ng-template>
|
||||
@ -142,9 +142,9 @@
|
||||
<div class="col-lg-4 col-md-12 pe-2">
|
||||
<div class="mb-3">
|
||||
<label for="language" class="form-label">{{t('language-label')}}</label>
|
||||
<app-typeahead (selectedData)="updateLanguage($event)" [settings]="languageSettings"
|
||||
<app-typeahead (selectedData)="updateLanguage($event);metadata.languageLocked = true;" [settings]="languageSettings"
|
||||
[(locked)]="metadata.languageLocked" (onUnlock)="metadata.languageLocked = false"
|
||||
(newItemAdded)="metadata.languageLocked = true" (selectedData)="metadata.languageLocked = true">
|
||||
(newItemAdded)="metadata.languageLocked = true">
|
||||
<ng-template #badgeItem let-item let-position="idx">
|
||||
{{item.title}}
|
||||
</ng-template>
|
||||
@ -159,7 +159,7 @@
|
||||
<label for="age-rating" class="form-label">{{t('age-rating-label')}}</label>
|
||||
<div class="input-group {{metadata.ageRatingLocked ? 'lock-active' : ''}}">
|
||||
<ng-container [ngTemplateOutlet]="lock" [ngTemplateOutletContext]="{ item: metadata, field: 'ageRatingLocked' }"></ng-container>
|
||||
<select class="form-select"id="age-rating" formControlName="ageRating">
|
||||
<select class="form-select" id="age-rating" formControlName="ageRating">
|
||||
<option *ngFor="let opt of ageRatings" [value]="opt.value">{{opt.title | titlecase}}</option>
|
||||
</select>
|
||||
</div>
|
||||
@ -170,7 +170,7 @@
|
||||
<label for="publication-status" class="form-label">{{t('publication-status-label')}}</label>
|
||||
<div class="input-group {{metadata.publicationStatusLocked ? 'lock-active' : ''}}">
|
||||
<ng-container [ngTemplateOutlet]="lock" [ngTemplateOutletContext]="{ item: metadata, field: 'publicationStatusLocked' }"></ng-container>
|
||||
<select class="form-select"id="publication-status" formControlName="publicationStatus">
|
||||
<select class="form-select" id="publication-status" formControlName="publicationStatus">
|
||||
<option *ngFor="let opt of publicationStatuses" [value]="opt.value">{{opt.title | titlecase}}</option>
|
||||
</select>
|
||||
</div>
|
||||
@ -186,9 +186,9 @@
|
||||
<div class="row g-0">
|
||||
<div class="mb-3">
|
||||
<label for="writer" class="form-label">{{t('writer-label')}}</label>
|
||||
<app-typeahead (selectedData)="updatePerson($event, PersonRole.Writer)" [settings]="getPersonsSettings(PersonRole.Writer)"
|
||||
<app-typeahead (selectedData)="updatePerson($event, PersonRole.Writer);metadata.writerLocked = true" [settings]="getPersonsSettings(PersonRole.Writer)"
|
||||
[(locked)]="metadata.writerLocked" (onUnlock)="metadata.writerLocked = false"
|
||||
(newItemAdded)="metadata.writerLocked = true" (selectedData)="metadata.writerLocked = true">
|
||||
(newItemAdded)="metadata.writerLocked = true">
|
||||
<ng-template #badgeItem let-item let-position="idx">
|
||||
{{item.name}}
|
||||
</ng-template>
|
||||
@ -201,9 +201,9 @@
|
||||
<div class="row g-0">
|
||||
<div class="mb-3">
|
||||
<label for="cover-artist" class="form-label">{{t('cover-artist-label')}}</label>
|
||||
<app-typeahead (selectedData)="updatePerson($event, PersonRole.CoverArtist)" [settings]="getPersonsSettings(PersonRole.CoverArtist)"
|
||||
<app-typeahead (selectedData)="updatePerson($event, PersonRole.CoverArtist);metadata.coverArtistLocked = true" [settings]="getPersonsSettings(PersonRole.CoverArtist)"
|
||||
[(locked)]="metadata.coverArtistLocked" (onUnlock)="metadata.coverArtistLocked = false"
|
||||
(newItemAdded)="metadata.coverArtistLocked = true" (selectedData)="metadata.coverArtistLocked = true">
|
||||
(newItemAdded)="metadata.coverArtistLocked = true">
|
||||
<ng-template #badgeItem let-item let-position="idx">
|
||||
{{item.name}}
|
||||
</ng-template>
|
||||
@ -218,9 +218,9 @@
|
||||
<div class="row g-0">
|
||||
<div class="mb-3">
|
||||
<label for="publisher" class="form-label">{{t('publisher-label')}}</label>
|
||||
<app-typeahead (selectedData)="updatePerson($event, PersonRole.Publisher)" [settings]="getPersonsSettings(PersonRole.Publisher)"
|
||||
<app-typeahead (selectedData)="updatePerson($event, PersonRole.Publisher);metadata.publisherLocked = true" [settings]="getPersonsSettings(PersonRole.Publisher)"
|
||||
[(locked)]="metadata.publisherLocked" (onUnlock)="metadata.publisherLocked = false"
|
||||
(newItemAdded)="metadata.publisherLocked = true" (selectedData)="metadata.publisherLocked = true">
|
||||
(newItemAdded)="metadata.publisherLocked = true">
|
||||
<ng-template #badgeItem let-item let-position="idx">
|
||||
{{item.name}}
|
||||
</ng-template>
|
||||
@ -233,9 +233,9 @@
|
||||
<div class="row g-0">
|
||||
<div class="mb-3">
|
||||
<label for="penciller" class="form-label">{{t('penciller-label')}}</label>
|
||||
<app-typeahead (selectedData)="updatePerson($event, PersonRole.Penciller)" [settings]="getPersonsSettings(PersonRole.Penciller)"
|
||||
<app-typeahead (selectedData)="updatePerson($event, PersonRole.Penciller);metadata.pencillerLocked = true" [settings]="getPersonsSettings(PersonRole.Penciller)"
|
||||
[(locked)]="metadata.pencillerLocked" (onUnlock)="metadata.pencillerLocked = false"
|
||||
(newItemAdded)="metadata.pencillerLocked = true" (selectedData)="metadata.pencillerLocked = true">
|
||||
(newItemAdded)="metadata.pencillerLocked = true">
|
||||
<ng-template #badgeItem let-item let-position="idx">
|
||||
{{item.name}}
|
||||
</ng-template>
|
||||
@ -250,9 +250,9 @@
|
||||
<div class="row g-0">
|
||||
<div class="mb-3">
|
||||
<label for="letterer" class="form-label">{{t('letterer-label')}}</label>
|
||||
<app-typeahead (selectedData)="updatePerson($event, PersonRole.Letterer)" [settings]="getPersonsSettings(PersonRole.Letterer)"
|
||||
<app-typeahead (selectedData)="updatePerson($event, PersonRole.Letterer);metadata.lettererLocked = true" [settings]="getPersonsSettings(PersonRole.Letterer)"
|
||||
[(locked)]="metadata.lettererLocked" (onUnlock)="metadata.lettererLocked = false"
|
||||
(newItemAdded)="metadata.lettererLocked = true" (selectedData)="metadata.lettererLocked = true">
|
||||
(newItemAdded)="metadata.lettererLocked = true">
|
||||
<ng-template #badgeItem let-item let-position="idx">
|
||||
{{item.name}}
|
||||
</ng-template>
|
||||
@ -265,9 +265,9 @@
|
||||
<div class="row g-0">
|
||||
<div class="mb-3">
|
||||
<label for="inker" class="form-label">{{t('inker-label')}}</label>
|
||||
<app-typeahead (selectedData)="updatePerson($event, PersonRole.Inker)" [settings]="getPersonsSettings(PersonRole.Inker)"
|
||||
<app-typeahead (selectedData)="updatePerson($event, PersonRole.Inker);metadata.inkerLocked = true" [settings]="getPersonsSettings(PersonRole.Inker)"
|
||||
[(locked)]="metadata.inkerLocked" (onUnlock)="metadata.inkerLocked = false"
|
||||
(newItemAdded)="metadata.inkerLocked = true" (selectedData)="metadata.inkerLocked = true">
|
||||
(newItemAdded)="metadata.inkerLocked = true">
|
||||
<ng-template #badgeItem let-item let-position="idx">
|
||||
{{item.name}}
|
||||
</ng-template>
|
||||
@ -283,9 +283,9 @@
|
||||
<div class="row g-0">
|
||||
<div class="mb-3">
|
||||
<label for="editor" class="form-label">{{t('editor-label')}}</label>
|
||||
<app-typeahead (selectedData)="updatePerson($event, PersonRole.Editor)" [settings]="getPersonsSettings(PersonRole.Editor)"
|
||||
<app-typeahead (selectedData)="updatePerson($event, PersonRole.Editor);metadata.editorLocked = true" [settings]="getPersonsSettings(PersonRole.Editor)"
|
||||
[(locked)]="metadata.editorLocked" (onUnlock)="metadata.editorLocked = false"
|
||||
(newItemAdded)="metadata.editorLocked = true" (selectedData)="metadata.editorLocked = true">
|
||||
(newItemAdded)="metadata.editorLocked = true">
|
||||
<ng-template #badgeItem let-item let-position="idx">
|
||||
{{item.name}}
|
||||
</ng-template>
|
||||
@ -298,9 +298,9 @@
|
||||
<div class="row g-0">
|
||||
<div class="mb-3">
|
||||
<label for="colorist" class="form-label">{{t('colorist-label')}}</label>
|
||||
<app-typeahead (selectedData)="updatePerson($event, PersonRole.Colorist)" [settings]="getPersonsSettings(PersonRole.Colorist)"
|
||||
<app-typeahead (selectedData)="updatePerson($event, PersonRole.Colorist);metadata.coloristLocked = true" [settings]="getPersonsSettings(PersonRole.Colorist)"
|
||||
[(locked)]="metadata.coloristLocked" (onUnlock)="metadata.coloristLocked = false"
|
||||
(newItemAdded)="metadata.coloristLocked = true" (selectedData)="metadata.coloristLocked = true">
|
||||
(newItemAdded)="metadata.coloristLocked = true">
|
||||
<ng-template #badgeItem let-item let-position="idx">
|
||||
{{item.name}}
|
||||
</ng-template>
|
||||
@ -316,9 +316,9 @@
|
||||
<div class="row g-0">
|
||||
<div class="mb-3">
|
||||
<label for="character" class="form-label">{{t('character-label')}}</label>
|
||||
<app-typeahead (selectedData)="updatePerson($event, PersonRole.Character)" [settings]="getPersonsSettings(PersonRole.Character)"
|
||||
<app-typeahead (selectedData)="updatePerson($event, PersonRole.Character);metadata.characterLocked = true" [settings]="getPersonsSettings(PersonRole.Character)"
|
||||
[(locked)]="metadata.characterLocked" (onUnlock)="metadata.characterLocked = false"
|
||||
(newItemAdded)="metadata.characterLocked = true" (selectedData)="metadata.characterLocked = true">
|
||||
(newItemAdded)="metadata.characterLocked = true">
|
||||
<ng-template #badgeItem let-item let-position="idx">
|
||||
{{item.name}}
|
||||
</ng-template>
|
||||
@ -331,9 +331,9 @@
|
||||
<div class="row g-0">
|
||||
<div class="mb-3">
|
||||
<label for="translator" class="form-label">{{t('translator-label')}}</label>
|
||||
<app-typeahead (selectedData)="updatePerson($event, PersonRole.Translator)" [settings]="getPersonsSettings(PersonRole.Translator)"
|
||||
<app-typeahead (selectedData)="updatePerson($event, PersonRole.Translator);metadata.translatorLocked = true;" [settings]="getPersonsSettings(PersonRole.Translator)"
|
||||
[(locked)]="metadata.translatorLocked" (onUnlock)="metadata.translatorLocked = false"
|
||||
(newItemAdded)="metadata.translatorLocked = true" (selectedData)="metadata.translatorLocked = true">
|
||||
(newItemAdded)="metadata.translatorLocked = true">
|
||||
<ng-template #badgeItem let-item let-position="idx">
|
||||
{{item.name}}
|
||||
</ng-template>
|
||||
|
@ -47,6 +47,15 @@ interface RelationControl {
|
||||
})
|
||||
export class EditSeriesRelationComponent implements OnInit {
|
||||
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
private readonly cdRef = inject(ChangeDetectorRef);
|
||||
private readonly seriesService = inject(SeriesService);
|
||||
private readonly utilityService = inject(UtilityService);
|
||||
private readonly libraryService = inject(LibraryService);
|
||||
private readonly searchService = inject(SearchService);
|
||||
public readonly imageService = inject(ImageService);
|
||||
protected readonly RelationKind = RelationKind;
|
||||
|
||||
@Input({required: true}) series!: Series;
|
||||
/**
|
||||
* This will tell the component to save based on its internal state
|
||||
@ -60,16 +69,6 @@ export class EditSeriesRelationComponent implements OnInit {
|
||||
libraryNames: {[key:number]: string} = {};
|
||||
|
||||
focusTypeahead = new EventEmitter();
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
|
||||
get RelationKind() {
|
||||
return RelationKind;
|
||||
}
|
||||
|
||||
|
||||
constructor(private seriesService: SeriesService, private utilityService: UtilityService,
|
||||
public imageService: ImageService, private libraryService: LibraryService, private searchService: SearchService,
|
||||
private readonly cdRef: ChangeDetectorRef) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.seriesService.getRelatedForSeries(this.series.id).subscribe(async relations => {
|
||||
|
@ -76,9 +76,8 @@ export class DoubleRendererComponent implements OnInit, ImageRenderer {
|
||||
shouldRenderDouble$!: Observable<boolean>;
|
||||
|
||||
|
||||
get ReaderMode() {return ReaderMode;}
|
||||
get LayoutMode() {return LayoutMode;}
|
||||
|
||||
protected readonly ReaderMode = ReaderMode;
|
||||
protected readonly LayoutMode = LayoutMode;
|
||||
|
||||
|
||||
constructor(private readonly cdRef: ChangeDetectorRef, public mangaReaderService: ManagaReaderService,
|
||||
|
@ -19,7 +19,7 @@
|
||||
<div>
|
||||
<div style="font-weight: bold;">{{title}} <span class="clickable" *ngIf="incognitoMode" (click)="turnOffIncognito()" role="button" [attr.aria-label]="t('incognito-alt')">(<i class="fa fa-glasses" aria-hidden="true"></i><span class="visually-hidden">{{t('incognito-title')}}</span>)</span></div>
|
||||
<div class="subtitle">
|
||||
{{subtitle}} <span *ngIf="totalSeriesPages > 0">{{t('series-progress', {percentage: (((totalSeriesPagesRead + pageNum) / totalSeriesPages) | percent)}) }}</span>
|
||||
{{subtitle}} <span *ngIf="totalSeriesPages > 0">{{t('series-progress', {percentage: (Math.min(1, ((totalSeriesPagesRead + pageNum) / totalSeriesPages)) | percent)}) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -36,7 +36,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<app-loading [loading]="isLoading" [absolute]="true"></app-loading>
|
||||
<app-loading [loading]="isLoading || !(currentImage$ | async)?.complete" [absolute]="true"></app-loading>
|
||||
<div class="reading-area"
|
||||
ngSwipe (swipeEnd)="onSwipeEnd($event)" (swipeMove)="onSwipeMove($event)"
|
||||
[ngStyle]="{'background-color': backgroundColor, 'height': readerMode === ReaderMode.Webtoon ? 'inherit' : 'calc(var(--vh)*100)'}" #readingArea>
|
||||
|
@ -13,7 +13,7 @@ import {
|
||||
OnInit,
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
import {DOCUMENT, NgStyle, NgIf, NgFor, NgSwitch, NgSwitchCase, PercentPipe, NgClass} from '@angular/common';
|
||||
import {DOCUMENT, NgStyle, NgIf, NgFor, NgSwitch, NgSwitchCase, PercentPipe, NgClass, AsyncPipe} from '@angular/common';
|
||||
import {ActivatedRoute, Router} from '@angular/router';
|
||||
import {
|
||||
BehaviorSubject,
|
||||
@ -68,6 +68,7 @@ import { InfiniteScrollerComponent } from '../infinite-scroller/infinite-scrolle
|
||||
import { SwipeDirective } from '../../../ng-swipe/ng-swipe.directive';
|
||||
import { LoadingComponent } from '../../../shared/loading/loading.component';
|
||||
import {translate, TranslocoDirective} from "@ngneat/transloco";
|
||||
import {shareReplay} from "rxjs/operators";
|
||||
|
||||
|
||||
const PREFETCH_PAGES = 10;
|
||||
@ -124,7 +125,7 @@ enum KeyDirection {
|
||||
imports: [NgStyle, NgIf, LoadingComponent, SwipeDirective, CanvasRendererComponent, SingleRendererComponent,
|
||||
DoubleRendererComponent, DoubleReverseRendererComponent, DoubleNoCoverRendererComponent, InfiniteScrollerComponent,
|
||||
NgxSliderModule, ReactiveFormsModule, NgFor, NgSwitch, NgSwitchCase, FittingIconPipe, ReaderModeIconPipe,
|
||||
FullscreenIconPipe, TranslocoDirective, NgbProgressbar, PercentPipe, NgClass]
|
||||
FullscreenIconPipe, TranslocoDirective, NgbProgressbar, PercentPipe, NgClass, AsyncPipe]
|
||||
})
|
||||
export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
|
||||
@ -158,6 +159,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
protected readonly LayoutMode = LayoutMode;
|
||||
protected readonly ReadingDirection = ReadingDirection;
|
||||
protected readonly Breakpoint = Breakpoint;
|
||||
protected readonly Math = Math;
|
||||
|
||||
libraryId!: number;
|
||||
seriesId!: number;
|
||||
@ -400,7 +402,9 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
// Renderer interaction
|
||||
readerSettings$!: Observable<ReaderSetting>;
|
||||
private currentImage: Subject<HTMLImageElement | null> = new ReplaySubject(1);
|
||||
currentImage$: Observable<HTMLImageElement | null> = this.currentImage.asObservable();
|
||||
currentImage$: Observable<HTMLImageElement | null> = this.currentImage.asObservable().pipe(
|
||||
shareReplay({refCount: true, bufferSize: 2})
|
||||
);
|
||||
|
||||
private pageNumSubject: Subject<{pageNum: number, maxPages: number}> = new ReplaySubject();
|
||||
pageNum$: Observable<{pageNum: number, maxPages: number}> = this.pageNumSubject.asObservable();
|
||||
@ -743,18 +747,34 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
* @param pageNum Page Number to load
|
||||
* @param forceNew Forces to fetch a new image
|
||||
* @param chapterId ChapterId to fetch page from. Defaults to current chapterId. Not used when in bookmark mode
|
||||
* @returns
|
||||
* @returns HTMLImageElement | undefined
|
||||
*/
|
||||
getPage(pageNum: number, chapterId: number = this.chapterId, forceNew: boolean = false) {
|
||||
|
||||
let img;
|
||||
let img: HTMLImageElement | undefined;
|
||||
if (this.bookmarkMode) img = this.cachedImages.find(img => this.readerService.imageUrlToPageNum(img.src) === pageNum);
|
||||
else img = this.cachedImages.find(img => this.readerService.imageUrlToPageNum(img.src) === pageNum
|
||||
&& (this.readerService.imageUrlToChapterId(img.src) == chapterId || this.readerService.imageUrlToChapterId(img.src) === -1)
|
||||
);
|
||||
|
||||
//console.log('Requesting page ', pageNum, ' found page: ', img, ' and app is requesting new image? ', forceNew);
|
||||
if (!img || forceNew) {
|
||||
img = new Image();
|
||||
img.src = this.getPageUrl(pageNum, chapterId);
|
||||
img.onload = (evt) => {
|
||||
this.currentImage.next(img!);
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
// img.onerror = (evt) => {
|
||||
// const event = evt as Event;
|
||||
// const page = this.readerService.imageUrlToPageNum((event.target as HTMLImageElement).src);
|
||||
// console.error('Image failed to load: ', page);
|
||||
// (event.target as HTMLImageElement).onerror = null;
|
||||
// const newSrc = this.getPageUrl(pageNum, chapterId) + '#' + new Date().getTime();
|
||||
// console.log('requesting page ', page, ' with url: ', newSrc);
|
||||
// (event.target as HTMLImageElement).src = newSrc;
|
||||
// this.cdRef.markForCheck();
|
||||
// }
|
||||
}
|
||||
|
||||
return img;
|
||||
@ -1224,6 +1244,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
*/
|
||||
setCanvasImage() {
|
||||
if (this.cachedImages === undefined) return;
|
||||
|
||||
this.canvasImage = this.getPage(this.pageNum, this.chapterId, this.layoutMode !== LayoutMode.Single);
|
||||
if (!this.canvasImage.complete) {
|
||||
this.canvasImage.addEventListener('load', () => {
|
||||
@ -1346,19 +1367,9 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
const index = (numOffset % this.cachedImages.length + this.cachedImages.length) % this.cachedImages.length;
|
||||
const cachedImagePageNum = this.readerService.imageUrlToPageNum(this.cachedImages[index].src);
|
||||
if (cachedImagePageNum !== numOffset) {
|
||||
this.cachedImages[index] = new Image();
|
||||
this.cachedImages[index].src = this.getPageUrl(numOffset);
|
||||
this.cachedImages[index].onload = (evt) => {
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
this.cachedImages[index] = this.getPage(numOffset, this.chapterId);
|
||||
}
|
||||
}
|
||||
|
||||
//const pages = this.cachedImages.map(img => [this.readerService.imageUrlToChapterId(img.src), this.readerService.imageUrlToPageNum(img.src)]);
|
||||
// console.log(this.pageNum, ' Prefetched pages: ', pages.map(p => {
|
||||
// if (this.pageNum === p[1]) return '[' + p + ']';
|
||||
// return '' + p
|
||||
// }));
|
||||
}
|
||||
|
||||
|
||||
@ -1681,4 +1692,5 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
d.text = translate('preferences.' + o.text);
|
||||
return d;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
<ng-container *ngIf="isValid() && !this.mangaReaderService.shouldSplit(this.currentImage, this.pageSplit)">
|
||||
<div class="image-container {{imageFitClass$ | async}} {{emulateBookClass$ | async}}"
|
||||
[style.filter]="(darkness$ | async) ?? '' | safeStyle" [style.height]="(imageContainerHeight$ | async) ?? '' | safeStyle">
|
||||
<ng-container *ngIf="currentImage">
|
||||
<img alt=" "
|
||||
#image [src]="currentImage.src"
|
||||
id="image-1"
|
||||
class="{{imageFitClass$ | async}} {{readerModeClass$ | async}} {{showClickOverlayClass$ | async}}"
|
||||
>
|
||||
</ng-container>
|
||||
</div>
|
||||
</ng-container>
|
||||
@if(isValid() && !mangaReaderService.shouldSplit(currentImage, pageSplit)) {
|
||||
<div class="image-container {{imageFitClass$ | async}} {{emulateBookClass$ | async}}"
|
||||
[style.filter]="(darkness$ | async) ?? '' | safeStyle" [style.height]="(imageContainerHeight$ | async) ?? '' | safeStyle">
|
||||
@if(currentImage) {
|
||||
<img alt=" "
|
||||
#image
|
||||
[src]="currentImage.src"
|
||||
id="image-1"
|
||||
class="{{imageFitClass$ | async}} {{readerModeClass$ | async}} {{showClickOverlayClass$ | async}}" />
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ import { SafeStylePipe } from '../../../_pipes/safe-style.pipe';
|
||||
styleUrls: ['./single-renderer.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
imports: [NgIf, AsyncPipe, SafeStylePipe]
|
||||
imports: [AsyncPipe, SafeStylePipe]
|
||||
})
|
||||
export class SingleRendererComponent implements OnInit, ImageRenderer {
|
||||
|
||||
@ -137,7 +137,7 @@ export class SingleRendererComponent implements OnInit, ImageRenderer {
|
||||
|
||||
return fit;
|
||||
}),
|
||||
shareReplay(),
|
||||
shareReplay({refCount: true, bufferSize: 1}),
|
||||
filter(_ => this.isValid()),
|
||||
takeUntilDestroyed(this.destroyRef),
|
||||
);
|
||||
|
@ -153,7 +153,8 @@ export class LibrarySettingsModalComponent implements OnInit {
|
||||
// This needs to only apply after first render
|
||||
this.libraryForm.get('type')?.valueChanges.pipe(
|
||||
tap((type: LibraryType) => {
|
||||
switch (type) {
|
||||
const libType = parseInt(type + '', 10) as LibraryType;
|
||||
switch (libType) {
|
||||
case LibraryType.Manga:
|
||||
this.libraryForm.get(FileTypeGroup.Archive + '')?.setValue(true);
|
||||
this.libraryForm.get(FileTypeGroup.Images + '')?.setValue(true);
|
||||
@ -177,6 +178,7 @@ export class LibrarySettingsModalComponent implements OnInit {
|
||||
this.libraryForm.get(FileTypeGroup.Images + '')?.setValue(true);
|
||||
this.libraryForm.get(FileTypeGroup.Pdf + '')?.setValue(false);
|
||||
this.libraryForm.get(FileTypeGroup.Epub + '')?.setValue(false);
|
||||
break;
|
||||
}
|
||||
}),
|
||||
takeUntilDestroyed(this.destroyRef)
|
||||
@ -203,15 +205,17 @@ export class LibrarySettingsModalComponent implements OnInit {
|
||||
for(let glob of this.library.excludePatterns) {
|
||||
this.libraryForm.addControl('excludeGlob-' , new FormControl(glob, []));
|
||||
}
|
||||
this.excludePatterns = this.library.excludePatterns;
|
||||
} else {
|
||||
for(let fileTypeGroup of allFileTypeGroup) {
|
||||
this.libraryForm.addControl(fileTypeGroup + '', new FormControl(true, []));
|
||||
}
|
||||
}
|
||||
this.excludePatterns = this.library.excludePatterns;
|
||||
|
||||
if (this.excludePatterns.length === 0) {
|
||||
this.excludePatterns = [''];
|
||||
}
|
||||
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user