import { useEffect, useMemo, useRef, useState } from 'react'; import { useKeydown } from '../../hooks/use-keydown'; import { useOutsideClick } from '../../hooks/use-outside-click'; import { isLoggedIn } from '../../lib/jwt'; import { getTopicStatus, refreshProgressCounters, renderTopicProgress, updateResourceProgress, } from '../../lib/resource-progress'; import type { ResourceProgressType, ResourceType, } from '../../lib/resource-progress'; import { showLoginPopup } from '../../lib/popup'; import { useToast } from '../../hooks/use-toast'; import { Spinner } from '../ReactIcons/Spinner'; import { ChevronDown } from 'lucide-react'; type TopicProgressButtonProps = { topicId: string; resourceId: string; resourceType: ResourceType; onClose: () => void; }; const statusColors: Record = { done: 'bg-green-500', learning: 'bg-yellow-500', pending: 'bg-gray-300', skipped: 'bg-black', removed: '', }; export function TopicProgressButton(props: TopicProgressButtonProps) { const { topicId, resourceId, resourceType, onClose } = props; const toast = useToast(); const [isUpdatingProgress, setIsUpdatingProgress] = useState(true); const [progress, setProgress] = useState('pending'); const [showChangeStatus, setShowChangeStatus] = useState(false); const changeStatusRef = useRef(null); useOutsideClick(changeStatusRef, () => { setShowChangeStatus(false); }); const isGuest = useMemo(() => !isLoggedIn(), []); useEffect(() => { if (!topicId || !resourceId || !resourceType) { return; } setIsUpdatingProgress(true); getTopicStatus({ topicId, resourceId, resourceType }) .then((status) => { setIsUpdatingProgress(false); setProgress(status); }) .catch(console.error); }, [topicId, resourceId, resourceType]); // Mark as done useKeydown( 'd', () => { if (progress === 'done') { onClose(); return; } handleUpdateResourceProgress('done'); }, [progress], ); // Mark as learning useKeydown( 'l', () => { if (progress === 'learning') { return; } handleUpdateResourceProgress('learning'); }, [progress], ); // Mark as learning useKeydown( 's', () => { if (progress === 'skipped') { onClose(); return; } handleUpdateResourceProgress('skipped'); }, [progress], ); // Mark as pending useKeydown( 'r', () => { if (progress === 'pending') { onClose(); return; } handleUpdateResourceProgress('pending'); }, [progress], ); const handleUpdateResourceProgress = (progress: ResourceProgressType) => { if (isGuest) { onClose(); showLoginPopup(); return; } setIsUpdatingProgress(true); updateResourceProgress( { topicId, resourceId, resourceType, }, progress, ) .then(() => { setProgress(progress); if (progress !== 'learning') { onClose(); } renderTopicProgress(topicId, progress); refreshProgressCounters(); }) .catch((err) => { toast.error(err.message || 'Error updating progress'); console.error(err); }) .finally(() => { setIsUpdatingProgress(false); }); }; const allowMarkingSkipped = ['pending', 'learning', 'done'].includes( progress, ); const allowMarkingDone = ['skipped', 'pending', 'learning'].includes( progress, ); const allowMarkingLearning = ['done', 'skipped', 'pending'].includes( progress, ); const allowMarkingPending = ['skipped', 'done', 'learning'].includes( progress, ); if (isUpdatingProgress) { return ( ); } return (
{progress === 'learning' ? 'In Progress' : progress} {showChangeStatus && (
{allowMarkingDone && ( )} {allowMarkingLearning && ( )} {allowMarkingPending && ( )} {allowMarkingSkipped && ( )}
)}
); }