[FA-misc] Various UI updates
All checks were successful
CI / build-backend (pull_request) Successful in 1m38s
CI / build-frontend (pull_request) Successful in 37s

This commit is contained in:
gamer147
2025-12-10 20:37:30 -05:00
parent 45afb57df5
commit f0ea71e00e
13 changed files with 298 additions and 28 deletions

View File

@@ -1,5 +1,7 @@
<script lang="ts" module>
import type { NovelQuery, NovelStatus, Language } from '$lib/graphql/__generated__/graphql';
import { TagType } from '$lib/graphql/__generated__/graphql';
import { SystemTags } from '$lib/constants/systemTags';
export type NovelNode = NonNullable<NonNullable<NovelQuery['novels']>['nodes']>[number];
@@ -80,6 +82,8 @@
};
let viewerOpen = $state(false);
let viewerIndex = $state(0);
let activeTab = $state('chapters');
let galleryLoaded = $state(false);
const DESCRIPTION_PREVIEW_LENGTH = 300;
@@ -110,6 +114,10 @@
const chapterCount = $derived(novel?.chapters?.length ?? 0);
// Filter out system tags for display, check for NSFW
const displayTags = $derived(novel?.tags?.filter((tag) => tag.tagType !== TagType.System) ?? []);
const isNsfw = $derived(novel?.tags?.some((tag) => tag.key === SystemTags.Nsfw) ?? false);
const canRefresh = $derived(() => {
if (status === 'COMPLETED') return false;
if (!lastUpdated) return true;
@@ -146,6 +154,14 @@
});
const currentImage = $derived(galleryImages[viewerIndex]);
const imageCount = $derived(galleryImages.length);
// Load gallery images when tab is first activated
$effect(() => {
if (activeTab === 'gallery' && !galleryLoaded) {
galleryLoaded = true;
}
});
// Image viewer functions
function openImageViewer(index: number) {
@@ -321,6 +337,9 @@
<!-- Badges -->
<div class="flex flex-wrap gap-2 items-center">
<Badge class={statusColor}>{statusLabel}</Badge>
{#if isNsfw}
<Badge class="bg-red-600 text-white">NSFW</Badge>
{/if}
<Badge variant="outline">{languageLabel}</Badge>
{#if $isAuthenticated}
<TooltipProvider>
@@ -390,9 +409,9 @@
</div>
<!-- Tags -->
{#if novel.tags && novel.tags.length > 0}
{#if displayTags.length > 0}
<div class="flex flex-wrap gap-1.5 pt-1">
{#each novel.tags as tag (tag.key)}
{#each displayTags as tag (tag.key)}
<Badge
variant="secondary"
href="/novels?tags={tag.key}"
@@ -435,20 +454,20 @@
<!-- Tabbed Content -->
<Card>
<Tabs value="chapters" class="w-full">
<Tabs bind:value={activeTab} class="w-full">
<CardHeader class="pb-0">
<TabsList class="grid w-full grid-cols-3 bg-muted/50 p-1 rounded-lg">
<TabsTrigger
value="chapters"
class="rounded-md data-[state=active]:bg-background data-[state=active]:shadow-sm px-3 py-1.5 text-sm font-medium transition-all"
>
Chapters
Chapters ({chapterCount})
</TabsTrigger>
<TabsTrigger
value="gallery"
class="rounded-md data-[state=active]:bg-background data-[state=active]:shadow-sm px-3 py-1.5 text-sm font-medium transition-all"
>
Gallery
Gallery ({imageCount})
</TabsTrigger>
<TabsTrigger
value="bookmarks"
@@ -498,7 +517,7 @@
<p class="text-muted-foreground text-sm py-4 text-center">
No images available.
</p>
{:else}
{:else if galleryLoaded}
<div class="grid grid-cols-3 sm:grid-cols-4 md:grid-cols-5 gap-2">
{#each galleryImages as image, index (image.src)}
<button
@@ -506,13 +525,20 @@
onclick={() => openImageViewer(index)}
class="relative aspect-square overflow-hidden rounded-md bg-muted/50 hover:ring-2 ring-primary transition-all"
>
<img src={image.src} alt={image.alt} class="h-full w-full object-cover" />
<img src={image.src} alt={image.alt} class="h-full w-full object-cover" loading="lazy" />
{#if image.isCover}
<Badge class="absolute top-1 left-1 text-xs">Cover</Badge>
{/if}
</button>
{/each}
</div>
{:else}
<div class="flex items-center justify-center py-8">
<div
class="border-primary h-8 w-8 animate-spin rounded-full border-2 border-t-transparent"
aria-label="Loading gallery"
></div>
</div>
{/if}
</TabsContent>