Skip to content

Commit ca32e32

Browse files
authored
Sammajayi/connect a wallet fixes (#135)
* fix: UI polish and lint fixes for connect-a-wallet-widget - Fix missing Alert import crash on error/unsupported states - Remove unused address prop from ChainLinkRow (lint fix) - Restructure PrimaryIdentityCard layout (shield/text/copy) - Reduce GlowCard shadow opacity and radius - Update unlink SVG path to match lucide icon - Add flexWrap to ChainLinkRow for 320px viewport support - Reduce input/button sizes for small viewports - Center address in verified identity box with wide gap - Adjust heading spacing and checkmark position - Update Playwright test screenshots * fix: address Copilot review findings on connect-a-wallet-widget Adds a changeSecondaryAddress adapter action and "Change address" button so users can check a different address without reloading, replaces a hardcoded RGBA background with the $infoMuted token, and swaps the copy-address control for an accessible, focusable Button. * fix: address pheobeayo review findings on connect-a-wallet-widget Remove unused ChainRowCard export, disambiguate the duplicate "Connect or Disconnect Address" heading, swap hover-tracked useState for hoverStyle on the disconnect button (RN Web/touch compatible), and clear the copy-feedback timeout on unmount. * fix: restore hover text contrast on disconnect button hoverStyle only flips the background to red; icon/label were still hardcoded to error color, making them invisible on hover. Reintroduce a small hover flag via onHoverIn/onHoverOut to swap text/icon to white on hover, per pheobeayo's review. * test: refresh connect-a-wallet-widget screenshots and fix stale spec assertion The ready-state heading changed to 'Linked address' in earlier review-fix commits; update the unsupported-network assertion and regenerate caw-05, 06, 08 and 09 so the committed evidence matches the current UI. * fix: clear pending copy timeout before scheduling a new one
1 parent 5107fa7 commit ca32e32

20 files changed

Lines changed: 376 additions & 96 deletions

‎examples/storybook/src/stories/helpers/connectAWalletWidgetStories.tsx‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ function createAdapter(
6363
connectWallet: async () => {},
6464
setSecondaryAddressInput: () => {},
6565
checkSecondaryAddress: async () => {},
66+
changeSecondaryAddress: () => {},
6667
connectChain: async () => {},
6768
disconnectChain: async () => {},
6869
...actionOverrides,

‎packages/connect-a-wallet-widget/src/adapter.ts‎

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,16 @@ export function useConnectAWalletAdapter(
221221
setSecondaryAddressInputState(value)
222222
}, [])
223223

224+
// Lets the user re-open the address form from the 'ready' state to check a
225+
// different address, since there's otherwise no way back to it once a
226+
// secondary address has been validated.
227+
const changeSecondaryAddress = useCallback(() => {
228+
setSecondaryAddress(null)
229+
setSecondaryAddressInputState('')
230+
setError(null)
231+
setStatus('connected_no_input')
232+
}, [])
233+
224234
const handleConnectWallet = useCallback(async (): Promise<void> => {
225235
setIsConnectingWallet(true)
226236
try {
@@ -402,10 +412,18 @@ export function useConnectAWalletAdapter(
402412
connectWallet: handleConnectWallet,
403413
setSecondaryAddressInput,
404414
checkSecondaryAddress,
415+
changeSecondaryAddress,
405416
connectChain,
406417
disconnectChain,
407418
}),
408-
[handleConnectWallet, setSecondaryAddressInput, checkSecondaryAddress, connectChain, disconnectChain],
419+
[
420+
handleConnectWallet,
421+
setSecondaryAddressInput,
422+
checkSecondaryAddress,
423+
changeSecondaryAddress,
424+
connectChain,
425+
disconnectChain,
426+
],
409427
)
410428

411429
return { state, actions }

‎packages/connect-a-wallet-widget/src/components/AddressLinkForm.tsx‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ export function AddressLinkForm({
1717
}: AddressLinkFormProps) {
1818
return (
1919
<AddressFormCard>
20-
<Heading level={5}>Wallet address to link</Heading>
21-
<Text secondary>
20+
<Heading level={6} fontSize="$1">Connect or Disconnect Address</Heading>
21+
<Text fontSize="$1" secondary>
2222
Enter the address you want to connect to or disconnect from your GoodID, then check its
2323
status on each supported chain.
2424
</Text>
@@ -27,9 +27,10 @@ export function AddressLinkForm({
2727
onChangeText={onChangeAddressInput}
2828
placeholder="0x…"
2929
disabled={isChecking}
30+
size="sm"
3031
/>
31-
<ActionButton onPress={onCheckAddress} disabled={isChecking || !addressInput} fullWidth>
32-
{isChecking ? <Spinner size="sm" /> : <ButtonText>Check address</ButtonText>}
32+
<ActionButton onPress={onCheckAddress} disabled={isChecking || !addressInput} fullWidth size="sm">
33+
{isChecking ? <Spinner size="sm" /> : <ButtonText fontSize="$1">Check address</ButtonText>}
3334
</ActionButton>
3435
</AddressFormCard>
3536
)
Lines changed: 105 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
import React from 'react'
2-
import { AddressDisplay, Badge, BadgeText, ButtonText, ChainBadge, Spinner } from '@goodwidget/ui'
1+
import React, { useState } from 'react'
2+
import { ButtonText, Icon, Spinner, Stack, Text, XStack, YStack } from '@goodwidget/ui'
33
import type { ConnectAWalletChainLinkState } from '../widgetRuntimeContract'
44
import { chainLinkRowPresentation } from './format'
5-
import { ActionButton, ChainRowCard } from './shared'
5+
import { ActionButton } from './shared'
66

77
interface ChainLinkRowProps {
88
row: ConnectAWalletChainLinkState
9-
address: `0x${string}`
109
onConnect: () => void
1110
onDisconnect: () => void
1211
}
@@ -16,24 +15,111 @@ interface ChainLinkRowProps {
1615
* Disconnect (never hidden) with a Spinner while a status is in flight, per
1716
* Bounty Lead sign-off on the human-reviewer checklist.
1817
*/
19-
export function ChainLinkRow({ row, address, onConnect, onDisconnect }: ChainLinkRowProps) {
18+
export function ChainLinkRow({ row, onConnect, onDisconnect }: ChainLinkRowProps) {
2019
const { actionLabel, isBusy, isDisabled } = chainLinkRowPresentation(row.status)
2120
const handlePress = actionLabel === 'Connect' ? onConnect : onDisconnect
21+
const [disconnectHovered, setDisconnectHovered] = useState(false)
22+
23+
// Map status to dot color and display text
24+
let dotColor = '$placeholderColor'
25+
let statusText = 'Not Connected'
26+
27+
if (row.status === 'connected') {
28+
dotColor = '$success'
29+
statusText = 'Connected'
30+
} else if (row.status === 'connecting') {
31+
statusText = 'Connecting...'
32+
} else if (row.status === 'disconnecting') {
33+
statusText = 'Disconnecting...'
34+
} else if (row.status === 'checking') {
35+
statusText = 'Checking...'
36+
}
37+
38+
const isDisconnect = actionLabel === 'Disconnect'
2239

2340
return (
24-
<ChainRowCard>
25-
<ChainBadge chainId={row.chainId} name={row.chainName} />
26-
<AddressDisplay address={address} size="sm" />
27-
<Badge type={row.status === 'connected' ? 'success' : 'info'}>
28-
<BadgeText>{row.status === 'checking' ? 'checking…' : row.status.replace('_', ' ')}</BadgeText>
29-
</Badge>
30-
<ActionButton
31-
onPress={handlePress}
32-
disabled={isDisabled}
33-
variant={actionLabel === 'Disconnect' ? 'outline' : 'primary'}
34-
>
35-
{isBusy ? <Spinner size="sm" /> : <ButtonText>{actionLabel}</ButtonText>}
36-
</ActionButton>
37-
</ChainRowCard>
41+
<XStack
42+
padding="$4"
43+
alignItems="center"
44+
justifyContent="space-between"
45+
gap="$3"
46+
borderBottomWidth={1}
47+
borderBottomColor="$borderColor"
48+
flexWrap="wrap"
49+
>
50+
<XStack alignItems="center" gap="$2" flex={1} minWidth={0}>
51+
<Stack
52+
width={32}
53+
height={32}
54+
borderRadius="$full"
55+
backgroundColor="$backgroundPress"
56+
alignItems="center"
57+
justifyContent="center"
58+
flexShrink={0}
59+
>
60+
<Text fontWeight="700" fontSize="$1" color="$color">
61+
{row.chainName.charAt(0)}
62+
</Text>
63+
</Stack>
64+
65+
<YStack gap="$0.5" flex={1} minWidth={0}>
66+
<Text fontWeight="700" fontSize="$1" color="$color" numberOfLines={1}>
67+
{row.chainName}
68+
</Text>
69+
<XStack alignItems="center" gap="$1">
70+
<Stack width={5} height={5} borderRadius={3} backgroundColor={dotColor} flexShrink={0} />
71+
<Text fontSize="$1" fontWeight="600" color="$placeholderColor">
72+
{statusText}
73+
</Text>
74+
</XStack>
75+
</YStack>
76+
</XStack>
77+
78+
{isBusy ? (
79+
<ActionButton
80+
disabled
81+
variant={isDisconnect ? 'outline' : 'primary'}
82+
borderColor={isDisconnect ? '$error' : undefined}
83+
paddingHorizontal="$3.5"
84+
flexShrink={0}
85+
>
86+
<Spinner size="sm" />
87+
</ActionButton>
88+
) : isDisconnect ? (
89+
<ActionButton
90+
onPress={handlePress}
91+
disabled={isDisabled}
92+
variant="outline"
93+
borderColor="$error"
94+
hoverStyle={{ backgroundColor: '$error' }}
95+
onHoverIn={() => setDisconnectHovered(true)}
96+
onHoverOut={() => setDisconnectHovered(false)}
97+
paddingHorizontal="$2.5"
98+
paddingVertical="$1.5"
99+
flexShrink={0}
100+
>
101+
<XStack alignItems="center" gap="$1">
102+
<Icon name="unlink" size="xs" color={disconnectHovered ? 'white' : 'error'} />
103+
<ButtonText fontSize="$1" color={disconnectHovered ? '$white' : '$error'}>
104+
Disconnect
105+
</ButtonText>
106+
</XStack>
107+
</ActionButton>
108+
) : (
109+
<ActionButton
110+
onPress={handlePress}
111+
disabled={isDisabled}
112+
variant="primary"
113+
paddingHorizontal="$2.5"
114+
paddingVertical="$1.5"
115+
flexShrink={0}
116+
>
117+
<XStack alignItems="center" gap="$1">
118+
<Icon name="link" size="xs" color="white" />
119+
<ButtonText fontSize="$1" color="$white">Connect</ButtonText>
120+
</XStack>
121+
</ActionButton>
122+
)}
123+
</XStack>
38124
)
39125
}

0 commit comments

Comments
 (0)