Update some flows
- Promote/Create new from implicit profile - Fix save to parent updating implicit instead - Remove skip in the manga reader as the resolver has made it redundant - Add confirm to delete profile flow
This commit is contained in:
parent
9109988514
commit
45a44480e1
13 changed files with 289 additions and 106 deletions
|
|
@ -42,22 +42,6 @@ public class ReadingProfileController(ILogger<ReadingProfileController> logger,
|
||||||
return Ok(await readingProfileService.GetReadingProfileDtoForSeries(User.GetUserId(), seriesId));
|
return Ok(await readingProfileService.GetReadingProfileDtoForSeries(User.GetUserId(), seriesId));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Updates the given reading profile, must belong to the current user
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dto"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
/// <remarks>
|
|
||||||
/// This does not update connected series, and libraries.
|
|
||||||
/// Deletes all implicit profiles for series linked to this profile
|
|
||||||
/// </remarks>
|
|
||||||
[HttpPost]
|
|
||||||
public async Task<ActionResult> UpdateReadingProfile([FromBody] UserReadingProfileDto dto)
|
|
||||||
{
|
|
||||||
await readingProfileService.UpdateReadingProfile(User.GetUserId(), dto);
|
|
||||||
return Ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new reading profile for the current user
|
/// Creates a new reading profile for the current user
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -69,6 +53,17 @@ public class ReadingProfileController(ILogger<ReadingProfileController> logger,
|
||||||
return Ok(await readingProfileService.CreateReadingProfile(User.GetUserId(), dto));
|
return Ok(await readingProfileService.CreateReadingProfile(User.GetUserId(), dto));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Promotes the implicit profile to a user profile. Removes the series from other profiles
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="profileId"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost("promote")]
|
||||||
|
public async Task<ActionResult<UserReadingProfileDto>> PromoteImplicitReadingProfile([FromQuery] int profileId)
|
||||||
|
{
|
||||||
|
return Ok(await readingProfileService.PromoteImplicitProfile(User.GetUserId(), profileId));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Update the implicit reading profile for a series, creates one if none exists
|
/// Update the implicit reading profile for a series, creates one if none exists
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -76,10 +71,39 @@ public class ReadingProfileController(ILogger<ReadingProfileController> logger,
|
||||||
/// <param name="seriesId"></param>
|
/// <param name="seriesId"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost("series")]
|
[HttpPost("series")]
|
||||||
public async Task<ActionResult> UpdateReadingProfileForSeries([FromBody] UserReadingProfileDto dto, [FromQuery] int seriesId)
|
public async Task<ActionResult<UserReadingProfileDto>> UpdateReadingProfileForSeries([FromBody] UserReadingProfileDto dto, [FromQuery] int seriesId)
|
||||||
{
|
{
|
||||||
await readingProfileService.UpdateImplicitReadingProfile(User.GetUserId(), seriesId, dto);
|
var updatedProfile = await readingProfileService.UpdateImplicitReadingProfile(User.GetUserId(), seriesId, dto);
|
||||||
return Ok();
|
return Ok(updatedProfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the non-implicit reading profile for the given series, and removes implicit profiles
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dto"></param>
|
||||||
|
/// <param name="seriesId"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost("update-parent")]
|
||||||
|
public async Task<ActionResult<UserReadingProfileDto>> UpdateParentProfileForSeries([FromBody] UserReadingProfileDto dto, [FromQuery] int seriesId)
|
||||||
|
{
|
||||||
|
var newParentProfile = await readingProfileService.UpdateParent(User.GetUserId(), seriesId, dto);
|
||||||
|
return Ok(newParentProfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the given reading profile, must belong to the current user
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dto"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// This does not update connected series, and libraries.
|
||||||
|
/// Deletes all implicit profiles for series linked to this profile
|
||||||
|
/// </remarks>
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<ActionResult<UserReadingProfileDto>> UpdateReadingProfile([FromBody] UserReadingProfileDto dto)
|
||||||
|
{
|
||||||
|
var newProfile = await readingProfileService.UpdateReadingProfile(User.GetUserId(), dto);
|
||||||
|
return Ok(newProfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -107,8 +107,6 @@ public class UsersController : BaseApiController
|
||||||
existingPreferences.BlurUnreadSummaries = preferencesDto.BlurUnreadSummaries;
|
existingPreferences.BlurUnreadSummaries = preferencesDto.BlurUnreadSummaries;
|
||||||
existingPreferences.PromptForDownloadSize = preferencesDto.PromptForDownloadSize;
|
existingPreferences.PromptForDownloadSize = preferencesDto.PromptForDownloadSize;
|
||||||
existingPreferences.NoTransitions = preferencesDto.NoTransitions;
|
existingPreferences.NoTransitions = preferencesDto.NoTransitions;
|
||||||
existingPreferences.SwipeToPaginate = preferencesDto.SwipeToPaginate;
|
|
||||||
existingPreferences.AllowAutomaticWebtoonReaderDetection = preferencesDto.AllowAutomaticWebtoonReaderDetection;
|
|
||||||
existingPreferences.CollapseSeriesRelationships = preferencesDto.CollapseSeriesRelationships;
|
existingPreferences.CollapseSeriesRelationships = preferencesDto.CollapseSeriesRelationships;
|
||||||
existingPreferences.ShareReviews = preferencesDto.ShareReviews;
|
existingPreferences.ShareReviews = preferencesDto.ShareReviews;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -230,6 +230,8 @@
|
||||||
"scan-libraries": "Scan Libraries",
|
"scan-libraries": "Scan Libraries",
|
||||||
"kavita+-data-refresh": "Kavita+ Data Refresh",
|
"kavita+-data-refresh": "Kavita+ Data Refresh",
|
||||||
"backup": "Backup",
|
"backup": "Backup",
|
||||||
"update-yearly-stats": "Update Yearly Stats"
|
"update-yearly-stats": "Update Yearly Stats",
|
||||||
|
|
||||||
|
"generated-reading-profile-name": "Generated from {0}"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,15 +25,6 @@ public interface IReadingProfileService
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<UserReadingProfileDto> GetReadingProfileDtoForSeries(int userId, int seriesId);
|
Task<UserReadingProfileDto> GetReadingProfileDtoForSeries(int userId, int seriesId);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Updates a given reading profile for a user, and deletes all implicit profiles
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="userId"></param>
|
|
||||||
/// <param name="dto"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
/// <remarks>Does not update connected series and libraries</remarks>
|
|
||||||
Task UpdateReadingProfile(int userId, UserReadingProfileDto dto);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new reading profile for a user. Name must be unique per user
|
/// Creates a new reading profile for a user. Name must be unique per user
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -42,6 +33,14 @@ public interface IReadingProfileService
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<UserReadingProfileDto> CreateReadingProfile(int userId, UserReadingProfileDto dto);
|
Task<UserReadingProfileDto> CreateReadingProfile(int userId, UserReadingProfileDto dto);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Promotes the implicit profile to a user profile. Removes the series from other profiles
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userId"></param>
|
||||||
|
/// <param name="profileId"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<UserReadingProfileDto> PromoteImplicitProfile(int userId, int profileId);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the implicit reading profile for a series, creates one if none exists
|
/// Updates the implicit reading profile for a series, creates one if none exists
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -49,7 +48,25 @@ public interface IReadingProfileService
|
||||||
/// <param name="seriesId"></param>
|
/// <param name="seriesId"></param>
|
||||||
/// <param name="dto"></param>
|
/// <param name="dto"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task UpdateImplicitReadingProfile(int userId, int seriesId, UserReadingProfileDto dto);
|
Task<UserReadingProfileDto> UpdateImplicitReadingProfile(int userId, int seriesId, UserReadingProfileDto dto);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the non-implicit reading profile for the given series, and removes implicit profiles
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userId"></param>
|
||||||
|
/// <param name="seriesId"></param>
|
||||||
|
/// <param name="dto"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<UserReadingProfileDto> UpdateParent(int userId, int seriesId, UserReadingProfileDto dto);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates a given reading profile for a user, and deletes all implicit profiles
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userId"></param>
|
||||||
|
/// <param name="dto"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <remarks>Does not update connected series and libraries</remarks>
|
||||||
|
Task<UserReadingProfileDto> UpdateReadingProfile(int userId, UserReadingProfileDto dto);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Deletes a given profile for a user
|
/// Deletes a given profile for a user
|
||||||
|
|
@ -111,13 +128,16 @@ public class ReadingProfileService(IUnitOfWork unitOfWork, ILocalizationService
|
||||||
return mapper.Map<UserReadingProfileDto>(await GetReadingProfileForSeries(userId, seriesId));
|
return mapper.Map<UserReadingProfileDto>(await GetReadingProfileForSeries(userId, seriesId));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<AppUserReadingProfile> GetReadingProfileForSeries(int userId, int seriesId)
|
public async Task<AppUserReadingProfile> GetReadingProfileForSeries(int userId, int seriesId, bool skipImplicit = false)
|
||||||
{
|
{
|
||||||
var profiles = await unitOfWork.AppUserReadingProfileRepository.GetProfilesForUser(userId);
|
var profiles = await unitOfWork.AppUserReadingProfileRepository.GetProfilesForUser(userId);
|
||||||
|
|
||||||
var implicitSeriesProfile = profiles
|
if (!skipImplicit)
|
||||||
.FirstOrDefault(p => p.SeriesIds.Contains(seriesId) && p.Kind == ReadingProfileKind.Implicit);
|
{
|
||||||
if (implicitSeriesProfile != null) return implicitSeriesProfile;
|
var implicitSeriesProfile = profiles
|
||||||
|
.FirstOrDefault(p => p.SeriesIds.Contains(seriesId) && p.Kind == ReadingProfileKind.Implicit);
|
||||||
|
if (implicitSeriesProfile != null) return implicitSeriesProfile;
|
||||||
|
}
|
||||||
|
|
||||||
var seriesProfile = profiles
|
var seriesProfile = profiles
|
||||||
.FirstOrDefault(p => p.SeriesIds.Contains(seriesId) && p.Kind != ReadingProfileKind.Implicit);
|
.FirstOrDefault(p => p.SeriesIds.Contains(seriesId) && p.Kind != ReadingProfileKind.Implicit);
|
||||||
|
|
@ -134,7 +154,20 @@ public class ReadingProfileService(IUnitOfWork unitOfWork, ILocalizationService
|
||||||
return profiles.First(p => p.Kind == ReadingProfileKind.Default);
|
return profiles.First(p => p.Kind == ReadingProfileKind.Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateReadingProfile(int userId, UserReadingProfileDto dto)
|
public async Task<UserReadingProfileDto> UpdateParent(int userId, int seriesId, UserReadingProfileDto dto)
|
||||||
|
{
|
||||||
|
var parentProfile = await GetReadingProfileForSeries(userId, seriesId, true);
|
||||||
|
|
||||||
|
UpdateReaderProfileFields(parentProfile, dto, false);
|
||||||
|
unitOfWork.AppUserReadingProfileRepository.Update(parentProfile);
|
||||||
|
|
||||||
|
await DeleteImplicateReadingProfilesForSeries(userId, [seriesId]);
|
||||||
|
|
||||||
|
await unitOfWork.CommitAsync();
|
||||||
|
return mapper.Map<UserReadingProfileDto>(parentProfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<UserReadingProfileDto> UpdateReadingProfile(int userId, UserReadingProfileDto dto)
|
||||||
{
|
{
|
||||||
var profile = await unitOfWork.AppUserReadingProfileRepository.GetUserProfile(userId, dto.Id);
|
var profile = await unitOfWork.AppUserReadingProfileRepository.GetUserProfile(userId, dto.Id);
|
||||||
if (profile == null) throw new KavitaException("profile-does-not-exist");
|
if (profile == null) throw new KavitaException("profile-does-not-exist");
|
||||||
|
|
@ -145,6 +178,7 @@ public class ReadingProfileService(IUnitOfWork unitOfWork, ILocalizationService
|
||||||
await DeleteImplicateReadingProfilesForSeries(userId, profile.SeriesIds);
|
await DeleteImplicateReadingProfilesForSeries(userId, profile.SeriesIds);
|
||||||
|
|
||||||
await unitOfWork.CommitAsync();
|
await unitOfWork.CommitAsync();
|
||||||
|
return mapper.Map<UserReadingProfileDto>(profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<UserReadingProfileDto> CreateReadingProfile(int userId, UserReadingProfileDto dto)
|
public async Task<UserReadingProfileDto> CreateReadingProfile(int userId, UserReadingProfileDto dto)
|
||||||
|
|
@ -165,7 +199,30 @@ public class ReadingProfileService(IUnitOfWork unitOfWork, ILocalizationService
|
||||||
return mapper.Map<UserReadingProfileDto>(newProfile);
|
return mapper.Map<UserReadingProfileDto>(newProfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateImplicitReadingProfile(int userId, int seriesId, UserReadingProfileDto dto)
|
public async Task<UserReadingProfileDto> PromoteImplicitProfile(int userId, int profileId)
|
||||||
|
{
|
||||||
|
var profile = await unitOfWork.AppUserReadingProfileRepository.GetUserProfile(userId, profileId);
|
||||||
|
if (profile == null) throw new KavitaException("profile-does-not-exist");
|
||||||
|
|
||||||
|
if (profile.Kind != ReadingProfileKind.Implicit) throw new KavitaException("cannot-promote-non-implicit-profile");
|
||||||
|
|
||||||
|
// Implicit profiles are only bound to one profile
|
||||||
|
var series = await unitOfWork.SeriesRepository.GetSeriesByIdAsync(profile.SeriesIds[0]);
|
||||||
|
if (series == null) throw new KavitaException("series-doesnt-exist"); // Shouldn't happen
|
||||||
|
|
||||||
|
await RemoveSeriesFromUserProfiles(userId, [series.Id]);
|
||||||
|
|
||||||
|
profile.Kind = ReadingProfileKind.User;
|
||||||
|
profile.Name = await localizationService.Translate(userId, "generated-reading-profile-name", series.Name);
|
||||||
|
profile.NormalizedName = profile.Name.ToNormalized();
|
||||||
|
|
||||||
|
unitOfWork.AppUserReadingProfileRepository.Update(profile);
|
||||||
|
await unitOfWork.CommitAsync();
|
||||||
|
|
||||||
|
return mapper.Map<UserReadingProfileDto>(profile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<UserReadingProfileDto> UpdateImplicitReadingProfile(int userId, int seriesId, UserReadingProfileDto dto)
|
||||||
{
|
{
|
||||||
var user = await unitOfWork.UserRepository.GetUserByIdAsync(userId, AppUserIncludes.UserPreferences);
|
var user = await unitOfWork.UserRepository.GetUserByIdAsync(userId, AppUserIncludes.UserPreferences);
|
||||||
if (user == null) throw new UnauthorizedAccessException();
|
if (user == null) throw new UnauthorizedAccessException();
|
||||||
|
|
@ -179,7 +236,8 @@ public class ReadingProfileService(IUnitOfWork unitOfWork, ILocalizationService
|
||||||
UpdateReaderProfileFields(existingProfile, dto, false);
|
UpdateReaderProfileFields(existingProfile, dto, false);
|
||||||
unitOfWork.AppUserReadingProfileRepository.Update(existingProfile);
|
unitOfWork.AppUserReadingProfileRepository.Update(existingProfile);
|
||||||
await unitOfWork.CommitAsync();
|
await unitOfWork.CommitAsync();
|
||||||
return;
|
|
||||||
|
return mapper.Map<UserReadingProfileDto>(existingProfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
var series = await unitOfWork.SeriesRepository.GetSeriesByIdAsync(seriesId) ?? throw new KeyNotFoundException();
|
var series = await unitOfWork.SeriesRepository.GetSeriesByIdAsync(seriesId) ?? throw new KeyNotFoundException();
|
||||||
|
|
@ -195,6 +253,8 @@ public class ReadingProfileService(IUnitOfWork unitOfWork, ILocalizationService
|
||||||
|
|
||||||
user.ReadingProfiles.Add(newProfile);
|
user.ReadingProfiles.Add(newProfile);
|
||||||
await unitOfWork.CommitAsync();
|
await unitOfWork.CommitAsync();
|
||||||
|
|
||||||
|
return mapper.Map<UserReadingProfileDto>(newProfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DeleteReadingProfile(int userId, int profileId)
|
public async Task DeleteReadingProfile(int userId, int profileId)
|
||||||
|
|
@ -300,6 +360,16 @@ public class ReadingProfileService(IUnitOfWork unitOfWork, ILocalizationService
|
||||||
unitOfWork.AppUserReadingProfileRepository.RemoveRange(implicitProfiles);
|
unitOfWork.AppUserReadingProfileRepository.RemoveRange(implicitProfiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task RemoveSeriesFromUserProfiles(int userId, IList<int> seriesIds)
|
||||||
|
{
|
||||||
|
var profiles = await unitOfWork.AppUserReadingProfileRepository.GetProfilesForUser(userId);
|
||||||
|
var userProfiles = profiles
|
||||||
|
.Where(rp => rp.SeriesIds.Intersect(seriesIds).Any())
|
||||||
|
.Where(rp => rp.Kind == ReadingProfileKind.User)
|
||||||
|
.ToList();
|
||||||
|
unitOfWork.AppUserReadingProfileRepository.RemoveRange(userProfiles);
|
||||||
|
}
|
||||||
|
|
||||||
public static void UpdateReaderProfileFields(AppUserReadingProfile existingProfile, UserReadingProfileDto dto, bool updateName = true)
|
public static void UpdateReaderProfileFields(AppUserReadingProfile existingProfile, UserReadingProfileDto dto, bool updateName = true)
|
||||||
{
|
{
|
||||||
if (updateName && !string.IsNullOrEmpty(dto.Name) && existingProfile.NormalizedName != dto.Name.ToNormalized())
|
if (updateName && !string.IsNullOrEmpty(dto.Name) && existingProfile.NormalizedName != dto.Name.ToNormalized())
|
||||||
|
|
|
||||||
|
|
@ -17,15 +17,23 @@ export class ReadingProfileService {
|
||||||
}
|
}
|
||||||
|
|
||||||
updateProfile(profile: ReadingProfile) {
|
updateProfile(profile: ReadingProfile) {
|
||||||
return this.httpClient.post(this.baseUrl + "ReadingProfile", profile);
|
return this.httpClient.post<ReadingProfile>(this.baseUrl + "ReadingProfile", profile);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateParentProfile(seriesId: number, profile: ReadingProfile) {
|
||||||
|
return this.httpClient.post<ReadingProfile>(this.baseUrl + `ReadingProfile/update-parent?seriesId=${seriesId}`, profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
createProfile(profile: ReadingProfile) {
|
createProfile(profile: ReadingProfile) {
|
||||||
return this.httpClient.post<ReadingProfile>(this.baseUrl + "ReadingProfile/create", profile);
|
return this.httpClient.post<ReadingProfile>(this.baseUrl + "ReadingProfile/create", profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
promoteProfile(profileId: number) {
|
||||||
|
return this.httpClient.post<ReadingProfile>(this.baseUrl + "ReadingProfile/promote?profileId=" + profileId, {});
|
||||||
|
}
|
||||||
|
|
||||||
updateImplicit(profile: ReadingProfile, seriesId: number) {
|
updateImplicit(profile: ReadingProfile, seriesId: number) {
|
||||||
return this.httpClient.post(this.baseUrl + "ReadingProfile/series?seriesId="+seriesId, profile);
|
return this.httpClient.post<ReadingProfile>(this.baseUrl + "ReadingProfile/series?seriesId="+seriesId, profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
all() {
|
all() {
|
||||||
|
|
|
||||||
|
|
@ -618,7 +618,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
this.router.navigateByUrl('/home');
|
this.router.navigateByUrl('/home');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//this.setupReaderSettings(); // TODO: Implement this Amelia
|
//this.setupReaderSettings(); // TODO: Implement this Amelia, it works without am I missing something?
|
||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -168,10 +168,21 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row g-0 justify-content-between mt-2">
|
<div class="d-flex gap-2 mt-2 flex-wrap mb-2">
|
||||||
<button (click)="savePref()" class="btn btn-primary col">{{t('save-global')}}</button>
|
<button class="btn btn-primary"
|
||||||
|
[disabled]="readingProfile.kind !== ReadingProfileKind.Implicit"
|
||||||
|
(click)="updateParentPref()">
|
||||||
|
{{ t('update-parent') }}
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-primary"
|
||||||
|
[ngbTooltip]="t('create-new-tooltip')"
|
||||||
|
[disabled]="readingProfile.kind !== ReadingProfileKind.Implicit"
|
||||||
|
(click)="createNewProfileFromImplicit()">
|
||||||
|
{{ t('create-new') }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,10 +36,11 @@ import {
|
||||||
NgbAccordionItem,
|
NgbAccordionItem,
|
||||||
NgbTooltip
|
NgbTooltip
|
||||||
} from '@ng-bootstrap/ng-bootstrap';
|
} from '@ng-bootstrap/ng-bootstrap';
|
||||||
import {TranslocoDirective} from "@jsverse/transloco";
|
import {translate, TranslocoDirective} from "@jsverse/transloco";
|
||||||
import {ReadingProfileService} from "../../../_services/reading-profile.service";
|
import {ReadingProfileService} from "../../../_services/reading-profile.service";
|
||||||
import {ReadingProfile} from "../../../_models/preferences/reading-profiles";
|
import {ReadingProfile, ReadingProfileKind} from "../../../_models/preferences/reading-profiles";
|
||||||
import {debounceTime, distinctUntilChanged, tap} from "rxjs/operators";
|
import {debounceTime, distinctUntilChanged, tap} from "rxjs/operators";
|
||||||
|
import {ToastrService} from "ngx-toastr";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used for book reader. Do not use for other components
|
* Used for book reader. Do not use for other components
|
||||||
|
|
@ -185,7 +186,8 @@ export class ReaderSettingsComponent implements OnInit {
|
||||||
|
|
||||||
constructor(private bookService: BookService, private accountService: AccountService,
|
constructor(private bookService: BookService, private accountService: AccountService,
|
||||||
@Inject(DOCUMENT) private document: Document, private themeService: ThemeService,
|
@Inject(DOCUMENT) private document: Document, private themeService: ThemeService,
|
||||||
private readonly cdRef: ChangeDetectorRef, private readingProfileService: ReadingProfileService) {}
|
private readonly cdRef: ChangeDetectorRef, private readingProfileService: ReadingProfileService,
|
||||||
|
private toastr: ToastrService) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
|
||||||
|
|
@ -226,14 +228,13 @@ export class ReaderSettingsComponent implements OnInit {
|
||||||
this.layoutModeUpdate.emit(this.readingProfile.bookReaderLayoutMode);
|
this.layoutModeUpdate.emit(this.readingProfile.bookReaderLayoutMode);
|
||||||
this.immersiveMode.emit(this.readingProfile.bookReaderImmersiveMode);
|
this.immersiveMode.emit(this.readingProfile.bookReaderImmersiveMode);
|
||||||
|
|
||||||
this.resetSettings();
|
|
||||||
|
|
||||||
this.accountService.currentUser$.pipe(take(1)).subscribe(user => {
|
this.accountService.currentUser$.pipe(take(1)).subscribe(user => {
|
||||||
if (user) {
|
if (user) {
|
||||||
this.user = user;
|
this.user = user;
|
||||||
} else {
|
|
||||||
this.resetSettings();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// User needs to be loaded before we call this
|
||||||
|
this.resetSettings();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -393,8 +394,29 @@ export class ReaderSettingsComponent implements OnInit {
|
||||||
this.fullscreen.emit();
|
this.fullscreen.emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
savePref() {
|
// menu only code
|
||||||
this.readingProfileService.updateProfile(this.packReadingProfile()).subscribe()
|
updateParentPref() {
|
||||||
|
if (this.readingProfile.kind !== ReadingProfileKind.Implicit) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.readingProfileService.updateParentProfile(this.seriesId, this.packReadingProfile()).subscribe(newProfile => {
|
||||||
|
this.readingProfile = newProfile;
|
||||||
|
this.toastr.success(translate('manga-reader.reading-profile-updated'));
|
||||||
|
this.cdRef.markForCheck();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
createNewProfileFromImplicit() {
|
||||||
|
if (this.readingProfile.kind !== ReadingProfileKind.Implicit) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.readingProfileService.promoteProfile(this.readingProfile.id).subscribe(newProfile => {
|
||||||
|
this.readingProfile = newProfile;
|
||||||
|
this.toastr.success(translate("manga-reader.reading-profile-promoted"));
|
||||||
|
this.cdRef.markForCheck();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private packReadingProfile(): ReadingProfile {
|
private packReadingProfile(): ReadingProfile {
|
||||||
|
|
@ -417,4 +439,5 @@ export class ReaderSettingsComponent implements OnInit {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected readonly ReadingProfileKind = ReadingProfileKind;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -310,7 +310,8 @@
|
||||||
|
|
||||||
|
|
||||||
<div class="col-md-6 col-sm-12">
|
<div class="col-md-6 col-sm-12">
|
||||||
<button class="btn btn-primary" (click)="savePref()">{{t('save-globally')}}</button>
|
<button class="btn btn-primary" [disabled]="readingProfile.kind !== ReadingProfileKind.Implicit" (click)="updateParentPref()">{{t('update-parent')}}</button>
|
||||||
|
<button class="btn btn-primary ms-2" [ngbTooltip]="t('create-new-tooltip')" [disabled]="readingProfile.kind !== ReadingProfileKind.Implicit" (click)="createNewProfileFromImplicit()">{{t('create-new')}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@ import {
|
||||||
merge,
|
merge,
|
||||||
Observable,
|
Observable,
|
||||||
ReplaySubject,
|
ReplaySubject,
|
||||||
skip,
|
|
||||||
Subject,
|
Subject,
|
||||||
take,
|
take,
|
||||||
tap
|
tap
|
||||||
|
|
@ -33,7 +32,7 @@ import {
|
||||||
import {ChangeContext, LabelType, NgxSliderModule, Options} from '@angular-slider/ngx-slider';
|
import {ChangeContext, LabelType, NgxSliderModule, Options} from '@angular-slider/ngx-slider';
|
||||||
import {animate, state, style, transition, trigger} from '@angular/animations';
|
import {animate, state, style, transition, trigger} from '@angular/animations';
|
||||||
import {FormBuilder, FormControl, FormGroup, ReactiveFormsModule} from '@angular/forms';
|
import {FormBuilder, FormControl, FormGroup, ReactiveFormsModule} from '@angular/forms';
|
||||||
import {NgbModal} from '@ng-bootstrap/ng-bootstrap';
|
import {NgbModal, NgbTooltip} from '@ng-bootstrap/ng-bootstrap';
|
||||||
import {ToastrService} from 'ngx-toastr';
|
import {ToastrService} from 'ngx-toastr';
|
||||||
import {ShortcutsModalComponent} from 'src/app/reader-shared/_modals/shortcuts-modal/shortcuts-modal.component';
|
import {ShortcutsModalComponent} from 'src/app/reader-shared/_modals/shortcuts-modal/shortcuts-modal.component';
|
||||||
import {Stack} from 'src/app/shared/data-structures/stack';
|
import {Stack} from 'src/app/shared/data-structures/stack';
|
||||||
|
|
@ -70,7 +69,12 @@ import {LoadingComponent} from '../../../shared/loading/loading.component';
|
||||||
import {translate, TranslocoDirective} from "@jsverse/transloco";
|
import {translate, TranslocoDirective} from "@jsverse/transloco";
|
||||||
import {shareReplay} from "rxjs/operators";
|
import {shareReplay} from "rxjs/operators";
|
||||||
import {DblClickDirective} from "../../../_directives/dbl-click.directive";
|
import {DblClickDirective} from "../../../_directives/dbl-click.directive";
|
||||||
import {layoutModes, pageSplitOptions, ReadingProfile} from "../../../_models/preferences/reading-profiles";
|
import {
|
||||||
|
layoutModes,
|
||||||
|
pageSplitOptions,
|
||||||
|
ReadingProfile,
|
||||||
|
ReadingProfileKind
|
||||||
|
} from "../../../_models/preferences/reading-profiles";
|
||||||
import {ReadingProfileService} from "../../../_services/reading-profile.service";
|
import {ReadingProfileService} from "../../../_services/reading-profile.service";
|
||||||
import {ConfirmService} from "../../../shared/confirm.service";
|
import {ConfirmService} from "../../../shared/confirm.service";
|
||||||
|
|
||||||
|
|
@ -125,10 +129,10 @@ enum KeyDirection {
|
||||||
])
|
])
|
||||||
])
|
])
|
||||||
],
|
],
|
||||||
imports: [NgStyle, LoadingComponent, SwipeDirective, CanvasRendererComponent, SingleRendererComponent,
|
imports: [NgStyle, LoadingComponent, SwipeDirective, CanvasRendererComponent, SingleRendererComponent,
|
||||||
DoubleRendererComponent, DoubleReverseRendererComponent, DoubleNoCoverRendererComponent, InfiniteScrollerComponent,
|
DoubleRendererComponent, DoubleReverseRendererComponent, DoubleNoCoverRendererComponent, InfiniteScrollerComponent,
|
||||||
NgxSliderModule, ReactiveFormsModule, FittingIconPipe, ReaderModeIconPipe,
|
NgxSliderModule, ReactiveFormsModule, FittingIconPipe, ReaderModeIconPipe,
|
||||||
FullscreenIconPipe, TranslocoDirective, PercentPipe, NgClass, AsyncPipe, DblClickDirective]
|
FullscreenIconPipe, TranslocoDirective, PercentPipe, NgClass, AsyncPipe, DblClickDirective, NgbTooltip]
|
||||||
})
|
})
|
||||||
export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
|
|
||||||
|
|
@ -537,14 +541,13 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
this.generalSettingsForm.valueChanges.pipe(
|
this.generalSettingsForm.valueChanges.pipe(
|
||||||
debounceTime(300),
|
debounceTime(300),
|
||||||
distinctUntilChanged(),
|
distinctUntilChanged(),
|
||||||
skip(1), // Skip the initial creation of the form, we do not want an implicit profile of this snapshot
|
|
||||||
takeUntilDestroyed(this.destroyRef),
|
takeUntilDestroyed(this.destroyRef),
|
||||||
map(_ => this.packReadingProfile()),
|
map(_ => this.packReadingProfile()),
|
||||||
distinctUntilChanged(),
|
distinctUntilChanged(),
|
||||||
tap(newProfile => {
|
tap(newProfile => {
|
||||||
this.readingProfileService.updateImplicit(newProfile, this.seriesId).subscribe({
|
this.readingProfileService.updateImplicit(newProfile, this.seriesId).subscribe({
|
||||||
next: () => {
|
next: updatedProfile => {
|
||||||
this.readingProfile = newProfile;
|
this.readingProfile = updatedProfile;
|
||||||
},
|
},
|
||||||
error: err => {
|
error: err => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
|
@ -1783,10 +1786,28 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
// menu only code
|
// menu only code
|
||||||
savePref() {
|
updateParentPref() {
|
||||||
this.readingProfileService.updateProfile(this.packReadingProfile()).subscribe(_ => {
|
if (this.readingProfile.kind !== ReadingProfileKind.Implicit) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.readingProfileService.updateParentProfile(this.seriesId, this.packReadingProfile()).subscribe(newProfile => {
|
||||||
|
this.readingProfile = newProfile;
|
||||||
this.toastr.success(translate('manga-reader.reading-profile-updated'));
|
this.toastr.success(translate('manga-reader.reading-profile-updated'));
|
||||||
})
|
this.cdRef.markForCheck();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
createNewProfileFromImplicit() {
|
||||||
|
if (this.readingProfile.kind !== ReadingProfileKind.Implicit) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.readingProfileService.promoteProfile(this.readingProfile.id).subscribe(newProfile => {
|
||||||
|
this.readingProfile = newProfile;
|
||||||
|
this.toastr.success(translate("manga-reader.reading-profile-promoted"));
|
||||||
|
this.cdRef.markForCheck();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
translatePrefOptions(o: {text: string, value: any}) {
|
translatePrefOptions(o: {text: string, value: any}) {
|
||||||
|
|
@ -1811,4 +1832,5 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected readonly ReadingProfileKind = ReadingProfileKind;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
@if (!loading) {
|
@if (!loading) {
|
||||||
<div class="position-relative">
|
<div class="position-relative">
|
||||||
<button class="btn btn-outline-primary position-absolute custom-position" (click)="addNew()" [title]="t('add')">
|
<button class="btn btn-outline-primary position-absolute custom-position" [ngbTooltip]="t('add-tooltip')" (click)="addNew()" [title]="t('add')">
|
||||||
<i class="fa fa-plus" aria-hidden="true"></i><span class="phone-hidden ms-1">{{t('add')}}</span>
|
<i class="fa fa-plus" aria-hidden="true"></i><span class="phone-hidden ms-1">{{t('add')}}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -55,7 +55,7 @@
|
||||||
|
|
||||||
@if (selectedProfile.id !== 0) {
|
@if (selectedProfile.id !== 0) {
|
||||||
<div class="d-flex justify-content-between">
|
<div class="d-flex justify-content-between">
|
||||||
<button class="btn btn-danger" (click)="delete(selectedProfile!.id)" [disabled]="selectedProfile.kind === ReadingProfileKind.Default">
|
<button class="btn btn-danger" (click)="delete(selectedProfile!)" [disabled]="selectedProfile.kind === ReadingProfileKind.Default">
|
||||||
<i class="fa fa-trash" aria-hidden="true"></i>
|
<i class="fa fa-trash" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import {
|
||||||
ReadingProfileKind,
|
ReadingProfileKind,
|
||||||
scalingOptions
|
scalingOptions
|
||||||
} from "../../_models/preferences/reading-profiles";
|
} from "../../_models/preferences/reading-profiles";
|
||||||
import {translate, TranslocoDirective} from "@jsverse/transloco";
|
import {translate, TranslocoDirective, TranslocoService} from "@jsverse/transloco";
|
||||||
import {NgStyle, NgTemplateOutlet, TitleCasePipe} from "@angular/common";
|
import {NgStyle, NgTemplateOutlet, TitleCasePipe} from "@angular/common";
|
||||||
import {VirtualScrollerModule} from "@iharbeck/ngx-virtual-scroller";
|
import {VirtualScrollerModule} from "@iharbeck/ngx-virtual-scroller";
|
||||||
import {User} from "../../_models/user";
|
import {User} from "../../_models/user";
|
||||||
|
|
@ -41,11 +41,12 @@ import {SettingItemComponent} from "../../settings/_components/setting-item/sett
|
||||||
import {SettingSwitchComponent} from "../../settings/_components/setting-switch/setting-switch.component";
|
import {SettingSwitchComponent} from "../../settings/_components/setting-switch/setting-switch.component";
|
||||||
import {WritingStylePipe} from "../../_pipes/writing-style.pipe";
|
import {WritingStylePipe} from "../../_pipes/writing-style.pipe";
|
||||||
import {ColorPickerDirective} from "ngx-color-picker";
|
import {ColorPickerDirective} from "ngx-color-picker";
|
||||||
import {NgbNav, NgbNavContent, NgbNavItem, NgbNavLinkBase, NgbNavOutlet} from "@ng-bootstrap/ng-bootstrap";
|
import {NgbNav, NgbNavContent, NgbNavItem, NgbNavLinkBase, NgbNavOutlet, NgbTooltip} from "@ng-bootstrap/ng-bootstrap";
|
||||||
import {filter} from "rxjs";
|
import {filter} from "rxjs";
|
||||||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||||
import {LoadingComponent} from "../../shared/loading/loading.component";
|
import {LoadingComponent} from "../../shared/loading/loading.component";
|
||||||
import {ToastrService} from "ngx-toastr";
|
import {ToastrService} from "ngx-toastr";
|
||||||
|
import {ConfirmService} from "../../shared/confirm.service";
|
||||||
|
|
||||||
enum TabId {
|
enum TabId {
|
||||||
ImageReader = "image-reader",
|
ImageReader = "image-reader",
|
||||||
|
|
@ -83,6 +84,7 @@ enum TabId {
|
||||||
NgbNavContent,
|
NgbNavContent,
|
||||||
NgbNavOutlet,
|
NgbNavOutlet,
|
||||||
LoadingComponent,
|
LoadingComponent,
|
||||||
|
NgbTooltip,
|
||||||
],
|
],
|
||||||
templateUrl: './manage-reading-profiles.component.html',
|
templateUrl: './manage-reading-profiles.component.html',
|
||||||
styleUrl: './manage-reading-profiles.component.scss',
|
styleUrl: './manage-reading-profiles.component.scss',
|
||||||
|
|
@ -113,6 +115,8 @@ export class ManageReadingProfilesComponent implements OnInit {
|
||||||
private bookService: BookService,
|
private bookService: BookService,
|
||||||
private destroyRef: DestroyRef,
|
private destroyRef: DestroyRef,
|
||||||
private toastr: ToastrService,
|
private toastr: ToastrService,
|
||||||
|
private confirmService: ConfirmService,
|
||||||
|
private transLoco: TranslocoService,
|
||||||
) {
|
) {
|
||||||
this.fontFamilies = this.bookService.getFontFamilies().map(f => f.title);
|
this.fontFamilies = this.bookService.getFontFamilies().map(f => f.title);
|
||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
|
|
@ -129,15 +133,24 @@ export class ManageReadingProfilesComponent implements OnInit {
|
||||||
this.readingProfiles = profiles;
|
this.readingProfiles = profiles;
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
this.setupForm();
|
this.setupForm();
|
||||||
|
|
||||||
|
const defaultProfile = this.readingProfiles.find(rp => rp.kind === ReadingProfileKind.Default);
|
||||||
|
this.selectProfile(defaultProfile);
|
||||||
|
|
||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(id: number) {
|
async delete(readingProfile: ReadingProfile) {
|
||||||
this.readingProfileService.delete(id).subscribe(() => {
|
if (!await this.confirmService.confirm(this.transLoco.translate("manage-reading-profiles.confirm", {name: readingProfile.name}))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
this.readingProfileService.delete(readingProfile.id).subscribe(() => {
|
||||||
this.selectProfile(undefined);
|
this.selectProfile(undefined);
|
||||||
this.readingProfiles = this.readingProfiles.filter(o => o.id !== id);
|
this.readingProfiles = this.readingProfiles.filter(o => o.id !== readingProfile.id);
|
||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -204,39 +217,41 @@ export class ManageReadingProfilesComponent implements OnInit {
|
||||||
distinctUntilChanged(),
|
distinctUntilChanged(),
|
||||||
filter(_ => this.readingProfileForm!.valid),
|
filter(_ => this.readingProfileForm!.valid),
|
||||||
takeUntilDestroyed(this.destroyRef),
|
takeUntilDestroyed(this.destroyRef),
|
||||||
tap(_ => {
|
tap(_ => this.autoSave()),
|
||||||
if (this.selectedProfile!.id == 0) {
|
|
||||||
this.readingProfileService.createProfile(this.packData()).subscribe({
|
|
||||||
next: createdProfile => {
|
|
||||||
this.selectedProfile = createdProfile;
|
|
||||||
this.readingProfiles.push(createdProfile);
|
|
||||||
this.cdRef.markForCheck();
|
|
||||||
},
|
|
||||||
error: err => {
|
|
||||||
console.log(err);
|
|
||||||
this.toastr.error(err.message);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
const profile = this.packData();
|
|
||||||
this.readingProfileService.updateProfile(profile).subscribe({
|
|
||||||
next: _ => {
|
|
||||||
this.readingProfiles = this.readingProfiles.map(p => {
|
|
||||||
if (p.id !== profile.id) return p;
|
|
||||||
return profile;
|
|
||||||
});
|
|
||||||
this.cdRef.markForCheck();
|
|
||||||
},
|
|
||||||
error: err => {
|
|
||||||
console.log(err);
|
|
||||||
this.toastr.error(err.message);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
).subscribe();
|
).subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private autoSave() {
|
||||||
|
if (this.selectedProfile!.id == 0) {
|
||||||
|
this.readingProfileService.createProfile(this.packData()).subscribe({
|
||||||
|
next: createdProfile => {
|
||||||
|
this.selectedProfile = createdProfile;
|
||||||
|
this.readingProfiles.push(createdProfile);
|
||||||
|
this.cdRef.markForCheck();
|
||||||
|
},
|
||||||
|
error: err => {
|
||||||
|
console.log(err);
|
||||||
|
this.toastr.error(err.message);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
const profile = this.packData();
|
||||||
|
this.readingProfileService.updateProfile(profile).subscribe({
|
||||||
|
next: _ => {
|
||||||
|
this.readingProfiles = this.readingProfiles.map(p => {
|
||||||
|
if (p.id !== profile.id) return p;
|
||||||
|
return profile;
|
||||||
|
});
|
||||||
|
this.cdRef.markForCheck();
|
||||||
|
},
|
||||||
|
error: err => {
|
||||||
|
console.log(err);
|
||||||
|
this.toastr.error(err.message);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private packData(): ReadingProfile {
|
private packData(): ReadingProfile {
|
||||||
const data: ReadingProfile = this.readingProfileForm!.getRawValue();
|
const data: ReadingProfile = this.readingProfileForm!.getRawValue();
|
||||||
data.id = this.selectedProfile!.id;
|
data.id = this.selectedProfile!.id;
|
||||||
|
|
|
||||||
|
|
@ -1132,7 +1132,11 @@
|
||||||
"line-spacing-label": "{{manage-reading-profiles.line-height-book-label}}",
|
"line-spacing-label": "{{manage-reading-profiles.line-height-book-label}}",
|
||||||
"margin-label": "{{manage-reading-profiles.margin-book-label}}",
|
"margin-label": "{{manage-reading-profiles.margin-book-label}}",
|
||||||
"reset-to-defaults": "Reset to Defaults",
|
"reset-to-defaults": "Reset to Defaults",
|
||||||
"save-global": "Save to your reading profile",
|
"update-parent": "Update parent profile",
|
||||||
|
"create-new": "Promote profile",
|
||||||
|
"create-new-tooltip": "Create a new manageable profile from your current implicit one",
|
||||||
|
"reading-profile-updated": "Reading profile updated",
|
||||||
|
"reading-profile-promoted": "Reading profile promoted",
|
||||||
"reader-settings-title": "Reader Settings",
|
"reader-settings-title": "Reader Settings",
|
||||||
"reading-direction-label": "{{manage-reading-profiles.reading-direction-book-label}}",
|
"reading-direction-label": "{{manage-reading-profiles.reading-direction-book-label}}",
|
||||||
"right-to-left": "Right to Left",
|
"right-to-left": "Right to Left",
|
||||||
|
|
@ -1948,7 +1952,9 @@
|
||||||
|
|
||||||
"manga-reader": {
|
"manga-reader": {
|
||||||
"back": "Back",
|
"back": "Back",
|
||||||
"save-globally": "Save Globally",
|
"update-parent": "Update parent profile",
|
||||||
|
"create-new": "Promote profile",
|
||||||
|
"create-new-tooltip": "Create a new manageable profile from your current implicit one",
|
||||||
"incognito-alt": "Incognito mode is on. Toggle to turn off.",
|
"incognito-alt": "Incognito mode is on. Toggle to turn off.",
|
||||||
"incognito-title": "Incognito Mode:",
|
"incognito-title": "Incognito Mode:",
|
||||||
"shortcuts-menu-alt": "Keyboard Shortcuts Modal",
|
"shortcuts-menu-alt": "Keyboard Shortcuts Modal",
|
||||||
|
|
@ -1985,6 +1991,7 @@
|
||||||
"no-next-chapter": "No Next Chapter",
|
"no-next-chapter": "No Next Chapter",
|
||||||
"no-prev-chapter": "No Previous Chapter",
|
"no-prev-chapter": "No Previous Chapter",
|
||||||
"reading-profile-updated": "Reading profile updated",
|
"reading-profile-updated": "Reading profile updated",
|
||||||
|
"reading-profile-promoted": "Reading profile promoted",
|
||||||
"emulate-comic-book-label": "{{manage-reading-profiles.emulate-comic-book-label}}",
|
"emulate-comic-book-label": "{{manage-reading-profiles.emulate-comic-book-label}}",
|
||||||
"series-progress": "Series Progress: {{percentage}}"
|
"series-progress": "Series Progress: {{percentage}}"
|
||||||
},
|
},
|
||||||
|
|
@ -2813,8 +2820,10 @@
|
||||||
"profiles-title": "Your reading profiles",
|
"profiles-title": "Your reading profiles",
|
||||||
"default-profile": "Default",
|
"default-profile": "Default",
|
||||||
"add": "{{common.add}}",
|
"add": "{{common.add}}",
|
||||||
|
"add-tooltip": "Your new profile will be saved after making a change to it",
|
||||||
"make-default": "Set as default",
|
"make-default": "Set as default",
|
||||||
"no-selected": "No profile selected",
|
"no-selected": "No profile selected",
|
||||||
|
"confirm": "Are you sure you want to delete the reading profile {{name}}?",
|
||||||
"selection-tip": "Select a profile from the list, or create a new one at the top right",
|
"selection-tip": "Select a profile from the list, or create a new one at the top right",
|
||||||
|
|
||||||
"image-reader-settings-title": "Image Reader",
|
"image-reader-settings-title": "Image Reader",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue