Skip to content

Commit

Permalink
feat: can connect wallet
Browse files Browse the repository at this point in the history
  • Loading branch information
0xzio committed Jun 29, 2024
1 parent 424a7c3 commit d5faad0
Show file tree
Hide file tree
Showing 19 changed files with 6,358 additions and 5,863 deletions.
3 changes: 2 additions & 1 deletion apps/desktop/.env
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
VITE_API_URL=https://app.penx.io
# VITE_API_URL=https://app.penx.io
VITE_API_URL=http://localhost:3000

NEXT_PUBLIC_PLATFORM=DESKTOP

Expand Down
10 changes: 7 additions & 3 deletions apps/desktop/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "desktop",
"private": true,
"version": "0.3.33",
"version": "0.3.34",
"type": "module",
"scripts": {
"vite:dev": "vite",
Expand Down Expand Up @@ -31,6 +31,7 @@
"@penx/hooks": "workspace:*",
"@penx/icons": "workspace:*",
"@penx/local-db": "workspace:*",
"@penx/math": "workspace:*",
"@penx/mnemonic": "workspace:*",
"@penx/model": "workspace:*",
"@penx/model-types": "workspace:*",
Expand All @@ -42,6 +43,7 @@
"@penx/unique-id": "workspace:*",
"@penx/widget": "workspace:*",
"@penx/worker": "workspace:*",
"@penxio/api": "workspace:*",
"@penxio/preset-ui": "workspace:*",
"@tanstack/react-query": "^5.45.1",
"@tauri-apps/api": "2.0.0-beta.13",
Expand All @@ -61,16 +63,18 @@
"ky": "^1.1.3",
"lucide-react": "^0.344.0",
"next": "14.1.4",
"@penxio/api": "workspace:*",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-fast-compare": "^3.2.2",
"react-inlinesvg": "^4.1.3",
"react-markdown": "^9.0.1",
"siwe": "^2.3.2",
"tauri-plugin-clipboard-api": "^2.0.4",
"tauri-plugin-jarvis-api": "workspace:*",
"tauri-plugin-shellx-api": "^2.0.7",
"uikit": "workspace:*"
"uikit": "workspace:*",
"viem": "^2.9.28",
"wagmi": "^2.10.8"
},
"devDependencies": {
"@biomejs/biome": "^1.6.3",
Expand Down
14 changes: 9 additions & 5 deletions apps/desktop/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useEffect } from 'react'
import { WagmiProvider } from 'wagmi'
import { ToastContainer } from 'uikit'
import { appEmitter } from '@penx/event'
import { StoreProvider } from '@penx/store'
Expand All @@ -17,6 +18,7 @@ import { useInitThemeMode } from './hooks/useInitThemeMode'
import { MainApp } from './MainApp'
import '~/styles/globals.css'
import '~/styles/command.scss'
import { config } from './config'

initFower()

Expand Down Expand Up @@ -56,11 +58,13 @@ function MyApp() {

return (
<StoreProvider>
<TrpcProvider>
<ToastContainer position="bottom-right" />
<MainApp />
<div id="portal" />
</TrpcProvider>
<WagmiProvider config={config}>
<TrpcProvider>
<ToastContainer position="bottom-right" />
<MainApp />
<div id="portal" />
</TrpcProvider>
</WagmiProvider>
</StoreProvider>
)
}
Expand Down
2 changes: 2 additions & 0 deletions apps/desktop/src/MainApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { installBuiltinExtension } from '~/common/installBuiltinExtension'
import { CommandPalette } from '~/components/CommandPalette/CommandPalette'
import { DesktopWelcome } from '~/components/DesktopWelcome'
import { InitUserToStore } from '~/components/InitUserToStore'
import { SiweModal } from './wallet/SiweModal'

