fix: post-purchase crash in CelebrationOverlay (read of null current)
CI/CD / CI - API (dotnet build + engine sim) (push) Successful in 2m22s
CI/CD / CI - Web (tsc + next build) (push) Successful in 1m5s
CI/CD / Deploy - local stack (db + server + web) (push) Has been cancelled

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 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-07 07:48:45 +03:30
parent b0668e6e31
commit 3dd22aee1e
+6 -4
View File
@@ -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 (
<AnimatePresence>
{current && <Card key={current.id} />}
{current && <Card key={current.id} c={current} />}
</AnimatePresence>
);
}
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);