diff --git a/packages/connect-a-wallet-widget/src/components/AddressLinkForm.tsx b/packages/connect-a-wallet-widget/src/components/AddressLinkForm.tsx
index 4a058de2..60ece13f 100644
--- a/packages/connect-a-wallet-widget/src/components/AddressLinkForm.tsx
+++ b/packages/connect-a-wallet-widget/src/components/AddressLinkForm.tsx
@@ -17,8 +17,8 @@ export function AddressLinkForm({
}: AddressLinkFormProps) {
return (
- Wallet address to link
-
+ Connect or Disconnect Address
+
Enter the address you want to connect to or disconnect from your GoodID, then check its
status on each supported chain.
@@ -27,9 +27,10 @@ export function AddressLinkForm({
onChangeText={onChangeAddressInput}
placeholder="0x…"
disabled={isChecking}
+ size="sm"
/>
-
- {isChecking ? : Check address}
+
+ {isChecking ? : Check address}
)
diff --git a/packages/connect-a-wallet-widget/src/components/ChainLinkRow.tsx b/packages/connect-a-wallet-widget/src/components/ChainLinkRow.tsx
index c0d41eeb..c341850c 100644
--- a/packages/connect-a-wallet-widget/src/components/ChainLinkRow.tsx
+++ b/packages/connect-a-wallet-widget/src/components/ChainLinkRow.tsx
@@ -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
}
@@ -16,24 +15,109 @@ 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 (
-
-
-
-
- {row.status === 'checking' ? 'checking…' : row.status.replace('_', ' ')}
-
-
- {isBusy ? : {actionLabel}}
-
-
+
+
+
+
+ {row.chainName.charAt(0)}
+
+
+
+
+
+ {row.chainName}
+
+
+
+
+ {statusText}
+
+
+
+
+
+ {isBusy ? (
+
+
+
+ ) : isDisconnect ? (
+ setDisconnectHovered(true)}
+ onMouseLeave={() => setDisconnectHovered(false)}
+ >
+
+
+ Disconnect
+
+
+ ) : (
+
+
+
+ Connect
+
+
+ )}
+
)
}
diff --git a/packages/connect-a-wallet-widget/src/components/ConnectAWalletWidgetView.tsx b/packages/connect-a-wallet-widget/src/components/ConnectAWalletWidgetView.tsx
index 01a100d2..adb7a29f 100644
--- a/packages/connect-a-wallet-widget/src/components/ConnectAWalletWidgetView.tsx
+++ b/packages/connect-a-wallet-widget/src/components/ConnectAWalletWidgetView.tsx
@@ -1,5 +1,17 @@
import React from 'react'
-import { Alert, Badge, BadgeText, ButtonText, Heading, Spinner, XStack } from '@goodwidget/ui'
+import {
+ AddressDisplay,
+ Alert,
+ ButtonText,
+ Card,
+ Heading,
+ Icon,
+ Spinner,
+ Stack,
+ Text,
+ XStack,
+ YStack,
+} from '@goodwidget/ui'
import type { ConnectAWalletWidgetAdapterResult } from '../widgetRuntimeContract'
import { AddressLinkForm } from './AddressLinkForm'
import { ChainLinkRow } from './ChainLinkRow'
@@ -7,6 +19,12 @@ import { PrimaryIdentityCard } from './PrimaryIdentityCard'
import { ActionButton, EmptyStateCard, WidgetContent } from './shared'
import { WalletGate } from './WalletGate'
+const CHAIN_NAMES: Record = {
+ 122: 'Fuse',
+ 42220: 'Celo',
+ 50: 'XDC',
+}
+
interface ConnectAWalletWidgetViewProps {
adapter: ConnectAWalletWidgetAdapterResult
'data-testid'?: string
@@ -22,16 +40,61 @@ export function ConnectAWalletWidgetView({
// header rather than relying on the host shell for it (see
// StreamingWidgetView) — matches the #113 design reference's top bar.
const header = (
-
- GoodDollar
- {state.activeChainId && (
-
- Chain {state.activeChainId}
-
- )}
+
+
+ GoodDollar
+ {state.activeChainId && (
+
+
+ {CHAIN_NAMES[state.activeChainId] ?? `Chain ${state.activeChainId}`}
+
+
+ )}
+
+
+
+ Connect identity
+
+
+
+
+ )
+
+ const infoCallout = (
+
+
+
+
+ You can connect multiple wallet addresses.
+
+
+ However, only one claim per day is available, shared between the connected accounts.
+
+
)
+ const footer = (
+
+ Supported Networks: Celo, XDC, Fuse
+
+ )
+
if (!state.isWalletConnected) {
return (
@@ -45,10 +108,14 @@ export function ConnectAWalletWidgetView({
)
}
+ const showForm = state.status !== 'error' && !(state.status === 'ready' && state.secondaryAddress)
+
return (
{header}
+ {infoCallout}
+
{!state.isActiveChainSupported && (
@@ -61,17 +128,17 @@ export function ConnectAWalletWidgetView({
{state.status === 'error' && (
-
+
Couldn't load link status
-
- Retry
+
+ Retry
)}
- {state.status !== 'error' && (
+ {showForm && (
- {state.chainLinks.map((row) => (
- actions.connectChain(row.chainId)}
- onDisconnect={() => actions.disconnectChain(row.chainId)}
- />
- ))}
+ Connect or Disconnect Address
+
+
+
+
+
+
+
+
+
+
+ {state.chainLinks.map((row) => (
+ actions.connectChain(row.chainId)}
+ onDisconnect={() => actions.disconnectChain(row.chainId)}
+ />
+ ))}
+
>
)}
+
+ {footer}
)
}
diff --git a/packages/connect-a-wallet-widget/src/components/PrimaryIdentityCard.tsx b/packages/connect-a-wallet-widget/src/components/PrimaryIdentityCard.tsx
index 75e150b3..9f0e0240 100644
--- a/packages/connect-a-wallet-widget/src/components/PrimaryIdentityCard.tsx
+++ b/packages/connect-a-wallet-widget/src/components/PrimaryIdentityCard.tsx
@@ -1,25 +1,5 @@
-import React from 'react'
-import { AddressDisplay, Icon, Text, XStack, YStack, createComponent } from '@goodwidget/ui'
-
-const IdentityIconBadge = createComponent(YStack, {
- name: 'PrimaryIdentityIconBadge',
- width: 36,
- height: 36,
- borderRadius: '$full',
- alignItems: 'center',
- justifyContent: 'center',
- backgroundColor: '$infoMuted',
-})
-
-const IdentityCard = createComponent(XStack, {
- name: 'PrimaryIdentityCard',
- alignItems: 'center',
- gap: '$3',
- padding: '$3',
- borderRadius: '$3',
- borderWidth: 1,
- borderColor: '$borderColor',
-})
+import React, { useState } from 'react'
+import { AddressDisplay, GlowCard, Icon, Text, XStack, YStack, copyTextToClipboard } from '@goodwidget/ui'
interface PrimaryIdentityCardProps {
walletAddress: string | null
@@ -31,21 +11,61 @@ interface PrimaryIdentityCardProps {
* the "Primary Verified Identity" card in the #113 design reference.
*/
export function PrimaryIdentityCard({ walletAddress }: PrimaryIdentityCardProps) {
+ const [copied, setCopied] = useState(false)
+
if (!walletAddress) {
return null
}
+ const handleCopy = async () => {
+ const success = await copyTextToClipboard(walletAddress)
+ if (success) {
+ setCopied(true)
+ setTimeout(() => setCopied(false), 2000)
+ }
+ }
+
return (
-
-
-
-
-
-
- Primary verified identity
-
-
-
-
+
+
+
+
+
+
+
+ Primary Verified Identity
+
+
+
+
+
+
+
+
)
}
diff --git a/packages/connect-a-wallet-widget/src/components/shared.tsx b/packages/connect-a-wallet-widget/src/components/shared.tsx
index a7924a07..1b1b8d87 100644
--- a/packages/connect-a-wallet-widget/src/components/shared.tsx
+++ b/packages/connect-a-wallet-widget/src/components/shared.tsx
@@ -22,6 +22,7 @@ export const ChainRowCard = createComponent(Card, {
flexDirection: 'row' as const,
alignItems: 'center' as const,
justifyContent: 'space-between' as const,
+ flexWrap: 'wrap' as const,
gap: '$2',
})
@@ -31,9 +32,9 @@ export const AddressFormCard = createComponent(Card, {
gap: '$3',
})
-export function ActionButton({ children, minWidth = 108, size = 'sm', ...props }: ButtonProps) {
+export function ActionButton({ children, size = 'sm', ...props }: ButtonProps) {
return (
-