diff --git a/FictionArchive.Service.UserNovelDataService/FictionArchive.Service.UserNovelDataService.csproj b/FictionArchive.Service.UserNovelDataService/FictionArchive.Service.UserNovelDataService.csproj index 6bee3ff..e486c8c 100644 --- a/FictionArchive.Service.UserNovelDataService/FictionArchive.Service.UserNovelDataService.csproj +++ b/FictionArchive.Service.UserNovelDataService/FictionArchive.Service.UserNovelDataService.csproj @@ -7,6 +7,13 @@ Linux + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + .dockerignore diff --git a/FictionArchive.Service.UserNovelDataService/GraphQL/Mutation.cs b/FictionArchive.Service.UserNovelDataService/GraphQL/Mutation.cs index 5810b81..09a46e1 100644 --- a/FictionArchive.Service.UserNovelDataService/GraphQL/Mutation.cs +++ b/FictionArchive.Service.UserNovelDataService/GraphQL/Mutation.cs @@ -1,6 +1,109 @@ +using System.Security.Claims; +using FictionArchive.Service.UserNovelDataService.Models.Database; +using FictionArchive.Service.UserNovelDataService.Models.DTOs; +using FictionArchive.Service.UserNovelDataService.Services; +using HotChocolate.Authorization; +using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; + namespace FictionArchive.Service.UserNovelDataService.GraphQL; public class Mutation { - -} \ No newline at end of file + [Authorize] + [Error] + public async Task UpsertBookmark( + UserNovelDataServiceDbContext dbContext, + ClaimsPrincipal claimsPrincipal, + UpsertBookmarkInput input) + { + var oAuthProviderId = claimsPrincipal.FindFirst(ClaimTypes.NameIdentifier)?.Value; + if (string.IsNullOrEmpty(oAuthProviderId)) + { + throw new InvalidOperationException("Unable to determine current user identity"); + } + + var user = await dbContext.Users + .FirstOrDefaultAsync(u => u.OAuthProviderId == oAuthProviderId); + + if (user == null) + { + // Auto-create user if not exists + user = new User { OAuthProviderId = oAuthProviderId }; + dbContext.Users.Add(user); + await dbContext.SaveChangesAsync(); + } + + var existingBookmark = await dbContext.Bookmarks + .FirstOrDefaultAsync(b => b.UserId == user.Id && b.ChapterId == input.ChapterId); + + if (existingBookmark != null) + { + // Update existing + existingBookmark.Description = input.Description; + } + else + { + // Create new + existingBookmark = new Bookmark + { + UserId = user.Id, + NovelId = input.NovelId, + ChapterId = input.ChapterId, + Description = input.Description + }; + dbContext.Bookmarks.Add(existingBookmark); + } + + await dbContext.SaveChangesAsync(); + + return new BookmarkPayload + { + Success = true, + Bookmark = new BookmarkDto + { + Id = existingBookmark.Id, + ChapterId = existingBookmark.ChapterId, + NovelId = existingBookmark.NovelId, + Description = existingBookmark.Description, + CreatedTime = existingBookmark.CreatedTime + } + }; + } + + [Authorize] + [Error] + public async Task RemoveBookmark( + UserNovelDataServiceDbContext dbContext, + ClaimsPrincipal claimsPrincipal, + uint chapterId) + { + var oAuthProviderId = claimsPrincipal.FindFirst(ClaimTypes.NameIdentifier)?.Value; + if (string.IsNullOrEmpty(oAuthProviderId)) + { + throw new InvalidOperationException("Unable to determine current user identity"); + } + + var user = await dbContext.Users + .AsNoTracking() + .FirstOrDefaultAsync(u => u.OAuthProviderId == oAuthProviderId); + + if (user == null) + { + return new BookmarkPayload { Success = false }; + } + + var bookmark = await dbContext.Bookmarks + .FirstOrDefaultAsync(b => b.UserId == user.Id && b.ChapterId == chapterId); + + if (bookmark == null) + { + return new BookmarkPayload { Success = false }; + } + + dbContext.Bookmarks.Remove(bookmark); + await dbContext.SaveChangesAsync(); + + return new BookmarkPayload { Success = true }; + } +} diff --git a/FictionArchive.Service.UserNovelDataService/GraphQL/Query.cs b/FictionArchive.Service.UserNovelDataService/GraphQL/Query.cs index 7ad2845..0b6e297 100644 --- a/FictionArchive.Service.UserNovelDataService/GraphQL/Query.cs +++ b/FictionArchive.Service.UserNovelDataService/GraphQL/Query.cs @@ -1,6 +1,45 @@ +using System.Security.Claims; +using FictionArchive.Service.UserNovelDataService.Models.DTOs; +using FictionArchive.Service.UserNovelDataService.Services; +using HotChocolate.Authorization; +using Microsoft.EntityFrameworkCore; + namespace FictionArchive.Service.UserNovelDataService.GraphQL; public class Query { - -} \ No newline at end of file + [Authorize] + public async Task> GetBookmarks( + UserNovelDataServiceDbContext dbContext, + ClaimsPrincipal claimsPrincipal, + uint novelId) + { + var oAuthProviderId = claimsPrincipal.FindFirst(ClaimTypes.NameIdentifier)?.Value; + if (string.IsNullOrEmpty(oAuthProviderId)) + { + return new List().AsQueryable(); + } + + var user = await dbContext.Users + .AsNoTracking() + .FirstOrDefaultAsync(u => u.OAuthProviderId == oAuthProviderId); + + if (user == null) + { + return new List().AsQueryable(); + } + + return dbContext.Bookmarks + .AsNoTracking() + .Where(b => b.UserId == user.Id && b.NovelId == novelId) + .OrderByDescending(b => b.CreatedTime) + .Select(b => new BookmarkDto + { + Id = b.Id, + ChapterId = b.ChapterId, + NovelId = b.NovelId, + Description = b.Description, + CreatedTime = b.CreatedTime + }); + } +} diff --git a/FictionArchive.Service.UserNovelDataService/Migrations/20251230181559_AddBookmarks.Designer.cs b/FictionArchive.Service.UserNovelDataService/Migrations/20251230181559_AddBookmarks.Designer.cs new file mode 100644 index 0000000..fdb9d07 --- /dev/null +++ b/FictionArchive.Service.UserNovelDataService/Migrations/20251230181559_AddBookmarks.Designer.cs @@ -0,0 +1,99 @@ +// +using System; +using FictionArchive.Service.UserNovelDataService.Services; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NodaTime; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace FictionArchive.Service.UserNovelDataService.Migrations +{ + [DbContext(typeof(UserNovelDataServiceDbContext))] + [Migration("20251230181559_AddBookmarks")] + partial class AddBookmarks + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("FictionArchive.Service.UserNovelDataService.Models.Database.Bookmark", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ChapterId") + .HasColumnType("bigint"); + + b.Property("CreatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("LastUpdatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("NovelId") + .HasColumnType("bigint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "ChapterId") + .IsUnique(); + + b.HasIndex("UserId", "NovelId"); + + b.ToTable("Bookmarks"); + }); + + modelBuilder.Entity("FictionArchive.Service.UserNovelDataService.Models.Database.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("LastUpdatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("OAuthProviderId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("FictionArchive.Service.UserNovelDataService.Models.Database.Bookmark", b => + { + b.HasOne("FictionArchive.Service.UserNovelDataService.Models.Database.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/FictionArchive.Service.UserNovelDataService/Migrations/20251230181559_AddBookmarks.cs b/FictionArchive.Service.UserNovelDataService/Migrations/20251230181559_AddBookmarks.cs new file mode 100644 index 0000000..f987bda --- /dev/null +++ b/FictionArchive.Service.UserNovelDataService/Migrations/20251230181559_AddBookmarks.cs @@ -0,0 +1,76 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using NodaTime; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace FictionArchive.Service.UserNovelDataService.Migrations +{ + /// + public partial class AddBookmarks : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Users", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + OAuthProviderId = table.Column(type: "text", nullable: false), + CreatedTime = table.Column(type: "timestamp with time zone", nullable: false), + LastUpdatedTime = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Users", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Bookmarks", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + UserId = table.Column(type: "uuid", nullable: false), + ChapterId = table.Column(type: "bigint", nullable: false), + NovelId = table.Column(type: "bigint", nullable: false), + Description = table.Column(type: "text", nullable: true), + CreatedTime = table.Column(type: "timestamp with time zone", nullable: false), + LastUpdatedTime = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Bookmarks", x => x.Id); + table.ForeignKey( + name: "FK_Bookmarks_Users_UserId", + column: x => x.UserId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_Bookmarks_UserId_ChapterId", + table: "Bookmarks", + columns: new[] { "UserId", "ChapterId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Bookmarks_UserId_NovelId", + table: "Bookmarks", + columns: new[] { "UserId", "NovelId" }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Bookmarks"); + + migrationBuilder.DropTable( + name: "Users"); + } + } +} diff --git a/FictionArchive.Service.UserNovelDataService/Migrations/UserNovelDataServiceDbContextModelSnapshot.cs b/FictionArchive.Service.UserNovelDataService/Migrations/UserNovelDataServiceDbContextModelSnapshot.cs new file mode 100644 index 0000000..1e50402 --- /dev/null +++ b/FictionArchive.Service.UserNovelDataService/Migrations/UserNovelDataServiceDbContextModelSnapshot.cs @@ -0,0 +1,96 @@ +// +using System; +using FictionArchive.Service.UserNovelDataService.Services; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NodaTime; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace FictionArchive.Service.UserNovelDataService.Migrations +{ + [DbContext(typeof(UserNovelDataServiceDbContext))] + partial class UserNovelDataServiceDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("FictionArchive.Service.UserNovelDataService.Models.Database.Bookmark", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ChapterId") + .HasColumnType("bigint"); + + b.Property("CreatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("LastUpdatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("NovelId") + .HasColumnType("bigint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "ChapterId") + .IsUnique(); + + b.HasIndex("UserId", "NovelId"); + + b.ToTable("Bookmarks"); + }); + + modelBuilder.Entity("FictionArchive.Service.UserNovelDataService.Models.Database.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("LastUpdatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("OAuthProviderId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("FictionArchive.Service.UserNovelDataService.Models.Database.Bookmark", b => + { + b.HasOne("FictionArchive.Service.UserNovelDataService.Models.Database.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/FictionArchive.Service.UserNovelDataService/Models/DTOs/BookmarkDto.cs b/FictionArchive.Service.UserNovelDataService/Models/DTOs/BookmarkDto.cs new file mode 100644 index 0000000..844eaca --- /dev/null +++ b/FictionArchive.Service.UserNovelDataService/Models/DTOs/BookmarkDto.cs @@ -0,0 +1,12 @@ +using NodaTime; + +namespace FictionArchive.Service.UserNovelDataService.Models.DTOs; + +public class BookmarkDto +{ + public int Id { get; init; } + public uint ChapterId { get; init; } + public uint NovelId { get; init; } + public string? Description { get; init; } + public Instant CreatedTime { get; init; } +} diff --git a/FictionArchive.Service.UserNovelDataService/Models/DTOs/BookmarkPayload.cs b/FictionArchive.Service.UserNovelDataService/Models/DTOs/BookmarkPayload.cs new file mode 100644 index 0000000..cfc334f --- /dev/null +++ b/FictionArchive.Service.UserNovelDataService/Models/DTOs/BookmarkPayload.cs @@ -0,0 +1,7 @@ +namespace FictionArchive.Service.UserNovelDataService.Models.DTOs; + +public class BookmarkPayload +{ + public BookmarkDto? Bookmark { get; init; } + public bool Success { get; init; } +} diff --git a/FictionArchive.Service.UserNovelDataService/Models/DTOs/UpsertBookmarkInput.cs b/FictionArchive.Service.UserNovelDataService/Models/DTOs/UpsertBookmarkInput.cs new file mode 100644 index 0000000..06cfebb --- /dev/null +++ b/FictionArchive.Service.UserNovelDataService/Models/DTOs/UpsertBookmarkInput.cs @@ -0,0 +1,3 @@ +namespace FictionArchive.Service.UserNovelDataService.Models.DTOs; + +public record UpsertBookmarkInput(uint NovelId, uint ChapterId, string? Description); diff --git a/FictionArchive.Service.UserNovelDataService/Models/Database/Bookmark.cs b/FictionArchive.Service.UserNovelDataService/Models/Database/Bookmark.cs new file mode 100644 index 0000000..9a7e836 --- /dev/null +++ b/FictionArchive.Service.UserNovelDataService/Models/Database/Bookmark.cs @@ -0,0 +1,14 @@ +using FictionArchive.Service.Shared.Models; + +namespace FictionArchive.Service.UserNovelDataService.Models.Database; + +public class Bookmark : BaseEntity +{ + public Guid UserId { get; set; } + public virtual User User { get; set; } = null!; + + public uint ChapterId { get; set; } + public uint NovelId { get; set; } + + public string? Description { get; set; } +} diff --git a/FictionArchive.Service.UserNovelDataService/Models/Database/Chapter.cs b/FictionArchive.Service.UserNovelDataService/Models/Database/Chapter.cs new file mode 100644 index 0000000..1cc7e91 --- /dev/null +++ b/FictionArchive.Service.UserNovelDataService/Models/Database/Chapter.cs @@ -0,0 +1,8 @@ +using FictionArchive.Service.Shared.Models; + +namespace FictionArchive.Service.UserNovelDataService.Models.Database; + +public class Chapter : BaseEntity +{ + +} \ No newline at end of file diff --git a/FictionArchive.Service.UserNovelDataService/Models/Database/Novel.cs b/FictionArchive.Service.UserNovelDataService/Models/Database/Novel.cs new file mode 100644 index 0000000..fc979d8 --- /dev/null +++ b/FictionArchive.Service.UserNovelDataService/Models/Database/Novel.cs @@ -0,0 +1,8 @@ +using FictionArchive.Service.Shared.Models; + +namespace FictionArchive.Service.UserNovelDataService.Models.Database; + +public class Novel : BaseEntity +{ + public virtual ICollection Volumes { get; set; } +} \ No newline at end of file diff --git a/FictionArchive.Service.UserNovelDataService/Models/Database/User.cs b/FictionArchive.Service.UserNovelDataService/Models/Database/User.cs new file mode 100644 index 0000000..9863b97 --- /dev/null +++ b/FictionArchive.Service.UserNovelDataService/Models/Database/User.cs @@ -0,0 +1,8 @@ +using FictionArchive.Service.Shared.Models; + +namespace FictionArchive.Service.UserNovelDataService.Models.Database; + +public class User : BaseEntity +{ + public required string OAuthProviderId { get; set; } +} \ No newline at end of file diff --git a/FictionArchive.Service.UserNovelDataService/Models/Database/Volume.cs b/FictionArchive.Service.UserNovelDataService/Models/Database/Volume.cs new file mode 100644 index 0000000..f5b0293 --- /dev/null +++ b/FictionArchive.Service.UserNovelDataService/Models/Database/Volume.cs @@ -0,0 +1,8 @@ +using FictionArchive.Service.Shared.Models; + +namespace FictionArchive.Service.UserNovelDataService.Models.Database; + +public class Volume : BaseEntity +{ + public virtual ICollection Chapters { get; set; } +} \ No newline at end of file diff --git a/FictionArchive.Service.UserNovelDataService/Services/UserNovelDataServiceDbContext.cs b/FictionArchive.Service.UserNovelDataService/Services/UserNovelDataServiceDbContext.cs index 78383ca..38eb08c 100644 --- a/FictionArchive.Service.UserNovelDataService/Services/UserNovelDataServiceDbContext.cs +++ b/FictionArchive.Service.UserNovelDataService/Services/UserNovelDataServiceDbContext.cs @@ -1,11 +1,35 @@ using FictionArchive.Service.Shared.Services.Database; +using FictionArchive.Service.UserNovelDataService.Models.Database; using Microsoft.EntityFrameworkCore; namespace FictionArchive.Service.UserNovelDataService.Services; public class UserNovelDataServiceDbContext : FictionArchiveDbContext { + public DbSet Users { get; set; } + public DbSet Bookmarks { get; set; } + public UserNovelDataServiceDbContext(DbContextOptions options, ILogger logger) : base(options, logger) { } -} \ No newline at end of file + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + modelBuilder.Entity(entity => + { + // Unique constraint: one bookmark per chapter per user + entity.HasIndex(b => new { b.UserId, b.ChapterId }).IsUnique(); + + // Index for efficient "get bookmarks for novel" queries + entity.HasIndex(b => new { b.UserId, b.NovelId }); + + // User relationship + entity.HasOne(b => b.User) + .WithMany() + .HasForeignKey(b => b.UserId) + .OnDelete(DeleteBehavior.Cascade); + }); + } +} diff --git a/FictionArchive.Service.UserNovelDataService/appsettings.json b/FictionArchive.Service.UserNovelDataService/appsettings.json index baea518..425535a 100644 --- a/FictionArchive.Service.UserNovelDataService/appsettings.json +++ b/FictionArchive.Service.UserNovelDataService/appsettings.json @@ -12,5 +12,15 @@ "ConnectionString": "amqp://localhost", "ClientIdentifier": "UserNovelDataService" }, + "OIDC": { + "Authority": "https://auth.orfl.xyz/application/o/fiction-archive/", + "ClientId": "ldi5IpEidq2WW0Ka1lehVskb2SOBjnYRaZCpEyBh", + "Audience": "ldi5IpEidq2WW0Ka1lehVskb2SOBjnYRaZCpEyBh", + "ValidIssuer": "https://auth.orfl.xyz/application/o/fiction-archive/", + "ValidateIssuer": true, + "ValidateAudience": true, + "ValidateLifetime": true, + "ValidateIssuerSigningKey": true + }, "AllowedHosts": "*" } diff --git a/FictionArchive.Service.UserNovelDataService/subgraph-config.json b/FictionArchive.Service.UserNovelDataService/subgraph-config.json new file mode 100644 index 0000000..200a4f4 --- /dev/null +++ b/FictionArchive.Service.UserNovelDataService/subgraph-config.json @@ -0,0 +1,6 @@ +{ + "subgraph": "UserNovelData", + "http": { + "baseAddress": "https://localhost:7298/graphql" + } +} \ No newline at end of file diff --git a/fictionarchive-web-astro/src/lib/components/ChapterNavigation.svelte b/fictionarchive-web-astro/src/lib/components/ChapterNavigation.svelte index 6f547fc..f7eadc4 100644 --- a/fictionarchive-web-astro/src/lib/components/ChapterNavigation.svelte +++ b/fictionarchive-web-astro/src/lib/components/ChapterNavigation.svelte @@ -1,29 +1,131 @@
@@ -38,10 +140,72 @@ - +
+ + + {#if chapterId} + + + {#snippet child({ props })} + + {/snippet} + + +
+
+

+ {isBookmarked ? 'Edit bookmark' : 'Bookmark this chapter'} +

+

+ {isBookmarked ? 'Update your note or remove the bookmark.' : 'Add an optional note to remember why you bookmarked this.'} +

+
+ diff --git a/fictionarchive-web-astro/src/lib/graphql/__generated__/graphql.ts b/fictionarchive-web-astro/src/lib/graphql/__generated__/graphql.ts index a003e28..6fa4e20 100644 --- a/fictionarchive-web-astro/src/lib/graphql/__generated__/graphql.ts +++ b/fictionarchive-web-astro/src/lib/graphql/__generated__/graphql.ts @@ -29,6 +29,19 @@ export const ApplyPolicy = { } as const; export type ApplyPolicy = typeof ApplyPolicy[keyof typeof ApplyPolicy]; +export type BookmarkDto = { + chapterId: Scalars['UnsignedInt']['output']; + createdTime: Scalars['Instant']['output']; + description: Maybe; + id: Scalars['Int']['output']; + novelId: Scalars['UnsignedInt']['output']; +}; + +export type BookmarkPayload = { + bookmark: Maybe; + success: Scalars['Boolean']['output']; +}; + export type ChapterDto = { body: Scalars['String']['output']; createdTime: Scalars['Instant']['output']; @@ -266,9 +279,11 @@ export type Mutation = { fetchChapterContents: FetchChapterContentsPayload; importNovel: ImportNovelPayload; inviteUser: InviteUserPayload; + removeBookmark: RemoveBookmarkPayload; runJob: RunJobPayload; scheduleEventJob: ScheduleEventJobPayload; translateText: TranslateTextPayload; + upsertBookmark: UpsertBookmarkPayload; }; @@ -297,6 +312,11 @@ export type MutationInviteUserArgs = { }; +export type MutationRemoveBookmarkArgs = { + input: RemoveBookmarkInput; +}; + + export type MutationRunJobArgs = { input: RunJobInput; }; @@ -311,6 +331,11 @@ export type MutationTranslateTextArgs = { input: TranslateTextInput; }; + +export type MutationUpsertBookmarkArgs = { + input: UpsertBookmarkInput; +}; + export type NovelDto = { author: PersonDto; coverImage: Maybe; @@ -471,6 +496,7 @@ export type PersonDtoSortInput = { }; export type Query = { + bookmarks: Array; chapter: Maybe; currentUser: Maybe; jobs: Array; @@ -480,6 +506,11 @@ export type Query = { }; +export type QueryBookmarksArgs = { + novelId: Scalars['UnsignedInt']['input']; +}; + + export type QueryChapterArgs = { chapterOrder: Scalars['UnsignedInt']['input']; novelId: Scalars['UnsignedInt']['input']; @@ -514,6 +545,17 @@ export type QueryTranslationRequestsArgs = { where?: InputMaybe; }; +export type RemoveBookmarkError = InvalidOperationError; + +export type RemoveBookmarkInput = { + chapterId: Scalars['UnsignedInt']['input']; +}; + +export type RemoveBookmarkPayload = { + bookmarkPayload: Maybe; + errors: Maybe>; +}; + export type RunJobError = JobPersistenceError; export type RunJobInput = { @@ -739,6 +781,19 @@ export type UnsignedIntOperationFilterInputType = { nlte?: InputMaybe; }; +export type UpsertBookmarkError = InvalidOperationError; + +export type UpsertBookmarkInput = { + chapterId: Scalars['UnsignedInt']['input']; + description?: InputMaybe; + novelId: Scalars['UnsignedInt']['input']; +}; + +export type UpsertBookmarkPayload = { + bookmarkPayload: Maybe; + errors: Maybe>; +}; + export type UserDto = { availableInvites: Scalars['Int']['output']; createdTime: Scalars['Instant']['output']; @@ -807,6 +862,27 @@ export type InviteUserMutationVariables = Exact<{ export type InviteUserMutation = { inviteUser: { userDto: { id: any, username: string, email: string } | null, errors: Array<{ message: string }> | null } }; +export type RemoveBookmarkMutationVariables = Exact<{ + input: RemoveBookmarkInput; +}>; + + +export type RemoveBookmarkMutation = { removeBookmark: { bookmarkPayload: { success: boolean } | null, errors: Array<{ message: string }> | null } }; + +export type UpsertBookmarkMutationVariables = Exact<{ + input: UpsertBookmarkInput; +}>; + + +export type UpsertBookmarkMutation = { upsertBookmark: { bookmarkPayload: { success: boolean, bookmark: { id: number, chapterId: any, novelId: any, description: string | null, createdTime: any } | null } | null, errors: Array<{ message: string }> | null } }; + +export type GetBookmarksQueryVariables = Exact<{ + novelId: Scalars['UnsignedInt']['input']; +}>; + + +export type GetBookmarksQuery = { bookmarks: Array<{ id: number, chapterId: any, novelId: any, description: string | null, createdTime: any }> }; + export type GetChapterQueryVariables = Exact<{ novelId: Scalars['UnsignedInt']['input']; volumeOrder: Scalars['UnsignedInt']['input']; @@ -842,6 +918,9 @@ export type GetSettingsPageDataQuery = { currentUser: { id: any, username: strin export const DeleteNovelDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteNovel"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"DeleteNovelInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteNovel"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"boolean"}},{"kind":"Field","name":{"kind":"Name","value":"errors"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Error"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"message"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const ImportNovelDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"ImportNovel"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ImportNovelInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"importNovel"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"novelUpdateRequestedEvent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"novelUrl"}}]}}]}}]}}]} as unknown as DocumentNode; export const InviteUserDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"InviteUser"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"InviteUserInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"inviteUser"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userDto"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"username"}},{"kind":"Field","name":{"kind":"Name","value":"email"}}]}},{"kind":"Field","name":{"kind":"Name","value":"errors"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"InvalidOperationError"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"message"}}]}}]}}]}}]}}]} as unknown as DocumentNode; +export const RemoveBookmarkDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"RemoveBookmark"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"RemoveBookmarkInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"removeBookmark"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"bookmarkPayload"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"success"}}]}},{"kind":"Field","name":{"kind":"Name","value":"errors"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Error"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"message"}}]}}]}}]}}]}}]} as unknown as DocumentNode; +export const UpsertBookmarkDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpsertBookmark"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UpsertBookmarkInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"upsertBookmark"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"bookmarkPayload"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"success"}},{"kind":"Field","name":{"kind":"Name","value":"bookmark"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"chapterId"}},{"kind":"Field","name":{"kind":"Name","value":"novelId"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"createdTime"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"errors"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Error"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"message"}}]}}]}}]}}]}}]} as unknown as DocumentNode; +export const GetBookmarksDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetBookmarks"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"novelId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UnsignedInt"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"bookmarks"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"novelId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"novelId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"chapterId"}},{"kind":"Field","name":{"kind":"Name","value":"novelId"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"createdTime"}}]}}]}}]} as unknown as DocumentNode; export const GetChapterDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetChapter"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"novelId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UnsignedInt"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"volumeOrder"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UnsignedInt"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"chapterOrder"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UnsignedInt"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"chapter"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"novelId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"novelId"}}},{"kind":"Argument","name":{"kind":"Name","value":"volumeOrder"},"value":{"kind":"Variable","name":{"kind":"Name","value":"volumeOrder"}}},{"kind":"Argument","name":{"kind":"Name","value":"chapterOrder"},"value":{"kind":"Variable","name":{"kind":"Name","value":"chapterOrder"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"order"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"body"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"revision"}},{"kind":"Field","name":{"kind":"Name","value":"createdTime"}},{"kind":"Field","name":{"kind":"Name","value":"lastUpdatedTime"}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"newPath"}}]}},{"kind":"Field","name":{"kind":"Name","value":"novelId"}},{"kind":"Field","name":{"kind":"Name","value":"novelName"}},{"kind":"Field","name":{"kind":"Name","value":"volumeId"}},{"kind":"Field","name":{"kind":"Name","value":"volumeName"}},{"kind":"Field","name":{"kind":"Name","value":"volumeOrder"}},{"kind":"Field","name":{"kind":"Name","value":"totalChaptersInVolume"}},{"kind":"Field","name":{"kind":"Name","value":"prevChapterVolumeOrder"}},{"kind":"Field","name":{"kind":"Name","value":"prevChapterOrder"}},{"kind":"Field","name":{"kind":"Name","value":"nextChapterVolumeOrder"}},{"kind":"Field","name":{"kind":"Name","value":"nextChapterOrder"}}]}}]}}]} as unknown as DocumentNode; export const NovelDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Novel"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UnsignedInt"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"novels"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"id"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}]}}]}},{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"1"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"rawLanguage"}},{"kind":"Field","name":{"kind":"Name","value":"rawStatus"}},{"kind":"Field","name":{"kind":"Name","value":"statusOverride"}},{"kind":"Field","name":{"kind":"Name","value":"externalId"}},{"kind":"Field","name":{"kind":"Name","value":"createdTime"}},{"kind":"Field","name":{"kind":"Name","value":"lastUpdatedTime"}},{"kind":"Field","name":{"kind":"Name","value":"author"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"externalUrl"}}]}},{"kind":"Field","name":{"kind":"Name","value":"source"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"url"}}]}},{"kind":"Field","name":{"kind":"Name","value":"coverImage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"newPath"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"tagType"}}]}},{"kind":"Field","name":{"kind":"Name","value":"volumes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"order"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"createdTime"}},{"kind":"Field","name":{"kind":"Name","value":"lastUpdatedTime"}},{"kind":"Field","name":{"kind":"Name","value":"chapters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"order"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"lastUpdatedTime"}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"newPath"}}]}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const NovelsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Novels"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"first"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"after"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"where"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"NovelDtoFilterInput"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"order"}},"type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"NovelDtoSortInput"}}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"novels"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"Variable","name":{"kind":"Name","value":"first"}}},{"kind":"Argument","name":{"kind":"Name","value":"after"},"value":{"kind":"Variable","name":{"kind":"Name","value":"after"}}},{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"Variable","name":{"kind":"Name","value":"where"}}},{"kind":"Argument","name":{"kind":"Name","value":"order"},"value":{"kind":"Variable","name":{"kind":"Name","value":"order"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"cursor"}},{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"coverImage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"newPath"}}]}},{"kind":"Field","name":{"kind":"Name","value":"rawStatus"}},{"kind":"Field","name":{"kind":"Name","value":"lastUpdatedTime"}},{"kind":"Field","name":{"kind":"Name","value":"volumes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"order"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"chapters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"order"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"tagType"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hasNextPage"}},{"kind":"Field","name":{"kind":"Name","value":"endCursor"}}]}}]}}]}}]} as unknown as DocumentNode; diff --git a/fictionarchive-web-astro/src/lib/graphql/mutations/removeBookmark.graphql b/fictionarchive-web-astro/src/lib/graphql/mutations/removeBookmark.graphql new file mode 100644 index 0000000..d46852d --- /dev/null +++ b/fictionarchive-web-astro/src/lib/graphql/mutations/removeBookmark.graphql @@ -0,0 +1,12 @@ +mutation RemoveBookmark($input: RemoveBookmarkInput!) { + removeBookmark(input: $input) { + bookmarkPayload { + success + } + errors { + ... on Error { + message + } + } + } +} diff --git a/fictionarchive-web-astro/src/lib/graphql/mutations/upsertBookmark.graphql b/fictionarchive-web-astro/src/lib/graphql/mutations/upsertBookmark.graphql new file mode 100644 index 0000000..b5da4ba --- /dev/null +++ b/fictionarchive-web-astro/src/lib/graphql/mutations/upsertBookmark.graphql @@ -0,0 +1,19 @@ +mutation UpsertBookmark($input: UpsertBookmarkInput!) { + upsertBookmark(input: $input) { + bookmarkPayload { + success + bookmark { + id + chapterId + novelId + description + createdTime + } + } + errors { + ... on Error { + message + } + } + } +} diff --git a/fictionarchive-web-astro/src/lib/graphql/queries/bookmarks.graphql b/fictionarchive-web-astro/src/lib/graphql/queries/bookmarks.graphql new file mode 100644 index 0000000..543ea9b --- /dev/null +++ b/fictionarchive-web-astro/src/lib/graphql/queries/bookmarks.graphql @@ -0,0 +1,9 @@ +query GetBookmarks($novelId: UnsignedInt!) { + bookmarks(novelId: $novelId) { + id + chapterId + novelId + description + createdTime + } +}