All files / src/components/buttons/FavoriteButton FavoriteButton.tsx

0% Statements 0/31
0% Branches 0/30
0% Functions 0/2
0% Lines 0/29

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118                                                                                                                                                                                                                                           
'use client';
 
import { useMutation } from '@apollo/client/react';
import { ActionIcon, Tooltip } from '@mantine/core';
import { notifications } from '@mantine/notifications';
import { IconHeart, IconHeartFilled } from '@tabler/icons-react';
import { useSession } from 'next-auth/react';
import { useTranslations } from 'next-intl';
import { useCallback, useState } from 'react';
import {
  ADD_TO_FAVORITE_RECIPES,
  REMOVE_FROM_FAVORITE_RECIPES,
} from '@/lib/graphql/mutations';
import { sizeMap } from '../consts';
import type { FavoriteButtonProps, FavoriteMutationData } from './types';
 
const FavoriteButton = ({
  recipeId,
  isFavorite = false,
  size = 'md',
}: FavoriteButtonProps) => {
  const { data: session } = useSession();
  const translate = useTranslations('response');
  const tFav = useTranslations('favorites');
  const [optimisticFavorite, setOptimisticFavorite] = useState(isFavorite);
 
  const [addToFavorite, { loading: addLoading }] =
    useMutation<FavoriteMutationData>(ADD_TO_FAVORITE_RECIPES);
  const [removeFromFavorite, { loading: removeLoading }] =
    useMutation<FavoriteMutationData>(REMOVE_FROM_FAVORITE_RECIPES);
 
  const loading = addLoading || removeLoading;
  const userId = (session?.user as { id?: string })?.id;
 
  const handleToggle = useCallback(
    async (e: React.MouseEvent) => {
      e.preventDefault();
      e.stopPropagation();
 
      if (!userId || loading) return;
 
      const previousState = optimisticFavorite;
      setOptimisticFavorite(!previousState);
 
      try {
        const mutation = previousState ? removeFromFavorite : addToFavorite;
        const result = await mutation({
          variables: { userId, recipeId },
          refetchQueries: ['getFavoriteRecipes', 'getRecipeById'],
        });
 
        const response = previousState
          ? result.data?.removeFromFavoriteRecipes
          : result.data?.addToFavoriteRecipes;
 
        if (response && !response.success) {
          setOptimisticFavorite(previousState);
          notifications.show({
            title: translate('error'),
            message: translate(
              response.messageKey?.replace('response.', '') ?? 'unknownError',
            ),
            color: 'red',
          });
        }
      } catch {
        setOptimisticFavorite(previousState);
        notifications.show({
          title: translate('error'),
          message: translate('somethingWentWrong'),
          color: 'red',
        });
      }
    },
    [
      userId,
      recipeId,
      optimisticFavorite,
      loading,
      addToFavorite,
      removeFromFavorite,
      translate,
    ],
  );
 
  if (!userId) return null;
 
  const iconSize = sizeMap[size];
  const HeartIcon = optimisticFavorite ? IconHeartFilled : IconHeart;
 
  return (
    <Tooltip
      label={optimisticFavorite ? tFav('remove') : tFav('add')}
      withArrow
    >
      <ActionIcon
        variant="subtle"
        color={optimisticFavorite ? 'red' : 'gray'}
        onClick={handleToggle}
        loading={loading}
        aria-label={optimisticFavorite ? tFav('remove') : tFav('add')}
        size={size}
      >
        <HeartIcon
          size={iconSize}
          style={{
            color: optimisticFavorite
              ? 'var(--mantine-color-red-6)'
              : undefined,
          }}
        />
      </ActionIcon>
    </Tooltip>
  );
};
 
export default FavoriteButton;