diff --git a/components/identities/availableIdentities.tsx b/components/identities/availableIdentities.tsx index 0d78d68e..4ed5c2f4 100644 --- a/components/identities/availableIdentities.tsx +++ b/components/identities/availableIdentities.tsx @@ -29,11 +29,21 @@ import IdentitiesSkeleton from "./skeletons/identitiesSkeleton"; import { Tooltip } from "@mui/material"; import { isIdentityExpired } from "../../utils/dateService"; import DomainExpiredModal from "@/components/UI/domainExpiredModal"; +import { useIdentityRefresh } from "../../hooks/useIdentityRefresh"; const AvailableIdentities = ({ tokenId }: { tokenId: string }) => { const router = useRouter(); const { address } = useAccount(); - //const tokenId: string = router.query.tokenId as string; + + let registerRefreshCallback: ((tokenId: string, callback: () => void) => void) | null = null; + let unregisterRefreshCallback: ((tokenId: string) => void) | null = null; + try { + const context = useIdentityRefresh(); + registerRefreshCallback = context.registerRefreshCallback; + unregisterRefreshCallback = context.unregisterRefreshCallback; + } catch { + console.error("Unable to refresh user identity."); + } const [identity, setIdentity] = useState(); const [isIdentityADomain, setIsIdentityADomain] = useState< boolean | undefined @@ -150,9 +160,19 @@ const AvailableIdentities = ({ tokenId }: { tokenId: string }) => { if (tokenId) { refreshData(); const timer = setInterval(() => refreshData(), 30e3); - return () => clearInterval(timer); + + if (registerRefreshCallback) { + registerRefreshCallback(tokenId, refreshData); + } + + return () => { + clearInterval(timer); + if (unregisterRefreshCallback) { + unregisterRefreshCallback(tokenId); + } + }; } - }, [tokenId, refreshData]); + }, [tokenId, refreshData, registerRefreshCallback, unregisterRefreshCallback]); function mint() { execute(); diff --git a/components/providers/IdentityRefreshProvider.tsx b/components/providers/IdentityRefreshProvider.tsx new file mode 100644 index 00000000..2d4e708a --- /dev/null +++ b/components/providers/IdentityRefreshProvider.tsx @@ -0,0 +1,37 @@ +import React, { useCallback, useRef } from 'react'; +import { IdentityRefreshContext } from '../../hooks/useIdentityRefresh'; + +interface IdentityRefreshProviderProps { + children: React.ReactNode; +} + +export const IdentityRefreshProvider: React.FC = ({ children }) => { + const refreshCallbacks = useRef void>>(new Map()); + + const registerRefreshCallback = useCallback((tokenId: string, callback: () => void) => { + refreshCallbacks.current.set(tokenId, callback); + }, []); + + const unregisterRefreshCallback = useCallback((tokenId: string) => { + refreshCallbacks.current.delete(tokenId); + }, []); + + const refreshIdentity = useCallback((tokenId: string) => { + const callback = refreshCallbacks.current.get(tokenId); + if (callback) { + callback(); + } + }, []); + + return ( + + {children} + + ); +}; \ No newline at end of file diff --git a/hooks/useIdentityRefresh.ts b/hooks/useIdentityRefresh.ts new file mode 100644 index 00000000..8fde1240 --- /dev/null +++ b/hooks/useIdentityRefresh.ts @@ -0,0 +1,17 @@ +import { createContext, useContext } from 'react'; + +interface IdentityRefreshContextType { + refreshIdentity: (tokenId: string) => void; + registerRefreshCallback: (tokenId: string, callback: () => void) => void; + unregisterRefreshCallback: (tokenId: string) => void; +} + +export const IdentityRefreshContext = createContext(null); + +export const useIdentityRefresh = () => { + const context = useContext(IdentityRefreshContext); + if (!context) { + throw new Error('useIdentityRefresh must be used within an IdentityRefreshProvider'); + } + return context; +}; \ No newline at end of file diff --git a/hooks/useNotificationManager.ts b/hooks/useNotificationManager.ts index 2360b165..4d73d497 100644 --- a/hooks/useNotificationManager.ts +++ b/hooks/useNotificationManager.ts @@ -3,11 +3,12 @@ import { useAtom } from "jotai"; import { atomWithStorage } from "jotai/utils"; import { useEffect } from "react"; import { hexToDecimal } from "../utils/feltService"; -import { NotificationType } from "../utils/constants"; +import { NotificationType, TransactionType } from "../utils/constants"; import { RejectedTransactionReceiptResponse, RevertedTransactionReceiptResponse, } from "starknet"; +import { useIdentityRefresh } from "./useIdentityRefresh"; const notificationsAtom = atomWithStorage[]>( "userNotifications_SID", @@ -18,6 +19,14 @@ export function useNotificationManager() { const { provider } = useProvider(); const { address } = useAccount(); const [notifications, setNotifications] = useAtom(notificationsAtom); + + let refreshIdentity: ((tokenId: string) => void) | null = null; + try { + const context = useIdentityRefresh(); + refreshIdentity = context.refreshIdentity; + } catch { + + } useEffect(() => { const checkTransactionStatus = async ( @@ -58,6 +67,21 @@ export function useNotificationManager() { transactionReceipt.finality_status; updatedTransactions[index].data.status = "success"; setNotifications(updatedTransactions); + + const verificationTypes = [ + TransactionType.VERIFIER_GITHUB, + TransactionType.VERIFIER_TWITTER, + TransactionType.VERIFIER_DISCORD, + TransactionType.VERIFIER_POP + ]; + + if (verificationTypes.includes(transaction.type) && refreshIdentity) { + const subtextMatch = notification.subtext?.match(/Starknet ID #(\d+)/); + if (subtextMatch) { + const tokenId = subtextMatch[1]; + setTimeout(() => refreshIdentity(tokenId), 1000); + } + } } } }; @@ -67,7 +91,7 @@ export function useNotificationManager() { }, 5000); return () => clearInterval(intervalId); - }, [notifications, address, provider, setNotifications]); + }, [notifications, address, provider, setNotifications, refreshIdentity]); const filteredNotifications = address ? notifications.filter( diff --git a/pages/_app.tsx b/pages/_app.tsx index 3e0e9a49..d0d30f37 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -17,6 +17,7 @@ import posthog from "posthog-js"; import AcceptCookies from "../components/legal/acceptCookies"; import { Chain, sepolia, mainnet } from "@starknet-react/chains"; import { getConnectors } from "@/utils/connectorWrapper"; +import { IdentityRefreshProvider } from "../components/providers/IdentityRefreshProvider"; if (typeof window !== "undefined") { posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY as string, { @@ -52,21 +53,23 @@ function MyApp({ Component, pageProps }: AppProps) { autoConnect > - - - Starknet.id - - - - - - - - - + + + + Starknet.id + + + + + + + + + +