Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 26 additions & 2 deletions client/src/AppRoutes.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react'
import { Route, Routes } from 'react-router-dom'
import React, { useEffect } from 'react'
import { Outlet, Route, Routes } from 'react-router-dom'
import Signup from './pages/Signup'
import Wallet from './pages/Wallet'
import Recover from './pages/Recover'
Expand All @@ -11,10 +11,34 @@ import Archive from './pages/Archive'
import TgSignup from './pages/TgSignup'
import TgRecover from './pages/TgRecover'
import SaveRecoverySecret from './pages/SaveRecoverySecret'
import querystring from 'query-string'
import TgRouter from './pages/TgRouter'
import useMultipleWallet from './hooks/useMultipleWallet'

const UserIdHandler = () => {
const qs = querystring.parse(location.search) as Record<string, string>
const { userId } = qs

const { wallet, containWallet, switchWallet } = useMultipleWallet()

useEffect(() => {
if (wallet?.address && wallet.pk && wallet.phone === userId) {
return
} else if (containWallet(userId)) {
switchWallet(userId)
}
}, [wallet, switchWallet, containWallet, userId])

return (
<Outlet />
)
}

const AppRoutes = (): React.JSX.Element => {
return (
<Routes>
<Route index element={<UserIdHandler />} />

{/* <Route exact path='/' element={() => <Landing />} /> */}
<Route path={paths.root} element={<Wallet />} />
<Route path={paths.wallet} element={<Wallet />} />
Expand Down
14 changes: 5 additions & 9 deletions client/src/components/Container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,21 @@ import MenuIcon from '../../assets/menu.svg'
import { Col, Main, Modal, Row } from './Layout'
import { Button, LinkWrarpper } from './Controls'
import { walletActions } from '../state/modules/wallet'
import { useDispatch, useSelector } from 'react-redux'
import { useDispatch } from 'react-redux'
import paths from '../pages/paths'
import apis from '../api/index'
import { useNavigate } from 'react-router'
import { type RootState } from '../state/rootReducer'
import { type WalletState } from '../state/modules/wallet/reducers'
import config from '../config'
import useMultipleWallet from '../hooks/useMultipleWallet'

const MainContainer = ({ children, withMenu = false }): React.JSX.Element => {
const navigate = useNavigate()
const dispatch = useDispatch()
const wallet = useSelector<RootState, WalletState>(state => state.wallet ?? {})
const address = Object.keys(wallet).find(e => apis.web3.isValidAddress(e))
const { wallet } = useMultipleWallet()

const [menuVisible, setMenuVisible] = useState(false)
const [logoutModalVisible, setLogoutModalVisible] = useState(false)

const state = wallet[address ?? '']
const p = state?.p
const p = wallet?.p
const logout = (): void => {
dispatch(walletActions.deleteAllWallet())
setLogoutModalVisible(false)
Expand All @@ -44,7 +40,7 @@ const MainContainer = ({ children, withMenu = false }): React.JSX.Element => {
<IconImg onClick={() => { setMenuVisible(!menuVisible) }} src={MenuIcon as string} />
{menuVisible &&
<MenuItems>
{wallet && <MenuItemLink onClick={() => { navigate(paths.wallet) }}>{wallet[address ?? ''].phone}</MenuItemLink>}
{wallet && <MenuItemLink onClick={() => { navigate(paths.wallet) }}>{wallet.phone}</MenuItemLink>}
<MenuItemLink onClick={() => { setLogoutModalVisible(true) }}>Logout</MenuItemLink>
</MenuItems>}
</MenuIconContainer>}
Expand Down
59 changes: 59 additions & 0 deletions client/src/hooks/useMultipleWallet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import React, { useCallback, useContext, useState } from "react"
import { useSelector } from "react-redux"
import { RootState } from "../state/rootReducer"
import { WalletState, Wallet } from "../state/modules/wallet/reducers"
import apis from '../api'

type WalletType = {
wallet?: Wallet
switchWallet: (phone: string) => Wallet | undefined
containWallet: (phone: string) => boolean
}

const WalletContext = React.createContext<WalletType>({
switchWallet: () => undefined,
containWallet: () => true
})

type MultipleWalletProviderProps = {
children: React.ReactNode
}

export const MultipleWalletProvider = ({ children }: MultipleWalletProviderProps) => {
const wallets = useSelector<RootState, WalletState>(state => state.wallet || {})

const [address, setAddress] = useState<string>()

const containWallet = useCallback((phone: string) =>
Object.keys(wallets).some(e => wallets[e].phone === phone && apis.web3.isValidAddress(e)),
[wallets]
)

const switchWallet = useCallback((phone: string) => {
const address = Object.keys(wallets).find(e => wallets[e].phone === phone && apis.web3.isValidAddress(e))

if (!address) {
return undefined
}

setAddress(address)

return wallets[address]
}, [wallets])

return (
<WalletContext.Provider
value={{
wallet: address ? wallets[address] : undefined,
switchWallet,
containWallet
}}
>
{children}
</WalletContext.Provider>
)
}

const useWalletAddress = () => useContext(WalletContext)

export default useWalletAddress
5 changes: 4 additions & 1 deletion client/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import 'react-phone-number-input/style.css'
import { HistoryRouter } from 'redux-first-history/rr6'
import { PersistGate } from 'redux-persist/es/integration/react'
import { Loading } from './components/Misc'
import { MultipleWalletProvider } from './hooks/useMultipleWallet'

// eslint-disable-next-line @typescript-eslint/no-empty-function
document.body.ontouchstart = function (): void {}
Expand All @@ -26,7 +27,9 @@ if (container != null) {
<Provider store={store}>
<PersistGate loading={<Loading/>} persistor={persistor}>
<HistoryRouter history={history}>
<Routes />
<MultipleWalletProvider>
<Routes />
</MultipleWalletProvider>
<ToastContainer />
</HistoryRouter>
</PersistGate>
Expand Down
20 changes: 8 additions & 12 deletions client/src/pages/ApproveTransaction.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useState, useEffect } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { useDispatch } from 'react-redux'
import paths from './paths'
import MainContainer from '../components/Container'
import querystring from 'query-string'
Expand All @@ -11,10 +11,9 @@ import { Row } from '../components/Layout'
import { utils } from '../utils'
import { pick } from 'lodash'
import { globalActions } from '../state/modules/global'
import { type RootState } from '../state/rootReducer'
import { type WalletState } from '../state/modules/wallet/reducers'
import { type TransactionReceipt } from 'ethers'
import { Navigate, useNavigate } from 'react-router'
import useMultipleWallet from '../hooks/useMultipleWallet'

export interface CallData {
method?: string
Expand Down Expand Up @@ -43,16 +42,15 @@ export interface ApproveTransactionParams {
}

export const ApproveTransaction = ({ calldata, caller, callback, comment, inputAmount, dest, onComplete }: ApproveTransactionParams): React.JSX.Element => {
const wallet = useSelector((state: RootState) => state.wallet || {})
const address = Object.keys(wallet).find(e => apis.web3.isValidAddress(e))
const { wallet } = useMultipleWallet()

const [showDetails, setShowDetails] = useState(false)
const { balance: amount, formatted: amountFormatted } = utils.toBalance(inputAmount ?? '0')

if (!address || !wallet[address]?.pk) {
if (wallet?.pk === undefined) {
return <Navigate to={paths.signup} />
}
const pk = wallet[address]?.pk
const pk = wallet.pk

const execute = async (): Promise<void> => {
if (!calldata) {
Expand Down Expand Up @@ -97,7 +95,7 @@ export const ApproveTransaction = ({ calldata, caller, callback, comment, inputA
const returnUrl = new URL(callback)
returnUrl.searchParams.append('success', 'true')
returnUrl.searchParams.append('hash', receipt.hash)
returnUrl.searchParams.append('address', address)
returnUrl.searchParams.append('address', wallet.address)
toast.success(`Returning to app at ${returnUrl.hostname}`)
setTimeout(() => {
location.href = returnUrl.href
Expand Down Expand Up @@ -185,10 +183,8 @@ const ApproveTransactionPage = (): React.JSX.Element => {
const callback = utils.safeURL(Buffer.from(decodeURIComponent(callbackEncoded ?? ''), 'base64').toString())

const calldata = decodeCalldata(calldataB64Encoded)

const wallet = useSelector<RootState, WalletState>(state => state.wallet ?? {})
const address = Object.keys(wallet).find(e => apis.web3.isValidAddress(e)) ?? ''
const pk = wallet[address]?.pk
const { wallet } = useMultipleWallet()
const pk = wallet?.pk

useEffect(() => {
if (phone) {
Expand Down
19 changes: 10 additions & 9 deletions client/src/pages/NFT.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
type UseMetadataParams,
type UseMetadataResult
} from './types'
import useMultipleWallet from '../hooks/useMultipleWallet'

export const MetadataURITransformer = (url?: string): string | undefined => {
const IPFSIO = /https:\/\/ipfs\.io\/ipfs\/(.+)/
Expand Down Expand Up @@ -352,9 +353,9 @@ const NFTSendModal = ({ modelVisible, setModelVisible, maxQuantity, contractAddr
const [to, setTo] = useState('')
const [amount, setAmount] = useState('1')
const [isSending, setIsSending] = useState(false)
const wallet = useSelector<RootState, WalletState>(state => state.wallet || {})
const address = Object.keys(wallet).find(e => apis.web3.isValidAddress(e))
const pk = wallet[address ?? '']?.pk
const { wallet } = useMultipleWallet()
const pk = wallet?.pk
const address = wallet?.address

const send = async (): Promise<void> => {
if (!address || !pk) {
Expand Down Expand Up @@ -489,9 +490,9 @@ const NFTViewer = ({ visible, setVisible, onClose, contractAddress, resolvedImag
const [sendModelVisible, setSendModalVisible] = useState(false)
const [managementVisible, setManagementVisible] = useState(false)
const [isTracking, setIsTracking] = useState(false)
const wallet = useSelector<RootState, WalletState>(state => state.wallet || {})
const address = Object.keys(wallet).find(e => apis.web3.isValidAddress(e))
const pk = wallet[address ?? '']?.pk
const { wallet } = useMultipleWallet()
const address = wallet?.address
const pk = wallet?.pk
const key = utils.computeTokenKey({ contractAddress, tokenId, tokenType }).string
const balance = BigInt(useSelector<RootState, string>(state => state.balance?.[address ?? '']?.tokenBalances?.[key] || ''))

Expand Down Expand Up @@ -611,9 +612,9 @@ const NFTTracker = ({ visible, setVisible }: NFTTrackerParams): React.JSX.Elemen
const [contract, setContract] = useState('')
const [tokenId, setTokenId] = useState('')
const [isTracking, setIsTracking] = useState(false)
const wallet = useSelector<RootState, WalletState>(state => state.wallet || {})
const address = Object.keys(wallet).find(e => apis.web3.isValidAddress(e))
const pk = wallet[address ?? '']?.pk
const { wallet } = useMultipleWallet()
const address = wallet?.address
const pk = wallet?.pk

const track = async (): Promise<void> => {
if (!address || !pk) {
Expand Down
11 changes: 5 additions & 6 deletions client/src/pages/Request.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { useDispatch } from 'react-redux'
import { Navigate, useNavigate, useParams } from 'react-router'
import paths from './paths'
import MainContainer from '../components/Container'
Expand All @@ -11,15 +11,15 @@ import { ApproveTransaction, decodeCalldata } from './ApproveTransaction'
import { Button } from '../components/Controls'
import { TailSpin } from 'react-loading-icons'
import { globalActions } from '../state/modules/global'
import { type RootState } from '../state/rootReducer'
import { type WalletState } from '../state/modules/wallet/reducers'
import querystring from 'query-string'
import useMultipleWallet from '../hooks/useMultipleWallet'

const Request = (): React.JSX.Element => {
const dispatch = useDispatch()
const navigate = useNavigate()
const wallet = useSelector<RootState, WalletState>(state => state.wallet || {})
const address = Object.keys(wallet).find(e => apis.web3.isValidAddress(e))
const { wallet } = useMultipleWallet()
const address = wallet?.address
const pk = wallet?.pk
const match = useParams()
const { id } = match ?? {}
const qs = querystring.parse(location.search) as Record<string, string>
Expand All @@ -33,7 +33,6 @@ const Request = (): React.JSX.Element => {
} = request ?? {}
const calldata = decodeCalldata(calldataB64Encoded)
const callback = utils.safeURL(callbackURL ?? '')
const pk = wallet[address ?? '']?.pk

useEffect(() => {
if (phone) {
Expand Down
26 changes: 12 additions & 14 deletions client/src/pages/SaveRecoverySecret.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,25 @@
import React, { useEffect, useState } from 'react'
import MainContainer from '../components/Container'
import { useDispatch, useSelector } from 'react-redux'
import { type RootState } from '../state/rootReducer'
import { type WalletState } from '../state/modules/wallet/reducers'
import apis from '../api'
import { useDispatch } from 'react-redux'
import { Navigate, useNavigate } from 'react-router'
import paths from './paths'
import { BaseText, DescLeft, LinkText, SmallText } from '../components/Text'
import { Col, Row } from '../components/Layout'
import { Button, LinkWrarpper } from '../components/Controls'
import { globalActions } from '../state/modules/global'
import { utils } from '../utils'
import { walletActions } from '../state/modules/wallet'
import { toast } from 'react-toastify'
import useMultipleWallet from '../hooks/useMultipleWallet'

const SaveRecoverySecret = (): React.JSX.Element => {
const [tgDest, setTgDest] = useState('')
const [emailDest, setEmailDest] = useState('')
const navigate = useNavigate()
const dispatch = useDispatch()
const wallet = useSelector<RootState, WalletState>(state => state.wallet || {})
const address = Object.keys(wallet).find(e => apis.web3.isValidAddress(e))

const state = wallet[address ?? '']
const pk = state?.pk
const phone = state?.phone
const p = state?.p
const { wallet } = useMultipleWallet()
const address = wallet?.address
const pk = wallet?.pk
const phone = wallet?.phone
const p = wallet?.p

useEffect(() => {
if (!pk || !p || !phone) {
Expand All @@ -41,7 +35,11 @@ const SaveRecoverySecret = (): React.JSX.Element => {
}, [phone, p, pk, address])

const cleanup = (): void => {
dispatch(walletActions.updateWallet({ ...state, p: '' }))
if (!wallet) {
return
}

dispatch(walletActions.updateWallet({ ...wallet, p: '' }))
toast.info('Recovery secret is cleaned up')
navigate(paths.wallet)
}
Expand Down
12 changes: 6 additions & 6 deletions client/src/pages/SignMessage.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useEffect } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { useDispatch } from 'react-redux'
import paths from './paths'
import MainContainer from '../components/Container'
import querystring from 'query-string'
Expand All @@ -10,15 +10,12 @@ import { toast } from 'react-toastify'
import { Row } from '../components/Layout'
import { utils } from '../utils'
import { globalActions } from '../state/modules/global'
import { type RootState } from '../state/rootReducer'
import { type WalletState } from '../state/modules/wallet/reducers'
import { Navigate, useNavigate } from 'react-router'
import useMultipleWallet from '../hooks/useMultipleWallet'

const SignMessage = (): React.JSX.Element => {
const dispatch = useDispatch()
const navigate = useNavigate()
const wallet = useSelector<RootState, WalletState>(state => state.wallet || {})
const address = Object.keys(wallet).find(e => apis.web3.isValidAddress(e))
const qs = querystring.parse(location.search) as Record<string, string | undefined>
const callback = utils.safeURL(Buffer.from(decodeURIComponent(qs.callback ?? ''), 'base64').toString())
const { caller, message, comment, phone } = qs
Expand All @@ -29,7 +26,10 @@ const SignMessage = (): React.JSX.Element => {
}
}, [dispatch, phone])

const pk = wallet[address ?? '']?.pk
const { wallet } = useMultipleWallet()
const pk = wallet?.pk
const address = wallet?.address

if (!pk) {
dispatch(globalActions.setNextAction({ path: paths.sign, query: location.search }))
return <Navigate to={paths.signup} />
Expand Down
Loading