Skip to content

Commit bc1e27d

Browse files
Connect wallet (#74)
feat: connect a wallet --------- Co-authored-by: Neil Campbell <[email protected]>
1 parent 4669b29 commit bc1e27d

File tree

19 files changed

+1792
-75
lines changed

19 files changed

+1792
-75
lines changed

package-lock.json

Lines changed: 1362 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,12 @@
2424
"dependencies": {
2525
"@algorandfoundation/algokit-subscriber": "^1.3.0-beta.6",
2626
"@algorandfoundation/algokit-utils": "^6.0.4",
27+
"@blockshake/defly-connect": "^1.1.6",
28+
"@daffiwallet/connect": "^1.0.3",
29+
"@perawallet/connect": "^1.3.4",
2730
"@radix-ui/react-dialog": "^1.0.5",
2831
"@radix-ui/react-dropdown-menu": "^2.0.6",
32+
"@radix-ui/react-hover-card": "^1.0.7",
2933
"@radix-ui/react-icons": "^1.3.0",
3034
"@radix-ui/react-label": "^2.0.2",
3135
"@radix-ui/react-navigation-menu": "^1.1.4",
@@ -35,6 +39,7 @@
3539
"@radix-ui/react-tooltip": "^1.0.7",
3640
"@tanstack/react-table": "^8.16.0",
3741
"@tauri-apps/api": "^1.5.3",
42+
"@txnlab/use-wallet": "^2.8.2",
3843
"class-variance-authority": "^0.7.0",
3944
"clsx": "^2.1.0",
4045
"cmdk": "^1.0.0",
@@ -44,6 +49,7 @@
4449
"jotai": "^2.7.2",
4550
"jotai-effect": "^0.6.0",
4651
"lucide-react": "^0.356.0",
52+
"lute-connect": "^1.2.0",
4753
"multiformats": "^13.1.0",
4854
"react": "^18.2.0",
4955
"react-dom": "^18.2.0",
@@ -86,6 +92,7 @@
8692
"tailwindcss": "^3.4.1",
8793
"typescript": "^5.2.2",
8894
"vite": "^5.2.9",
95+
"vite-plugin-node-polyfills": "^0.22.0",
8996
"vitest": "^1.5.0"
9097
},
9198
"release": {

src/App.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,17 @@ import './App.css'
22
import { routes } from './App.routes'
33
import { RouterProvider, createBrowserRouter } from 'react-router-dom'
44
import { TooltipProvider } from './features/common/components/tooltip'
5-
import { DataProvider } from './features/common/components/data-provider'
5+
import { PlatformProvider } from './features/common/components/platform-provider'
66

77
const router = createBrowserRouter(routes)
88

99
function App() {
1010
return (
11-
<DataProvider>
11+
<PlatformProvider>
1212
<TooltipProvider>
1313
<RouterProvider router={router} />
1414
</TooltipProvider>
15-
</DataProvider>
15+
</PlatformProvider>
1616
)
1717
}
1818

src/features/accounts/components/account-link.tsx

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { cn } from '@/features/common/utils'
22
import { TemplatedNavLink } from '@/features/routing/components/templated-nav-link/templated-nav-link'
33
import { Urls } from '@/routes/urls'
44
import { ellipseAddress } from '@/utils/ellipse-address'
5+
import { fixedForwardRef } from '@/utils/fixed-forward-ref'
56
import { PropsWithChildren } from 'react'
67

78
type Props = PropsWithChildren<{
@@ -10,14 +11,18 @@ type Props = PropsWithChildren<{
1011
className?: string
1112
}>
1213

13-
export function AccountLink({ address, short, className, children }: Props) {
14-
return (
15-
<TemplatedNavLink
16-
className={cn(!children && 'text-primary underline', className)}
17-
urlTemplate={Urls.Explore.Account.ById}
18-
urlParams={{ address }}
19-
>
20-
{children ? children : short ? ellipseAddress(address) : address}
21-
</TemplatedNavLink>
22-
)
23-
}
14+
export const AccountLink = fixedForwardRef(
15+
({ address, short, className, children, ...rest }: Props, ref?: React.LegacyRef<HTMLAnchorElement>) => {
16+
return (
17+
<TemplatedNavLink
18+
className={cn(!children && 'text-primary underline', className)}
19+
urlTemplate={Urls.Explore.Account.ById}
20+
urlParams={{ address }}
21+
ref={ref}
22+
{...rest}
23+
>
24+
{children ? children : short ? ellipseAddress(address) : address}
25+
</TemplatedNavLink>
26+
)
27+
}
28+
)
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import * as React from 'react'
2+
import * as HoverCardPrimitive from '@radix-ui/react-hover-card'
3+
4+
import { cn } from '@/features/common/utils'
5+
6+
const HoverCard = HoverCardPrimitive.Root
7+
8+
const HoverCardTrigger = HoverCardPrimitive.Trigger
9+
10+
const HoverCardContent = React.forwardRef<
11+
React.ElementRef<typeof HoverCardPrimitive.Content>,
12+
React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Content>
13+
>(({ className, align = 'center', sideOffset = 4, ...props }, ref) => (
14+
<HoverCardPrimitive.Content
15+
ref={ref}
16+
align={align}
17+
sideOffset={sideOffset}
18+
className={cn(
19+
'z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
20+
className
21+
)}
22+
{...props}
23+
/>
24+
))
25+
HoverCardContent.displayName = HoverCardPrimitive.Content.displayName
26+
27+
export { HoverCard, HoverCardTrigger, HoverCardContent }
Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,28 @@
1-
import { NetworkConfig, useNetworkConfig, useTheme } from '@/features/settings/data'
21
import { PropsWithChildren, useEffect } from 'react'
3-
import { JotaiStore } from '../data/types'
42
import { useDataStore } from '../data/data-store'
5-
import { Provider } from 'jotai'
3+
import { Provider as DataProvider } from 'jotai'
4+
import { JotaiStore } from '../data/types'
5+
import { WalletProvider } from './wallet-provider'
6+
import { NetworkConfig, useTheme } from '@/features/settings/data'
7+
import { SupportedProviders } from '@txnlab/use-wallet'
68

7-
type ContextualDataProviderProps = PropsWithChildren<{
9+
type Props = PropsWithChildren<{
810
networkConfig: NetworkConfig
11+
walletProviders: SupportedProviders | null
912
store?: JotaiStore // This is only used for unit tests
1013
}>
1114

12-
function JotaiDataProvider({ children, networkConfig, store }: ContextualDataProviderProps) {
15+
export function PlatformProviderInner({ networkConfig, store, walletProviders, children }: Props) {
1316
const dataStore = useDataStore(networkConfig, store)
1417

15-
return (
16-
<Provider key={networkConfig.id} store={dataStore}>
17-
{children}
18-
</Provider>
19-
)
20-
}
21-
22-
type Props = PropsWithChildren<{
23-
store?: JotaiStore // This is only used for unit tests
24-
}>
25-
26-
export function DataProvider({ children, store }: Props) {
27-
const networkConfig = useNetworkConfig()
2818
const [theme] = useTheme()
2919

3020
useEffect(() => {
3121
const root = window.document.documentElement
32-
3322
root.classList.remove('light', 'dark')
3423

3524
if (theme === 'system') {
3625
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
37-
3826
root.classList.add(systemTheme)
3927
return
4028
}
@@ -43,8 +31,10 @@ export function DataProvider({ children, store }: Props) {
4331
}, [theme])
4432

4533
return (
46-
<JotaiDataProvider key={networkConfig.id} networkConfig={networkConfig} store={store}>
47-
{children}
48-
</JotaiDataProvider>
34+
<DataProvider key={networkConfig.id} store={dataStore}>
35+
<WalletProvider networkConfig={networkConfig} walletProviders={walletProviders}>
36+
{children}
37+
</WalletProvider>
38+
</DataProvider>
4939
)
5040
}

src/features/common/components/data-provider.test.tsx renamed to src/features/common/components/platform-provider.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { render, waitFor } from '@/tests/testing-library'
33
import MatchMediaMock from '@/tests/mock-match-media'
44
import { executeComponentTest } from '@/tests/test-component'
55

6-
describe('data-provider', () => {
6+
describe('platform-provider', () => {
77
describe('when using the default system light theme', () => {
88
it('the theme is set to light', () => {
99
return executeComponentTest(
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { useNetworkConfig } from '@/features/settings/data'
2+
import { PropsWithChildren } from 'react'
3+
4+
import { PROVIDER_ID, useInitializeProviders } from '@txnlab/use-wallet'
5+
import { DaffiWalletConnect } from '@daffiwallet/connect'
6+
import { PeraWalletConnect } from '@perawallet/connect'
7+
import LuteConnect from 'lute-connect'
8+
import algosdk from 'algosdk'
9+
import { DeflyWalletConnect } from '@blockshake/defly-connect'
10+
import { PlatformProviderInner } from './platform-provider-inner'
11+
12+
export function PlatformProvider({ children }: PropsWithChildren) {
13+
const networkConfig = useNetworkConfig()
14+
15+
const walletProviders = useInitializeProviders({
16+
providers: [
17+
{ id: PROVIDER_ID.DEFLY, clientStatic: DeflyWalletConnect },
18+
{ id: PROVIDER_ID.DAFFI, clientStatic: DaffiWalletConnect },
19+
{ id: PROVIDER_ID.PERA, clientStatic: PeraWalletConnect },
20+
{ id: PROVIDER_ID.EXODUS },
21+
{
22+
id: PROVIDER_ID.LUTE,
23+
clientStatic: LuteConnect,
24+
clientOptions: { siteName: 'Algorand Studio' },
25+
},
26+
],
27+
nodeConfig: {
28+
network: networkConfig.id,
29+
nodeServer: networkConfig.algod.server,
30+
nodePort: networkConfig.algod.port,
31+
},
32+
algosdkStatic: algosdk,
33+
})
34+
35+
return (
36+
<PlatformProviderInner networkConfig={networkConfig} walletProviders={walletProviders}>
37+
{children}
38+
</PlatformProviderInner>
39+
)
40+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { NetworkConfig } from '@/features/settings/data'
2+
import { SupportedProviders, WalletProvider as UseWalletProvider, useWallet } from '@txnlab/use-wallet'
3+
import { PropsWithChildren, useEffect } from 'react'
4+
5+
type Props = PropsWithChildren<{
6+
networkConfig: NetworkConfig
7+
walletProviders: SupportedProviders | null
8+
}>
9+
10+
function WalletProviderInner({ networkConfig, children }: Omit<Props, 'walletProviders'>) {
11+
const { providers } = useWallet()
12+
13+
useEffect(() => {
14+
// Disconnect wallets that aren't applicable to the chosen network
15+
providers?.forEach((provider) => {
16+
if (provider.isConnected && !networkConfig.walletProviders.includes(provider.metadata.id)) {
17+
provider.disconnect()
18+
}
19+
})
20+
}, [networkConfig.walletProviders, providers])
21+
22+
return children
23+
}
24+
25+
export function WalletProvider({ networkConfig, walletProviders, children }: Props) {
26+
return (
27+
<UseWalletProvider value={walletProviders}>
28+
<WalletProviderInner networkConfig={networkConfig}>{children}</WalletProviderInner>
29+
</UseWalletProvider>
30+
)
31+
}

src/features/layout/components/connect-wallet.tsx

Lines changed: 0 additions & 10 deletions
This file was deleted.

0 commit comments

Comments
 (0)