From 69c33c5c8116981e0e66927a3fcb19492e420a9c Mon Sep 17 00:00:00 2001 From: Vital Date: Thu, 21 Nov 2024 16:39:33 -0500 Subject: [PATCH] ENG-4832 feat(portal): use graphql queries within follow modal (#943) ## Affected Packages Apps - [ ] data populator - [x] portal - [ ] template Packages - [ ] 1ui - [ ] api - [ ] graphql - [ ] protocol - [ ] sdk Tools - [ ] tools ## Overview - Now fetches the vaultDetails, followClaim, etc inside of the follow modal rather than passing it in from route ## Screen Captures If applicable, add screenshots or screen captures of your changes. ## Declaration - [x] I hereby declare that I have abided by the rules and regulations as outlined in the [CONTRIBUTING.md](https://github.com/0xIntuition/intuition-ts/blob/main/CONTRIBUTING.md) --- .../create-claim/create-claim-form.tsx | 2 +- .../app/components/follow/follow-button.tsx | 7 +- .../app/components/follow/follow-form.tsx | 31 +++-- .../app/components/follow/follow-modal.tsx | 109 ++++++++++++------ .../app/components/follow/follow-review.tsx | 11 +- .../lib/hooks/mutations/useFollowMutation.tsx | 22 ++-- .../app/routes/app+/profile+/$wallet.tsx | 32 +++-- 7 files changed, 126 insertions(+), 88 deletions(-) diff --git a/apps/portal/app/components/create-claim/create-claim-form.tsx b/apps/portal/app/components/create-claim/create-claim-form.tsx index 9fb65bd6b..d525c8b94 100644 --- a/apps/portal/app/components/create-claim/create-claim-form.tsx +++ b/apps/portal/app/components/create-claim/create-claim-form.tsx @@ -173,7 +173,7 @@ function CreateClaimForm({ } return 2000 }, - queryKey: ['GetTriple', { id: vaultId ? parseFloat(vaultId) : 0 }], + queryKey: ['get-triple', { id: vaultId ? parseFloat(vaultId) : 0 }], }, ) diff --git a/apps/portal/app/components/follow/follow-button.tsx b/apps/portal/app/components/follow/follow-button.tsx index 0129ccbe2..c0e40b129 100644 --- a/apps/portal/app/components/follow/follow-button.tsx +++ b/apps/portal/app/components/follow/follow-button.tsx @@ -4,7 +4,6 @@ import { Button, cn } from '@0xintuition/1ui' import { stakeModalAtom } from '@lib/state/store' import { getChainEnvConfig } from '@lib/utils/environment' -import { formatBalance } from '@lib/utils/misc' import { useNavigation } from '@remix-run/react' import { CURRENT_ENV } from 'app/consts' import { @@ -120,10 +119,8 @@ const FollowButton: React.FC = ({ handleSwitch() } else if (val !== '') { const errors = [] - if (+val < +formatUnits(BigInt(min_deposit), 18)) { - errors.push( - `Minimum deposit is ${formatBalance(min_deposit, 18)} ETH`, - ) + if (+val < +min_deposit) { + errors.push(`Minimum deposit is ${min_deposit} ETH`) } if (+val * +formattedConvictionPrice > +walletBalance) { errors.push('Insufficient funds') diff --git a/apps/portal/app/components/follow/follow-form.tsx b/apps/portal/app/components/follow/follow-form.tsx index 2185092bd..4f4197c9c 100644 --- a/apps/portal/app/components/follow/follow-form.tsx +++ b/apps/portal/app/components/follow/follow-form.tsx @@ -10,19 +10,11 @@ import { Identity, Text, } from '@0xintuition/1ui' -import { IdentityPresenter } from '@0xintuition/api' import { InfoTooltip } from '@components/info-tooltip' import StakingRadioGroup from '@components/staking-radio-group' import { TransactionState } from '@components/transaction-state' -import { - formatBalance, - getAtomDescription, - getAtomImage, - getAtomIpfsLink, - getAtomLabel, - getAtomLink, -} from '@lib/utils/misc' +import { formatBalance } from '@lib/utils/misc' import { TransactionActionType, TransactionStateType, @@ -32,7 +24,8 @@ import FollowReview from './follow-review' interface FollowFormProps { walletBalance: string - identity: IdentityPresenter + identityLabel: string + identityAvatar: string user_assets: string entry_fee: string exit_fee: string @@ -50,7 +43,8 @@ interface FollowFormProps { export default function FollowForm({ walletBalance, - identity, + identityLabel, + identityAvatar, user_assets, entry_fee, exit_fee, @@ -122,12 +116,12 @@ export default function FollowForm({ }} object={{ variant: Identity.user, - label: getAtomLabel(identity), - imgSrc: getAtomImage(identity), - id: identity.identity_id, - description: getAtomDescription(identity), - ipfsLink: getAtomIpfsLink(identity), - link: getAtomLink(identity), + label: identityLabel, + imgSrc: identityAvatar, + id: '', + description: '', + ipfsLink: '', + link: '', shouldHover: false, }} maxIdentityLength={16} @@ -163,7 +157,8 @@ export default function FollowForm({ val={val} dispatch={dispatch} state={state} - identity={identity} + identityLabel={identityLabel} + identityAvatar={identityAvatar} user_assets={user_assets} entry_fee={entry_fee} exit_fee={exit_fee} diff --git a/apps/portal/app/components/follow/follow-modal.tsx b/apps/portal/app/components/follow/follow-modal.tsx index 21bd359e8..8e3fb2a2a 100644 --- a/apps/portal/app/components/follow/follow-modal.tsx +++ b/apps/portal/app/components/follow/follow-modal.tsx @@ -1,21 +1,23 @@ import { useEffect, useState } from 'react' import { Dialog, DialogContent, DialogFooter, toast } from '@0xintuition/1ui' -import { ClaimPresenter, IdentityPresenter } from '@0xintuition/api' +import { useGetTripleQuery } from '@0xintuition/graphql' import { multivaultAbi } from '@lib/abis/multivault' import { useFollowMutation } from '@lib/hooks/mutations/useFollowMutation' +import { useCheckClaim } from '@lib/hooks/useCheckClaim' import { useCreateClaimConfig } from '@lib/hooks/useCreateClaimConfig' +import { useGetVaultDetails } from '@lib/hooks/useGetVaultDetails' import { useGetWalletBalance } from '@lib/hooks/useGetWalletBalance' import { transactionReducer } from '@lib/hooks/useTransactionReducer' +import { getSpecialPredicate } from '@lib/utils/app' import { useGenericTxState } from '@lib/utils/use-tx-reducer' import { useLocation } from '@remix-run/react' -import { MIN_DEPOSIT } from 'app/consts' +import { CURRENT_ENV, MIN_DEPOSIT } from 'app/consts' import { TransactionActionType, TransactionStateType, } from 'app/types/transaction' -import { VaultDetailsType } from 'app/types/vault' import { Address, decodeEventLog } from 'viem' import { useAccount, usePublicClient } from 'wagmi' @@ -34,9 +36,9 @@ interface FollowModalProps { userWallet: string contract: string open: boolean - identity: IdentityPresenter - claim: ClaimPresenter - vaultDetails: VaultDetailsType + identityVaultId: string + identityLabel: string + identityAvatar: string onClose?: () => void } @@ -44,20 +46,11 @@ export default function FollowModal({ userWallet, contract, open = false, - identity, - claim, - vaultDetails, + identityVaultId, + identityLabel, + identityAvatar, onClose = () => {}, }: FollowModalProps) { - const { - conviction_price = '0', - user_conviction = '0', - user_assets = '0', - min_deposit = '0', - formatted_entry_fee = '0', - formatted_exit_fee = '0', - } = vaultDetails ? vaultDetails : {} - const [val, setVal] = useState(MIN_DEPOSIT) const [mode, setMode] = useState<'follow' | 'unfollow'>('follow') const [showErrors, setShowErrors] = useState(false) @@ -68,13 +61,47 @@ export default function FollowModal({ TransactionActionType >(transactionReducer, initialTxState) const publicClient = usePublicClient() - const userVaultId = identity.vault_id - - let vault_id: string = '0' - vault_id = claim ? claim.vault_id : '0' const { address } = useAccount() + const { data: claimCheckData } = useCheckClaim({ + subjectId: getSpecialPredicate(CURRENT_ENV).iPredicate.vaultId.toString(), + predicateId: + getSpecialPredicate(CURRENT_ENV).amFollowingPredicate.vaultId.toString(), + objectId: identityVaultId, + }) + + const { data: claimData } = useGetTripleQuery( + { tripleId: claimCheckData?.result }, + { + enabled: Boolean(claimCheckData?.result !== '0'), + retry: 1, + retryDelay: 2000, + refetchInterval: (query) => { + if (query.state.status === 'success') { + return false + } + return 2000 + }, + queryKey: ['get-triple', { id: claimCheckData?.result }], + }, + ) + + const vaultDetails = useGetVaultDetails( + contract, + claimData?.triple?.vaultId, + claimData?.triple?.counterVaultId, + { + queryKey: [ + 'get-vault-details', + contract, + claimData?.triple?.vaultId, + claimData?.triple?.counterVaultId, + ], + enabled: open, + }, + ) + const { data: configData, isLoading: isLoadingConfig } = useCreateClaimConfig() @@ -85,18 +112,21 @@ export default function FollowModal({ awaitingOnChainConfirmation, isError, reset, - } = useFollowMutation(contract, claim, user_conviction, mode) + } = useFollowMutation( + contract, + vaultDetails?.data?.user_conviction ?? '0', + mode, + claimData?.triple?.vaultId, + ) const handleAction = async () => { try { const txHash = await follow({ val, userWallet, - vaultId: vault_id, - claim, - identity, + vaultId: claimData?.triple?.vaultId, tripleCost: BigInt(configData?.fees.tripleCost ?? '0'), - userVaultId, + userVaultId: identityVaultId, }) if (publicClient && txHash) { @@ -229,7 +259,7 @@ export default function FollowModal({ } const handleUnfollowButtonClick = async () => { - if (+val > +(user_conviction ?? '0')) { + if (+val > +(vaultDetails?.data?.user_conviction ?? '0')) { setShowErrors(true) return } @@ -273,11 +303,12 @@ export default function FollowModal({
'0' && state.status === 'idle') || mode !== 'follow' ? '' : 'hidden'}`} + className={`${(vaultDetails?.data?.user_conviction && vaultDetails?.data?.user_conviction > '0' && state.status === 'idle') || mode !== 'follow' ? '' : 'hidden'}`} /> void state: TransactionStateType isError?: boolean - identity: IdentityPresenter + identityLabel: string + identityAvatar: string user_assets: string entry_fee: string exit_fee: string @@ -37,7 +37,8 @@ export default function FollowReview({ dispatch, state, isError, - identity, + identityLabel, + identityAvatar, user_assets, entry_fee, exit_fee, @@ -113,8 +114,8 @@ export default function FollowReview({ }} object={{ variant: Identity.user, - imgSrc: identity.user?.image ?? identity.image, - label: identity.user?.display_name ?? identity.display_name, + imgSrc: identityAvatar, + label: identityLabel, shouldHover: false, }} /> diff --git a/apps/portal/app/lib/hooks/mutations/useFollowMutation.tsx b/apps/portal/app/lib/hooks/mutations/useFollowMutation.tsx index 3e13ab92c..a985a1d95 100644 --- a/apps/portal/app/lib/hooks/mutations/useFollowMutation.tsx +++ b/apps/portal/app/lib/hooks/mutations/useFollowMutation.tsx @@ -1,5 +1,3 @@ -import { ClaimPresenter, IdentityPresenter } from '@0xintuition/api' - import { multivaultAbi } from '@lib/abis/multivault' import { useCreateTriple } from '@lib/hooks/useCreateTriple' import { useDepositTriple } from '@lib/hooks/useDepositTriple' @@ -13,24 +11,23 @@ interface FollowMutationParams { val: string userWallet: string vaultId: string - claim?: ClaimPresenter - identity?: IdentityPresenter + claimId?: string userVaultId?: string tripleCost?: bigint } export function useFollowMutation( contract: string, - claim: ClaimPresenter, user_conviction: string, mode: 'follow' | 'unfollow', + claimId?: string, ) { const queryClient = useQueryClient() const depositHook = useDepositTriple(contract) const redeemHook = useRedeemTriple(contract) const createHook = useCreateTriple() - const activeHook = !claim + const activeHook = !claimId ? createHook : mode === 'follow' ? depositHook @@ -47,29 +44,28 @@ export function useFollowMutation( return { ...useMutation({ mutationFn: async (params: FollowMutationParams) => { - const { val, userWallet, vaultId, claim, userVaultId, tripleCost } = - params + const { val, userWallet, userVaultId, tripleCost } = params const parsedValue = parseUnits(val === '' ? '0' : val, 18) return writeContractAsync({ address: contract as `0x${string}`, abi: multivaultAbi as Abi, - functionName: !claim + functionName: !claimId ? 'createTriple' : mode === 'follow' ? 'depositTriple' : 'redeemTriple', - args: !claim + args: !claimId ? [ getSpecialPredicate(CURRENT_ENV).iPredicate.vaultId, getSpecialPredicate(CURRENT_ENV).amFollowingPredicate.vaultId, userVaultId, ] : mode === 'follow' - ? [userWallet as `0x${string}`, vaultId] - : [user_conviction, userWallet as `0x${string}`, vaultId], + ? [userWallet as `0x${string}`, claimId] + : [user_conviction, userWallet as `0x${string}`, claimId], value: - !claim && tripleCost + !claimId && tripleCost ? tripleCost + parsedValue : mode === 'follow' ? parsedValue diff --git a/apps/portal/app/routes/app+/profile+/$wallet.tsx b/apps/portal/app/routes/app+/profile+/$wallet.tsx index 19cbe29cc..cf2b53efc 100644 --- a/apps/portal/app/routes/app+/profile+/$wallet.tsx +++ b/apps/portal/app/routes/app+/profile+/$wallet.tsx @@ -41,6 +41,7 @@ import { } from '@0xintuition/graphql' import { ErrorPage } from '@components/error-page' +import FollowModal from '@components/follow/follow-modal' import NavigationButton from '@components/navigation-link' import ImageModal from '@components/profile/image-modal' import SaveListModal from '@components/save-list/save-list-modal' @@ -54,6 +55,7 @@ import { useLiveLoader } from '@lib/hooks/useLiveLoader' import { getIdentityOrPending } from '@lib/services/identities' import { getPurchaseIntentsByAddress } from '@lib/services/phosphor' import { + followModalAtom, imageModalAtom, saveListModalAtom, shareModalAtom, @@ -355,7 +357,7 @@ export default function Profile() { useAtom(saveListModalAtom) const [imageModalActive, setImageModalActive] = useAtom(imageModalAtom) const [shareModalActive, setShareModalActive] = useAtom(shareModalAtom) - // const [followModalActive, setFollowModalActive] = useAtom(followModalAtom) + const [followModalActive, setFollowModalActive] = useAtom(followModalAtom) const [selectedTag, setSelectedTag] = useState< IdentityPresenter | null | undefined @@ -422,13 +424,13 @@ export default function Profile() { {!isPending && (