Hey is a decentralized and permissionless social media app built with Lens Protocol 馃尶
at main 123 lines 4.0 kB view raw
1import { CheckCircleIcon } from "@heroicons/react/24/solid"; 2import { ERRORS } from "@hey/data/errors"; 3import { 4 ManagedAccountsVisibility, 5 useAccountsAvailableQuery, 6 useSwitchAccountMutation 7} from "@hey/indexer"; 8import { useCallback, useState } from "react"; 9import { useAccount } from "wagmi"; 10import Loader from "@/components/Shared/Loader"; 11import { ErrorMessage, Spinner, WarningMessage } from "@/components/Shared/UI"; 12import cn from "@/helpers/cn"; 13import errorToast from "@/helpers/errorToast"; 14import reloadAllTabs from "@/helpers/reloadAllTabs"; 15import { useAccountStore } from "@/store/persisted/useAccountStore"; 16import { signIn } from "@/store/persisted/useAuthStore"; 17import SmallSingleAccount from "./SmallSingleAccount"; 18 19const SwitchAccounts = () => { 20 const { currentAccount } = useAccountStore(); 21 const [isSubmitting, setIsSubmitting] = useState(false); 22 const [loggingInAccountId, setLoggingInAccountId] = useState<null | string>( 23 null 24 ); 25 const { address } = useAccount(); 26 27 const onError = useCallback((error?: unknown) => { 28 setIsSubmitting(false); 29 setLoggingInAccountId(null); 30 errorToast(error); 31 }, []); 32 33 const { data, error, loading } = useAccountsAvailableQuery({ 34 skip: !address, 35 variables: { 36 accountsAvailableRequest: { 37 hiddenFilter: ManagedAccountsVisibility.NoneHidden, 38 managedBy: address 39 }, 40 lastLoggedInAccountRequest: { address: address } 41 } 42 }); 43 const [switchAccount] = useSwitchAccountMutation(); 44 45 if (!address) { 46 return ( 47 <WarningMessage 48 className="m-5" 49 message="Connect your wallet to switch accounts" 50 title="No wallet connected" 51 /> 52 ); 53 } 54 55 if (loading) { 56 return <Loader className="my-5" message="Loading Accounts" />; 57 } 58 59 const accountsAvailable = data?.accountsAvailable.items || []; 60 61 const handleSwitchAccount = async (account: string) => { 62 try { 63 setLoggingInAccountId(account); 64 setIsSubmitting(true); 65 66 const auth = await switchAccount({ variables: { request: { account } } }); 67 68 if (auth.data?.switchAccount.__typename === "AuthenticationTokens") { 69 const accessToken = auth.data?.switchAccount.accessToken; 70 const refreshToken = auth.data?.switchAccount.refreshToken; 71 // Preserve theme and other local UI state by not signing out completely. 72 signIn({ accessToken, refreshToken }); 73 reloadAllTabs(); 74 return; 75 } 76 77 return onError({ message: ERRORS.SomethingWentWrong }); 78 } catch { 79 onError(); 80 } 81 }; 82 83 return ( 84 <div className="max-h-[80vh] overflow-y-auto p-2"> 85 <ErrorMessage 86 className="m-2" 87 error={error} 88 title="Failed to load accounts" 89 /> 90 {accountsAvailable.map((accountAvailable, index) => ( 91 <button 92 className="flex w-full cursor-pointer items-center justify-between space-x-2 rounded-lg py-3 pr-4 pl-3 text-gray-700 hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-gray-800" 93 disabled={ 94 currentAccount?.address === accountAvailable.account.address 95 } 96 key={accountAvailable?.account.address} 97 onClick={async () => { 98 const selectedAccount = accountsAvailable[index].account; 99 await handleSwitchAccount(selectedAccount.address); 100 }} 101 type="button" 102 > 103 <div 104 className={cn( 105 currentAccount?.address === accountAvailable.account.address && 106 "font-bold" 107 )} 108 > 109 <SmallSingleAccount account={accountAvailable.account} /> 110 </div> 111 {isSubmitting && 112 accountAvailable.account.address === loggingInAccountId ? ( 113 <Spinner size="xs" /> 114 ) : currentAccount?.address === accountAvailable.account.address ? ( 115 <CheckCircleIcon className="size-5 text-green-600" /> 116 ) : null} 117 </button> 118 ))} 119 </div> 120 ); 121}; 122 123export default SwitchAccounts;