|
|
@ -5,6 +5,12 @@ import { useToast } from '../../hooks/use-toast'; |
|
|
|
import { Flame, X } from 'lucide-react'; |
|
|
|
import { Flame, X } from 'lucide-react'; |
|
|
|
import { useOutsideClick } from '../../hooks/use-outside-click'; |
|
|
|
import { useOutsideClick } from '../../hooks/use-outside-click'; |
|
|
|
import { StreakDay } from './StreakDay'; |
|
|
|
import { StreakDay } from './StreakDay'; |
|
|
|
|
|
|
|
import { |
|
|
|
|
|
|
|
navigationDropdownOpen, |
|
|
|
|
|
|
|
roadmapsDropdownOpen, |
|
|
|
|
|
|
|
} from '../../stores/page.ts'; |
|
|
|
|
|
|
|
import { useStore } from '@nanostores/react'; |
|
|
|
|
|
|
|
import { cn } from '../../lib/classname.ts'; |
|
|
|
|
|
|
|
|
|
|
|
type StreakResponse = { |
|
|
|
type StreakResponse = { |
|
|
|
count: number; |
|
|
|
count: number; |
|
|
@ -29,6 +35,15 @@ export function AccountStreak(props: AccountStreakProps) { |
|
|
|
}); |
|
|
|
}); |
|
|
|
const [showDropdown, setShowDropdown] = useState(false); |
|
|
|
const [showDropdown, setShowDropdown] = useState(false); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const $roadmapsDropdownOpen = useStore(roadmapsDropdownOpen); |
|
|
|
|
|
|
|
const $navigationDropdownOpen = useStore(navigationDropdownOpen); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
|
|
|
|
if ($roadmapsDropdownOpen || $navigationDropdownOpen) { |
|
|
|
|
|
|
|
setShowDropdown(false); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}, [$roadmapsDropdownOpen, $navigationDropdownOpen]); |
|
|
|
|
|
|
|
|
|
|
|
const loadAccountStreak = async () => { |
|
|
|
const loadAccountStreak = async () => { |
|
|
|
if (!isLoggedIn()) { |
|
|
|
if (!isLoggedIn()) { |
|
|
|
return; |
|
|
|
return; |
|
|
@ -76,7 +91,12 @@ export function AccountStreak(props: AccountStreakProps) { |
|
|
|
return ( |
|
|
|
return ( |
|
|
|
<div className="relative z-[90] animate-fade-in"> |
|
|
|
<div className="relative z-[90] animate-fade-in"> |
|
|
|
<button |
|
|
|
<button |
|
|
|
className="flex items-center justify-center rounded-lg p-1.5 px-2 text-purple-400 hover:bg-purple-100/10 focus:outline-none" |
|
|
|
className={cn( |
|
|
|
|
|
|
|
'flex items-center justify-center rounded-lg p-1.5 px-2 text-purple-400 hover:bg-purple-100/10 focus:outline-none', |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
'bg-purple-100/10': showDropdown, |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
)} |
|
|
|
onClick={() => setShowDropdown(true)} |
|
|
|
onClick={() => setShowDropdown(true)} |
|
|
|
> |
|
|
|
> |
|
|
|
<Flame className="size-5" /> |
|
|
|
<Flame className="size-5" /> |
|
|
@ -90,15 +110,15 @@ export function AccountStreak(props: AccountStreakProps) { |
|
|
|
ref={dropdownRef} |
|
|
|
ref={dropdownRef} |
|
|
|
className="absolute right-0 top-full z-50 w-[320px] translate-y-1 rounded-lg bg-slate-800 shadow-xl" |
|
|
|
className="absolute right-0 top-full z-50 w-[320px] translate-y-1 rounded-lg bg-slate-800 shadow-xl" |
|
|
|
> |
|
|
|
> |
|
|
|
<div className="px-3 py-2"> |
|
|
|
<div className="px-4 py-2.5"> |
|
|
|
<div className="flex items-center justify-between gap-2"> |
|
|
|
<div className="flex items-center justify-between gap-2 text-sm text-slate-500"> |
|
|
|
<p className="text-sm text-slate-400"> |
|
|
|
<p> |
|
|
|
Current Streak |
|
|
|
Current Streak |
|
|
|
<span className="ml-2 font-medium text-white"> |
|
|
|
<span className="ml-2 font-medium text-white"> |
|
|
|
{accountStreak?.count || 0} |
|
|
|
{accountStreak?.count || 0} |
|
|
|
</span> |
|
|
|
</span> |
|
|
|
</p> |
|
|
|
</p> |
|
|
|
<p className="text-sm text-slate-400"> |
|
|
|
<p> |
|
|
|
Longest Streak |
|
|
|
Longest Streak |
|
|
|
<span className="ml-2 font-medium text-white"> |
|
|
|
<span className="ml-2 font-medium text-white"> |
|
|
|
{accountStreak?.longestCount || 0} |
|
|
|
{accountStreak?.longestCount || 0} |
|
|
@ -106,12 +126,13 @@ export function AccountStreak(props: AccountStreakProps) { |
|
|
|
</p> |
|
|
|
</p> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<div className="mt-8"> |
|
|
|
<div className="mb-5 mt-8"> |
|
|
|
<div className="grid grid-cols-10 gap-1"> |
|
|
|
<div className="grid grid-cols-10 gap-1"> |
|
|
|
{Array.from({ length: totalCircles }).map((_, index) => { |
|
|
|
{Array.from({ length: totalCircles }).map((_, index) => { |
|
|
|
let dayCount, |
|
|
|
let dayCount, |
|
|
|
icon, |
|
|
|
icon, |
|
|
|
isPreviousStreakDay, |
|
|
|
isPreviousStreakDay, |
|
|
|
|
|
|
|
isBrokenStreakDay, |
|
|
|
isCurrentStreakDay, |
|
|
|
isCurrentStreakDay, |
|
|
|
isRemainingStreakDay, |
|
|
|
isRemainingStreakDay, |
|
|
|
isToday; |
|
|
|
isToday; |
|
|
@ -120,9 +141,10 @@ export function AccountStreak(props: AccountStreakProps) { |
|
|
|
// Previous streak days
|
|
|
|
// Previous streak days
|
|
|
|
dayCount = previousCount - leftCircleCount + index + 1 + 1; |
|
|
|
dayCount = previousCount - leftCircleCount + index + 1 + 1; |
|
|
|
isPreviousStreakDay = true; |
|
|
|
isPreviousStreakDay = true; |
|
|
|
icon = |
|
|
|
isBrokenStreakDay = index === leftCircleCount - 1; |
|
|
|
index === leftCircleCount - 1 ? ( |
|
|
|
|
|
|
|
<X className="size-3.5 text-white" /> |
|
|
|
icon = isBrokenStreakDay ? ( |
|
|
|
|
|
|
|
<X className="opacit size-3.5 text-white" /> |
|
|
|
) : ( |
|
|
|
) : ( |
|
|
|
<Flame className="size-3.5 text-white" /> |
|
|
|
<Flame className="size-3.5 text-white" /> |
|
|
|
); |
|
|
|
); |
|
|
@ -147,6 +169,7 @@ export function AccountStreak(props: AccountStreakProps) { |
|
|
|
key={`streak-${index}`} |
|
|
|
key={`streak-${index}`} |
|
|
|
dayCount={dayCount} |
|
|
|
dayCount={dayCount} |
|
|
|
icon={icon} |
|
|
|
icon={icon} |
|
|
|
|
|
|
|
isBrokenStreakDay={isBrokenStreakDay} |
|
|
|
isPreviousStreakDay={isPreviousStreakDay} |
|
|
|
isPreviousStreakDay={isPreviousStreakDay} |
|
|
|
isCurrentStreakDay={isCurrentStreakDay} |
|
|
|
isCurrentStreakDay={isCurrentStreakDay} |
|
|
|
isRemainingStreakDay={isRemainingStreakDay} |
|
|
|
isRemainingStreakDay={isRemainingStreakDay} |
|
|
@ -156,6 +179,10 @@ export function AccountStreak(props: AccountStreakProps) { |
|
|
|
})} |
|
|
|
})} |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<p className="text-center text-xs text-slate-500"> |
|
|
|
|
|
|
|
Visit every day to keep your streak alive! |
|
|
|
|
|
|
|
</p> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
)} |
|
|
|
)} |
|
|
|