From 3dd22aee1e78c510413c9083eb9ac1133600fd6d Mon Sep 17 00:00:00 2001 From: "soroush.asadi" Date: Sun, 7 Jun 2026 07:48:45 +0330 Subject: [PATCH] fix: post-purchase crash in CelebrationOverlay (read of null current) Card read useCelebrationStore(s=>s.current)! but AnimatePresence keeps it mounted through the exit animation; after dismiss() sets current=null it re-rendered and threw "Cannot read properties of null (reading 'levelAfter')", crashing the page after every purchase/XP/daily celebration. Pass the celebration as a prop so the exiting card keeps its data. Verified: tsc + next build clean; web rebuilt on :1500. Co-Authored-By: Claude Opus 4.8 --- src/components/online/CelebrationOverlay.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/online/CelebrationOverlay.tsx b/src/components/online/CelebrationOverlay.tsx index 8e3b033..e3e8978 100644 --- a/src/components/online/CelebrationOverlay.tsx +++ b/src/components/online/CelebrationOverlay.tsx @@ -3,7 +3,7 @@ import { AnimatePresence, motion } from "framer-motion"; import { Coins, Sparkles, Star, TrendingUp } from "lucide-react"; import { useEffect, useRef, useState } from "react"; -import { useCelebrationStore } from "@/lib/celebration-store"; +import { useCelebrationStore, type Celebration } from "@/lib/celebration-store"; import { useI18n } from "@/lib/i18n"; import { sound } from "@/lib/sound"; import { stickerPackForAchievement } from "@/lib/online/gamification"; @@ -45,15 +45,17 @@ export function CelebrationOverlay() { return ( - {current && } + {current && } ); } -function Card() { +// Takes the celebration as a PROP (not from the store) so it keeps its data while +// AnimatePresence runs the exit animation after dismiss() sets `current` to null. +function Card({ c }: { c: Celebration }) { const { t, locale } = useI18n(); - const current = useCelebrationStore((s) => s.current)!; const dismiss = useCelebrationStore((s) => s.dismiss); + const current = c; const leveled = (current.levelAfter ?? 0) > (current.levelBefore ?? 0); const xp = useCountUp(current.xpGained ?? 0, 900, current.variant === "xp"); const coins = useCountUp(current.coins ?? 0, 1000, (current.coins ?? 0) > 0);