diff --git a/android/app/build.gradle b/android/app/build.gradle index 6f1a63b67a2..f8cdab0b8f6 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -274,7 +274,8 @@ dependencies { implementation "com.facebook.react:react-native:+" // From node_modules implementation project(':react-native-branch') - implementation "io.branch.sdk.android:library:3.0.4" + implementation "io.branch.sdk.android:library:4.1.2" + implementation project(':react-native-camera') implementation project(':react-native-share') diff --git a/app/components/Nav/Main/index.js b/app/components/Nav/Main/index.js index be84cf05058..45ebca5b649 100644 --- a/app/components/Nav/Main/index.js +++ b/app/components/Nav/Main/index.js @@ -87,6 +87,7 @@ import contractMap from 'eth-contract-metadata'; import { BN } from 'gaba'; import { BNToHex } from 'gaba/util'; import MessageSign from '../../UI/MessageSign'; +import WalletConnectReturnToBrowserModal from '../../UI/WalletConnectReturnToBrowserModal'; const styles = StyleSheet.create({ flex: { @@ -384,6 +385,7 @@ class Main extends PureComponent { signType: '', walletConnectRequest: false, walletConnectRequestInfo: {}, + walletConnectReturnModalVisible: false, paymentChannelRequest: false, paymentChannelRequestLoading: false, paymentChannelRequestCompleted: false, @@ -498,6 +500,9 @@ class Main extends PureComponent { WalletConnect.hub.on('walletconnectSessionRequest', peerInfo => { this.setState({ walletConnectRequest: true, walletConnectRequestInfo: peerInfo }); }); + WalletConnect.hub.on('walletconnect:return', () => { + this.setState({ walletConnectReturnModalVisible: true }); + }); WalletConnect.init(); }; @@ -739,6 +744,7 @@ class Main extends PureComponent { // If the app is now in background, we need to start // the background timer, which is less intense if (this.backgroundMode) { + this.setState({ walletConnectReturnModalVisible: false }); BackgroundTimer.runBackgroundTimer(async () => { await Engine.refreshTransactionHistory(); }, AppConstants.TX_CHECK_BACKGROUND_FREQUENCY); @@ -902,6 +908,10 @@ class Main extends PureComponent { ); }; + renderWalletConnectReturnModal = () => ( + + ); + renderPaymentChannelRequestApproval = () => { const { paymentChannelRequest, @@ -952,6 +962,7 @@ class Main extends PureComponent { {this.renderSigningModal()} {this.renderWalletConnectSessionRequestModal()} {this.renderPaymentChannelRequestApproval()} + {this.renderWalletConnectReturnModal()} ); } diff --git a/app/components/UI/BlockingActionModal/__snapshots__/index.test.js.snap b/app/components/UI/BlockingActionModal/__snapshots__/index.test.js.snap new file mode 100644 index 00000000000..877a1a1b324 --- /dev/null +++ b/app/components/UI/BlockingActionModal/__snapshots__/index.test.js.snap @@ -0,0 +1,81 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`BlockingActionModal should render correctly 1`] = ` + + + + + Please wait + + + + + +`; diff --git a/app/components/UI/BlockingActionModal/index.js b/app/components/UI/BlockingActionModal/index.js new file mode 100644 index 00000000000..eafdc7ae3dc --- /dev/null +++ b/app/components/UI/BlockingActionModal/index.js @@ -0,0 +1,56 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { ActivityIndicator, StyleSheet, View } from 'react-native'; +import Modal from 'react-native-modal'; +import { colors, baseStyles } from '../../../styles/common'; + +const styles = StyleSheet.create({ + modal: { + margin: 0, + width: '100%' + }, + modalView: { + justifyContent: 'center', + alignItems: 'center', + alignSelf: 'center', + backgroundColor: colors.white, + width: '90%', + borderRadius: 6, + minHeight: 200, + padding: 15 + }, + loader: { + marginTop: 30 + } +}); + +/** + * View that renders an action modal + */ +export default function BlockingActionModal({ children, modalVisible, isLoadingAction }) { + return ( + + + + {children} + {isLoadingAction && } + + + + ); +} + +BlockingActionModal.propTypes = { + /** + * Whether modal is shown + */ + modalVisible: PropTypes.bool, + /** + * Whether a spinner is shown + */ + isLoadingAction: PropTypes.bool, + /** + * Content to display above the action buttons + */ + children: PropTypes.node +}; diff --git a/app/components/UI/BlockingActionModal/index.test.js b/app/components/UI/BlockingActionModal/index.test.js new file mode 100644 index 00000000000..65049b24175 --- /dev/null +++ b/app/components/UI/BlockingActionModal/index.test.js @@ -0,0 +1,15 @@ +import React from 'react'; +import { Text } from 'react-native'; +import { shallow } from 'enzyme'; +import BlockingActionModal from './'; + +describe('BlockingActionModal', () => { + it('should render correctly', () => { + const wrapper = shallow( + + Please wait + + ); + expect(wrapper).toMatchSnapshot(); + }); +}); diff --git a/app/components/UI/WalletConnectReturnToBrowserModal/__snapshots__/index.test.js.snap b/app/components/UI/WalletConnectReturnToBrowserModal/__snapshots__/index.test.js.snap new file mode 100644 index 00000000000..49238492ef0 --- /dev/null +++ b/app/components/UI/WalletConnectReturnToBrowserModal/__snapshots__/index.test.js.snap @@ -0,0 +1,51 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`WalletConnectReturnToBrowserModal should render correctly 1`] = ` + + + You're all set! + + + You can now return to your browser + + + +`; diff --git a/app/components/UI/WalletConnectReturnToBrowserModal/index.js b/app/components/UI/WalletConnectReturnToBrowserModal/index.js new file mode 100644 index 00000000000..5ec71eec0fc --- /dev/null +++ b/app/components/UI/WalletConnectReturnToBrowserModal/index.js @@ -0,0 +1,54 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { StyleSheet, Text } from 'react-native'; +import Icon from 'react-native-vector-icons/Ionicons'; +import { fontStyles, colors } from '../../../styles/common'; +import { strings } from '../../../../locales/i18n'; +import BlockingActionModal from '../BlockingActionModal'; + +const styles = StyleSheet.create({ + blockingModalText: { + alignSelf: 'center', + borderRadius: 10, + fontSize: 16, + textAlign: 'center', + color: colors.fontSecondary, + ...fontStyles.normal + }, + blockingModalTitle: { + color: colors.fontPrimary, + fontSize: 22, + marginBottom: 15, + textAlign: 'center', + ...fontStyles.bold + }, + returnIcon: { + marginTop: 15, + textAlign: 'center', + color: colors.fontSecondary, + fontSize: 60, + lineHeight: 60 + } +}); + +/** + * View that renders an action modal + */ +export default function WalletConnectReturnToBrowserModal({ modalVisible }) { + return ( + + + {strings('walletconnect_return_modal.title')} + {strings('walletconnect_return_modal.text')} + + + + ); +} + +WalletConnectReturnToBrowserModal.propTypes = { + /** + * Whether modal is shown + */ + modalVisible: PropTypes.bool +}; diff --git a/app/components/UI/WalletConnectReturnToBrowserModal/index.test.js b/app/components/UI/WalletConnectReturnToBrowserModal/index.test.js new file mode 100644 index 00000000000..0e6f5eb2f0b --- /dev/null +++ b/app/components/UI/WalletConnectReturnToBrowserModal/index.test.js @@ -0,0 +1,10 @@ +import React from 'react'; +import { shallow } from 'enzyme'; +import WalletConnectReturnToBrowserModal from './'; + +describe('WalletConnectReturnToBrowserModal', () => { + it('should render correctly', () => { + const wrapper = shallow(); + expect(wrapper).toMatchSnapshot(); + }); +}); diff --git a/app/core/DeeplinkManager.js b/app/core/DeeplinkManager.js index 54b5b8c5f8f..b6a05bafbe2 100644 --- a/app/core/DeeplinkManager.js +++ b/app/core/DeeplinkManager.js @@ -110,12 +110,7 @@ class DeeplinkManager { const redirect = params && params.redirect; // eslint-disable-next-line no-case-declarations const autosign = params && params.autosign; - - if (urlObj.hostname === 'sign' || urlObj.hostname === 'send') { - WalletConnect.setRedirectUri(redirect); - } else { - WalletConnect.newSession(url, redirect, autosign); - } + WalletConnect.newSession(url, redirect, autosign); break; case 'ethereum': this.handleEthereumUrl(url); diff --git a/app/core/WalletConnect.js b/app/core/WalletConnect.js index 0a216fc7df9..36b0c4d200a 100644 --- a/app/core/WalletConnect.js +++ b/app/core/WalletConnect.js @@ -1,6 +1,5 @@ import RNWalletConnect from '@walletconnect/react-native'; import Engine from './Engine'; -import { Linking } from 'react-native'; import Logger from '../util/Logger'; // eslint-disable-next-line import/no-nodejs-modules import { EventEmitter } from 'events'; @@ -8,7 +7,6 @@ import AsyncStorage from '@react-native-community/async-storage'; const hub = new EventEmitter(); let connectors = []; -let pendingRedirect = null; const CLIENT_OPTIONS = { clientMeta: { @@ -37,7 +35,7 @@ class WalletConnect { constructor(options) { if (options.redirect) { - pendingRedirect = options.redirect; + this.redirectUrl = options.redirect; } if (options.autosign) { @@ -207,6 +205,7 @@ class WalletConnect { }); } } + this.redirectIfNeeded(); } }); @@ -290,11 +289,10 @@ class WalletConnect { }); redirectIfNeeded = () => { - if (pendingRedirect) { + if (this.redirectUrl) { setTimeout(() => { - Linking.openURL(pendingRedirect); - pendingRedirect = null; - }, 500); + hub.emit('walletconnect:return'); + }, 1500); } }; } @@ -353,9 +351,6 @@ const instance = { shutdown() { Engine.context.TransactionController.hub.removeAllListeners(); Engine.context.PreferencesController.unsubscribe(); - }, - setRedirectUri: uri => { - pendingRedirect = uri; } }; diff --git a/locales/en.json b/locales/en.json index f097eff8c3f..508e2e269fd 100644 --- a/locales/en.json +++ b/locales/en.json @@ -986,5 +986,9 @@ "amount_error_message": "The amount must be a number", "address_error_message": "The address is invalid", "balance_error_message": "Insufficient balance" + }, + "walletconnect_return_modal": { + "title": "You're all set!", + "text": "You can now return to your browser" } } diff --git a/locales/es.json b/locales/es.json index 5a5a34d8518..8b35ecfddd6 100644 --- a/locales/es.json +++ b/locales/es.json @@ -966,5 +966,9 @@ "amount_error_message": "El monto debe ser un número", "address_error_message": "La dirección no es válida", "balance_error_message": "Balance insuficiente" + }, + "walletconnect_return_modal": { + "title": "Listo!", + "text": "Ahora puedes volver a tu navegador" } }