feat(auth-wallet): add handling for push methods/events (#145)
Co-authored-by: Ben Kremer <ben@walletconnect.com>
This commit is contained in:
parent
2ff0874778
commit
34cf292b06
1022
wallets/react-wallet-auth/package-lock.json
generated
1022
wallets/react-wallet-auth/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -15,12 +15,15 @@
|
|||||||
"@nextui-org/react": "1.0.8-beta.5",
|
"@nextui-org/react": "1.0.8-beta.5",
|
||||||
"@polkadot/keyring": "^10.1.2",
|
"@polkadot/keyring": "^10.1.2",
|
||||||
"@solana/web3.js": "1.43.0",
|
"@solana/web3.js": "1.43.0",
|
||||||
"@walletconnect/auth-client": "2.1.0",
|
"@walletconnect/auth-client": "^2.1.0",
|
||||||
"@walletconnect/utils": "2.7.6",
|
"@walletconnect/core": "^2.7.6",
|
||||||
|
"@walletconnect/push-client": "0.10.0",
|
||||||
|
"@walletconnect/utils": "^2.7.6",
|
||||||
"bs58": "5.0.0",
|
"bs58": "5.0.0",
|
||||||
"cosmos-wallet": "1.2.0",
|
"cosmos-wallet": "1.2.0",
|
||||||
"ethers": "5.6.6",
|
"ethers": "5.6.6",
|
||||||
"framer-motion": "6.3.3",
|
"framer-motion": "6.3.3",
|
||||||
|
"lokijs": "^1.5.12",
|
||||||
"mnemonic-keyring": "1.4.0",
|
"mnemonic-keyring": "1.4.0",
|
||||||
"next": "12.1.5",
|
"next": "12.1.5",
|
||||||
"react": "17.0.2",
|
"react": "17.0.2",
|
||||||
@ -33,7 +36,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "17.0.35",
|
"@types/node": "17.0.35",
|
||||||
"@types/react": "18.0.9",
|
"@types/react": "18.0.9",
|
||||||
"@walletconnect/types": "2.7.6",
|
"@walletconnect/types": "^2.7.6",
|
||||||
"eslint": "8.15.0",
|
"eslint": "8.15.0",
|
||||||
"eslint-config-next": "12.1.6",
|
"eslint-config-next": "12.1.6",
|
||||||
"eslint-config-prettier": "8.5.0",
|
"eslint-config-prettier": "8.5.0",
|
||||||
|
10
wallets/react-wallet-auth/public/icons/notification-icon.svg
Normal file
10
wallets/react-wallet-auth/public/icons/notification-icon.svg
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<svg width="15" height="23" viewBox="0 0 15 23" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M9.0001 0C6.07054 0 3.52589 2.0154 2.8549 4.86708L0.578472 14.5419C0.283081 15.7973 1.23561 17 2.52531 17H15.4749C16.7646 17 17.7171 15.7973 17.4217 14.5419L15.1453 4.86709C14.4743 2.0154 11.9297 0 9.0001 0Z" fill="url(#gradient)" />
|
||||||
|
<path d="M12.7231 19.9655C13.0268 19.1947 12.3285 15.5 11.5001 15.5H6.5001C5.67167 15.5 4.97344 19.1947 5.27708 19.9655C5.86177 21.4497 7.30827 22.5 9.0001 22.5C10.6919 22.5 12.1384 21.4497 12.7231 19.9655Z" fill="url(#gradient" />
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="gradient" x1="0" y1="0" x2="0" y2="100%">
|
||||||
|
<stop offset="0%" stop-color="#A562D5" />
|
||||||
|
<stop offset="100%" stop-color="#306FEB" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 786 B |
@ -1,5 +1,6 @@
|
|||||||
import ModalStore from '@/store/ModalStore'
|
import ModalStore from '@/store/ModalStore'
|
||||||
import AuthenticationRequestModal from '@/views/AuthenticationRequestModal'
|
import AuthenticationRequestModal from '@/views/AuthenticationRequestModal'
|
||||||
|
import PushRequestModal from '@/views/PushRequestModal'
|
||||||
import { Modal as NextModal } from '@nextui-org/react'
|
import { Modal as NextModal } from '@nextui-org/react'
|
||||||
import { useSnapshot } from 'valtio'
|
import { useSnapshot } from 'valtio'
|
||||||
|
|
||||||
@ -9,6 +10,7 @@ export default function Modal() {
|
|||||||
return (
|
return (
|
||||||
<NextModal blur open={open} style={{ border: '1px solid rgba(139, 139, 139, 0.4)' }}>
|
<NextModal blur open={open} style={{ border: '1px solid rgba(139, 139, 139, 0.4)' }}>
|
||||||
{view === 'AuthenticationRequest' && <AuthenticationRequestModal />}
|
{view === 'AuthenticationRequest' && <AuthenticationRequestModal />}
|
||||||
|
{view === 'PushRequest' && <PushRequestModal />}
|
||||||
</NextModal>
|
</NextModal>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,17 @@ export default function Navigation() {
|
|||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
|
<Link href="/notifications" passHref>
|
||||||
|
<a className="navLink">
|
||||||
|
<Image
|
||||||
|
alt="notifications icon"
|
||||||
|
src="/icons/notification-icon.svg"
|
||||||
|
width={25}
|
||||||
|
height={25}
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
|
|
||||||
<Link href="/walletconnect" passHref>
|
<Link href="/walletconnect" passHref>
|
||||||
<a className="navLink">
|
<a className="navLink">
|
||||||
<Avatar
|
<Avatar
|
||||||
|
@ -0,0 +1,75 @@
|
|||||||
|
import { truncate } from '@/utils/HelperUtil'
|
||||||
|
import { Avatar, Button, Card, Link, Text, Tooltip } from '@nextui-org/react'
|
||||||
|
import Image from 'next/image'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Types
|
||||||
|
*/
|
||||||
|
interface IProps {
|
||||||
|
logo?: string
|
||||||
|
title?: string
|
||||||
|
body?: string
|
||||||
|
url?: string
|
||||||
|
publishedAt?: number
|
||||||
|
onDelete: () => Promise<void>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component
|
||||||
|
*/
|
||||||
|
export default function NotificationItem({
|
||||||
|
logo,
|
||||||
|
title,
|
||||||
|
body,
|
||||||
|
url,
|
||||||
|
publishedAt,
|
||||||
|
onDelete
|
||||||
|
}: IProps) {
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
bordered
|
||||||
|
borderWeight="light"
|
||||||
|
css={{
|
||||||
|
position: 'relative',
|
||||||
|
marginBottom: '$6',
|
||||||
|
minHeight: '70px'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Card.Body
|
||||||
|
css={{
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
overflow: 'hidden'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Avatar src={logo} />
|
||||||
|
<div style={{ flex: 1 }}>
|
||||||
|
<Text h5 css={{ marginLeft: '$9' }}>
|
||||||
|
{title}
|
||||||
|
</Text>
|
||||||
|
{/* <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||||
|
|
||||||
|
</div> */}
|
||||||
|
<Text h6 css={{ marginLeft: '$9' }}>
|
||||||
|
{body}
|
||||||
|
</Text>
|
||||||
|
{publishedAt && (
|
||||||
|
<Text span css={{ marginLeft: '$9', fontSize: '12px' }}>
|
||||||
|
{new Date(publishedAt).toLocaleDateString()} -{' '}
|
||||||
|
{new Date(publishedAt).toLocaleTimeString()}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
<Link href={url} css={{ marginLeft: '$9' }} target="_blank" rel="noopener noreferrer">
|
||||||
|
{url}
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
<Tooltip content="Delete" placement="left">
|
||||||
|
<Button size="sm" color="error" flat onClick={onDelete} css={{ minWidth: 'auto' }}>
|
||||||
|
<Image src={'/icons/delete-icon.svg'} width={15} height={15} alt="delete icon" />
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
</Card.Body>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import SettingsStore from '@/store/SettingsStore'
|
import SettingsStore from '@/store/SettingsStore'
|
||||||
import { createOrRestoreEIP155Wallet } from '@/utils/EIP155WalletUtil'
|
import { createOrRestoreEIP155Wallet } from '@/utils/EIP155WalletUtil'
|
||||||
import { createAuthClient } from '@/utils/WalletConnectUtil'
|
import { createAuthClient, createPushClient } from '@/utils/WalletConnectUtil'
|
||||||
import { useCallback, useEffect, useState } from 'react'
|
import { useCallback, useEffect, useState } from 'react'
|
||||||
|
|
||||||
export default function useInitialization() {
|
export default function useInitialization() {
|
||||||
@ -11,6 +11,7 @@ export default function useInitialization() {
|
|||||||
const { eip155Addresses } = createOrRestoreEIP155Wallet()
|
const { eip155Addresses } = createOrRestoreEIP155Wallet()
|
||||||
SettingsStore.setEIP155Address(eip155Addresses[0])
|
SettingsStore.setEIP155Address(eip155Addresses[0])
|
||||||
await createAuthClient()
|
await createAuthClient()
|
||||||
|
await createPushClient()
|
||||||
setInitialized(true)
|
setInitialized(true)
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
alert(err)
|
alert(err)
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { EIP155_SIGNING_METHODS } from '@/data/EIP155Data'
|
|
||||||
import ModalStore from '@/store/ModalStore'
|
import ModalStore from '@/store/ModalStore'
|
||||||
import { authClient } from '@/utils/WalletConnectUtil'
|
import { authClient, pushClient } from '@/utils/WalletConnectUtil'
|
||||||
import { useCallback, useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
|
|
||||||
export default function useWalletConnectEventsManager(initialized: boolean) {
|
export default function useWalletConnectEventsManager(initialized: boolean) {
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
@ -9,6 +8,7 @@ export default function useWalletConnectEventsManager(initialized: boolean) {
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (initialized) {
|
if (initialized) {
|
||||||
|
// Auth client events
|
||||||
authClient.on('auth_request', ({ id, params }) => {
|
authClient.on('auth_request', ({ id, params }) => {
|
||||||
console.log('auth_request', { id, params })
|
console.log('auth_request', { id, params })
|
||||||
ModalStore.open('AuthenticationRequest', {
|
ModalStore.open('AuthenticationRequest', {
|
||||||
@ -19,5 +19,19 @@ export default function useWalletConnectEventsManager(initialized: boolean) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pushClient) {
|
||||||
|
// Push client events
|
||||||
|
pushClient.on('push_proposal', async ({ id, topic, params }) => {
|
||||||
|
console.log('push_proposal', { id, topic, params })
|
||||||
|
ModalStore.open('PushRequest', {
|
||||||
|
pushRequest: {
|
||||||
|
id,
|
||||||
|
topic,
|
||||||
|
params
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
}, [initialized])
|
}, [initialized])
|
||||||
}
|
}
|
||||||
|
103
wallets/react-wallet-auth/src/pages/notifications.tsx
Normal file
103
wallets/react-wallet-auth/src/pages/notifications.tsx
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
import NotificationItem from '@/components/NotificationItem'
|
||||||
|
import PageHeader from '@/components/PageHeader'
|
||||||
|
import { getAndFormatNotifications, pushClient } from '@/utils/WalletConnectUtil'
|
||||||
|
import { Text, Collapse, Grid, Avatar, Button } from '@nextui-org/react'
|
||||||
|
import { PushClientTypes } from '@walletconnect/push-client'
|
||||||
|
import Image from 'next/image'
|
||||||
|
import { Fragment, useCallback, useEffect, useState } from 'react'
|
||||||
|
|
||||||
|
export default function NotificationsPage() {
|
||||||
|
const [notifications, setNotifications] = useState<
|
||||||
|
{
|
||||||
|
subscription: PushClientTypes.PushSubscription
|
||||||
|
messages: PushClientTypes.PushMessageRecord[]
|
||||||
|
}[]
|
||||||
|
>([])
|
||||||
|
|
||||||
|
const handleDeleteNotification = useCallback(async (messageId: number) => {
|
||||||
|
pushClient.deletePushMessage({ id: messageId })
|
||||||
|
const formattedNotifications = getAndFormatNotifications()
|
||||||
|
setNotifications(formattedNotifications)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const handleDeleteSubscription = useCallback(async (topic: string) => {
|
||||||
|
await pushClient.deleteSubscription({ topic })
|
||||||
|
const formattedNotifications = getAndFormatNotifications()
|
||||||
|
setNotifications(formattedNotifications)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
pushClient.on('push_message', () => {
|
||||||
|
const formattedNotifications = getAndFormatNotifications()
|
||||||
|
setNotifications(formattedNotifications)
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
// Get notifications for the initial state
|
||||||
|
useEffect(() => {
|
||||||
|
const formattedNotifications = getAndFormatNotifications()
|
||||||
|
setNotifications(formattedNotifications)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<PageHeader title="Notifications" />
|
||||||
|
|
||||||
|
<Grid.Container gap={2}>
|
||||||
|
<Grid>
|
||||||
|
<Collapse.Group shadow>
|
||||||
|
{notifications.map(notification => (
|
||||||
|
<Collapse
|
||||||
|
key={notification.subscription.topic}
|
||||||
|
title={<Text h4>{notification.subscription.metadata.name}</Text>}
|
||||||
|
subtitle={notification.subscription.metadata.description}
|
||||||
|
contentLeft={
|
||||||
|
<Avatar
|
||||||
|
size="lg"
|
||||||
|
src={notification.subscription.metadata.icons[0]}
|
||||||
|
color="secondary"
|
||||||
|
bordered
|
||||||
|
squared
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
bordered
|
||||||
|
css={{
|
||||||
|
width: '100%',
|
||||||
|
marginBottom: '$6'
|
||||||
|
}}
|
||||||
|
color="error"
|
||||||
|
auto
|
||||||
|
onClick={() => handleDeleteSubscription(notification.subscription.topic)}
|
||||||
|
>
|
||||||
|
<Image src={'/icons/delete-icon.svg'} width={15} height={15} alt="delete icon" />
|
||||||
|
Delete subscription
|
||||||
|
</Button>
|
||||||
|
{notification.messages.length ? (
|
||||||
|
notification.messages
|
||||||
|
.sort((a, b) => b.publishedAt - a.publishedAt)
|
||||||
|
.map(({ id, message, publishedAt }) => (
|
||||||
|
<NotificationItem
|
||||||
|
key={id}
|
||||||
|
logo={message.icon}
|
||||||
|
url={message.url}
|
||||||
|
title={message.title}
|
||||||
|
body={message.body}
|
||||||
|
publishedAt={publishedAt}
|
||||||
|
onDelete={() => handleDeleteNotification(id)}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<Text css={{ opacity: '0.5', textAlign: 'center', marginTop: '$6' }}>
|
||||||
|
No notifications
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</Collapse>
|
||||||
|
))}
|
||||||
|
</Collapse.Group>
|
||||||
|
</Grid>
|
||||||
|
</Grid.Container>
|
||||||
|
</Fragment>
|
||||||
|
)
|
||||||
|
}
|
@ -1,16 +1,16 @@
|
|||||||
import { SessionTypes, SignClientTypes } from '@walletconnect/types'
|
|
||||||
import { proxy } from 'valtio'
|
import { proxy } from 'valtio'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Types
|
* Types
|
||||||
*/
|
*/
|
||||||
interface ModalData {
|
interface ModalData {
|
||||||
authenticationRequest: any
|
authenticationRequest?: any
|
||||||
|
pushRequest?: any
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
open: boolean
|
open: boolean
|
||||||
view?: 'AuthenticationRequest'
|
view?: 'AuthenticationRequest' | 'PushRequest'
|
||||||
data?: ModalData
|
data?: ModalData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,20 @@
|
|||||||
import AuthClient from '@walletconnect/auth-client'
|
import AuthClient from '@walletconnect/auth-client'
|
||||||
import pkg from '@walletconnect/auth-client/package.json'
|
import pkg from '@walletconnect/auth-client/package.json'
|
||||||
|
import { Core } from '@walletconnect/core'
|
||||||
|
import { WalletClient } from '@walletconnect/push-client'
|
||||||
|
|
||||||
|
const core = new Core({
|
||||||
|
projectId: process.env.NEXT_PUBLIC_PROJECT_ID!
|
||||||
|
})
|
||||||
|
|
||||||
console.log(`AuthClient@${pkg.version}`)
|
console.log(`AuthClient@${pkg.version}`)
|
||||||
|
|
||||||
export let authClient: AuthClient
|
export let authClient: AuthClient
|
||||||
|
export let pushClient: WalletClient
|
||||||
|
|
||||||
export async function createAuthClient() {
|
export async function createAuthClient() {
|
||||||
authClient = await AuthClient.init({
|
authClient = await AuthClient.init({
|
||||||
|
core,
|
||||||
projectId: process.env.NEXT_PUBLIC_PROJECT_ID!,
|
projectId: process.env.NEXT_PUBLIC_PROJECT_ID!,
|
||||||
relayUrl: process.env.NEXT_PUBLIC_RELAY_URL || 'wss://relay.walletconnect.com',
|
relayUrl: process.env.NEXT_PUBLIC_RELAY_URL || 'wss://relay.walletconnect.com',
|
||||||
metadata: {
|
metadata: {
|
||||||
@ -16,4 +24,36 @@ export async function createAuthClient() {
|
|||||||
icons: ['https://avatars.githubusercontent.com/u/37784886']
|
icons: ['https://avatars.githubusercontent.com/u/37784886']
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
const authClientId = await authClient.core.crypto.getClientId()
|
||||||
|
console.log({ authClientId })
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createPushClient() {
|
||||||
|
pushClient = await WalletClient.init({
|
||||||
|
logger: 'debug',
|
||||||
|
core,
|
||||||
|
projectId: process.env.NEXT_PUBLIC_PROJECT_ID!,
|
||||||
|
relayUrl: process.env.NEXT_PUBLIC_RELAY_URL || 'wss://relay.walletconnect.com'
|
||||||
|
})
|
||||||
|
const pushClientId = await pushClient.core.crypto.getClientId()
|
||||||
|
console.log({ pushClientId })
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getAndFormatNotifications = () => {
|
||||||
|
if (!pushClient) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
const activeSubscriptions = pushClient.getActiveSubscriptions()
|
||||||
|
const allMessagesWithSubscription = Object.entries(activeSubscriptions)
|
||||||
|
.map(([topic, subscription]) => ({
|
||||||
|
subscription,
|
||||||
|
messages: Object.values(
|
||||||
|
pushClient.getMessageHistory({
|
||||||
|
topic
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
.reverse()
|
||||||
|
|
||||||
|
return allMessagesWithSubscription
|
||||||
}
|
}
|
||||||
|
71
wallets/react-wallet-auth/src/views/PushRequestModal.tsx
Normal file
71
wallets/react-wallet-auth/src/views/PushRequestModal.tsx
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import { Fragment, useCallback, useEffect, useState } from 'react'
|
||||||
|
import RequestModalContainer from '@/components/RequestModalContainer'
|
||||||
|
import ModalStore from '@/store/ModalStore'
|
||||||
|
import { Button, Col, Link, Modal, Row, Text } from '@nextui-org/react'
|
||||||
|
import { createOrRestoreEIP155Wallet } from '@/utils/EIP155WalletUtil'
|
||||||
|
import { pushClient } from '@/utils/WalletConnectUtil'
|
||||||
|
import { getWalletAddressFromParams } from '@/utils/HelperUtil'
|
||||||
|
|
||||||
|
export default function PushRequestModal() {
|
||||||
|
const pushRequest = ModalStore.state.data?.pushRequest
|
||||||
|
const { params, id } = pushRequest
|
||||||
|
const [iss, setIss] = useState<string>()
|
||||||
|
const { eip155Wallets, eip155Addresses } = createOrRestoreEIP155Wallet()
|
||||||
|
useEffect(() => {
|
||||||
|
const iss = `did:pkh:${pushRequest.params.account}`
|
||||||
|
setIss(iss)
|
||||||
|
}, [pushRequest.params.account, eip155Addresses])
|
||||||
|
|
||||||
|
const onSign = useCallback(
|
||||||
|
async (message: string) => {
|
||||||
|
const wallet = eip155Wallets[getWalletAddressFromParams(eip155Addresses, params.account)]
|
||||||
|
const signature = await wallet.signMessage(message)
|
||||||
|
return signature
|
||||||
|
},
|
||||||
|
[eip155Wallets, eip155Addresses, params.account]
|
||||||
|
)
|
||||||
|
|
||||||
|
const onApprove = useCallback(async () => {
|
||||||
|
if (pushRequest && iss) {
|
||||||
|
await pushClient.approve({ id, onSign })
|
||||||
|
ModalStore.close()
|
||||||
|
}
|
||||||
|
}, [pushRequest, id, iss, onSign])
|
||||||
|
|
||||||
|
if (!pushRequest) {
|
||||||
|
return <Text>Missing push request</Text>
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle reject action
|
||||||
|
async function onReject() {
|
||||||
|
await pushClient.reject({ id, reason: 'User rejected push subscription request' })
|
||||||
|
|
||||||
|
ModalStore.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<RequestModalContainer title="Push Request">
|
||||||
|
<Row>
|
||||||
|
<Col>
|
||||||
|
<Text h5>Message</Text>
|
||||||
|
<Text style={{ whiteSpace: 'pre-wrap' }} color="$gray400">
|
||||||
|
Subscribe to{' '}
|
||||||
|
<Link href={params.metadata.url} target="_blank" rel="noopener noreferrer">
|
||||||
|
{params.metadata.url}
|
||||||
|
</Link>
|
||||||
|
</Text>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</RequestModalContainer>
|
||||||
|
<Modal.Footer>
|
||||||
|
<Button auto flat color="error" onClick={onReject}>
|
||||||
|
Reject
|
||||||
|
</Button>
|
||||||
|
<Button auto flat color="success" onClick={onApprove}>
|
||||||
|
Approve
|
||||||
|
</Button>
|
||||||
|
</Modal.Footer>
|
||||||
|
</Fragment>
|
||||||
|
)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user