From 5fae414857bf31a43abe42267c718ae4c0cf9b33 Mon Sep 17 00:00:00 2001 From: thedoublejay Date: Wed, 4 Aug 2021 12:34:13 +0800 Subject: [PATCH] wallet address context --- .../OceanInterface/OceanInterface.test.tsx | 10 ++++- .../OceanInterface/OceanInterface.tsx | 3 +- app/contexts/WalletAddressContext.tsx | 38 +++++++++++++++++++ app/hooks/wallet/TokensAPI.ts | 3 +- .../screens/Balances/BalancesScreen.test.tsx | 11 ++++-- .../screens/Balances/BalancesScreen.tsx | 6 +-- .../Balances/screens/ReceiveScreen.test.tsx | 10 ++++- .../Balances/screens/ReceiveScreen.tsx | 5 +-- .../AppNavigator/screens/Dex/DexScreen.tsx | 3 +- .../sections/PlaygroundUTXO.tsx | 31 +++++++++++---- .../sections/PlaygroundWallet.tsx | 13 ------- app/screens/RootNavigator.tsx | 31 ++------------- app/store/wallet.test.ts | 8 ---- app/store/wallet.ts | 10 +---- 14 files changed, 100 insertions(+), 82 deletions(-) create mode 100644 app/contexts/WalletAddressContext.tsx diff --git a/app/components/OceanInterface/OceanInterface.test.tsx b/app/components/OceanInterface/OceanInterface.test.tsx index db59cf9f56..1dfba2af57 100644 --- a/app/components/OceanInterface/OceanInterface.test.tsx +++ b/app/components/OceanInterface/OceanInterface.test.tsx @@ -15,6 +15,14 @@ jest.mock('../../contexts/WalletContext', () => ({ }) })) +jest.mock("../../contexts/WalletAddressContext", () => ({ + useWalletAddressContext: () => { + return { + address: 'bcrt1q6np0fh47ykhznjhrtfvduh73cgjg32yac8t07d' + } + } +})); + describe('oceanInterface', () => { it('should match snapshot with error', async () => { const initialState: Partial = { @@ -24,7 +32,6 @@ describe('oceanInterface', () => { err: new Error('An unknown error has occurred') }, wallet: { - address: 'bcrt1q6np0fh47ykhznjhrtfvduh73cgjg32yac8t07d', utxoBalance: '77', tokens: [] } @@ -56,7 +63,6 @@ describe('oceanInterface', () => { }] }, wallet: { - address: 'bcrt1q6np0fh47ykhznjhrtfvduh73cgjg32yac8t07d', utxoBalance: '77', tokens: [] } diff --git a/app/components/OceanInterface/OceanInterface.tsx b/app/components/OceanInterface/OceanInterface.tsx index ae018282fa..2dae56dc61 100644 --- a/app/components/OceanInterface/OceanInterface.tsx +++ b/app/components/OceanInterface/OceanInterface.tsx @@ -7,6 +7,7 @@ import { ActivityIndicator, Animated, Linking, TouchableOpacity, View } from 're import { useDispatch, useSelector } from 'react-redux' import { Text } from '..' import { Logging } from '../../api' +import { useWalletAddressContext } from '../../contexts/WalletAddressContext' import { useWallet } from '../../contexts/WalletContext' import { useWhaleApiClient } from '../../contexts/WhaleContext' import { getEnvironment } from '../../environment' @@ -87,11 +88,11 @@ export function OceanInterface (): JSX.Element | null { const { height, err: e } = useSelector((state: RootState) => state.ocean) const transaction = useSelector((state: RootState) => firstTransactionSelector(state.ocean)) const slideAnim = useRef(new Animated.Value(0)).current - const address = useSelector((state: RootState) => state.wallet.address) // state const [tx, setTx] = useState(transaction) const [err, setError] = useState(e) const [txid, setTxid] = useState() + const { address } = useWalletAddressContext() const dismissDrawer = useCallback(() => { setTx(undefined) diff --git a/app/contexts/WalletAddressContext.tsx b/app/contexts/WalletAddressContext.tsx new file mode 100644 index 0000000000..7ab919e996 --- /dev/null +++ b/app/contexts/WalletAddressContext.tsx @@ -0,0 +1,38 @@ +import React, { createContext, useContext, useEffect, useState } from 'react' +import { Logging } from '../api' +import { useWallet } from './WalletContext' + +interface WalletAddress { + address: string +} + +const WalletAddressContext = createContext(undefined as any) + +export function useWalletAddressContext (): WalletAddress { + return useContext(WalletAddressContext) +} + +export function WalletAddressProvider (props: React.PropsWithChildren): JSX.Element | null { + const wallet = useWallet() + const [address, setAddress] = useState() + + useEffect(() => { + wallet.get(0).getAddress().then(value => { + setAddress(value) + }).catch(Logging.error) + }, []) + + if (address === undefined) { + return null + } + + const context: WalletAddress = { + address + } + + return ( + + {props.children} + + ) +} diff --git a/app/hooks/wallet/TokensAPI.ts b/app/hooks/wallet/TokensAPI.ts index 99c65634f8..d7f0e1bb58 100644 --- a/app/hooks/wallet/TokensAPI.ts +++ b/app/hooks/wallet/TokensAPI.ts @@ -2,6 +2,7 @@ import { WhaleApiClient } from '@defichain/whale-api-client' import { useEffect } from 'react' import { useDispatch, useSelector } from 'react-redux' import { Dispatch } from 'redux' +import { useWalletAddressContext } from '../../contexts/WalletAddressContext' import { useWhaleApiClient } from '../../contexts/WhaleContext' import { RootState } from '../../store' import { tokensSelector, wallet, WalletToken } from '../../store/wallet' @@ -25,7 +26,7 @@ export function fetchTokens (client: WhaleApiClient, address: string, dispatch: export function useTokensAPI (): WalletToken[] { const client = useWhaleApiClient() const tokens = useSelector((state: RootState) => tokensSelector(state.wallet)) - const address = useSelector((state: RootState) => state.wallet.address) + const { address } = useWalletAddressContext() const dispatch = useDispatch() useEffect(() => { diff --git a/app/screens/AppNavigator/screens/Balances/BalancesScreen.test.tsx b/app/screens/AppNavigator/screens/Balances/BalancesScreen.test.tsx index 8414785dff..9bce81d668 100644 --- a/app/screens/AppNavigator/screens/Balances/BalancesScreen.test.tsx +++ b/app/screens/AppNavigator/screens/Balances/BalancesScreen.test.tsx @@ -40,11 +40,18 @@ jest.mock("../../../../hooks/wallet/TokensAPI", () => ({ }] })); +jest.mock("../../../../contexts/WalletAddressContext", () => ({ + useWalletAddressContext: () => { + return { + address: 'bcrt1q6np0fh47ykhznjhrtfvduh73cgjg32yac8t07d' + } + } +})); + describe('balances page', () => { it('should match snapshot', async () => { const initialState: Partial = { wallet: { - address: 'bcrt1q6np0fh47ykhznjhrtfvduh73cgjg32yac8t07d', utxoBalance: '77', tokens: [] } @@ -69,7 +76,6 @@ describe('balances page', () => { /*it.skip('should display navigation buttons', async () => { const initialState: Partial = { wallet: { - address: 'bcrt1q6np0fh47ykhznjhrtfvduh73cgjg32yac8t07d', utxoBalance: '77', tokens: [] } @@ -97,7 +103,6 @@ describe('balances page', () => { it('should navigate to token detail page', async () => { const initialState: Partial = { wallet: { - address: 'bcrt1q6np0fh47ykhznjhrtfvduh73cgjg32yac8t07d', utxoBalance: '77', tokens: [] } diff --git a/app/screens/AppNavigator/screens/Balances/BalancesScreen.tsx b/app/screens/AppNavigator/screens/Balances/BalancesScreen.tsx index fea388e32a..a1228ce8c6 100644 --- a/app/screens/AppNavigator/screens/Balances/BalancesScreen.tsx +++ b/app/screens/AppNavigator/screens/Balances/BalancesScreen.tsx @@ -5,13 +5,13 @@ import * as React from 'react' import { useCallback, useEffect, useState } from 'react' import { FlatList, RefreshControl, TouchableOpacity } from 'react-native' import NumberFormat from 'react-number-format' -import { useDispatch, useSelector } from 'react-redux' +import { useDispatch } from 'react-redux' import { Text, View } from '../../../../components' import { getTokenIcon } from '../../../../components/icons/tokens/_index' import { SectionTitle } from '../../../../components/SectionTitle' +import { useWalletAddressContext } from '../../../../contexts/WalletAddressContext' import { useWhaleApiClient } from '../../../../contexts/WhaleContext' import { fetchTokens, useTokensAPI } from '../../../../hooks/wallet/TokensAPI' -import { RootState } from '../../../../store' import { ocean } from '../../../../store/ocean' import { WalletToken } from '../../../../store/wallet' import { tailwind } from '../../../../tailwind' @@ -23,7 +23,7 @@ type Props = StackScreenProps export function BalancesScreen ({ navigation }: Props): JSX.Element { const height = useBottomTabBarHeight() const client = useWhaleApiClient() - const address = useSelector((state: RootState) => state.wallet.address) + const { address } = useWalletAddressContext() const [refreshing, setRefreshing] = useState(false) const dispatch = useDispatch() diff --git a/app/screens/AppNavigator/screens/Balances/screens/ReceiveScreen.test.tsx b/app/screens/AppNavigator/screens/Balances/screens/ReceiveScreen.test.tsx index d2303bc521..cff3b3fb38 100644 --- a/app/screens/AppNavigator/screens/Balances/screens/ReceiveScreen.test.tsx +++ b/app/screens/AppNavigator/screens/Balances/screens/ReceiveScreen.test.tsx @@ -7,6 +7,14 @@ import { RootState } from "../../../../../store"; import { wallet } from "../../../../../store/wallet"; import { ReceiveScreen } from "./ReceiveScreen"; +jest.mock("../../../../../contexts/WalletAddressContext", () => ({ + useWalletAddressContext: () => { + return { + address: 'bcrt1q6np0fh47ykhznjhrtfvduh73cgjg32yac8t07d' + } + } +})); + jest.mock("expo-clipboard", () => ({ setString: jest.fn() })) @@ -15,7 +23,6 @@ describe('receive page', () => { it('should match snapshot', async () => { const initialState: Partial = { wallet: { - address: 'bcrt1q6np0fh47ykhznjhrtfvduh73cgjg32yac8t07d', utxoBalance: '77', tokens: [] } @@ -36,7 +43,6 @@ describe('receive page', () => { it('should trigger copy', async () => { const initialState: Partial = { wallet: { - address: 'bcrt1q6np0fh47ykhznjhrtfvduh73cgjg32yac8t07d', utxoBalance: '77', tokens: [] } diff --git a/app/screens/AppNavigator/screens/Balances/screens/ReceiveScreen.tsx b/app/screens/AppNavigator/screens/Balances/screens/ReceiveScreen.tsx index 8fd0bdc746..e9642f14e9 100644 --- a/app/screens/AppNavigator/screens/Balances/screens/ReceiveScreen.tsx +++ b/app/screens/AppNavigator/screens/Balances/screens/ReceiveScreen.tsx @@ -3,14 +3,13 @@ import * as Clipboard from 'expo-clipboard' import React from 'react' import { TouchableOpacity, View } from 'react-native' import QRCode from 'react-native-qrcode-svg' -import { useSelector } from 'react-redux' import { Text } from '../../../../../components' -import { RootState } from '../../../../../store' +import { useWalletAddressContext } from '../../../../../contexts/WalletAddressContext' import { tailwind } from '../../../../../tailwind' import { translate } from '../../../../../translations' export function ReceiveScreen (): JSX.Element { - const address = useSelector((state: RootState) => state.wallet.address) + const { address } = useWalletAddressContext() const copyToClipboard = (text: string): void => { Clipboard.setString(text) diff --git a/app/screens/AppNavigator/screens/Dex/DexScreen.tsx b/app/screens/AppNavigator/screens/Dex/DexScreen.tsx index b3079823e6..b599f82691 100644 --- a/app/screens/AppNavigator/screens/Dex/DexScreen.tsx +++ b/app/screens/AppNavigator/screens/Dex/DexScreen.tsx @@ -10,6 +10,7 @@ import { useDispatch, useSelector } from 'react-redux' import { Text, View } from '../../../../components' import { getTokenIcon } from '../../../../components/icons/tokens/_index' import { SectionTitle } from '../../../../components/SectionTitle' +import { useWalletAddressContext } from '../../../../contexts/WalletAddressContext' import { useWhaleApiClient } from '../../../../contexts/WhaleContext' import { fetchTokens } from '../../../../hooks/wallet/TokensAPI' import { RootState } from '../../../../store' @@ -19,7 +20,7 @@ import { DexParamList } from './DexNavigator' export function DexScreen (): JSX.Element { const client = useWhaleApiClient() - const address = useSelector((state: RootState) => state.wallet.address) + const { address } = useWalletAddressContext() const [pairs, setPairs] = useState>>([]) const dispatch = useDispatch() const navigation = useNavigation>() diff --git a/app/screens/PlaygroundNavigator/sections/PlaygroundUTXO.tsx b/app/screens/PlaygroundNavigator/sections/PlaygroundUTXO.tsx index 48a013e116..0218046b94 100644 --- a/app/screens/PlaygroundNavigator/sections/PlaygroundUTXO.tsx +++ b/app/screens/PlaygroundNavigator/sections/PlaygroundUTXO.tsx @@ -1,13 +1,18 @@ import React, { useEffect, useState } from 'react' +import { useDispatch } from 'react-redux' import { Text, View } from '../../../components' import { usePlaygroundContext } from '../../../contexts/PlaygroundContext' import { useWallet } from '../../../contexts/WalletContext' +import { useWhaleApiClient } from '../../../contexts/WhaleContext' +import { fetchTokens } from '../../../hooks/wallet/TokensAPI' import { tailwind } from '../../../tailwind' import { PlaygroundAction } from '../components/PlaygroundAction' import { PlaygroundStatus } from '../components/PlaygroundStatus' export function PlaygroundUTXO (): JSX.Element { const wallet = useWallet() + const whaleApiClient = useWhaleApiClient() + const dispatch = useDispatch() const { api, rpc } = usePlaygroundContext() const [status, setStatus] = useState('loading') @@ -33,14 +38,24 @@ export function PlaygroundUTXO (): JSX.Element { {status === 'online' ? ( - { - const address = await wallet.get(0).getAddress() - await rpc.wallet.sendToAddress(address, 10) - }} - /> + <> + { + const address = await wallet.get(0).getAddress() + await rpc.wallet.sendToAddress(address, 10) + }} + /> + { + const address = await wallet.get(0).getAddress() + fetchTokens(whaleApiClient, address, dispatch) + }} + /> + ) : null} ) diff --git a/app/screens/PlaygroundNavigator/sections/PlaygroundWallet.tsx b/app/screens/PlaygroundNavigator/sections/PlaygroundWallet.tsx index ad2f26f9b2..ec3e5ffe93 100644 --- a/app/screens/PlaygroundNavigator/sections/PlaygroundWallet.tsx +++ b/app/screens/PlaygroundNavigator/sections/PlaygroundWallet.tsx @@ -1,14 +1,10 @@ import { generateMnemonicWords } from '@defichain/jellyfish-wallet-mnemonic' import * as Random from 'expo-random' import React from 'react' -import { useDispatch, useSelector } from 'react-redux' import { MnemonicUnprotected } from '../../../api/wallet' import { Text, View } from '../../../components' import { useNetworkContext } from '../../../contexts/NetworkContext' import { useWalletPersistenceContext } from '../../../contexts/WalletPersistenceContext' -import { useWhaleApiClient } from '../../../contexts/WhaleContext' -import { fetchTokens } from '../../../hooks/wallet/TokensAPI' -import { RootState } from '../../../store' import { tailwind } from '../../../tailwind' import { PlaygroundAction } from '../components/PlaygroundAction' import { PlaygroundStatus } from '../components/PlaygroundStatus' @@ -16,9 +12,6 @@ import { PlaygroundStatus } from '../components/PlaygroundStatus' export function PlaygroundWallet (): JSX.Element | null { const { wallets, clearWallets, setWallet } = useWalletPersistenceContext() const network = useNetworkContext() - const whaleApiClient = useWhaleApiClient() - const dispatch = useDispatch() - const address = useSelector((state: RootState) => state.wallet.address) return ( @@ -56,12 +49,6 @@ export function PlaygroundWallet (): JSX.Element | null { await setWallet(MnemonicUnprotected.toData(words, network.network)) }} /> - - fetchTokens(whaleApiClient, address, dispatch)} - /> ) } diff --git a/app/screens/RootNavigator.tsx b/app/screens/RootNavigator.tsx index 779f261b6d..c95359c04e 100644 --- a/app/screens/RootNavigator.tsx +++ b/app/screens/RootNavigator.tsx @@ -1,9 +1,7 @@ -import React, { PropsWithChildren, useEffect, useState } from 'react' -import { useDispatch } from 'react-redux' -import { Logging } from '../api' -import { useWallet, WalletProvider } from '../contexts/WalletContext' +import React from 'react' +import { WalletAddressProvider } from '../contexts/WalletAddressContext' +import { WalletProvider } from '../contexts/WalletContext' import { useWalletPersistenceContext } from '../contexts/WalletPersistenceContext' -import { wallet as store } from '../store/wallet' import { AppNavigator } from './AppNavigator/AppNavigator' import { WalletNavigator } from './WalletNavigator/WalletNavigator' @@ -25,26 +23,3 @@ export function RootNavigator (): JSX.Element { ) } - -/** - * TODO(fuxingloh): to deprecate completely - * @deprecated included for legacy reasons, moving forward address should not be set in store - */ -function WalletAddressProvider (props: PropsWithChildren): JSX.Element | null { - const wallet = useWallet() - const dispatch = useDispatch() - const [isLoaded, setLoaded] = useState(false) - - useEffect(() => { - wallet.get(0).getAddress().then(address => { - dispatch(store.actions.setAddress(address)) - setLoaded(true) - }).catch(Logging.error) - }, [wallet]) - - if (!isLoaded) { - return null - } - - return props.children -} diff --git a/app/store/wallet.test.ts b/app/store/wallet.test.ts index d708b3a1ce..c11c4aa7f3 100644 --- a/app/store/wallet.test.ts +++ b/app/store/wallet.test.ts @@ -9,7 +9,6 @@ describe('wallet reducer', () => { initialState = { tokens: [], utxoBalance: '0', - address: '' }; tokenDFI = { id: '0', @@ -35,7 +34,6 @@ describe('wallet reducer', () => { expect(wallet.reducer(undefined, { type: 'unknown' })).toEqual({ utxoBalance: '0', tokens: [], - address: '' }); }); @@ -51,12 +49,6 @@ describe('wallet reducer', () => { expect(actual.utxoBalance).toStrictEqual(utxoAmount) }); - it('should handle setAddress', () => { - const address = 'bcrt1q6np0fh47ykhznjhrtfvduh73cgjg32yac8t07d' - const actual = wallet.reducer(initialState, wallet.actions.setAddress(address)); - expect(actual.address).toStrictEqual(address) - }); - it('should able to select tokens with default DFIs', () => { const actual = tokensSelector({ ...initialState, utxoBalance: '77' }) expect(actual).toStrictEqual([{ ...utxoDFI, amount: '77' }, { ...tokenDFI, amount: '0' }]) diff --git a/app/store/wallet.ts b/app/store/wallet.ts index 9c07271d84..e1230f9721 100644 --- a/app/store/wallet.ts +++ b/app/store/wallet.ts @@ -10,16 +10,11 @@ export interface WalletToken extends AddressToken { export interface WalletState { utxoBalance: string tokens: WalletToken[] - /** - * @deprecated use `useWalletContext().get(0).getAddress()` instead - */ - address: string } const initialState: WalletState = { utxoBalance: '0', - tokens: [], - address: '' + tokens: [] } const tokenDFI: WalletToken = { @@ -61,9 +56,6 @@ export const wallet = createSlice({ }, setUtxoBalance: (state, action: PayloadAction) => { state.utxoBalance = action.payload - }, - setAddress: (state, action: PayloadAction) => { - state.address = action.payload } } })