Correctly remove implicit profiles when updating

- Add actions (library actions aren't working ?)
- Auto update for implicit is going off too often
This commit is contained in:
Amelia 2025-05-30 12:58:47 +02:00
parent 823121f335
commit 558a1d73f5
No known key found for this signature in database
GPG key ID: D6D0ECE365407EAA
14 changed files with 242 additions and 73 deletions

View file

@ -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)
{

View file

@ -127,15 +127,14 @@ public class ReadingProfileController(ILogger<ReadingProfileController> logger,
}
/// <summary>
/// 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
/// </summary>
/// <param name="seriesId"></param>
/// <param name="profileId"></param>
/// <returns></returns>
[HttpDelete("series/{seriesId}")]
public async Task<IActionResult> DeleteProfileFromSeries(int seriesId, [FromQuery] int profileId)
public async Task<IActionResult> 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<ReadingProfileController> logger,
}
/// <summary>
/// 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
/// </summary>
/// <param name="libraryId"></param>
/// <param name="profileId"></param>
/// <returns></returns>
[HttpDelete("library/{libraryId}")]
public async Task<IActionResult> DeleteProfileFromLibrary(int libraryId, [FromQuery] int profileId)
public async Task<IActionResult> 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<ReadingProfileController> logger,
/// <param name="profileId"></param>
/// <param name="seriesIds"></param>
/// <returns></returns>
[HttpPost("batch")]
public async Task<IActionResult> BatchAddReadingProfile([FromQuery] int profileId, [FromBody] IList<int> seriesIds)
[HttpPost("bulk")]
public async Task<IActionResult> BulkAddReadingProfile([FromQuery] int profileId, [FromBody] IList<int> seriesIds)
{
await readingProfileService.BatchAddProfileToSeries(User.GetUserId(), profileId, seriesIds);
await readingProfileService.BulkAddProfileToSeries(User.GetUserId(), profileId, seriesIds);
return Ok();
}

View file

@ -26,6 +26,14 @@ public interface IAppUserReadingProfileRepository
Task<IList<AppUserReadingProfile>> GetProfilesForUser(int userId, bool nonImplicitOnly, ReadingProfileIncludes includes = ReadingProfileIncludes.None);
Task<IList<UserReadingProfileDto>> GetProfilesDtoForUser(int userId, bool nonImplicitOnly, ReadingProfileIncludes includes = ReadingProfileIncludes.None);
Task<AppUserReadingProfile?> GetProfileForSeries(int userId, int seriesId, ReadingProfileIncludes includes = ReadingProfileIncludes.None);
/// <summary>
/// Returns both implicit and "real" reading profiles
/// </summary>
/// <param name="userId"></param>
/// <param name="seriesId"></param>
/// <param name="includes"></param>
/// <returns></returns>
Task<IList<AppUserReadingProfile>> GetAllProfilesForSeries(int userId, int seriesId, ReadingProfileIncludes includes = ReadingProfileIncludes.None);
Task<IList<AppUserReadingProfile>> GetProfilesForSeries(int userId, IList<int> seriesIds, bool implicitOnly, ReadingProfileIncludes includes = ReadingProfileIncludes.None);
Task<UserReadingProfileDto?> GetProfileDtoForSeries(int userId, int seriesId);
Task<AppUserReadingProfile?> GetProfileForLibrary(int userId, int libraryId, ReadingProfileIncludes includes = ReadingProfileIncludes.None);
@ -76,6 +84,14 @@ public class AppUserReadingProfileRepository(DataContext context, IMapper mapper
.FirstOrDefaultAsync();
}
public async Task<IList<AppUserReadingProfile>> 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<IList<AppUserReadingProfile>> GetProfilesForSeries(int userId, IList<int> seriesIds, bool implicitOnly, ReadingProfileIncludes includes = ReadingProfileIncludes.None)
{
return await context.AppUserReadingProfile

View file

@ -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<int> seriesIds);
Task RemoveProfileFromSeries(int userId, int profileId, int seriesId);
Task BulkAddProfileToSeries(int userId, int profileId, IList<int> 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<int> seriesIds)
public async Task BulkAddProfileToSeries(int userId, int profileId, IList<int> 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();
}

View file

@ -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',

View file

@ -819,12 +819,38 @@ export class ActionService {
* @param series
* @param callback
*/
SetReadingProfileForMultiple(series: Array<Series>, callback?: BooleanActionCallback) {
setReadingProfileForMultiple(series: Array<Series>, 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;

View file

@ -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);
}
}

View file

@ -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<number> = [];
@Input() libraryId: number | undefined;
@ViewChild('title') inputElem!: ElementRef<HTMLInputElement>;
profiles: Array<ReadingProfile> = [];
@ -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) => {

View file

@ -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;

View file

@ -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;

View file

@ -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();
});
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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",