Skip to content

Commit

Permalink
feat: wallet button (#3415)
Browse files Browse the repository at this point in the history
Co-authored-by: tomiir <rocchitomas@gmail.com>
  • Loading branch information
magiziz and tomiir authored Dec 10, 2024
1 parent 50c619a commit 9e1e4c9
Show file tree
Hide file tree
Showing 58 changed files with 2,051 additions and 386 deletions.
88 changes: 88 additions & 0 deletions .changeset/sour-berries-shout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
---
'@reown/appkit-adapter-solana': patch
'@reown/appkit-wallet-button': patch
'@reown/appkit-utils': patch
'@reown/appkit-scaffold-ui': patch
'@reown/appkit-common': patch
'@reown/appkit-core': patch
'@reown/appkit-siwx': patch
'@reown/appkit-ui': patch
'@apps/builder': patch
'@reown/appkit-adapter-ethers': patch
'@reown/appkit-adapter-ethers5': patch
'@reown/appkit-adapter-wagmi': patch
'@reown/appkit': patch
'@reown/appkit-cdn': patch
'appkit-cli': patch
'@reown/appkit-experimental': patch
'@reown/appkit-polyfills': patch
'@reown/appkit-siwe': patch
'@reown/appkit-wallet': patch
---

Introduced wallet button component and custom hook for headless integration.

Components example:
```tsx
import '@reown/appkit-wallet-button'

export function YourApp() {
return (
<>
{/* QR Code (WalletConnect) */}
<appkit-wallet-button wallet="walletConnect" />

{/* Wallets */}
<appkit-wallet-button wallet="metamask" />
<appkit-wallet-button wallet="trust" />
<appkit-wallet-button wallet="coinbase" />

{/* Socials */}
<appkit-wallet-button wallet="google" />
<appkit-wallet-button wallet="x" />
<appkit-wallet-button wallet="discord" />
<appkit-wallet-button wallet="farcaster" />
</>
)
}
```

Hook example:
```tsx
import { useAppKitWallet } from '@reown/appkit-wallet-button/react'

export function YourApp() {
const { data, error, isPending, isSuccess, isError, connect } = useAppKitWallet({
onError: err => {
// ...
},
onSuccess: data => {
// ...
}
})

return (
<>
<button onClick={() => connect('walletConnect')}>Open QR modal</button>
<button onClick={() => connect('metamask')}>Connect to MetaMask</button>
<button onClick={() => connect('google')}>Connect to Google</button>
</>
)
}
```

Additionally a new theme variable property called `--w3m-qr-color` was added where you can configure a custom color for the QR code.

```tsx
import { createAppKit } from '@reown/appkit/react'

const modal = createAppKit({
/* Your Config */
themeVariables: {
'--w3m-qr-color': '...', // Optional
'--w3m-color-mix': '...',
'--w3m-color-mix-strength': 50
}
})

```
5 changes: 5 additions & 0 deletions apps/gallery/stories/composites/wui-qr-code.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import '@reown/appkit-ui/src/composites/wui-qr-code'
import type { WuiQrCode } from '@reown/appkit-ui/src/composites/wui-qr-code'
import { html } from 'lit'
import { themeOptions, walletImageSrc } from '../../utils/PresetUtils'
import { ifDefined } from 'lit/directives/if-defined.js'

type Component = Meta<WuiQrCode>

Expand All @@ -19,6 +20,9 @@ export default {
theme: {
options: themeOptions,
control: { type: 'select' }
},
color: {
control: { type: 'color' }
}
}
} as Component
Expand All @@ -31,5 +35,6 @@ export const Default: Component = {
theme=${args.theme}
uri=${args.uri}
size=${args.size}
color=${ifDefined(args.color)}
></wui-qr-code>`
}
56 changes: 56 additions & 0 deletions apps/gallery/stories/composites/wui-wallet-button.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import type { Meta } from '@storybook/web-components'
import '@reown/appkit-ui/src/composites/wui-wallet-button'
import { html } from 'lit'
import { iconOptions, walletImageSrc } from '../../utils/PresetUtils'
import type { WuiWalletButton } from '@reown/appkit-ui/dist/types/src/composites/wui-wallet-button'

type Component = Meta<WuiWalletButton>

export default {
title: 'Composites/wui-wallet-button',
args: {
name: 'Rainbow',
imageSrc: walletImageSrc,
loading: false,
error: false,
icon: undefined,
disabled: false
},
argTypes: {
name: {
control: { type: 'text' }
},
imageSrc: {
control: { type: 'text' }
},
loading: {
control: { type: 'boolean' }
},
error: {
control: { type: 'boolean' }
},
icon: {
options: [undefined, ...iconOptions],
control: { type: 'select' }
},
disabled: {
control: { type: 'boolean' }
},
size: {
options: ['md', 'lg'],
control: { type: 'select' }
}
}
} as Component

export const Default: Component = {
render: args =>
html`<wui-wallet-button
.imageSrc=${args.imageSrc}
.icon=${args.icon}
name=${args.name}
?loading=${args.loading}
?error=${args.error}
?disabled=${args.disabled}
></wui-wallet-button>`
}
3 changes: 3 additions & 0 deletions apps/laboratory/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"playwright:test:metamask": "playwright test --grep 'metamask.spec.ts'",
"playwright:test:mobile-wallet-features": "playwright test --grep 'mobile-wallet-features.spec.ts'",
"playwright:test:basic": "playwright test --grep 'basic-tests.spec.ts'",
"playwright:test:wallet-button": "playwright test --grep 'wallet-button.spec.ts'",
"playwright:test:wallet": "playwright test --grep 'wallet.spec.ts'",
"playwright:test:no-email": "playwright test --grep 'no-email.spec.ts'",
"playwright:test:no-socials": "playwright test --grep 'no-socials.spec.ts'",
Expand Down Expand Up @@ -44,6 +45,7 @@
"playwright:debug:metamask": "pnpm playwright:test:metamask --debug",
"playwright:debug:mobile-wallet-features": "pnpm playwright:test:mobile-wallet-features --debug",
"playwright:debug:basic": "pnpm playwright:test:basic --debug",
"playwright:debug:wallet-button": "pnpm playwright:test:wallet-button --debug",
"playwright:debug:wallet": "pnpm playwright:test:wallet --debug",
"playwright:debug:no-email": "pnpm playwright:test:no-email --debug",
"playwright:debug:no-socials": "pnpm playwright:test:no-socials --debug",
Expand Down Expand Up @@ -86,6 +88,7 @@
"@reown/appkit-siwe": "workspace:*",
"@reown/appkit-siwx": "workspace:*",
"@reown/appkit-wallet": "workspace:*",
"@reown/appkit-wallet-button": "workspace:*",
"@sentry/browser": "7.119.1",
"@sentry/react": "7.92.0",
"@solana/wallet-adapter-wallets": "0.19.32",
Expand Down
117 changes: 117 additions & 0 deletions apps/laboratory/src/components/AppKitWalletButtons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import {
Button,
Card,
CardBody,
CardHeader,
Flex,
Heading,
Stack,
StackDivider
} from '@chakra-ui/react'
import * as AppKitWalletButton from '@reown/appkit-wallet-button/react'
import type { Wallet } from '@reown/appkit-wallet-button'
import { Fragment, useState } from 'react'
import { useAppKitAccount, type SocialProvider } from '@reown/appkit/react'
import { useChakraToast } from './Toast'
import { ConstantsUtil } from '../utils/ConstantsUtil'

interface AppKitWalletButtonsProps {
wallets: Wallet[]
}

interface WalletButtonHooksProps {
wallets: Wallet[]
}

interface WalletButtonComponentsProps {
wallets: Wallet[]
}

export function AppKitWalletButtons({ wallets }: AppKitWalletButtonsProps) {
return (
<Card marginTop={10} marginBottom={10}>
<CardHeader>
<Heading size="md">Wallet Buttons</Heading>
</CardHeader>

<CardBody>
<Stack divider={<StackDivider />} spacing="4" flexWrap="wrap">
<Flex flexDirection="column" gap="4">
<Heading size="xs" textTransform="uppercase">
Components
</Heading>

<Flex display="flex" flexWrap="wrap" gap="4">
<WalletButtonComponents wallets={wallets} />
</Flex>
</Flex>

<Flex flexDirection="column" gap="4">
<Heading size="xs" textTransform="uppercase">
Hooks Interactions
</Heading>

<Flex display="flex" flexWrap="wrap" gap="4">
<WalletButtonHooks wallets={wallets} />
</Flex>
</Flex>
</Stack>
</CardBody>
</Card>
)
}

function WalletButtonComponents({ wallets }: WalletButtonComponentsProps) {
return wallets.map(wallet => (
<Fragment key={`wallet-button-${wallet}`}>
<appkit-wallet-button wallet={wallet} data-testid={`wallet-button-${wallet}`} />
</Fragment>
))
}

function WalletButtonHooks({ wallets }: WalletButtonHooksProps) {
const [pendingWallet, setPendingWallet] = useState<Wallet>()

const toast = useChakraToast()

const { caipAddress } = useAppKitAccount()

const { isReady, isPending, connect } = AppKitWalletButton.useAppKitWallet({
onSuccess() {
setPendingWallet(undefined)
},
onError(error) {
toast({
title: 'Wallet Button',
description: error.message,
type: 'error'
})
setPendingWallet(undefined)
}
})

return wallets.map(wallet => {
const isSocial = ConstantsUtil.Socials.includes(wallet as SocialProvider)
const isWalletConnect = wallet === 'walletConnect'

const isWalletButtonDisabled = !isWalletConnect && !isSocial && !isReady

return (
<Button
key={`wallet-button-hook-${wallet}`}
onClick={() => {
setPendingWallet(wallet)
connect(wallet)
}}
maxW="fit-content"
size="md"
isLoading={isPending && pendingWallet === wallet}
isDisabled={Boolean(caipAddress) || isWalletButtonDisabled}
textTransform="capitalize"
data-testid={`wallet-button-hook-${wallet}`}
>
{wallet}
</Button>
)
})
}
40 changes: 40 additions & 0 deletions apps/laboratory/src/components/Theming/QrColorInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Grid, Heading, useRadioGroup } from '@chakra-ui/react'
import { colors } from '../../utils/DataUtil'
import RadioColor from './RadioColor'
import { ThemeStore } from '../../utils/StoreUtil'
import { useProxy } from 'valtio/utils'

export default function QrColorInput() {
const state = useProxy(ThemeStore.state)

function handleColorChange(e: string) {
ThemeStore.setQRColor(e)
}

const { getRootProps, getRadioProps } = useRadioGroup({
name: 'qrColor',
onChange: handleColorChange,
defaultValue: state.qrColor ? state.qrColor : undefined
})

const group = getRootProps()

return (
<>
<Heading size="sm" fontWeight="400" as="h2">
QR color
</Heading>
<Grid templateColumns="repeat(15, 1fr)" gap={2} {...group}>
{colors.map(value => {
const radio = getRadioProps({ value })

return (
<RadioColor key={value} {...radio}>
{value}
</RadioColor>
)
})}
</Grid>
</>
)
}
4 changes: 4 additions & 0 deletions apps/laboratory/src/layout/OptionsDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import BorderRadiusInput from '../components/Theming/BorderRadiusInput'
import MixColorInput from '../components/Theming/MixColorInput'
import { useEffect } from 'react'
import { ThemeStore } from '../utils/StoreUtil'
import QrColorInput from '../components/Theming/QrColorInput'

interface Props {
controls: ReturnType<typeof useDisclosure>
Expand Down Expand Up @@ -75,6 +76,9 @@ export function OptionsDrawer({ controls }: Props) {
<Flex gridGap="4" flexDirection="column">
<AccentColorInput />
</Flex>
<Flex gridGap="4" flexDirection="column">
<QrColorInput />
</Flex>
<Flex gridGap="4" flexDirection="column">
<BorderRadiusInput />
</Flex>
Expand Down
Loading

0 comments on commit 9e1e4c9

Please sign in to comment.