feat(detail): like/dislike buttons on the manga page
Add a tri-state like/dislike toggle beside the bookmark action (signed-in only): click a reaction to set it, the other to switch, or the active one to clear — optimistic with rollback, mirroring the bookmark toggle. Seeds from the new GET /me/reactions/:id in the detail loader (non-critical: any failure degrades to an unset toggle). Unit tests cover the state machine; e2e drives set → switch → clear against the real endpoints. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -20,6 +20,7 @@
|
||||
import { session } from '$lib/session.svelte';
|
||||
import Chip from '$lib/components/Chip.svelte';
|
||||
import MangaCard from '$lib/components/MangaCard.svelte';
|
||||
import ReactionButtons from '$lib/components/ReactionButtons.svelte';
|
||||
import Sheet from '$lib/components/Sheet.svelte';
|
||||
import AddToCollectionModal from '$lib/components/AddToCollectionModal.svelte';
|
||||
import Plus from '@lucide/svelte/icons/plus';
|
||||
@@ -541,6 +542,7 @@
|
||||
>
|
||||
{mangaBookmark ? '★ Bookmarked' : '☆ Bookmark'}
|
||||
</button>
|
||||
<ReactionButtons mangaId={manga.id} initial={data.reaction} />
|
||||
<button
|
||||
type="button"
|
||||
class="action"
|
||||
|
||||
@@ -2,17 +2,22 @@ import { getManga, getSimilarMangas, type MangaCard } from '$lib/api/mangas';
|
||||
import { listChapters } from '$lib/api/chapters';
|
||||
import { listMyBookmarksOrEmpty } from '$lib/api/bookmarks';
|
||||
import { getMyReadProgressForManga } from '$lib/api/read_progress';
|
||||
import { getMyReactionForManga } from '$lib/api/reactions';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const ssr = false;
|
||||
|
||||
export const load: PageLoad = async ({ params }) => {
|
||||
const [manga, chapters, bookmarks, readProgress, similar] = await Promise.all([
|
||||
const [manga, chapters, bookmarks, readProgress, reaction, similar] = await Promise.all([
|
||||
getManga(params.id),
|
||||
listChapters(params.id),
|
||||
listMyBookmarksOrEmpty(),
|
||||
// Null when guest or never-read — page handles both cases.
|
||||
getMyReadProgressForManga(params.id),
|
||||
// Null when guest or not reacted — seeds the like/dislike toggle.
|
||||
// Non-critical: any failure degrades to an unset toggle, never a
|
||||
// broken page.
|
||||
getMyReactionForManga(params.id).catch(() => null),
|
||||
// Recommendations are non-critical: a failure here must not break
|
||||
// the detail page, so fall back to an empty list.
|
||||
getSimilarMangas(params.id).catch(() => [] as MangaCard[])
|
||||
@@ -22,6 +27,7 @@ export const load: PageLoad = async ({ params }) => {
|
||||
chapters: chapters.items,
|
||||
bookmarks: bookmarks.items,
|
||||
readProgress,
|
||||
reaction,
|
||||
similar
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user