diff --git a/src/components/StreakTracker.tsx b/src/components/StreakTracker.tsx index 03ef1aa1..a23896a5 100644 --- a/src/components/StreakTracker.tsx +++ b/src/components/StreakTracker.tsx @@ -8,6 +8,8 @@ import { toast } from "sonner"; import { toPng } from "html-to-image"; const STREAK_MILESTONES = [7, 30, 50, 100, 200, 365]; +const STREAK_FREEZE_TOOLTIP = + "A streak freeze protects your streak for one missed day. You can only use one freeze at a time."; interface StreakData { current: number; @@ -46,6 +48,7 @@ export default function StreakTracker() { const [cancelling, setCancelling] = useState(false); const [confirmCancel, setConfirmCancel] = useState(false); const [isDownloading, setIsDownloading] = useState(false); + const [applyingFreeze, setApplyingFreeze] = useState(false); const containerRef = useRef(null); @@ -189,6 +192,38 @@ export default function StreakTracker() { } } + async function handleApplyFreeze() { + setApplyingFreeze(true); + + try { + const res = await fetch("/api/streak/freeze", { method: "POST" }); + if (!res.ok) throw new Error("Failed to apply freeze"); + + const streakUrl = + selectedAccount !== null + ? `/api/metrics/streak?accountId=${encodeURIComponent(selectedAccount)}` + : "/api/metrics/streak"; + const [streakRes, freezeRes] = await Promise.all([ + fetch(streakUrl), + fetch("/api/streak/freeze"), + ]); + const [streakData, freezeData] = await Promise.all([ + streakRes.json() as Promise, + freezeRes.json() as Promise, + ]); + + setData(streakData); + setFreeze(freezeData); + setFreezeDates(streakData.freezeDates || []); + toast.success("Streak freeze activated."); + } catch { + fetchFreeze(); + toast.error("Couldn't activate your streak freeze. Please try again."); + } finally { + setApplyingFreeze(false); + } + } + if (loading) { return (
@@ -539,6 +574,32 @@ export default function StreakTracker() {

)} + {!freezeLoading && !freeze?.hasFreeze && ( +
+ Protect your streak +
+ + +
+ )} + {!freezeLoading && freeze?.hasFreeze && (
✓ Freeze active today