mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-05-24 00:52:23 -04:00
Release Polish 3 (#3359)
This commit is contained in:
parent
dd3dec269f
commit
f812f61001
@ -42,10 +42,15 @@ public class PersonController : BaseApiController
|
||||
return Ok(await _unitOfWork.PersonRepository.GetPersonDtoByName(name, User.GetUserId()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all roles for a Person
|
||||
/// </summary>
|
||||
/// <param name="personId"></param>
|
||||
/// <returns></returns>
|
||||
[HttpGet("roles")]
|
||||
public async Task<ActionResult<IEnumerable<PersonRole>>> GetRolesForPersonByName(string name)
|
||||
public async Task<ActionResult<IEnumerable<PersonRole>>> GetRolesForPersonByName(int personId)
|
||||
{
|
||||
return Ok(await _unitOfWork.PersonRepository.GetRolesForPersonByName(name, User.GetUserId()));
|
||||
return Ok(await _unitOfWork.PersonRepository.GetRolesForPersonByName(personId, User.GetUserId()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -490,23 +490,27 @@ public class UploadController : BaseApiController
|
||||
[HttpPost("person")]
|
||||
public async Task<ActionResult> UploadPersonCoverImageFromUrl(UploadFileDto uploadFileDto)
|
||||
{
|
||||
// Check if Url is non-empty, request the image and place in temp, then ask image service to handle it.
|
||||
// See if we can do this all in memory without touching underlying system
|
||||
if (string.IsNullOrEmpty(uploadFileDto.Url))
|
||||
{
|
||||
return BadRequest(await _localizationService.Translate(User.GetUserId(), "url-required"));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var person = await _unitOfWork.PersonRepository.GetPersonById(uploadFileDto.Id);
|
||||
if (person == null) return BadRequest(await _localizationService.Translate(User.GetUserId(), "person-doesnt-exist"));
|
||||
var filePath = await CreateThumbnail(uploadFileDto, $"{ImageService.GetPersonFormat(uploadFileDto.Id)}");
|
||||
|
||||
if (!string.IsNullOrEmpty(filePath))
|
||||
if (!string.IsNullOrEmpty(uploadFileDto.Url))
|
||||
{
|
||||
person.CoverImage = filePath;
|
||||
person.CoverImageLocked = true;
|
||||
var filePath = await CreateThumbnail(uploadFileDto, $"{ImageService.GetPersonFormat(uploadFileDto.Id)}");
|
||||
|
||||
if (!string.IsNullOrEmpty(filePath))
|
||||
{
|
||||
person.CoverImage = filePath;
|
||||
person.CoverImageLocked = true;
|
||||
_imageService.UpdateColorScape(person);
|
||||
_unitOfWork.PersonRepository.Update(person);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
person.CoverImage = string.Empty;
|
||||
person.CoverImageLocked = false;
|
||||
_imageService.UpdateColorScape(person);
|
||||
_unitOfWork.PersonRepository.Update(person);
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ public interface IPersonRepository
|
||||
|
||||
Task<string> GetCoverImageAsync(int personId);
|
||||
Task<string?> GetCoverImageByNameAsync(string name);
|
||||
Task<IEnumerable<PersonRole>> GetRolesForPersonByName(string name, int userId);
|
||||
Task<IEnumerable<PersonRole>> GetRolesForPersonByName(int personId, int userId);
|
||||
Task<PagedList<BrowsePersonDto>> GetAllWritersAndSeriesCount(int userId, UserParams userParams);
|
||||
Task<Person?> GetPersonById(int personId);
|
||||
Task<PersonDto?> GetPersonDtoByName(string name, int userId);
|
||||
@ -145,18 +145,28 @@ public class PersonRepository : IPersonRepository
|
||||
.SingleOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<PersonRole>> GetRolesForPersonByName(string name, int userId)
|
||||
public async Task<IEnumerable<PersonRole>> GetRolesForPersonByName(int personId, int userId)
|
||||
{
|
||||
// TODO: This will need to check both series and chapters (in cases where komf only updates series)
|
||||
var normalized = name.ToNormalized();
|
||||
var ageRating = await _context.AppUser.GetUserAgeRestriction(userId);
|
||||
|
||||
return await _context.Person
|
||||
.Where(p => p.NormalizedName == normalized)
|
||||
// Query roles from ChapterPeople
|
||||
var chapterRoles = await _context.Person
|
||||
.Where(p => p.Id == personId)
|
||||
.RestrictAgainstAgeRestriction(ageRating)
|
||||
.SelectMany(p => p.ChapterPeople.Select(cp => cp.Role))
|
||||
.Distinct()
|
||||
.ToListAsync();
|
||||
|
||||
// Query roles from SeriesMetadataPeople
|
||||
var seriesRoles = await _context.Person
|
||||
.Where(p => p.Id == personId)
|
||||
.RestrictAgainstAgeRestriction(ageRating)
|
||||
.SelectMany(p => p.SeriesMetadataPeople.Select(smp => smp.Role))
|
||||
.Distinct()
|
||||
.ToListAsync();
|
||||
|
||||
// Combine and return distinct roles
|
||||
return chapterRoles.Union(seriesRoles).Distinct();
|
||||
}
|
||||
|
||||
public async Task<PagedList<BrowsePersonDto>> GetAllWritersAndSeriesCount(int userId, UserParams userParams)
|
||||
|
@ -1046,8 +1046,6 @@ public class SeriesRepository : ISeriesRepository
|
||||
.Select(u => u.CollapseSeriesRelationships)
|
||||
.SingleOrDefaultAsync();
|
||||
|
||||
|
||||
|
||||
query ??= _context.Series
|
||||
.AsNoTracking();
|
||||
|
||||
@ -1064,7 +1062,6 @@ public class SeriesRepository : ISeriesRepository
|
||||
|
||||
query = ApplyWantToReadFilter(filter, query, userId);
|
||||
|
||||
|
||||
query = await ApplyCollectionFilter(filter, query, userId, userRating);
|
||||
|
||||
|
||||
@ -1136,6 +1133,7 @@ public class SeriesRepository : ISeriesRepository
|
||||
var seriesIds = _context.AppUser.Where(u => u.Id == userId)
|
||||
.SelectMany(u => u.WantToRead)
|
||||
.Select(s => s.SeriesId);
|
||||
|
||||
if (bool.Parse(wantToReadStmt.Value))
|
||||
{
|
||||
query = query.Where(s => seriesIds.Contains(s.Id));
|
||||
@ -1152,6 +1150,7 @@ public class SeriesRepository : ISeriesRepository
|
||||
{
|
||||
var filterIncludeLibs = new List<int>();
|
||||
var filterExcludeLibs = new List<int>();
|
||||
|
||||
if (filter.Statements != null)
|
||||
{
|
||||
foreach (var stmt in filter.Statements.Where(stmt => stmt.Field == FilterField.Libraries))
|
||||
@ -1993,17 +1992,25 @@ public class SeriesRepository : ISeriesRepository
|
||||
public async Task<PagedList<SeriesDto>> GetWantToReadForUserV2Async(int userId, UserParams userParams, FilterV2Dto filter)
|
||||
{
|
||||
var libraryIds = await _context.Library.GetUserLibraries(userId).ToListAsync();
|
||||
var query = _context.AppUser
|
||||
var seriesIds = await _context.AppUser
|
||||
.Where(user => user.Id == userId)
|
||||
.SelectMany(u => u.WantToRead)
|
||||
.Where(s => libraryIds.Contains(s.Series.LibraryId))
|
||||
.Select(w => w.Series)
|
||||
.Select(w => w.Series.Id)
|
||||
.Distinct()
|
||||
.ToListAsync();
|
||||
|
||||
var query = await CreateFilteredSearchQueryableV2(userId, filter, QueryContext.None);
|
||||
|
||||
// Apply the Want to Read filtering
|
||||
query = query.Where(s => seriesIds.Contains(s.Id));
|
||||
|
||||
var retSeries = query
|
||||
.ProjectTo<SeriesDto>(_mapper.ConfigurationProvider)
|
||||
.AsSplitQuery()
|
||||
.AsNoTracking();
|
||||
|
||||
var filteredQuery = await CreateFilteredSearchQueryableV2(userId, filter, QueryContext.None, query);
|
||||
|
||||
return await PagedList<SeriesDto>.CreateAsync(filteredQuery.ProjectTo<SeriesDto>(_mapper.ConfigurationProvider), userParams.PageNumber, userParams.PageSize);
|
||||
return await PagedList<SeriesDto>.CreateAsync(retSeries, userParams.PageNumber, userParams.PageSize);
|
||||
}
|
||||
|
||||
public async Task<IList<Series>> GetWantToReadForUserAsync(int userId)
|
||||
|
@ -259,8 +259,7 @@ public static class SeriesFilter
|
||||
.Where(p => p != null && p.AppUserId == userId)
|
||||
.Sum(p => p != null ? (p.PagesRead * 1.0f / s.Pages) : 0) * 100
|
||||
})
|
||||
.AsSplitQuery()
|
||||
.AsEnumerable();
|
||||
.AsSplitQuery();
|
||||
|
||||
switch (comparison)
|
||||
{
|
||||
|
@ -79,7 +79,6 @@ public static class QueryableExtensions
|
||||
.Include(l => l.AppUsers)
|
||||
.Where(lib => lib.AppUsers.Any(user => user.Id == userId))
|
||||
.IsRestricted(queryContext)
|
||||
.AsNoTracking()
|
||||
.AsSplitQuery()
|
||||
.Select(lib => lib.Id);
|
||||
}
|
||||
|
@ -585,6 +585,7 @@ public class ImageService : IImageService
|
||||
/// <inheritdoc />
|
||||
public string CreateThumbnailFromBase64(string encodedImage, string fileName, EncodeFormat encodeFormat, int thumbnailWidth = ThumbnailWidth)
|
||||
{
|
||||
// TODO: This code has no concept of cropping nor Thumbnail Size
|
||||
try
|
||||
{
|
||||
using var thumbnail = Image.ThumbnailBuffer(Convert.FromBase64String(encodedImage), thumbnailWidth);
|
||||
|
@ -334,7 +334,10 @@ public class SeriesService : ISeriesService
|
||||
private async Task HandlePeopleUpdateAsync(SeriesMetadata metadata, ICollection<PersonDto> peopleDtos, PersonRole role)
|
||||
{
|
||||
// Normalize all names from the DTOs
|
||||
var normalizedNames = peopleDtos.Select(p => Parser.Normalize(p.Name)).ToList();
|
||||
var normalizedNames = peopleDtos
|
||||
.Select(p => Parser.Normalize(p.Name))
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
// Bulk select people who already exist in the database
|
||||
var existingPeople = await _unitOfWork.PersonRepository.GetPeopleByNames(normalizedNames);
|
||||
|
@ -83,6 +83,7 @@ public class TaskScheduler : ITaskScheduler
|
||||
|
||||
public static readonly ImmutableArray<string> ScanTasks =
|
||||
["ScannerService", "ScanLibrary", "ScanLibraries", "ScanFolder", "ScanSeries"];
|
||||
private static readonly ImmutableArray<string> NonCronOptions = ["disabled", "daily", "weekly"];
|
||||
|
||||
private static readonly Random Rnd = new Random();
|
||||
|
||||
@ -122,10 +123,10 @@ public class TaskScheduler : ITaskScheduler
|
||||
public async Task ScheduleTasks()
|
||||
{
|
||||
_logger.LogInformation("Scheduling reoccurring tasks");
|
||||
var nonCronOptions = new List<string>(["disabled", "daily", "weekly"]);
|
||||
|
||||
|
||||
var setting = (await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.TaskScan)).Value;
|
||||
if (setting == null || (!nonCronOptions.Contains(setting) && !CronHelper.IsValidCron(setting)))
|
||||
if (IsInvalidCronSetting(setting))
|
||||
{
|
||||
_logger.LogError("Scan Task has invalid cron, defaulting to Daily");
|
||||
RecurringJob.AddOrUpdate(ScanLibrariesTaskId, () => ScanLibraries(false),
|
||||
@ -141,9 +142,9 @@ public class TaskScheduler : ITaskScheduler
|
||||
|
||||
|
||||
setting = (await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.TaskBackup)).Value;
|
||||
if (setting == null || (!nonCronOptions.Contains(setting) && !CronHelper.IsValidCron(setting)))
|
||||
if (IsInvalidCronSetting(setting))
|
||||
{
|
||||
_logger.LogError("Backup Task has invalid cron, defaulting to Daily");
|
||||
_logger.LogError("Backup Task has invalid cron, defaulting to Weekly");
|
||||
RecurringJob.AddOrUpdate(BackupTaskId, () => _backupService.BackupDatabase(),
|
||||
Cron.Weekly, RecurringJobOptions);
|
||||
}
|
||||
@ -161,18 +162,18 @@ public class TaskScheduler : ITaskScheduler
|
||||
}
|
||||
|
||||
setting = (await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.TaskCleanup)).Value;
|
||||
if (setting == null || (!nonCronOptions.Contains(setting) && !CronHelper.IsValidCron(setting)))
|
||||
{
|
||||
_logger.LogDebug("Scheduling Cleanup Task for {Setting}", setting);
|
||||
RecurringJob.AddOrUpdate(CleanupTaskId, () => _cleanupService.Cleanup(),
|
||||
CronConverter.ConvertToCronNotation(setting), RecurringJobOptions);
|
||||
}
|
||||
else
|
||||
if (IsInvalidCronSetting(setting))
|
||||
{
|
||||
_logger.LogError("Cleanup Task has invalid cron, defaulting to Daily");
|
||||
RecurringJob.AddOrUpdate(CleanupTaskId, () => _cleanupService.Cleanup(),
|
||||
Cron.Daily, RecurringJobOptions);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogDebug("Scheduling Cleanup Task for {Setting}", setting);
|
||||
RecurringJob.AddOrUpdate(CleanupTaskId, () => _cleanupService.Cleanup(),
|
||||
CronConverter.ConvertToCronNotation(setting), RecurringJobOptions);
|
||||
}
|
||||
|
||||
|
||||
RecurringJob.AddOrUpdate(RemoveFromWantToReadTaskId, () => _cleanupService.CleanupWantToRead(),
|
||||
@ -186,6 +187,11 @@ public class TaskScheduler : ITaskScheduler
|
||||
await ScheduleKavitaPlusTasks();
|
||||
}
|
||||
|
||||
private static bool IsInvalidCronSetting(string setting)
|
||||
{
|
||||
return setting == null || (!NonCronOptions.Contains(setting) && !CronHelper.IsValidCron(setting));
|
||||
}
|
||||
|
||||
public async Task ScheduleKavitaPlusTasks()
|
||||
{
|
||||
// KavitaPlus based (needs license check)
|
||||
|
1
UI/Web/src/app/_models/default-modal-options.ts
Normal file
1
UI/Web/src/app/_models/default-modal-options.ts
Normal file
@ -0,0 +1 @@
|
||||
export const DefaultModalOptions = {scrollable: true, size: 'xl', fullscreen: 'xl'};
|
@ -1,6 +1,6 @@
|
||||
import {inject, Pipe, PipeTransform} from '@angular/core';
|
||||
import { PersonRole } from '../_models/metadata/person';
|
||||
import {TranslocoService} from "@jsverse/transloco";
|
||||
import {translate, TranslocoService} from "@jsverse/transloco";
|
||||
|
||||
@Pipe({
|
||||
name: 'personRole',
|
||||
@ -8,39 +8,38 @@ import {TranslocoService} from "@jsverse/transloco";
|
||||
})
|
||||
export class PersonRolePipe implements PipeTransform {
|
||||
|
||||
translocoService = inject(TranslocoService);
|
||||
transform(value: PersonRole): string {
|
||||
switch (value) {
|
||||
case PersonRole.Artist:
|
||||
return this.translocoService.translate('person-role-pipe.artist');
|
||||
return translate('person-role-pipe.artist');
|
||||
case PersonRole.Character:
|
||||
return this.translocoService.translate('person-role-pipe.character');
|
||||
return translate('person-role-pipe.character');
|
||||
case PersonRole.Colorist:
|
||||
return this.translocoService.translate('person-role-pipe.colorist');
|
||||
return translate('person-role-pipe.colorist');
|
||||
case PersonRole.CoverArtist:
|
||||
return this.translocoService.translate('person-role-pipe.artist');
|
||||
return translate('person-role-pipe.artist');
|
||||
case PersonRole.Editor:
|
||||
return this.translocoService.translate('person-role-pipe.editor');
|
||||
return translate('person-role-pipe.editor');
|
||||
case PersonRole.Inker:
|
||||
return this.translocoService.translate('person-role-pipe.inker');
|
||||
return translate('person-role-pipe.inker');
|
||||
case PersonRole.Letterer:
|
||||
return this.translocoService.translate('person-role-pipe.letterer');
|
||||
return translate('person-role-pipe.letterer');
|
||||
case PersonRole.Penciller:
|
||||
return this.translocoService.translate('person-role-pipe.penciller');
|
||||
return translate('person-role-pipe.penciller');
|
||||
case PersonRole.Publisher:
|
||||
return this.translocoService.translate('person-role-pipe.publisher');
|
||||
return translate('person-role-pipe.publisher');
|
||||
case PersonRole.Imprint:
|
||||
return this.translocoService.translate('person-role-pipe.imprint');
|
||||
return translate('person-role-pipe.imprint');
|
||||
case PersonRole.Writer:
|
||||
return this.translocoService.translate('person-role-pipe.writer');
|
||||
return translate('person-role-pipe.writer');
|
||||
case PersonRole.Team:
|
||||
return this.translocoService.translate('person-role-pipe.team');
|
||||
return translate('person-role-pipe.team');
|
||||
case PersonRole.Location:
|
||||
return this.translocoService.translate('person-role-pipe.location');
|
||||
return translate('person-role-pipe.location');
|
||||
case PersonRole.Translator:
|
||||
return this.translocoService.translate('person-role-pipe.translator');
|
||||
return translate('person-role-pipe.translator');
|
||||
case PersonRole.Other:
|
||||
return this.translocoService.translate('person-role-pipe.other');
|
||||
return translate('person-role-pipe.other');
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ import {FilterService} from "./filter.service";
|
||||
import {ReadingListService} from "./reading-list.service";
|
||||
import {ChapterService} from "./chapter.service";
|
||||
import {VolumeService} from "./volume.service";
|
||||
import {DefaultModalOptions} from "../_models/default-modal-options";
|
||||
|
||||
export type LibraryActionCallback = (library: Partial<Library>) => void;
|
||||
export type SeriesActionCallback = (series: Series) => void;
|
||||
@ -121,7 +122,7 @@ export class ActionService {
|
||||
}
|
||||
|
||||
editLibrary(library: Partial<Library>, callback?: LibraryActionCallback) {
|
||||
const modalRef = this.modalService.open(LibrarySettingsModalComponent, {size: 'xl', fullscreen: 'md'});
|
||||
const modalRef = this.modalService.open(LibrarySettingsModalComponent, DefaultModalOptions);
|
||||
modalRef.componentInstance.library = library;
|
||||
modalRef.closed.subscribe((closeResult: {success: boolean, library: Library, coverImageUpdate: boolean}) => {
|
||||
if (callback) callback(library)
|
||||
|
@ -16,7 +16,8 @@ export class JumpbarService {
|
||||
|
||||
|
||||
getResumeKey(key: string) {
|
||||
if (this.resumeKeys.hasOwnProperty(key)) return this.resumeKeys[key];
|
||||
const k = key.toUpperCase();
|
||||
if (this.resumeKeys.hasOwnProperty(k)) return this.resumeKeys[k];
|
||||
return '';
|
||||
}
|
||||
|
||||
@ -26,7 +27,8 @@ export class JumpbarService {
|
||||
}
|
||||
|
||||
saveResumeKey(key: string, value: string) {
|
||||
this.resumeKeys[key] = value;
|
||||
const k = key.toUpperCase();
|
||||
this.resumeKeys[k] = value;
|
||||
}
|
||||
|
||||
saveResumePosition(url: string, value: number) {
|
||||
@ -75,9 +77,11 @@ export class JumpbarService {
|
||||
|
||||
_removeFirstPartOfJumpBar(midPoint: number, numberOfRemovals: number = 1, jumpBarKeys: Array<JumpKey>, jumpBarKeysToRender: Array<JumpKey>) {
|
||||
const removedIndexes: Array<number> = [];
|
||||
|
||||
for(let removal = 0; removal < numberOfRemovals; removal++) {
|
||||
let min = 100000000;
|
||||
let minIndex = -1;
|
||||
|
||||
for(let i = 1; i < midPoint; i++) {
|
||||
if (jumpBarKeys[i].size < min && !removedIndexes.includes(i)) {
|
||||
min = jumpBarKeys[i].size;
|
||||
@ -101,7 +105,7 @@ export class JumpbarService {
|
||||
getJumpKeys(data :Array<any>, keySelector: (data: any) => string) {
|
||||
const keys: {[key: string]: number} = {};
|
||||
data.forEach(obj => {
|
||||
let ch = keySelector(obj).charAt(0);
|
||||
let ch = keySelector(obj).charAt(0).toUpperCase();
|
||||
if (/\d|\#|!|%|@|\(|\)|\^|\.|_|\*/g.test(ch)) {
|
||||
ch = '#';
|
||||
}
|
||||
@ -111,10 +115,11 @@ export class JumpbarService {
|
||||
keys[ch] += 1;
|
||||
});
|
||||
return Object.keys(keys).map(k => {
|
||||
k = k.toUpperCase();
|
||||
return {
|
||||
key: k,
|
||||
size: keys[k],
|
||||
title: k.toUpperCase()
|
||||
title: k
|
||||
}
|
||||
}).sort((a, b) => {
|
||||
if (a.key < b.key) return -1;
|
||||
|
@ -29,8 +29,8 @@ export class PersonService {
|
||||
return this.httpClient.get<Person>(this.baseUrl + `person?name=${name}`);
|
||||
}
|
||||
|
||||
getRolesForPerson(name: string) {
|
||||
return this.httpClient.get<Array<PersonRole>>(this.baseUrl + `person/roles?name=${name}`);
|
||||
getRolesForPerson(personId: number) {
|
||||
return this.httpClient.get<Array<PersonRole>>(this.baseUrl + `person/roles?personId=${personId}`);
|
||||
}
|
||||
|
||||
getSeriesMostKnownFor(personId: number) {
|
||||
|
@ -4,7 +4,7 @@
|
||||
<div class="publisher-side publisher-front">
|
||||
<div class="publisher-img-container d-inline-flex align-items-center me-2 position-relative">
|
||||
<app-image
|
||||
[imageUrl]="imageService.getPersonImage(currentPublisher!.id)"
|
||||
[imageUrl]="imageService.getPublisherImage(currentPublisher!.name)"
|
||||
[classes]="'me-2'"
|
||||
[hideOnError]="true"
|
||||
width="32px"
|
||||
@ -20,7 +20,7 @@
|
||||
<div class="publisher-side publisher-back">
|
||||
<div class="publisher-img-container d-inline-flex align-items-center me-2 position-relative">
|
||||
<app-image
|
||||
[imageUrl]="imageService.getPersonImage(nextPublisher!.id)"
|
||||
[imageUrl]="imageService.getPublisherImage(nextPublisher!.name)"
|
||||
[classes]="'me-2'"
|
||||
[hideOnError]="true"
|
||||
width="32px"
|
||||
|
@ -1,18 +1,33 @@
|
||||
//.publisher-flipper-container {
|
||||
// perspective: 1000px;
|
||||
//}
|
||||
//
|
||||
//.publisher-img-container {
|
||||
// transform-style: preserve-3d;
|
||||
// transition: transform 0.5s;
|
||||
// background-color: var(--card-bg-color);
|
||||
// border-radius: 3px;
|
||||
// padding: 2px 5px;
|
||||
// font-size: 0.8rem;
|
||||
// vertical-align: middle;
|
||||
//
|
||||
// div {
|
||||
// min-height: 32px;
|
||||
// line-height: 32px;
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
// jumpbar example
|
||||
// Animation code
|
||||
|
||||
.publisher-wrapper {
|
||||
perspective: 1000px;
|
||||
height: 32px;
|
||||
|
||||
background-color: var(--card-bg-color);
|
||||
border-radius: 3px;
|
||||
padding: 2px 5px;
|
||||
font-size: 0.8rem;
|
||||
vertical-align: middle;
|
||||
|
||||
div {
|
||||
min-height: 32px;
|
||||
line-height: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
.publisher-flipper {
|
||||
@ -20,7 +35,7 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-align: left;
|
||||
transition: transform 0.6s;
|
||||
transition: transform 0.6s ease;
|
||||
transform-style: preserve-3d;
|
||||
}
|
||||
|
||||
@ -33,7 +48,6 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
backface-visibility: hidden;
|
||||
transform-style: preserve-3d;
|
||||
}
|
||||
|
||||
.publisher-front {
|
||||
|
@ -1,4 +1,14 @@
|
||||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, Input, OnDestroy, OnInit} from '@angular/core';
|
||||
import {
|
||||
AfterViewChecked,
|
||||
AfterViewInit,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
inject,
|
||||
Input,
|
||||
OnDestroy,
|
||||
OnInit
|
||||
} from '@angular/core';
|
||||
import {ImageComponent} from "../../shared/image/image.component";
|
||||
import {FilterField} from "../../_models/metadata/v2/filter-field";
|
||||
import {Person} from "../../_models/metadata/person";
|
||||
@ -19,7 +29,7 @@ const ANIMATION_TIME = 3000;
|
||||
styleUrl: './publisher-flipper.component.scss',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class PublisherFlipperComponent implements OnInit, OnDestroy {
|
||||
export class PublisherFlipperComponent implements OnInit, OnDestroy, AfterViewInit, AfterViewChecked {
|
||||
|
||||
protected readonly imageService = inject(ImageService);
|
||||
private readonly filterUtilityService = inject(FilterUtilitiesService);
|
||||
@ -36,14 +46,26 @@ export class PublisherFlipperComponent implements OnInit, OnDestroy {
|
||||
isFlipped = false;
|
||||
private intervalId: any;
|
||||
|
||||
|
||||
ngOnInit() {
|
||||
if (this.publishers.length > 0) {
|
||||
this.currentPublisher = this.publishers[0];
|
||||
this.nextPublisher = this.publishers[1] || this.publishers[0];
|
||||
if (this.publishers.length > 1) {
|
||||
this.startFlipping();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
if (this.publishers.length > 1) {
|
||||
this.startFlipping(); // Start flipping cycle once the view is initialized
|
||||
}
|
||||
}
|
||||
|
||||
ngAfterViewChecked() {
|
||||
// This lifecycle hook will be called after Angular performs change detection in each cycle
|
||||
if (this.isFlipped) {
|
||||
// Only update publishers after the flip is complete
|
||||
this.currentIndex = (this.currentIndex + 1) % this.publishers.length;
|
||||
this.currentPublisher = this.publishers[this.currentIndex];
|
||||
this.nextPublisher = this.publishers[(this.currentIndex + 1) % this.publishers.length];
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,22 +77,9 @@ export class PublisherFlipperComponent implements OnInit, OnDestroy {
|
||||
|
||||
private startFlipping() {
|
||||
this.intervalId = setInterval(() => {
|
||||
// First flip
|
||||
this.isFlipped = true;
|
||||
this.cdRef.markForCheck();
|
||||
|
||||
// Update content after flip animation completes
|
||||
setTimeout(() => {
|
||||
// Update indices and content
|
||||
this.currentIndex = (this.currentIndex + 1) % this.publishers.length;
|
||||
this.currentPublisher = this.publishers[this.currentIndex];
|
||||
this.nextPublisher = this.publishers[(this.currentIndex + 1) % this.publishers.length];
|
||||
|
||||
// Reset flip
|
||||
this.isFlipped = false;
|
||||
|
||||
this.cdRef.markForCheck();
|
||||
}, ANIMATION_TIME); // Full transition time to ensure flip completes
|
||||
// Toggle flip state, initiating the flip animation
|
||||
this.isFlipped = !this.isFlipped;
|
||||
this.cdRef.detectChanges(); // Explicitly detect changes to trigger re-render
|
||||
}, ANIMATION_TIME);
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,7 @@ import {DefaultDatePipe} from "../../_pipes/default-date.pipe";
|
||||
import {DefaultValuePipe} from "../../_pipes/default-value.pipe";
|
||||
import {TranslocoLocaleModule} from "@jsverse/transloco-locale";
|
||||
import {UtcToLocalTimePipe} from "../../_pipes/utc-to-local-time.pipe";
|
||||
import {DefaultModalOptions} from "../../_models/default-modal-options";
|
||||
|
||||
@Component({
|
||||
selector: 'app-manage-scrobble-errors',
|
||||
@ -111,7 +112,7 @@ export class ManageScrobbleErrorsComponent implements OnInit {
|
||||
|
||||
editSeries(seriesId: number) {
|
||||
this.seriesService.getSeries(seriesId).subscribe(series => {
|
||||
const modalRef = this.modalService.open(EditSeriesModalComponent, { size: 'xl' });
|
||||
const modalRef = this.modalService.open(EditSeriesModalComponent, DefaultModalOptions);
|
||||
modalRef.componentInstance.series = series;
|
||||
});
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||
import {SettingItemComponent} from "../../settings/_components/setting-item/setting-item.component";
|
||||
import {ConfirmService} from "../../shared/confirm.service";
|
||||
import {SettingButtonComponent} from "../../settings/_components/setting-button/setting-button.component";
|
||||
import {DefaultModalOptions} from "../../_models/default-modal-options";
|
||||
|
||||
interface AdhocTask {
|
||||
name: string;
|
||||
@ -128,7 +129,7 @@ export class ManageTasksSettingsComponent implements OnInit {
|
||||
this.toastr.info(translate('toasts.no-updates'));
|
||||
return;
|
||||
}
|
||||
const modalRef = this.modalService.open(UpdateNotificationModalComponent, { scrollable: true, size: 'lg' });
|
||||
const modalRef = this.modalService.open(UpdateNotificationModalComponent, DefaultModalOptions);
|
||||
modalRef.componentInstance.updateData = update;
|
||||
}
|
||||
},
|
||||
|
@ -22,6 +22,7 @@ import {makeBindingParser} from "@angular/compiler";
|
||||
import {LoadingComponent} from "../../shared/loading/loading.component";
|
||||
import {TimeAgoPipe} from "../../_pipes/time-ago.pipe";
|
||||
import {SentenceCasePipe} from "../../_pipes/sentence-case.pipe";
|
||||
import {DefaultModalOptions} from "../../_models/default-modal-options";
|
||||
|
||||
@Component({
|
||||
selector: 'app-manage-users',
|
||||
@ -87,7 +88,7 @@ export class ManageUsersComponent implements OnInit {
|
||||
}
|
||||
|
||||
openEditUser(member: Member) {
|
||||
const modalRef = this.modalService.open(EditUserComponent, { scrollable: true, size: 'xl', fullscreen: 'md' });
|
||||
const modalRef = this.modalService.open(EditUserComponent, DefaultModalOptions);
|
||||
modalRef.componentInstance.member = member;
|
||||
modalRef.closed.subscribe(() => {
|
||||
this.loadMembers();
|
||||
@ -107,7 +108,7 @@ export class ManageUsersComponent implements OnInit {
|
||||
}
|
||||
|
||||
inviteUser() {
|
||||
const modalRef = this.modalService.open(InviteUserComponent, {size: 'xl'});
|
||||
const modalRef = this.modalService.open(InviteUserComponent, DefaultModalOptions);
|
||||
modalRef.closed.subscribe((successful: boolean) => {
|
||||
this.loadMembers();
|
||||
});
|
||||
@ -133,7 +134,7 @@ export class ManageUsersComponent implements OnInit {
|
||||
}
|
||||
|
||||
updatePassword(member: Member) {
|
||||
const modalRef = this.modalService.open(ResetPasswordModalComponent);
|
||||
const modalRef = this.modalService.open(ResetPasswordModalComponent, DefaultModalOptions);
|
||||
modalRef.componentInstance.member = member;
|
||||
}
|
||||
|
||||
|
@ -43,6 +43,7 @@ import {User} from "../../_models/user";
|
||||
import {ScrollService} from "../../_services/scroll.service";
|
||||
import {ReaderService} from "../../_services/reader.service";
|
||||
import {SeriesFormatComponent} from "../../shared/series-format/series-format.component";
|
||||
import {DefaultModalOptions} from "../../_models/default-modal-options";
|
||||
|
||||
function deepClone(obj: any): any {
|
||||
if (obj === null || typeof obj !== 'object') {
|
||||
@ -278,7 +279,7 @@ export class SeriesCardComponent implements OnInit, OnChanges {
|
||||
}
|
||||
|
||||
openEditModal(data: Series) {
|
||||
const modalRef = this.modalService.open(EditSeriesModalComponent, { size: 'lg' });
|
||||
const modalRef = this.modalService.open(EditSeriesModalComponent, DefaultModalOptions);
|
||||
modalRef.componentInstance.series = data;
|
||||
modalRef.closed.subscribe((closeResult: {success: boolean, series: Series, coverImageUpdate: boolean}) => {
|
||||
if (closeResult.success) {
|
||||
|
@ -83,6 +83,7 @@ import {PublicationStatusPipe} from "../_pipes/publication-status.pipe";
|
||||
import {DefaultDatePipe} from "../_pipes/default-date.pipe";
|
||||
import {MangaFormatPipe} from "../_pipes/manga-format.pipe";
|
||||
import {CoverImageComponent} from "../_single-module/cover-image/cover-image.component";
|
||||
import {DefaultModalOptions} from "../_models/default-modal-options";
|
||||
|
||||
enum TabID {
|
||||
Related = 'related-tab',
|
||||
@ -317,7 +318,7 @@ export class ChapterDetailComponent implements OnInit {
|
||||
}
|
||||
|
||||
openEditModal() {
|
||||
const ref = this.modalService.open(EditChapterModalComponent, { size: 'xl' });
|
||||
const ref = this.modalService.open(EditChapterModalComponent, DefaultModalOptions);
|
||||
ref.componentInstance.chapter = this.chapter;
|
||||
ref.componentInstance.libraryType = this.libraryType;
|
||||
ref.componentInstance.libraryId = this.libraryId;
|
||||
|
@ -42,6 +42,7 @@ import {SeriesCardComponent} from "../../../cards/series-card/series-card.compon
|
||||
import {ActionService} from "../../../_services/action.service";
|
||||
import {KEY_CODES} from "../../../shared/_services/utility.service";
|
||||
import {WikiLink} from "../../../_models/wiki";
|
||||
import {DefaultModalOptions} from "../../../_models/default-modal-options";
|
||||
|
||||
|
||||
@Component({
|
||||
@ -153,7 +154,7 @@ export class AllCollectionsComponent implements OnInit {
|
||||
});
|
||||
break;
|
||||
case(Action.Edit):
|
||||
const modalRef = this.modalService.open(EditCollectionTagsComponent, { size: 'lg', scrollable: true });
|
||||
const modalRef = this.modalService.open(EditCollectionTagsComponent, DefaultModalOptions);
|
||||
modalRef.componentInstance.tag = collectionTag;
|
||||
modalRef.closed.subscribe((results: {success: boolean, coverImageUpdated: boolean}) => {
|
||||
if (results.success) {
|
||||
|
@ -64,6 +64,7 @@ import {PromotedIconComponent} from "../../../shared/_components/promoted-icon/p
|
||||
import {
|
||||
SmartCollectionDrawerComponent
|
||||
} from "../../../_single-module/smart-collection-drawer/smart-collection-drawer.component";
|
||||
import {DefaultModalOptions} from "../../../_models/default-modal-options";
|
||||
|
||||
@Component({
|
||||
selector: 'app-collection-detail',
|
||||
@ -330,7 +331,7 @@ export class CollectionDetailComponent implements OnInit, AfterContentChecked {
|
||||
}
|
||||
|
||||
openEditCollectionTagModal(collectionTag: UserCollection) {
|
||||
const modalRef = this.modalService.open(EditCollectionTagsComponent, { size: 'lg', scrollable: true });
|
||||
const modalRef = this.modalService.open(EditCollectionTagsComponent, DefaultModalOptions);
|
||||
modalRef.componentInstance.tag = this.collectionTag;
|
||||
modalRef.closed.subscribe((results: {success: boolean, coverImageUpdated: boolean}) => {
|
||||
this.updateTag(this.collectionTag.id);
|
||||
|
@ -26,6 +26,7 @@ import { SentenceCasePipe } from '../../../_pipes/sentence-case.pipe';
|
||||
import { CircularLoaderComponent } from '../../../shared/circular-loader/circular-loader.component';
|
||||
import { NgClass, NgStyle, AsyncPipe } from '@angular/common';
|
||||
import {TranslocoDirective} from "@jsverse/transloco";
|
||||
import {DefaultModalOptions} from "../../../_models/default-modal-options";
|
||||
|
||||
@Component({
|
||||
selector: 'app-nav-events-toggle',
|
||||
@ -159,7 +160,7 @@ export class EventsWidgetComponent implements OnInit, OnDestroy {
|
||||
|
||||
handleUpdateAvailableClick(message: NotificationProgressEvent | UpdateVersionEvent) {
|
||||
if (this.updateNotificationModalRef != null) { return; }
|
||||
this.updateNotificationModalRef = this.modalService.open(UpdateNotificationModalComponent, { scrollable: true, size: 'lg' });
|
||||
this.updateNotificationModalRef = this.modalService.open(UpdateNotificationModalComponent, DefaultModalOptions);
|
||||
if (message.hasOwnProperty('body')) {
|
||||
this.updateNotificationModalRef.componentInstance.updateData = (message as NotificationProgressEvent).body as UpdateVersionEvent;
|
||||
} else {
|
||||
|
@ -38,6 +38,7 @@ import {EditPersonModalComponent} from "./_modal/edit-person-modal/edit-person-m
|
||||
import {translate, TranslocoDirective} from "@jsverse/transloco";
|
||||
import {ChapterCardComponent} from "../cards/chapter-card/chapter-card.component";
|
||||
import {ThemeService} from "../_services/theme.service";
|
||||
import {DefaultModalOptions} from "../_models/default-modal-options";
|
||||
|
||||
@Component({
|
||||
selector: 'app-person-detail',
|
||||
@ -109,7 +110,7 @@ export class PersonDetailComponent {
|
||||
this.themeService.setColorScape(person.primaryColor || '', person.secondaryColor);
|
||||
|
||||
// Fetch roles and process them
|
||||
this.roles$ = this.personService.getRolesForPerson(this.personName).pipe(
|
||||
this.roles$ = this.personService.getRolesForPerson(this.person.id).pipe(
|
||||
tap(roles => {
|
||||
this.roles = roles;
|
||||
this.filter = this.createFilter(roles);
|
||||
@ -187,7 +188,7 @@ export class PersonDetailComponent {
|
||||
handleAction(action: ActionItem<Person>, person: Person) {
|
||||
switch (action.action) {
|
||||
case(Action.Edit):
|
||||
const ref = this.modalService.open(EditPersonModalComponent, {scrollable: true, size: 'lg', fullscreen: 'md'});
|
||||
const ref = this.modalService.open(EditPersonModalComponent, DefaultModalOptions);
|
||||
ref.componentInstance.person = this.person;
|
||||
|
||||
ref.closed.subscribe(r => {
|
||||
|
@ -5,10 +5,9 @@
|
||||
<app-image [imageUrl]="imageService.getPublisherImage(entity.publishers[0].name)" [classes]="'me-2'" [hideOnError]="true" width="32px" height="32px"
|
||||
aria-hidden="true"></app-image>
|
||||
<div class="position-relative d-inline-block" (click)="openGeneric(FilterField.Publisher, entity.publishers[0].id)">{{entity.publishers[0].name}}</div>
|
||||
<!-- <app-publisher-flipper [publishers]="entity.publishers"></app-publisher-flipper>-->
|
||||
</div>
|
||||
}
|
||||
<!-- TODO: Figure out if I can implement this animation (ROBBIE)-->
|
||||
<!-- <app-publisher-flipper [publishers]="entity.publishers"></app-publisher-flipper>-->
|
||||
<span class="me-2">
|
||||
<app-age-rating-image [rating]="ageRating"></app-age-rating-image>
|
||||
</span>
|
||||
@ -17,7 +16,7 @@
|
||||
<app-series-format [format]="mangaFormat" [useTitle]="false"></app-series-format>
|
||||
</span>
|
||||
|
||||
@if (libraryType === LibraryType.Book || libraryType === LibraryType.LightNovel) {
|
||||
@if ((libraryType === LibraryType.Book || libraryType === LibraryType.LightNovel) && mangaFormat !== MangaFormat.PDF) {
|
||||
<span class="word-count me-3">{{t('words-count', {num: readingTimeEntity.wordCount | compactNumber})}}</span>
|
||||
} @else {
|
||||
<span class="word-count me-3">{{t('pages-count', {num: readingTimeEntity.pages | compactNumber})}}</span>
|
||||
|
@ -46,6 +46,8 @@ export class MetadataDetailRowComponent {
|
||||
private readonly filterUtilityService = inject(FilterUtilitiesService);
|
||||
|
||||
protected readonly LibraryType = LibraryType;
|
||||
protected readonly FilterField = FilterField;
|
||||
protected readonly MangaFormat = MangaFormat;
|
||||
|
||||
@Input({required: true}) entity!: IHasCast;
|
||||
@Input({required: true}) readingTimeEntity!: IHasReadingTime;
|
||||
@ -59,7 +61,4 @@ export class MetadataDetailRowComponent {
|
||||
if (queryParamName === FilterField.None) return;
|
||||
this.filterUtilityService.applyFilter(['all-series'], queryParamName, FilterComparison.Equal, `${filter}`).subscribe();
|
||||
}
|
||||
|
||||
|
||||
protected readonly FilterField = FilterField;
|
||||
}
|
||||
|
@ -145,6 +145,7 @@ import {UserCollection} from "../../../_models/collection-tag";
|
||||
import {SeriesFormatComponent} from "../../../shared/series-format/series-format.component";
|
||||
import {MangaFormatPipe} from "../../../_pipes/manga-format.pipe";
|
||||
import {CoverImageComponent} from "../../../_single-module/cover-image/cover-image.component";
|
||||
import {DefaultModalOptions} from "../../../_models/default-modal-options";
|
||||
|
||||
|
||||
enum TabID {
|
||||
@ -1041,7 +1042,7 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
|
||||
}
|
||||
|
||||
openEditChapter(chapter: Chapter) {
|
||||
const ref = this.modalService.open(EditChapterModalComponent, { size: 'xl' });
|
||||
const ref = this.modalService.open(EditChapterModalComponent, DefaultModalOptions);
|
||||
ref.componentInstance.chapter = chapter;
|
||||
ref.componentInstance.libraryType = this.libraryType;
|
||||
ref.componentInstance.seriesId = this.series?.id;
|
||||
@ -1055,7 +1056,7 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
|
||||
}
|
||||
|
||||
openEditVolume(volume: Volume) {
|
||||
const ref = this.modalService.open(EditVolumeModalComponent, { size: 'xl' });
|
||||
const ref = this.modalService.open(EditVolumeModalComponent, DefaultModalOptions);
|
||||
ref.componentInstance.volume = volume;
|
||||
ref.componentInstance.libraryType = this.libraryType;
|
||||
ref.componentInstance.seriesId = this.series?.id;
|
||||
@ -1069,7 +1070,7 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
|
||||
}
|
||||
|
||||
openEditSeriesModal() {
|
||||
const modalRef = this.modalService.open(EditSeriesModalComponent, { size: 'xl' });
|
||||
const modalRef = this.modalService.open(EditSeriesModalComponent, DefaultModalOptions);
|
||||
modalRef.componentInstance.series = this.series;
|
||||
modalRef.closed.subscribe((closeResult: EditSeriesModalCloseResult) => {
|
||||
if (closeResult.success) {
|
||||
@ -1088,7 +1089,7 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked {
|
||||
openReviewModal() {
|
||||
const userReview = this.getUserReview();
|
||||
|
||||
const modalRef = this.modalService.open(ReviewSeriesModalComponent, { scrollable: true, size: 'lg' });
|
||||
const modalRef = this.modalService.open(ReviewSeriesModalComponent, DefaultModalOptions);
|
||||
modalRef.componentInstance.series = this.series;
|
||||
if (userReview.length > 0) {
|
||||
modalRef.componentInstance.review = userReview[0];
|
||||
|
@ -2,13 +2,16 @@
|
||||
<div class="tagbadge cursor clickable">
|
||||
<div class="d-flex flex-column">
|
||||
@if (HasCoverImage) {
|
||||
<app-image height="24px" width="24px" [styles]="{'object-fit': 'contain'}"
|
||||
classes="align-self-center text-center mb-2"
|
||||
[imageUrl]="ImageUrl"
|
||||
[errorImage]="imageService.noPersonImage">
|
||||
</app-image>
|
||||
<div class="mx-auto">
|
||||
<app-image height="24px" width="24px" [styles]="{'background': 'none', 'max-height': '48px', 'height': '48px', 'width': '48px', 'border-radius': '50%'}"
|
||||
[imageUrl]="ImageUrl"
|
||||
[errorImage]="imageService.noPersonImage">
|
||||
</app-image>
|
||||
</div>
|
||||
} @else {
|
||||
<i class="fas fa-user mx-auto" aria-hidden="true"></i>
|
||||
<div style="background: none; max-height: 48px; height: 48px; width: 48px; border-radius: 50%" class="mx-auto">
|
||||
<i class="fas fa-user" aria-hidden="true"></i>
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
|
@ -7,11 +7,11 @@
|
||||
font-size: .8rem;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
width: 100px;
|
||||
width: 120px;
|
||||
word-break: break-word;
|
||||
|
||||
i {
|
||||
font-size: 2rem;
|
||||
font-size: 2.96rem;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ import {ScrobbleEventTypePipe} from "../../_pipes/scrobble-event-type.pipe";
|
||||
import {SortableHeader} from "../../_single-module/table/_directives/sortable-header.directive";
|
||||
import {UtcToLocalTimePipe} from "../../_pipes/utc-to-local-time.pipe";
|
||||
import {EditDeviceModalComponent} from "../_modals/edit-device-modal/edit-device-modal.component";
|
||||
import {DefaultModalOptions} from "../../_models/default-modal-options";
|
||||
|
||||
@Component({
|
||||
selector: 'app-manage-devices',
|
||||
@ -71,7 +72,7 @@ export class ManageDevicesComponent implements OnInit {
|
||||
}
|
||||
|
||||
addDevice() {
|
||||
const ref = this.modalService.open(EditDeviceModalComponent, { scrollable: true, size: 'xl', fullscreen: 'md' });
|
||||
const ref = this.modalService.open(EditDeviceModalComponent, DefaultModalOptions);
|
||||
ref.componentInstance.device = null;
|
||||
|
||||
ref.closed.subscribe((result: Device | null) => {
|
||||
@ -82,7 +83,7 @@ export class ManageDevicesComponent implements OnInit {
|
||||
}
|
||||
|
||||
editDevice(device: Device) {
|
||||
const ref = this.modalService.open(EditDeviceModalComponent, { scrollable: true, size: 'xl', fullscreen: 'md' });
|
||||
const ref = this.modalService.open(EditDeviceModalComponent, DefaultModalOptions);
|
||||
ref.componentInstance.device = device;
|
||||
|
||||
ref.closed.subscribe((result: Device | null) => {
|
||||
|
@ -31,6 +31,7 @@ import {Select2Module} from "ng-select2-component";
|
||||
import {LoadingComponent} from "../../shared/loading/loading.component";
|
||||
import {NgbModal} from "@ng-bootstrap/ng-bootstrap";
|
||||
import {PreviewImageModalComponent} from "../../shared/_components/carousel-modal/preview-image-modal.component";
|
||||
import {DefaultModalOptions} from "../../_models/default-modal-options";
|
||||
|
||||
interface ThemeContainer {
|
||||
downloadable?: DownloadableSiteTheme;
|
||||
@ -191,7 +192,7 @@ export class ThemeManagerComponent {
|
||||
previewImage(imgUrl: string) {
|
||||
if (imgUrl === '') return;
|
||||
|
||||
const ref = this.modalService.open(PreviewImageModalComponent, {size: 'xl', fullscreen: 'lg'});
|
||||
const ref = this.modalService.open(PreviewImageModalComponent, DefaultModalOptions);
|
||||
ref.componentInstance.title = this.selectedTheme!.name;
|
||||
ref.componentInstance.image = imgUrl;
|
||||
}
|
||||
|
@ -88,6 +88,7 @@ import {BulkOperationsComponent} from "../cards/bulk-operations/bulk-operations.
|
||||
import {DefaultDatePipe} from "../_pipes/default-date.pipe";
|
||||
import {MangaFormatPipe} from "../_pipes/manga-format.pipe";
|
||||
import {CoverImageComponent} from "../_single-module/cover-image/cover-image.component";
|
||||
import {DefaultModalOptions} from "../_models/default-modal-options";
|
||||
|
||||
enum TabID {
|
||||
|
||||
@ -527,7 +528,7 @@ export class VolumeDetailComponent implements OnInit {
|
||||
}
|
||||
|
||||
openEditModal() {
|
||||
const ref = this.modalService.open(EditVolumeModalComponent, { size: 'xl' });
|
||||
const ref = this.modalService.open(EditVolumeModalComponent, DefaultModalOptions);
|
||||
ref.componentInstance.volume = this.volume;
|
||||
ref.componentInstance.libraryType = this.libraryType;
|
||||
ref.componentInstance.libraryId = this.libraryId;
|
||||
@ -537,7 +538,7 @@ export class VolumeDetailComponent implements OnInit {
|
||||
}
|
||||
|
||||
openEditChapterModal(chapter: Chapter) {
|
||||
const ref = this.modalService.open(EditChapterModalComponent, { size: 'xl' });
|
||||
const ref = this.modalService.open(EditChapterModalComponent, DefaultModalOptions);
|
||||
ref.componentInstance.chapter = chapter;
|
||||
ref.componentInstance.libraryType = this.libraryType;
|
||||
ref.componentInstance.libraryId = this.libraryId;
|
||||
|
Loading…
x
Reference in New Issue
Block a user