import { useEffect, useRef, useState } from 'react'; import { isLoggedIn } from '../../lib/jwt'; import { httpGet } from '../../lib/http'; import { useToast } from '../../hooks/use-toast'; import { Zap, ZapOff } from 'lucide-react'; import { useOutsideClick } from '../../hooks/use-outside-click'; import { StreakDay } from './StreakDay'; import { navigationDropdownOpen, roadmapsDropdownOpen, } from '../../stores/page.ts'; import { useStore } from '@nanostores/react'; import { cn } from '../../lib/classname.ts'; import { $accountStreak, type StreakResponse } from '../../stores/streak.ts'; import { InviteFriends } from './InviteFriends.tsx'; type AccountStreakProps = {}; export function AccountStreak(props: AccountStreakProps) { const toast = useToast(); const dropdownRef = useRef(null); const [isLoading, setIsLoading] = useState(true); const accountStreak = useStore($accountStreak); const [showDropdown, setShowDropdown] = useState(false); const $roadmapsDropdownOpen = useStore(roadmapsDropdownOpen); const $navigationDropdownOpen = useStore(navigationDropdownOpen); useEffect(() => { if ($roadmapsDropdownOpen || $navigationDropdownOpen) { setShowDropdown(false); } }, [$roadmapsDropdownOpen, $navigationDropdownOpen]); const loadAccountStreak = async () => { if (!isLoggedIn()) { return; } if (accountStreak) { setIsLoading(false); return; } setIsLoading(true); const { response, error } = await httpGet( `${import.meta.env.PUBLIC_API_URL}/v1-streak`, ); if (error || !response) { toast.error(error?.message || 'Failed to load account streak'); setIsLoading(false); return; } $accountStreak.set(response); setIsLoading(false); }; useOutsideClick(dropdownRef, () => { setShowDropdown(false); }); useEffect(() => { loadAccountStreak().finally(() => {}); }, []); if (!isLoggedIn() || isLoading) { return null; } let { count: currentCount = 0 } = accountStreak || {}; const previousCount = accountStreak?.previousCount || accountStreak?.count || 0; // Adding one to show the current day const currentCircleCount = Math.min(currentCount, 5) + 1; // Adding one day to show the streak they broke const leftCircleCount = Math.min(5 - currentCircleCount, previousCount) + 1; // In the maximum case, we will show 10 circles const remainingCount = Math.max(0, 10 - leftCircleCount - currentCircleCount); const totalCircles = leftCircleCount + currentCircleCount + remainingCount; return (
{showDropdown && (

Current Streak {accountStreak?.count || 0}

Longest Streak {accountStreak?.longestCount || 0}

{Array.from({ length: totalCircles }).map((_, index) => { let dayCount, icon, isPreviousStreakDay, isBrokenStreakDay, isCurrentStreakDay, isRemainingStreakDay, isToday; if (index < leftCircleCount) { // Previous streak days dayCount = previousCount - leftCircleCount + index + 1 + 1; isPreviousStreakDay = true; isBrokenStreakDay = index === leftCircleCount - 1; icon = isBrokenStreakDay ? ( ) : ( ); } else if (index < leftCircleCount + currentCircleCount) { // Current streak days const currentIndex = index - leftCircleCount; dayCount = currentCount - currentCircleCount + currentIndex + 1 + 1; isCurrentStreakDay = true; isToday = currentIndex === currentCircleCount - 1; icon = ; } else { // Remaining streak days const remainingIndex = index - leftCircleCount - currentCircleCount; dayCount = currentCount + remainingIndex + 1 + 1; isRemainingStreakDay = true; } return ( ); })}

Visit every day to keep your streak going!

)}
); }