[FA-27] Still need to test events
This commit is contained in:
@@ -54,6 +54,7 @@
|
||||
} from '$lib/components/ui/tooltip';
|
||||
import { formatRelativeTime, formatAbsoluteTime } from '$lib/utils/time';
|
||||
import { sanitizeHtml } from '$lib/utils/sanitize';
|
||||
import ChapterBookmarkButton from './ChapterBookmarkButton.svelte';
|
||||
// Direct imports for faster builds
|
||||
import ArrowLeft from '@lucide/svelte/icons/arrow-left';
|
||||
import ExternalLink from '@lucide/svelte/icons/external-link';
|
||||
@@ -144,6 +145,32 @@
|
||||
)
|
||||
);
|
||||
|
||||
// Bookmark lookup by chapterId for quick access in chapter list
|
||||
const bookmarkLookup = $derived(
|
||||
new Map(bookmarks.map((b) => [b.chapterId, b]))
|
||||
);
|
||||
|
||||
function handleChapterBookmarkChange(chapterId: number, isBookmarked: boolean, description?: string | null) {
|
||||
if (isBookmarked) {
|
||||
// Add or update bookmark in local state
|
||||
const existingIndex = bookmarks.findIndex((b) => b.chapterId === chapterId);
|
||||
const newBookmark = {
|
||||
id: existingIndex >= 0 ? bookmarks[existingIndex].id : -1, // temp id
|
||||
chapterId,
|
||||
description: description ?? null,
|
||||
createdTime: new Date().toISOString()
|
||||
};
|
||||
if (existingIndex >= 0) {
|
||||
bookmarks[existingIndex] = newBookmark;
|
||||
} else {
|
||||
bookmarks = [...bookmarks, newBookmark];
|
||||
}
|
||||
} else {
|
||||
// Remove bookmark from local state
|
||||
bookmarks = bookmarks.filter((b) => b.chapterId !== chapterId);
|
||||
}
|
||||
}
|
||||
|
||||
const chapterCount = $derived(
|
||||
sortedVolumes.reduce((sum, v) => sum + v.chapters.length, 0)
|
||||
);
|
||||
@@ -200,9 +227,9 @@
|
||||
}
|
||||
});
|
||||
|
||||
// Load bookmarks when tab is first activated
|
||||
// Load bookmarks when novel is loaded (for count display)
|
||||
$effect(() => {
|
||||
if (activeTab === 'bookmarks' && !bookmarksLoaded && novelId) {
|
||||
if (novel && !bookmarksLoaded && novelId) {
|
||||
fetchBookmarks();
|
||||
}
|
||||
});
|
||||
@@ -591,24 +618,36 @@
|
||||
<div class="max-h-96 overflow-y-auto -mx-2">
|
||||
{#each singleVolumeChapters as chapter (chapter.id)}
|
||||
{@const chapterDate = chapter.lastUpdatedTime ? new Date(chapter.lastUpdatedTime) : null}
|
||||
<a
|
||||
href="/novels/{novelId}/volumes/{sortedVolumes[0]?.order}/chapters/{chapter.order}"
|
||||
class="flex items-center justify-between px-3 py-2.5 hover:bg-muted/50 rounded-md transition-colors group"
|
||||
>
|
||||
<div class="flex items-center gap-3 min-w-0">
|
||||
{@const chapterBookmark = bookmarkLookup.get(chapter.id)}
|
||||
<div class="flex items-center px-3 py-2.5 hover:bg-muted/50 rounded-md transition-colors group">
|
||||
<a
|
||||
href="/novels/{novelId}/volumes/{sortedVolumes[0]?.order}/chapters/{chapter.order}"
|
||||
class="flex items-center gap-3 min-w-0 flex-1"
|
||||
>
|
||||
<span class="text-muted-foreground text-sm font-medium shrink-0 w-14">
|
||||
Ch. {chapter.order}
|
||||
</span>
|
||||
<span class="text-sm truncate group-hover:text-primary transition-colors">
|
||||
{chapter.name}
|
||||
</span>
|
||||
</a>
|
||||
<div class="flex items-center gap-2 shrink-0 ml-2">
|
||||
{#if chapterDate}
|
||||
<span class="text-xs text-muted-foreground/70">
|
||||
{formatRelativeTime(chapterDate)}
|
||||
</span>
|
||||
{/if}
|
||||
{#if novelId}
|
||||
<ChapterBookmarkButton
|
||||
novelId={parseInt(novelId, 10)}
|
||||
chapterId={chapter.id}
|
||||
isBookmarked={!!chapterBookmark}
|
||||
bookmarkDescription={chapterBookmark?.description}
|
||||
onBookmarkChange={(isBookmarked, description) => handleChapterBookmarkChange(chapter.id, isBookmarked, description)}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{#if chapterDate}
|
||||
<span class="text-xs text-muted-foreground/70 shrink-0 ml-2">
|
||||
{formatRelativeTime(chapterDate)}
|
||||
</span>
|
||||
{/if}
|
||||
</a>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
@@ -630,24 +669,36 @@
|
||||
<div class="space-y-0.5">
|
||||
{#each volumeChapters as chapter (chapter.id)}
|
||||
{@const chapterDate = chapter.lastUpdatedTime ? new Date(chapter.lastUpdatedTime) : null}
|
||||
<a
|
||||
href="/novels/{novelId}/volumes/{volume.order}/chapters/{chapter.order}"
|
||||
class="flex items-center justify-between px-3 py-2.5 hover:bg-muted/50 rounded-md transition-colors group"
|
||||
>
|
||||
<div class="flex items-center gap-3 min-w-0">
|
||||
{@const chapterBookmark = bookmarkLookup.get(chapter.id)}
|
||||
<div class="flex items-center px-3 py-2.5 hover:bg-muted/50 rounded-md transition-colors group">
|
||||
<a
|
||||
href="/novels/{novelId}/volumes/{volume.order}/chapters/{chapter.order}"
|
||||
class="flex items-center gap-3 min-w-0 flex-1"
|
||||
>
|
||||
<span class="text-muted-foreground text-sm font-medium shrink-0 w-14">
|
||||
Ch. {chapter.order}
|
||||
</span>
|
||||
<span class="text-sm truncate group-hover:text-primary transition-colors">
|
||||
{chapter.name}
|
||||
</span>
|
||||
</a>
|
||||
<div class="flex items-center gap-2 shrink-0 ml-2">
|
||||
{#if chapterDate}
|
||||
<span class="text-xs text-muted-foreground/70">
|
||||
{formatRelativeTime(chapterDate)}
|
||||
</span>
|
||||
{/if}
|
||||
{#if novelId}
|
||||
<ChapterBookmarkButton
|
||||
novelId={parseInt(novelId, 10)}
|
||||
chapterId={chapter.id}
|
||||
isBookmarked={!!chapterBookmark}
|
||||
bookmarkDescription={chapterBookmark?.description}
|
||||
onBookmarkChange={(isBookmarked, description) => handleChapterBookmarkChange(chapter.id, isBookmarked, description)}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{#if chapterDate}
|
||||
<span class="text-xs text-muted-foreground/70 shrink-0 ml-2">
|
||||
{formatRelativeTime(chapterDate)}
|
||||
</span>
|
||||
{/if}
|
||||
</a>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</AccordionContent>
|
||||
|
||||
Reference in New Issue
Block a user