export function MainApp() {
const {
Expand Down Expand Up @@ -71,6 +72,7 @@ export function MainApp() {
}}
>
{session && <InitUserToStore userId={session?.userId} />}
<SiweModal />

<Box
relative
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useCurrentCommand } from '~/hooks/useCurrentCommand'
import { useCommands, useItems } from '~/hooks/useItems'
import { useSearch } from '~/hooks/useSearch'
import { CommandService } from '~/services/CommandService'
import { WalletConnect } from '~/wallet/WalletConnect'
import { AddRowButton } from './AddRowButton'
import { BackRootButton } from './BackRootButton'
import { CommandAppLoading } from './CommandAppLoading'
Expand Down Expand Up @@ -35,6 +36,7 @@ export const SearchBar = ({ searchBarHeight }: Props) => {
borderNeutral200
relative
h={searchBarHeight}
mr2
>
{isCommandApp && <BackRootButton pl3 mr--8 />}

Expand Down Expand Up @@ -88,7 +90,7 @@ export const SearchBar = ({ searchBarHeight }: Props) => {
{isCommandApp && currentCommand?.filters && (
<SearchBarFilter filters={currentCommand?.filters} />
)}

<WalletConnect></WalletConnect>
<CommandAppLoading />
</Box>
)
Expand Down
15 changes: 15 additions & 0 deletions apps/desktop/src/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { createConfig, http } from 'wagmi'
import { arbitrumSepolia, base, mainnet, optimism } from 'wagmi/chains'
import { injected, metaMask, safe, walletConnect } from 'wagmi/connectors'

const projectId = '75879ee8d0e1f47758a4bb4577361f08'

export const config = createConfig({
chains: [mainnet, base],
connectors: [walletConnect({ projectId: projectId })],
transports: {
[mainnet.id]: http(),
[base.id]: http(),
[arbitrumSepolia.id]: http(),
},
})
15 changes: 15 additions & 0 deletions apps/desktop/src/wallet/AccountAvatar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Avatar, AvatarFallback } from 'uikit'

interface AccountAvatarProps {
size?: number
}
export function AccountAvatar({ size = 24 }: AccountAvatarProps) {
return (
<Avatar size={size}>
<AvatarFallback
bgGradientX={['red500', 'orange500', 'yellow400']}
borderNone
></AvatarFallback>
</Avatar>
)
}
25 changes: 25 additions & 0 deletions apps/desktop/src/wallet/AccountPopover.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Box } from '@fower/react'
import { LogOut } from 'lucide-react'
import { useAccount, useDisconnect } from 'wagmi'
import { Button, Menu, MenuItem, Popover, PopoverContent, PopoverTrigger } from 'uikit'
import { AccountAvatar } from './AccountAvatar'
import { WalletInfo } from './WalletInfo'

export function AccountPopover() {
const { address = '' } = useAccount()

const name = address.slice(0, 3) + '...' + address.slice(-3)
return (
<Popover placement="bottom-end">
<PopoverTrigger asChild cursorPointer>
<Box cursorPointer toCenterY gap1 bgNeutral100 px2 py2 roundedLG>
<AccountAvatar />
{address && <Box neutral500>{name}</Box>}
</Box>
</PopoverTrigger>
<PopoverContent w-300 p5 bgNeutral100--L20>
<WalletInfo />
</PopoverContent>
</Popover>
)
}
21 changes: 21 additions & 0 deletions apps/desktop/src/wallet/ConnectButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Box } from '@fower/react'
import { useConnect } from 'wagmi'
import { Button } from 'uikit'
import { IconWalletConnect } from '@penx/icons'

export function ConnectButton() {
const { connectors, connect } = useConnect()

return connectors.map((connector) => (
<Button
key={connector.uid}
colorScheme="neutral900"
textSM
variant="light"
onClick={() => connect({ connector })}
>
<IconWalletConnect sky500 />
<Box>Connect Wallet</Box>
</Button>
))
}
88 changes: 88 additions & 0 deletions apps/desktop/src/wallet/SiweModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { useEffect, useState } from 'react'
import { Box } from '@fower/react'
import { useAccount, useChainId, useDisconnect, useSignMessage } from 'wagmi'
import { Button, Modal, ModalContent, modalController, ModalOverlay, Spinner, toast } from 'uikit'
import { ModalNames } from '@penx/constants'
import { useHandleSignIn } from './hooks/useHandleSignIn'

