-
Notifications
You must be signed in to change notification settings - Fork 58
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
4127: send to address #4137
4127: send to address #4137
Changes from 2 commits
ceb53a1
0b3bdc2
a952fc1
8245996
b5d8baa
f68ac96
d3236a1
17bbbf9
d8d4d00
c4152eb
e4f7e61
8b8e0ed
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -21,6 +21,7 @@ const InputText = ({ | |||||||||||
adornmentStyle, | ||||||||||||
adornmentColor, | ||||||||||||
adornmentDisabled = false, | ||||||||||||
iconAlignment = 'right', | ||||||||||||
showError = true, | ||||||||||||
error, | ||||||||||||
styles, | ||||||||||||
|
@@ -76,15 +77,20 @@ const InputText = ({ | |||||||||||
return ( | ||||||||||||
<View style={[styles.view, containerStyle]}> | ||||||||||||
<View style={styles.view}> | ||||||||||||
{showAdornment && error !== '' && iconAlignment === 'left' && ( | ||||||||||||
<TouchableOpacity style={[styles.adornment, adornmentStyle]} disabled={adornmentDisabled} onPress={_onPress}> | ||||||||||||
<Icon size={normalize(adornmentSize)} color={adornmentColor || inputColor} name={adornment} /> | ||||||||||||
</TouchableOpacity> | ||||||||||||
)} | ||||||||||||
<TextInput | ||||||||||||
{...props} | ||||||||||||
ref={getRef} | ||||||||||||
style={[styles.input, { borderBottomColor: inputColor, color: inputColor }, style]} | ||||||||||||
style={[styles.input, { borderBottomColor: error ? 'red' : inputColor, color: inputColor }, style]} | ||||||||||||
placeholderTextColor={placeholderTextColor || theme.colors.gray50Percent} | ||||||||||||
onTouchStart={onTouchStart} | ||||||||||||
onBlur={onBlurHandler} | ||||||||||||
/> | ||||||||||||
{showAdornment && error !== '' && ( | ||||||||||||
{showAdornment && error !== '' && iconAlignment === 'right' && ( | ||||||||||||
<TouchableOpacity style={[styles.adornment, adornmentStyle]} disabled={adornmentDisabled} onPress={_onPress}> | ||||||||||||
<Icon size={normalize(adornmentSize)} color={adornmentColor || inputColor} name={adornment} /> | ||||||||||||
</TouchableOpacity> | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,12 @@ | ||
// @flow | ||
import React, { useCallback, useContext, useMemo, useState } from 'react' | ||
import { KeyboardAvoidingView } from 'react-native' | ||
import { KeyboardAvoidingView, View } from 'react-native' | ||
import { t } from '@lingui/macro' | ||
import { useGetBridgeData } from '@gooddollar/web3sdk-v2' | ||
|
||
import InputWithAdornment from '../common/form/InputWithAdornment' | ||
import logger from '../../lib/logger/js-logger' | ||
import { AmountInput, ScanQRButton, Section, Wrapper } from '../common' | ||
import { AmountInput, CustomButton, ScanQRButton, Section, Wrapper } from '../common' | ||
import TopBar from '../common/view/TopBar' | ||
import { BackButton, NextButton, useScreenState } from '../appNavigation/stackNavigation' | ||
import { | ||
|
@@ -14,11 +16,24 @@ | |
useSwitchNetwork, | ||
useWallet, | ||
} from '../../lib/wallet/GoodWalletProvider' | ||
|
||
// hooks | ||
import usePermissions from '../permissions/hooks/usePermissions' | ||
import { Permissions } from '../permissions/types' | ||
import { useClipboardPaste } from '../../lib/hooks/useClipboard' | ||
|
||
import { isIOS } from '../../lib/utils/platform' | ||
import { withStyles } from '../../lib/styles' | ||
import { getDesignRelativeWidth } from '../../lib/utils/sizes' | ||
import Config from '../../config/config' | ||
import { ACTION_RECEIVE, navigationOptions } from './utils/sendReceiveFlow' | ||
import { theme } from '../theme/styles' | ||
import { | ||
ACTION_BRIDGE, | ||
ACTION_RECEIVE, | ||
ACTION_SEND, | ||
ACTION_SEND_TO_ADDRESS, | ||
navigationOptions, | ||
} from './utils/sendReceiveFlow' | ||
|
||
export type AmountProps = { | ||
screenProps: any, | ||
|
@@ -45,12 +60,81 @@ | |
|
||
const log = logger.child({ from: 'Amount' }) | ||
|
||
const NextPageButton = ({ action, cbContinue, loading, values, ...props }) => { | ||
const routeMap = { | ||
[ACTION_BRIDGE]: ['SendLinkSummary', 'Home'], | ||
[ACTION_RECEIVE]: ['Reason', 'ReceiveSummary', 'TransactionConfirmation'], | ||
isNativeFlow: ['SendToAddress', 'SendLinkSummary'], | ||
} | ||
|
||
const nextRoute = routeMap[action] || ['Reason', 'SendLinkSummary', 'TransactionConfirmation'] | ||
|
||
return ( | ||
<NextButton | ||
contentStyle={{ justifyContent: 'flex-start' }} | ||
nextRoutes={nextRoute} | ||
canContinue={cbContinue} | ||
disabled={loading} | ||
values={values} | ||
action={action} | ||
{...props} | ||
/> | ||
) | ||
} | ||
|
||
export const AddressDetails = ({ address, cb, error, setAddress, screenProps }) => { | ||
const pasteUri = useClipboardPaste(data => { | ||
setAddress(data) | ||
}) | ||
|
||
// check clipboard permission an show dialog is not allowed | ||
const [, requestClipboardPermissions] = usePermissions(Permissions.Clipboard, { | ||
requestOnMounted: false, | ||
onAllowed: pasteUri, | ||
navigate: screenProps.navigate, | ||
}) | ||
const handlePastePress = useCallback(requestClipboardPermissions) | ||
|
||
return ( | ||
<View style={{ marginTop: 8 }}> | ||
<View style={{ flexDirection: 'row' }}> | ||
<View style={{ flex: 2, justifyContent: 'flex-end' }}> | ||
<View | ||
style={{ | ||
borderWidth: 1, | ||
borderRadius: 5, | ||
paddingLeft: 8, | ||
paddingRight: 8, | ||
paddingTop: 12, | ||
paddingBottom: 12, | ||
borderColor: error ? 'red' : theme.colors.primary, | ||
}} | ||
> | ||
<InputWithAdornment | ||
showAdornment={true} | ||
adornment={'paste'} | ||
adornmentSize={32} | ||
adornmentAction={handlePastePress} | ||
adornmentStyle={{ bottom: 0, left: 8, width: 16 }} | ||
iconAlignment="left" | ||
onChangeText={cb} | ||
value={address} | ||
error={error} | ||
placeholder="Enter Wallet Address: 0x1234..." | ||
style={{ borderBottomWidth: error ? 1 : 0, textAlign: 'left' }} | ||
/> | ||
</View> | ||
</View> | ||
</View> | ||
</View> | ||
) | ||
} | ||
|
||
const Amount = (props: AmountProps) => { | ||
const { screenProps, styles } = props | ||
const { push } = screenProps | ||
const [screenState, setScreenState] = useScreenState(screenProps) | ||
const { params = {} } = props.navigation.state | ||
const { isBridge = false } = params | ||
const { amount = 0, ...restState } = screenState || {} | ||
const goodWallet = useWallet() | ||
const { currentNetwork } = useSwitchNetwork() | ||
|
@@ -59,7 +143,11 @@ | |
const { native, token, balance } = useContext(TokenContext) | ||
const { toDecimals, fromDecimals } = useFormatToken(token) | ||
const formatFixed = useFixedDecimals(token) | ||
|
||
const isNativeFlow = isDeltaApp && native | ||
const isReceive = params && params.action === ACTION_RECEIVE | ||
const isSend = params && params.action === ACTION_SEND | ||
const isBridge = params && params.action === ACTION_BRIDGE | ||
|
||
const bridgeState = isBridge | ||
? { | ||
|
@@ -71,10 +159,12 @@ | |
const [GDAmount, setGDAmount] = useState(() => (amount ? formatFixed(amount) : '')) | ||
const [loading, setLoading] = useState(() => !amount) | ||
const [error, setError] = useState() | ||
const [addressError, setAddressError] = useState() | ||
|
||
const GDAmountInWei = useMemo(() => GDAmount && fromDecimals(GDAmount), [GDAmount]) | ||
const [sendViaAddress, setSendAddress] = useState(false) | ||
const [address, setAddress] = useState('') | ||
|
||
const isReceive = params && params.action === ACTION_RECEIVE | ||
const GDAmountInWei = useMemo(() => GDAmount && fromDecimals(GDAmount), [GDAmount]) | ||
|
||
const handlePressQR = useCallback(() => push('SendByQR'), [push]) | ||
|
||
|
@@ -96,10 +186,14 @@ | |
} | ||
} | ||
|
||
const canSend = await (isNativeFlow ? goodWallet.canSendNative(weiAmount) : goodWallet.canSend(weiAmount)) | ||
|
||
let canSend = await (isNativeFlow ? goodWallet.canSendNative(weiAmount) : goodWallet.canSend(weiAmount)) | ||
if (!canSend) { | ||
setError(t`Sorry, you don't have enough ${token}s`) | ||
return canSend | ||
} | ||
|
||
if (sendViaAddress) { | ||
canSend = handleSendViaAddress(address) | ||
} | ||
|
||
return canSend | ||
|
@@ -110,6 +204,20 @@ | |
} | ||
} | ||
|
||
const handleSendViaAddress = input => { | ||
setAddressError('') | ||
setAddress(input) | ||
const isEth = /^0x[a-fA-F0-9]{40}$/ | ||
const isEthAddress = isEth.test(input) | ||
if (!isEthAddress) { | ||
setAddressError(t`Sorry, this is not a valid address.`) | ||
return false | ||
} | ||
|
||
setScreenState({ action: ACTION_SEND_TO_ADDRESS }) | ||
return true | ||
} | ||
|
||
const handleContinue = async () => { | ||
setLoading(true) | ||
|
||
|
@@ -127,6 +235,10 @@ | |
setError('') | ||
} | ||
|
||
const handleRequestAddress = () => { | ||
setSendAddress(value => !value) | ||
} | ||
|
||
const showScanQR = !isReceive && !params?.counterPartyDisplayName // ot in receive flow and also QR wasnt displayed on Who screen | ||
|
||
return ( | ||
|
@@ -146,30 +258,69 @@ | |
unit={token} | ||
/> | ||
</Section.Stack> | ||
<Section.Row> | ||
<Section.Row grow={1} justifyContent="flex-start"> | ||
<BackButton mode="text" screenProps={screenProps}> | ||
{t`Cancel`} | ||
</BackButton> | ||
</Section.Row> | ||
<Section.Stack grow={3} style={styles.nextButtonContainer}> | ||
<NextButton | ||
nextRoutes={ | ||
isBridge | ||
? ['SendLinkSummary', 'Home'] | ||
: isReceive | ||
? ['Reason', 'ReceiveSummary', 'TransactionConfirmation'] | ||
: isNativeFlow | ||
? ['SendToAddress', 'SendLinkSummary'] | ||
: ['Reason', 'SendLinkSummary', 'TransactionConfirmation'] | ||
} | ||
canContinue={handleContinue} | ||
values={{ ...params, ...restState, amount: GDAmountInWei, ...bridgeState }} | ||
disabled={loading} | ||
{...props} | ||
/> | ||
{isSend && ( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. have you tested it with the native flow ? set REACT_APP_DELTA=true in Your local env, at dev instance you will be able to switch onto Goerli and select gEth to send. If you need some Goerli pls ask me There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes I did test, have not tested sending any funds yet tho. will do |
||
<Section.Stack style={{ marginBottom: 16 }}> | ||
<CustomButton | ||
icon={sendViaAddress ? 'success' : undefined} | ||
iconAlignment="left" | ||
iconColor={theme.colors.primary} | ||
contentStyle={{ justifyContent: 'flex-start' }} | ||
style={{ marginBottom: 8 }} | ||
color={sendViaAddress ? theme.colors.white : theme.colors.primary} | ||
textStyle={{ fontSize: 16, color: sendViaAddress ? theme.colors.primary : theme.colors.white }} | ||
onPress={handleRequestAddress} | ||
mode={'contained'} | ||
withoutDone | ||
noElevation | ||
> | ||
SEND VIA ADDRESS | ||
</CustomButton> | ||
{!sendViaAddress ? ( | ||
<NextPageButton | ||
action={'Send'} | ||
label="SEND VIA LINK" | ||
cbContinue={handleContinue} | ||
loading={loading} | ||
values={{ ...params, ...restState, amount: GDAmountInWei, ...bridgeState }} | ||
{...props} | ||
/> | ||
) : ( | ||
<AddressDetails | ||
address={address} | ||
cb={handleSendViaAddress} | ||
setAddress={setAddress} | ||
screenProps={screenProps} | ||
error={addressError} | ||
/> | ||
)} | ||
</Section.Stack> | ||
</Section.Row> | ||
)} | ||
|
||
{!isSend || | ||
(isSend && sendViaAddress && ( | ||
<Section.Row> | ||
<Section.Row grow={1} justifyContent="flex-start"> | ||
<BackButton mode="text" screenProps={screenProps}> | ||
{t`Cancel`} | ||
</BackButton> | ||
</Section.Row> | ||
<Section.Stack grow={3} style={styles.nextButtonContainer}> | ||
<NextPageButton | ||
action={isNativeFlow ? 'isNative' : sendViaAddress ? ACTION_SEND_TO_ADDRESS : params.action} | ||
|
||
cbContinue={handleContinue} | ||
loading={loading} | ||
values={{ | ||
amount: GDAmountInWei, | ||
address: address, | ||
...params, | ||
...restState, | ||
...bridgeState, | ||
}} | ||
{...props} | ||
/> | ||
</Section.Stack> | ||
</Section.Row> | ||
))} | ||
</Section> | ||
</Wrapper> | ||
</KeyboardAvoidingView> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1, what have you decided about both paste + scan QR options ?
in this case you need to display 2 icon buttons somehow - need to discuss this