Fix: show w3a addr, unifiedWallet across UI, refresh when disconnect w3a

This commit is contained in:
SaraJane
2026-01-15 12:43:19 +00:00
parent 5a1d5791f4
commit c11daa5aff
4 changed files with 82 additions and 29 deletions

View File

@ -1,4 +1,4 @@
import { useWallet } from '@txnlab/use-wallet-react'
import { useUnifiedWallet } from './hooks/useUnifiedWallet'
import { Link } from 'react-router-dom'
/**
@ -7,7 +7,7 @@ import { Link } from 'react-router-dom'
* Displays features, how it works, and CTAs to connect wallet and create assets
*/
export default function Home() {
const { activeAddress } = useWallet()
const { activeAddress } = useUnifiedWallet()
return (
<div className="bg-white dark:bg-slate-950">

View File

@ -1,30 +1,49 @@
import { useWallet } from '@txnlab/use-wallet-react'
import { useMemo } from 'react'
import { useUnifiedWallet } from '../hooks/useUnifiedWallet'
import { ellipseAddress } from '../utils/ellipseAddress'
import { getAlgodConfigFromViteEnvironment } from '../utils/network/getAlgoClientConfigs'
/**
* Account Component
* Displays the connected wallet address (shortened) and current network
* Address links to Lora explorer for easy account tracking
*
* Displays the connected Algorand address (shortened)
* and current network.
*
* Works for BOTH:
* - Web3Auth (Google login)
* - Traditional wallets (Pera / Defly / etc)
*
* Address links to Lora explorer.
*/
const Account = () => {
const { activeAddress } = useWallet()
const { activeAddress } = useUnifiedWallet()
const algoConfig = getAlgodConfigFromViteEnvironment()
// Normalize network name for Lora
const networkName = useMemo(() => {
return algoConfig.network === '' ? 'localnet' : algoConfig.network.toLocaleLowerCase()
return algoConfig.network === '' ? 'localnet' : algoConfig.network.toLowerCase()
}, [algoConfig.network])
// Normalize address to string (VERY IMPORTANT)
const address = typeof activeAddress === 'string' ? activeAddress : activeAddress ? String(activeAddress) : null
if (!address) {
return null
}
const loraUrl = `https://lora.algokit.io/${networkName}/account/${address}/`
return (
<div>
<a
className="text-xl text-gray-900 dark:text-slate-100 hover:text-teal-600 dark:hover:text-teal-400 transition"
href={loraUrl}
target="_blank"
href={`https://lora.algokit.io/${networkName}/account/${activeAddress}/`}
rel="noreferrer"
className="text-xl text-gray-900 dark:text-slate-100 hover:text-teal-600 dark:hover:text-teal-400 transition font-mono"
>
Address: {ellipseAddress(activeAddress)}
Address: {ellipseAddress(address)}
</a>
<div className="text-xl text-gray-900 dark:text-slate-100 mt-2">Network: {networkName}</div>
</div>
)

View File

@ -12,9 +12,7 @@ import { useWeb3Auth } from './Web3AuthProvider'
* Features:
* - Wallet connection/disconnection with Web3Auth (Google OAuth)
* - Auto-generation of Algorand wallet from Google credentials
* - Ellipsized address display for better UX
* - Loading states and error handling
* - Beautiful Google-style sign-in button
*
* Usage:
* ```tsx
@ -26,6 +24,9 @@ export function Web3AuthButton() {
const [isDropdownOpen, setIsDropdownOpen] = useState(false)
const [copied, setCopied] = useState(false)
// Lora explorer base (TestNet)
const LORA_ACCOUNT_BASE = 'https://lora.algokit.io/testnet/account'
// Close dropdown when clicking outside
useEffect(() => {
const handleClickOutside = (e: MouseEvent) => {
@ -42,22 +43,10 @@ export function Web3AuthButton() {
// Get address as string safely
const getAddressString = (): string => {
if (!algorandAccount?.address) return ''
// Handle if address is an object (like from algosdk with publicKey property)
if (typeof algorandAccount.address === 'object' && algorandAccount.address !== null) {
// If it has a toString method, use it
if ('toString' in algorandAccount.address && typeof algorandAccount.address === 'function') {
return algorandAccount.address
}
// If it has an addr property (algosdk Account object)
if ('addr' in algorandAccount.address) {
return String(algorandAccount.address)
}
return ''
}
return String(algorandAccount.address)
}
const getLoraAccountUrl = (address: string) => `${LORA_ACCOUNT_BASE}/${address}`
// Handle login with error feedback
const handleLogin = async () => {
@ -135,13 +124,14 @@ export function Web3AuthButton() {
if (algorandAccount && isConnected) {
const address = getAddressString()
const firstLetter = address ? address[0].toUpperCase() : 'A'
const loraUrl = address ? getLoraAccountUrl(address) : '#'
return (
<div className="dropdown dropdown-end" data-dropdown>
<button
onClick={() => setIsDropdownOpen(!isDropdownOpen)}
className="btn btn-sm btn-ghost gap-2 hover:bg-base-200"
title={`Connected: ${address} ${userInfo?.email}`}
title={`Connected: ${address} ${userInfo?.email ?? ''}`}
>
<div className="flex items-center gap-2">
{/* Profile picture - always show first letter of address */}
@ -156,7 +146,10 @@ export function Web3AuthButton() {
{firstLetter}
</div>
)}
{/* Keep address visible in navbar, but not a link here (button toggles dropdown) */}
<span className="font-mono text-sm font-medium">{address}</span>
<svg
className={`w-4 h-4 transition-transform ${isDropdownOpen ? 'rotate-180' : ''}`}
fill="none"
@ -176,7 +169,11 @@ export function Web3AuthButton() {
<li className="menu-title px-3 py-2">
<div className="flex items-center gap-3">
{userInfo.profileImage ? (
<img src={userInfo.profileImage} alt="Profile" className="w-10 h-10 rounded-full object-cover ring-2 ring-primary" />
<img
src={userInfo.profileImage}
alt="Profile"
className="w-10 h-10 rounded-full object-cover ring-2 ring-primary"
/>
) : (
<div className="w-10 h-10 rounded-full bg-primary text-primary-content flex items-center justify-center text-lg font-bold">
{firstLetter}
@ -196,8 +193,20 @@ export function Web3AuthButton() {
<li className="menu-title px-3">
<span className="text-xs uppercase">Algorand Address</span>
</li>
{/* Clickable address -> Lora */}
<li>
<div className="bg-base-200 rounded-lg p-2 font-mono text-xs break-all cursor-default hover:bg-base-200">{address}</div>
<a
href={loraUrl}
target="_blank"
rel="noopener noreferrer"
className="bg-base-200 rounded-lg p-2 font-mono text-xs break-all hover:bg-base-300 transition"
title="View account on Lora explorer"
onClick={() => setIsDropdownOpen(false)}
>
{address}
<span className="ml-2 opacity-70"></span>
</a>
</li>
{/* Copy Address Button */}
@ -217,6 +226,21 @@ export function Web3AuthButton() {
</button>
</li>
{/* Explicit "View on Lora" CTA (extra clarity) */}
<li>
<a
href={loraUrl}
target="_blank"
rel="noopener noreferrer"
className="text-sm gap-2"
title="Open in Lora explorer"
onClick={() => setIsDropdownOpen(false)}
>
<span>View on Lora</span>
<span className="opacity-70"></span>
</a>
</li>
<div className="divider my-1"></div>
{/* Disconnect Button */}

View File

@ -64,6 +64,7 @@ export function Web3AuthProvider({ children }: { children: ReactNode }) {
console.error('🎯 Failed to fetch user info:', err)
}
}
setIsInitialized(true)
} catch (err) {
const errorMessage = err instanceof Error ? err.message : 'Failed to initialize Web3Auth'
@ -125,6 +126,7 @@ export function Web3AuthProvider({ children }: { children: ReactNode }) {
setIsConnected(false)
setProvider(null)
setAlgorandAccount(null)
setUserInfo(null)
} finally {
setIsLoading(false)
}
@ -137,10 +139,18 @@ export function Web3AuthProvider({ children }: { children: ReactNode }) {
await logoutFromWeb3Auth()
// Clear React state
setProvider(null)
setIsConnected(false)
setAlgorandAccount(null)
setUserInfo(null)
/**
* ✅ Fix A (most reliable for templates):
* Force a full refresh after logout so Web3Auth doesn't get stuck
* in an in-between cached state (e.g. button stuck on "Connecting...").
*/
window.location.reload()
} catch (err) {
console.error('🎯 LOGOUT: Error:', err)
setError(err instanceof Error ? err.message : 'Logout failed')