Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ function createAdapter(
connectWallet: async () => {},
setSecondaryAddressInput: () => {},
checkSecondaryAddress: async () => {},
changeSecondaryAddress: () => {},
connectChain: async () => {},
disconnectChain: async () => {},
...actionOverrides,
Expand Down
20 changes: 19 additions & 1 deletion packages/connect-a-wallet-widget/src/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,16 @@ export function useConnectAWalletAdapter(
setSecondaryAddressInputState(value)
}, [])

// Lets the user re-open the address form from the 'ready' state to check a
// different address, since there's otherwise no way back to it once a
// secondary address has been validated.
const changeSecondaryAddress = useCallback(() => {
setSecondaryAddress(null)
setSecondaryAddressInputState('')
setError(null)
setStatus('connected_no_input')
}, [])

const handleConnectWallet = useCallback(async (): Promise<void> => {
setIsConnectingWallet(true)
try {
Expand Down Expand Up @@ -402,10 +412,18 @@ export function useConnectAWalletAdapter(
connectWallet: handleConnectWallet,
setSecondaryAddressInput,
checkSecondaryAddress,
changeSecondaryAddress,
connectChain,
disconnectChain,
}),
[handleConnectWallet, setSecondaryAddressInput, checkSecondaryAddress, connectChain, disconnectChain],
[
handleConnectWallet,
setSecondaryAddressInput,
checkSecondaryAddress,
changeSecondaryAddress,
connectChain,
disconnectChain,
],
)

return { state, actions }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ export function AddressLinkForm({
}: AddressLinkFormProps) {
return (
<AddressFormCard>
<Heading level={5}>Wallet address to link</Heading>
<Text secondary>
<Heading level={6} fontSize="$1">Connect or Disconnect Address</Heading>
<Text fontSize="$1" secondary>
Enter the address you want to connect to or disconnect from your GoodID, then check its
status on each supported chain.
</Text>
Expand All @@ -27,9 +27,10 @@ export function AddressLinkForm({
onChangeText={onChangeAddressInput}
placeholder="0x…"
disabled={isChecking}
size="sm"
/>
<ActionButton onPress={onCheckAddress} disabled={isChecking || !addressInput} fullWidth>
{isChecking ? <Spinner size="sm" /> : <ButtonText>Check address</ButtonText>}
<ActionButton onPress={onCheckAddress} disabled={isChecking || !addressInput} fullWidth size="sm">
{isChecking ? <Spinner size="sm" /> : <ButtonText fontSize="$1">Check address</ButtonText>}
</ActionButton>
</AddressFormCard>
)
Expand Down
124 changes: 105 additions & 19 deletions packages/connect-a-wallet-widget/src/components/ChainLinkRow.tsx
Comment thread
L03TJ3 marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import React from 'react'
import { AddressDisplay, Badge, BadgeText, ButtonText, ChainBadge, Spinner } from '@goodwidget/ui'
import React, { useState } from 'react'
import { ButtonText, Icon, Spinner, Stack, Text, XStack, YStack } from '@goodwidget/ui'
import type { ConnectAWalletChainLinkState } from '../widgetRuntimeContract'
import { chainLinkRowPresentation } from './format'
import { ActionButton, ChainRowCard } from './shared'
import { ActionButton } from './shared'

interface ChainLinkRowProps {
row: ConnectAWalletChainLinkState
address: `0x${string}`
onConnect: () => void
onDisconnect: () => void
}
Expand All @@ -16,24 +15,111 @@ interface ChainLinkRowProps {
* Disconnect (never hidden) with a Spinner while a status is in flight, per
* Bounty Lead sign-off on the human-reviewer checklist.
*/
export function ChainLinkRow({ row, address, onConnect, onDisconnect }: ChainLinkRowProps) {
export function ChainLinkRow({ row, onConnect, onDisconnect }: ChainLinkRowProps) {
const { actionLabel, isBusy, isDisabled } = chainLinkRowPresentation(row.status)
const handlePress = actionLabel === 'Connect' ? onConnect : onDisconnect
const [disconnectHovered, setDisconnectHovered] = useState(false)

// Map status to dot color and display text
let dotColor = '$placeholderColor'
let statusText = 'Not Connected'

if (row.status === 'connected') {
dotColor = '$success'
statusText = 'Connected'
} else if (row.status === 'connecting') {
statusText = 'Connecting...'
} else if (row.status === 'disconnecting') {
statusText = 'Disconnecting...'
} else if (row.status === 'checking') {
statusText = 'Checking...'
}

const isDisconnect = actionLabel === 'Disconnect'

return (
<ChainRowCard>
<ChainBadge chainId={row.chainId} name={row.chainName} />
<AddressDisplay address={address} size="sm" />
<Badge type={row.status === 'connected' ? 'success' : 'info'}>
<BadgeText>{row.status === 'checking' ? 'checking…' : row.status.replace('_', ' ')}</BadgeText>
</Badge>
<ActionButton
onPress={handlePress}
disabled={isDisabled}
variant={actionLabel === 'Disconnect' ? 'outline' : 'primary'}
>
{isBusy ? <Spinner size="sm" /> : <ButtonText>{actionLabel}</ButtonText>}
</ActionButton>
</ChainRowCard>
<XStack
padding="$4"
alignItems="center"
justifyContent="space-between"
gap="$3"
borderBottomWidth={1}
borderBottomColor="$borderColor"
flexWrap="wrap"
>
<XStack alignItems="center" gap="$2" flex={1} minWidth={0}>
<Stack
width={32}
height={32}
borderRadius="$full"
backgroundColor="$backgroundPress"
alignItems="center"
justifyContent="center"
flexShrink={0}
>
<Text fontWeight="700" fontSize="$1" color="$color">
{row.chainName.charAt(0)}
</Text>
</Stack>

<YStack gap="$0.5" flex={1} minWidth={0}>
<Text fontWeight="700" fontSize="$1" color="$color" numberOfLines={1}>
{row.chainName}
</Text>
<XStack alignItems="center" gap="$1">
<Stack width={5} height={5} borderRadius={3} backgroundColor={dotColor} flexShrink={0} />
<Text fontSize="$1" fontWeight="600" color="$placeholderColor">
{statusText}
</Text>
</XStack>
</YStack>
</XStack>

{isBusy ? (
<ActionButton
disabled
variant={isDisconnect ? 'outline' : 'primary'}
borderColor={isDisconnect ? '$error' : undefined}
paddingHorizontal="$3.5"
flexShrink={0}
>
<Spinner size="sm" />
</ActionButton>
) : isDisconnect ? (
<ActionButton
onPress={handlePress}
disabled={isDisabled}
variant="outline"
borderColor="$error"
hoverStyle={{ backgroundColor: '$error' }}
onHoverIn={() => setDisconnectHovered(true)}
onHoverOut={() => setDisconnectHovered(false)}
paddingHorizontal="$2.5"
paddingVertical="$1.5"
flexShrink={0}
>
Comment thread
L03TJ3 marked this conversation as resolved.
<XStack alignItems="center" gap="$1">
<Icon name="unlink" size="xs" color={disconnectHovered ? 'white' : 'error'} />
<ButtonText fontSize="$1" color={disconnectHovered ? '$white' : '$error'}>
Disconnect
</ButtonText>
</XStack>
</ActionButton>
) : (
<ActionButton
onPress={handlePress}
disabled={isDisabled}
variant="primary"
paddingHorizontal="$2.5"
paddingVertical="$1.5"
flexShrink={0}
>
<XStack alignItems="center" gap="$1">
<Icon name="link" size="xs" color="white" />
<ButtonText fontSize="$1" color="$white">Connect</ButtonText>
</XStack>
</ActionButton>
)}
</XStack>
)
}
Loading