fix(detail): resync reaction toggle across manga navigation
All checks were successful
deploy / test-backend (push) Successful in 36m1s
deploy / test-frontend (push) Successful in 10m41s
deploy / build-and-push (push) Successful in 11m41s
deploy / deploy (push) Successful in 13s

The detail page component is reused across /manga/A -> /manga/B
navigations (clicking a similar or recommendation card), so
ReactionButtons' locally-held `current` state kept the previous manga's
reaction until interacted with — a visibly wrong thumb on the new page.
Re-seed `current` from the loader-supplied prop via an effect keyed on
mangaId. Also fetch the homepage's two independent /me/* shelves
concurrently (Promise.allSettled) instead of in series.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-07-05 18:17:18 +02:00
parent 19db66a845
commit ef8d226ba6
7 changed files with 46 additions and 18 deletions

View File

@@ -18,6 +18,16 @@
let current = $state<Reaction | null>(initial);
let busy = $state(false);
// The detail page reuses this component across manga -> manga navigation
// (similar / recommendation cards), so `current` must re-seed when the
// loader hands us a new manga's reaction. `mangaId` is referenced so the
// effect also re-runs when navigating between two never-reacted mangas
// (both `initial === null`).
$effect(() => {
mangaId;
current = initial;
});
// Click the active reaction to clear it; click the other to switch.
async function apply(next: Reaction) {
if (busy) return;

View File

@@ -51,6 +51,24 @@ describe('ReactionButtons', () => {
expect(pressed('reaction-like')).toBe('false');
});
it('resyncs when navigated to a different manga', async () => {
// The detail page component is reused across /manga/A -> /manga/B
// navigations (e.g. clicking a similar or recommendation card), so
// new props must overwrite the locally-held reaction state.
const { rerender } = render(ReactionButtons, {
props: { mangaId: 'm1', initial: 'like' }
});
expect(pressed('reaction-like')).toBe('true');
await rerender({ mangaId: 'm2', initial: 'dislike' });
expect(pressed('reaction-dislike')).toBe('true');
expect(pressed('reaction-like')).toBe('false');
await rerender({ mangaId: 'm3', initial: null });
expect(pressed('reaction-like')).toBe('false');
expect(pressed('reaction-dislike')).toBe('false');
});
it('rolls back on failure', async () => {
setReaction.mockRejectedValue(new Error('boom'));
render(ReactionButtons, { props: { mangaId: 'm1', initial: null } });