Skip to content

Commit

Permalink
Merge bd250de into 58a0443
Browse files Browse the repository at this point in the history
  • Loading branch information
thedoublejay authored Aug 3, 2021
2 parents 58a0443 + bd250de commit fb86622
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 30 deletions.
15 changes: 13 additions & 2 deletions app/components/OceanInterface/OceanInterface.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Provider } from "react-redux";
import { SmartBuffer } from 'smart-buffer';
import { RootState } from "../../store";
import { ocean } from "../../store/ocean";
import { wallet } from "../../store/wallet";
import { OceanInterface } from "./OceanInterface";

jest.mock('../../contexts/WalletContext', () => ({
Expand All @@ -21,11 +22,16 @@ describe('oceanInterface', () => {
height: 49,
transactions: [],
err: new Error('An unknown error has occurred')
},
wallet: {
address: 'bcrt1q6np0fh47ykhznjhrtfvduh73cgjg32yac8t07d',
utxoBalance: '77',
tokens: []
}
};
const store = configureStore({
preloadedState: initialState,
reducer: { ocean: ocean.reducer }
reducer: { ocean: ocean.reducer, wallet: wallet.reducer }
})
const component = (
<Provider store={store}>
Expand All @@ -48,11 +54,16 @@ describe('oceanInterface', () => {
broadcasted: false,
sign: async () => signed
}]
},
wallet: {
address: 'bcrt1q6np0fh47ykhznjhrtfvduh73cgjg32yac8t07d',
utxoBalance: '77',
tokens: []
}
};
const store = configureStore({
preloadedState: initialState,
reducer: { ocean: ocean.reducer }
reducer: { ocean: ocean.reducer, wallet: wallet.reducer }
})
const component = (
<Provider store={store}>
Expand Down
90 changes: 76 additions & 14 deletions app/components/OceanInterface/OceanInterface.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
import { CTransactionSegWit } from '@defichain/jellyfish-transaction/dist'
import { WhaleApiClient } from '@defichain/whale-api-client'
import { Transaction } from '@defichain/whale-api-client/dist/api/transactions'
import { MaterialIcons } from '@expo/vector-icons'
import React, { useCallback, useEffect, useRef, useState } from 'react'
import { ActivityIndicator, Animated, Linking, TouchableOpacity, View } from 'react-native'
import { useDispatch, useSelector } from 'react-redux'
import { Text } from '..'
import { Logging } from '../../api/logging'
import { Logging } from '../../api'
import { useWallet } from '../../contexts/WalletContext'
import { useWhaleApiClient } from '../../contexts/WhaleContext'
import { getEnvironment } from '../../environment'
import { fetchTokens } from '../../hooks/wallet/TokensAPI'
import { RootState } from '../../store'
import { firstTransactionSelector, ocean, OceanTransaction } from '../../store/ocean'
import { tailwind } from '../../tailwind'
import { translate } from '../../translations'

const MAX_AUTO_RETRY = 1
const MAX_TIMEOUT = 300000
const INTERVAL_TIME = 5000

async function gotoExplorer (txid: string): Promise<void> {
// TODO(thedoublejay) explorer URL
Expand All @@ -37,6 +42,38 @@ async function broadcastTransaction (tx: CTransactionSegWit, client: WhaleApiCli
}
}

async function waitForTxConfirmation (id: string, client: WhaleApiClient): Promise<Transaction> {
const initialTime = getEnvironment().debug ? 5000 : 30000
let start = initialTime

return await new Promise((resolve, reject) => {
let intervalID: number
const callTransaction = (): void => {
client.transactions.get(id).then((tx) => {
if (intervalID !== undefined) {
clearInterval(intervalID)
}
resolve(tx)
}).catch((e) => {
if (start >= MAX_TIMEOUT) {
Logging.error(e)
if (intervalID !== undefined) {
clearInterval(intervalID)
}
reject(e)
}
})
}
setTimeout(() => {
callTransaction()
intervalID = setInterval(() => {
start += INTERVAL_TIME
callTransaction()
}, INTERVAL_TIME)
}, initialTime)
})
}

/**
* @description - Global component to be used for async calls, network errors etc. This component is positioned above the bottom tab.
* Need to get the height of bottom tab via `useBottomTabBarHeight()` hook to be called on screen.
Expand All @@ -50,6 +87,7 @@ 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<OceanTransaction | undefined>(transaction)
const [err, setError] = useState<Error | undefined>(e)
Expand All @@ -72,21 +110,41 @@ export function OceanInterface (): JSX.Element | null {
transaction.sign(walletContext.get(0))
.then(async signedTx => {
setTxid(signedTx.txId)
setTx({
...transaction,
title: translate('screens/OceanInterface', 'Broadcasting...')
})
await broadcastTransaction(signedTx, client)
setTx({
...transaction,
title: translate('screens/OceanInterface', 'Waiting for confirmation')
})

let title
try {
await waitForTxConfirmation(signedTx.txId, client)
title = 'Transaction Completed'
} catch (e) {
Logging.error(e)
title = 'Sent but not confirmed'
}
setTx({
...transaction,
broadcasted: true,
title: translate('screens/OceanInterface', title)
})
})
.then(() => setTx({
...transaction,
broadcasted: true,
title: translate('screens/OceanInterface', 'Transaction Sent')
}))
.catch((e: Error) => {
let errMsg = e.message
if (txid !== undefined) {
errMsg = `${errMsg}. Txid: ${txid}`
}
setError(new Error(errMsg))
})
.finally(() => dispatch(ocean.actions.popTransaction())) // remove the job as soon as completion
.finally(() => {
dispatch(ocean.actions.popTransaction())
fetchTokens(client, address, dispatch)
}) // remove the job as soon as completion
}
}, [transaction, walletContext])

Expand All @@ -104,16 +162,19 @@ export function OceanInterface (): JSX.Element | null {
{
err !== undefined
? <TransactionError errMsg={err.message} onClose={dismissDrawer} />
: <TransactionDetail broadcasted={tx.broadcasted} txid={txid} onClose={dismissDrawer} />
: <TransactionDetail broadcasted={tx.broadcasted} title={tx.title} txid={txid} onClose={dismissDrawer} />
}
</Animated.View>
)
}

function TransactionDetail ({ broadcasted, txid, onClose }: { broadcasted: boolean, txid?: string, onClose: () => void }): JSX.Element {
let title = 'Signing...'
if (txid !== undefined) title = 'Broadcasting...'
if (broadcasted) title = 'Transaction Sent'
function TransactionDetail ({
broadcasted,
txid,
onClose,
title
}: { broadcasted: boolean, txid?: string, onClose: () => void, title?: string }): JSX.Element {
title = title ?? translate('screens/OceanInterface', 'Signing...')
return (
<>
{
Expand All @@ -123,7 +184,7 @@ function TransactionDetail ({ broadcasted, txid, onClose }: { broadcasted: boole
<View style={tailwind('flex-auto mx-6 justify-center items-center text-center')}>
<Text
style={tailwind('text-sm font-bold')}
>{translate('screens/OceanInterface', title)}
>{title}
</Text>
{
txid !== undefined && <TransactionIDButton txid={txid} onPress={async () => await gotoExplorer(txid)} />
Expand Down Expand Up @@ -177,7 +238,8 @@ function TransactionIDButton ({ txid, onPress }: { txid: string, onPress?: () =>
function TransactionCloseButton (props: { onPress: () => void }): JSX.Element {
return (
<TouchableOpacity
testID='oceanInterface_close' onPress={props.onPress} style={tailwind('px-2 py-1 rounded border border-gray-300 rounded flex-row justify-center items-center')}
testID='oceanInterface_close' onPress={props.onPress}
style={tailwind('px-2 py-1 rounded border border-gray-300 rounded flex-row justify-center items-center')}
>
<Text style={tailwind('text-sm text-primary')}>
{translate('screens/OceanInterface', 'OK')}
Expand Down
13 changes: 11 additions & 2 deletions app/screens/AppNavigator/screens/Balances/screens/SendScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ import { ScrollView, TouchableOpacity, View } from 'react-native'
import NumberFormat from 'react-number-format'
import { useDispatch, useSelector } from 'react-redux'
import { Dispatch } from 'redux'
import { Logging } from '../../../../../api/logging'
import { Logging } from '../../../../../api'
import { Text, TextInput } from '../../../../../components'
import { Button } from '../../../../../components/Button'
import { getTokenIcon } from '../../../../../components/icons/tokens/_index'
import { SectionTitle } from '../../../../../components/SectionTitle'
import { AmountButtonTypes, SetAmountButton } from '../../../../../components/SetAmountButton'
import { useNetworkContext } from '../../../../../contexts/NetworkContext'
import { useWhaleApiClient } from '../../../../../contexts/WhaleContext'
import { useTokensAPI } from '../../../../../hooks/wallet/TokensAPI'
import { RootState } from '../../../../../store'
import { hasTxQueued, ocean } from '../../../../../store/ocean'
import { WalletToken } from '../../../../../store/wallet'
Expand Down Expand Up @@ -74,7 +75,8 @@ type Props = StackScreenProps<BalanceParamList, 'SendScreen'>
export function SendScreen ({ route, navigation }: Props): JSX.Element {
const { networkName } = useNetworkContext()
const client = useWhaleApiClient()
const [token] = useState(route.params.token)
const tokens = useTokensAPI()
const [token, setToken] = useState(route.params.token)
const { control, setValue, formState: { isValid }, getValues, trigger } = useForm({ mode: 'onChange' })
const dispatch = useDispatch()
const [fee, setFee] = useState<BigNumber>(new BigNumber(0.0001))
Expand All @@ -85,6 +87,13 @@ export function SendScreen ({ route, navigation }: Props): JSX.Element {
client.transactions.estimateFee().then((f) => setFee(new BigNumber(f))).catch((e) => Logging.error(e))
}, [])

useEffect(() => {
const t = tokens.find((t) => t.id === token.id)
if (t !== undefined) {
setToken({ ...t })
}
}, [tokens])

async function onSubmit (): Promise<void> {
if (hasPendingJob) {
return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ context('wallet/balances/convert - bi-direction success case', () => {

it('utxosToToken: should be able to convert successfully', function () {
cy.intercept('/v0/playground/transactions/send').as('sendRaw')
cy.getByTestID('button_continue_convert').click().wait(4000)
cy.getByTestID('oceanInterface_close').click()
cy.getByTestID('button_continue_convert').click()
cy.closeOceanInterface()

// check UI redirected (balances root)
// cy.getByTestID('balances_list').should('exist')
Expand Down Expand Up @@ -113,8 +113,8 @@ context('wallet/balances/convert - bi-direction success case', () => {

it('tokenToUtxos: should be able to convert successfully', function () {
cy.intercept('/v0/playground/transactions/send').as('sendRaw')
cy.getByTestID('button_continue_convert').click().wait(4000)
cy.getByTestID('oceanInterface_close').click()
cy.getByTestID('button_continue_convert').click()
cy.closeOceanInterface()

// check UI redirected (balances root)
cy.getByTestID('balances_list').should('exist')
Expand Down
4 changes: 2 additions & 2 deletions cypress/integration/functional/balances/send.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ context('wallet/send', () => {
cy.getByTestID('amount_input').clear().type('1')
cy.getByTestID('send_submit_button').should('not.have.attr', 'disabled')
cy.getByTestID('send_submit_button').click()
cy.wait(5000).getByTestID('oceanInterface_close').click().wait(5000)
cy.closeOceanInterface()
cy.getByTestID('playground_wallet_fetch_balances').click()
cy.getByTestID('bottom_tab_balances').click()
})
Expand Down Expand Up @@ -115,7 +115,7 @@ context('wallet/send', () => {
cy.getByTestID('address_input').type(address)
cy.getByTestID('MAX_amount_button').click()
cy.getByTestID('send_submit_button').click()
cy.wait(5000).getByTestID('oceanInterface_close').click().wait(5000)
cy.closeOceanInterface()
cy.getByTestID('playground_wallet_fetch_balances').click()
cy.getByTestID('bottom_tab_balances').click()
cy.getByTestID('balances_row_1_amount').should('not.exist')
Expand Down
2 changes: 1 addition & 1 deletion cypress/integration/functional/dex/poolswap.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ context('poolswap with values', () => {
cy.getByTestID('text_price_row_minimum_0').then(() => {
// const tokenValue = $txt[0].textContent.replace(' LTC', '').replace(',', '')
cy.getByTestID('button_submit').click()
cy.wait(5000).getByTestID('oceanInterface_close').click().wait(5000)
cy.closeOceanInterface()
cy.getByTestID('playground_wallet_fetch_balances').click()
cy.getByTestID('bottom_tab_balances').click()
cy.getByTestID('balances_row_4').should('exist')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ context('app/dex/removeLiquidity', () => {
cy.getByTestID('button_slider_max').click().wait(1000)
cy.getByTestID('button_continue_remove_liq').click().wait(4000)
cy.getByTestID('bottom_tab_dex').click().wait(1000)
cy.wait(5000).getByTestID('oceanInterface_close').click().wait(5000)
cy.closeOceanInterface()

// redirected back to dex root page
cy.getByTestID('liquidity_screen_list').should('exist')
Expand Down
18 changes: 14 additions & 4 deletions cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,32 +33,38 @@ declare global {
* @example cy.getByTestID('settings')
*/
getByTestID (value: string): Chainable<Element>

/**
* @description Redirects to main page and creates an empty wallet for testing. Useful on starts of tests.
* @param {boolean} [isRandom=false] default = false, creates randomly generated mnemonic seed or abandon x23
* @example cy.createEmptyWallet(isRandom?: boolean)
*/
createEmptyWallet (isRandom?: boolean): Chainable<Element>

/**
* @description Sends UTXO DFI to wallet.
* @example cy.sendDFItoWallet().wait(4000)
*/
sendDFItoWallet (): Chainable<Element>

/**
* @description Sends DFI Token to wallet.
* @example cy.sendDFITokentoWallet().wait(4000)
*/
sendDFITokentoWallet (): Chainable<Element>

/**
* @description Sends token to wallet. Accepts a list of token symbols to be sent.
* @param {string[]} tokens to be sent
* @example cy.sendTokenToWallet(['BTC', 'ETH']).wait(4000)
*/
sendTokenToWallet (tokens: string[]): Chainable<Element>

/**
* @description Wait for the ocean interface to be confirmed then close the drawer
* @example cy.closeOceanInterface()
*/
closeOceanInterface (): Chainable<Element>
}
}
}
Expand Down Expand Up @@ -91,3 +97,7 @@ Cypress.Commands.add('sendTokenToWallet', (tokens: string[]) => {
})
cy.wait(['@sendTokensToAddress'])
})

Cypress.Commands.add('closeOceanInterface', () => {
cy.wait(5000).getByTestID('oceanInterface_close').click().wait(2000)
})

0 comments on commit fb86622

Please sign in to comment.