diff --git a/API.Tests/Services/ReadingProfileServiceTest.cs b/API.Tests/Services/ReadingProfileServiceTest.cs index fd2358b47..5c33af956 100644 --- a/API.Tests/Services/ReadingProfileServiceTest.cs +++ b/API.Tests/Services/ReadingProfileServiceTest.cs @@ -221,7 +221,7 @@ public class ReadingProfileServiceTest: AbstractDbTest Context.AppUserReadingProfile.Add(profile1); await UnitOfWork.CommitAsync(); - await rps.RemoveProfileFromSeries(user.Id, profile1.Id, series.Id); + await rps.ClearSeriesProfile(user.Id, series.Id); var profile = await rps.GetReadingProfileForSeries(user.Id, series.Id); Assert.Null(profile); @@ -254,7 +254,7 @@ public class ReadingProfileServiceTest: AbstractDbTest await UnitOfWork.CommitAsync(); var someSeriesIds = lib.Series.Take(lib.Series.Count / 2).Select(s => s.Id).ToList(); - await rps.BatchAddProfileToSeries(user.Id, profile.Id, someSeriesIds); + await rps.BulkAddProfileToSeries(user.Id, profile.Id, someSeriesIds); foreach (var id in someSeriesIds) { @@ -264,7 +264,7 @@ public class ReadingProfileServiceTest: AbstractDbTest } var allIds = lib.Series.Select(s => s.Id).ToList(); - await rps.BatchAddProfileToSeries(user.Id, profile2.Id, allIds); + await rps.BulkAddProfileToSeries(user.Id, profile2.Id, allIds); foreach (var id in allIds) { @@ -342,7 +342,7 @@ public class ReadingProfileServiceTest: AbstractDbTest Assert.True(seriesProfile.Implicit); } - await rps.BatchAddProfileToSeries(user.Id, profile.Id, ids); + await rps.BulkAddProfileToSeries(user.Id, profile.Id, ids); foreach (var id in ids) { diff --git a/API/Controllers/ReadingProfileController.cs b/API/Controllers/ReadingProfileController.cs index 97671b8ca..07cacd2b3 100644 --- a/API/Controllers/ReadingProfileController.cs +++ b/API/Controllers/ReadingProfileController.cs @@ -127,15 +127,14 @@ public class ReadingProfileController(ILogger logger, } /// - /// Deletes the reading profile from a series for the current logged-in user + /// Clears the reading profile for the given series for the currently logged-in user /// /// - /// /// [HttpDelete("series/{seriesId}")] - public async Task DeleteProfileFromSeries(int seriesId, [FromQuery] int profileId) + public async Task ClearSeriesProfile(int seriesId) { - await readingProfileService.RemoveProfileFromSeries(User.GetUserId(), profileId, seriesId); + await readingProfileService.ClearSeriesProfile(User.GetUserId(), seriesId); return Ok(); } @@ -153,15 +152,15 @@ public class ReadingProfileController(ILogger logger, } /// - /// Remove the reading profile from a library for the current logged-in user + /// Clears the reading profile for the given library for the currently logged-in user /// /// /// /// [HttpDelete("library/{libraryId}")] - public async Task DeleteProfileFromLibrary(int libraryId, [FromQuery] int profileId) + public async Task ClearLibraryProfile(int libraryId) { - await readingProfileService.RemoveProfileFromLibrary(User.GetUserId(), profileId, libraryId); + await readingProfileService.ClearLibraryProfile(User.GetUserId(), libraryId); return Ok(); } @@ -171,10 +170,10 @@ public class ReadingProfileController(ILogger logger, /// /// /// - [HttpPost("batch")] - public async Task BatchAddReadingProfile([FromQuery] int profileId, [FromBody] IList seriesIds) + [HttpPost("bulk")] + public async Task BulkAddReadingProfile([FromQuery] int profileId, [FromBody] IList seriesIds) { - await readingProfileService.BatchAddProfileToSeries(User.GetUserId(), profileId, seriesIds); + await readingProfileService.BulkAddProfileToSeries(User.GetUserId(), profileId, seriesIds); return Ok(); } diff --git a/API/Data/Repositories/AppUserReadingProfileRepository.cs b/API/Data/Repositories/AppUserReadingProfileRepository.cs index 27694d7fb..1c785b647 100644 --- a/API/Data/Repositories/AppUserReadingProfileRepository.cs +++ b/API/Data/Repositories/AppUserReadingProfileRepository.cs @@ -26,6 +26,14 @@ public interface IAppUserReadingProfileRepository Task> GetProfilesForUser(int userId, bool nonImplicitOnly, ReadingProfileIncludes includes = ReadingProfileIncludes.None); Task> GetProfilesDtoForUser(int userId, bool nonImplicitOnly, ReadingProfileIncludes includes = ReadingProfileIncludes.None); Task GetProfileForSeries(int userId, int seriesId, ReadingProfileIncludes includes = ReadingProfileIncludes.None); + /// + /// Returns both implicit and "real" reading profiles + /// + /// + /// + /// + /// + Task> GetAllProfilesForSeries(int userId, int seriesId, ReadingProfileIncludes includes = ReadingProfileIncludes.None); Task> GetProfilesForSeries(int userId, IList seriesIds, bool implicitOnly, ReadingProfileIncludes includes = ReadingProfileIncludes.None); Task GetProfileDtoForSeries(int userId, int seriesId); Task GetProfileForLibrary(int userId, int libraryId, ReadingProfileIncludes includes = ReadingProfileIncludes.None); @@ -76,6 +84,14 @@ public class AppUserReadingProfileRepository(DataContext context, IMapper mapper .FirstOrDefaultAsync(); } + public async Task> GetAllProfilesForSeries(int userId, int seriesId, ReadingProfileIncludes includes = ReadingProfileIncludes.None) + { + return await context.AppUserReadingProfile + .Where(rp => rp.UserId == userId && rp.Series.Any(s => s.SeriesId == seriesId)) + .Includes(includes) + .ToListAsync(); + } + public async Task> GetProfilesForSeries(int userId, IList seriesIds, bool implicitOnly, ReadingProfileIncludes includes = ReadingProfileIncludes.None) { return await context.AppUserReadingProfile diff --git a/API/Services/ReadingProfileService.cs b/API/Services/ReadingProfileService.cs index 3de59e77e..e9ae32fe8 100644 --- a/API/Services/ReadingProfileService.cs +++ b/API/Services/ReadingProfileService.cs @@ -68,11 +68,11 @@ public interface IReadingProfileService Task SetDefaultReadingProfile(int userId, int profileId); Task AddProfileToSeries(int userId, int profileId, int seriesId); - Task BatchAddProfileToSeries(int userId, int profileId, IList seriesIds); - Task RemoveProfileFromSeries(int userId, int profileId, int seriesId); + Task BulkAddProfileToSeries(int userId, int profileId, IList seriesIds); + Task ClearSeriesProfile(int userId, int seriesId); Task AddProfileToLibrary(int userId, int profileId, int libraryId); - Task RemoveProfileFromLibrary(int userId, int profileId, int libraryId); + Task ClearLibraryProfile(int userId, int libraryId); } @@ -200,6 +200,10 @@ public class ReadingProfileService(IUnitOfWork unitOfWork, ILocalizationService if (profile.UserId != userId) throw new UnauthorizedAccessException(); + // Remove all implicit profiles + var implicitProfiles = await unitOfWork.AppUserReadingProfileRepository.GetProfilesForSeries(userId, [seriesId], true); + unitOfWork.AppUserReadingProfileRepository.RemoveRange(implicitProfiles); + var seriesProfile = await unitOfWork.AppUserReadingProfileRepository.GetSeriesProfile(userId, seriesId); if (seriesProfile != null) { @@ -219,7 +223,7 @@ public class ReadingProfileService(IUnitOfWork unitOfWork, ILocalizationService await unitOfWork.CommitAsync(); } - public async Task BatchAddProfileToSeries(int userId, int profileId, IList seriesIds) + public async Task BulkAddProfileToSeries(int userId, int profileId, IList seriesIds) { var profile = await unitOfWork.AppUserReadingProfileRepository.GetProfile(profileId, ReadingProfileIncludes.Series); if (profile == null) throw new KavitaException("profile-not-found"); @@ -254,14 +258,23 @@ public class ReadingProfileService(IUnitOfWork unitOfWork, ILocalizationService await unitOfWork.CommitAsync(); } - public async Task RemoveProfileFromSeries(int userId, int profileId, int seriesId) + public async Task ClearSeriesProfile(int userId, int seriesId) { - var profile = await unitOfWork.AppUserReadingProfileRepository.GetProfile(profileId); - if (profile == null) throw new KavitaException("profile-not-found"); + var profiles = await unitOfWork.AppUserReadingProfileRepository.GetAllProfilesForSeries(userId, seriesId, ReadingProfileIncludes.Series); + if (!profiles.Any()) return; - if (profile.UserId != userId) throw new UnauthorizedAccessException(); + foreach (var profile in profiles) + { + if (profile.Implicit) + { + unitOfWork.AppUserReadingProfileRepository.Remove(profile); + } + else + { + profile.Series = profile.Series.Where(s => !(s.SeriesId == seriesId && s.AppUserId == userId)).ToList(); + } + } - profile.Series = profile.Series.Where(s => s.SeriesId != seriesId).ToList(); await unitOfWork.CommitAsync(); } @@ -286,14 +299,21 @@ public class ReadingProfileService(IUnitOfWork unitOfWork, ILocalizationService await unitOfWork.CommitAsync(); } - public async Task RemoveProfileFromLibrary(int userId, int profileId, int libraryId) + public async Task ClearLibraryProfile(int userId, int libraryId) { - var profile = await unitOfWork.AppUserReadingProfileRepository.GetProfile(profileId); - if (profile == null) throw new KavitaException("profile-not-found"); + var profile = await unitOfWork.AppUserReadingProfileRepository.GetProfileForLibrary(userId, libraryId, ReadingProfileIncludes.Library); + if (profile == null) return; - if (profile.UserId != userId) throw new UnauthorizedAccessException(); + if (profile.Implicit) + { + unitOfWork.AppUserReadingProfileRepository.Remove(profile); + await unitOfWork.CommitAsync(); + return; + } - profile.Libraries = profile.Libraries.Where(s => s.LibraryId != libraryId).ToList(); + profile.Libraries = profile.Libraries + .Where(s => !(s.LibraryId == libraryId && s.AppUserId == userId)) + .ToList(); await unitOfWork.CommitAsync(); } diff --git a/UI/Web/src/app/_services/action-factory.service.ts b/UI/Web/src/app/_services/action-factory.service.ts index 995bc4593..88d601165 100644 --- a/UI/Web/src/app/_services/action-factory.service.ts +++ b/UI/Web/src/app/_services/action-factory.service.ts @@ -126,6 +126,10 @@ export enum Action { * Add to a reading profile */ SetReadingProfile = 30, + /** + * Remove the reading profile from the entity + */ + ClearReadingProfile = 31, } /** @@ -346,6 +350,37 @@ export class ActionFactoryService { requiredRoles: [Role.Admin], children: [], }, + { + action: Action.Submenu, + title: 'reading-profiles', + description: '', + callback: this.dummyCallback, + shouldRender: this.dummyShouldRender, + requiresAdmin: false, + requiredRoles: [], + children: [ + { + action: Action.SetReadingProfile, + title: 'set-reading-profile', + description: 'set-reading-profile-tooltip', + callback: this.dummyCallback, + shouldRender: this.dummyShouldRender, + requiresAdmin: false, + requiredRoles: [], + children: [], + }, + { + action: Action.ClearReadingProfile, + title: 'clear-reading-profile', + description: 'clear-reading-profile-tooltip', + callback: this.dummyCallback, + shouldRender: this.dummyShouldRender, + requiresAdmin: false, + requiredRoles: [], + children: [], + }, + ], + }, { action: Action.Submenu, title: 'others', @@ -559,6 +594,37 @@ export class ActionFactoryService { } ], }, + { + action: Action.Submenu, + title: 'reading-profiles', + description: '', + callback: this.dummyCallback, + shouldRender: this.dummyShouldRender, + requiresAdmin: false, + requiredRoles: [], + children: [ + { + action: Action.SetReadingProfile, + title: 'set-reading-profile', + description: 'set-reading-profile-tooltip', + callback: this.dummyCallback, + shouldRender: this.dummyShouldRender, + requiresAdmin: false, + requiredRoles: [], + children: [], + }, + { + action: Action.ClearReadingProfile, + title: 'clear-reading-profile', + description: 'clear-reading-profile-tooltip', + callback: this.dummyCallback, + shouldRender: this.dummyShouldRender, + requiresAdmin: false, + requiredRoles: [], + children: [], + }, + ], + }, { action: Action.Submenu, title: 'others', @@ -598,16 +664,6 @@ export class ActionFactoryService { requiredRoles: [Role.Admin], children: [], }, - { - action: Action.SetReadingProfile, - title: 'set-reading-profile', - description: 'set-reading-profile-tooltip', - callback: this.dummyCallback, - shouldRender: this.dummyShouldRender, - requiresAdmin: false, - requiredRoles: [], - children: [], - }, { action: Action.Delete, title: 'delete', diff --git a/UI/Web/src/app/_services/action.service.ts b/UI/Web/src/app/_services/action.service.ts index 468f84d87..d02be7b81 100644 --- a/UI/Web/src/app/_services/action.service.ts +++ b/UI/Web/src/app/_services/action.service.ts @@ -819,12 +819,38 @@ export class ActionService { * @param series * @param callback */ - SetReadingProfileForMultiple(series: Array, callback?: BooleanActionCallback) { + setReadingProfileForMultiple(series: Array, callback?: BooleanActionCallback) { if (this.readingListModalRef != null) { return; } this.readingListModalRef = this.modalService.open(BulkSetReadingProfileComponent, { scrollable: true, size: 'md', fullscreen: 'md' }); this.readingListModalRef.componentInstance.seriesIds = series.map(s => s.id) - this.readingListModalRef.componentInstance.title = "hi" + this.readingListModalRef.componentInstance.title = "" + + this.readingListModalRef.closed.pipe(take(1)).subscribe(() => { + this.readingListModalRef = null; + if (callback) { + callback(true); + } + }); + this.readingListModalRef.dismissed.pipe(take(1)).subscribe(() => { + this.readingListModalRef = null; + if (callback) { + callback(false); + } + }); + } + + /** + * Sets the reading profile for multiple series + * @param library + * @param callback + */ + setReadingProfileForLibrary(library: Library, callback?: BooleanActionCallback) { + if (this.readingListModalRef != null) { return; } + + this.readingListModalRef = this.modalService.open(BulkSetReadingProfileComponent, { scrollable: true, size: 'md', fullscreen: 'md' }); + this.readingListModalRef.componentInstance.libraryId = library.id; + this.readingListModalRef.componentInstance.title = "" this.readingListModalRef.closed.pipe(take(1)).subscribe(() => { this.readingListModalRef = null; diff --git a/UI/Web/src/app/_services/reading-profile.service.ts b/UI/Web/src/app/_services/reading-profile.service.ts index 7bf32e81b..fa0976f97 100644 --- a/UI/Web/src/app/_services/reading-profile.service.ts +++ b/UI/Web/src/app/_services/reading-profile.service.ts @@ -44,20 +44,20 @@ export class ReadingProfileService { return this.httpClient.post(this.baseUrl + `ReadingProfile/series/${seriesId}?profileId=${id}`, {}); } - removeFromSeries(id: number, seriesId: number) { - return this.httpClient.delete(this.baseUrl + `ReadingProfile/series/${seriesId}?profileId=${id}`, {}); + clearSeriesProfiles(seriesId: number) { + return this.httpClient.delete(this.baseUrl + `ReadingProfile/series/${seriesId}`, {}); } addToLibrary(id: number, libraryId: number) { return this.httpClient.post(this.baseUrl + `ReadingProfile/library/${libraryId}?profileId=${id}`, {}); } - removeFromLibrary(id: number, libraryId: number) { - return this.httpClient.delete(this.baseUrl + `ReadingProfile/library/${libraryId}?profileId=${id}`, {}); + clearLibraryProfiles(libraryId: number) { + return this.httpClient.delete(this.baseUrl + `ReadingProfile/library/${libraryId}`, {}); } - batchAddToSeries(id: number, seriesIds: number[]) { - return this.httpClient.post(this.baseUrl + `ReadingProfile/batch?profileId=${id}`, seriesIds); + bulkAddToSeries(id: number, seriesIds: number[]) { + return this.httpClient.post(this.baseUrl + `ReadingProfile/bulk?profileId=${id}`, seriesIds); } } diff --git a/UI/Web/src/app/cards/_modals/bulk-set-reading-profile/bulk-set-reading-profile.component.ts b/UI/Web/src/app/cards/_modals/bulk-set-reading-profile/bulk-set-reading-profile.component.ts index dbdb1c160..ce0396947 100644 --- a/UI/Web/src/app/cards/_modals/bulk-set-reading-profile/bulk-set-reading-profile.component.ts +++ b/UI/Web/src/app/cards/_modals/bulk-set-reading-profile/bulk-set-reading-profile.component.ts @@ -27,9 +27,10 @@ export class BulkSetReadingProfileComponent implements OnInit, AfterViewInit { @Input({required: true}) title!: string; /** - * Series Ids to add to Collection Tag + * Series Ids to add to Reading Profile */ @Input() seriesIds: Array = []; + @Input() libraryId: number | undefined; @ViewChild('title') inputElem!: ElementRef; profiles: Array = []; @@ -63,12 +64,28 @@ export class BulkSetReadingProfileComponent implements OnInit, AfterViewInit { } addToProfile(profile: ReadingProfile) { - if (this.seriesIds.length === 0) return; + if (this.seriesIds.length == 1) { + this.readingProfileService.addToSeries(profile.id, this.seriesIds[0]).subscribe(() => { + this.toastr.success(translate('toasts.series-added-to-reading-profile', {name: profile.name})); + this.modal.close(); + }); + return; + } - this.readingProfileService.batchAddToSeries(profile.id, this.seriesIds).subscribe(() => { - this.toastr.success(translate('toasts.series-added-to-reading-profile', {name: profile.name})); - this.modal.close(); - }); + if (this.seriesIds.length > 1) { + this.readingProfileService.bulkAddToSeries(profile.id, this.seriesIds).subscribe(() => { + this.toastr.success(translate('toasts.series-added-to-reading-profile', {name: profile.name})); + this.modal.close(); + }); + return; + } + + if (this.libraryId) { + this.readingProfileService.addToLibrary(profile.id, this.libraryId).subscribe(() => { + this.toastr.success(translate('toasts.library-added-to-reading-profile', {name: profile.name})); + this.modal.close(); + }); + } } filterList = (listItem: ReadingProfile) => { diff --git a/UI/Web/src/app/cards/series-card/series-card.component.ts b/UI/Web/src/app/cards/series-card/series-card.component.ts index bef8b8372..052f71107 100644 --- a/UI/Web/src/app/cards/series-card/series-card.component.ts +++ b/UI/Web/src/app/cards/series-card/series-card.component.ts @@ -24,7 +24,7 @@ import {RelationKind} from 'src/app/_models/series-detail/relation-kind'; import {DecimalPipe} from "@angular/common"; import {RelationshipPipe} from "../../_pipes/relationship.pipe"; import {Device} from "../../_models/device/device"; -import {translate, TranslocoDirective} from "@jsverse/transloco"; +import {translate, TranslocoDirective, TranslocoService} from "@jsverse/transloco"; import {SeriesPreviewDrawerComponent} from "../../_single-module/series-preview-drawer/series-preview-drawer.component"; import {CardActionablesComponent} from "../../_single-module/card-actionables/card-actionables.component"; import {DefaultValuePipe} from "../../_pipes/default-value.pipe"; @@ -41,6 +41,7 @@ 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"; +import {ReadingProfileService} from "../../_services/reading-profile.service"; function deepClone(obj: any): any { if (obj === null || typeof obj !== 'object') { @@ -92,6 +93,8 @@ export class SeriesCardComponent implements OnInit, OnChanges { private readonly downloadService = inject(DownloadService); private readonly scrollService = inject(ScrollService); private readonly readerService = inject(ReaderService); + private readonly readingProfilesService = inject(ReadingProfileService); + private readonly translocoService = inject(TranslocoService); @Input({required: true}) series!: Series; @Input() libraryId = 0; @@ -277,7 +280,12 @@ export class SeriesCardComponent implements OnInit, OnChanges { this.downloadService.download('series', this.series); break; case Action.SetReadingProfile: - this.actionService.SetReadingProfileForMultiple([this.series]); + this.actionService.setReadingProfileForMultiple([series]); + break; + case Action.ClearReadingProfile: + this.readingProfilesService.clearSeriesProfiles(series.id).subscribe(() => { + this.toastr.success(this.translocoService.translate('actionable.cleared-profile')); + }); break; default: break; diff --git a/UI/Web/src/app/library-detail/library-detail.component.ts b/UI/Web/src/app/library-detail/library-detail.component.ts index 786485c21..b2563a1a6 100644 --- a/UI/Web/src/app/library-detail/library-detail.component.ts +++ b/UI/Web/src/app/library-detail/library-detail.component.ts @@ -150,7 +150,7 @@ export class LibraryDetailComponent implements OnInit { }); break; case Action.SetReadingProfile: - this.actionService.SetReadingProfileForMultiple(selectedSeries, (success) => { + this.actionService.setReadingProfileForMultiple(selectedSeries, (success) => { this.bulkLoader = false; this.cdRef.markForCheck(); if (!success) return; diff --git a/UI/Web/src/app/manga-reader/_components/manga-reader/manga-reader.component.ts b/UI/Web/src/app/manga-reader/_components/manga-reader/manga-reader.component.ts index f2932458b..a800bdf25 100644 --- a/UI/Web/src/app/manga-reader/_components/manga-reader/manga-reader.component.ts +++ b/UI/Web/src/app/manga-reader/_components/manga-reader/manga-reader.component.ts @@ -533,21 +533,6 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy { swipeToPaginate: new FormControl(this.readingProfile.swipeToPaginate) }); - // Update implicit reading profile while changing settings - this.generalSettingsForm.valueChanges.pipe( - debounceTime(300), - distinctUntilChanged(), - takeUntilDestroyed(this.destroyRef), - tap(_ => { - this.readingProfileService.updateImplicit(this.packReadingProfile(), this.seriesId).subscribe({ - error: err => { - console.error(err); - } - }) - }) - ).subscribe(); - - this.readerModeSubject.next(this.readerMode); this.pagingDirectionSubject.next(this.pagingDirection); @@ -630,6 +615,26 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy { }); this.init(); + + // TODO: Fix this, it's going off way too often + // Update implicit reading profile while changing settings + this.generalSettingsForm.valueChanges.pipe( + debounceTime(300), + distinctUntilChanged(), + takeUntilDestroyed(this.destroyRef), + map(_ => this.packReadingProfile()), + distinctUntilChanged(), + tap(newProfile => { + this.readingProfileService.updateImplicit(newProfile, this.seriesId).subscribe({ + next: () => { + this.readingProfile = newProfile; + }, + error: err => { + console.error(err); + } + }) + }) + ).subscribe(); }); } diff --git a/UI/Web/src/app/series-detail/_components/series-detail/series-detail.component.ts b/UI/Web/src/app/series-detail/_components/series-detail/series-detail.component.ts index caad12b9e..9da74a016 100644 --- a/UI/Web/src/app/series-detail/_components/series-detail/series-detail.component.ts +++ b/UI/Web/src/app/series-detail/_components/series-detail/series-detail.component.ts @@ -110,6 +110,7 @@ import {LicenseService} from "../../../_services/license.service"; import {PageBookmark} from "../../../_models/readers/page-bookmark"; import {VolumeRemovedEvent} from "../../../_models/events/volume-removed-event"; import {ReviewsComponent} from "../../../_single-module/reviews/reviews.component"; +import {ReadingProfileService} from "../../../_services/reading-profile.service"; enum TabID { @@ -175,6 +176,7 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked { private readonly cdRef = inject(ChangeDetectorRef); private readonly scrollService = inject(ScrollService); private readonly translocoService = inject(TranslocoService); + private readonly readingProfileService = inject(ReadingProfileService); protected readonly bulkSelectionService = inject(BulkSelectionService); protected readonly utilityService = inject(UtilityService); protected readonly imageService = inject(ImageService); @@ -610,7 +612,12 @@ export class SeriesDetailComponent implements OnInit, AfterContentChecked { break; } case Action.SetReadingProfile: - this.actionService.SetReadingProfileForMultiple([this.series]); + this.actionService.setReadingProfileForMultiple([this.series]); + break; + case Action.ClearReadingProfile: + this.readingProfileService.clearSeriesProfiles(this.seriesId).subscribe(() => { + this.toastr.success(this.translocoService.translate('actionable.cleared-profile')); + }); break; default: break; diff --git a/UI/Web/src/app/sidenav/_components/side-nav/side-nav.component.ts b/UI/Web/src/app/sidenav/_components/side-nav/side-nav.component.ts index 547da378e..617db2500 100644 --- a/UI/Web/src/app/sidenav/_components/side-nav/side-nav.component.ts +++ b/UI/Web/src/app/sidenav/_components/side-nav/side-nav.component.ts @@ -16,7 +16,7 @@ import {AsyncPipe, NgClass} from "@angular/common"; import {SideNavItemComponent} from "../side-nav-item/side-nav-item.component"; import {FilterPipe} from "../../../_pipes/filter.pipe"; import {FormsModule} from "@angular/forms"; -import {translate, TranslocoDirective} from "@jsverse/transloco"; +import {translate, TranslocoDirective, TranslocoService} from "@jsverse/transloco"; import {CardActionablesComponent} from "../../../_single-module/card-actionables/card-actionables.component"; import {SideNavStream} from "../../../_models/sidenav/sidenav-stream"; import {SideNavStreamType} from "../../../_models/sidenav/sidenav-stream-type.enum"; @@ -25,6 +25,7 @@ import {SettingsTabId} from "../../preference-nav/preference-nav.component"; import {LicenseService} from "../../../_services/license.service"; import {CdkDrag, CdkDragDrop, CdkDropList} from "@angular/cdk/drag-drop"; import {ToastrService} from "ngx-toastr"; +import {ReadingProfileService} from "../../../_services/reading-profile.service"; @Component({ selector: 'app-side-nav', @@ -53,7 +54,9 @@ export class SideNavComponent implements OnInit { protected readonly licenseService = inject(LicenseService); private readonly destroyRef = inject(DestroyRef); private readonly actionFactoryService = inject(ActionFactoryService); - private readonly toastr = inject(ToastrService) + private readonly toastr = inject(ToastrService); + private readonly readingProfilesService = inject(ReadingProfileService); + private readonly translocoService = inject(TranslocoService); cachedData: SideNavStream[] | null = null; @@ -175,6 +178,14 @@ export class SideNavComponent implements OnInit { case (Action.Edit): this.actionService.editLibrary(lib, () => window.scrollTo(0, 0)); break; + case (Action.SetReadingProfile): + this.actionService.setReadingProfileForLibrary(lib); + break; + case (Action.ClearReadingProfile): + this.readingProfilesService.clearLibraryProfiles(lib.id).subscribe(() => { + this.toastr.success(this.translocoService.translate('actionable.cleared-profile')); + }); + break; default: break; } diff --git a/UI/Web/src/assets/langs/en.json b/UI/Web/src/assets/langs/en.json index 532ad21a5..cd7ffe9bd 100644 --- a/UI/Web/src/assets/langs/en.json +++ b/UI/Web/src/assets/langs/en.json @@ -2714,7 +2714,11 @@ "remove-from-want-to-read-tooltip": "Remove series from Want to Read", "remove-from-on-deck": "Remove From On Deck", "remove-from-on-deck-tooltip": "Remove series from showing from On Deck", + + "reading-profiles": "Reading Profiles", "set-reading-profile": "Set Reading Profile", + "clear-reading-profile": "Clear Reading Profile", + "cleared-profile": "Cleared Reading Profile", "others": "Others", "add-to-reading-list": "Add to Reading List",