Hey is a decentralized and permissionless social media app built with Lens Protocol 馃尶
at main 177 lines 5.8 kB view raw
1import { CheckCircleIcon } from "@heroicons/react/24/outline"; 2import { 3 DEFAULT_COLLECT_TOKEN, 4 PERMISSIONS, 5 STATIC_IMAGES_URL, 6 SUBSCRIPTION_AMOUNT, 7 WRAPPED_NATIVE_TOKEN_SYMBOL 8} from "@hey/data/constants"; 9import { 10 type AccountFragment, 11 useBalancesBulkQuery, 12 useJoinGroupMutation 13} from "@hey/indexer"; 14import type { ApolloClientError } from "@hey/types/errors"; 15import { useCallback, useState } from "react"; 16import SingleAccount from "@/components/Shared/Account/SingleAccount"; 17import TopUpButton from "@/components/Shared/Account/TopUp/Button"; 18import { Button, Image, Spinner, Tooltip } from "@/components/Shared/UI"; 19import errorToast from "@/helpers/errorToast"; 20import getTokenImage from "@/helpers/getTokenImage"; 21import useTransactionLifecycle from "@/hooks/useTransactionLifecycle"; 22import useWaitForTransactionToComplete from "@/hooks/useWaitForTransactionToComplete"; 23import { useAccountStore } from "@/store/persisted/useAccountStore"; 24 25const Subscribe = () => { 26 const { currentAccount } = useAccountStore(); 27 const [isSubmitting, setIsSubmitting] = useState(false); 28 const handleTransactionLifecycle = useTransactionLifecycle(); 29 const waitForTransactionToComplete = useWaitForTransactionToComplete(); 30 31 const { data: balance, loading: balanceLoading } = useBalancesBulkQuery({ 32 fetchPolicy: "no-cache", 33 pollInterval: 3000, 34 skip: !currentAccount?.address, 35 variables: { 36 request: { 37 address: currentAccount?.address, 38 tokens: [DEFAULT_COLLECT_TOKEN] 39 } 40 } 41 }); 42 43 const onCompleted = async (hash: string) => { 44 await waitForTransactionToComplete(hash); 45 location.reload(); 46 }; 47 48 const onError = useCallback((error: ApolloClientError) => { 49 setIsSubmitting(false); 50 errorToast(error); 51 }, []); 52 53 const tokenBalance = 54 balance?.balancesBulk[0].__typename === "Erc20Amount" 55 ? Number(balance.balancesBulk[0].value).toFixed(2) 56 : 0; 57 58 const canSubscribe = Number(tokenBalance) >= SUBSCRIPTION_AMOUNT; 59 60 const [joinGroup] = useJoinGroupMutation({ 61 onCompleted: async ({ joinGroup }) => { 62 return await handleTransactionLifecycle({ 63 onCompleted, 64 onError, 65 transactionData: joinGroup 66 }); 67 }, 68 onError 69 }); 70 71 const handleSubscribe = async () => { 72 setIsSubmitting(true); 73 74 return await joinGroup({ 75 variables: { request: { group: PERMISSIONS.SUBSCRIPTION } } 76 }); 77 }; 78 79 const hasSubscribed = currentAccount?.hasSubscribed; 80 81 return ( 82 <div className="mx-5 my-10 flex flex-col items-center gap-y-8"> 83 <Image 84 alt="Pro" 85 className="w-32" 86 src={`${STATIC_IMAGES_URL}/pro.png`} 87 width={128} 88 /> 89 <div className="max-w-md text-center text-gray-500"> 90 {hasSubscribed ? ( 91 <div className="text-gray-500"> 92 Thanks for being a valuable <b>Hey Pro</b> member! 93 </div> 94 ) : ( 95 <> 96 Join Hey Pro for{" "} 97 <b className="inline-flex items-center gap-x-1"> 98 {SUBSCRIPTION_AMOUNT}{" "} 99 <Tooltip content={WRAPPED_NATIVE_TOKEN_SYMBOL} placement="top"> 100 <img 101 alt={WRAPPED_NATIVE_TOKEN_SYMBOL} 102 className="size-5" 103 src={getTokenImage(WRAPPED_NATIVE_TOKEN_SYMBOL)} 104 /> 105 </Tooltip> 106 /year 107 </b> 108 . 109 </> 110 )} 111 </div> 112 <SingleAccount 113 account={currentAccount as AccountFragment} 114 isVerified 115 linkToAccount={false} 116 showUserPreview={false} 117 /> 118 {hasSubscribed ? null : ( 119 <> 120 <div className="flex flex-col items-center gap-y-2 text-gray-500"> 121 <div className="flex items-center gap-x-1"> 122 <CheckCircleIcon className="size-4.5" /> 123 <span className="text-sm">Subscription Badge</span> 124 </div> 125 <div className="flex items-center gap-x-1"> 126 <CheckCircleIcon className="size-4.5" /> 127 <span className="text-sm">Exclusive Hey features</span> 128 </div> 129 <div className="flex items-center gap-x-1"> 130 <CheckCircleIcon className="size-4.5" /> 131 <span className="text-sm">Special NFT for early members</span> 132 </div> 133 <div className="flex items-center gap-x-1"> 134 <CheckCircleIcon className="size-4.5" /> 135 <span className="text-sm">Contribute to Hey's growth</span> 136 </div> 137 </div> 138 {balanceLoading ? ( 139 <Button 140 className="w-sm" 141 disabled 142 icon={<Spinner className="my-1" size="xs" />} 143 /> 144 ) : canSubscribe ? ( 145 <Button 146 className="w-sm" 147 disabled={isSubmitting} 148 loading={isSubmitting} 149 onClick={handleSubscribe} 150 > 151 Subscribe for ${SUBSCRIPTION_AMOUNT}/year 152 </Button> 153 ) : ( 154 <TopUpButton 155 amountToTopUp={ 156 Math.ceil((SUBSCRIPTION_AMOUNT - Number(tokenBalance)) * 20) / 157 20 158 } 159 className="w-sm" 160 label={`Top-up ${SUBSCRIPTION_AMOUNT} ${WRAPPED_NATIVE_TOKEN_SYMBOL} to your account`} 161 outline 162 token={{ 163 contractAddress: DEFAULT_COLLECT_TOKEN, 164 symbol: WRAPPED_NATIVE_TOKEN_SYMBOL 165 }} 166 /> 167 )} 168 <div className="-mt-1 text-center text-gray-500 text-xs"> 169 One-time payment. Manual renewal required next year. 170 </div> 171 </> 172 )} 173 </div> 174 ); 175}; 176 177export default Subscribe;