Slight changes to the entity, more UI work
This commit is contained in:
parent
5656fb2148
commit
9b4a4b8a50
24 changed files with 864 additions and 315 deletions
|
|
@ -1,12 +1,14 @@
|
|||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Data;
|
||||
using API.Data.Repositories;
|
||||
using API.DTOs;
|
||||
using API.Extensions;
|
||||
using API.Services;
|
||||
using AutoMapper;
|
||||
using Kavita.Common;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
|
@ -24,7 +26,11 @@ public class ReadingProfileController(ILogger<ReadingProfileController> logger,
|
|||
[HttpGet("all")]
|
||||
public async Task<ActionResult<IList<UserReadingProfileDto>>> GetAllReadingProfiles()
|
||||
{
|
||||
return Ok(await unitOfWork.AppUserReadingProfileRepository.GetProfilesForUser(User.GetUserId(), true));
|
||||
var profiles = await unitOfWork.AppUserReadingProfileRepository
|
||||
.GetProfilesDtoForUser(User.GetUserId(), true,
|
||||
ReadingProfileIncludes.Series | ReadingProfileIncludes.Library);
|
||||
|
||||
return Ok(profiles);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -48,6 +54,10 @@ public class ReadingProfileController(ILogger<ReadingProfileController> logger,
|
|||
/// If set, will delete the implicit reading profile if it exists
|
||||
/// </param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>This does not update connected series, and libraries. Use
|
||||
/// <see cref="DeleteProfileFromSeries"/>, <see cref="AddProfileToSeries"/>,
|
||||
/// <see cref="DeleteProfileFromLibrary"/>, <see cref="AddProfileToLibrary"/>
|
||||
/// </remarks>
|
||||
[HttpPost]
|
||||
public async Task<ActionResult> UpdateReadingProfile([FromBody] UserReadingProfileDto dto, [FromQuery] int? seriesCtx)
|
||||
{
|
||||
|
|
@ -116,4 +126,56 @@ public class ReadingProfileController(ILogger<ReadingProfileController> logger,
|
|||
return Ok();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the reading profile for a given series, removes the old one
|
||||
/// </summary>
|
||||
/// <param name="seriesId"></param>
|
||||
/// <param name="profileId"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("series/{seriesId}")]
|
||||
public async Task<IActionResult> AddProfileToSeries(int seriesId, [FromQuery] int profileId)
|
||||
{
|
||||
await readingProfileService.AddProfileToSeries(User.GetUserId(), profileId, seriesId);
|
||||
return Ok();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the reading profile from a series for the current 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)
|
||||
{
|
||||
await readingProfileService.RemoveProfileFromSeries(User.GetUserId(), profileId, seriesId);
|
||||
return Ok();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the reading profile for a given library, removes the old one
|
||||
/// </summary>
|
||||
/// <param name="libraryId"></param>
|
||||
/// <param name="profileId"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("library/{libraryId}")]
|
||||
public async Task<IActionResult> AddProfileToLibrary(int libraryId, [FromQuery] int profileId)
|
||||
{
|
||||
await readingProfileService.AddProfileToLibrary(User.GetUserId(), profileId, libraryId);
|
||||
return Ok();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the reading profile from a library for the current 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)
|
||||
{
|
||||
await readingProfileService.RemoveProfileFromLibrary(User.GetUserId(), profileId, libraryId);
|
||||
return Ok();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using API.Entities;
|
||||
using API.Entities.Enums;
|
||||
|
|
@ -128,4 +129,12 @@ public sealed record UserReadingProfileDto
|
|||
|
||||
#endregion
|
||||
|
||||
#region Relations
|
||||
|
||||
public IList<int> SeriesIds { get; set; } = [];
|
||||
|
||||
public IList<int> LibraryIds { get; set; } = [];
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,6 +82,8 @@ public sealed class DataContext : IdentityDbContext<AppUser, AppRole, int,
|
|||
public DbSet<MetadataFieldMapping> MetadataFieldMapping { get; set; } = null!;
|
||||
public DbSet<AppUserChapterRating> AppUserChapterRating { get; set; } = null!;
|
||||
public DbSet<AppUserReadingProfile> AppUserReadingProfile { get; set; } = null!;
|
||||
public DbSet<SeriesReadingProfile> SeriesReadingProfile { get; set; } = null!;
|
||||
public DbSet<LibraryReadingProfile> LibraryReadingProfile { get; set; } = null!;
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|||
namespace API.Data.Migrations
|
||||
{
|
||||
[DbContext(typeof(DataContext))]
|
||||
[Migration("20250517195000_AppUserReadingProfiles")]
|
||||
[Migration("20250519113715_AppUserReadingProfiles")]
|
||||
partial class AppUserReadingProfiles
|
||||
{
|
||||
/// <inheritdoc />
|
||||
|
|
@ -1380,6 +1380,33 @@ namespace API.Data.Migrations
|
|||
b.ToTable("LibraryFileTypeGroup");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.LibraryReadingProfile", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("AppUserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("LibraryId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ReadingProfileId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AppUserId");
|
||||
|
||||
b.HasIndex("ReadingProfileId");
|
||||
|
||||
b.HasIndex("LibraryId", "AppUserId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("LibraryReadingProfile");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.MangaFile", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
|
|
@ -2367,6 +2394,32 @@ namespace API.Data.Migrations
|
|||
b.ToTable("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.SeriesReadingProfile", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("AppUserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ReadingProfileId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SeriesId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AppUserId");
|
||||
|
||||
b.HasIndex("ReadingProfileId");
|
||||
|
||||
b.HasIndex("SeriesId");
|
||||
|
||||
b.ToTable("SeriesReadingProfile");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.ServerSetting", b =>
|
||||
{
|
||||
b.Property<int>("Key")
|
||||
|
|
@ -2598,36 +2651,6 @@ namespace API.Data.Migrations
|
|||
b.ToTable("AppUserLibrary");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AppUserReadingProfileLibrary", b =>
|
||||
{
|
||||
b.Property<int>("LibrariesId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ReadingProfilesId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("LibrariesId", "ReadingProfilesId");
|
||||
|
||||
b.HasIndex("ReadingProfilesId");
|
||||
|
||||
b.ToTable("AppUserReadingProfileLibrary");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AppUserReadingProfileSeries", b =>
|
||||
{
|
||||
b.Property<int>("ReadingProfilesId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SeriesId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("ReadingProfilesId", "SeriesId");
|
||||
|
||||
b.HasIndex("SeriesId");
|
||||
|
||||
b.ToTable("AppUserReadingProfileSeries");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ChapterGenre", b =>
|
||||
{
|
||||
b.Property<int>("ChaptersId")
|
||||
|
|
@ -3161,6 +3184,33 @@ namespace API.Data.Migrations
|
|||
b.Navigation("Library");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.LibraryReadingProfile", b =>
|
||||
{
|
||||
b.HasOne("API.Entities.AppUser", "AppUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("AppUserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("API.Entities.Library", "Library")
|
||||
.WithMany("ReadingProfiles")
|
||||
.HasForeignKey("LibraryId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("API.Entities.AppUserReadingProfile", "ReadingProfile")
|
||||
.WithMany("Libraries")
|
||||
.HasForeignKey("ReadingProfileId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("AppUser");
|
||||
|
||||
b.Navigation("Library");
|
||||
|
||||
b.Navigation("ReadingProfile");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.MangaFile", b =>
|
||||
{
|
||||
b.HasOne("API.Entities.Chapter", "Chapter")
|
||||
|
|
@ -3418,6 +3468,33 @@ namespace API.Data.Migrations
|
|||
b.Navigation("Library");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.SeriesReadingProfile", b =>
|
||||
{
|
||||
b.HasOne("API.Entities.AppUser", "AppUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("AppUserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("API.Entities.AppUserReadingProfile", "ReadingProfile")
|
||||
.WithMany("Series")
|
||||
.HasForeignKey("ReadingProfileId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("API.Entities.Series", "Series")
|
||||
.WithMany("ReadingProfiles")
|
||||
.HasForeignKey("SeriesId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("AppUser");
|
||||
|
||||
b.Navigation("ReadingProfile");
|
||||
|
||||
b.Navigation("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.Volume", b =>
|
||||
{
|
||||
b.HasOne("API.Entities.Series", "Series")
|
||||
|
|
@ -3459,36 +3536,6 @@ namespace API.Data.Migrations
|
|||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AppUserReadingProfileLibrary", b =>
|
||||
{
|
||||
b.HasOne("API.Entities.Library", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("LibrariesId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("API.Entities.AppUserReadingProfile", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("ReadingProfilesId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AppUserReadingProfileSeries", b =>
|
||||
{
|
||||
b.HasOne("API.Entities.AppUserReadingProfile", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("ReadingProfilesId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("API.Entities.Series", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("SeriesId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ChapterGenre", b =>
|
||||
{
|
||||
b.HasOne("API.Entities.Chapter", null)
|
||||
|
|
@ -3690,6 +3737,13 @@ namespace API.Data.Migrations
|
|||
b.Navigation("ReadingProfiles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.AppUserReadingProfile", b =>
|
||||
{
|
||||
b.Navigation("Libraries");
|
||||
|
||||
b.Navigation("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.Chapter", b =>
|
||||
{
|
||||
b.Navigation("ExternalRatings");
|
||||
|
|
@ -3713,6 +3767,8 @@ namespace API.Data.Migrations
|
|||
|
||||
b.Navigation("LibraryFileTypes");
|
||||
|
||||
b.Navigation("ReadingProfiles");
|
||||
|
||||
b.Navigation("Series");
|
||||
});
|
||||
|
||||
|
|
@ -3750,6 +3806,8 @@ namespace API.Data.Migrations
|
|||
|
||||
b.Navigation("Ratings");
|
||||
|
||||
b.Navigation("ReadingProfiles");
|
||||
|
||||
b.Navigation("RelationOf");
|
||||
|
||||
b.Navigation("Relations");
|
||||
|
|
@ -71,47 +71,65 @@ namespace API.Data.Migrations
|
|||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AppUserReadingProfileLibrary",
|
||||
name: "LibraryReadingProfile",
|
||||
columns: table => new
|
||||
{
|
||||
LibrariesId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
ReadingProfilesId = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
AppUserId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
LibraryId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
ReadingProfileId = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AppUserReadingProfileLibrary", x => new { x.LibrariesId, x.ReadingProfilesId });
|
||||
table.PrimaryKey("PK_LibraryReadingProfile", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_AppUserReadingProfileLibrary_AppUserReadingProfile_ReadingProfilesId",
|
||||
column: x => x.ReadingProfilesId,
|
||||
name: "FK_LibraryReadingProfile_AppUserReadingProfile_ReadingProfileId",
|
||||
column: x => x.ReadingProfileId,
|
||||
principalTable: "AppUserReadingProfile",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_AppUserReadingProfileLibrary_Library_LibrariesId",
|
||||
column: x => x.LibrariesId,
|
||||
name: "FK_LibraryReadingProfile_AspNetUsers_AppUserId",
|
||||
column: x => x.AppUserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_LibraryReadingProfile_Library_LibraryId",
|
||||
column: x => x.LibraryId,
|
||||
principalTable: "Library",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AppUserReadingProfileSeries",
|
||||
name: "SeriesReadingProfile",
|
||||
columns: table => new
|
||||
{
|
||||
ReadingProfilesId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
SeriesId = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
AppUserId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
SeriesId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
ReadingProfileId = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AppUserReadingProfileSeries", x => new { x.ReadingProfilesId, x.SeriesId });
|
||||
table.PrimaryKey("PK_SeriesReadingProfile", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_AppUserReadingProfileSeries_AppUserReadingProfile_ReadingProfilesId",
|
||||
column: x => x.ReadingProfilesId,
|
||||
name: "FK_SeriesReadingProfile_AppUserReadingProfile_ReadingProfileId",
|
||||
column: x => x.ReadingProfileId,
|
||||
principalTable: "AppUserReadingProfile",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_AppUserReadingProfileSeries_Series_SeriesId",
|
||||
name: "FK_SeriesReadingProfile_AspNetUsers_AppUserId",
|
||||
column: x => x.AppUserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_SeriesReadingProfile_Series_SeriesId",
|
||||
column: x => x.SeriesId,
|
||||
principalTable: "Series",
|
||||
principalColumn: "Id",
|
||||
|
|
@ -129,13 +147,34 @@ namespace API.Data.Migrations
|
|||
column: "UserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AppUserReadingProfileLibrary_ReadingProfilesId",
|
||||
table: "AppUserReadingProfileLibrary",
|
||||
column: "ReadingProfilesId");
|
||||
name: "IX_LibraryReadingProfile_AppUserId",
|
||||
table: "LibraryReadingProfile",
|
||||
column: "AppUserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AppUserReadingProfileSeries_SeriesId",
|
||||
table: "AppUserReadingProfileSeries",
|
||||
name: "IX_LibraryReadingProfile_LibraryId_AppUserId",
|
||||
table: "LibraryReadingProfile",
|
||||
columns: new[] { "LibraryId", "AppUserId" },
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_LibraryReadingProfile_ReadingProfileId",
|
||||
table: "LibraryReadingProfile",
|
||||
column: "ReadingProfileId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_SeriesReadingProfile_AppUserId",
|
||||
table: "SeriesReadingProfile",
|
||||
column: "AppUserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_SeriesReadingProfile_ReadingProfileId",
|
||||
table: "SeriesReadingProfile",
|
||||
column: "ReadingProfileId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_SeriesReadingProfile_SeriesId",
|
||||
table: "SeriesReadingProfile",
|
||||
column: "SeriesId");
|
||||
}
|
||||
|
||||
|
|
@ -143,10 +182,10 @@ namespace API.Data.Migrations
|
|||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "AppUserReadingProfileLibrary");
|
||||
name: "LibraryReadingProfile");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AppUserReadingProfileSeries");
|
||||
name: "SeriesReadingProfile");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AppUserReadingProfile");
|
||||
|
|
@ -1377,6 +1377,33 @@ namespace API.Data.Migrations
|
|||
b.ToTable("LibraryFileTypeGroup");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.LibraryReadingProfile", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("AppUserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("LibraryId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ReadingProfileId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AppUserId");
|
||||
|
||||
b.HasIndex("ReadingProfileId");
|
||||
|
||||
b.HasIndex("LibraryId", "AppUserId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("LibraryReadingProfile");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.MangaFile", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
|
|
@ -2364,6 +2391,32 @@ namespace API.Data.Migrations
|
|||
b.ToTable("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.SeriesReadingProfile", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("AppUserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ReadingProfileId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SeriesId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AppUserId");
|
||||
|
||||
b.HasIndex("ReadingProfileId");
|
||||
|
||||
b.HasIndex("SeriesId");
|
||||
|
||||
b.ToTable("SeriesReadingProfile");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.ServerSetting", b =>
|
||||
{
|
||||
b.Property<int>("Key")
|
||||
|
|
@ -2595,36 +2648,6 @@ namespace API.Data.Migrations
|
|||
b.ToTable("AppUserLibrary");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AppUserReadingProfileLibrary", b =>
|
||||
{
|
||||
b.Property<int>("LibrariesId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ReadingProfilesId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("LibrariesId", "ReadingProfilesId");
|
||||
|
||||
b.HasIndex("ReadingProfilesId");
|
||||
|
||||
b.ToTable("AppUserReadingProfileLibrary");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AppUserReadingProfileSeries", b =>
|
||||
{
|
||||
b.Property<int>("ReadingProfilesId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SeriesId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("ReadingProfilesId", "SeriesId");
|
||||
|
||||
b.HasIndex("SeriesId");
|
||||
|
||||
b.ToTable("AppUserReadingProfileSeries");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ChapterGenre", b =>
|
||||
{
|
||||
b.Property<int>("ChaptersId")
|
||||
|
|
@ -3158,6 +3181,33 @@ namespace API.Data.Migrations
|
|||
b.Navigation("Library");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.LibraryReadingProfile", b =>
|
||||
{
|
||||
b.HasOne("API.Entities.AppUser", "AppUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("AppUserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("API.Entities.Library", "Library")
|
||||
.WithMany("ReadingProfiles")
|
||||
.HasForeignKey("LibraryId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("API.Entities.AppUserReadingProfile", "ReadingProfile")
|
||||
.WithMany("Libraries")
|
||||
.HasForeignKey("ReadingProfileId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("AppUser");
|
||||
|
||||
b.Navigation("Library");
|
||||
|
||||
b.Navigation("ReadingProfile");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.MangaFile", b =>
|
||||
{
|
||||
b.HasOne("API.Entities.Chapter", "Chapter")
|
||||
|
|
@ -3415,6 +3465,33 @@ namespace API.Data.Migrations
|
|||
b.Navigation("Library");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.SeriesReadingProfile", b =>
|
||||
{
|
||||
b.HasOne("API.Entities.AppUser", "AppUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("AppUserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("API.Entities.AppUserReadingProfile", "ReadingProfile")
|
||||
.WithMany("Series")
|
||||
.HasForeignKey("ReadingProfileId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("API.Entities.Series", "Series")
|
||||
.WithMany("ReadingProfiles")
|
||||
.HasForeignKey("SeriesId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("AppUser");
|
||||
|
||||
b.Navigation("ReadingProfile");
|
||||
|
||||
b.Navigation("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.Volume", b =>
|
||||
{
|
||||
b.HasOne("API.Entities.Series", "Series")
|
||||
|
|
@ -3456,36 +3533,6 @@ namespace API.Data.Migrations
|
|||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AppUserReadingProfileLibrary", b =>
|
||||
{
|
||||
b.HasOne("API.Entities.Library", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("LibrariesId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("API.Entities.AppUserReadingProfile", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("ReadingProfilesId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AppUserReadingProfileSeries", b =>
|
||||
{
|
||||
b.HasOne("API.Entities.AppUserReadingProfile", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("ReadingProfilesId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("API.Entities.Series", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("SeriesId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ChapterGenre", b =>
|
||||
{
|
||||
b.HasOne("API.Entities.Chapter", null)
|
||||
|
|
@ -3687,6 +3734,13 @@ namespace API.Data.Migrations
|
|||
b.Navigation("ReadingProfiles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.AppUserReadingProfile", b =>
|
||||
{
|
||||
b.Navigation("Libraries");
|
||||
|
||||
b.Navigation("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.Chapter", b =>
|
||||
{
|
||||
b.Navigation("ExternalRatings");
|
||||
|
|
@ -3710,6 +3764,8 @@ namespace API.Data.Migrations
|
|||
|
||||
b.Navigation("LibraryFileTypes");
|
||||
|
||||
b.Navigation("ReadingProfiles");
|
||||
|
||||
b.Navigation("Series");
|
||||
});
|
||||
|
||||
|
|
@ -3747,6 +3803,8 @@ namespace API.Data.Migrations
|
|||
|
||||
b.Navigation("Ratings");
|
||||
|
||||
b.Navigation("ReadingProfiles");
|
||||
|
||||
b.Navigation("RelationOf");
|
||||
|
||||
b.Navigation("Relations");
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ public enum ReadingProfileIncludes
|
|||
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);
|
||||
Task<UserReadingProfileDto?> GetProfileDtoForSeries(int userId, int seriesId);
|
||||
Task<AppUserReadingProfile?> GetProfileForLibrary(int userId, int libraryId, ReadingProfileIncludes includes = ReadingProfileIncludes.None);
|
||||
|
|
@ -31,6 +32,8 @@ public interface IAppUserReadingProfileRepository
|
|||
Task<AppUserReadingProfile?> GetProfile(int profileId, ReadingProfileIncludes includes = ReadingProfileIncludes.None);
|
||||
Task<UserReadingProfileDto?> GetProfileDto(int profileId);
|
||||
Task<AppUserReadingProfile?> GetProfileByName(int userId, string name);
|
||||
Task<SeriesReadingProfile?> GetSeriesProfile(int userId, int seriesId);
|
||||
Task<LibraryReadingProfile?> GetLibraryProfile(int userId, int libraryId);
|
||||
|
||||
void Add(AppUserReadingProfile readingProfile);
|
||||
void Update(AppUserReadingProfile readingProfile);
|
||||
|
|
@ -48,10 +51,20 @@ public class AppUserReadingProfileRepository(DataContext context, IMapper mapper
|
|||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<IList<UserReadingProfileDto>> GetProfilesDtoForUser(int userId, bool nonImplicitOnly,
|
||||
ReadingProfileIncludes includes = ReadingProfileIncludes.None)
|
||||
{
|
||||
return await context.AppUserReadingProfile
|
||||
.Where(rp => rp.UserId == userId && !(nonImplicitOnly && rp.Implicit))
|
||||
.Includes(includes)
|
||||
.ProjectTo<UserReadingProfileDto>(mapper.ConfigurationProvider)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<AppUserReadingProfile?> GetProfileForSeries(int userId, int seriesId, ReadingProfileIncludes includes = ReadingProfileIncludes.None)
|
||||
{
|
||||
return await context.AppUserReadingProfile
|
||||
.Where(rp => rp.UserId == userId && rp.Series.Any(s => s.Id == seriesId))
|
||||
.Where(rp => rp.UserId == userId && rp.Series.Any(s => s.SeriesId == seriesId))
|
||||
.Includes(includes)
|
||||
.OrderByDescending(rp => rp.Implicit) // Get implicit profiles first
|
||||
.FirstOrDefaultAsync();
|
||||
|
|
@ -60,7 +73,7 @@ public class AppUserReadingProfileRepository(DataContext context, IMapper mapper
|
|||
public async Task<UserReadingProfileDto?> GetProfileDtoForSeries(int userId, int seriesId)
|
||||
{
|
||||
return await context.AppUserReadingProfile
|
||||
.Where(rp => rp.UserId == userId && rp.Series.Any(s => s.Id == seriesId))
|
||||
.Where(rp => rp.UserId == userId && rp.Series.Any(s => s.SeriesId == seriesId))
|
||||
.OrderByDescending(rp => rp.Implicit) // Get implicit profiles first
|
||||
.ProjectTo<UserReadingProfileDto>(mapper.ConfigurationProvider)
|
||||
.FirstOrDefaultAsync();
|
||||
|
|
@ -69,7 +82,7 @@ public class AppUserReadingProfileRepository(DataContext context, IMapper mapper
|
|||
public async Task<AppUserReadingProfile?> GetProfileForLibrary(int userId, int libraryId, ReadingProfileIncludes includes = ReadingProfileIncludes.None)
|
||||
{
|
||||
return await context.AppUserReadingProfile
|
||||
.Where(rp => rp.UserId == userId && rp.Libraries.Any(s => s.Id == libraryId))
|
||||
.Where(rp => rp.UserId == userId && rp.Libraries.Any(s => s.LibraryId == libraryId))
|
||||
.Includes(includes)
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
|
|
@ -77,7 +90,7 @@ public class AppUserReadingProfileRepository(DataContext context, IMapper mapper
|
|||
public async Task<UserReadingProfileDto?> GetProfileDtoForLibrary(int userId, int libraryId)
|
||||
{
|
||||
return await context.AppUserReadingProfile
|
||||
.Where(rp => rp.UserId == userId && rp.Libraries.Any(s => s.Id == libraryId))
|
||||
.Where(rp => rp.UserId == userId && rp.Libraries.Any(s => s.LibraryId == libraryId))
|
||||
.ProjectTo<UserReadingProfileDto>(mapper.ConfigurationProvider)
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
|
|
@ -106,6 +119,20 @@ public class AppUserReadingProfileRepository(DataContext context, IMapper mapper
|
|||
.FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<SeriesReadingProfile?> GetSeriesProfile(int userId, int seriesId)
|
||||
{
|
||||
return await context.SeriesReadingProfile
|
||||
.Where(rp => rp.SeriesId == seriesId && rp.AppUserId == userId)
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<LibraryReadingProfile?> GetLibraryProfile(int userId, int libraryId)
|
||||
{
|
||||
return await context.LibraryReadingProfile
|
||||
.Where(rp => rp.LibraryId == libraryId && rp.AppUserId == userId)
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public void Add(AppUserReadingProfile readingProfile)
|
||||
{
|
||||
context.AppUserReadingProfile.Add(readingProfile);
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ public class Library : IEntityDate, IHasCoverImage
|
|||
public ICollection<Series> Series { get; set; } = null!;
|
||||
public ICollection<LibraryFileTypeGroup> LibraryFileTypes { get; set; } = new List<LibraryFileTypeGroup>();
|
||||
public ICollection<LibraryExcludePattern> LibraryExcludePatterns { get; set; } = new List<LibraryExcludePattern>();
|
||||
public ICollection<AppUserReadingProfile> ReadingProfiles { get; set; } = null!;
|
||||
public ICollection<LibraryReadingProfile> ReadingProfiles { get; set; } = null!;
|
||||
|
||||
public void UpdateLastModified()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ public class AppUserReadingProfile
|
|||
public int UserId { get; set; }
|
||||
public AppUser User { get; set; }
|
||||
|
||||
public ICollection<Series> Series { get; set; }
|
||||
public ICollection<Library> Libraries { get; set; }
|
||||
public ICollection<SeriesReadingProfile> Series { get; set; }
|
||||
public ICollection<LibraryReadingProfile> Libraries { get; set; }
|
||||
|
||||
#region MangaReader
|
||||
|
||||
20
API/Entities/ReadingProfile/LibraryReadingProfile.cs
Normal file
20
API/Entities/ReadingProfile/LibraryReadingProfile.cs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace API.Entities;
|
||||
|
||||
[Index(nameof(LibraryId), nameof(AppUserId), IsUnique = true)]
|
||||
public class LibraryReadingProfile
|
||||
{
|
||||
|
||||
public int Id { get; set; }
|
||||
|
||||
public int AppUserId { get; set; }
|
||||
public AppUser AppUser { get; set; }
|
||||
|
||||
public int LibraryId { get; set; }
|
||||
public Library Library { get; set; }
|
||||
|
||||
public int ReadingProfileId { get; set; }
|
||||
public AppUserReadingProfile ReadingProfile { get; set; }
|
||||
|
||||
}
|
||||
17
API/Entities/ReadingProfile/SeriesReadingProfile.cs
Normal file
17
API/Entities/ReadingProfile/SeriesReadingProfile.cs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
namespace API.Entities;
|
||||
|
||||
public class SeriesReadingProfile
|
||||
{
|
||||
|
||||
public int Id { get; set; }
|
||||
|
||||
public int AppUserId { get; set; }
|
||||
public AppUser AppUser { get; set; }
|
||||
|
||||
public int SeriesId { get; set; }
|
||||
public Series Series { get; set; }
|
||||
|
||||
public int ReadingProfileId { get; set; }
|
||||
public AppUserReadingProfile ReadingProfile { get; set; }
|
||||
|
||||
}
|
||||
|
|
@ -135,7 +135,7 @@ public class Series : IEntityDate, IHasReadTimeEstimate, IHasCoverImage
|
|||
public List<Volume> Volumes { get; set; } = null!;
|
||||
public Library Library { get; set; } = null!;
|
||||
public int LibraryId { get; set; }
|
||||
public ICollection<AppUserReadingProfile> ReadingProfiles { get; set; } = null!;
|
||||
public ICollection<SeriesReadingProfile> ReadingProfiles { get; set; } = null!;
|
||||
|
||||
|
||||
public void UpdateLastFolderScanned()
|
||||
|
|
|
|||
|
|
@ -283,7 +283,13 @@ public class AutoMapperProfiles : Profile
|
|||
opt.MapFrom(src => src.BookThemeName))
|
||||
.ForMember(dest => dest.BookReaderLayoutMode,
|
||||
opt =>
|
||||
opt.MapFrom(src => src.BookReaderLayoutMode));
|
||||
opt.MapFrom(src => src.BookReaderLayoutMode))
|
||||
.ForMember(dest => dest.SeriesIds,
|
||||
opt =>
|
||||
opt.MapFrom(src => src.Series.Select(s => s.SeriesId).ToList()))
|
||||
.ForMember(dest => dest.LibraryIds,
|
||||
opt =>
|
||||
opt.MapFrom(src => src.Libraries.Select(s => s.LibraryId).ToList()));
|
||||
|
||||
|
||||
CreateMap<AppUserBookmark, BookmarkDto>();
|
||||
|
|
|
|||
|
|
@ -21,13 +21,21 @@ public class AppUserReadingProfileBuilder
|
|||
|
||||
public AppUserReadingProfileBuilder WithSeries(Series series)
|
||||
{
|
||||
_profile.Series.Add(series);
|
||||
_profile.Series.Add(new SeriesReadingProfile
|
||||
{
|
||||
Series = series,
|
||||
AppUserId = _profile.UserId,
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
public AppUserReadingProfileBuilder WithLibrary(Library library)
|
||||
{
|
||||
_profile.Libraries.Add(library);
|
||||
_profile.Libraries.Add(new LibraryReadingProfile
|
||||
{
|
||||
Library = library,
|
||||
AppUserId = _profile.UserId,
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ public interface IReadingProfileService
|
|||
/// <param name="userId"></param>
|
||||
/// <param name="dto"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>Does not update connected series and libraries</remarks>
|
||||
Task<bool> UpdateReadingProfile(int userId, UserReadingProfileDto dto);
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -74,6 +75,12 @@ public interface IReadingProfileService
|
|||
/// <returns></returns>
|
||||
Task SetDefaultReadingProfile(int userId, int profileId);
|
||||
|
||||
Task AddProfileToSeries(int userId, int profileId, int seriesId);
|
||||
Task RemoveProfileFromSeries(int userId, int profileId, int seriesId);
|
||||
|
||||
Task AddProfileToLibrary(int userId, int profileId, int libraryId);
|
||||
Task RemoveProfileFromLibrary(int userId, int profileId, int libraryId);
|
||||
|
||||
}
|
||||
|
||||
public class ReadingProfileService(IUnitOfWork unitOfWork, ILocalizationService localizationService): IReadingProfileService
|
||||
|
|
@ -164,7 +171,7 @@ public class ReadingProfileService(IUnitOfWork unitOfWork, ILocalizationService
|
|||
|
||||
if (!profile.Implicit) return;
|
||||
|
||||
profile.Series = profile.Series.Where(s => s.Id != seriesId).ToList();
|
||||
profile.Series = profile.Series.Where(s => s.SeriesId != seriesId).ToList();
|
||||
|
||||
await unitOfWork.CommitAsync();
|
||||
}
|
||||
|
|
@ -197,6 +204,70 @@ public class ReadingProfileService(IUnitOfWork unitOfWork, ILocalizationService
|
|||
await unitOfWork.CommitAsync();
|
||||
}
|
||||
|
||||
public async Task AddProfileToSeries(int userId, int profileId, int seriesId)
|
||||
{
|
||||
var profile = await unitOfWork.AppUserReadingProfileRepository.GetProfile(profileId);
|
||||
if (profile == null) throw new KavitaException("profile-not-found");
|
||||
|
||||
if (profile.UserId != userId) throw new UnauthorizedAccessException();
|
||||
|
||||
var seriesProfile = await unitOfWork.AppUserReadingProfileRepository.GetSeriesProfile(userId, seriesId);
|
||||
if (seriesProfile == null)
|
||||
{
|
||||
seriesProfile = new SeriesReadingProfile
|
||||
{
|
||||
AppUserId = userId,
|
||||
SeriesId = seriesId,
|
||||
};
|
||||
}
|
||||
|
||||
seriesProfile.ReadingProfile = profile;
|
||||
await unitOfWork.CommitAsync();
|
||||
}
|
||||
|
||||
public async Task RemoveProfileFromSeries(int userId, int profileId, int seriesId)
|
||||
{
|
||||
var profile = await unitOfWork.AppUserReadingProfileRepository.GetProfile(profileId);
|
||||
if (profile == null) throw new KavitaException("profile-not-found");
|
||||
|
||||
if (profile.UserId != userId) throw new UnauthorizedAccessException();
|
||||
|
||||
profile.Series = profile.Series.Where(s => s.SeriesId != seriesId).ToList();
|
||||
await unitOfWork.CommitAsync();
|
||||
}
|
||||
|
||||
public async Task AddProfileToLibrary(int userId, int profileId, int libraryId)
|
||||
{
|
||||
var profile = await unitOfWork.AppUserReadingProfileRepository.GetProfile(profileId);
|
||||
if (profile == null) throw new KavitaException("profile-not-found");
|
||||
|
||||
if (profile.UserId != userId) throw new UnauthorizedAccessException();
|
||||
|
||||
var libraryProfile = await unitOfWork.AppUserReadingProfileRepository.GetLibraryProfile(userId, libraryId);
|
||||
if (libraryProfile == null)
|
||||
{
|
||||
libraryProfile = new LibraryReadingProfile
|
||||
{
|
||||
AppUserId = userId,
|
||||
LibraryId = libraryId,
|
||||
};
|
||||
}
|
||||
|
||||
libraryProfile.ReadingProfile = profile;
|
||||
await unitOfWork.CommitAsync();
|
||||
}
|
||||
|
||||
public async Task RemoveProfileFromLibrary(int userId, int profileId, int libraryId)
|
||||
{
|
||||
var profile = await unitOfWork.AppUserReadingProfileRepository.GetProfile(profileId);
|
||||
if (profile == null) throw new KavitaException("profile-not-found");
|
||||
|
||||
if (profile.UserId != userId) throw new UnauthorizedAccessException();
|
||||
|
||||
profile.Libraries = profile.Libraries.Where(s => s.LibraryId != libraryId).ToList();
|
||||
await unitOfWork.CommitAsync();
|
||||
}
|
||||
|
||||
private static void UpdateReaderProfileFields(AppUserReadingProfile existingProfile, UserReadingProfileDto dto, bool updateName = true)
|
||||
{
|
||||
if (updateName && !string.IsNullOrEmpty(dto.Name) && existingProfile.NormalizedName != dto.Name.ToNormalized())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue