import { useEffect, useState } from 'react'; import type { MouseEvent } from "react"; import { httpPatch } from '../../lib/http'; import type { ResourceType } from '../../lib/resource-progress'; import { isLoggedIn } from '../../lib/jwt'; import { showLoginPopup } from '../../lib/popup'; import { FavoriteIcon } from './FavoriteIcon'; import { Spinner } from '../ReactIcons/Spinner'; import { useToast } from '../../hooks/use-toast'; type MarkFavoriteType = { resourceType: ResourceType; resourceId: string; favorite?: boolean; className?: string; }; export function MarkFavorite({ resourceId, resourceType, favorite, className, }: MarkFavoriteType) { const isAuthenticated = isLoggedIn(); const localStorageKey = `${resourceType}-${resourceId}-favorite`; const toast = useToast(); const [isLoading, setIsLoading] = useState(false); const [isFavorite, setIsFavorite] = useState( isAuthenticated ? (favorite ?? localStorage.getItem(localStorageKey) === '1') : false ); async function toggleFavoriteHandler(e: MouseEvent) { e.preventDefault(); if (!isLoggedIn()) { showLoginPopup(); return; } if (isLoading) { return; } setIsLoading(true); const { error } = await httpPatch<{ status: 'ok' }>( `${import.meta.env.PUBLIC_API_URL}/v1-mark-favorite`, { resourceType, resourceId, } ); if (error) { setIsLoading(false); toast.error('Failed to update favorite status'); return; } // Dispatching an event instead of setting the state because // MarkFavorite component is used in the HeroSection as well // as featured items section. We will let the custom event // listener set the update `useEffect` window.dispatchEvent( new CustomEvent('mark-favorite', { detail: { resourceId, resourceType, isFavorite: !isFavorite, }, }) ); window.dispatchEvent(new CustomEvent('refresh-favorites', {})); setIsLoading(false); } useEffect(() => { const listener = (e: Event) => { const { resourceId: id, resourceType: type, isFavorite: fav, } = (e as CustomEvent).detail; if (id === resourceId && type === resourceType) { setIsFavorite(fav); localStorage.setItem(localStorageKey, fav ? '1' : '0'); } }; window.addEventListener('mark-favorite', listener); return () => { window.removeEventListener('mark-favorite', listener); }; }, []); return ( ); }