mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-05-31 04:04:19 -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);
|
var userId = await GetUser(apiKey);
|
||||||
if (!(await _unitOfWork.SettingsRepository.GetSettingsDtoAsync()).EnableOpds)
|
if (!(await _unitOfWork.SettingsRepository.GetSettingsDtoAsync()).EnableOpds)
|
||||||
return BadRequest(await _localizationService.Translate(userId, "opds-disabled"));
|
return BadRequest(await _localizationService.Translate(userId, "opds-disabled"));
|
||||||
var (baseUrl, prefix) = await GetPrefix();
|
var (_, prefix) = await GetPrefix();
|
||||||
|
|
||||||
var filters = _unitOfWork.AppUserSmartFilterRepository.GetAllDtosByUserId(userId);
|
var filters = _unitOfWork.AppUserSmartFilterRepository.GetAllDtosByUserId(userId);
|
||||||
var feed = CreateFeed(await _localizationService.Translate(userId, "smartFilters"), $"{prefix}{apiKey}/smart-filters", apiKey, prefix);
|
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);
|
var userId = await GetUser(apiKey);
|
||||||
if (!(await _unitOfWork.SettingsRepository.GetSettingsDtoAsync()).EnableOpds)
|
if (!(await _unitOfWork.SettingsRepository.GetSettingsDtoAsync()).EnableOpds)
|
||||||
return BadRequest(await _localizationService.Translate(userId, "opds-disabled"));
|
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 externalSources = await _unitOfWork.AppUserExternalSourceRepository.GetExternalSources(userId);
|
||||||
var feed = CreateFeed(await _localizationService.Translate(userId, "external-sources"), $"{prefix}{apiKey}/external-sources", apiKey, prefix);
|
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)
|
if (!(await _unitOfWork.SettingsRepository.GetSettingsDtoAsync()).EnableOpds)
|
||||||
return BadRequest(await _localizationService.Translate(userId, "opds-disabled"));
|
return BadRequest(await _localizationService.Translate(userId, "opds-disabled"));
|
||||||
var (baseUrl, prefix) = await GetPrefix();
|
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);
|
var feed = CreateFeed(await _localizationService.Translate(userId, "libraries"), $"{prefix}{apiKey}/libraries", apiKey, prefix);
|
||||||
SetFeedId(feed, "libraries");
|
SetFeedId(feed, "libraries");
|
||||||
|
|
||||||
// Ensure libraries follow SideNav order
|
// Ensure libraries follow SideNav order
|
||||||
var userSideNavStreams = await _unitOfWork.UserRepository.GetSideNavStreams(userId, false);
|
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()
|
feed.Entries.Add(new FeedEntry()
|
||||||
{
|
{
|
||||||
Id = library!.Id.ToString(),
|
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),
|
var chapters = (await _unitOfWork.ChapterRepository.GetChaptersAsync(volume.Id)).OrderBy(x => double.Parse(x.Number, CultureInfo.InvariantCulture),
|
||||||
_chapterSortComparer);
|
_chapterSortComparer);
|
||||||
|
|
||||||
foreach (var chapter in chapters)
|
foreach (var chapterId in chapters.Select(c => c.Id))
|
||||||
{
|
{
|
||||||
var files = await _unitOfWork.ChapterRepository.GetFilesForChapterAsync(chapter.Id);
|
var files = await _unitOfWork.ChapterRepository.GetFilesForChapterAsync(chapterId);
|
||||||
var chapterTest = await _unitOfWork.ChapterRepository.GetChapterDtoAsync(chapter.Id);
|
var chapterTest = await _unitOfWork.ChapterRepository.GetChapterDtoAsync(chapterId);
|
||||||
foreach (var mangaFile in files)
|
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>
|
/// <param name="extractPdf">Should Kavita extract pdf into images. Defaults to false.</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpGet("image")]
|
[HttpGet("image")]
|
||||||
[ResponseCache(CacheProfileName = ResponseCacheProfiles.Hour, VaryByQueryKeys = new []{"chapterId","page", "extractPdf", "apiKey"})]
|
[ResponseCache(CacheProfileName = ResponseCacheProfiles.Hour, VaryByQueryKeys = new []{"chapterId", "page", "extractPdf", "apiKey"})]
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
public async Task<ActionResult> GetImage(int chapterId, int page, string apiKey, bool extractPdf = false)
|
public async Task<ActionResult> GetImage(int chapterId, int page, string apiKey, bool extractPdf = false)
|
||||||
{
|
{
|
||||||
if (page < 0) page = 0;
|
if (page < 0) page = 0;
|
||||||
var userId = await _unitOfWork.UserRepository.GetUserIdByApiKeyAsync(apiKey);
|
var userId = await _unitOfWork.UserRepository.GetUserIdByApiKeyAsync(apiKey);
|
||||||
if (userId == 0) return BadRequest();
|
if (userId == 0) return BadRequest();
|
||||||
var chapter = await _cacheService.Ensure(chapterId, extractPdf);
|
|
||||||
if (chapter == null) return NoContent();
|
|
||||||
|
|
||||||
try
|
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);
|
var path = _cacheService.GetCachedPagePath(chapter.Id, page);
|
||||||
if (string.IsNullOrEmpty(path) || !System.IO.File.Exists(path))
|
if (string.IsNullOrEmpty(path) || !System.IO.File.Exists(path))
|
||||||
return BadRequest(await _localizationService.Translate(userId, "no-image-for-page", page));
|
return BadRequest(await _localizationService.Translate(userId, "no-image-for-page", page));
|
||||||
@ -245,8 +250,8 @@ public class ReaderController : BaseApiController
|
|||||||
LibraryId = dto.LibraryId,
|
LibraryId = dto.LibraryId,
|
||||||
IsSpecial = dto.IsSpecial,
|
IsSpecial = dto.IsSpecial,
|
||||||
Pages = dto.Pages,
|
Pages = dto.Pages,
|
||||||
SeriesTotalPages = series?.Pages ?? 0,
|
SeriesTotalPages = series.Pages,
|
||||||
SeriesTotalPagesRead = series?.PagesRead ?? 0,
|
SeriesTotalPagesRead = series.PagesRead,
|
||||||
ChapterTitle = dto.ChapterTitle ?? string.Empty,
|
ChapterTitle = dto.ChapterTitle ?? string.Empty,
|
||||||
Subtitle = string.Empty,
|
Subtitle = string.Empty,
|
||||||
Title = dto.SeriesName,
|
Title = dto.SeriesName,
|
||||||
|
@ -280,7 +280,7 @@ public class LibraryRepository : ILibraryRepository
|
|||||||
{
|
{
|
||||||
Title = s,
|
Title = s,
|
||||||
IsoCode = s
|
IsoCode = s
|
||||||
};;
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<PublicationStatusDto> GetAllPublicationStatusesDtosForLibrariesAsync(List<int> libraryIds)
|
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
|
/// Scans a directory by utilizing a recursive folder search. If a .kavitaignore file is found, will ignore matching patterns
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="folderPath"></param>
|
/// <param name="folderPath"></param>
|
||||||
|
/// <param name="supportedExtensions"></param>
|
||||||
/// <param name="matcher"></param>
|
/// <param name="matcher"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public IList<string> ScanFiles(string folderPath, string supportedExtensions, GlobMatcher? matcher = null)
|
public IList<string> ScanFiles(string folderPath, string supportedExtensions, GlobMatcher? matcher = null)
|
||||||
|
@ -398,7 +398,7 @@ public class ReaderService : IReaderService
|
|||||||
// Handle Chapters within next Volume
|
// Handle Chapters within next Volume
|
||||||
// ! When selecting the chapter for the next volume, we need to make sure a c0 comes before a c1+
|
// ! 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();
|
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
|
// 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;
|
if (currentChapter.IsSpecial) return -1;
|
||||||
|
@ -813,7 +813,7 @@ public class SeriesService : ISeriesService
|
|||||||
|
|
||||||
private static double ExponentialSmoothing(IList<double> data, double alpha)
|
private static double ExponentialSmoothing(IList<double> data, double alpha)
|
||||||
{
|
{
|
||||||
var forecast = data.First();
|
var forecast = data[0];
|
||||||
|
|
||||||
foreach (var value in data)
|
foreach (var value in data)
|
||||||
{
|
{
|
||||||
|
@ -71,7 +71,7 @@ public class StreamService : IStreamService
|
|||||||
var smartFilter = await _unitOfWork.AppUserSmartFilterRepository.GetById(smartFilterId);
|
var smartFilter = await _unitOfWork.AppUserSmartFilterRepository.GetById(smartFilterId);
|
||||||
if (smartFilter == null) throw new KavitaException(await _localizationService.Translate(userId, "smart-filter-doesnt-exist"));
|
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"));
|
if (stream != null) throw new KavitaException(await _localizationService.Translate(userId, "smart-filter-already-in-use"));
|
||||||
|
|
||||||
var maxOrder = user!.DashboardStreams.Max(d => d.Order);
|
var maxOrder = user!.DashboardStreams.Max(d => d.Order);
|
||||||
@ -159,7 +159,7 @@ public class StreamService : IStreamService
|
|||||||
var smartFilter = await _unitOfWork.AppUserSmartFilterRepository.GetById(smartFilterId);
|
var smartFilter = await _unitOfWork.AppUserSmartFilterRepository.GetById(smartFilterId);
|
||||||
if (smartFilter == null) throw new KavitaException(await _localizationService.Translate(userId, "smart-filter-doesnt-exist"));
|
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"));
|
if (stream != null) throw new KavitaException(await _localizationService.Translate(userId, "smart-filter-already-in-use"));
|
||||||
|
|
||||||
var maxOrder = user!.SideNavStreams.Max(d => d.Order);
|
var maxOrder = user!.SideNavStreams.Max(d => d.Order);
|
||||||
|
@ -72,7 +72,7 @@ public class TachiyomiService : ITachiyomiService
|
|||||||
if (looseLeafChapterVolume == null)
|
if (looseLeafChapterVolume == null)
|
||||||
{
|
{
|
||||||
var volumeChapter = _mapper.Map<ChapterDto>(volumes
|
var volumeChapter = _mapper.Map<ChapterDto>(volumes
|
||||||
.Last().Chapters
|
[^1].Chapters
|
||||||
.OrderBy(c => c.Number.AsFloat(), ChapterSortComparerZeroFirst.Default)
|
.OrderBy(c => c.Number.AsFloat(), ChapterSortComparerZeroFirst.Default)
|
||||||
.Last());
|
.Last());
|
||||||
if (volumeChapter.Number == "0")
|
if (volumeChapter.Number == "0")
|
||||||
|
@ -179,7 +179,7 @@ public class LibraryWatcher : ILibraryWatcher
|
|||||||
/// <param name="e"></param>
|
/// <param name="e"></param>
|
||||||
private void OnError(object sender, ErrorEventArgs e)
|
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;
|
bool condition;
|
||||||
lock (Lock)
|
lock (Lock)
|
||||||
{
|
{
|
||||||
|
@ -312,7 +312,6 @@ public class ParseScannedFiles
|
|||||||
}
|
}
|
||||||
|
|
||||||
await _eventHub.SendMessageAsync(MessageFactory.NotificationProgress, MessageFactory.FileScanProgressEvent("File Scan Done", library.Name, ProgressEventType.Ended));
|
await _eventHub.SendMessageAsync(MessageFactory.NotificationProgress, MessageFactory.FileScanProgressEvent("File Scan Done", library.Name, ProgressEventType.Ended));
|
||||||
return;
|
|
||||||
|
|
||||||
async Task ProcessFolder(IList<string> files, string folder)
|
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)
|
if (series.Format == MangaFormat.Epub || series.Format == MangaFormat.Pdf && chapters.Count == 1)
|
||||||
{
|
{
|
||||||
series.Metadata.MaxCount = 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
|
// If a series has a TotalCount of 1 and there is only a Special, mark it as Complete
|
||||||
series.Metadata.MaxCount = series.Metadata.TotalCount;
|
series.Metadata.MaxCount = series.Metadata.TotalCount;
|
||||||
|
@ -347,6 +347,7 @@ public class Startup
|
|||||||
=>
|
=>
|
||||||
{
|
{
|
||||||
opts.EnrichDiagnosticContext = LogEnricher.EnrichFromRequest;
|
opts.EnrichDiagnosticContext = LogEnricher.EnrichFromRequest;
|
||||||
|
opts.IncludeQueryInRequestPath = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
app.Use(async (context, next) =>
|
app.Use(async (context, next) =>
|
||||||
|
@ -17,7 +17,7 @@ export enum RelationKind {
|
|||||||
Edition = 13
|
Edition = 13
|
||||||
}
|
}
|
||||||
|
|
||||||
export const RelationKinds = [
|
const RelationKindsUnsorted = [
|
||||||
{text: 'Prequel', value: RelationKind.Prequel},
|
{text: 'Prequel', value: RelationKind.Prequel},
|
||||||
{text: 'Sequel', value: RelationKind.Sequel},
|
{text: 'Sequel', value: RelationKind.Sequel},
|
||||||
{text: 'Spin Off', value: RelationKind.SpinOff},
|
{text: 'Spin Off', value: RelationKind.SpinOff},
|
||||||
@ -31,3 +31,5 @@ export const RelationKinds = [
|
|||||||
{text: 'Doujinshi', value: RelationKind.Doujinshi},
|
{text: 'Doujinshi', value: RelationKind.Doujinshi},
|
||||||
{text: 'Other', value: RelationKind.Other},
|
{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}) pageNumber: number = 0;
|
||||||
@Input({required: true}) parent: ElementRef | undefined;
|
@Input({required: true}) parent: ElementRef | undefined;
|
||||||
@Output() refreshToC: EventEmitter<void> = new EventEmitter();
|
@Output() refreshToC: EventEmitter<void> = new EventEmitter();
|
||||||
|
@Output() isOpen: EventEmitter<boolean> = new EventEmitter(false);
|
||||||
|
|
||||||
xPath: string = '';
|
xPath: string = '';
|
||||||
selectedText: string = '';
|
selectedText: string = '';
|
||||||
@ -84,6 +85,8 @@ export class BookLineOverlayComponent implements OnInit {
|
|||||||
if (!event.target) return;
|
if (!event.target) return;
|
||||||
|
|
||||||
if ((!selection || selection.toString().trim() === '' || selection.toString().trim() === this.selectedText)) {
|
if ((!selection || selection.toString().trim() === '' || selection.toString().trim() === this.selectedText)) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
this.reset();
|
this.reset();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -96,6 +99,7 @@ export class BookLineOverlayComponent implements OnInit {
|
|||||||
this.xPath = '//' + this.xPath;
|
this.xPath = '//' + this.xPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.isOpen.emit(true);
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
}
|
}
|
||||||
@ -137,6 +141,7 @@ export class BookLineOverlayComponent implements OnInit {
|
|||||||
if (selection) {
|
if (selection) {
|
||||||
selection.removeAllRanges();
|
selection.removeAllRanges();
|
||||||
}
|
}
|
||||||
|
this.isOpen.emit(false);
|
||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,13 +3,14 @@
|
|||||||
<ng-container *transloco="let t; read: 'book-reader'">
|
<ng-container *transloco="let t; read: 'book-reader'">
|
||||||
<div class="fixed-top" #stickyTop>
|
<div class="fixed-top" #stickyTop>
|
||||||
<a class="visually-hidden-focusable focus-visible" href="javascript:void(0);" (click)="moveFocus()">{{t('skip-header')}}</a>
|
<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"
|
<app-book-line-overlay [parent]="bookContainerElemRef" *ngIf="page !== undefined"
|
||||||
[libraryId]="libraryId"
|
[libraryId]="libraryId"
|
||||||
[volumeId]="volumeId"
|
[volumeId]="volumeId"
|
||||||
[chapterId]="chapterId"
|
[chapterId]="chapterId"
|
||||||
[seriesId]="seriesId"
|
[seriesId]="seriesId"
|
||||||
[pageNumber]="pageNum"
|
[pageNumber]="pageNum"
|
||||||
|
(isOpen)="updateLineOverlayOpen($event)"
|
||||||
(refreshToC)="refreshPersonalToC()">
|
(refreshToC)="refreshPersonalToC()">
|
||||||
</app-book-line-overlay>
|
</app-book-line-overlay>
|
||||||
<app-drawer #commentDrawer="drawer" [(isOpen)]="drawerOpen" [options]="{topOffset: topOffset}">
|
<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>
|
<span style="font-size: 14px; color: var(--primary-color)" tabindex="0" role="button" (click)="closeReader()">{{t('close-reader')}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div subheader>
|
<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">
|
<div class="pagination-cont">
|
||||||
<ng-container *ngIf="layoutMode !== BookPageLayoutMode.Default">
|
<ng-container *ngIf="layoutMode !== BookPageLayoutMode.Default">
|
||||||
<div class="virt-pagination-cont">
|
<div class="virt-pagination-cont">
|
||||||
@ -129,13 +125,14 @@
|
|||||||
<div *ngIf="page !== undefined && (scrollbarNeeded || layoutMode !== BookPageLayoutMode.Default) && !(writingStyle === WritingStyle.Vertical && layoutMode === BookPageLayoutMode.Default)"
|
<div *ngIf="page !== undefined && (scrollbarNeeded || layoutMode !== BookPageLayoutMode.Default) && !(writingStyle === WritingStyle.Vertical && layoutMode === BookPageLayoutMode.Default)"
|
||||||
(click)="$event.stopPropagation();"
|
(click)="$event.stopPropagation();"
|
||||||
[ngClass]="{'bottom-bar': layoutMode !== BookPageLayoutMode.Default}">
|
[ngClass]="{'bottom-bar': layoutMode !== BookPageLayoutMode.Default}">
|
||||||
<ng-container [ngTemplateOutlet]="actionBar"></ng-container>
|
<ng-container [ngTemplateOutlet]="actionBar" [ngTemplateOutletContext]="{isTop: false}"></ng-container>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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">
|
<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"
|
[disabled]="readingDirection === ReadingDirection.LeftToRight ? IsPrevDisabled : IsNextDisabled"
|
||||||
title="{{readingDirection === ReadingDirection.LeftToRight ? t('previous') : t('next')}} Page">
|
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>
|
<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()">
|
<button class="btn btn-secondary col-2 col-xs-1" (click)="toggleDrawer()">
|
||||||
<i class="fa fa-bars" aria-hidden="true"></i></button>
|
<i class="fa fa-bars" aria-hidden="true"></i></button>
|
||||||
<div class="book-title col-2 d-none d-sm-block">
|
<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">
|
<div class="spinner-border spinner-border-sm text-primary" style="border-radius: 50%;" role="status">
|
||||||
<span class="visually-hidden">{{t('loading-book')}}</span>
|
<span class="visually-hidden">{{t('loading-book')}}</span>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
} @else {
|
||||||
<ng-template #showTitle>
|
<span *ngIf="incognitoMode" (click)="turnOffIncognito()" role="button" [attr.aria-label]="t('incognito-mode-alt')">
|
||||||
<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>
|
(<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>
|
<span class="book-title-text ms-1" [ngbTooltip]="bookTitle">{{bookTitle}}</span>
|
||||||
</ng-template>
|
}
|
||||||
</div>
|
</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-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"
|
<button class="btn btn-outline-secondary btn-icon col-2 col-xs-1"
|
||||||
[disabled]="readingDirection === ReadingDirection.LeftToRight ? IsNextDisabled : IsPrevDisabled"
|
[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">
|
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>
|
<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>
|
</button>
|
||||||
|
@ -185,6 +185,10 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
* Belongs to drawer component
|
* Belongs to drawer component
|
||||||
*/
|
*/
|
||||||
drawerOpen = false;
|
drawerOpen = false;
|
||||||
|
/**
|
||||||
|
* If the word/line overlay is open
|
||||||
|
*/
|
||||||
|
isLineOverlayOpen = false;
|
||||||
/**
|
/**
|
||||||
* If the action bar is visible
|
* If the action bar is visible
|
||||||
*/
|
*/
|
||||||
@ -1630,20 +1634,21 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Responsible for handling pagination only
|
|
||||||
handleContainerClick(event: MouseEvent) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isCursorOverLeftPaginationArea(event)) {
|
if (this.clickToPaginate) {
|
||||||
this.movePage(this.readingDirection === ReadingDirection.LeftToRight ? PAGING_DIRECTION.BACKWARDS : PAGING_DIRECTION.FORWARD);
|
if (this.isCursorOverLeftPaginationArea(event)) {
|
||||||
} else if (this.isCursorOverRightPaginationArea(event)) {
|
this.movePage(this.readingDirection === ReadingDirection.LeftToRight ? PAGING_DIRECTION.BACKWARDS : PAGING_DIRECTION.FORWARD);
|
||||||
this.movePage(this.readingDirection === ReadingDirection.LeftToRight ? PAGING_DIRECTION.FORWARD : PAGING_DIRECTION.BACKWARDS)
|
} else if (this.isCursorOverRightPaginationArea(event)) {
|
||||||
} else {
|
this.movePage(this.readingDirection === ReadingDirection.LeftToRight ? PAGING_DIRECTION.FORWARD : PAGING_DIRECTION.BACKWARDS)
|
||||||
this.toggleMenu(event);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.toggleMenu(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleReaderClick(event: MouseEvent) {
|
handleReaderClick(event: MouseEvent) {
|
||||||
@ -1706,4 +1711,13 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
refreshPersonalToC() {
|
refreshPersonalToC() {
|
||||||
this.refreshPToC.emit();
|
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="col-md-12">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="genres" class="form-label">{{t('genres-label')}}</label>
|
<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"
|
[(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">
|
<ng-template #badgeItem let-item let-position="idx">
|
||||||
{{item.title}}
|
{{item.title}}
|
||||||
</ng-template>
|
</ng-template>
|
||||||
@ -142,9 +142,9 @@
|
|||||||
<div class="col-lg-4 col-md-12 pe-2">
|
<div class="col-lg-4 col-md-12 pe-2">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="language" class="form-label">{{t('language-label')}}</label>
|
<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"
|
[(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">
|
<ng-template #badgeItem let-item let-position="idx">
|
||||||
{{item.title}}
|
{{item.title}}
|
||||||
</ng-template>
|
</ng-template>
|
||||||
@ -159,7 +159,7 @@
|
|||||||
<label for="age-rating" class="form-label">{{t('age-rating-label')}}</label>
|
<label for="age-rating" class="form-label">{{t('age-rating-label')}}</label>
|
||||||
<div class="input-group {{metadata.ageRatingLocked ? 'lock-active' : ''}}">
|
<div class="input-group {{metadata.ageRatingLocked ? 'lock-active' : ''}}">
|
||||||
<ng-container [ngTemplateOutlet]="lock" [ngTemplateOutletContext]="{ item: metadata, field: 'ageRatingLocked' }"></ng-container>
|
<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>
|
<option *ngFor="let opt of ageRatings" [value]="opt.value">{{opt.title | titlecase}}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
@ -170,7 +170,7 @@
|
|||||||
<label for="publication-status" class="form-label">{{t('publication-status-label')}}</label>
|
<label for="publication-status" class="form-label">{{t('publication-status-label')}}</label>
|
||||||
<div class="input-group {{metadata.publicationStatusLocked ? 'lock-active' : ''}}">
|
<div class="input-group {{metadata.publicationStatusLocked ? 'lock-active' : ''}}">
|
||||||
<ng-container [ngTemplateOutlet]="lock" [ngTemplateOutletContext]="{ item: metadata, field: 'publicationStatusLocked' }"></ng-container>
|
<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>
|
<option *ngFor="let opt of publicationStatuses" [value]="opt.value">{{opt.title | titlecase}}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
@ -186,9 +186,9 @@
|
|||||||
<div class="row g-0">
|
<div class="row g-0">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="writer" class="form-label">{{t('writer-label')}}</label>
|
<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"
|
[(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">
|
<ng-template #badgeItem let-item let-position="idx">
|
||||||
{{item.name}}
|
{{item.name}}
|
||||||
</ng-template>
|
</ng-template>
|
||||||
@ -201,9 +201,9 @@
|
|||||||
<div class="row g-0">
|
<div class="row g-0">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="cover-artist" class="form-label">{{t('cover-artist-label')}}</label>
|
<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"
|
[(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">
|
<ng-template #badgeItem let-item let-position="idx">
|
||||||
{{item.name}}
|
{{item.name}}
|
||||||
</ng-template>
|
</ng-template>
|
||||||
@ -218,9 +218,9 @@
|
|||||||
<div class="row g-0">
|
<div class="row g-0">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="publisher" class="form-label">{{t('publisher-label')}}</label>
|
<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"
|
[(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">
|
<ng-template #badgeItem let-item let-position="idx">
|
||||||
{{item.name}}
|
{{item.name}}
|
||||||
</ng-template>
|
</ng-template>
|
||||||
@ -233,9 +233,9 @@
|
|||||||
<div class="row g-0">
|
<div class="row g-0">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="penciller" class="form-label">{{t('penciller-label')}}</label>
|
<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"
|
[(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">
|
<ng-template #badgeItem let-item let-position="idx">
|
||||||
{{item.name}}
|
{{item.name}}
|
||||||
</ng-template>
|
</ng-template>
|
||||||
@ -250,9 +250,9 @@
|
|||||||
<div class="row g-0">
|
<div class="row g-0">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="letterer" class="form-label">{{t('letterer-label')}}</label>
|
<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"
|
[(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">
|
<ng-template #badgeItem let-item let-position="idx">
|
||||||
{{item.name}}
|
{{item.name}}
|
||||||
</ng-template>
|
</ng-template>
|
||||||
@ -265,9 +265,9 @@
|
|||||||
<div class="row g-0">
|
<div class="row g-0">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="inker" class="form-label">{{t('inker-label')}}</label>
|
<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"
|
[(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">
|
<ng-template #badgeItem let-item let-position="idx">
|
||||||
{{item.name}}
|
{{item.name}}
|
||||||
</ng-template>
|
</ng-template>
|
||||||
@ -283,9 +283,9 @@
|
|||||||
<div class="row g-0">
|
<div class="row g-0">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="editor" class="form-label">{{t('editor-label')}}</label>
|
<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"
|
[(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">
|
<ng-template #badgeItem let-item let-position="idx">
|
||||||
{{item.name}}
|
{{item.name}}
|
||||||
</ng-template>
|
</ng-template>
|
||||||
@ -298,9 +298,9 @@
|
|||||||
<div class="row g-0">
|
<div class="row g-0">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="colorist" class="form-label">{{t('colorist-label')}}</label>
|
<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"
|
[(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">
|
<ng-template #badgeItem let-item let-position="idx">
|
||||||
{{item.name}}
|
{{item.name}}
|
||||||
</ng-template>
|
</ng-template>
|
||||||
@ -316,9 +316,9 @@
|
|||||||
<div class="row g-0">
|
<div class="row g-0">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="character" class="form-label">{{t('character-label')}}</label>
|
<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"
|
[(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">
|
<ng-template #badgeItem let-item let-position="idx">
|
||||||
{{item.name}}
|
{{item.name}}
|
||||||
</ng-template>
|
</ng-template>
|
||||||
@ -331,9 +331,9 @@
|
|||||||
<div class="row g-0">
|
<div class="row g-0">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="translator" class="form-label">{{t('translator-label')}}</label>
|
<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"
|
[(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">
|
<ng-template #badgeItem let-item let-position="idx">
|
||||||
{{item.name}}
|
{{item.name}}
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
@ -47,6 +47,15 @@ interface RelationControl {
|
|||||||
})
|
})
|
||||||
export class EditSeriesRelationComponent implements OnInit {
|
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;
|
@Input({required: true}) series!: Series;
|
||||||
/**
|
/**
|
||||||
* This will tell the component to save based on its internal state
|
* 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} = {};
|
libraryNames: {[key:number]: string} = {};
|
||||||
|
|
||||||
focusTypeahead = new EventEmitter();
|
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 {
|
ngOnInit(): void {
|
||||||
this.seriesService.getRelatedForSeries(this.series.id).subscribe(async relations => {
|
this.seriesService.getRelatedForSeries(this.series.id).subscribe(async relations => {
|
||||||
|
@ -76,9 +76,8 @@ export class DoubleRendererComponent implements OnInit, ImageRenderer {
|
|||||||
shouldRenderDouble$!: Observable<boolean>;
|
shouldRenderDouble$!: Observable<boolean>;
|
||||||
|
|
||||||
|
|
||||||
get ReaderMode() {return ReaderMode;}
|
protected readonly ReaderMode = ReaderMode;
|
||||||
get LayoutMode() {return LayoutMode;}
|
protected readonly LayoutMode = LayoutMode;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
constructor(private readonly cdRef: ChangeDetectorRef, public mangaReaderService: ManagaReaderService,
|
constructor(private readonly cdRef: ChangeDetectorRef, public mangaReaderService: ManagaReaderService,
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
<div>
|
<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 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">
|
<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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -36,7 +36,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</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"
|
<div class="reading-area"
|
||||||
ngSwipe (swipeEnd)="onSwipeEnd($event)" (swipeMove)="onSwipeMove($event)"
|
ngSwipe (swipeEnd)="onSwipeEnd($event)" (swipeMove)="onSwipeMove($event)"
|
||||||
[ngStyle]="{'background-color': backgroundColor, 'height': readerMode === ReaderMode.Webtoon ? 'inherit' : 'calc(var(--vh)*100)'}" #readingArea>
|
[ngStyle]="{'background-color': backgroundColor, 'height': readerMode === ReaderMode.Webtoon ? 'inherit' : 'calc(var(--vh)*100)'}" #readingArea>
|
||||||
|
@ -13,7 +13,7 @@ import {
|
|||||||
OnInit,
|
OnInit,
|
||||||
ViewChild
|
ViewChild
|
||||||
} from '@angular/core';
|
} 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 {ActivatedRoute, Router} from '@angular/router';
|
||||||
import {
|
import {
|
||||||
BehaviorSubject,
|
BehaviorSubject,
|
||||||
@ -68,6 +68,7 @@ import { InfiniteScrollerComponent } from '../infinite-scroller/infinite-scrolle
|
|||||||
import { SwipeDirective } from '../../../ng-swipe/ng-swipe.directive';
|
import { SwipeDirective } from '../../../ng-swipe/ng-swipe.directive';
|
||||||
import { LoadingComponent } from '../../../shared/loading/loading.component';
|
import { LoadingComponent } from '../../../shared/loading/loading.component';
|
||||||
import {translate, TranslocoDirective} from "@ngneat/transloco";
|
import {translate, TranslocoDirective} from "@ngneat/transloco";
|
||||||
|
import {shareReplay} from "rxjs/operators";
|
||||||
|
|
||||||
|
|
||||||
const PREFETCH_PAGES = 10;
|
const PREFETCH_PAGES = 10;
|
||||||
@ -124,7 +125,7 @@ enum KeyDirection {
|
|||||||
imports: [NgStyle, NgIf, LoadingComponent, SwipeDirective, CanvasRendererComponent, SingleRendererComponent,
|
imports: [NgStyle, NgIf, LoadingComponent, SwipeDirective, CanvasRendererComponent, SingleRendererComponent,
|
||||||
DoubleRendererComponent, DoubleReverseRendererComponent, DoubleNoCoverRendererComponent, InfiniteScrollerComponent,
|
DoubleRendererComponent, DoubleReverseRendererComponent, DoubleNoCoverRendererComponent, InfiniteScrollerComponent,
|
||||||
NgxSliderModule, ReactiveFormsModule, NgFor, NgSwitch, NgSwitchCase, FittingIconPipe, ReaderModeIconPipe,
|
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 {
|
export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
|
|
||||||
@ -158,6 +159,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
protected readonly LayoutMode = LayoutMode;
|
protected readonly LayoutMode = LayoutMode;
|
||||||
protected readonly ReadingDirection = ReadingDirection;
|
protected readonly ReadingDirection = ReadingDirection;
|
||||||
protected readonly Breakpoint = Breakpoint;
|
protected readonly Breakpoint = Breakpoint;
|
||||||
|
protected readonly Math = Math;
|
||||||
|
|
||||||
libraryId!: number;
|
libraryId!: number;
|
||||||
seriesId!: number;
|
seriesId!: number;
|
||||||
@ -400,7 +402,9 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
// Renderer interaction
|
// Renderer interaction
|
||||||
readerSettings$!: Observable<ReaderSetting>;
|
readerSettings$!: Observable<ReaderSetting>;
|
||||||
private currentImage: Subject<HTMLImageElement | null> = new ReplaySubject(1);
|
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();
|
private pageNumSubject: Subject<{pageNum: number, maxPages: number}> = new ReplaySubject();
|
||||||
pageNum$: Observable<{pageNum: number, maxPages: number}> = this.pageNumSubject.asObservable();
|
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 pageNum Page Number to load
|
||||||
* @param forceNew Forces to fetch a new image
|
* @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
|
* @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) {
|
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);
|
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
|
else img = this.cachedImages.find(img => this.readerService.imageUrlToPageNum(img.src) === pageNum
|
||||||
&& (this.readerService.imageUrlToChapterId(img.src) == chapterId || this.readerService.imageUrlToChapterId(img.src) === -1)
|
&& (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) {
|
if (!img || forceNew) {
|
||||||
img = new Image();
|
img = new Image();
|
||||||
img.src = this.getPageUrl(pageNum, chapterId);
|
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;
|
return img;
|
||||||
@ -1224,6 +1244,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
*/
|
*/
|
||||||
setCanvasImage() {
|
setCanvasImage() {
|
||||||
if (this.cachedImages === undefined) return;
|
if (this.cachedImages === undefined) return;
|
||||||
|
|
||||||
this.canvasImage = this.getPage(this.pageNum, this.chapterId, this.layoutMode !== LayoutMode.Single);
|
this.canvasImage = this.getPage(this.pageNum, this.chapterId, this.layoutMode !== LayoutMode.Single);
|
||||||
if (!this.canvasImage.complete) {
|
if (!this.canvasImage.complete) {
|
||||||
this.canvasImage.addEventListener('load', () => {
|
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 index = (numOffset % this.cachedImages.length + this.cachedImages.length) % this.cachedImages.length;
|
||||||
const cachedImagePageNum = this.readerService.imageUrlToPageNum(this.cachedImages[index].src);
|
const cachedImagePageNum = this.readerService.imageUrlToPageNum(this.cachedImages[index].src);
|
||||||
if (cachedImagePageNum !== numOffset) {
|
if (cachedImagePageNum !== numOffset) {
|
||||||
this.cachedImages[index] = new Image();
|
this.cachedImages[index] = this.getPage(numOffset, this.chapterId);
|
||||||
this.cachedImages[index].src = this.getPageUrl(numOffset);
|
|
||||||
this.cachedImages[index].onload = (evt) => {
|
|
||||||
this.cdRef.markForCheck();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//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);
|
d.text = translate('preferences.' + o.text);
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
<ng-container *ngIf="isValid() && !this.mangaReaderService.shouldSplit(this.currentImage, this.pageSplit)">
|
@if(isValid() && !mangaReaderService.shouldSplit(currentImage, pageSplit)) {
|
||||||
<div class="image-container {{imageFitClass$ | async}} {{emulateBookClass$ | async}}"
|
<div class="image-container {{imageFitClass$ | async}} {{emulateBookClass$ | async}}"
|
||||||
[style.filter]="(darkness$ | async) ?? '' | safeStyle" [style.height]="(imageContainerHeight$ | async) ?? '' | safeStyle">
|
[style.filter]="(darkness$ | async) ?? '' | safeStyle" [style.height]="(imageContainerHeight$ | async) ?? '' | safeStyle">
|
||||||
<ng-container *ngIf="currentImage">
|
@if(currentImage) {
|
||||||
<img alt=" "
|
<img alt=" "
|
||||||
#image [src]="currentImage.src"
|
#image
|
||||||
id="image-1"
|
[src]="currentImage.src"
|
||||||
class="{{imageFitClass$ | async}} {{readerModeClass$ | async}} {{showClickOverlayClass$ | async}}"
|
id="image-1"
|
||||||
>
|
class="{{imageFitClass$ | async}} {{readerModeClass$ | async}} {{showClickOverlayClass$ | async}}" />
|
||||||
</ng-container>
|
}
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
}
|
||||||
|
@ -27,7 +27,7 @@ import { SafeStylePipe } from '../../../_pipes/safe-style.pipe';
|
|||||||
styleUrls: ['./single-renderer.component.scss'],
|
styleUrls: ['./single-renderer.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [NgIf, AsyncPipe, SafeStylePipe]
|
imports: [AsyncPipe, SafeStylePipe]
|
||||||
})
|
})
|
||||||
export class SingleRendererComponent implements OnInit, ImageRenderer {
|
export class SingleRendererComponent implements OnInit, ImageRenderer {
|
||||||
|
|
||||||
@ -137,7 +137,7 @@ export class SingleRendererComponent implements OnInit, ImageRenderer {
|
|||||||
|
|
||||||
return fit;
|
return fit;
|
||||||
}),
|
}),
|
||||||
shareReplay(),
|
shareReplay({refCount: true, bufferSize: 1}),
|
||||||
filter(_ => this.isValid()),
|
filter(_ => this.isValid()),
|
||||||
takeUntilDestroyed(this.destroyRef),
|
takeUntilDestroyed(this.destroyRef),
|
||||||
);
|
);
|
||||||
|
@ -153,7 +153,8 @@ export class LibrarySettingsModalComponent implements OnInit {
|
|||||||
// This needs to only apply after first render
|
// This needs to only apply after first render
|
||||||
this.libraryForm.get('type')?.valueChanges.pipe(
|
this.libraryForm.get('type')?.valueChanges.pipe(
|
||||||
tap((type: LibraryType) => {
|
tap((type: LibraryType) => {
|
||||||
switch (type) {
|
const libType = parseInt(type + '', 10) as LibraryType;
|
||||||
|
switch (libType) {
|
||||||
case LibraryType.Manga:
|
case LibraryType.Manga:
|
||||||
this.libraryForm.get(FileTypeGroup.Archive + '')?.setValue(true);
|
this.libraryForm.get(FileTypeGroup.Archive + '')?.setValue(true);
|
||||||
this.libraryForm.get(FileTypeGroup.Images + '')?.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.Images + '')?.setValue(true);
|
||||||
this.libraryForm.get(FileTypeGroup.Pdf + '')?.setValue(false);
|
this.libraryForm.get(FileTypeGroup.Pdf + '')?.setValue(false);
|
||||||
this.libraryForm.get(FileTypeGroup.Epub + '')?.setValue(false);
|
this.libraryForm.get(FileTypeGroup.Epub + '')?.setValue(false);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
takeUntilDestroyed(this.destroyRef)
|
takeUntilDestroyed(this.destroyRef)
|
||||||
@ -203,15 +205,17 @@ export class LibrarySettingsModalComponent implements OnInit {
|
|||||||
for(let glob of this.library.excludePatterns) {
|
for(let glob of this.library.excludePatterns) {
|
||||||
this.libraryForm.addControl('excludeGlob-' , new FormControl(glob, []));
|
this.libraryForm.addControl('excludeGlob-' , new FormControl(glob, []));
|
||||||
}
|
}
|
||||||
|
this.excludePatterns = this.library.excludePatterns;
|
||||||
} else {
|
} else {
|
||||||
for(let fileTypeGroup of allFileTypeGroup) {
|
for(let fileTypeGroup of allFileTypeGroup) {
|
||||||
this.libraryForm.addControl(fileTypeGroup + '', new FormControl(true, []));
|
this.libraryForm.addControl(fileTypeGroup + '', new FormControl(true, []));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.excludePatterns = this.library.excludePatterns;
|
|
||||||
if (this.excludePatterns.length === 0) {
|
if (this.excludePatterns.length === 0) {
|
||||||
this.excludePatterns = [''];
|
this.excludePatterns = [''];
|
||||||
}
|
}
|
||||||
|
|
||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user