export function SiweModal() {
const { isConnected, address = '' } = useAccount()
const { disconnect } = useDisconnect()
const [loading, setLoading] = useState(false)

const handleSignIn = useHandleSignIn()

useEffect(() => {
const token = localStorage.getItem('PENX_TOKEN')
if (isConnected && !token) {
modalController.open(ModalNames.SIWE)
}
}, [isConnected])

const shortenAddress = `${address?.slice(0, 18)}...${address?.slice(-4)}`

return (
<Modal name={ModalNames.SIWE} closeOnOverlayClick={false}>
<ModalOverlay />
<ModalContent w={['96%', 360]} px={[20, 20]} py0 column gap4>
<Box column toCenterX>
<Box fontSemibold text2XL leadingNone>
Sign in to PenX
</Box>
</Box>

<Box toCenter>
<Box gray800 py1 px3 bgNeutral100 roundedFull inlineFlex>
{shortenAddress}
</Box>
</Box>

<Box textCenter neutral500 leadingTight textSM>
Sign this message to prove you own this wallet and proceed. Canceling will disconnect you.
</Box>

<Box textCenter green500 leadingTight textXS>
Please verify by signing in with mobile wallet.
</Box>

<Box toCenterY gap2 mt2>
<Button
type="button"
roundedFull
colorScheme="white"
flex-1
onClick={() => {
modalController.close(ModalNames.SIWE)
disconnect()
}}
>
Cancel
</Button>

<Button
roundedFull
gap2
flex-1
disabled={loading}
onClick={async () => {
if (loading) return
try {
setLoading(true)
await handleSignIn()
modalController.close(ModalNames.SIWE)
setLoading(false)
} catch (error) {
setLoading(false)
toast.error((error as any)?.message || 'Something went wrong')
console.log('====error:', error)
}
}}
>
{loading && <Spinner white square5 />}
<Box>Sign in</Box>
</Button>
</Box>
</ModalContent>
</Modal>
)
}
9 changes: 9 additions & 0 deletions apps/desktop/src/wallet/WalletConnect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { useAccount } from 'wagmi'
import { AccountPopover } from './AccountPopover'
import { ConnectButton } from './ConnectButton'

export function WalletConnect() {
const { isConnected } = useAccount()
if (isConnected) return <AccountPopover />
return <ConnectButton />
}
61 changes: 61 additions & 0 deletions apps/desktop/src/wallet/WalletInfo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { Box } from '@fower/react'
import { Copy, Power, Wallet } from 'lucide-react'
import { useAccount, useDisconnect } from 'wagmi'
import { Button, Skeleton, toast } from 'uikit'
import { precision } from '@penx/math'
import { useCopyToClipboard } from '@penx/shared'
import { AccountAvatar } from './AccountAvatar'
import { useEthBalance } from './hooks/useEthBalance'

export function WalletInfo() {
const { data, isLoading } = useEthBalance()
const { disconnect } = useDisconnect()
const { copy } = useCopyToClipboard()
const { address = '' } = useAccount()

const shortAddress = address.slice(0, 10) + '...' + address.slice(-4)
return (
<Box column gap3>
<Box toBetween toCenterY>
<Box toCenterY gap2>
<AccountAvatar />
<Box>{shortAddress}</Box>
</Box>
<Box toCenterY gap-1>
<Button
size={28}
colorScheme="neutral600"
variant="ghost"
isSquare
onClick={() => {
copy(address)
toast.success('Copied to clipboard')
}}
>
<Copy size={18} />
</Button>

<Button
size={28}
colorScheme="neutral600"
variant="ghost"
isSquare
onClick={() => {
disconnect()
localStorage.removeItem('PENX_TOKEN')
}}
>
<Power size={18} />
</Button>
</Box>
</Box>

<Box toCenter h-60>
{isLoading && <Skeleton h-40 w-80p roundedXL />}
<Box text4XL fontBold>
{typeof data !== 'undefined' && `${precision.toDecimal(data).toFixed(5)} ETH`}
</Box>
</Box>
</Box>
)
}
22 changes: 22 additions & 0 deletions apps/desktop/src/wallet/hooks/useEthBalance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { useQuery } from '@tanstack/react-query'
import { createPublicClient, http } from 'viem'
import { arbitrumSepolia } from 'viem/chains'
import { useAccount } from 'wagmi'

export function useEthBalance() {
const { address } = useAccount()

return useQuery({
queryKey: ['eth_balance', address],
queryFn: async () => {
const publicClient = createPublicClient({
chain: arbitrumSepolia,
transport: http(),
})
const data = await publicClient.getBalance({
address: address!,
})
return data
},
})
}
Loading

0 comments on commit d5faad0

Please sign in to comment.