From 2508c39f48087ff3d46a6d4810fa64d5a74fe238 Mon Sep 17 00:00:00 2001 From: mehditorabiv Date: Sun, 1 Sep 2024 22:13:35 +0300 Subject: [PATCH] add reveal functionality --- src/pages/Identifiers/Identifiers.tsx | 560 ++++++-------------------- 1 file changed, 112 insertions(+), 448 deletions(-) diff --git a/src/pages/Identifiers/Identifiers.tsx b/src/pages/Identifiers/Identifiers.tsx index 85cfa22..140b1cc 100644 --- a/src/pages/Identifiers/Identifiers.tsx +++ b/src/pages/Identifiers/Identifiers.tsx @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/no-shadow */ import { useCallback, useEffect, useState } from 'react'; +import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline'; import VerifiedIcon from '@mui/icons-material/Verified'; import { Alert, @@ -9,10 +10,13 @@ import { Box, Button, CircularProgress, + ClickAwayListener, + IconButton, List, ListItem, ListItemSecondaryAction, ListItemText, + Tooltip, Typography, } from '@mui/material'; import clsx from 'clsx'; @@ -22,7 +26,10 @@ import { Address } from 'viem'; import useAttestations from '../../hooks/useAttestations'; import { IAttestation, RevokePayload } from '../../interfaces'; -import { useRevokeIdentifierMutation } from '../../services/api/eas/query'; +import { + useDecryptAttestationsSecretMutation, + useRevokeIdentifierMutation, +} from '../../services/api/eas/query'; import EASService from '../../services/eas.service'; import sepoliaChain from '../../utils/contracts/eas/sepoliaChain.json'; import { useSigner } from '../../utils/eas-wagmi-utils'; @@ -32,6 +39,7 @@ interface Identifier { icon: React.ElementType; verified: boolean; uid: string; + revealedSecret?: string; } export default function Identifiers() { @@ -40,6 +48,10 @@ export default function Identifiers() { { name: 'Discord', icon: FaDiscord, verified: false, uid: '' }, { name: 'Google', icon: FaGoogle, verified: false, uid: '' }, ]); + const [openTooltips, setOpenTooltips] = useState<{ [key: string]: boolean }>( + {} + ); + const [loading, setLoading] = useState<{ [key: string]: boolean }>({}); const signer = useSigner(); @@ -55,6 +67,9 @@ export default function Identifiers() { } = useAttestations(); const { mutate: mutateRevoke } = useRevokeIdentifierMutation(); + const { mutate: mutateDecryptAttestationsSecret } = + useDecryptAttestationsSecretMutation(); + const matchIdentifiersWithAttestations = ( identifiers: Identifier[], attestationList: IAttestation[] @@ -121,6 +136,56 @@ export default function Identifiers() { [mutateRevoke, chainId, easService] ); + const handleReveal = useCallback( + (identifier: Identifier) => { + console.log('Identifier:', identifier); + + const siweJwt = localStorage.getItem('OCI_TOKEN'); + + if (!siweJwt) throw new Error('OCI SIWE token not found'); + + setLoading((prev) => ({ ...prev, [identifier.uid]: true })); + + if (identifier.revealedSecret) { + setOpenTooltips((prev) => ({ ...prev, [identifier.uid]: true })); + setLoading((prev) => ({ ...prev, [identifier.uid]: false })); + return; + } + + mutateDecryptAttestationsSecret( + { + uid: identifier.uid, + siweJwt, + chainId, + }, + { + onSuccess: (response) => { + console.log('Decrypted secret:', response); + setUserIdentifiers((prev) => + prev.map((id) => { + if (id.uid === identifier.uid) { + return { + ...id, + revealedSecret: response.data.id, + }; + } + return id; + }) + ); + setOpenTooltips((prev) => ({ ...prev, [identifier.uid]: true })); + }, + onError: (error) => { + console.error('Error decrypting secret:', error); + }, + onSettled: () => { + setLoading((prev) => ({ ...prev, [identifier.uid]: false })); + }, + } + ); + }, + [mutateDecryptAttestationsSecret, chainId] + ); + const handleConnectAttestation = useCallback( (name: string) => { navigate(`/identifiers/${name.toLowerCase()}/attestation`); @@ -128,6 +193,10 @@ export default function Identifiers() { [navigate] ); + const handleTooltipClose = (uid: string) => { + setOpenTooltips((prev) => ({ ...prev, [uid]: false })); + }; + if (attestationsLoading) { return ; } @@ -184,9 +253,48 @@ export default function Identifiers() { - - {identifier.name} - +
+ {identifier.name} + {identifier.verified && ( + handleTooltipClose(identifier.uid)} + > +
+ + ) : ( + identifier.revealedSecret && + `Account ID: ${identifier.revealedSecret}` + ) + } + arrow + open={openTooltips[identifier.uid] || false} + onClose={() => handleTooltipClose(identifier.uid)} + disableHoverListener + disableTouchListener + disableFocusListener + placement="top" + > + handleReveal(identifier)} + sx={{ + ml: 1, + p: 0, + }} + > + {loading[identifier.uid] ? ( + + ) : ( + + )} + + +
+
+ )} +
-// ) : ( -// -// )} -// -// -// -// -// ); - -// export function Identifiers() { -// const { showSnackbar } = useSnackbarStore(); -// const { chainId, address } = useAccount(); -// const navigate = useNavigate(); - -// const signer = useSigner(); - -// const [identifiers, setIdentifiers] = useState([ -// { -// name: 'Discord', -// icon: FaDiscord, -// verified: false, -// uid: '', -// }, -// { name: 'Google', icon: FaGoogle, verified: false, uid: '' }, -// ]); - -// const [attestations, setAttestations] = useState< -// (IAttestation & { provider?: string; id?: string })[] -// >([]); -// const { -// data: attestationsResponse, -// refetch, -// isLoading, -// } = useGetAttestations(address as `0x${string}`); - -// const { mutate: mutateRevokeIdentifier, data: revokeIdentifierResponse } = -// useRevokeIdentifierMutation(); - -// const { mutate: mutateDecryptAttestationsSecret } = -// useDecryptAttestationsSecretMutation(); - -// const [loadingIdentifiers, setLoadingIdentifiers] = useState<{ -// [uid: string]: boolean; -// }>({}); - -// const [revealedIdentifiers, setRevealedIdentifiers] = useState<{ -// [uid: string]: string; -// }>({}); - -// const [revealing, setRevealing] = useState<{ [uid: string]: boolean }>({}); - -// useEffect(() => { -// const processAttestations = () => { -// if (!attestationsResponse) { -// return; -// } - -// const attestationsData = attestationsResponse.map((attestation) => { -// const decodedData = decodeAttestationData(attestation.data); - -// const providerData = decodedData.find( -// (data) => data.name === 'provider' -// ); - -// return { -// ...attestation, -// provider: -// typeof providerData?.value.value === 'string' -// ? providerData.value.value -// : undefined, -// decodedData, -// }; -// }); - -// setAttestations(attestationsData); -// }; - -// processAttestations(); -// }, [attestationsResponse]); - -// useEffect(() => { -// const updatedIdentifiers = identifiers.map((identifier) => { -// const matchingAttestation = attestations.find( -// (attestation) => -// (attestation.provider as string)?.toLowerCase() === -// identifier.name.toLowerCase() -// ); - -// return { -// ...identifier, -// verified: !!matchingAttestation, -// uid: matchingAttestation?.id || '', -// }; -// }); - -// setIdentifiers(updatedIdentifiers); - -// const initialRevealedState = updatedIdentifiers.reduce( -// (acc, identifier) => { -// acc[identifier.uid] = '*********'; -// return acc; -// }, -// {} as { [uid: string]: string } -// ); - -// setRevealedIdentifiers(initialRevealedState); -// }, [attestations]); - -// const handleRevoke = useCallback( -// (uid: string) => { -// const siweJwt = localStorage.getItem('OCI_TOKEN'); - -// if (!siweJwt) throw new Error('OCI SIWE token not found'); - -// setLoadingIdentifiers((prev) => ({ ...prev, [uid]: true })); - -// mutateRevokeIdentifier({ -// uid, -// siweJwt, -// chainId, -// }); -// }, -// [mutateRevokeIdentifier, chainId] -// ); - -// const handleConnect = useCallback( -// (identifier: string) => { -// navigate(`/identifiers/${identifier.toLowerCase()}/attestation`); -// }, -// [navigate] -// ); - -// const handleReveal = useCallback( -// (uid: string) => { -// // Toggle between showing and hiding the identifier -// if (revealedIdentifiers[uid] !== '*********') { -// setRevealedIdentifiers((prev) => ({ -// ...prev, -// [uid]: '*********', -// })); -// return; -// } - -// setRevealing((prev) => ({ -// ...prev, -// [uid]: true, -// })); - -// const siweJwt = localStorage.getItem('OCI_TOKEN'); - -// if (!siweJwt) throw new Error('OCI SIWE token not found'); - -// mutateDecryptAttestationsSecret( -// { -// uid, -// siweJwt, -// chainId, -// }, -// { -// onSuccess: (response) => { -// console.log('Decrypted secret:', response); - -// setRevealedIdentifiers((prev) => ({ -// ...prev, -// [uid]: response.data.id, -// })); -// setRevealing((prev) => ({ -// ...prev, -// [uid]: false, -// })); -// }, -// onError: (error) => { -// console.error('Error decrypting secret:', error); -// setRevealing((prev) => ({ -// ...prev, -// [uid]: false, -// })); -// }, -// } -// ); -// }, -// [chainId, mutateDecryptAttestationsSecret, revealedIdentifiers] -// ); - -// useEffect(() => { -// const revokeIdentifier = async () => { -// if (revokeIdentifierResponse) { -// console.log('Revoke identifier response:', revokeIdentifierResponse); - -// const payload: RevokePayload = convertStringsToBigInts( -// revokeIdentifierResponse.data -// ) as RevokePayload; - -// console.log('Payload:', payload); - -// try { -// const eas = new EAS(sepoliaChain.easContractAddress as Address); - -// if (!signer) throw new Error('Signer not found'); - -// if (!revokeIdentifierResponse) -// throw new Error('No linking identifier'); - -// eas.connect(signer); - -// if ('revoker' in payload.message) { -// const transformedPayload: DelegatedRevocationRequest = { -// schema: payload.message.schema, -// data: { -// uid: payload.message.uid, -// }, -// signature: payload.signature, -// revoker: payload.message.revoker, -// deadline: 0n, -// }; - -// const tx = await eas.revokeByDelegation(transformedPayload); -// await tx.wait(); -// console.log({ tx }); - -// showSnackbar('Identifier revoked successfully', { -// severity: 'success', -// }); - -// setLoadingIdentifiers((prev) => ({ -// ...prev, -// [payload.message.uid]: false, -// })); -// } else { -// throw new Error('Invalid message type for revocation'); -// } -// } catch (error: any) { -// const errorCode = error?.info?.error?.code || ''; - -// if (errorCode === 4001) { -// showSnackbar( -// `${errorCode}, you reject the transaction. please try again...`, -// { -// severity: 'error', -// } -// ); -// } - -// if ('uid' in payload.message) { -// setLoadingIdentifiers((prev) => ({ -// ...prev, -// [payload.message.uid]: false, -// })); -// } -// } finally { -// refetch(); -// } -// } -// }; - -// revokeIdentifier(); -// }, [revokeIdentifierResponse]); - -// if (isLoading) { -// return ( -// theme.zIndex.drawer + 1, -// background: '#fff', -// color: 'black', -// }} -// > -// -// -// -// Loading... -// -// -// -// ); -// } - -// return ( -//
-// -// Identifiers -// -// -// -// -// -// -// Actions -// -// -// -// -// {identifiers.map((identifier) => ( -// handleReveal(identifier.uid)} -// /> -// ))} -// -//
-// ); -// } - -// export default Identifiers;