Show the default bound profile on the set modal.
This commit is contained in:
parent
d417a0a610
commit
0899373a27
8 changed files with 78 additions and 13 deletions
|
|
@ -43,6 +43,17 @@ public class ReadingProfileController(ILogger<ReadingProfileController> logger,
|
||||||
return Ok(await readingProfileService.GetReadingProfileDtoForSeries(User.GetUserId(), seriesId, skipImplicit));
|
return Ok(await readingProfileService.GetReadingProfileDtoForSeries(User.GetUserId(), seriesId, skipImplicit));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the (potential) Reading Profile bound to the library
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="libraryId"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpGet("library")]
|
||||||
|
public async Task<ActionResult<UserReadingProfileDto?>> GetProfileForLibrary(int libraryId)
|
||||||
|
{
|
||||||
|
return Ok(await readingProfileService.GetReadingProfileDtoForLibrary(User.GetUserId(), libraryId));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new reading profile for the current user
|
/// Creates a new reading profile for the current user
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ using AutoMapper;
|
||||||
using Kavita.Common;
|
using Kavita.Common;
|
||||||
|
|
||||||
namespace API.Services;
|
namespace API.Services;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
public interface IReadingProfileService
|
public interface IReadingProfileService
|
||||||
{
|
{
|
||||||
|
|
@ -73,7 +74,7 @@ public interface IReadingProfileService
|
||||||
Task DeleteReadingProfile(int userId, int profileId);
|
Task DeleteReadingProfile(int userId, int profileId);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Assigns the reading profile to the series, and remove the implicit RP from the series if it exists
|
/// Binds the reading profile to the series, and remove the implicit RP from the series if it exists
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="userId"></param>
|
/// <param name="userId"></param>
|
||||||
/// <param name="profileId"></param>
|
/// <param name="profileId"></param>
|
||||||
|
|
@ -81,7 +82,7 @@ public interface IReadingProfileService
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task AddProfileToSeries(int userId, int profileId, int seriesId);
|
Task AddProfileToSeries(int userId, int profileId, int seriesId);
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Assigns the reading profile to many series, and remove the implicit RP from the series if it exists
|
/// Binds the reading profile to many series, and remove the implicit RP from the series if it exists
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="userId"></param>
|
/// <param name="userId"></param>
|
||||||
/// <param name="profileId"></param>
|
/// <param name="profileId"></param>
|
||||||
|
|
@ -89,7 +90,7 @@ public interface IReadingProfileService
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task BulkAddProfileToSeries(int userId, int profileId, IList<int> seriesIds);
|
Task BulkAddProfileToSeries(int userId, int profileId, IList<int> seriesIds);
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Remove all reading profiles from the series
|
/// Remove all reading profiles bound to the series
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="userId"></param>
|
/// <param name="userId"></param>
|
||||||
/// <param name="seriesId"></param>
|
/// <param name="seriesId"></param>
|
||||||
|
|
@ -97,7 +98,7 @@ public interface IReadingProfileService
|
||||||
Task ClearSeriesProfile(int userId, int seriesId);
|
Task ClearSeriesProfile(int userId, int seriesId);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Assign the reading profile to the library
|
/// Bind the reading profile to the library
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="userId"></param>
|
/// <param name="userId"></param>
|
||||||
/// <param name="profileId"></param>
|
/// <param name="profileId"></param>
|
||||||
|
|
@ -105,13 +106,19 @@ public interface IReadingProfileService
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task AddProfileToLibrary(int userId, int profileId, int libraryId);
|
Task AddProfileToLibrary(int userId, int profileId, int libraryId);
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Remove the reading profile from the library, if it exists
|
/// Remove the reading profile bound to the library, if it exists
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="userId"></param>
|
/// <param name="userId"></param>
|
||||||
/// <param name="libraryId"></param>
|
/// <param name="libraryId"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task ClearLibraryProfile(int userId, int libraryId);
|
Task ClearLibraryProfile(int userId, int libraryId);
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the bound Reading Profile to a Library
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userId"></param>
|
||||||
|
/// <param name="libraryId"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<UserReadingProfileDto?> GetReadingProfileDtoForLibrary(int userId, int libraryId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ReadingProfileService(IUnitOfWork unitOfWork, ILocalizationService localizationService, IMapper mapper): IReadingProfileService
|
public class ReadingProfileService(IUnitOfWork unitOfWork, ILocalizationService localizationService, IMapper mapper): IReadingProfileService
|
||||||
|
|
@ -356,6 +363,12 @@ public class ReadingProfileService(IUnitOfWork unitOfWork, ILocalizationService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<UserReadingProfileDto?> GetReadingProfileDtoForLibrary(int userId, int libraryId)
|
||||||
|
{
|
||||||
|
var profiles = await unitOfWork.AppUserReadingProfileRepository.GetProfilesForUser(userId, true);
|
||||||
|
return mapper.Map<UserReadingProfileDto>(profiles.FirstOrDefault(p => p.LibraryIds.Contains(libraryId)));
|
||||||
|
}
|
||||||
|
|
||||||
private async Task DeleteImplicitAndRemoveFromUserProfiles(int userId, IList<int> seriesIds, IList<int> libraryIds)
|
private async Task DeleteImplicitAndRemoveFromUserProfiles(int userId, IList<int> seriesIds, IList<int> libraryIds)
|
||||||
{
|
{
|
||||||
var profiles = await unitOfWork.AppUserReadingProfileRepository.GetProfilesForUser(userId);
|
var profiles = await unitOfWork.AppUserReadingProfileRepository.GetProfilesForUser(userId);
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,10 @@ export class ReadingProfileService {
|
||||||
return this.httpClient.get<ReadingProfile>(this.baseUrl + `reading-profile/${seriesId}?skipImplicit=${skipImplicit}`);
|
return this.httpClient.get<ReadingProfile>(this.baseUrl + `reading-profile/${seriesId}?skipImplicit=${skipImplicit}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getForLibrary(libraryId: number) {
|
||||||
|
return this.httpClient.get<ReadingProfile | null>(this.baseUrl + `reading-profile/library?libraryId=${libraryId}`);
|
||||||
|
}
|
||||||
|
|
||||||
updateProfile(profile: ReadingProfile) {
|
updateProfile(profile: ReadingProfile) {
|
||||||
return this.httpClient.post<ReadingProfile>(this.baseUrl + 'reading-profile', profile);
|
return this.httpClient.post<ReadingProfile>(this.baseUrl + 'reading-profile', profile);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,14 @@
|
||||||
<ul class="list-group">
|
<ul class="list-group">
|
||||||
@for(profile of profiles | filter: filterList; let i = $index; track profile.name) {
|
@for(profile of profiles | filter: filterList; let i = $index; track profile.name) {
|
||||||
<li class="list-group-item clickable" tabindex="0" role="option" (click)="addToProfile(profile)">
|
<li class="list-group-item clickable" tabindex="0" role="option" (click)="addToProfile(profile)">
|
||||||
{{profile.name}}
|
<div class="p-2 group-item d-flex justify-content-between align-items-center">
|
||||||
|
|
||||||
|
<div class="fw-bold">{{profile.name | sentenceCase}}</div>
|
||||||
|
|
||||||
|
@if (currentProfile && currentProfile.name === profile.name) {
|
||||||
|
<span class="pill p-1 ms-1">{{t('bound')}}</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,14 @@
|
||||||
.clickable:hover, .clickable:focus {
|
.clickable:hover, .clickable:focus {
|
||||||
background-color: var(--list-group-hover-bg-color, --primary-color);
|
background-color: var(--list-group-hover-bg-color, --primary-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pill {
|
||||||
|
font-size: .8rem;
|
||||||
|
background-color: var(--card-bg-color);
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
color: var(--badge-text-color);
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color : var(--primary-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,15 +4,17 @@ import {ToastrService} from "ngx-toastr";
|
||||||
import {FormControl, FormGroup, ReactiveFormsModule} from "@angular/forms";
|
import {FormControl, FormGroup, ReactiveFormsModule} from "@angular/forms";
|
||||||
import {translate, 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 {FilterPipe} from "../../../_pipes/filter.pipe";
|
import {FilterPipe} from "../../../_pipes/filter.pipe";
|
||||||
|
import {SentenceCasePipe} from "../../../_pipes/sentence-case.pipe";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-bulk-set-reading-profile-modal',
|
selector: 'app-bulk-set-reading-profile-modal',
|
||||||
imports: [
|
imports: [
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
FilterPipe,
|
FilterPipe,
|
||||||
TranslocoDirective
|
TranslocoDirective,
|
||||||
|
SentenceCasePipe
|
||||||
],
|
],
|
||||||
templateUrl: './bulk-set-reading-profile-modal.component.html',
|
templateUrl: './bulk-set-reading-profile-modal.component.html',
|
||||||
styleUrl: './bulk-set-reading-profile-modal.component.scss'
|
styleUrl: './bulk-set-reading-profile-modal.component.scss'
|
||||||
|
|
@ -35,6 +37,7 @@ export class BulkSetReadingProfileModalComponent implements OnInit, AfterViewIni
|
||||||
@Input() libraryId: number | undefined;
|
@Input() libraryId: number | undefined;
|
||||||
@ViewChild('title') inputElem!: ElementRef<HTMLInputElement>;
|
@ViewChild('title') inputElem!: ElementRef<HTMLInputElement>;
|
||||||
|
|
||||||
|
currentProfile: ReadingProfile | null = null;
|
||||||
profiles: Array<ReadingProfile> = [];
|
profiles: Array<ReadingProfile> = [];
|
||||||
isLoading: boolean = false;
|
isLoading: boolean = false;
|
||||||
profileForm: FormGroup = new FormGroup({
|
profileForm: FormGroup = new FormGroup({
|
||||||
|
|
@ -47,6 +50,20 @@ export class BulkSetReadingProfileModalComponent implements OnInit, AfterViewIni
|
||||||
|
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
|
|
||||||
|
if (this.libraryId !== undefined) {
|
||||||
|
this.readingProfileService.getForLibrary(this.libraryId).subscribe(profile => {
|
||||||
|
this.currentProfile = profile;
|
||||||
|
this.cdRef.markForCheck();
|
||||||
|
});
|
||||||
|
} else if (this.seriesIds.length === 1) {
|
||||||
|
this.readingProfileService.getForSeries(this.seriesIds[0], true).subscribe(profile => {
|
||||||
|
this.currentProfile = profile;
|
||||||
|
this.cdRef.markForCheck();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
this.readingProfileService.getAllProfiles().subscribe(profiles => {
|
this.readingProfileService.getAllProfiles().subscribe(profiles => {
|
||||||
this.profiles = profiles;
|
this.profiles = profiles;
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
|
|
@ -98,4 +115,6 @@ export class BulkSetReadingProfileModalComponent implements OnInit, AfterViewIni
|
||||||
clear() {
|
clear() {
|
||||||
this.profileForm.get('filterQuery')?.setValue('');
|
this.profileForm.get('filterQuery')?.setValue('');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected readonly ReadingProfileKind = ReadingProfileKind;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -495,14 +495,13 @@
|
||||||
<ng-template #readingProfileOption let-profile>
|
<ng-template #readingProfileOption let-profile>
|
||||||
|
|
||||||
<div class="p-2 group-item d-flex justify-content-between align-items-start {{selectedProfile && profile.id === selectedProfile!.id ? 'active' : ''}}"
|
<div class="p-2 group-item d-flex justify-content-between align-items-start {{selectedProfile && profile.id === selectedProfile!.id ? 'active' : ''}}"
|
||||||
(click)="selectProfile(profile)"
|
(click)="selectProfile(profile)">
|
||||||
>
|
|
||||||
<div class="fw-bold">{{profile.name | sentenceCase}}</div>
|
<div class="fw-bold">{{profile.name | sentenceCase}}</div>
|
||||||
|
|
||||||
@if (profile.kind === ReadingProfileKind.Default) {
|
@if (profile.kind === ReadingProfileKind.Default) {
|
||||||
<span class="pill p-1 ms-1">{{t('default-profile')}}</span>
|
<span class="pill p-1 ms-1">{{t('default-profile')}}</span>
|
||||||
}
|
}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1283,7 +1283,8 @@
|
||||||
"clear": "{{common.clear}}",
|
"clear": "{{common.clear}}",
|
||||||
"no-data": "No collections created yet",
|
"no-data": "No collections created yet",
|
||||||
"loading": "{{common.loading}}",
|
"loading": "{{common.loading}}",
|
||||||
"create": "{{common.create}}"
|
"create": "{{common.create}}",
|
||||||
|
"bound": "Bound"
|
||||||
},
|
},
|
||||||
|
|
||||||
"entity-title": {
|
"entity-title": {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue