From c45b5401a74f7e0c09603d4c76a16c99ea3b61f8 Mon Sep 17 00:00:00 2001 From: ivan Date: Mon, 19 Jul 2021 16:30:08 +0800 Subject: [PATCH 1/9] add qr scanner as a standalone page in balancestacknav --- app/components/BarCodeScanner.tsx | 52 +++++++++++++++++++ .../screens/Balances/BalancesNavigator.tsx | 10 ++++ .../screens/Balances/screens/SendScreen.tsx | 20 ++++--- package-lock.json | 20 +++++++ package.json | 1 + 5 files changed, 97 insertions(+), 6 deletions(-) create mode 100644 app/components/BarCodeScanner.tsx diff --git a/app/components/BarCodeScanner.tsx b/app/components/BarCodeScanner.tsx new file mode 100644 index 0000000000..8e69305dc1 --- /dev/null +++ b/app/components/BarCodeScanner.tsx @@ -0,0 +1,52 @@ +import React, { useState, useEffect } from 'react' +import { Text } from 'react-native' +import { BarCodeScanner as DefaultBarCodeScanner } from 'expo-barcode-scanner' +import { Logging } from '../api/logging' +import { translate } from '../translations' +import tailwind from 'tailwind-rn' +import { View } from '.' +import { StackScreenProps } from '@react-navigation/stack' +import { BalanceParamList } from '../screens/AppNavigator/screens/Balances/BalancesNavigator' + +type Props = StackScreenProps + +export function BarCodeScanner ({ route, navigation }: Props): JSX.Element { + // null => undetermined + const [hasPermission, setHasPermission] = useState(null) + + useEffect(() => { + DefaultBarCodeScanner.requestPermissionsAsync() + .then(({ status }) => { + switch (status) { + case 'granted': setHasPermission(true); break + case 'denied': setHasPermission(false); break + } + }) + .catch(e => Logging.error(e)) + }, []) + + const handleBarCodeScanned = ({ data }: { type: string, data: string }): void => { + route.params.onQrScanned(data) + navigation.goBack() + } + + if (hasPermission === null) { + return ( + + {translate('components/BarCodeScanner', 'Requesting for camera permission')} + + ) + } + if (!hasPermission) { + + {translate('components/BarCodeScanner', 'You have denied the permission request to use your camera')} + + } + + return ( + + ) +} diff --git a/app/screens/AppNavigator/screens/Balances/BalancesNavigator.tsx b/app/screens/AppNavigator/screens/Balances/BalancesNavigator.tsx index 45e7841f40..de20a9c67a 100644 --- a/app/screens/AppNavigator/screens/Balances/BalancesNavigator.tsx +++ b/app/screens/AppNavigator/screens/Balances/BalancesNavigator.tsx @@ -3,6 +3,7 @@ import * as React from 'react' import { View } from 'react-native' import tailwind from 'tailwind-rn' import { Text } from '../../../../components' +import { BarCodeScanner } from '../../../../components/BarCodeScanner' import { getTokenIcon } from '../../../../components/icons/tokens/_index' import { WalletToken } from '../../../../store/wallet' import { translate } from '../../../../translations' @@ -18,6 +19,7 @@ export interface BalanceParamList { SendScreen: { token: WalletToken } TokenDetailScreen: { token: WalletToken } ConvertScreen: { mode: ConversionMode } + BarCodeScanner: { onQrScanned: (value: string) => void } [key: string]: undefined | object } @@ -76,6 +78,14 @@ export function BalancesNavigator (): JSX.Element { headerBackTitleVisible: false }} /> + ) } diff --git a/app/screens/AppNavigator/screens/Balances/screens/SendScreen.tsx b/app/screens/AppNavigator/screens/Balances/screens/SendScreen.tsx index 9047b9b737..59a18e1059 100644 --- a/app/screens/AppNavigator/screens/Balances/screens/SendScreen.tsx +++ b/app/screens/AppNavigator/screens/Balances/screens/SendScreen.tsx @@ -2,6 +2,7 @@ import { DeFiAddress } from '@defichain/jellyfish-address' import { NetworkName } from '@defichain/jellyfish-network' import { CTransactionSegWit, TransactionSegWit } from '@defichain/jellyfish-transaction' import { AddressToken } from '@defichain/whale-api-client/dist/api/address' +import { MaterialIcons } from '@expo/vector-icons' import { StackScreenProps } from '@react-navigation/stack' import BigNumber from 'bignumber.js' import React, { useEffect, useState } from 'react' @@ -17,7 +18,7 @@ import { Text, TextInput } from '../../../../../components' import { getTokenIcon } from '../../../../../components/icons/tokens/_index' import { PrimaryButton } from '../../../../../components/PrimaryButton' import { SectionTitle } from '../../../../../components/SectionTitle' -import { PrimaryColorStyle } from '../../../../../constants/Theme' +import { PrimaryColor, PrimaryColorStyle } from '../../../../../constants/Theme' import { useNetworkContext } from '../../../../../contexts/NetworkContext' import { useWallet } from '../../../../../contexts/WalletContext' import { useWhaleApiClient } from '../../../../../contexts/WhaleContext' @@ -72,7 +73,7 @@ async function send ({ type Props = StackScreenProps -export function SendScreen ({ route }: Props): JSX.Element { +export function SendScreen ({ route, navigation }: Props): JSX.Element { const { networkName } = useNetworkContext() const wallet = useWallet() const client = useWhaleApiClient() @@ -101,7 +102,13 @@ export function SendScreen ({ route }: Props): JSX.Element { return ( - + navigation.navigate('BarCodeScanner', { + onQrScanned: value => setValue('address', value) + })} + /> { @@ -129,7 +136,7 @@ export function SendScreen ({ route }: Props): JSX.Element { ) } -function AddressRow ({ control, networkName }: { control: Control, networkName: NetworkName }): JSX.Element { +function AddressRow ({ control, networkName, onQrButtonPress }: { control: Control, networkName: NetworkName, onQrButtonPress: () => void }): JSX.Element { return ( <> - {/* - */} + )} name='address' diff --git a/package-lock.json b/package-lock.json index 6ce8ff2416..7db9e19592 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,6 +30,7 @@ "buffer": "^6.0.3", "expo": "^42.0.0", "expo-asset": "~8.3.2", + "expo-barcode-scanner": "^10.2.2", "expo-cli": "^4.7.3", "expo-clipboard": "~1.1.0", "expo-constants": "~11.0.1", @@ -14583,6 +14584,15 @@ "url-parse": "^1.4.4" } }, + "node_modules/expo-barcode-scanner": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/expo-barcode-scanner/-/expo-barcode-scanner-10.2.2.tgz", + "integrity": "sha512-FWGyNB88kntUV1ckpEcCiq8iepu9EJO/cK0bDnp42ieydBhKlTPXH/d/3ipBFbE+Ia7MNfddjzmzbDCadJukcg==", + "dependencies": { + "@expo/config-plugins": "^3.0.0", + "expo-modules-core": "~0.2.0" + } + }, "node_modules/expo-cli": { "version": "4.7.3", "resolved": "https://registry.npmjs.org/expo-cli/-/expo-cli-4.7.3.tgz", @@ -48879,6 +48889,15 @@ "url-parse": "^1.4.4" } }, + "expo-barcode-scanner": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/expo-barcode-scanner/-/expo-barcode-scanner-10.2.2.tgz", + "integrity": "sha512-FWGyNB88kntUV1ckpEcCiq8iepu9EJO/cK0bDnp42ieydBhKlTPXH/d/3ipBFbE+Ia7MNfddjzmzbDCadJukcg==", + "requires": { + "@expo/config-plugins": "^3.0.0", + "expo-modules-core": "~0.2.0" + } + }, "expo-cli": { "version": "4.7.3", "resolved": "https://registry.npmjs.org/expo-cli/-/expo-cli-4.7.3.tgz", @@ -56454,6 +56473,7 @@ "resolved": "https://registry.npmjs.org/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.59.0.tgz", "integrity": "sha512-1O3wrnMq4NcPQ1asEcl9lRDn/t+F1Oef6S9WaYVIKEhg9m/EQRGVrrTVP+R6B5Eeaj3+zNKbzM8Dx/NWy1hUbQ==", "requires": { + "@babel/core": "^7.0.0", "babel-preset-fbjs": "^3.3.0", "metro-babel-transformer": "0.59.0", "metro-react-native-babel-preset": "0.59.0", diff --git a/package.json b/package.json index 994a86d677..0d54b32c89 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "buffer": "^6.0.3", "expo": "^42.0.0", "expo-asset": "~8.3.2", + "expo-barcode-scanner": "^10.2.2", "expo-cli": "^4.7.3", "expo-clipboard": "~1.1.0", "expo-constants": "~11.0.1", From 51f4faf2dfdbab9b396262595bf3e3b669c6784f Mon Sep 17 00:00:00 2001 From: ivan Date: Mon, 19 Jul 2021 17:17:09 +0800 Subject: [PATCH 2/9] fix scanner not navigate correctly, add hardcoded address callback in debug mode --- app/components/BarCodeScanner.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/components/BarCodeScanner.tsx b/app/components/BarCodeScanner.tsx index 8e69305dc1..605a1ac04f 100644 --- a/app/components/BarCodeScanner.tsx +++ b/app/components/BarCodeScanner.tsx @@ -7,6 +7,7 @@ import tailwind from 'tailwind-rn' import { View } from '.' import { StackScreenProps } from '@react-navigation/stack' import { BalanceParamList } from '../screens/AppNavigator/screens/Balances/BalancesNavigator' +import { getEnvironment } from '../environment' type Props = StackScreenProps @@ -23,11 +24,16 @@ export function BarCodeScanner ({ route, navigation }: Props): JSX.Element { } }) .catch(e => Logging.error(e)) + + // for dev/test use, browser scenario, auto return with a hardcoded regtest env address + if (getEnvironment().debug) { + setTimeout(() => handleBarCodeScanned({ data: 'mmtWjUvgmyWXpekoxBgMJqKVMTtqA6NH1T' }), 3000) + } }, []) - const handleBarCodeScanned = ({ data }: { type: string, data: string }): void => { + const handleBarCodeScanned = ({ data }: { data: string }): void => { route.params.onQrScanned(data) - navigation.goBack() + navigation.pop() } if (hasPermission === null) { From ee2ba71b1394be53b2fd3796f0dac6901b2dfbf6 Mon Sep 17 00:00:00 2001 From: ivan Date: Mon, 19 Jul 2021 17:31:42 +0800 Subject: [PATCH 3/9] try fixing pop more than 1 page in mobile --- app/components/BarCodeScanner.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/components/BarCodeScanner.tsx b/app/components/BarCodeScanner.tsx index 605a1ac04f..212da1159d 100644 --- a/app/components/BarCodeScanner.tsx +++ b/app/components/BarCodeScanner.tsx @@ -14,6 +14,7 @@ type Props = StackScreenProps export function BarCodeScanner ({ route, navigation }: Props): JSX.Element { // null => undetermined const [hasPermission, setHasPermission] = useState(null) + const scanned = false useEffect(() => { DefaultBarCodeScanner.requestPermissionsAsync() @@ -32,8 +33,10 @@ export function BarCodeScanner ({ route, navigation }: Props): JSX.Element { }, []) const handleBarCodeScanned = ({ data }: { data: string }): void => { - route.params.onQrScanned(data) - navigation.pop() + if (!scanned) { + route.params.onQrScanned(data) + navigation.pop() + } } if (hasPermission === null) { From f1e088f5563f91e62fd3cac3671b938bc360ca4a Mon Sep 17 00:00:00 2001 From: ivan Date: Mon, 19 Jul 2021 17:38:16 +0800 Subject: [PATCH 4/9] move flag to useState --- app/components/BarCodeScanner.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/components/BarCodeScanner.tsx b/app/components/BarCodeScanner.tsx index 212da1159d..6bcb8b38f7 100644 --- a/app/components/BarCodeScanner.tsx +++ b/app/components/BarCodeScanner.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react' +import React, { useState, useEffect, useCallback } from 'react' import { Text } from 'react-native' import { BarCodeScanner as DefaultBarCodeScanner } from 'expo-barcode-scanner' import { Logging } from '../api/logging' @@ -14,7 +14,7 @@ type Props = StackScreenProps export function BarCodeScanner ({ route, navigation }: Props): JSX.Element { // null => undetermined const [hasPermission, setHasPermission] = useState(null) - const scanned = false + const [scanned, setScanned] = useState(false) useEffect(() => { DefaultBarCodeScanner.requestPermissionsAsync() @@ -32,12 +32,13 @@ export function BarCodeScanner ({ route, navigation }: Props): JSX.Element { } }, []) - const handleBarCodeScanned = ({ data }: { data: string }): void => { + const handleBarCodeScanned = useCallback(({ data }: { data: string }): void => { if (!scanned) { + setScanned(true) route.params.onQrScanned(data) navigation.pop() } - } + }, []) if (hasPermission === null) { return ( From 4d04cd339ad0b1cfa8b376bf3251c7fd7f039657 Mon Sep 17 00:00:00 2001 From: ivan Date: Mon, 19 Jul 2021 17:47:12 +0800 Subject: [PATCH 5/9] remove debug mode code, published preview has debug enabled --- app/components/BarCodeScanner.tsx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/app/components/BarCodeScanner.tsx b/app/components/BarCodeScanner.tsx index 6bcb8b38f7..d1bd9b557b 100644 --- a/app/components/BarCodeScanner.tsx +++ b/app/components/BarCodeScanner.tsx @@ -7,7 +7,6 @@ import tailwind from 'tailwind-rn' import { View } from '.' import { StackScreenProps } from '@react-navigation/stack' import { BalanceParamList } from '../screens/AppNavigator/screens/Balances/BalancesNavigator' -import { getEnvironment } from '../environment' type Props = StackScreenProps @@ -25,11 +24,6 @@ export function BarCodeScanner ({ route, navigation }: Props): JSX.Element { } }) .catch(e => Logging.error(e)) - - // for dev/test use, browser scenario, auto return with a hardcoded regtest env address - if (getEnvironment().debug) { - setTimeout(() => handleBarCodeScanned({ data: 'mmtWjUvgmyWXpekoxBgMJqKVMTtqA6NH1T' }), 3000) - } }, []) const handleBarCodeScanned = useCallback(({ data }: { data: string }): void => { From 663f05ba875a4671891a1a2e32486a06258f83e7 Mon Sep 17 00:00:00 2001 From: ivan Date: Mon, 19 Jul 2021 18:06:59 +0800 Subject: [PATCH 6/9] test disabling pop --- app/components/BarCodeScanner.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/BarCodeScanner.tsx b/app/components/BarCodeScanner.tsx index d1bd9b557b..46b30ac800 100644 --- a/app/components/BarCodeScanner.tsx +++ b/app/components/BarCodeScanner.tsx @@ -30,7 +30,7 @@ export function BarCodeScanner ({ route, navigation }: Props): JSX.Element { if (!scanned) { setScanned(true) route.params.onQrScanned(data) - navigation.pop() + // navigation.pop() } }, []) From d5aa2a1daaebb2f407e35fd5e6a066a791601a61 Mon Sep 17 00:00:00 2001 From: ivan Date: Mon, 19 Jul 2021 18:35:52 +0800 Subject: [PATCH 7/9] try alternative --- app/components/BarCodeScanner.tsx | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/app/components/BarCodeScanner.tsx b/app/components/BarCodeScanner.tsx index 46b30ac800..eae70fa0ce 100644 --- a/app/components/BarCodeScanner.tsx +++ b/app/components/BarCodeScanner.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect, useCallback } from 'react' +import React, { useState, useEffect } from 'react' import { Text } from 'react-native' import { BarCodeScanner as DefaultBarCodeScanner } from 'expo-barcode-scanner' import { Logging } from '../api/logging' @@ -13,7 +13,15 @@ type Props = StackScreenProps export function BarCodeScanner ({ route, navigation }: Props): JSX.Element { // null => undetermined const [hasPermission, setHasPermission] = useState(null) - const [scanned, setScanned] = useState(false) + const [value, setValue] = useState() + + // to ensure callback only can be fired once as value change + useEffect(() => { + if (value !== undefined) { + route.params.onQrScanned(value) + navigation.pop() + } + }, [value]) useEffect(() => { DefaultBarCodeScanner.requestPermissionsAsync() @@ -26,14 +34,6 @@ export function BarCodeScanner ({ route, navigation }: Props): JSX.Element { .catch(e => Logging.error(e)) }, []) - const handleBarCodeScanned = useCallback(({ data }: { data: string }): void => { - if (!scanned) { - setScanned(true) - route.params.onQrScanned(data) - // navigation.pop() - } - }, []) - if (hasPermission === null) { return ( @@ -50,7 +50,7 @@ export function BarCodeScanner ({ route, navigation }: Props): JSX.Element { return ( { setValue(e.data) }} /> ) } From b963d36901aa8a7912cdb51a161362194e666097 Mon Sep 17 00:00:00 2001 From: ivan Date: Mon, 19 Jul 2021 19:17:46 +0800 Subject: [PATCH 8/9] fix length text push away qr scanner button --- .../AppNavigator/screens/Balances/screens/SendScreen.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/screens/AppNavigator/screens/Balances/screens/SendScreen.tsx b/app/screens/AppNavigator/screens/Balances/screens/SendScreen.tsx index 59a18e1059..72445184d6 100644 --- a/app/screens/AppNavigator/screens/Balances/screens/SendScreen.tsx +++ b/app/screens/AppNavigator/screens/Balances/screens/SendScreen.tsx @@ -162,7 +162,7 @@ function AddressRow ({ control, networkName, onQrButtonPress }: { control: Contr placeholder={translate('screens/SendScreen', 'Enter an address')} /> From 2cf41b4a8438b099c739cecabc560147967f3059 Mon Sep 17 00:00:00 2001 From: thedoublejay Date: Wed, 21 Jul 2021 13:54:34 +0800 Subject: [PATCH 9/9] updates --- app/components/BarCodeScanner.tsx | 23 +++++++++++-------- .../screens/Balances/screens/SendScreen.tsx | 3 ++- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/app/components/BarCodeScanner.tsx b/app/components/BarCodeScanner.tsx index eae70fa0ce..0781da6fea 100644 --- a/app/components/BarCodeScanner.tsx +++ b/app/components/BarCodeScanner.tsx @@ -1,12 +1,12 @@ -import React, { useState, useEffect } from 'react' -import { Text } from 'react-native' +import { StackScreenProps } from '@react-navigation/stack' import { BarCodeScanner as DefaultBarCodeScanner } from 'expo-barcode-scanner' -import { Logging } from '../api/logging' -import { translate } from '../translations' +import React, { useEffect, useState } from 'react' +import { Text } from 'react-native' import tailwind from 'tailwind-rn' import { View } from '.' -import { StackScreenProps } from '@react-navigation/stack' +import { Logging } from '../api/logging' import { BalanceParamList } from '../screens/AppNavigator/screens/Balances/BalancesNavigator' +import { translate } from '../translations' type Props = StackScreenProps @@ -42,15 +42,20 @@ export function BarCodeScanner ({ route, navigation }: Props): JSX.Element { ) } if (!hasPermission) { - - {translate('components/BarCodeScanner', 'You have denied the permission request to use your camera')} - + return ( + + {translate('components/BarCodeScanner', 'You have denied the permission request to use your camera')} + + ) } return ( { setValue(e.data) }} + barCodeTypes={[DefaultBarCodeScanner.Constants.BarCodeType.qr]} + onBarCodeScanned={(e) => { + setValue(e.data) + }} /> ) } diff --git a/app/screens/AppNavigator/screens/Balances/screens/SendScreen.tsx b/app/screens/AppNavigator/screens/Balances/screens/SendScreen.tsx index 72445184d6..8f046bc942 100644 --- a/app/screens/AppNavigator/screens/Balances/screens/SendScreen.tsx +++ b/app/screens/AppNavigator/screens/Balances/screens/SendScreen.tsx @@ -185,7 +185,8 @@ interface AmountForm { function AmountRow ({ token, control, onMaxPress, fee }: AmountForm): JSX.Element { const Icon = getTokenIcon(token.avatarSymbol) - const maxAmount = token.symbol === 'DFI' ? new BigNumber(token.amount).minus(fee).toFixed(8) : token.amount + let maxAmount = token.symbol === 'DFI' ? new BigNumber(token.amount).minus(fee).toFixed(8) : token.amount + maxAmount = BigNumber.max(maxAmount, 0).toFixed(8) return ( <>