Skip to content

Commit 34cf292

Browse files
Cali93bkrem
andauthored
feat(auth-wallet): add handling for push methods/events (#145)
Co-authored-by: Ben Kremer <[email protected]>
1 parent 2ff0874 commit 34cf292

12 files changed

+1399
-189
lines changed

wallets/react-wallet-auth/package-lock.json

+1,059-179
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

wallets/react-wallet-auth/package.json

+6-3
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,15 @@
1515
"@nextui-org/react": "1.0.8-beta.5",
1616
"@polkadot/keyring": "^10.1.2",
1717
"@solana/web3.js": "1.43.0",
18-
"@walletconnect/auth-client": "2.1.0",
19-
"@walletconnect/utils": "2.7.6",
18+
"@walletconnect/auth-client": "^2.1.0",
19+
"@walletconnect/core": "^2.7.6",
20+
"@walletconnect/push-client": "0.10.0",
21+
"@walletconnect/utils": "^2.7.6",
2022
"bs58": "5.0.0",
2123
"cosmos-wallet": "1.2.0",
2224
"ethers": "5.6.6",
2325
"framer-motion": "6.3.3",
26+
"lokijs": "^1.5.12",
2427
"mnemonic-keyring": "1.4.0",
2528
"next": "12.1.5",
2629
"react": "17.0.2",
@@ -33,7 +36,7 @@
3336
"devDependencies": {
3437
"@types/node": "17.0.35",
3538
"@types/react": "18.0.9",
36-
"@walletconnect/types": "2.7.6",
39+
"@walletconnect/types": "^2.7.6",
3740
"eslint": "8.15.0",
3841
"eslint-config-next": "12.1.6",
3942
"eslint-config-prettier": "8.5.0",
Loading

wallets/react-wallet-auth/src/components/Modal.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import ModalStore from '@/store/ModalStore'
22
import AuthenticationRequestModal from '@/views/AuthenticationRequestModal'
3+
import PushRequestModal from '@/views/PushRequestModal'
34
import { Modal as NextModal } from '@nextui-org/react'
45
import { useSnapshot } from 'valtio'
56

@@ -9,6 +10,7 @@ export default function Modal() {
910
return (
1011
<NextModal blur open={open} style={{ border: '1px solid rgba(139, 139, 139, 0.4)' }}>
1112
{view === 'AuthenticationRequest' && <AuthenticationRequestModal />}
13+
{view === 'PushRequest' && <PushRequestModal />}
1214
</NextModal>
1315
)
1416
}

wallets/react-wallet-auth/src/components/Navigation.tsx

+11
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,17 @@ export default function Navigation() {
2323
</a>
2424
</Link>
2525

26+
<Link href="/notifications" passHref>
27+
<a className="navLink">
28+
<Image
29+
alt="notifications icon"
30+
src="/icons/notification-icon.svg"
31+
width={25}
32+
height={25}
33+
/>
34+
</a>
35+
</Link>
36+
2637
<Link href="/walletconnect" passHref>
2738
<a className="navLink">
2839
<Avatar
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { truncate } from '@/utils/HelperUtil'
2+
import { Avatar, Button, Card, Link, Text, Tooltip } from '@nextui-org/react'
3+
import Image from 'next/image'
4+
5+
/**
6+
* Types
7+
*/
8+
interface IProps {
9+
logo?: string
10+
title?: string
11+
body?: string
12+
url?: string
13+
publishedAt?: number
14+
onDelete: () => Promise<void>
15+
}
16+
17+
/**
18+
* Component
19+
*/
20+
export default function NotificationItem({
21+
logo,
22+
title,
23+
body,
24+
url,
25+
publishedAt,
26+
onDelete
27+
}: IProps) {
28+
return (
29+
<Card
30+
bordered
31+
borderWeight="light"
32+
css={{
33+
position: 'relative',
34+
marginBottom: '$6',
35+
minHeight: '70px'
36+
}}
37+
>
38+
<Card.Body
39+
css={{
40+
flexDirection: 'row',
41+
alignItems: 'center',
42+
justifyContent: 'space-between',
43+
overflow: 'hidden'
44+
}}
45+
>
46+
<Avatar src={logo} />
47+
<div style={{ flex: 1 }}>
48+
<Text h5 css={{ marginLeft: '$9' }}>
49+
{title}
50+
</Text>
51+
{/* <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
52+
53+
</div> */}
54+
<Text h6 css={{ marginLeft: '$9' }}>
55+
{body}
56+
</Text>
57+
{publishedAt && (
58+
<Text span css={{ marginLeft: '$9', fontSize: '12px' }}>
59+
{new Date(publishedAt).toLocaleDateString()} -{' '}
60+
{new Date(publishedAt).toLocaleTimeString()}
61+
</Text>
62+
)}
63+
<Link href={url} css={{ marginLeft: '$9' }} target="_blank" rel="noopener noreferrer">
64+
{url}
65+
</Link>
66+
</div>
67+
<Tooltip content="Delete" placement="left">
68+
<Button size="sm" color="error" flat onClick={onDelete} css={{ minWidth: 'auto' }}>
69+
<Image src={'/icons/delete-icon.svg'} width={15} height={15} alt="delete icon" />
70+
</Button>
71+
</Tooltip>
72+
</Card.Body>
73+
</Card>
74+
)
75+
}

wallets/react-wallet-auth/src/hooks/useInitialization.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import SettingsStore from '@/store/SettingsStore'
22
import { createOrRestoreEIP155Wallet } from '@/utils/EIP155WalletUtil'
3-
import { createAuthClient } from '@/utils/WalletConnectUtil'
3+
import { createAuthClient, createPushClient } from '@/utils/WalletConnectUtil'
44
import { useCallback, useEffect, useState } from 'react'
55

66
export default function useInitialization() {
@@ -11,6 +11,7 @@ export default function useInitialization() {
1111
const { eip155Addresses } = createOrRestoreEIP155Wallet()
1212
SettingsStore.setEIP155Address(eip155Addresses[0])
1313
await createAuthClient()
14+
await createPushClient()
1415
setInitialized(true)
1516
} catch (err: unknown) {
1617
alert(err)
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
import { EIP155_SIGNING_METHODS } from '@/data/EIP155Data'
21
import ModalStore from '@/store/ModalStore'
3-
import { authClient } from '@/utils/WalletConnectUtil'
4-
import { useCallback, useEffect } from 'react'
2+
import { authClient, pushClient } from '@/utils/WalletConnectUtil'
3+
import { useEffect } from 'react'
54

65
export default function useWalletConnectEventsManager(initialized: boolean) {
76
/******************************************************************************
87
* Set up WalletConnect event listeners
98
*****************************************************************************/
109
useEffect(() => {
1110
if (initialized) {
11+
// Auth client events
1212
authClient.on('auth_request', ({ id, params }) => {
1313
console.log('auth_request', { id, params })
1414
ModalStore.open('AuthenticationRequest', {
@@ -19,5 +19,19 @@ export default function useWalletConnectEventsManager(initialized: boolean) {
1919
})
2020
})
2121
}
22+
23+
if (pushClient) {
24+
// Push client events
25+
pushClient.on('push_proposal', async ({ id, topic, params }) => {
26+
console.log('push_proposal', { id, topic, params })
27+
ModalStore.open('PushRequest', {
28+
pushRequest: {
29+
id,
30+
topic,
31+
params
32+
}
33+
})
34+
})
35+
}
2236
}, [initialized])
2337
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import NotificationItem from '@/components/NotificationItem'
2+
import PageHeader from '@/components/PageHeader'
3+
import { getAndFormatNotifications, pushClient } from '@/utils/WalletConnectUtil'
4+
import { Text, Collapse, Grid, Avatar, Button } from '@nextui-org/react'
5+
import { PushClientTypes } from '@walletconnect/push-client'
6+
import Image from 'next/image'
7+
import { Fragment, useCallback, useEffect, useState } from 'react'
8+
9+
export default function NotificationsPage() {
10+
const [notifications, setNotifications] = useState<
11+
{
12+
subscription: PushClientTypes.PushSubscription
13+
messages: PushClientTypes.PushMessageRecord[]
14+
}[]
15+
>([])
16+
17+
const handleDeleteNotification = useCallback(async (messageId: number) => {
18+
pushClient.deletePushMessage({ id: messageId })
19+
const formattedNotifications = getAndFormatNotifications()
20+
setNotifications(formattedNotifications)
21+
}, [])
22+
23+
const handleDeleteSubscription = useCallback(async (topic: string) => {
24+
await pushClient.deleteSubscription({ topic })
25+
const formattedNotifications = getAndFormatNotifications()
26+
setNotifications(formattedNotifications)
27+
}, [])
28+
29+
useEffect(() => {
30+
pushClient.on('push_message', () => {
31+
const formattedNotifications = getAndFormatNotifications()
32+
setNotifications(formattedNotifications)
33+
})
34+
}, [])
35+
36+
// Get notifications for the initial state
37+
useEffect(() => {
38+
const formattedNotifications = getAndFormatNotifications()
39+
setNotifications(formattedNotifications)
40+
}, [])
41+
42+
return (
43+
<Fragment>
44+
<PageHeader title="Notifications" />
45+
46+
<Grid.Container gap={2}>
47+
<Grid>
48+
<Collapse.Group shadow>
49+
{notifications.map(notification => (
50+
<Collapse
51+
key={notification.subscription.topic}
52+
title={<Text h4>{notification.subscription.metadata.name}</Text>}
53+
subtitle={notification.subscription.metadata.description}
54+
contentLeft={
55+
<Avatar
56+
size="lg"
57+
src={notification.subscription.metadata.icons[0]}
58+
color="secondary"
59+
bordered
60+
squared
61+
/>
62+
}
63+
>
64+
<Button
65+
bordered
66+
css={{
67+
width: '100%',
68+
marginBottom: '$6'
69+
}}
70+
color="error"
71+
auto
72+
onClick={() => handleDeleteSubscription(notification.subscription.topic)}
73+
>
74+
<Image src={'/icons/delete-icon.svg'} width={15} height={15} alt="delete icon" />
75+
Delete subscription
76+
</Button>
77+
{notification.messages.length ? (
78+
notification.messages
79+
.sort((a, b) => b.publishedAt - a.publishedAt)
80+
.map(({ id, message, publishedAt }) => (
81+
<NotificationItem
82+
key={id}
83+
logo={message.icon}
84+
url={message.url}
85+
title={message.title}
86+
body={message.body}
87+
publishedAt={publishedAt}
88+
onDelete={() => handleDeleteNotification(id)}
89+
/>
90+
))
91+
) : (
92+
<Text css={{ opacity: '0.5', textAlign: 'center', marginTop: '$6' }}>
93+
No notifications
94+
</Text>
95+
)}
96+
</Collapse>
97+
))}
98+
</Collapse.Group>
99+
</Grid>
100+
</Grid.Container>
101+
</Fragment>
102+
)
103+
}

wallets/react-wallet-auth/src/store/ModalStore.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
import { SessionTypes, SignClientTypes } from '@walletconnect/types'
21
import { proxy } from 'valtio'
32

43
/**
54
* Types
65
*/
76
interface ModalData {
8-
authenticationRequest: any
7+
authenticationRequest?: any
8+
pushRequest?: any
99
}
1010

1111
interface State {
1212
open: boolean
13-
view?: 'AuthenticationRequest'
13+
view?: 'AuthenticationRequest' | 'PushRequest'
1414
data?: ModalData
1515
}
1616

wallets/react-wallet-auth/src/utils/WalletConnectUtil.ts

+40
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
11
import AuthClient from '@walletconnect/auth-client'
22
import pkg from '@walletconnect/auth-client/package.json'
3+
import { Core } from '@walletconnect/core'
4+
import { WalletClient } from '@walletconnect/push-client'
5+
6+
const core = new Core({
7+
projectId: process.env.NEXT_PUBLIC_PROJECT_ID!
8+
})
39

410
console.log(`AuthClient@${pkg.version}`)
511

612
export let authClient: AuthClient
13+
export let pushClient: WalletClient
714

815
export async function createAuthClient() {
916
authClient = await AuthClient.init({
17+
core,
1018
projectId: process.env.NEXT_PUBLIC_PROJECT_ID!,
1119
relayUrl: process.env.NEXT_PUBLIC_RELAY_URL || 'wss://relay.walletconnect.com',
1220
metadata: {
@@ -16,4 +24,36 @@ export async function createAuthClient() {
1624
icons: ['https://avatars.githubusercontent.com/u/37784886']
1725
}
1826
})
27+
const authClientId = await authClient.core.crypto.getClientId()
28+
console.log({ authClientId })
29+
}
30+
31+
export async function createPushClient() {
32+
pushClient = await WalletClient.init({
33+
logger: 'debug',
34+
core,
35+
projectId: process.env.NEXT_PUBLIC_PROJECT_ID!,
36+
relayUrl: process.env.NEXT_PUBLIC_RELAY_URL || 'wss://relay.walletconnect.com'
37+
})
38+
const pushClientId = await pushClient.core.crypto.getClientId()
39+
console.log({ pushClientId })
40+
}
41+
42+
export const getAndFormatNotifications = () => {
43+
if (!pushClient) {
44+
return []
45+
}
46+
const activeSubscriptions = pushClient.getActiveSubscriptions()
47+
const allMessagesWithSubscription = Object.entries(activeSubscriptions)
48+
.map(([topic, subscription]) => ({
49+
subscription,
50+
messages: Object.values(
51+
pushClient.getMessageHistory({
52+
topic
53+
})
54+
)
55+
}))
56+
.reverse()
57+
58+
return allMessagesWithSubscription
1959
}

0 commit comments

Comments
 (0)