import { useApolloClient } from "@apollo/client"; import { HEY_TREASURY, NATIVE_TOKEN_SYMBOL } from "@hey/data/constants"; import { type AccountFragment, type PostFragment, type TippingAmountInput, useBalancesBulkQuery, useExecuteAccountActionMutation, useExecutePostActionMutation } from "@hey/indexer"; import type { ApolloClientError } from "@hey/types/errors"; import type { ChangeEvent, RefObject } from "react"; import { memo, useCallback, useRef, useState } from "react"; import { toast } from "sonner"; import TopUpButton from "@/components/Shared/Account/TopUp/Button"; import LoginButton from "@/components/Shared/LoginButton"; import Skeleton from "@/components/Shared/Skeleton"; import { Button, Input, Spinner } from "@/components/Shared/UI"; import cn from "@/helpers/cn"; import errorToast from "@/helpers/errorToast"; import usePreventScrollOnNumberInput from "@/hooks/usePreventScrollOnNumberInput"; import useTransactionLifecycle from "@/hooks/useTransactionLifecycle"; import { useAccountStore } from "@/store/persisted/useAccountStore"; const submitButtonClassName = "w-full py-1.5 text-sm font-semibold"; interface TipMenuProps { closePopover: () => void; post?: PostFragment; account?: AccountFragment; } const TipMenu = ({ closePopover, post, account }: TipMenuProps) => { const { currentAccount } = useAccountStore(); const [isSubmitting, setIsSubmitting] = useState(false); const [amount, setAmount] = useState(1); const [other, setOther] = useState(false); const handleTransactionLifecycle = useTransactionLifecycle(); const { cache } = useApolloClient(); const inputRef = useRef(null); usePreventScrollOnNumberInput(inputRef as RefObject); const { data: balance, loading: balanceLoading } = useBalancesBulkQuery({ fetchPolicy: "no-cache", pollInterval: 3000, skip: !currentAccount?.address, variables: { request: { address: currentAccount?.address, includeNative: true } } }); const updateCache = () => { if (post) { if (!post.operations) { return; } cache.modify({ fields: { hasTipped: () => true }, id: cache.identify(post.operations) }); cache.modify({ fields: { stats: (existingData) => ({ ...existingData, tips: existingData.tips + 1 }) }, id: cache.identify(post) }); } }; const onCompleted = () => { setIsSubmitting(false); closePopover(); updateCache(); toast.success(`Tipped ${amount} ${NATIVE_TOKEN_SYMBOL}`); }; const onError = useCallback((error: ApolloClientError) => { setIsSubmitting(false); errorToast(error); }, []); const cryptoRate = Number(amount); const nativeBalance = balance?.balancesBulk[0].__typename === "NativeAmount" ? Number(balance.balancesBulk[0].value).toFixed(2) : 0; const canTip = Number(nativeBalance) >= cryptoRate; const [executePostAction] = useExecutePostActionMutation({ onCompleted: async ({ executePostAction }) => { if (executePostAction.__typename === "ExecutePostActionResponse") { return onCompleted(); } return await handleTransactionLifecycle({ onCompleted, onError, transactionData: executePostAction }); }, onError }); const [executeAccountAction] = useExecuteAccountActionMutation({ onCompleted: async ({ executeAccountAction }) => { if (executeAccountAction.__typename === "ExecuteAccountActionResponse") { return onCompleted(); } return await handleTransactionLifecycle({ onCompleted, onError, transactionData: executeAccountAction }); }, onError }); const handleSetAmount = (amount: number) => { setAmount(amount); setOther(false); }; const onOtherAmount = (event: ChangeEvent) => { const value = Number(event.target.value); setAmount(value); }; const handleTip = async () => { setIsSubmitting(true); const tipping: TippingAmountInput = { native: cryptoRate.toString(), // 11 is a calculated value based on the referral pool of 20% and the Lens fee of 2.1% after the 1.5% lens fees cut referrals: [{ address: HEY_TREASURY, percent: 11 }] }; if (post) { return executePostAction({ variables: { request: { action: { tipping }, post: post.id } } }); } if (account) { return executeAccountAction({ variables: { request: { account: account.address, action: { tipping } } } }); } }; const amountDisabled = isSubmitting || !currentAccount; if (!currentAccount) { return ; } return (
Balance: {nativeBalance ? ( `${nativeBalance} ${NATIVE_TOKEN_SYMBOL}` ) : ( )}
{other ? (
) : null} {isSubmitting || balanceLoading ? ( ) : ( )}
); }; export default memo(TipMenu);