Roadmap to becoming a developer in 2022
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

108 lines
2.8 KiB

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<HTMLButtonElement>) {
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 (
<button
onClick={toggleFavoriteHandler}
tabIndex={-1}
className={`${isFavorite ? '' : 'opacity-30 hover:opacity-100'} ${
className || 'absolute right-1.5 top-1.5 z-30 focus:outline-0'
}`}
>
{isLoading ? <Spinner /> : <FavoriteIcon isFavorite={isFavorite} />}
</button>
);
}