Files
FictionArchive/FictionArchive.Service.NovelService/GraphQL/Query.cs
2025-12-29 20:27:04 -05:00

264 lines
11 KiB
C#

using FictionArchive.Common.Enums;
using FictionArchive.Service.NovelService.Models.DTOs;
using FictionArchive.Service.NovelService.Services;
using HotChocolate.Authorization;
using HotChocolate.Data;
using HotChocolate.Types;
namespace FictionArchive.Service.NovelService.GraphQL;
public class Query
{
[Authorize]
[UsePaging]
[UseProjection]
[UseFiltering]
[UseSorting]
public IQueryable<NovelDto> GetNovels(
NovelServiceDbContext dbContext,
Language preferredLanguage = Language.En)
{
return dbContext.Novels.Select(novel => new NovelDto
{
Id = novel.Id,
CreatedTime = novel.CreatedTime,
LastUpdatedTime = novel.LastUpdatedTime,
Url = novel.Url,
RawLanguage = novel.RawLanguage,
RawStatus = novel.RawStatus,
StatusOverride = novel.StatusOverride,
ExternalId = novel.ExternalId,
Name = novel.Name.Texts
.Where(t => t.Language == preferredLanguage)
.Select(t => t.Text)
.FirstOrDefault()
?? novel.Name.Texts.Select(t => t.Text).FirstOrDefault()
?? "",
Description = novel.Description.Texts
.Where(t => t.Language == preferredLanguage)
.Select(t => t.Text)
.FirstOrDefault()
?? novel.Description.Texts.Select(t => t.Text).FirstOrDefault()
?? "",
Author = new PersonDto
{
Id = novel.Author.Id,
CreatedTime = novel.Author.CreatedTime,
LastUpdatedTime = novel.Author.LastUpdatedTime,
Name = novel.Author.Name.Texts
.Where(t => t.Language == preferredLanguage)
.Select(t => t.Text)
.FirstOrDefault()
?? novel.Author.Name.Texts.Select(t => t.Text).FirstOrDefault()
?? "",
ExternalUrl = novel.Author.ExternalUrl
},
Source = new SourceDto
{
Id = novel.Source.Id,
CreatedTime = novel.Source.CreatedTime,
LastUpdatedTime = novel.Source.LastUpdatedTime,
Name = novel.Source.Name,
Key = novel.Source.Key,
Url = novel.Source.Url
},
CoverImage = novel.CoverImage != null
? new ImageDto
{
Id = novel.CoverImage.Id,
CreatedTime = novel.CoverImage.CreatedTime,
LastUpdatedTime = novel.CoverImage.LastUpdatedTime,
NewPath = novel.CoverImage.NewPath
}
: null,
Volumes = novel.Volumes.OrderBy(v => v.Order).Select(volume => new VolumeDto
{
Id = volume.Id,
CreatedTime = volume.CreatedTime,
LastUpdatedTime = volume.LastUpdatedTime,
Order = volume.Order,
Name = volume.Name.Texts
.Where(t => t.Language == preferredLanguage)
.Select(t => t.Text)
.FirstOrDefault()
?? volume.Name.Texts.Select(t => t.Text).FirstOrDefault()
?? "",
Chapters = volume.Chapters.OrderBy(c => c.Order).Select(chapter => new ChapterDto
{
Id = chapter.Id,
CreatedTime = chapter.CreatedTime,
LastUpdatedTime = chapter.LastUpdatedTime,
Revision = chapter.Revision,
Order = chapter.Order,
Url = chapter.Url,
Name = chapter.Name.Texts
.Where(t => t.Language == preferredLanguage)
.Select(t => t.Text)
.FirstOrDefault()
?? chapter.Name.Texts.Select(t => t.Text).FirstOrDefault()
?? "",
Body = chapter.Body.Texts
.Where(t => t.Language == preferredLanguage)
.Select(t => t.Text)
.FirstOrDefault()
?? chapter.Body.Texts.Select(t => t.Text).FirstOrDefault()
?? "",
Images = chapter.Images.Select(image => new ImageDto
{
Id = image.Id,
CreatedTime = image.CreatedTime,
LastUpdatedTime = image.LastUpdatedTime,
NewPath = image.NewPath
}).ToList()
}).ToList()
}).ToList(),
Tags = novel.Tags.Select(tag => new NovelTagDto
{
Id = tag.Id,
CreatedTime = tag.CreatedTime,
LastUpdatedTime = tag.LastUpdatedTime,
Key = tag.Key,
TagType = tag.TagType,
DisplayName = tag.DisplayName.Texts
.Where(t => t.Language == preferredLanguage)
.Select(t => t.Text)
.FirstOrDefault()
?? tag.DisplayName.Texts.Select(t => t.Text).FirstOrDefault()
?? "",
Source = tag.Source != null
? new SourceDto
{
Id = tag.Source.Id,
CreatedTime = tag.Source.CreatedTime,
LastUpdatedTime = tag.Source.LastUpdatedTime,
Name = tag.Source.Name,
Key = tag.Source.Key,
Url = tag.Source.Url
}
: null
}).ToList()
});
}
[Authorize]
[UseFirstOrDefault]
[UseProjection]
public IQueryable<ChapterReaderDto> GetChapter(
NovelServiceDbContext dbContext,
uint novelId,
uint volumeId,
uint chapterOrder,
Language preferredLanguage = Language.En)
{
return dbContext.Chapters
.Where(c => c.Volume.Novel.Id == novelId && c.Volume.Id == volumeId && c.Order == chapterOrder)
.Select(chapter => new ChapterReaderDto
{
Id = chapter.Id,
CreatedTime = chapter.CreatedTime,
LastUpdatedTime = chapter.LastUpdatedTime,
Revision = chapter.Revision,
Order = chapter.Order,
Url = chapter.Url,
Name = chapter.Name.Texts
.Where(t => t.Language == preferredLanguage)
.Select(t => t.Text)
.FirstOrDefault()
?? chapter.Name.Texts.Select(t => t.Text).FirstOrDefault()
?? "",
Body = chapter.Body.Texts
.Where(t => t.Language == preferredLanguage)
.Select(t => t.Text)
.FirstOrDefault()
?? chapter.Body.Texts.Select(t => t.Text).FirstOrDefault()
?? "",
Images = chapter.Images.Select(image => new ImageDto
{
Id = image.Id,
CreatedTime = image.CreatedTime,
LastUpdatedTime = image.LastUpdatedTime,
NewPath = image.NewPath
}).ToList(),
NovelId = chapter.Volume.Novel.Id,
NovelName = chapter.Volume.Novel.Name.Texts
.Where(t => t.Language == preferredLanguage)
.Select(t => t.Text)
.FirstOrDefault()
?? chapter.Volume.Novel.Name.Texts.Select(t => t.Text).FirstOrDefault()
?? "",
// Volume context
VolumeId = chapter.Volume.Id,
VolumeName = chapter.Volume.Name.Texts
.Where(t => t.Language == preferredLanguage)
.Select(t => t.Text)
.FirstOrDefault()
?? chapter.Volume.Name.Texts.Select(t => t.Text).FirstOrDefault()
?? "",
VolumeOrder = chapter.Volume.Order,
TotalChaptersInVolume = chapter.Volume.Chapters.Count,
// Previous chapter: first try same volume, then last chapter of previous volume
PrevChapterVolumeId = chapter.Volume.Chapters
.Where(c => c.Order < chapterOrder)
.OrderByDescending(c => c.Order)
.Select(c => (uint?)chapter.Volume.Id)
.FirstOrDefault()
?? chapter.Volume.Novel.Volumes
.Where(v => v.Order < chapter.Volume.Order)
.OrderByDescending(v => v.Order)
.SelectMany(v => v.Chapters.OrderByDescending(c => c.Order).Take(1))
.Select(c => (uint?)c.Volume.Id)
.FirstOrDefault(),
PrevChapterOrder = chapter.Volume.Chapters
.Where(c => c.Order < chapterOrder)
.OrderByDescending(c => c.Order)
.Select(c => (uint?)c.Order)
.FirstOrDefault()
?? chapter.Volume.Novel.Volumes
.Where(v => v.Order < chapter.Volume.Order)
.OrderByDescending(v => v.Order)
.SelectMany(v => v.Chapters.OrderByDescending(c => c.Order).Take(1))
.Select(c => (uint?)c.Order)
.FirstOrDefault(),
// Next chapter: first try same volume, then first chapter of next volume
NextChapterVolumeId = chapter.Volume.Chapters
.Where(c => c.Order > chapterOrder)
.OrderBy(c => c.Order)
.Select(c => (uint?)chapter.Volume.Id)
.FirstOrDefault()
?? chapter.Volume.Novel.Volumes
.Where(v => v.Order > chapter.Volume.Order)
.OrderBy(v => v.Order)
.SelectMany(v => v.Chapters.OrderBy(c => c.Order).Take(1))
.Select(c => (uint?)c.Volume.Id)
.FirstOrDefault(),
NextChapterOrder = chapter.Volume.Chapters
.Where(c => c.Order > chapterOrder)
.OrderBy(c => c.Order)
.Select(c => (uint?)c.Order)
.FirstOrDefault()
?? chapter.Volume.Novel.Volumes
.Where(v => v.Order > chapter.Volume.Order)
.OrderBy(v => v.Order)
.SelectMany(v => v.Chapters.OrderBy(c => c.Order).Take(1))
.Select(c => (uint?)c.Order)
.FirstOrDefault()
});
}
}