From 30d15c796c44c6a1fb4625b6439a516e0d567ab3 Mon Sep 17 00:00:00 2001 From: Andre Pimenta Date: Fri, 2 Jul 2021 16:09:35 +0100 Subject: [PATCH] Improvement/react navigation upgrade 5 (#2731) * Move to new dependencies * move navigation.getparam to route.params * add navigationOptions * Fix dismiss and drawer working * Fix remaining route params * Fix switch navigator and fiat on ramp nav * Fix routename and pop * Remove screen props * Fix deeplink navigation * Fix navigation for inner screens * Ignore navigation warning * Fix get route * Fix showing protect wallet modal * Add route to proptypes * Add more missing proptypes * Update tests * Fix some more navigations * Update collectibles navigatoin * Use useNavigation * Use optional chaining on `dangerouslyGetParent()` * Removing ?? null and ?? undefined * Reorganize nav imports in App/index * Update findRouteNameFromNavigatorState * Remove noop functions * Adding testing for navigation * Add more navigation tests * Add comment explaining the test files * Remove unnecessary boolean casting * Remove unnecessary variable * Remove unnecessary boolean casting * Fixes navigation issues * Fix navigation issues * Fix QR scanner * fixed enroll navigation reset issue * Fix going to wallet view * updated test cases * Fix simple notification * Fix going to onboarding * added snapshots to test * fixed what new modal test * added picker to package.json * yarn clean * yarn update command * snapshot update * removed the dive method on the wrapper * snapshot update * update yarn unit test * commented out cache in build step * Bug/fix nft gesture experience (#2862) * Fix collections modal * Make eslint happy * Clean up modal code TODO: Converting components into hooks will save us from re-renders and from doing things like waiting to focus on inputs * Remove in-house safe area from reusable modal for now * Fix scroll interaction in collectible modal action sheet * Use color from overlay * Add test snap files * Update snapshots and make tests pass * uncommented cache for bitrise Co-authored-by: Pedro Pablo Aste Kompen Co-authored-by: sethkfman Co-authored-by: Cal Leung --- .eslintrc.js | 5 + .../Nav/App/__snapshots__/index.test.js.snap | 6 +- app/components/Nav/App/index.js | 181 +++-- app/components/Nav/Main/MainNavigator.js | 553 ++++++++------- .../Nav/Main/__snapshots__/index.test.js.snap | 671 +----------------- app/components/Nav/Main/index.js | 29 +- app/components/Nav/Main/index.test.js | 8 +- app/components/UI/AccountOverview/index.js | 7 +- .../UI/AddCustomCollectible/index.test.js | 2 +- .../UI/ApproveTransactionReview/index.js | 4 +- app/components/UI/AssetOverview/index.js | 14 +- app/components/UI/BackupAlert/index.js | 13 +- .../CollectibleContractInformation/index.js | 7 +- .../UI/CollectibleContracts/index.js | 32 +- .../UI/CollectibleContracts/index.test.js | 2 +- .../__snapshots__/index.test.js.snap | 125 +--- app/components/UI/CollectibleModal/index.js | 100 ++- .../UI/CollectibleModal/index.test.js | 14 +- .../__snapshots__/index.test.js.snap | 323 ++++----- .../UI/CollectibleOverview/index.js | 146 ++-- app/components/UI/CustomGas/index.test.js | 2 +- app/components/UI/DrawerView/index.js | 43 +- .../FiatOrders/PaymentMethodApplePay/index.js | 8 +- .../PaymentMethodSelector/index.android.js | 6 +- .../PaymentMethodSelector/index.ios.js | 6 +- .../PaymentMethodSelector/wyreApplePay.js | 6 +- .../UI/FiatOrders/TransakWebView/index.js | 12 +- .../components/PaymentMethod/Modal.js | 3 +- .../FiatOrders/orderProcessor/wyreApplePay.js | 7 +- .../__snapshots__/index.test.js.snap | 140 +++- .../UI/InvalidCustomNetworkAlert/index.js | 7 +- app/components/UI/Navbar/index.js | 447 ++++++------ app/components/UI/NavbarBrowserTitle/index.js | 18 +- .../Notification/SimpleNotification/index.js | 30 +- app/components/UI/Notification/index.js | 11 +- .../UI/OnboardingWizard/Step5/index.js | 7 +- app/components/UI/OnboardingWizard/index.js | 2 +- .../__snapshots__/index.test.js.snap | 9 +- app/components/UI/OptinMetrics/index.js | 46 +- app/components/UI/OptinMetrics/index.test.js | 2 +- app/components/UI/PaymentRequest/index.js | 14 +- .../UI/PaymentRequestSuccess/index.js | 14 +- .../UI/ProtectYourWalletModal/index.js | 12 +- app/components/UI/ReceiveRequest/index.js | 7 +- .../__snapshots__/index.test.js.snap | 9 + app/components/UI/ReusableModal/index.test.js | 15 + app/components/UI/ReusableModal/index.tsx | 218 ++++++ app/components/UI/SignatureRequest/index.js | 9 +- app/components/UI/Swaps/QuotesView.js | 22 +- .../UI/Swaps/components/InfoModal.js | 3 +- .../UI/Swaps/components/Onboarding.js | 11 +- .../UI/Swaps/components/QuotesModal.js | 12 +- app/components/UI/Swaps/index.js | 19 +- app/components/UI/Swaps/utils/index.js | 11 +- app/components/UI/Tokens/index.js | 2 +- .../UI/TransactionEditor/index.test.js | 2 +- .../TransactionDetails/index.js | 13 +- .../__snapshots__/index.test.js.snap | 43 +- .../TransactionReviewInformation/index.js | 5 +- .../TransactionReviewSummary/index.test.js | 2 +- .../__snapshots__/index.test.js.snap | 2 +- app/components/UI/TransactionReview/index.js | 10 +- .../UI/TransactionReview/index.test.js | 2 +- app/components/UI/Transactions/index.js | 5 +- .../__snapshots__/index.test.js.snap | 106 +-- app/components/UI/WhatsNewModal/index.js | 19 +- app/components/UI/WhatsNewModal/index.test.js | 7 +- .../UI/WhatsNewModal/whatsNewList.js | 2 +- .../__snapshots__/index.test.js.snap | 239 +------ .../Views/AccountBackupStep1/index.js | 37 +- .../Views/AccountBackupStep1/index.test.js | 19 +- .../Views/AccountBackupStep1B/index.js | 17 +- app/components/Views/ActivityView/index.js | 10 +- .../AddAsset/__snapshots__/index.test.js.snap | 18 - app/components/Views/AddAsset/index.js | 16 +- app/components/Views/AddAsset/index.test.js | 2 +- .../__snapshots__/index.test.js.snap | 4 +- app/components/Views/AddBookmark/index.js | 14 +- .../Views/AddBookmark/index.test.js | 2 +- app/components/Views/Approval/index.js | 3 +- app/components/Views/Asset/index.js | 18 +- app/components/Views/Browser/index.js | 36 +- app/components/Views/BrowserTab/index.js | 41 +- app/components/Views/ChoosePassword/index.js | 22 +- .../Views/ChoosePassword/index.test.js | 14 +- .../Views/ChoosePasswordSimple/index.js | 8 +- .../Views/ChoosePasswordSimple/index.test.js | 13 +- .../__snapshots__/index.test.js.snap | 27 - app/components/Views/Collectible/index.js | 14 +- .../Views/Collectible/index.test.js | 2 +- app/components/Views/CollectibleView/index.js | 113 +++ .../__snapshots__/index.test.js.snap | 2 +- app/components/Views/CreateWallet/index.js | 5 +- .../Views/EnterPasswordSimple/index.js | 8 +- .../Views/EnterPasswordSimple/index.test.js | 9 +- app/components/Views/Entry/index.js | 9 +- .../Views/GasEducationCarousel/index.js | 16 +- .../__snapshots__/index.test.js.snap | 7 - app/components/Views/ImportFromSeed/index.js | 4 +- .../Views/ImportFromSeed/index.test.js | 13 +- .../Views/ImportPrivateKey/index.js | 7 +- .../Views/ImportPrivateKey/index.test.js | 2 +- .../__snapshots__/index.test.js.snap | 7 +- .../ImportPrivateKeySuccess/index.test.js | 2 +- app/components/Views/LockScreen/index.js | 5 +- app/components/Views/Login/index.js | 10 +- .../Views/ManualBackupStep1/index.js | 11 +- .../Views/ManualBackupStep1/index.test.js | 33 +- .../Views/ManualBackupStep2/index.js | 22 +- .../Views/ManualBackupStep2/index.test.js | 40 +- .../Views/ManualBackupStep3/index.js | 23 +- .../NavigationUnitTest/TestScreen1.test.js | 10 + .../NavigationUnitTest/TestScreen2.test.js | 10 + .../NavigationUnitTest/TestScreen3.test.js | 10 + .../__snapshots__/TestScreen1.test.js.snap | 106 +++ .../__snapshots__/TestScreen2.test.js.snap | 201 ++++++ .../__snapshots__/TestScreen3.test.js.snap | 296 ++++++++ .../Views/NavigationUnitTest/index.js | 53 ++ app/components/Views/OfflineMode/index.js | 5 +- .../Views/OfflineMode/index.test.js | 2 +- app/components/Views/Onboarding/index.js | 15 +- .../Views/OnboardingCarousel/index.test.js | 2 +- app/components/Views/QRScanner/index.js | 28 +- app/components/Views/ResetPassword/index.js | 17 +- .../Views/ResetPassword/index.test.js | 15 +- .../Views/RevealPrivateCredential/index.js | 19 +- .../RevealPrivateCredential/index.test.js | 2 +- app/components/Views/Send/index.js | 25 +- app/components/Views/SendFlow/Amount/index.js | 17 +- .../Views/SendFlow/Confirm/index.js | 20 +- app/components/Views/SendFlow/SendTo/index.js | 38 +- .../Settings/AdvancedSettings/index.test.js | 13 +- .../__snapshots__/index.test.js.snap | 6 +- .../Views/Settings/AppInformation/index.js | 7 +- .../Settings/AppInformation/index.test.js | 2 +- .../Settings/Contacts/ContactForm/index.js | 18 +- .../Contacts/ContactForm/index.test.js | 13 +- .../Views/Settings/Contacts/index.test.js | 13 +- .../ExperimentalSettings/index.test.js | 13 +- .../Settings/GeneralSettings/index.test.js | 13 +- .../NetworksSettings/NetworkSettings/index.js | 10 +- .../NetworkSettings/index.test.js | 16 +- .../Settings/NetworksSettings/index.test.js | 16 +- .../Settings/SecuritySettings/index.test.js | 13 +- app/components/Views/Settings/index.test.js | 13 +- .../__snapshots__/index.test.js.snap | 5 +- app/components/Views/SimpleWebview/index.js | 14 +- .../Views/SimpleWebview/index.test.js | 2 +- .../Views/SyncWithExtensionSuccess/index.js | 17 +- .../Views/TermsAndConditions/index.js | 7 +- .../Views/TransactionsView/index.js | 2 +- app/core/DeeplinkManager.js | 8 +- app/util/general.js | 26 +- index.js | 3 +- ios/Podfile.lock | 7 +- package.json | 15 +- tsconfig.json | 60 ++ yarn.lock | 298 +++++--- 158 files changed, 3220 insertions(+), 2875 deletions(-) create mode 100644 app/components/UI/ReusableModal/__snapshots__/index.test.js.snap create mode 100644 app/components/UI/ReusableModal/index.test.js create mode 100644 app/components/UI/ReusableModal/index.tsx create mode 100644 app/components/Views/CollectibleView/index.js create mode 100644 app/components/Views/NavigationUnitTest/TestScreen1.test.js create mode 100644 app/components/Views/NavigationUnitTest/TestScreen2.test.js create mode 100644 app/components/Views/NavigationUnitTest/TestScreen3.test.js create mode 100644 app/components/Views/NavigationUnitTest/__snapshots__/TestScreen1.test.js.snap create mode 100644 app/components/Views/NavigationUnitTest/__snapshots__/TestScreen2.test.js.snap create mode 100644 app/components/Views/NavigationUnitTest/__snapshots__/TestScreen3.test.js.snap create mode 100644 app/components/Views/NavigationUnitTest/index.js create mode 100644 tsconfig.json diff --git a/.eslintrc.js b/.eslintrc.js index 2b876e8b391..5e378146d9c 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -20,6 +20,11 @@ module.exports = { waitFor: true, __DEV__: true }, + settings: { + 'import/resolver': { + typescript: {} // this loads /tsconfig.json to eslint + } + }, rules: { 'no-catch-shadow': 0, 'no-console': ['warn', { allow: ['warn', 'error'] }], diff --git a/app/components/Nav/App/__snapshots__/index.test.js.snap b/app/components/Nav/App/__snapshots__/index.test.js.snap index 5ef5a6c907c..ae0ec3bbf27 100644 --- a/app/components/Nav/App/__snapshots__/index.test.js.snap +++ b/app/components/Nav/App/__snapshots__/index.test.js.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`App should render correctly 1`] = ` - + + + `; diff --git a/app/components/Nav/App/index.js b/app/components/Nav/App/index.js index bfecc8ddaf9..3d6a1550cd9 100644 --- a/app/components/Nav/App/index.js +++ b/app/components/Nav/App/index.js @@ -1,8 +1,10 @@ import React, { PureComponent } from 'react'; -import { createAppContainer, createSwitchNavigator, NavigationActions } from 'react-navigation'; +import Branch from 'react-native-branch'; +import { NavigationContainer, CommonActions } from '@react-navigation/native'; +import { createSwitchNavigator } from '@react-navigation/compat'; +import { createStackNavigator } from '@react-navigation/stack'; +import { createDrawerNavigator } from '@react-navigation/drawer'; -import { createStackNavigator } from 'react-navigation-stack'; -import { createDrawerNavigator } from 'react-navigation-drawer'; import Login from '../../Views/Login'; import QRScanner from '../../Views/QRScanner'; import Onboarding from '../../Views/Onboarding'; @@ -22,121 +24,100 @@ import Main from '../Main'; import DrawerView from '../../UI/DrawerView'; import OptinMetrics from '../../UI/OptinMetrics'; import SimpleWebview from '../../Views/SimpleWebview'; -import DrawerStatusTracker from '../../../core/DrawerStatusTracker'; + +//import DrawerStatusTracker from '../../../core/DrawerStatusTracker'; import SharedDeeplinkManager from '../../../core/DeeplinkManager'; import Engine from '../../../core/Engine'; -import Logger from '../../../util/Logger'; -import Branch from 'react-native-branch'; import AppConstants from '../../../core/AppConstants'; +import Logger from '../../../util/Logger'; import { trackErrorAsAnalytics } from '../../../util/analyticsV2'; +const Stack = createStackNavigator(); +const Drawer = createDrawerNavigator(); /** * Stack navigator responsible for the onboarding process * Create Wallet, Import from Seed and Sync */ -const OnboardingNav = createStackNavigator( - { - Onboarding: { - screen: Onboarding - }, - OnboardingCarousel: { - screen: OnboardingCarousel - }, - CreateWallet: { - screen: CreateWallet - }, - ChoosePassword: { - screen: ChoosePassword - }, - AccountBackupStep1: { - screen: AccountBackupStep1 - }, - AccountBackupStep1B: { - screen: AccountBackupStep1B - }, - ManualBackupStep1: { - screen: ManualBackupStep1 - }, - ManualBackupStep2: { - screen: ManualBackupStep2 - }, - ManualBackupStep3: { - screen: ManualBackupStep3 - }, - ImportFromSeed: { - screen: ImportFromSeed - }, - OptinMetrics: { - screen: OptinMetrics - } - }, - { - initialRouteName: 'OnboardingCarousel' - } +const OnboardingNav = () => ( + + + + + + + + + + + + + ); /** * Parent Stack navigator that allows the * child OnboardingNav navigator to push modals on top of it */ -const OnboardingRootNav = createStackNavigator( - { - OnboardingNav: { - screen: OnboardingNav, - navigationOptions: { - header: null - } - }, - SyncWithExtensionSuccess: { - screen: SyncWithExtensionSuccess - }, - QRScanner: { - screen: QRScanner, - navigationOptions: { - header: null - } - }, - Webview: { - screen: createStackNavigator( - { - SimpleWebview: { - screen: SimpleWebview - } - }, - { - mode: 'modal' - } - ), - navigationOptions: { - header: null - } - } - }, - { - mode: 'modal' - } +const SimpleWebviewScreen = () => ( + + + +); + +const OnboardingRootNav = () => ( + + + + + + ); /** * Main app navigator which handles all the screens * after the user is already onboarded */ -const HomeNav = createDrawerNavigator( - { - Main: { - screen: Main - } - }, - { - contentComponent: DrawerView, - drawerWidth: 315, - overlayColor: 'rgba(0, 0, 0, 0.5)' - } + +const HomeNav = () => ( + } + // eslint-disable-next-line + drawerStyle={{ + backgroundColor: 'rgba(0, 0, 0, 0.5)', + width: 315 + }} + > + + ); +// Is this necessary? /** * Drawer status tracking - */ const defaultGetStateForAction = HomeNav.router.getStateForAction; DrawerStatusTracker.init(); HomeNav.router.getStateForAction = (action, state) => { @@ -150,11 +131,13 @@ HomeNav.router.getStateForAction = (action, state) => { return defaultGetStateForAction(action, state); }; +*/ /** * Top level switch navigator which decides * which top level view to show */ + const AppNavigator = createSwitchNavigator( { Entry, @@ -169,15 +152,13 @@ const AppNavigator = createSwitchNavigator( } ); -const AppContainer = createAppContainer(AppNavigator); - class App extends PureComponent { unsubscribeFromBranch; componentDidMount = () => { SharedDeeplinkManager.init({ - navigate: (routeName, opts) => { - this.navigator.dispatch(NavigationActions.navigate({ routeName, params: opts })); + navigate: (name, opts) => { + this.navigator.dispatch(CommonActions.navigate({ name, params: opts })); } }); if (!this.unsubscribeFromBranch) { @@ -213,11 +194,13 @@ class App extends PureComponent { render() { return ( - { this.navigator = nav; }} - /> + > + + ); } } diff --git a/app/components/Nav/Main/MainNavigator.js b/app/components/Nav/Main/MainNavigator.js index 37d1cfff7df..d8138736125 100644 --- a/app/components/Nav/Main/MainNavigator.js +++ b/app/components/Nav/Main/MainNavigator.js @@ -1,7 +1,7 @@ import React from 'react'; import { Image, StyleSheet } from 'react-native'; -import { createStackNavigator } from 'react-navigation-stack'; -import { createBottomTabNavigator } from 'react-navigation-tabs'; +import { createStackNavigator } from '@react-navigation/stack'; +import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; import Browser from '../../Views/Browser'; import AddBookmark from '../../Views/AddBookmark'; import SimpleWebview from '../../Views/SimpleWebview'; @@ -50,11 +50,19 @@ import ActivityView from '../../Views/ActivityView'; import SwapsAmountView from '../../UI/Swaps'; import SwapsQuotesView from '../../UI/Swaps/QuotesView'; import GasEducationCarousel from '../../Views/GasEducationCarousel'; +import CollectiblesDetails from '../../UI/CollectibleModal'; +import OptinMetrics from '../../UI/OptinMetrics'; + +const Stack = createStackNavigator(); +const Tab = createBottomTabNavigator(); const styles = StyleSheet.create({ headerLogo: { width: 125, height: 50 + }, + hidden: { + opacity: 0 } }); /** @@ -62,261 +70,290 @@ const styles = StyleSheet.create({ * the 2 main sections: Browser, Wallet */ -export default createStackNavigator( - { - Home: { - screen: createBottomTabNavigator( - { - WalletTabHome: createStackNavigator({ - WalletView: { - screen: Wallet - }, - Asset: { - screen: Asset - }, - AddAsset: { - screen: AddAsset - }, - Collectible: { - screen: Collectible - }, - RevealPrivateCredentialView: { - screen: RevealPrivateCredential - } - }), - BrowserTabHome: createStackNavigator({ - BrowserView: { - screen: Browser, - navigationOptions: { - gesturesEnabled: false - } - } - }), - TransactionsHome: createStackNavigator({ - TransactionsView: { - screen: ActivityView - } - }) - }, - { - defaultNavigationOptions: () => ({ - tabBarVisible: false - }) - } - ) - }, - Webview: { - screen: createStackNavigator( - { - SimpleWebview: { - screen: SimpleWebview - } - }, - { - mode: 'modal' - } - ) - }, - SettingsView: { - screen: createStackNavigator({ - Settings: { - screen: Settings - }, - GeneralSettings: { - screen: GeneralSettings - }, - AdvancedSettings: { - screen: AdvancedSettings - }, - SecuritySettings: { - screen: SecuritySettings - }, - ExperimentalSettings: { - screen: ExperimentalSettings - }, - NetworksSettings: { - screen: NetworksSettings - }, - NetworkSettings: { - screen: NetworkSettings - }, - CompanySettings: { - screen: AppInformation - }, - ContactsSettings: { - screen: Contacts - }, - ContactForm: { - screen: ContactForm - }, - RevealPrivateCredentialView: { - screen: RevealPrivateCredential - }, - WalletConnectSessionsView: { - screen: WalletConnectSessions - }, - ChoosePasswordSimple: { - screen: ChoosePasswordSimple - }, - ResetPassword: { - screen: ResetPassword - }, - ManualBackupStep1: { - screen: ManualBackupStep1 - }, - ManualBackupStep2: { - screen: ManualBackupStep2 - }, - ManualBackupStep3: { - screen: ManualBackupStep3 - }, - EnterPasswordSimple: { - screen: EnterPasswordSimple - } - }) - }, - ImportPrivateKeyView: { - screen: createStackNavigator( - { - ImportPrivateKey: { - screen: ImportPrivateKey - }, - ImportPrivateKeySuccess: { - screen: ImportPrivateKeySuccess - } - }, - { - headerMode: 'none' - } - ) - }, - SendView: { - screen: createStackNavigator({ - Send: { - screen: Send - } - }) - }, - SendFlowView: { - screen: createStackNavigator({ - SendTo: { - screen: SendTo - }, - Amount: { - screen: Amount - }, - Confirm: { - screen: Confirm - } - }) - }, - ApprovalView: { - screen: createStackNavigator({ - Approval: { - screen: Approval - } - }) - }, - ApproveView: { - screen: createStackNavigator({ - Approve: { - screen: Approve - } - }) - }, - AddBookmarkView: { - screen: createStackNavigator({ - AddBookmark: { - screen: AddBookmark - } - }) - }, - OfflineModeView: { - screen: createStackNavigator({ - OfflineMode: { - screen: OfflineMode - } - }) - }, - /** ALL FULL SCREEN MODALS SHOULD GO HERE */ - QRScanner: { - screen: QrScanner - }, - LockScreen: { - screen: LockScreen - }, - PaymentRequestView: { - screen: createStackNavigator( - { - PaymentRequest: { - screen: PaymentRequest - }, - PaymentRequestSuccess: { - screen: PaymentRequestSuccess - } - }, - { - mode: 'modal' - } - ) - }, - FiatOnRamp: { - screen: createStackNavigator({ - PaymentMethodSelector: { screen: PaymentMethodSelector }, - PaymentMethodApplePay: { screen: PaymentMethodApplePay }, - TransakFlow: { screen: TransakWebView }, - GasEducationCarousel: { screen: GasEducationCarousel } - }) - }, - Swaps: { - screen: createStackNavigator({ - SwapsAmountView: { screen: SwapsAmountView }, - SwapsQuotesView: { screen: SwapsQuotesView } - }) - }, - SetPasswordFlow: { - screen: createStackNavigator( - { - ChoosePassword: { - screen: ChoosePassword - }, - AccountBackupStep1: { - screen: AccountBackupStep1 - }, - AccountBackupStep1B: { - screen: AccountBackupStep1B - }, - ManualBackupStep1: { - screen: ManualBackupStep1 - }, - ManualBackupStep2: { - screen: ManualBackupStep2 - }, - ManualBackupStep3: { - screen: ManualBackupStep3 - } - }, - { - defaultNavigationOptions: { - // eslint-disable-next-line - headerTitle: () => ( - - ), - headerStyle: { - borderBottomWidth: 0 - } +const WalletTabHome = () => ( + + + + + + + + + +); + +const BrowserTabHome = () => ( + + + +); + +const TransactionsHome = () => ( + + + +); + +const HomeTabs = () => ( + + + + + +); + +const Webview = () => ( + + + +); + +const SettingsView = () => ( + + + + + + + + + + + + + + + + + + + + +); + +const ImportPrivateKeyView = () => ( + + + + +); + +const SendView = () => ( + + + +); + +const SendFlowView = () => ( + + + + + +); + +const ApprovalView = () => ( + + + +); + +const ApproveView = () => ( + + + +); + +const AddBookmarkView = () => ( + + + +); + +const OfflineModeView = () => ( + + + +); + +const PaymentRequestView = () => ( + + + + +); + +const FiatOnRamp = () => ( + + + + + + +); + +const Swaps = () => ( + + + + +); + +const SetPasswordFlow = () => ( + + + + + + + + + +); + +const MainNavigator = () => ( + + ({ + overlayStyle: { + opacity: 0 } - } - ) - } - }, - { - mode: 'modal', - headerMode: 'none', - lazy: true - } + }) + }} + /> + + + + + + + + + + + + + + + + ( + + )} + // eslint-disable-next-line react-native/no-inline-styles + headerStyle={{ borderBottomWidth: 0 }} + /> + ); + +export default MainNavigator; diff --git a/app/components/Nav/Main/__snapshots__/index.test.js.snap b/app/components/Nav/Main/__snapshots__/index.test.js.snap index 406e88dae87..f7f586f20ac 100644 --- a/app/components/Nav/Main/__snapshots__/index.test.js.snap +++ b/app/components/Nav/Main/__snapshots__/index.test.js.snap @@ -1,672 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Main should render correctly 1`] = ` - - - - - + + + `; diff --git a/app/components/Nav/Main/index.js b/app/components/Nav/Main/index.js index cc28cc32d55..c4740261ec3 100644 --- a/app/components/Nav/Main/index.js +++ b/app/components/Nav/Main/index.js @@ -461,6 +461,7 @@ const Main = props => { > {signType === 'personal' && ( { )} {signType === 'typed' && ( { const skipAccountModalSecureNow = () => { toggleRemindLater(); - props.navigation.navigate('AccountBackupStep1B', { ...props.navigation.state.params }); + props.navigation.navigate('SetPasswordFlow', { + screen: 'AccountBackupStep1B', + params: { ...props.route.params } + }); }; const skipAccountModalSkip = () => { @@ -655,16 +660,7 @@ const Main = props => { return ( - {!forceReload ? ( - - ) : ( - renderLoader() - )} + {!forceReload ? : renderLoader()} @@ -726,10 +722,6 @@ Main.propTypes = { */ hideCurrentNotification: PropTypes.func, removeNotificationById: PropTypes.func, - /** - * Indicates whether the current transaction is a deep link transaction - */ - isPaymentRequest: PropTypes.bool, /** * Indicates whether third party API mode is enabled */ @@ -773,7 +765,11 @@ Main.propTypes = { /** * Remove not visible notifications from state */ - removeNotVisibleNotifications: PropTypes.func + removeNotVisibleNotifications: PropTypes.func, + /** + * Object that represents the current route info like params passed to it + */ + route: PropTypes.object }; const mapStateToProps = state => ({ @@ -782,7 +778,6 @@ const mapStateToProps = state => ({ selectedAddress: state.engine.backgroundState.PreferencesController.selectedAddress, chainId: state.engine.backgroundState.NetworkController.provider.chainId, tokens: state.engine.backgroundState.AssetsController.tokens, - isPaymentRequest: state.transaction.paymentRequest, dappTransactionModalVisible: state.modals.dappTransactionModalVisible, approveModalVisible: state.modals.approveModalVisible, swapsTransactions: state.engine.backgroundState.TransactionController.swapsTransactions || {}, diff --git a/app/components/Nav/Main/index.test.js b/app/components/Nav/Main/index.test.js index 5830ae4b65b..eb556da3273 100644 --- a/app/components/Nav/Main/index.test.js +++ b/app/components/Nav/Main/index.test.js @@ -1,12 +1,16 @@ import React from 'react'; import { shallow } from 'enzyme'; // eslint-disable-next-line import/named -import { createAppContainer } from 'react-navigation'; +import { NavigationContainer } from '@react-navigation/native'; import Main from './'; describe('Main', () => { it('should render correctly', () => { - const MainAppContainer = createAppContainer(Main); + const MainAppContainer = () => ( + +
+ + ); const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); }); diff --git a/app/components/UI/AccountOverview/index.js b/app/components/UI/AccountOverview/index.js index 101bafc2f3e..1dd7dabe046 100644 --- a/app/components/UI/AccountOverview/index.js +++ b/app/components/UI/AccountOverview/index.js @@ -250,7 +250,7 @@ class AccountOverview extends PureComponent { }; onBuy = () => { - this.props.navigation.navigate('PaymentMethodSelector'); + this.props.navigation.navigate('FiatOnRamp'); InteractionManager.runAfterInteractions(() => { Analytics.trackEvent(ANALYTICS_EVENT_OPTS.WALLET_BUY_ETH); }); @@ -258,7 +258,10 @@ class AccountOverview extends PureComponent { goToSwaps = () => this.props.navigation.navigate('Swaps', { - sourceToken: swapsUtils.NATIVE_SWAPS_TOKEN_ADDRESS + screen: 'SwapsAmountView', + params: { + sourceToken: swapsUtils.NATIVE_SWAPS_TOKEN_ADDRESS + } }); render() { diff --git a/app/components/UI/AddCustomCollectible/index.test.js b/app/components/UI/AddCustomCollectible/index.test.js index 6bf88ac87b3..0a3784e1603 100644 --- a/app/components/UI/AddCustomCollectible/index.test.js +++ b/app/components/UI/AddCustomCollectible/index.test.js @@ -17,7 +17,7 @@ describe('AddCustomCollectible', () => { } }; - const wrapper = shallow(, { + const wrapper = shallow(, { context: { store: mockStore(initialState) } }); expect(wrapper.dive()).toMatchSnapshot(); diff --git a/app/components/UI/ApproveTransactionReview/index.js b/app/components/UI/ApproveTransactionReview/index.js index 63f028b01eb..63c1c073351 100644 --- a/app/components/UI/ApproveTransactionReview/index.js +++ b/app/components/UI/ApproveTransactionReview/index.js @@ -34,7 +34,7 @@ import TransactionReviewDetailsCard from '../../UI/TransactionReview/Transaction import Device from '../../../util/Device'; import AppConstants from '../../../core/AppConstants'; import { WALLET_CONNECT_ORIGIN } from '../../../util/walletconnect'; -import { withNavigation } from 'react-navigation'; +import { withNavigation } from '@react-navigation/compat'; import { getNetworkName, isMainNet, isMainnetByChainId } from '../../../util/networks'; import scaling from '../../../util/scaling'; import { capitalize } from '../../../util/general'; @@ -683,7 +683,7 @@ class ApproveTransactionReview extends PureComponent { const { navigation } = this.props; /* this is kinda weird, we have to reject the transaction to collapse the modal */ this.onCancelPress(); - navigation.navigate('PaymentMethodSelector'); + navigation.navigate('FiatOnRamp'); InteractionManager.runAfterInteractions(() => { Analytics.trackEvent(ANALYTICS_EVENT_OPTS.RECEIVE_OPTIONS_PAYMENT_REQUEST); }); diff --git a/app/components/UI/AssetOverview/index.js b/app/components/UI/AssetOverview/index.js index b4b56e83529..e1aa844b6ea 100644 --- a/app/components/UI/AssetOverview/index.js +++ b/app/components/UI/AssetOverview/index.js @@ -160,7 +160,7 @@ class AssetOverview extends PureComponent { }; onBuy = () => { - this.props.navigation.navigate('PaymentMethodSelector'); + this.props.navigation.navigate('FiatOnRamp'); InteractionManager.runAfterInteractions(() => { Analytics.trackEvent(ANALYTICS_EVENT_OPTS.WALLET_BUY_ETH); }); @@ -179,13 +179,19 @@ class AssetOverview extends PureComponent { goToSwaps = () => { this.props.navigation.navigate('Swaps', { - sourceToken: this.props.asset.isETH ? swapsUtils.NATIVE_SWAPS_TOKEN_ADDRESS : this.props.asset.address + screen: 'SwapsAmountView', + params: { + sourceToken: this.props.asset.isETH ? swapsUtils.NATIVE_SWAPS_TOKEN_ADDRESS : this.props.asset.address + } }); }; goToBrowserUrl(url) { - this.props.navigation.navigate('BrowserView', { - newTabUrl: url + this.props.navigation.navigate('BrowserTabHome', { + screen: 'BrowserView', + params: { + newTabUrl: url + } }); } diff --git a/app/components/UI/BackupAlert/index.js b/app/components/UI/BackupAlert/index.js index 86293475278..210615230d3 100644 --- a/app/components/UI/BackupAlert/index.js +++ b/app/components/UI/BackupAlert/index.js @@ -8,7 +8,7 @@ import { colors, fontStyles, baseStyles } from '../../../styles/common'; import Device from '../../../util/Device'; import { connect } from 'react-redux'; import { backUpSeedphraseAlertNotVisible } from '../../../actions/user'; -import { findBottomTabRouteNameFromNavigatorState, findRouteNameFromNavigatorState } from '../../../util/general'; +import { findRouteNameFromNavigatorState } from '../../../util/general'; const BROWSER_ROUTE = 'BrowserView'; @@ -112,20 +112,21 @@ class BackupAlert extends PureComponent { }; componentDidUpdate = async prevProps => { - if (prevProps.navigation.state !== this.props.navigation.state) { - const currentRouteName = findRouteNameFromNavigatorState(this.props.navigation.state); - const currentTabRouteName = findBottomTabRouteNameFromNavigatorState(this.props.navigation.state); + if (prevProps.navigation.dangerouslyGetState() !== this.props.navigation.dangerouslyGetState()) { + const currentRouteName = findRouteNameFromNavigatorState( + this.props.navigation.dangerouslyGetState().routes + ); const inBrowserView = currentRouteName === BROWSER_ROUTE; const blockedView = - BLOCKED_LIST.find(path => currentRouteName.includes(path)) || currentTabRouteName === 'SetPasswordFlow'; + BLOCKED_LIST.find(path => currentRouteName.includes(path)) || currentRouteName === 'SetPasswordFlow'; // eslint-disable-next-line react/no-did-update-set-state this.setState({ inBrowserView, blockedView }); } }; goToBackupFlow = () => { - this.props.navigation.navigate('AccountBackupStep1'); + this.props.navigation.navigate('SetPasswordFlow', { screen: 'AccountBackupStep1' }); }; onDismiss = () => { diff --git a/app/components/UI/CollectibleContractInformation/index.js b/app/components/UI/CollectibleContractInformation/index.js index f57c02c3e00..538dee2bb9a 100644 --- a/app/components/UI/CollectibleContractInformation/index.js +++ b/app/components/UI/CollectibleContractInformation/index.js @@ -138,8 +138,11 @@ class CollectibleContractInformation extends PureComponent { InteractionManager.runAfterInteractions(() => { this.closeModal(); this.props.navigation.push('Webview', { - url: openSeaUrl, - title: 'OpenSea' + screen: 'SimpleWebview', + params: { + url: openSeaUrl, + title: 'OpenSea' + } }); }); }; diff --git a/app/components/UI/CollectibleContracts/index.js b/app/components/UI/CollectibleContracts/index.js index b4b0b8371c5..5f769330b2d 100644 --- a/app/components/UI/CollectibleContracts/index.js +++ b/app/components/UI/CollectibleContracts/index.js @@ -1,4 +1,4 @@ -import React, { useState, useCallback } from 'react'; +import React, { useCallback } from 'react'; import PropTypes from 'prop-types'; import { TouchableOpacity, StyleSheet, View, InteractionManager, Image } from 'react-native'; import { connect } from 'react-redux'; @@ -8,7 +8,6 @@ import { strings } from '../../../../locales/i18n'; import CollectibleContractElement from '../CollectibleContractElement'; import Analytics from '../../../core/Analytics'; import { ANALYTICS_EVENT_OPTS } from '../../../util/analytics'; -import CollectibleModal from '../CollectibleModal'; import { favoritesCollectiblesObjectSelector } from '../../../reducers/collectibles'; import Text from '../../Base/Text'; import AppConstants from '../../../core/AppConstants'; @@ -66,17 +65,12 @@ const styles = StyleSheet.create({ * also known as ERC-721 Tokens */ const CollectibleContracts = ({ collectibleContracts, collectibles, navigation, favoriteCollectibles }) => { - const [collectible, setCollectible] = useState(null); - const [contractName, setContractName] = useState(null); - const [showCollectibleModal, setShowCollectibleModal] = useState(null); - const onItemPress = useCallback((collectible, contractName) => { - setCollectible(collectible); - setContractName(contractName); - setShowCollectibleModal(true); - }, []); - const hideCollectibleModal = () => { - setShowCollectibleModal(false); - }; + const onItemPress = useCallback( + (collectible, contractName) => { + navigation.navigate('CollectiblesDetails', { collectible, contractName }); + }, + [navigation] + ); const goToAddCollectible = () => { navigation.push('AddAsset', { assetType: 'collectible' }); @@ -141,7 +135,8 @@ const CollectibleContracts = ({ collectibleContracts, collectibles, navigation, [collectibleContracts, renderFavoriteCollectibles, renderCollectibleContract] ); - const goToLearnMore = () => navigation.navigate('SimpleWebview', { url: AppConstants.URLS.NFT }); + const goToLearnMore = () => + navigation.navigate('Webview', { screen: 'SimpleWebview', params: { url: AppConstants.URLS.NFT } }); const renderEmpty = () => ( @@ -168,15 +163,6 @@ const CollectibleContracts = ({ collectibleContracts, collectibles, navigation, {collectibles.length ? renderList() : renderEmpty()} {renderFooter()} - {collectible && contractName && ( - - )} ); }; diff --git a/app/components/UI/CollectibleContracts/index.test.js b/app/components/UI/CollectibleContracts/index.test.js index 0b3bca2b450..012a142e855 100644 --- a/app/components/UI/CollectibleContracts/index.test.js +++ b/app/components/UI/CollectibleContracts/index.test.js @@ -40,7 +40,7 @@ describe('CollectibleContracts', () => { const wrapper = shallow( - - - - - - - + } +/> `; diff --git a/app/components/UI/CollectibleModal/index.js b/app/components/UI/CollectibleModal/index.js index b0dca6e7050..ee257280080 100644 --- a/app/components/UI/CollectibleModal/index.js +++ b/app/components/UI/CollectibleModal/index.js @@ -1,14 +1,14 @@ -import React, { useCallback, useState } from 'react'; +import React, { useCallback, useRef, useState } from 'react'; import { View, StyleSheet } from 'react-native'; import PropTypes from 'prop-types'; import CollectibleOverview from '../../UI/CollectibleOverview'; import { connect } from 'react-redux'; import collectiblesTransferInformation from '../../../util/collectibles-transfer'; import { newAssetTransaction } from '../../../actions/transaction'; -import Modal from 'react-native-modal'; import CollectibleMedia from '../CollectibleMedia'; import { baseStyles, colors } from '../../../styles/common'; import Device from '../../../util/Device'; +import ReusableModal from '../ReusableModal'; const styles = StyleSheet.create({ bottomModal: { @@ -33,16 +33,17 @@ const styles = StyleSheet.create({ /** * View that displays a specific collectible asset */ -const CollectibleModal = ({ contractName, collectible, onHide, visible, navigation, newAssetTransaction }) => { - const [swippable, setSwippable] = useState('down'); +const CollectibleModal = props => { + const { route, navigation, newAssetTransaction } = props; + const { contractName, collectible } = route.params; + const [mediaZIndex, setMediaZIndex] = useState(20); const [overviewZIndex, setOverviewZIndex] = useState(10); const onSend = useCallback(async () => { - onHide(); newAssetTransaction({ contractName, ...collectible }); - navigation.navigate('SendFlowView'); - }, [contractName, collectible, newAssetTransaction, onHide, navigation]); + navigation.replace('SendFlowView'); + }, [contractName, collectible, newAssetTransaction, navigation]); const isTradable = useCallback(() => { const lowerAddress = collectible.address.toLowerCase(); @@ -54,14 +55,12 @@ const CollectibleModal = ({ contractName, collectible, onHide, visible, navigati return tradable; }, [collectible.address]); - const openLink = url => { - onHide(); - navigation.navigate('SimpleWebview', { url }); - }; - - const onTouchStart = useCallback(() => setSwippable(null), []); - - const onTouchEnd = useCallback(() => setSwippable('down'), []); + const openLink = useCallback( + url => { + navigation.replace('Webview', { screen: 'SimpleWebview', params: { url } }); + }, + [navigation] + ); /** * Method that moves the collectible media up or down in the screen depending on @@ -81,39 +80,32 @@ const CollectibleModal = ({ contractName, collectible, onHide, visible, navigati } }; + const modalRef = useRef(null); + return ( - - - - - - - - + navigation.navigate('WalletView')} style={styles.bottomModal}> + <> + + modalRef.current.dismissModal()} + cover + renderAnimation + collectible={collectible} + style={styles.round} + /> + + + + + + ); }; @@ -123,6 +115,10 @@ CollectibleModal.propTypes = { /* passed by the parent component */ navigation: PropTypes.object, + /** + * Route contains props passed when navigating + */ + route: PropTypes.object, /** * Start transaction with asset */ @@ -134,15 +130,7 @@ CollectibleModal.propTypes = { /** * Contract name of the collectible */ - contractName: PropTypes.string, - /** - * Function called when user swipes down the modal - */ - onHide: PropTypes.func, - /** - * If the modal should be visible or not - */ - visible: PropTypes.bool + contractName: PropTypes.string }; const mapDispatchToProps = dispatch => ({ diff --git a/app/components/UI/CollectibleModal/index.test.js b/app/components/UI/CollectibleModal/index.test.js index 057a8e6f9a1..d89ede47b3d 100644 --- a/app/components/UI/CollectibleModal/index.test.js +++ b/app/components/UI/CollectibleModal/index.test.js @@ -1,7 +1,7 @@ import React from 'react'; -import CollectibleModal from '.'; import configureMockStore from 'redux-mock-store'; import { shallow } from 'enzyme'; +import CollectibleModal from './index'; const mockStore = configureMockStore(); @@ -11,14 +11,18 @@ describe('CollectibleModal', () => { const wrapper = shallow( , { context: { store: mockStore(initialState) } } ); - expect(wrapper.dive()).toMatchSnapshot(); + expect(wrapper).toMatchSnapshot(); }); }); diff --git a/app/components/UI/CollectibleOverview/__snapshots__/index.test.js.snap b/app/components/UI/CollectibleOverview/__snapshots__/index.test.js.snap index 8f4070bbd80..c452684f402 100644 --- a/app/components/UI/CollectibleOverview/__snapshots__/index.test.js.snap +++ b/app/components/UI/CollectibleOverview/__snapshots__/index.test.js.snap @@ -1,40 +1,45 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`CollectibleOverview should render correctly 1`] = ` - - + - - - - + - - - + - - - - - + + + + + - - - - + + + + + - - - + - - - + + + `; diff --git a/app/components/UI/CollectibleOverview/index.js b/app/components/UI/CollectibleOverview/index.js index 2b647e46bc2..b8dc3d00473 100644 --- a/app/components/UI/CollectibleOverview/index.js +++ b/app/components/UI/CollectibleOverview/index.js @@ -1,5 +1,5 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { StyleSheet, View, Easing, Animated, SafeAreaView, ScrollView, TouchableWithoutFeedback } from 'react-native'; +import { StyleSheet, View, Easing, Animated, SafeAreaView, TouchableWithoutFeedback } from 'react-native'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { baseStyles, colors } from '../../../styles/common'; @@ -17,7 +17,7 @@ import etherscanLink from '@metamask/etherscan-link'; import { addFavoriteCollectible, removeFavoriteCollectible } from '../../../actions/collectibles'; import { favoritesCollectiblesObjectSelector, isCollectibleInFavorites } from '../../../reducers/collectibles'; import Share from 'react-native-share'; -import { PanGestureHandler, gestureHandlerRootHOC } from 'react-native-gesture-handler'; +import { PanGestureHandler, gestureHandlerRootHOC, ScrollView } from 'react-native-gesture-handler'; import AppConstants from '../../../core/AppConstants'; const ANIMATION_VELOCITY = 250; @@ -119,15 +119,13 @@ const CollectibleOverview = ({ removeFavoriteCollectible, isInFavorites, openLink, - onHide, - onTouchStart, - onTouchEnd, onTranslation }) => { const [headerHeight, setHeaderHeight] = useState(0); const [wrapperHeight, setWrapperHeight] = useState(0); const [position, setPosition] = useState(0); const positionAnimated = useRef(new Animated.Value(0)).current; + const scrollViewRef = useRef(null); const translationHeight = useMemo(() => wrapperHeight - headerHeight - ANIMATION_OFFSET, [ headerHeight, @@ -243,18 +241,23 @@ const CollectibleOverview = ({ if (toValue !== position) { onTranslation(toValue !== 0); animateViewPosition(toValue, ANIMATION_VELOCITY); - } else if (position !== 0) { - // if the modal is on bottom position we want to hide everyhing - onHide(); } }, - [translationHeight, position, onTranslation, animateViewPosition, onHide] + [translationHeight, position, onTranslation, animateViewPosition] ); - const gestureHandlerWrapper = child => ( - - {child} - + const gestureHandlerWrapper = useCallback( + child => ( + + {child} + + ), + [handleGesture, scrollViewRef] ); useEffect(() => { @@ -263,52 +266,46 @@ const CollectibleOverview = ({ } }, [headerHeight, wrapperHeight, translationHeight, animateViewPosition]); - return ( + return gestureHandlerWrapper( - {gestureHandlerWrapper( - - - - )} + + + + - {gestureHandlerWrapper( - - {collectible?.creator && ( - - - - {collectible.creator.user?.username && ( - - {collectible.creator.user.username} - - )} - - {collectible.contractName} + + {collectible?.creator && ( + + + + {collectible.creator.user?.username && ( + + {collectible.creator.user.username} - + )} + + {collectible.contractName} + - )} - - {collectible.name} - - - {strings('unit.token_id')} - {collectible.tokenId} - - - )} + + )} + + {collectible.name} + + + {strings('unit.token_id')} + {collectible.tokenId} + + {tradable && ( @@ -347,15 +344,18 @@ const CollectibleOverview = ({ {collectible?.description ? ( - {gestureHandlerWrapper( - - - {strings('collectible.collectible_description')} - - - )} + + + {strings('collectible.collectible_description')} + + + {renderScrollableDescription ? ( - + {collectible.description} @@ -363,20 +363,18 @@ const CollectibleOverview = ({ ) : ( - gestureHandlerWrapper( - - - {collectible.description} - - - ) + + + {collectible.description} + + )} ) : ( )} - {gestureHandlerWrapper({renderCollectibleInfo()})} + {{renderCollectibleInfo()}} ); @@ -419,22 +417,10 @@ CollectibleOverview.propTypes = { * Function to open a link on a webview */ openLink: PropTypes.func.isRequired, - /** - * View on touch start callback - */ - onTouchStart: PropTypes.func.isRequired, - /** - * View onn touch end callback - */ - onTouchEnd: PropTypes.func.isRequired, /** * callback to trigger when modal is being animated */ - onTranslation: PropTypes.func, - /** - * callback to trigger when modal is dragged down in bottom position - */ - onHide: PropTypes.func + onTranslation: PropTypes.func }; const mapStateToProps = (state, props) => { diff --git a/app/components/UI/CustomGas/index.test.js b/app/components/UI/CustomGas/index.test.js index 600b8329106..55bc3c748fa 100644 --- a/app/components/UI/CustomGas/index.test.js +++ b/app/components/UI/CustomGas/index.test.js @@ -40,7 +40,7 @@ describe('CustomGas', () => { const wrapper = shallow( { this.toggleAccountsModal(); - this.props.navigation.navigate('ImportPrivateKey'); + this.props.navigation.navigate('ImportPrivateKeyView'); this.hideDrawer(); }; @@ -789,7 +806,10 @@ class DrawerView extends PureComponent { onSecureWalletModalAction = () => { this.setState({ showProtectWalletModal: false }); - this.props.navigation.navigate(this.props.passwordSet ? 'AccountBackupStep1' : 'SetPasswordFlow'); + this.props.navigation.navigate( + 'SetPasswordFlow', + this.props.passwordSet ? { screen: 'AccountBackupStep1' } : undefined + ); }; renderProtectModal = () => ( @@ -844,7 +864,7 @@ class DrawerView extends PureComponent { } this.currentBalance = fiatBalance; const fiatBalanceStr = renderFiat(this.currentBalance, currentCurrency); - const currentRoute = findRouteNameFromNavigatorState(this.props.navigation.state); + const currentRoute = findRouteNameFromNavigatorState(this.props.navigation.dangerouslyGetState().routes); return ( @@ -1006,6 +1026,7 @@ class DrawerView extends PureComponent { diff --git a/app/components/UI/FiatOrders/PaymentMethodApplePay/index.js b/app/components/UI/FiatOrders/PaymentMethodApplePay/index.js index 45cea25791f..3e5beef5628 100644 --- a/app/components/UI/FiatOrders/PaymentMethodApplePay/index.js +++ b/app/components/UI/FiatOrders/PaymentMethodApplePay/index.js @@ -1,7 +1,7 @@ -import React, { useContext, useState, useCallback, useEffect, useMemo } from 'react'; +import React, { useState, useCallback, useEffect, useMemo } from 'react'; import PropTypes from 'prop-types'; import { View, StyleSheet, Image, TouchableOpacity, InteractionManager, ActivityIndicator } from 'react-native'; -import { NavigationContext } from 'react-navigation'; +import { useNavigation } from '@react-navigation/native'; import { connect } from 'react-redux'; import NotificationManager from '../../../../core/NotificationManager'; import Device from '../../../../util/Device'; @@ -175,7 +175,7 @@ function PaymentMethodApplePay({ setFiatOrdersCountry, protectWalletModalVisible }) { - const navigation = useContext(NavigationContext); + const navigation = useNavigation(); const [amount, setAmount] = useState('0'); const { symbol: currencySymbol, decimalSeparator, currency: selectedCurrency } = useCountryCurrency( selectedCountry @@ -245,7 +245,7 @@ function PaymentMethodApplePay({ if (order !== ABORTED) { if (order) { addOrder(order); - navigation.dismiss(); + navigation.dangerouslyGetParent()?.pop(); protectWalletModalVisible(); InteractionManager.runAfterInteractions(() => NotificationManager.showSimpleNotification(getNotificationDetails(order)) diff --git a/app/components/UI/FiatOrders/PaymentMethodSelector/index.android.js b/app/components/UI/FiatOrders/PaymentMethodSelector/index.android.js index 440c16ea770..5dcbb7272e5 100644 --- a/app/components/UI/FiatOrders/PaymentMethodSelector/index.android.js +++ b/app/components/UI/FiatOrders/PaymentMethodSelector/index.android.js @@ -1,7 +1,7 @@ -import React, { useContext, useCallback } from 'react'; +import React, { useCallback } from 'react'; import { InteractionManager } from 'react-native'; import PropTypes from 'prop-types'; -import { NavigationContext } from 'react-navigation'; +import { useNavigation } from '@react-navigation/native'; import { connect } from 'react-redux'; import { strings } from '../../../../../locales/i18n'; import Analytics from '../../../../core/Analytics'; @@ -22,7 +22,7 @@ function PaymentMethodSelectorView({ setGasEducationCarouselSeen, ...props }) { - const navigation = useContext(NavigationContext); + const navigation = useNavigation(); const transakURL = useTransakFlowURL(selectedAddress); const onPressTransak = useCallback(() => { diff --git a/app/components/UI/FiatOrders/PaymentMethodSelector/index.ios.js b/app/components/UI/FiatOrders/PaymentMethodSelector/index.ios.js index afdaae468c1..f9e4d74cf37 100644 --- a/app/components/UI/FiatOrders/PaymentMethodSelector/index.ios.js +++ b/app/components/UI/FiatOrders/PaymentMethodSelector/index.ios.js @@ -1,7 +1,7 @@ -import React, { useContext, useCallback } from 'react'; +import React, { useCallback } from 'react'; import { InteractionManager } from 'react-native'; import PropTypes from 'prop-types'; -import { NavigationContext } from 'react-navigation'; +import { useNavigation } from '@react-navigation/native'; import { connect } from 'react-redux'; import { strings } from '../../../../../locales/i18n'; import Analytics from '../../../../core/Analytics'; @@ -29,7 +29,7 @@ function PaymentMethodSelectorView({ setGasEducationCarouselSeen, ...props }) { - const navigation = useContext(NavigationContext); + const navigation = useNavigation(); const transakURL = useTransakFlowURL(selectedAddress); const onPressWyreApplePay = useCallback(() => { diff --git a/app/components/UI/FiatOrders/PaymentMethodSelector/wyreApplePay.js b/app/components/UI/FiatOrders/PaymentMethodSelector/wyreApplePay.js index aed6c23d58e..7ece8c3dd1f 100644 --- a/app/components/UI/FiatOrders/PaymentMethodSelector/wyreApplePay.js +++ b/app/components/UI/FiatOrders/PaymentMethodSelector/wyreApplePay.js @@ -1,7 +1,7 @@ -import React, { useContext } from 'react'; +import React from 'react'; import PropTypes from 'prop-types'; import { StyleSheet, Image, TouchableOpacity, View } from 'react-native'; -import { NavigationContext } from 'react-navigation'; +import { useNavigation } from '@react-navigation/native'; import { strings } from '../../../../../locales/i18n'; import { useWyreTerms } from '../orderProcessor/wyreApplePay'; @@ -42,7 +42,7 @@ const ApplePayMark = () => ; const WyreApplePayPaymentMethod = ({ onPress }) => { - const navigation = useContext(NavigationContext); + const navigation = useNavigation(); const [isVisible, , showModal, hideModal] = useModalHandler(false); const handleWyreTerms = useWyreTerms(navigation); diff --git a/app/components/UI/FiatOrders/TransakWebView/index.js b/app/components/UI/FiatOrders/TransakWebView/index.js index 5f98cdb476b..2068310fb9a 100644 --- a/app/components/UI/FiatOrders/TransakWebView/index.js +++ b/app/components/UI/FiatOrders/TransakWebView/index.js @@ -14,7 +14,7 @@ import { protectWalletModalVisible } from '../../../../actions/user'; import { addFiatOrder } from '../../../../reducers/fiatOrders'; class TransakWebView extends PureComponent { - static navigationOptions = ({ navigation }) => getTransakWebviewNavbar(navigation); + static navigationOptions = ({ navigation, route }) => getTransakWebviewNavbar(navigation, route); static propTypes = { navigation: PropTypes.object, @@ -29,7 +29,11 @@ class TransakWebView extends PureComponent { /** * Prompts protect wallet modal */ - protectWalletModalVisible: PropTypes.func + protectWalletModalVisible: PropTypes.func, + /** + * Object that represents the current route info like params passed to it + */ + route: PropTypes.object }; handleNavigationStateChange = async navState => { @@ -37,7 +41,7 @@ class TransakWebView extends PureComponent { const order = handleTransakRedirect(navState.url, this.props.network); this.props.addOrder(order); this.props.protectWalletModalVisible(); - this.props.navigation.dismiss(); + this.props.navigation.dangerouslyGetParent()?.pop(); InteractionManager.runAfterInteractions(() => NotificationManager.showSimpleNotification(getNotificationDetails(order)) ); @@ -45,7 +49,7 @@ class TransakWebView extends PureComponent { }; render() { - const uri = this.props.navigation.getParam('url', null); + const uri = this.props.route.params?.url; if (uri) { return ( diff --git a/app/components/UI/FiatOrders/components/PaymentMethod/Modal.js b/app/components/UI/FiatOrders/components/PaymentMethod/Modal.js index 0bc2f79e1c4..2e4a9cade81 100644 --- a/app/components/UI/FiatOrders/components/PaymentMethod/Modal.js +++ b/app/components/UI/FiatOrders/components/PaymentMethod/Modal.js @@ -1,7 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { StyleSheet, View, TouchableOpacity, ScrollView } from 'react-native'; -import { SafeAreaView } from 'react-navigation'; +import { StyleSheet, View, TouchableOpacity, ScrollView, SafeAreaView } from 'react-native'; import Modal from 'react-native-modal'; import IonicIcon from 'react-native-vector-icons/Ionicons'; import { strings } from '../../../../../../locales/i18n'; diff --git a/app/components/UI/FiatOrders/orderProcessor/wyreApplePay.js b/app/components/UI/FiatOrders/orderProcessor/wyreApplePay.js index a9dddfae1a2..9644b7bba7c 100644 --- a/app/components/UI/FiatOrders/orderProcessor/wyreApplePay.js +++ b/app/components/UI/FiatOrders/orderProcessor/wyreApplePay.js @@ -714,8 +714,11 @@ export function useWyreTerms(navigation) { const handleWyreTerms = useCallback( () => navigation.navigate('Webview', { - url: 'https://www.sendwyre.com/user-agreement/', - title: strings('fiat_on_ramp.wyre_user_agreement') + screen: 'SimpleWebview', + params: { + url: 'https://www.sendwyre.com/user-agreement/', + title: strings('fiat_on_ramp.wyre_user_agreement') + } }), [navigation] ); diff --git a/app/components/UI/InvalidCustomNetworkAlert/__snapshots__/index.test.js.snap b/app/components/UI/InvalidCustomNetworkAlert/__snapshots__/index.test.js.snap index 108ef3cd69e..236ebca9ba8 100644 --- a/app/components/UI/InvalidCustomNetworkAlert/__snapshots__/index.test.js.snap +++ b/app/components/UI/InvalidCustomNetworkAlert/__snapshots__/index.test.js.snap @@ -1,7 +1,141 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`InvalidCustomNetworkAlert should render correctly 1`] = ` - - - + + + + + The chain ID for custom network + [missing %{network} value] + has to be re-entered. + + + + + + To protect you from malicious or faulty network providers, chain IDs are now required for all custom networks. + + + + You can find the chain IDs of most popular networks on + + chainId.network + + . + + + + + Edit network + + + + + Cancel + + + + `; diff --git a/app/components/UI/InvalidCustomNetworkAlert/index.js b/app/components/UI/InvalidCustomNetworkAlert/index.js index 0eefe0457aa..d4db27eb256 100644 --- a/app/components/UI/InvalidCustomNetworkAlert/index.js +++ b/app/components/UI/InvalidCustomNetworkAlert/index.js @@ -4,7 +4,6 @@ import { colors, fontStyles } from '../../../styles/common'; import { strings } from '../../../../locales/i18n'; import StyledButton from '../StyledButton'; import PropTypes from 'prop-types'; -import { withNavigation } from 'react-navigation'; const styles = StyleSheet.create({ wrapper: { @@ -54,12 +53,12 @@ const InvalidCustomNetworkAlert = props => { const goToEditNetwork = () => { closeModal(); - props.navigation.navigate('NetworkSettings', { network: props.network }); + props.navigation.navigate('SettingsView', { screen: 'NetworkSettings', params: { network: props.network } }); }; const openLink = () => { closeModal(); - props.navigation.navigate('SimpleWebview', { url: 'https://chainid.network' }); + props.navigation.navigate('Webview', { screen: 'SimpleWebview', params: { url: 'https://chainid.network' } }); }; return ( @@ -110,4 +109,4 @@ InvalidCustomNetworkAlert.propTypes = { network: PropTypes.string }; -export default withNavigation(InvalidCustomNetworkAlert); +export default InvalidCustomNetworkAlert; diff --git a/app/components/UI/Navbar/index.js b/app/components/UI/Navbar/index.js index d4068ad7483..21d2d1ff21c 100644 --- a/app/components/UI/Navbar/index.js +++ b/app/components/UI/Navbar/index.js @@ -1,3 +1,4 @@ +/* eslint-disable react/display-name */ import React from 'react'; import NavbarTitle from '../NavbarTitle'; import ModalNavbarTitle from '../ModalNavbarTitle'; @@ -35,11 +36,6 @@ const trackEventWithParameters = (event, params) => { }; const styles = StyleSheet.create({ - rightButton: { - marginTop: 7, - marginRight: 12, - marginBottom: 12 - }, metamaskName: { width: 122, height: 15 @@ -49,10 +45,6 @@ const styles = StyleSheet.create({ height: 40, marginRight: 10 }, - closeIcon: { - paddingLeft: Device.isAndroid() ? 22 : 18, - color: colors.blue - }, backIcon: { color: colors.blue }, @@ -86,31 +78,15 @@ const styles = StyleSheet.create({ infoIcon: { color: colors.blue }, - flex: { - flex: 1 - }, closeButtonText: { color: colors.blue, fontSize: 14, ...fontStyles.normal }, - title: { - fontSize: 18, - ...fontStyles.normal - }, - titleWrapper: { - alignItems: 'center', - flex: 1 - }, browserRightButton: { flex: 1, marginRight: Device.isAndroid() ? 10 : 0 }, - tabIconAndroidWrapper: { - alignItems: 'center', - width: 36, - marginLeft: 5 - }, disabled: { opacity: 0.3 }, @@ -119,13 +95,6 @@ const styles = StyleSheet.create({ alignItems: 'center', marginHorizontal: Device.isIos() ? 20 : 0 }, - tabIconAndroid: { - marginTop: 13, - marginLeft: 0, - marginRight: 3, - width: 24, - height: 24 - }, metamaskNameTransparentWrapper: { alignItems: 'center', flex: 1 @@ -163,8 +132,8 @@ export default function getNavbarOptions(title, navigation, disableNetwork = fal } return { - headerTitle: , - headerLeft: ( + headerTitle: () => , + headerLeft: () => ( ), - headerRight: + headerRight: () => }; } @@ -197,7 +166,7 @@ export function getNavigationOptionsTitle(title, navigation) { ...fontStyles.normal }, headerTintColor: colors.blue, - headerLeft: ( + headerLeft: () => ( { - ''; - }); - const editMode = navigation.getParam('editMode', '') === 'edit'; - const addMode = navigation.getParam('mode', '') === 'add'; + const rightAction = route.params?.dispatch; + const editMode = route.params?.editMode ?? '' === 'edit'; + const addMode = route.params?.mode ?? '' === 'add'; return { title, headerTitleStyle: { @@ -234,7 +201,7 @@ export function getEditableOptions(title, navigation) { ...fontStyles.normal }, headerTintColor: colors.blue, - headerLeft: ( + headerLeft: () => ( ), - headerRight: !addMode ? ( - - - {editMode ? strings('address_book.edit') : strings('address_book.cancel')} - - - ) : ( - - ) + headerRight: () => + !addMode ? ( + + + {editMode ? strings('address_book.edit') : strings('address_book.cancel')} + + + ) : ( + + ) }; } @@ -263,8 +231,8 @@ export function getEditableOptions(title, navigation) { * @param {Object} navigation - Navigation object required to push new views * @returns {Object} - Corresponding navbar options containing title, headerLeft and headerRight */ -export function getPaymentRequestOptionsTitle(title, navigation) { - const goBack = navigation.getParam('dispatch', undefined); +export function getPaymentRequestOptionsTitle(title, navigation, route) { + const goBack = route.params?.dispatch; return { title, headerTitleStyle: { @@ -273,19 +241,24 @@ export function getPaymentRequestOptionsTitle(title, navigation) { ...fontStyles.normal }, headerTintColor: colors.blue, - headerLeft: goBack ? ( - // eslint-disable-next-line react/jsx-no-bind - - - - ) : ( - - ), - headerRight: ( + headerLeft: () => + goBack ? ( + // eslint-disable-next-line react/jsx-no-bind + + + + ) : ( + + ), + headerRight: () => ( // eslint-disable-next-line react/jsx-no-bind navigation.pop()} style={styles.closeButton}> @@ -308,9 +281,10 @@ export function getPaymentRequestSuccessOptionsTitle(navigation) { backgroundColor: colors.white, borderBottomWidth: 0 }, + title: null, headerTintColor: colors.blue, - headerLeft: , - headerRight: ( + headerLeft: () => , + headerRight: () => ( navigation.pop()} @@ -330,21 +304,19 @@ export function getPaymentRequestSuccessOptionsTitle(navigation) { * @param {string} title - Title in string format * @returns {Object} - Corresponding navbar options containing title and headerTitleStyle */ -export function getTransactionOptionsTitle(_title, navigation) { - const transactionMode = navigation.getParam('mode', ''); - const { routeName } = navigation.state; +export function getTransactionOptionsTitle(_title, navigation, route) { + const transactionMode = route.params?.mode ?? ''; + const { name } = route; const leftText = transactionMode === 'edit' ? strings('transaction.cancel') : strings('transaction.edit'); - const disableModeChange = navigation.getParam('disableModeChange'); - const modeChange = navigation.getParam('dispatch', () => { - ''; - }); + const disableModeChange = route.params?.disableModeChange; + const modeChange = route.params?.dispatch; const leftAction = () => modeChange('edit'); const rightAction = () => navigation.pop(); const rightText = strings('transaction.cancel'); const title = transactionMode === 'edit' ? 'transaction.edit' : _title; return { - headerTitle: , - headerLeft: + headerTitle: () => , + headerLeft: () => transactionMode !== 'edit' ? ( ), - headerRight: - routeName === 'Send' ? ( + headerRight: () => + name === 'Send' ? ( // eslint-disable-next-line react/jsx-no-bind {rightText} @@ -376,9 +348,9 @@ export function getTransactionOptionsTitle(_title, navigation) { export function getApproveNavbar(title) { return { - headerTitle: , - headerLeft: , - headerRight: + headerTitle: () => , + headerLeft: () => , + headerRight: () => }; } @@ -389,36 +361,38 @@ export function getApproveNavbar(title) { * @param {string} title - Title in string format * @returns {Object} - Corresponding navbar options containing title and headerTitleStyle */ -export function getSendFlowTitle(title, navigation, screenProps) { +export function getSendFlowTitle(title, navigation, route) { const rightAction = () => { - const providerType = navigation.getParam('providerType', ''); + const providerType = route.params?.providerType ?? ''; trackEventWithParameters(ANALYTICS_EVENT_OPTS.SEND_FLOW_CANCEL, { view: title.split('.')[1], network: providerType }); - navigation.dismiss(); + navigation.dangerouslyGetParent()?.pop(); }; const leftAction = () => navigation.pop(); - const canGoBack = title !== 'send.send_to' && !screenProps.isPaymentRequest; + + const canGoBack = title !== 'send.send_to' && !route?.params?.isPaymentRequest; const titleToRender = title; return { - headerTitle: , - headerRight: ( + headerTitle: () => , + headerRight: () => ( // eslint-disable-next-line react/jsx-no-bind {strings('transaction.cancel')} ), - headerLeft: canGoBack ? ( - // eslint-disable-next-line react/jsx-no-bind - - {strings('transaction.back')} - - ) : ( - - ) + headerLeft: () => + canGoBack ? ( + // eslint-disable-next-line react/jsx-no-bind + + {strings('transaction.back')} + + ) : ( + + ) }; } @@ -430,21 +404,21 @@ export function getSendFlowTitle(title, navigation, screenProps) { * @param {Object} navigation - Navigation object required to push new views * @returns {Object} - Corresponding navbar options containing headerTitle, headerLeft and headerRight */ -export function getBrowserViewNavbarOptions(navigation) { - const url = navigation.getParam('url', ''); +export function getBrowserViewNavbarOptions(navigation, route) { + const url = route.params?.url ?? ''; let hostname = null; let isHttps = false; const isHomepage = url => getHost(url) === getHost(HOMEPAGE_URL); - const error = navigation.getParam('error', ''); - const icon = navigation.getParam('icon', null); + const error = route.params?.error ?? ''; + const icon = route.params?.icon; if (url && !isHomepage(url)) { isHttps = url && url.toLowerCase().substr(0, 6) === 'https:'; const urlObj = new URL(url); hostname = urlObj.hostname.toLowerCase().replace(/^www\./, ''); if (isGatewayUrl(urlObj) && url.search(`${AppConstants.IPFS_OVERRIDE_PARAM}=false`) === -1) { - const ensUrl = navigation.getParam('currentEnsName', ''); + const ensUrl = route.params?.currentEnsName ?? ''; if (ensUrl) { hostname = ensUrl.toLowerCase().replace(/^www\./, ''); } @@ -460,7 +434,8 @@ export function getBrowserViewNavbarOptions(navigation) { } return { - headerLeft: ( + gestureEnabled: false, + headerLeft: () => ( ), - headerTitle: ( + headerTitle: () => ( ), - headerRight: ( + headerRight: () => ( @@ -496,7 +472,7 @@ export function getBrowserViewNavbarOptions(navigation) { */ export function getModalNavbarOptions(title) { return { - headerTitle: + headerTitle: () => }; } @@ -507,8 +483,8 @@ export function getModalNavbarOptions(title) { * * @returns {Object} - Corresponding navbar options containing headerTitle, headerTitle and headerTitle */ -export function getOnboardingNavbarOptions(navigation, { headerLeft } = {}) { - const headerLeftHide = headerLeft || navigation.getParam('headerLeft'); +export function getOnboardingNavbarOptions(navigation, route, { headerLeft } = {}) { + const headerLeftHide = headerLeft || route.params?.headerLeft; return { headerStyle: { @@ -517,13 +493,13 @@ export function getOnboardingNavbarOptions(navigation, { headerLeft } = {}) { backgroundColor: colors.white, borderBottomWidth: 0 }, - headerTitle: ( + headerTitle: () => ( ), headerBackTitle: strings('navigation.back'), - headerRight: , + headerRight: () => , headerLeft: headerLeftHide }; } @@ -536,13 +512,13 @@ export function getOnboardingNavbarOptions(navigation, { headerLeft } = {}) { export function getTransparentOnboardingNavbarOptions() { return { headerTransparent: true, - headerTitle: ( + headerTitle: () => ( ), - headerLeft: , - headerRight: + headerLeft: () => , + headerRight: () => }; } @@ -554,13 +530,13 @@ export function getTransparentOnboardingNavbarOptions() { export function getTransparentBackOnboardingNavbarOptions() { return { headerTransparent: true, - headerTitle: ( + headerTitle: () => ( ), headerBackTitle: strings('navigation.back'), - headerRight: + headerRight: () => }; } @@ -579,7 +555,8 @@ export function getOptinMetricsNavbarOptions() { borderBottomWidth: 0, height: 100 }, - headerLeft: ( + title: null, + headerLeft: () => ( @@ -607,15 +584,16 @@ export function getClosableNavigationOptions(title, backButtonText, navigation) fontSize: 20, ...fontStyles.normal }, - headerLeft: Device.isIos() ? ( - - {backButtonText} - - ) : ( - - - - ) + headerLeft: () => + Device.isIos() ? ( + + {backButtonText} + + ) : ( + + + + ) }; } @@ -633,18 +611,20 @@ export function getOfflineModalNavbar(navigation) { backgroundColor: colors.white, borderBottomWidth: 0 }, - headerLeft: Device.isAndroid() ? ( - // eslint-disable-next-line react/jsx-no-bind - navigation.pop()} style={styles.backButton}> - - - ) : null, - headerRight: Device.isIos() ? ( - // eslint-disable-next-line react/jsx-no-bind - navigation.pop()} style={styles.backButton}> - - - ) : null + headerLeft: () => + Device.isAndroid() ? ( + // eslint-disable-next-line react/jsx-no-bind + navigation.pop()} style={styles.backButton}> + + + ) : null, + headerRight: () => + Device.isIos() ? ( + // eslint-disable-next-line react/jsx-no-bind + navigation.pop()} style={styles.backButton}> + + + ) : null }; } @@ -671,7 +651,7 @@ export function getWalletNavbarOptions(title, navigation) { onPress: async () => { try { await importAccountFromPrivateKey(data.private_key); - navigation.navigate('ImportPrivateKeySuccess'); + navigation.navigate('ImportPrivateKeyView', { screen: 'ImportPrivateKeySuccess' }); } catch (e) { Alert.alert( strings('import_private_key.error_title'), @@ -705,8 +685,8 @@ export function getWalletNavbarOptions(title, navigation) { } return { - headerTitle: , - headerLeft: ( + headerTitle: () => , + headerLeft: () => ( ), - headerRight: ( + headerRight: () => ( , - headerLeft: ( + headerTitle: () => , + headerLeft: () => ( // eslint-disable-next-line react/jsx-no-bind navigation.pop()} style={styles.backButton} testID={'asset-back-button'}> ), - headerRight: + headerRight: () => }; } @@ -757,47 +737,43 @@ export function getNetworkNavbarOptions(title, translate, navigation) { * * @returns {Object} - Corresponding navbar options containing headerTitle and headerTitle */ -export function getWebviewNavbar(navigation) { - const title = navigation.getParam('title', ''); - const share = navigation.getParam('dispatch', () => { - ''; - }); +export function getWebviewNavbar(navigation, route) { + const title = route.params?.title ?? ''; + const share = route.params?.dispatch; return { - headerTitle: {title}, - headerLeft: Device.isAndroid() ? ( - // eslint-disable-next-line react/jsx-no-bind - navigation.pop()} style={styles.backButton}> - - - ) : ( - // eslint-disable-next-line react/jsx-no-bind - navigation.pop()} style={styles.backButton}> - - - ), - headerRight: Device.isAndroid() ? ( - // eslint-disable-next-line react/jsx-no-bind - share()} style={styles.backButton}> - - - ) : ( - // eslint-disable-next-line react/jsx-no-bind - share()} style={styles.backButton}> - - - ) + headerTitle: () => {title}, + headerLeft: () => + Device.isAndroid() ? ( + // eslint-disable-next-line react/jsx-no-bind + navigation.pop()} style={styles.backButton}> + + + ) : ( + // eslint-disable-next-line react/jsx-no-bind + navigation.pop()} style={styles.backButton}> + + + ), + headerRight: () => + Device.isAndroid() ? ( + + + + ) : ( + + + + ) }; } export function getPaymentSelectorMethodNavbar(navigation) { - const rightAction = navigation.dismiss; - return { - headerTitle: {strings('fiat_on_ramp.purchase_method')}, - headerLeft: , - headerRight: ( + headerTitle: () => {strings('fiat_on_ramp.purchase_method')}, + headerLeft: () => , + headerRight: () => ( // eslint-disable-next-line react/jsx-no-bind - + navigation.dangerouslyGetParent()?.pop()} style={styles.closeButton}> {strings('navigation.cancel')} ) @@ -812,28 +788,29 @@ export function getPaymentMethodApplePayNavbar(navigation) { color: colors.fontPrimary, ...fontStyles.normal }, - headerRight: ( + headerRight: () => ( // eslint-disable-next-line react/jsx-no-bind - navigation.dismiss()} style={styles.closeButton}> + navigation.dangerouslyGetParent()?.pop()} style={styles.closeButton}> {strings('navigation.cancel')} ), - headerLeft: Device.isAndroid() ? ( - // eslint-disable-next-line react/jsx-no-bind - navigation.pop()} style={styles.backButton}> - - - ) : ( - // eslint-disable-next-line react/jsx-no-bind - navigation.pop()} style={styles.closeButton}> - {strings('navigation.back')} - - ) + headerLeft: () => + Device.isAndroid() ? ( + // eslint-disable-next-line react/jsx-no-bind + navigation.pop()} style={styles.backButton}> + + + ) : ( + // eslint-disable-next-line react/jsx-no-bind + navigation.pop()} style={styles.closeButton}> + {strings('navigation.back')} + + ) }; } -export function getTransakWebviewNavbar(navigation) { - const title = navigation.getParam('title', ''); +export function getTransakWebviewNavbar(navigation, route) { + const title = route.params?.title ?? ''; return { title, headerTitleStyle: { @@ -841,43 +818,42 @@ export function getTransakWebviewNavbar(navigation) { color: colors.fontPrimary, ...fontStyles.normal }, - headerLeft: Device.isAndroid() ? ( - // eslint-disable-next-line react/jsx-no-bind - navigation.pop()} style={styles.backButton}> - - - ) : ( - // eslint-disable-next-line react/jsx-no-bind - navigation.pop()} style={styles.backButton}> - - - ) + headerLeft: () => + Device.isAndroid() ? ( + // eslint-disable-next-line react/jsx-no-bind + navigation.pop()} style={styles.backButton}> + + + ) : ( + // eslint-disable-next-line react/jsx-no-bind + navigation.pop()} style={styles.backButton}> + + + ) }; } -export function getSwapsAmountNavbar(navigation) { - const title = navigation.getParam('title', 'Swap'); - const rightAction = navigation.dismiss; - +export function getSwapsAmountNavbar(navigation, route) { + const title = route.params?.title ?? 'Swap'; return { - headerTitle: , - headerLeft: , - headerRight: ( + headerTitle: () => , + headerLeft: () => , + headerRight: () => ( // eslint-disable-next-line react/jsx-no-bind - + navigation.dangerouslyGetParent()?.pop()} style={styles.closeButton}> {strings('navigation.cancel')} ) }; } -export function getSwapsQuotesNavbar(navigation) { - const title = navigation.getParam('title', 'Swap'); - const leftActionText = navigation.getParam('leftAction', strings('navigation.back')); +export function getSwapsQuotesNavbar(navigation, route) { + const title = route.params?.title ?? 'Swap'; + const leftActionText = route.params?.leftAction ?? strings('navigation.back'); const leftAction = () => { - const trade = navigation.getParam('requestedTrade'); - const selectedQuote = navigation.getParam('selectedQuote'); - const quoteBegin = navigation.getParam('quoteBegin'); + const trade = route.params?.requestedTrade; + const selectedQuote = route.params?.selectedQuote; + const quoteBegin = route.params?.quoteBegin; if (!selectedQuote) { InteractionManager.runAfterInteractions(() => { Analytics.trackEventWithParameters(ANALYTICS_EVENT_OPTS.QUOTES_REQUEST_CANCELLED, { @@ -890,9 +866,9 @@ export function getSwapsQuotesNavbar(navigation) { }; const rightAction = () => { - const trade = navigation.getParam('requestedTrade'); - const selectedQuote = navigation.getParam('selectedQuote'); - const quoteBegin = navigation.getParam('quoteBegin'); + const trade = route.params?.requestedTrade; + const selectedQuote = route.params?.selectedQuote; + const quoteBegin = route.params?.quoteBegin; if (!selectedQuote) { InteractionManager.runAfterInteractions(() => { Analytics.trackEventWithParameters(ANALYTICS_EVENT_OPTS.QUOTES_REQUEST_CANCELLED, { @@ -901,23 +877,24 @@ export function getSwapsQuotesNavbar(navigation) { }); }); } - navigation.dismiss(); + navigation.dangerouslyGetParent()?.pop(); }; return { - headerTitle: , - headerLeft: Device.isAndroid() ? ( - // eslint-disable-next-line react/jsx-no-bind - - - - ) : ( - // eslint-disable-next-line react/jsx-no-bind - - {leftActionText} - - ), - headerRight: ( + headerTitle: () => , + headerLeft: () => + Device.isAndroid() ? ( + // eslint-disable-next-line react/jsx-no-bind + + + + ) : ( + // eslint-disable-next-line react/jsx-no-bind + + {leftActionText} + + ), + headerRight: () => ( // eslint-disable-next-line react/jsx-no-bind {strings('navigation.cancel')} diff --git a/app/components/UI/NavbarBrowserTitle/index.js b/app/components/UI/NavbarBrowserTitle/index.js index 85820945b15..cd7095171b9 100644 --- a/app/components/UI/NavbarBrowserTitle/index.js +++ b/app/components/UI/NavbarBrowserTitle/index.js @@ -17,13 +17,13 @@ const styles = StyleSheet.create({ marginBottom: 5 }, networkName: { - marginTop: -3, fontSize: 11, lineHeight: 11, color: colors.fontSecondary, ...fontStyles.normal }, networkIcon: { + marginTop: 3, width: 5, height: 5, borderRadius: 100, @@ -33,7 +33,8 @@ const styles = StyleSheet.create({ flexDirection: 'row', alignItems: 'center', justifyContent: 'center', - flex: 1 + flex: 1, + marginBottom: Device.isAndroid() ? 5 : 0 }, lockIcon: { marginTop: 2, @@ -60,10 +61,6 @@ const styles = StyleSheet.create({ */ class NavbarBrowserTitle extends PureComponent { static propTypes = { - /** - * Object representing the navigator - */ - navigation: PropTypes.object, /** * String representing the current url */ @@ -87,12 +84,15 @@ class NavbarBrowserTitle extends PureComponent { /** * Website icon */ - icon: PropTypes.string + icon: PropTypes.string, + /** + * Object that represents the current route info like params passed to it + */ + route: PropTypes.object }; onTitlePress = () => { - const showUrlModal = this.props.navigation.getParam('showUrlModal', () => null); - showUrlModal({ urlInput: this.props.url }); + this.props.route.params?.showUrlModal?.({ urlInput: this.props.url }); }; getNetworkName(network) { diff --git a/app/components/UI/Notification/SimpleNotification/index.js b/app/components/UI/Notification/SimpleNotification/index.js index bde4f6bfe56..5d265eb39e9 100644 --- a/app/components/UI/Notification/SimpleNotification/index.js +++ b/app/components/UI/Notification/SimpleNotification/index.js @@ -2,37 +2,33 @@ import React from 'react'; import { StyleSheet } from 'react-native'; import PropTypes from 'prop-types'; import Animated from 'react-native-reanimated'; -import { colors } from '../../../../styles/common'; import BaseNotification from './../BaseNotification'; import Device from '../../../../util/Device'; import ElevatedView from 'react-native-elevated-view'; const styles = StyleSheet.create({ - modalTypeView: { - position: 'absolute', - bottom: 0, - paddingBottom: Device.isIphoneX() ? 20 : 10, - left: 0, - right: 0, - backgroundColor: colors.transparent - }, modalTypeViewBrowser: { bottom: Device.isIphoneX() ? 70 : 60 }, notificationContainer: { - flex: 0.1, - flexDirection: 'row', - alignItems: 'flex-end' + position: 'absolute', + left: 0, + right: 0, + bottom: 0, + paddingBottom: Device.isIphoneX() ? 20 : 10 } }); function SimpleNotification({ isInBrowserView, notificationAnimated, hideCurrentNotification, currentNotification }) { return ( - - + + state.routes); const usePrevious = value => { const ref = useRef(); @@ -40,12 +44,7 @@ function Notification({ }).start(({ finished }) => finished && callback?.()); }, []); - const isInBrowserView = useMemo(() => { - const routes = navigation.state.routes; - let route = routes[routes.length - 1]; - while (route.index !== undefined) route = route.routes[route.index]; - return route?.routeName === BROWSER_ROUTE; - }, [navigation.state]); + const isInBrowserView = useMemo(() => findRouteNameFromNavigatorState(routes) === BROWSER_ROUTE, [routes]); useEffect( () => () => { diff --git a/app/components/UI/OnboardingWizard/Step5/index.js b/app/components/UI/OnboardingWizard/Step5/index.js index ab97f2faec9..318aeeb09bc 100644 --- a/app/components/UI/OnboardingWizard/Step5/index.js +++ b/app/components/UI/OnboardingWizard/Step5/index.js @@ -5,7 +5,7 @@ import { View, Text, StyleSheet, Dimensions } from 'react-native'; import { colors } from '../../../../styles/common'; import Coachmark from '../Coachmark'; import setOnboardingWizardStep from '../../../../actions/wizard'; -import { DrawerActions } from 'react-navigation-drawer'; // eslint-disable-line +import { DrawerActions } from '@react-navigation/native'; import { strings } from '../../../../../locales/i18n'; import onboardingStyles from './../styles'; import Device from '../../../../util/Device'; @@ -76,7 +76,10 @@ class Step5 extends PureComponent { const { navigation, setOnboardingWizardStep } = this.props; setOnboardingWizardStep && setOnboardingWizardStep(6); navigation && navigation.dispatch(DrawerActions.closeDrawer()); - navigation && navigation.navigate('BrowserView'); + navigation && + navigation.navigate('BrowserTabHome', { + screen: 'BrowserView' + }); }; /** diff --git a/app/components/UI/OnboardingWizard/index.js b/app/components/UI/OnboardingWizard/index.js index e26275849c0..0aeebb500fc 100644 --- a/app/components/UI/OnboardingWizard/index.js +++ b/app/components/UI/OnboardingWizard/index.js @@ -10,7 +10,7 @@ import Step4 from './Step4'; import Step5 from './Step5'; import Step6 from './Step6'; import setOnboardingWizardStep from '../../../actions/wizard'; -import { DrawerActions } from 'react-navigation-drawer'; // eslint-disable-line +import { DrawerActions } from '@react-navigation/native'; import { strings } from '../../../../locales/i18n'; import AsyncStorage from '@react-native-community/async-storage'; import ElevatedView from 'react-native-elevated-view'; diff --git a/app/components/UI/OptinMetrics/__snapshots__/index.test.js.snap b/app/components/UI/OptinMetrics/__snapshots__/index.test.js.snap index bf9e898d2bc..65d725b5c20 100644 --- a/app/components/UI/OptinMetrics/__snapshots__/index.test.js.snap +++ b/app/components/UI/OptinMetrics/__snapshots__/index.test.js.snap @@ -1,7 +1,8 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`OptinMetrics should render correctly 1`] = ` - - - -`; \ No newline at end of file + +`; diff --git a/app/components/UI/OptinMetrics/index.js b/app/components/UI/OptinMetrics/index.js index 7dbc8c01630..2476f800659 100644 --- a/app/components/UI/OptinMetrics/index.js +++ b/app/components/UI/OptinMetrics/index.js @@ -1,15 +1,5 @@ import React, { PureComponent } from 'react'; -import { - View, - SafeAreaView, - Text, - StyleSheet, - TouchableOpacity, - ScrollView, - BackHandler, - Alert, - InteractionManager -} from 'react-native'; +import { View, SafeAreaView, Text, StyleSheet, TouchableOpacity, ScrollView, BackHandler, Alert } from 'react-native'; import PropTypes from 'prop-types'; import { baseStyles, fontStyles, colors } from '../../../styles/common'; import AsyncStorage from '@react-native-community/async-storage'; @@ -18,7 +8,6 @@ import { getOptinMetricsNavbarOptions } from '../Navbar'; import { strings } from '../../../../locales/i18n'; import setOnboardingWizardStep from '../../../actions/wizard'; import { connect } from 'react-redux'; -import { withNavigationFocus } from 'react-navigation'; import StyledButton from '../StyledButton'; import Analytics from '../../../core/Analytics'; import { ANALYTICS_EVENT_OPTS } from '../../../util/analytics'; @@ -28,7 +17,8 @@ import AppConstants from '../../../core/AppConstants'; const styles = StyleSheet.create({ root: { - ...baseStyles.flexGrow + ...baseStyles.flexGrow, + backgroundColor: colors.white }, checkIcon: { color: colors.green500 @@ -106,10 +96,6 @@ class OptinMetrics extends PureComponent { * Action to set onboarding wizard step */ setOnboardingWizardStep: PropTypes.func, - /** - * React navigation prop to know if this view is focused - */ - isFocused: PropTypes.bool, /** * Onboarding events array created in previous onboarding views */ @@ -138,10 +124,7 @@ class OptinMetrics extends PureComponent { * Temporary disabling the back button so users can't go back */ handleBackPress = () => { - if (this.props.isFocused) { - Alert.alert(strings('onboarding.optin_back_title'), strings('onboarding.optin_back_desc')); - return true; - } + Alert.alert(strings('onboarding.optin_back_title'), strings('onboarding.optin_back_desc')); }; /** @@ -151,10 +134,10 @@ class OptinMetrics extends PureComponent { // Get onboarding wizard state const onboardingWizard = await AsyncStorage.getItem(ONBOARDING_WIZARD); if (onboardingWizard) { - this.props.navigation.navigate('HomeNav'); + this.props.navigation.reset({ routes: [{ name: 'HomeNav' }] }); } else { this.props.setOnboardingWizardStep(1); - this.props.navigation.navigate('WalletView'); + this.props.navigation.reset({ routes: [{ name: 'HomeNav' }] }); } }; @@ -179,7 +162,7 @@ class OptinMetrics extends PureComponent { * Callback on press cancel */ onCancel = async () => { - InteractionManager.runAfterInteractions(async () => { + setTimeout(async () => { if (this.props.events && this.props.events.length) { this.props.events.forEach(e => Analytics.trackEvent(e)); } @@ -187,7 +170,7 @@ class OptinMetrics extends PureComponent { this.props.clearOnboardingEvents(); await AsyncStorage.setItem(METRICS_OPT_IN, DENIED); Analytics.disableInstance(); - }); + }, 200); this.continue(); }; @@ -195,14 +178,14 @@ class OptinMetrics extends PureComponent { * Callback on press confirm */ onConfirm = async () => { - InteractionManager.runAfterInteractions(async () => { + setTimeout(async () => { if (this.props.events && this.props.events.length) { this.props.events.forEach(e => Analytics.trackEvent(e)); } Analytics.trackEvent(ANALYTICS_EVENT_OPTS.ONBOARDING_METRICS_OPT_IN); this.props.clearOnboardingEvents(); await AsyncStorage.setItem(METRICS_OPT_IN, AGREED); - }); + }, 200); this.continue(); }; @@ -211,8 +194,11 @@ class OptinMetrics extends PureComponent { */ onPressPolicy = () => { this.props.navigation.navigate('Webview', { - url: AppConstants.URLS.PRIVACY_POLICY, - title: strings('privacy_policy.title') + screen: 'SimpleWebview', + params: { + url: AppConstants.URLS.PRIVACY_POLICY, + title: strings('privacy_policy.title') + } }); }; @@ -279,4 +265,4 @@ const mapDispatchToProps = dispatch => ({ export default connect( mapStateToProps, mapDispatchToProps -)(withNavigationFocus(OptinMetrics)); +)(OptinMetrics); diff --git a/app/components/UI/OptinMetrics/index.test.js b/app/components/UI/OptinMetrics/index.test.js index 84f275d03b2..ef6bc99c680 100644 --- a/app/components/UI/OptinMetrics/index.test.js +++ b/app/components/UI/OptinMetrics/index.test.js @@ -16,6 +16,6 @@ describe('OptinMetrics', () => { const wrapper = shallow(, { context: { store: mockStore(initialState) } }); - expect(wrapper.dive()).toMatchSnapshot(); + expect(wrapper).toMatchSnapshot(); }); }); diff --git a/app/components/UI/PaymentRequest/index.js b/app/components/UI/PaymentRequest/index.js index e80ae8b75d7..40b5932d4fe 100644 --- a/app/components/UI/PaymentRequest/index.js +++ b/app/components/UI/PaymentRequest/index.js @@ -226,8 +226,8 @@ const MODE_AMOUNT = 'amount'; * View to generate a payment request link */ class PaymentRequest extends PureComponent { - static navigationOptions = ({ navigation }) => - getPaymentRequestOptionsTitle(strings('payment_request.title'), navigation); + static navigationOptions = ({ navigation, route }) => + getPaymentRequestOptionsTitle(strings('payment_request.title'), navigation, route); static propTypes = { /** @@ -265,7 +265,11 @@ class PaymentRequest extends PureComponent { /** * Current provider ticker */ - ticker: PropTypes.string + ticker: PropTypes.string, + /** + * Object that represents the current route info like params passed to it + */ + route: PropTypes.object }; amountInput = React.createRef(); @@ -288,8 +292,8 @@ class PaymentRequest extends PureComponent { * Set chainId, internalPrimaryCurrency and receiveAssets, if there is an asset set to this payment request chose it automatically, to state */ componentDidMount = () => { - const { primaryCurrency, navigation } = this.props; - const receiveAsset = navigation && navigation.getParam('receiveAsset', undefined); + const { primaryCurrency, route } = this.props; + const receiveAsset = route?.params?.receiveAsset; this.setState({ internalPrimaryCurrency: primaryCurrency, inputWidth: { width: '100%' } diff --git a/app/components/UI/PaymentRequestSuccess/index.js b/app/components/UI/PaymentRequestSuccess/index.js index 219f3395eb1..545de331d30 100644 --- a/app/components/UI/PaymentRequestSuccess/index.js +++ b/app/components/UI/PaymentRequestSuccess/index.js @@ -156,9 +156,9 @@ class PaymentRequestSuccess extends PureComponent { static propTypes = { /** - * Object that represents the navigator + * Object that represents the current route info like params passed to it */ - navigation: PropTypes.object, + route: PropTypes.object, /** /* Triggers global alert */ @@ -181,11 +181,11 @@ class PaymentRequestSuccess extends PureComponent { * Sets payment request link, amount and symbol of the asset to state */ componentDidMount = () => { - const { navigation } = this.props; - const link = navigation && navigation.getParam('link', ''); - const qrLink = navigation && navigation.getParam('qrLink', ''); - const amount = navigation && navigation.getParam('amount', ''); - const symbol = navigation && navigation.getParam('symbol', ''); + const { route } = this.props; + const link = route?.params?.link ?? ''; + const qrLink = route?.params?.qrLink ?? ''; + const amount = route?.params?.amount ?? ''; + const symbol = route?.params?.symbol ?? ''; this.setState({ link, qrLink, amount, symbol }); }; diff --git a/app/components/UI/ProtectYourWalletModal/index.js b/app/components/UI/ProtectYourWalletModal/index.js index 9e7c3874d47..1b02a796f5e 100644 --- a/app/components/UI/ProtectYourWalletModal/index.js +++ b/app/components/UI/ProtectYourWalletModal/index.js @@ -81,14 +81,20 @@ class ProtectYourWalletModal extends PureComponent { goToBackupFlow = () => { this.props.protectWalletModalNotVisible(); - this.props.navigation.navigate(this.props.passwordSet ? 'AccountBackupStep1' : 'SetPasswordFlow'); + this.props.navigation.navigate( + 'SetPasswordFlow', + this.props.passwordSet ? { screen: 'AccountBackupStep1' } : undefined + ); }; onLearnMore = () => { this.props.protectWalletModalNotVisible(); this.props.navigation.navigate('Webview', { - url: 'https://metamask.zendesk.com/hc/en-us/articles/360015489591-Basic-Safety-Tips', - title: strings('protect_wallet_modal.title') + screen: 'SimpleWebview', + params: { + url: 'https://metamask.zendesk.com/hc/en-us/articles/360015489591-Basic-Safety-Tips', + title: strings('protect_wallet_modal.title') + } }); }; diff --git a/app/components/UI/ReceiveRequest/index.js b/app/components/UI/ReceiveRequest/index.js index b5713d690e8..902b796773f 100644 --- a/app/components/UI/ReceiveRequest/index.js +++ b/app/components/UI/ReceiveRequest/index.js @@ -157,7 +157,7 @@ class ReceiveRequest extends PureComponent { Alert.alert(strings('fiat_on_ramp.network_not_supported'), strings('fiat_on_ramp.switch_network')); } else { toggleReceiveModal(); - navigation.navigate('PaymentMethodSelector'); + navigation.navigate('FiatOnRamp'); InteractionManager.runAfterInteractions(() => { Analytics.trackEvent(ANALYTICS_EVENT_OPTS.WALLET_BUY_ETH); }); @@ -199,7 +199,10 @@ class ReceiveRequest extends PureComponent { onReceive = () => { this.props.toggleReceiveModal(); - this.props.navigation.navigate('PaymentRequestView', { receiveAsset: this.props.receiveAsset }); + this.props.navigation.navigate('PaymentRequestView', { + screen: 'PaymentRequest', + params: { receiveAsset: this.props.receiveAsset } + }); InteractionManager.runAfterInteractions(() => { Analytics.trackEvent(ANALYTICS_EVENT_OPTS.RECEIVE_OPTIONS_PAYMENT_REQUEST); }); diff --git a/app/components/UI/ReusableModal/__snapshots__/index.test.js.snap b/app/components/UI/ReusableModal/__snapshots__/index.test.js.snap new file mode 100644 index 00000000000..1da55733932 --- /dev/null +++ b/app/components/UI/ReusableModal/__snapshots__/index.test.js.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ReusableModal should render correctly 1`] = ` + + + +`; diff --git a/app/components/UI/ReusableModal/index.test.js b/app/components/UI/ReusableModal/index.test.js new file mode 100644 index 00000000000..b7f6ef9b1c4 --- /dev/null +++ b/app/components/UI/ReusableModal/index.test.js @@ -0,0 +1,15 @@ +import React from 'react'; +import { SafeAreaView } from 'react-native'; +import { shallow } from 'enzyme'; +import ReusableModal from './'; + +describe('ReusableModal', () => { + it('should render correctly', () => { + const wrapper = shallow( + + + + ); + expect(wrapper).toMatchSnapshot(); + }); +}); diff --git a/app/components/UI/ReusableModal/index.tsx b/app/components/UI/ReusableModal/index.tsx new file mode 100644 index 00000000000..68a1999203c --- /dev/null +++ b/app/components/UI/ReusableModal/index.tsx @@ -0,0 +1,218 @@ +import React, { useEffect, ReactNode, forwardRef, useImperativeHandle, useMemo, useCallback, useRef } from 'react'; +import { View, TouchableOpacity, StyleSheet, ViewStyle, Dimensions, StyleProp } from 'react-native'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; +import { PanGestureHandler, State } from 'react-native-gesture-handler'; +import Animated, { + call, + eq, + Easing, + not, + block, + cond, + clockRunning, + Value, + interpolate, + useCode, + set +} from 'react-native-reanimated'; +import { onGestureEvent, withSpring, clamp, timing, spring } from 'react-native-redash'; +import { colors } from '../../../styles/common'; +const screenHeight = Dimensions.get('window').height; + +type DismissModal = () => void; + +type Actions = { + dismissModal: (callback?: DismissModal) => void; +}; + +type Props = { + ref?: React.Ref; + style?: StyleProp; + children?: ReactNode; + onDismiss?: DismissModal; +}; + +const ReusableModal = forwardRef((props, ref) => { + const { style, children, onDismiss } = props; + const topOffset = 0; + const bottomOffset = screenHeight; + const safeAreaInsets = useSafeAreaInsets(); + const trigger = useRef(); + + // Animation config + const animationConfig: Omit = { + damping: 100, + overshootClamping: false, + restSpeedThreshold: 5, + restDisplacementThreshold: 5, + stiffness: 800, + mass: 6 + }; + + // Animation is finished, process end state + const triggerDismissed = useCallback(() => { + onDismiss && onDismiss(); + trigger.current && trigger.current(); + }, [onDismiss]); + + // Set up gesture handler + const offset = useMemo(() => new Value(bottomOffset), []); + const state = useMemo(() => new Value(State.UNDETERMINED), []); + const velocityY = useMemo(() => new Value(0), []); + const translationY = useMemo(() => new Value(0), []); + const gestureHandler = useMemo(() => onGestureEvent({ translationY, state, velocityY }), []); + const clock = useMemo(() => new Animated.Clock(), []); + const translateY = useMemo( + () => + clamp( + withSpring({ + onSnap: val => { + const offset = val[0]; + if (offset == bottomOffset) { + // TODO: Use optional chaining once prettier is fixed + triggerDismissed(); + } + }, + state, + velocity: velocityY, + offset, + value: translationY, + snapPoints: [topOffset, bottomOffset], + config: animationConfig + }), + topOffset, + bottomOffset + ), + [bottomOffset, topOffset, translationY, velocityY, triggerDismissed] + ); + + // Programatically trigger hiding and showing + const triggerShowModal: Animated.Value<0 | 1> = useMemo(() => new Value(0), []); + const triggerDismissModal: Animated.Value<0 | 1> = useMemo(() => new Value(0), []); + + // Dismiss overlay + const dismissOverlay = useCallback(() => { + triggerDismissModal.setValue(1); + }, [triggerDismissModal]); + + // Define animated styles + const animatedStyles: StyleSheet.NamedStyles = useMemo(() => { + return { + overlayBackground: { + opacity: interpolate(translateY, { + inputRange: [topOffset, bottomOffset], + outputRange: [1, 0] + }) as any + }, + overlayBackgroundTouchable: { + ...StyleSheet.absoluteFillObject, + transform: [ + { + translateY: interpolate(translateY, { + inputRange: [0, 1], + outputRange: [0, bottomOffset] + }) as any + } + ] + }, + modal: { + transform: [{ translateY } as any], + // TODO: This could be used to handle universal safe area bottom padding + // paddingBottom: safeAreaInsets.bottom + flex: 1 + } + }; + }, [topOffset, bottomOffset, translateY, safeAreaInsets]); + + // Declarative logic that animates overlay + useCode( + () => + block([ + // Animate IN overlay + cond(eq(triggerShowModal, new Value(1)), [ + set( + offset, + spring({ + config: animationConfig, + clock, + from: offset, + to: topOffset + }) + ), + // Reset value that toggles animating in overlay + cond(not(clockRunning(clock)), block([set(triggerShowModal, 0)])) + ]), + // Animate OUT overlay + cond(eq(triggerDismissModal, new Value(1)), [ + set( + offset, + timing({ + clock, + from: offset, + easing: Easing.ease, + duration: 200, + to: bottomOffset + }) + ), + // Dismiss overlay after animating out + cond( + not(clockRunning(clock)), + block([call([], () => triggerDismissed()), set(triggerDismissModal, 0)]) + ) + ]) + ]), + [] + ); + + // Show modal + useEffect(() => { + triggerShowModal.setValue(1); + }, []); + + // Expose actions for external components + useImperativeHandle(ref, () => ({ + dismissModal: callback => { + trigger.current = callback; + dismissOverlay(); + } + })); + + const renderOverlay = useCallback(() => { + return ; + }, [animatedStyles]); + + const renderContent = useCallback(() => { + return ( + + + + + + {children} + + + ); + }, [gestureHandler, animatedStyles, style, children, dismissOverlay]); + + return ( + + {renderOverlay()} + {renderContent()} + + ); +}); + +const styles = StyleSheet.create({ + container: { + flex: 1 + }, + overlayBackground: { + backgroundColor: colors.overlay, + ...StyleSheet.absoluteFillObject + }, + fill: { + flex: 1 + } +}); + +export default ReusableModal; diff --git a/app/components/UI/SignatureRequest/index.js b/app/components/UI/SignatureRequest/index.js index c9b77ff7edc..e716cc78844 100644 --- a/app/components/UI/SignatureRequest/index.js +++ b/app/components/UI/SignatureRequest/index.js @@ -182,9 +182,12 @@ class SignatureRequest extends PureComponent { goToWarning = () => { this.props.onCancel(); - this.props.navigation.push('Webview', { - url: 'https://metamask.zendesk.com/hc/en-us/articles/360015488751', - title: 'metamask.zendesk.com' + this.props.navigation.navigate('Webview', { + screen: 'SimpleWebview', + params: { + url: 'https://metamask.zendesk.com/hc/en-us/articles/360015488751', + title: 'metamask.zendesk.com' + } }); }; diff --git a/app/components/UI/Swaps/QuotesView.js b/app/components/UI/Swaps/QuotesView.js index 5485acfad28..fa2fd570c3a 100644 --- a/app/components/UI/Swaps/QuotesView.js +++ b/app/components/UI/Swaps/QuotesView.js @@ -1,4 +1,4 @@ -import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import PropTypes from 'prop-types'; import { View, StyleSheet, ActivityIndicator, TouchableOpacity, InteractionManager, Linking } from 'react-native'; import { connect } from 'react-redux'; @@ -6,7 +6,7 @@ import IonicIcon from 'react-native-vector-icons/Ionicons'; import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'; import FAIcon from 'react-native-vector-icons/FontAwesome'; import BigNumber from 'bignumber.js'; -import { NavigationContext } from 'react-navigation'; +import { useNavigation, useRoute } from '@react-navigation/native'; import { swapsUtils } from '@metamask/swaps-controller'; import { WalletDevice, util } from '@metamask/controllers/'; @@ -286,11 +286,12 @@ function SwapsQuotesView({ quoteRefreshSeconds, usedGasPrice }) { - const navigation = useContext(NavigationContext); + const navigation = useNavigation(); + const route = useRoute(); /* Get params from navigation */ const { sourceTokenAddress, destinationTokenAddress, sourceAmount, slippage } = useMemo( - () => getQuotesNavigationsParams(navigation), - [navigation] + () => getQuotesNavigationsParams(route), + [route] ); /* Get tokens from the tokens list */ @@ -605,7 +606,7 @@ function SwapsQuotesView({ // send analytics } - navigation.dismiss(); + navigation.dangerouslyGetParent()?.pop(); }, [ chainId, navigation, @@ -835,7 +836,7 @@ function SwapsQuotesView({ }, [selectedQuote]); const buyEth = useCallback(() => { - navigation.navigate('PaymentMethodSelector'); + navigation.navigate('FiatOnRamp'); InteractionManager.runAfterInteractions(() => { Analytics.trackEvent(ANALYTICS_EVENT_OPTS.RECEIVE_OPTIONS_PAYMENT_REQUEST); }); @@ -844,7 +845,10 @@ function SwapsQuotesView({ const handleTermsPress = useCallback( () => navigation.navigate('Webview', { - url: AppConstants.URLS.TERMS_AND_CONDITIONS + screen: 'SimpleWebview', + params: { + url: AppConstants.URLS.TERMS_AND_CONDITIONS + } }), [navigation] ); @@ -1456,7 +1460,7 @@ function SwapsQuotesView({ ); } -SwapsQuotesView.navigationOptions = ({ navigation }) => getSwapsQuotesNavbar(navigation); +SwapsQuotesView.navigationOptions = ({ navigation, route }) => getSwapsQuotesNavbar(navigation, route); SwapsQuotesView.propTypes = { swapsTokens: PropTypes.arrayOf(PropTypes.object), diff --git a/app/components/UI/Swaps/components/InfoModal.js b/app/components/UI/Swaps/components/InfoModal.js index 78fb27c4aca..641a4c12a2c 100644 --- a/app/components/UI/Swaps/components/InfoModal.js +++ b/app/components/UI/Swaps/components/InfoModal.js @@ -1,7 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { StyleSheet, View, TouchableOpacity } from 'react-native'; -import { SafeAreaView } from 'react-navigation'; +import { StyleSheet, View, TouchableOpacity, SafeAreaView } from 'react-native'; import Modal from 'react-native-modal'; import IonicIcon from 'react-native-vector-icons/Ionicons'; diff --git a/app/components/UI/Swaps/components/Onboarding.js b/app/components/UI/Swaps/components/Onboarding.js index 06c84322d37..101df2ade4b 100644 --- a/app/components/UI/Swaps/components/Onboarding.js +++ b/app/components/UI/Swaps/components/Onboarding.js @@ -1,7 +1,7 @@ -import React, { useCallback, useContext } from 'react'; +import React, { useCallback } from 'react'; import PropTypes from 'prop-types'; import { View, StyleSheet, TouchableOpacity, Image, LayoutAnimation, Platform, UIManager } from 'react-native'; -import { NavigationContext } from 'react-navigation'; +import { useNavigation } from '@react-navigation/native'; import { strings } from '../../../../../locales/i18n'; import Device from '../../../../util/Device'; import Text from '../../../Base/Text'; @@ -56,7 +56,7 @@ if (Platform.OS === 'android' && UIManager.setLayoutAnimationEnabledExperimental } function Onboarding({ setHasOnboarded }) { - const navigation = useContext(NavigationContext); + const navigation = useNavigation(); const handleStartSwapping = useCallback(() => { LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut); setHasOnboarded(true); @@ -64,7 +64,10 @@ function Onboarding({ setHasOnboarded }) { const handleReviewAuditsPress = useCallback(() => { navigation.navigate('Webview', { - url: 'https://consensys.net/diligence/audits/2020/08/metaswap/' + screen: 'SimpleWebview', + params: { + url: 'https://consensys.net/diligence/audits/2020/08/metaswap/' + } }); }, [navigation]); diff --git a/app/components/UI/Swaps/components/QuotesModal.js b/app/components/UI/Swaps/components/QuotesModal.js index 514245d3497..5d5aa89af99 100644 --- a/app/components/UI/Swaps/components/QuotesModal.js +++ b/app/components/UI/Swaps/components/QuotesModal.js @@ -1,7 +1,15 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import PropTypes from 'prop-types'; -import { StyleSheet, View, ScrollView, TouchableOpacity, LayoutAnimation, UIManager, Platform } from 'react-native'; -import { SafeAreaView } from 'react-navigation'; +import { + StyleSheet, + View, + ScrollView, + TouchableOpacity, + LayoutAnimation, + UIManager, + Platform, + SafeAreaView +} from 'react-native'; import Modal from 'react-native-modal'; import IonicIcon from 'react-native-vector-icons/Ionicons'; import { connect } from 'react-redux'; diff --git a/app/components/UI/Swaps/index.js b/app/components/UI/Swaps/index.js index 80fe38e9c8a..85297466de6 100644 --- a/app/components/UI/Swaps/index.js +++ b/app/components/UI/Swaps/index.js @@ -1,8 +1,8 @@ -import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import PropTypes from 'prop-types'; import { ActivityIndicator, StyleSheet, View, TouchableOpacity, InteractionManager } from 'react-native'; import { connect } from 'react-redux'; -import { NavigationContext } from 'react-navigation'; +import { useNavigation, useRoute } from '@react-navigation/native'; import { View as AnimatableView } from 'react-native-animatable'; import IonicIcon from 'react-native-vector-icons/Ionicons'; import numberToBN from 'number-to-bn'; @@ -153,9 +153,11 @@ function SwapsAmountView({ setHasOnboarded, setLiveness }) { - const navigation = useContext(NavigationContext); + const navigation = useNavigation(); + const route = useRoute(); + const explorer = useBlockExplorer(provider, frequentRpcList); - const initialSource = navigation.getParam('sourceToken', SWAPS_NATIVE_ADDRESS); + const initialSource = route.params?.sourceToken ?? SWAPS_NATIVE_ADDRESS; const [amount, setAmount] = useState('0'); const [slippage, setSlippage] = useState(AppConstants.SWAPS.DEFAULT_SLIPPAGE); const [isInitialLoadingTokens, setInitialLoadingTokens] = useState(false); @@ -412,8 +414,11 @@ function SwapsAmountView({ } hideTokenVerificationModal(); navigation.navigate('Webview', { - url: explorer.token(destinationToken.address), - title: strings('swaps.verify') + screen: 'SimpleWebview', + params: { + url: explorer.token(destinationToken.address), + title: strings('swaps.verify') + } }); }, [explorer, destinationToken, hideTokenVerificationModal, navigation]); @@ -684,7 +689,7 @@ function SwapsAmountView({ ); } -SwapsAmountView.navigationOptions = ({ navigation }) => getSwapsAmountNavbar(navigation); +SwapsAmountView.navigationOptions = ({ navigation, route }) => getSwapsAmountNavbar(navigation, route); SwapsAmountView.propTypes = { swapsTokens: PropTypes.arrayOf(PropTypes.object), diff --git a/app/components/UI/Swaps/utils/index.js b/app/components/UI/Swaps/utils/index.js index d247021c47c..cd744d0f6fe 100644 --- a/app/components/UI/Swaps/utils/index.js +++ b/app/components/UI/Swaps/utils/index.js @@ -50,14 +50,13 @@ export function setQuotesNavigationsParams(sourceTokenAddress, destinationTokenA /** * Gets required parameters for Swaps Quotes View - * @param {object} navigation React-navigation's navigation prop * @return {object} Object containing sourceTokenAddress, destinationTokenAddress, sourceAmount and slippage */ -export function getQuotesNavigationsParams(navigation) { - const slippage = navigation.getParam('slippage', 1); - const sourceTokenAddress = navigation.getParam('sourceTokenAddress', ''); - const destinationTokenAddress = navigation.getParam('destinationTokenAddress', ''); - const sourceAmount = navigation.getParam('sourceAmount'); +export function getQuotesNavigationsParams(route) { + const slippage = route.params?.slippage ?? 1; + const sourceTokenAddress = route.params?.sourceTokenAddress ?? ''; + const destinationTokenAddress = route.params?.destinationTokenAddress ?? ''; + const sourceAmount = route.params?.sourceAmount; return { sourceTokenAddress, diff --git a/app/components/UI/Tokens/index.js b/app/components/UI/Tokens/index.js index 7903889d0e5..2a68f4c8e7e 100644 --- a/app/components/UI/Tokens/index.js +++ b/app/components/UI/Tokens/index.js @@ -229,7 +229,7 @@ class Tokens extends PureComponent { }; goToBuy = () => { - this.props.navigation.navigate('PaymentMethodSelector'); + this.props.navigation.navigate('FiatOnRamp'); InteractionManager.runAfterInteractions(() => { Analytics.trackEvent(ANALYTICS_EVENT_OPTS.WALLET_BUY_ETH); }); diff --git a/app/components/UI/TransactionEditor/index.test.js b/app/components/UI/TransactionEditor/index.test.js index 7cd96248ac5..f9ec45fb320 100644 --- a/app/components/UI/TransactionEditor/index.test.js +++ b/app/components/UI/TransactionEditor/index.test.js @@ -40,7 +40,7 @@ describe('TransactionEditor', () => { const wrapper = shallow( , { diff --git a/app/components/UI/TransactionElement/TransactionDetails/index.js b/app/components/UI/TransactionElement/TransactionDetails/index.js index 4a704a823b7..e0b151f4952 100644 --- a/app/components/UI/TransactionElement/TransactionDetails/index.js +++ b/app/components/UI/TransactionElement/TransactionDetails/index.js @@ -21,7 +21,7 @@ import StatusText from '../../../Base/StatusText'; import Text from '../../../Base/Text'; import DetailsModal from '../../../Base/DetailsModal'; import { RPC } from '../../../../constants/network'; -import { withNavigation } from 'react-navigation'; +import { withNavigation } from '@react-navigation/compat'; const styles = StyleSheet.create({ viewOnEtherscan: { @@ -133,16 +133,19 @@ class TransactionDetails extends PureComponent { const url = `${rpcBlockExplorer}/tx/${transactionHash}`; const title = new URL(rpcBlockExplorer).hostname; navigation.push('Webview', { - url, - title + screen: 'SimpleWebview', + params: { url, title } }); } else { const network = getNetworkTypeById(networkID); const url = getEtherscanTransactionUrl(network, transactionHash); const etherscan_url = getEtherscanBaseUrl(network).replace('https://', ''); navigation.push('Webview', { - url, - title: etherscan_url + screen: 'SimpleWebview', + params: { + url, + title: etherscan_url + } }); } close && close(); diff --git a/app/components/UI/TransactionReview/TransactionReviewInformation/__snapshots__/index.test.js.snap b/app/components/UI/TransactionReview/TransactionReviewInformation/__snapshots__/index.test.js.snap index 85bf3f18c3e..97d284eb011 100644 --- a/app/components/UI/TransactionReview/TransactionReviewInformation/__snapshots__/index.test.js.snap +++ b/app/components/UI/TransactionReview/TransactionReviewInformation/__snapshots__/index.test.js.snap @@ -1,7 +1,44 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`TransactionReviewInformation should render correctly 1`] = ` - - - + + + + + + View Data + + + + `; diff --git a/app/components/UI/TransactionReview/TransactionReviewInformation/index.js b/app/components/UI/TransactionReview/TransactionReviewInformation/index.js index 7f8e1f7f948..67b1e832b07 100644 --- a/app/components/UI/TransactionReview/TransactionReviewInformation/index.js +++ b/app/components/UI/TransactionReview/TransactionReviewInformation/index.js @@ -18,7 +18,6 @@ import { getTicker, getNormalizedTxState } from '../../../../util/transactions'; import TransactionReviewFeeCard from '../TransactionReviewFeeCard'; import Analytics from '../../../../core/Analytics'; import { ANALYTICS_EVENT_OPTS } from '../../../../util/analytics'; -import { withNavigation } from 'react-navigation'; import { getNetworkName, getNetworkNonce, isMainNet } from '../../../../util/networks'; import { capitalize } from '../../../../util/general'; import CustomNonceModal from '../../../UI/CustomNonceModal'; @@ -247,7 +246,7 @@ class TransactionReviewInformation extends PureComponent { const { navigation } = this.props; /* this is kinda weird, we have to reject the transaction to collapse the modal */ this.onCancelPress(); - navigation.navigate('PaymentMethodSelector'); + navigation.navigate('FiatOnRamp'); InteractionManager.runAfterInteractions(() => { Analytics.trackEvent(ANALYTICS_EVENT_OPTS.RECEIVE_OPTIONS_PAYMENT_REQUEST); }); @@ -448,4 +447,4 @@ const mapDispatchToProps = dispatch => ({ export default connect( mapStateToProps, mapDispatchToProps -)(withNavigation(TransactionReviewInformation)); +)(TransactionReviewInformation); diff --git a/app/components/UI/TransactionReview/TransactionReviewSummary/index.test.js b/app/components/UI/TransactionReview/TransactionReviewSummary/index.test.js index aec7ab7ec65..6ea6991b84b 100644 --- a/app/components/UI/TransactionReview/TransactionReviewSummary/index.test.js +++ b/app/components/UI/TransactionReview/TransactionReviewSummary/index.test.js @@ -44,7 +44,7 @@ describe('TransactionReviewSummary', () => { const wrapper = shallow( , { diff --git a/app/components/UI/TransactionReview/__snapshots__/index.test.js.snap b/app/components/UI/TransactionReview/__snapshots__/index.test.js.snap index 50ef6196360..90e8632b534 100644 --- a/app/components/UI/TransactionReview/__snapshots__/index.test.js.snap +++ b/app/components/UI/TransactionReview/__snapshots__/index.test.js.snap @@ -49,7 +49,7 @@ exports[`TransactionReview should render correctly 1`] = ` > - diff --git a/app/components/UI/TransactionReview/index.js b/app/components/UI/TransactionReview/index.js index e8c7520ee74..067d9e1aaf5 100644 --- a/app/components/UI/TransactionReview/index.js +++ b/app/components/UI/TransactionReview/index.js @@ -159,7 +159,11 @@ class TransactionReview extends PureComponent { /** * True if transaction is over the available funds */ - over: PropTypes.bool + over: PropTypes.bool, + /** + * Object that represents the navigator + */ + navigation: PropTypes.object }; state = { @@ -294,7 +298,8 @@ class TransactionReview extends PureComponent { hideData, saveTransactionReviewDataHeight, customGasHeight, - over + over, + navigation } = this.props; const { actionKey, error, assetAmount, conversionRate, fiatValue, approveTransaction } = this.state; const currentPageInformation = { url: this.getUrlFromBrowser() }; @@ -324,6 +329,7 @@ class TransactionReview extends PureComponent { { const wrapper = shallow( , diff --git a/app/components/UI/Transactions/index.js b/app/components/UI/Transactions/index.js index 4597f431f0f..f1ecc4c5807 100644 --- a/app/components/UI/Transactions/index.js +++ b/app/components/UI/Transactions/index.js @@ -219,7 +219,10 @@ class Transactions extends PureComponent { ); renderEmpty = () => ( - }> + } + > {this.props.header ? this.props.header : null} {strings('wallet.no_transactions')} diff --git a/app/components/UI/WhatsNewModal/__snapshots__/index.test.js.snap b/app/components/UI/WhatsNewModal/__snapshots__/index.test.js.snap index 2ae9617eab7..731b33b8a0e 100644 --- a/app/components/UI/WhatsNewModal/__snapshots__/index.test.js.snap +++ b/app/components/UI/WhatsNewModal/__snapshots__/index.test.js.snap @@ -1,90 +1,34 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`WhatsNewModal should render correctly 1`] = ` - - - - - - - See what's new - - - - - - - - - - + + + + + `; diff --git a/app/components/UI/WhatsNewModal/index.js b/app/components/UI/WhatsNewModal/index.js index 05d42a783ed..29ec32097db 100644 --- a/app/components/UI/WhatsNewModal/index.js +++ b/app/components/UI/WhatsNewModal/index.js @@ -9,6 +9,7 @@ import { Image, InteractionManager } from 'react-native'; +import { useNavigationState } from '@react-navigation/native'; import ActionModal from '../ActionModal'; import { colors, fontStyles } from '../../../styles/common'; import Icon from 'react-native-vector-icons/FontAwesome'; @@ -20,6 +21,7 @@ import { CURRENT_APP_VERSION, LAST_APP_VERSION, WHATS_NEW_APP_VERSION_SEEN } fro import compareVersions from 'compare-versions'; import scaling from '../../../util/scaling'; import PropTypes from 'prop-types'; +import { findRouteNameFromNavigatorState } from '../../../util/general'; const styles = StyleSheet.create({ wrapper: { @@ -100,6 +102,7 @@ const styles = StyleSheet.create({ const WhatsNewModal = props => { const [featuresToShow, setFeaturesToShow] = useState(null); const [show, setShow] = useState(false); + const routes = useNavigationState(state => state.routes); useEffect(() => { const shouldShow = async () => { @@ -150,17 +153,9 @@ const WhatsNewModal = props => { feature.buttonPress && feature.buttonPress(props); }; - const findRouteNameFromNavigatorState = ({ routes }) => { - let route = routes[routes.length - 1]; - while (route.index !== undefined) { - route = route.routes[route.index]; - } - return route?.routeName; - }; - useEffect(() => { if (props.enabled && !!featuresToShow) { - const route = findRouteNameFromNavigatorState(props.navigation.state); + const route = findRouteNameFromNavigatorState(routes); if (route === 'WalletView') { InteractionManager.runAfterInteractions(() => { setShow(true); @@ -169,7 +164,7 @@ const WhatsNewModal = props => { } else { setShow(false); } - }, [featuresToShow, props.enabled, props.navigation.state]); + }, [featuresToShow, props.enabled, routes]); return ( { }; WhatsNewModal.propTypes = { - /** - * navigation object required to push new views - */ - navigation: PropTypes.object, /** * Showing the modal is enabled */ diff --git a/app/components/UI/WhatsNewModal/index.test.js b/app/components/UI/WhatsNewModal/index.test.js index 631b8ee6518..10376d1c503 100644 --- a/app/components/UI/WhatsNewModal/index.test.js +++ b/app/components/UI/WhatsNewModal/index.test.js @@ -1,10 +1,15 @@ import React from 'react'; import { shallow } from 'enzyme'; import WhatsNewModal from './'; +import { NavigationContainer } from '@react-navigation/native'; describe('WhatsNewModal', () => { it('should render correctly', () => { - const wrapper = shallow(); + const wrapper = shallow( + + + + ); expect(wrapper).toMatchSnapshot(); }); }); diff --git a/app/components/UI/WhatsNewModal/whatsNewList.js b/app/components/UI/WhatsNewModal/whatsNewList.js index fc8ee118ed1..5066e1c381f 100644 --- a/app/components/UI/WhatsNewModal/whatsNewList.js +++ b/app/components/UI/WhatsNewModal/whatsNewList.js @@ -11,7 +11,7 @@ export const whatsNew = [ title: strings('whats_new.feature_security_settings_title'), text: strings('whats_new.feature_security_settings_text'), buttonText: strings('whats_new.feature_security_settings_button'), - buttonPress: props => props.navigation.navigate('SecuritySettings'), + buttonPress: props => props.navigation.navigate('SettingsView', { screen: 'SecuritySettings' }), image: require('../../../images/whats-new-security.png') } ] diff --git a/app/components/Views/AccountBackupStep1/__snapshots__/index.test.js.snap b/app/components/Views/AccountBackupStep1/__snapshots__/index.test.js.snap index d034cf424fb..fab7eff81f4 100644 --- a/app/components/Views/AccountBackupStep1/__snapshots__/index.test.js.snap +++ b/app/components/Views/AccountBackupStep1/__snapshots__/index.test.js.snap @@ -1,240 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`AccountBackupStep1 should render correctly 1`] = ` - - - - - - - Secure your wallet - - - - - Don’t risk losing your funds. Protect your wallet by saving your - - - Secret Recovery Phrase - - - in a place you trust. - - - It’s the only way to recover your wallet if you get locked out of the app or get a new device. - - - - - - - - - Remind me later - - - - (Not recommended) - - - - - Start - - - Highly recommended - - - - - - - - + `; diff --git a/app/components/Views/AccountBackupStep1/index.js b/app/components/Views/AccountBackupStep1/index.js index 2979c1ac39a..c59a8c269cd 100644 --- a/app/components/Views/AccountBackupStep1/index.js +++ b/app/components/Views/AccountBackupStep1/index.js @@ -16,7 +16,8 @@ import { ONBOARDING_WIZARD, METRICS_OPT_IN } from '../../../constants/storage'; import { CHOOSE_PASSWORD_STEPS } from '../../../constants/onboarding'; import SkipAccountSecurityModal from '../../UI/SkipAccountSecurityModal'; import SeedPhraseVideo from '../../UI/SeedPhraseVideo'; - +import { connect } from 'react-redux'; +import setOnboardingWizardStep from '../../../actions/wizard'; const styles = StyleSheet.create({ mainWrapper: { backgroundColor: colors.white, @@ -128,7 +129,7 @@ const AccountBackupStep1 = props => { ); const goNext = () => { - props.navigation.navigate('AccountBackupStep1B', { ...props.navigation.state.params }); + props.navigation.navigate('AccountBackupStep1B', { ...props.route.params }); }; const showRemindLater = () => { @@ -155,14 +156,14 @@ const AccountBackupStep1 = props => { const onboardingWizard = await AsyncStorage.getItem(ONBOARDING_WIZARD); // Check if user passed through metrics opt-in screen const metricsOptIn = await AsyncStorage.getItem(METRICS_OPT_IN); + if (!metricsOptIn) { props.navigation.navigate('OptinMetrics'); } else if (onboardingWizard) { - props.navigation.navigate('HomeNav'); - props.navigation.popToTop(); - props.navigation.goBack(null); + props.navigation.reset({ routes: [{ name: 'HomeNav' }] }); } else { - props.navigation.navigate('HomeNav'); + props.setOnboardingWizardStep(1); + props.navigation.reset({ routes: [{ name: 'HomeNav' }] }); } }; @@ -246,12 +247,28 @@ AccountBackupStep1.propTypes = { /** /* navigation object required to push and pop other views */ - navigation: PropTypes.object + navigation: PropTypes.object, + /** + * Object that represents the current route info like params passed to it + */ + route: PropTypes.object, + /** + * Action to set onboarding wizard step + */ + setOnboardingWizardStep: PropTypes.func }; -AccountBackupStep1.navigationOptions = ({ navigation }) => ({ - ...getOnboardingNavbarOptions(navigation, { headerLeft: }), +AccountBackupStep1.navigationOptions = ({ navigation, route }) => ({ + // eslint-disable-next-line react/display-name + ...getOnboardingNavbarOptions(navigation, route, { headerLeft: () => }), gesturesEnabled: false }); -export default AccountBackupStep1; +const mapDispatchToProps = dispatch => ({ + setOnboardingWizardStep: step => dispatch(setOnboardingWizardStep(step)) +}); + +export default connect( + null, + mapDispatchToProps +)(AccountBackupStep1); diff --git a/app/components/Views/AccountBackupStep1/index.test.js b/app/components/Views/AccountBackupStep1/index.test.js index 2cf8149148d..b904991c5ab 100644 --- a/app/components/Views/AccountBackupStep1/index.test.js +++ b/app/components/Views/AccountBackupStep1/index.test.js @@ -1,10 +1,27 @@ import React from 'react'; import { shallow } from 'enzyme'; import AccountBackupStep1 from './'; +import configureMockStore from 'redux-mock-store'; + +const mockStore = configureMockStore(); describe('AccountBackupStep1', () => { it('should render correctly', () => { - const wrapper = shallow(); + const initialState = { + engine: { + backgroundState: { + NetworkController: { + provider: { + chainId: '1' + } + } + } + } + }; + + const wrapper = shallow(, { + context: { store: mockStore(initialState) } + }); expect(wrapper).toMatchSnapshot(); }); }); diff --git a/app/components/Views/AccountBackupStep1B/index.js b/app/components/Views/AccountBackupStep1B/index.js index 478ca7d5db6..40ea154cef1 100644 --- a/app/components/Views/AccountBackupStep1B/index.js +++ b/app/components/Views/AccountBackupStep1B/index.js @@ -195,14 +195,17 @@ const AccountBackupStep1B = props => { const [showWhatIsSeedphraseModal, setWhatIsSeedphraseModal] = useState(false); const goNext = () => { - props.navigation.navigate('ManualBackupStep1', { ...props.navigation.state.params }); + props.navigation.navigate('ManualBackupStep1', { ...props.route.params }); }; const learnMore = () => { setWhySecureWalletModal(false); props.navigation.navigate('Webview', { - url: 'https://metamask.zendesk.com/hc/en-us/articles/360015489591-Basic-Safety-Tips', - title: strings('drawer.metamask_support') + screen: 'SimpleWebview', + params: { + url: 'https://metamask.zendesk.com/hc/en-us/articles/360015489591-Basic-Safety-Tips', + title: strings('drawer.metamask_support') + } }); }; @@ -324,9 +327,13 @@ AccountBackupStep1B.propTypes = { /** /* navigation object required to push and pop other views */ - navigation: PropTypes.object + navigation: PropTypes.object, + /** + * Object that represents the current route info like params passed to it + */ + route: PropTypes.object }; -AccountBackupStep1B.navigationOptions = ({ navigation }) => getOnboardingNavbarOptions(navigation); +AccountBackupStep1B.navigationOptions = ({ navigation, route }) => getOnboardingNavbarOptions(navigation, route); export default AccountBackupStep1B; diff --git a/app/components/Views/ActivityView/index.js b/app/components/Views/ActivityView/index.js index 2ec8536cefb..01116bce4c4 100644 --- a/app/components/Views/ActivityView/index.js +++ b/app/components/Views/ActivityView/index.js @@ -1,9 +1,9 @@ -import React, { useEffect, useContext } from 'react'; +import React, { useEffect } from 'react'; import PropTypes from 'prop-types'; import { View, StyleSheet } from 'react-native'; import ScrollableTabView from 'react-native-scrollable-tab-view'; import { connect } from 'react-redux'; -import { NavigationContext } from 'react-navigation'; +import { useNavigation } from '@react-navigation/native'; import { getHasOrders } from '../../../reducers/fiatOrders'; import getNavbarOptions from '../../UI/Navbar'; @@ -20,7 +20,7 @@ const styles = StyleSheet.create({ }); function ActivityView({ hasOrders, ...props }) { - const navigation = useContext(NavigationContext); + const navigation = useNavigation(); useEffect( () => { @@ -54,8 +54,8 @@ ActivityView.propTypes = { hasOrders: PropTypes.bool }; -ActivityView.navigationOptions = ({ navigation }) => { - const title = navigation.getParam('hasOrders', false) ? 'activity_view.title' : 'transactions_view.title'; +ActivityView.navigationOptions = ({ navigation, route }) => { + const title = route.params?.hasOrders ?? false ? 'activity_view.title' : 'transactions_view.title'; return getNavbarOptions(title, navigation); }; diff --git a/app/components/Views/AddAsset/__snapshots__/index.test.js.snap b/app/components/Views/AddAsset/__snapshots__/index.test.js.snap index 9d7f1615d13..ac4220cf1a0 100644 --- a/app/components/Views/AddAsset/__snapshots__/index.test.js.snap +++ b/app/components/Views/AddAsset/__snapshots__/index.test.js.snap @@ -23,28 +23,10 @@ exports[`AddAsset should render correctly 1`] = ` tabBarPosition="top" > diff --git a/app/components/Views/AddAsset/index.js b/app/components/Views/AddAsset/index.js index f39013be969..0cced882ff3 100644 --- a/app/components/Views/AddAsset/index.js +++ b/app/components/Views/AddAsset/index.js @@ -35,9 +35,9 @@ const styles = StyleSheet.create({ * PureComponent that provides ability to add assets. */ class AddAsset extends PureComponent { - static navigationOptions = ({ navigation }) => + static navigationOptions = ({ navigation, route }) => getNetworkNavbarOptions( - `add_asset.${navigation.state.params.assetType === 'token' ? 'title' : 'title_nft'}`, + `add_asset.${route.params.assetType === 'token' ? 'title' : 'title_nft'}`, true, navigation ); @@ -56,7 +56,11 @@ class AddAsset extends PureComponent { /** * Chain id */ - chainId: PropTypes.string + chainId: PropTypes.string, + /** + * Object that represents the current route info like params passed to it + */ + route: PropTypes.object }; renderTabBar() { @@ -74,10 +78,8 @@ class AddAsset extends PureComponent { render = () => { const { - navigation: { - state: { - params: { assetType, collectibleContract } - } + route: { + params: { assetType, collectibleContract } }, navigation } = this.props; diff --git a/app/components/Views/AddAsset/index.test.js b/app/components/Views/AddAsset/index.test.js index a3e33019bdd..2ee014fe7c2 100644 --- a/app/components/Views/AddAsset/index.test.js +++ b/app/components/Views/AddAsset/index.test.js @@ -19,7 +19,7 @@ describe('AddAsset', () => { } }; - const wrapper = shallow(, { + const wrapper = shallow(, { context: { store: mockStore(initialState) } }); expect(wrapper.dive()).toMatchSnapshot(); diff --git a/app/components/Views/AddBookmark/__snapshots__/index.test.js.snap b/app/components/Views/AddBookmark/__snapshots__/index.test.js.snap index 6949749d8f4..4df11ea295e 100644 --- a/app/components/Views/AddBookmark/__snapshots__/index.test.js.snap +++ b/app/components/Views/AddBookmark/__snapshots__/index.test.js.snap @@ -57,7 +57,7 @@ exports[`AddBookmark should render correctly 1`] = ` } } testID="add-bookmark-title" - value={null} + value="" /> { const { title, url } = this.state; if (title === '' || url === '') return false; - this.props.navigation.state.params.onAddBookmark({ name: title, url }); + this.props.route.params.onAddBookmark({ name: title, url }); this.props.navigation.pop(); }; diff --git a/app/components/Views/AddBookmark/index.test.js b/app/components/Views/AddBookmark/index.test.js index 0ae91df5920..37666fe4780 100644 --- a/app/components/Views/AddBookmark/index.test.js +++ b/app/components/Views/AddBookmark/index.test.js @@ -4,7 +4,7 @@ import AddBookmark from './'; describe('AddBookmark', () => { it('should render correctly', () => { - const wrapper = shallow( null, state: { params: {} } }} />); + const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); }); }); diff --git a/app/components/Views/Approval/index.js b/app/components/Views/Approval/index.js index a72d4d62c4e..4d8943f8c7e 100644 --- a/app/components/Views/Approval/index.js +++ b/app/components/Views/Approval/index.js @@ -33,7 +33,8 @@ const styles = StyleSheet.create({ * PureComponent that manages transaction approval from the dapp browser */ class Approval extends PureComponent { - static navigationOptions = ({ navigation }) => getTransactionOptionsTitle('approval.title', navigation); + static navigationOptions = ({ navigation, route }) => + getTransactionOptionsTitle('approval.title', navigation, route); static propTypes = { /** diff --git a/app/components/Views/Asset/index.js b/app/components/Views/Asset/index.js index 4bf712c8bc3..f0c82ae4719 100644 --- a/app/components/Views/Asset/index.js +++ b/app/components/Views/Asset/index.js @@ -73,7 +73,11 @@ class Asset extends PureComponent { * Indicates whether third party API mode is enabled */ thirdPartyApiMode: PropTypes.bool, - swapsTransactions: PropTypes.object + swapsTransactions: PropTypes.object, + /** + * Object that represents the current route info like params passed to it + */ + route: PropTypes.object }; state = { @@ -93,16 +97,16 @@ class Asset extends PureComponent { navSymbol = undefined; navAddress = undefined; - static navigationOptions = ({ navigation }) => - getNetworkNavbarOptions(navigation.getParam('symbol', ''), false, navigation); + static navigationOptions = ({ navigation, route }) => + getNetworkNavbarOptions(route.params?.symbol ?? '', false, navigation); componentDidMount() { InteractionManager.runAfterInteractions(() => { this.normalizeTransactions(); this.mounted = true; }); - this.navSymbol = this.props.navigation.getParam('symbol', '').toLowerCase(); - this.navAddress = this.props.navigation.getParam('address', '').toLowerCase(); + this.navSymbol = (this.props.route.params?.symbol ?? '').toLowerCase(); + this.navAddress = (this.props.route.params?.address ?? '').toLowerCase(); if (this.navSymbol.toUpperCase() !== 'ETH' && this.navAddress !== '') { this.filter = this.noEthFilter; } else { @@ -268,9 +272,7 @@ class Asset extends PureComponent { render = () => { const { loading, transactions, submittedTxs, confirmedTxs, transactionsUpdated } = this.state; const { - navigation: { - state: { params } - }, + route: { params }, navigation, conversionRate, currentCurrency, diff --git a/app/components/Views/Browser/index.js b/app/components/Views/Browser/index.js index 4d7b4aa02fe..f7dca0e337e 100644 --- a/app/components/Views/Browser/index.js +++ b/app/components/Views/Browser/index.js @@ -53,9 +53,13 @@ class Browser extends PureComponent { /** * ID of the active tab */ - activeTab: PropTypes.number + activeTab: PropTypes.number, + /** + * Object that represents the current route info like params passed to it + */ + route: PropTypes.object }; - static navigationOptions = ({ navigation }) => getBrowserViewNavbarOptions(navigation); + static navigationOptions = ({ navigation, route }) => getBrowserViewNavbarOptions(navigation, route); constructor(props) { super(props); @@ -72,17 +76,17 @@ class Browser extends PureComponent { this.props.tabs.length > 0 && this.switchToTab(this.props.tabs[0]); } - const currentUrl = this.props.navigation.getParam('newTabUrl', null); + const currentUrl = this.props.route.params?.newTabUrl; if (currentUrl) this.goToNewTab(currentUrl); } componentDidUpdate(prevProps) { - const prevNavigation = prevProps.navigation; - const { navigation } = this.props; + const prevRoute = prevProps.route; + const { route } = this.props; - if (prevNavigation && navigation) { - const prevUrl = prevNavigation.getParam('newTabUrl', null); - const currentUrl = navigation.getParam('newTabUrl', null); + if (prevRoute && route) { + const prevUrl = prevRoute.params?.newTabUrl; + const currentUrl = route.params?.newTabUrl; if (currentUrl && prevUrl !== currentUrl) { this.goToNewTab(currentUrl); @@ -93,7 +97,7 @@ class Browser extends PureComponent { goToNewTab = url => { this.newTab(url); this.props.navigation.setParams({ - ...this.props.navigation.state.params, + ...this.props.route.params, newTabUrl: null }); }; @@ -107,14 +111,14 @@ class Browser extends PureComponent { } this.props.navigation.setParams({ - ...this.props.navigation.state.params, + ...this.props.route.params, showTabs: true }); }; hideTabsAndUpdateUrl = url => { this.props.navigation.setParams({ - ...this.props.navigation.state.params, + ...this.props.route.params, showTabs: false, url, silent: false @@ -125,7 +129,7 @@ class Browser extends PureComponent { if (this.props.tabs.length) { this.props.closeAllTabs(); this.props.navigation.setParams({ - ...this.props.navigation.state.params, + ...this.props.route.params, url: null, silent: true }); @@ -156,7 +160,7 @@ class Browser extends PureComponent { } this.props.setActiveTab(newTab.id); this.props.navigation.setParams({ - ...this.props.navigation.state.params, + ...this.props.route.params, url: newTab.url, silent: true }); @@ -164,7 +168,7 @@ class Browser extends PureComponent { }); } else { this.props.navigation.setParams({ - ...this.props.navigation.state.params, + ...this.props.route.params, url: null, silent: true }); @@ -177,7 +181,7 @@ class Browser extends PureComponent { closeTabsView = () => { if (this.props.tabs.length) { this.props.navigation.setParams({ - ...this.props.navigation.state.params, + ...this.props.route.params, showTabs: false, silent: true }); @@ -192,7 +196,7 @@ class Browser extends PureComponent { renderTabsView() { const { tabs, activeTab } = this.props; - const showTabs = this.props.navigation.getParam('showTabs', false); + const showTabs = this.props.route.params?.showTabs; if (showTabs) { return ( { wallet_scanQRCode: () => new Promise((resolve, reject) => { - this.props.navigation.navigate('QRScanner', { + props.navigation.navigate('QRScanner', { onScanSuccess: data => { const regex = new RegExp(req.params[0]); if (regex && !regex.exec(data)) { @@ -1458,24 +1458,27 @@ export const BrowserTab = props => { const addBookmark = () => { toggleOptionsIfNeeded(); props.navigation.push('AddBookmarkView', { - title: title.current || '', - url: getMaskedUrl(url.current), - onAddBookmark: async ({ name, url }) => { - props.addBookmark({ name, url }); - if (Device.isIos()) { - const item = { - uniqueIdentifier: url, - title: name || getMaskedUrl(url), - contentDescription: `Launch ${name || url} on MetaMask`, - keywords: [name.split(' '), url, 'dapp'], - thumbnail: { - uri: icon.current || `https://api.faviconkit.com/${getHost(url)}/256` + screen: 'AddBookmark', + params: { + title: title.current || '', + url: getMaskedUrl(url.current), + onAddBookmark: async ({ name, url }) => { + props.addBookmark({ name, url }); + if (Device.isIos()) { + const item = { + uniqueIdentifier: url, + title: name || getMaskedUrl(url), + contentDescription: `Launch ${name || url} on MetaMask`, + keywords: [name.split(' '), url, 'dapp'], + thumbnail: { + uri: icon.current || `https://api.faviconkit.com/${getHost(url)}/256` + } + }; + try { + SearchApi.indexSpotlightItem(item); + } catch (e) { + Logger.error(e, 'Error adding to spotlight'); } - }; - try { - SearchApi.indexSpotlightItem(item); - } catch (e) { - Logger.error(e, 'Error adding to spotlight'); } } } diff --git a/app/components/Views/ChoosePassword/index.js b/app/components/Views/ChoosePassword/index.js index 731396c1a28..7361cbf90b6 100644 --- a/app/components/Views/ChoosePassword/index.js +++ b/app/components/Views/ChoosePassword/index.js @@ -199,7 +199,7 @@ const PASSCODE_NOT_SET_ERROR = 'Error: Passcode not set.'; * View where users can set their password for the first time */ class ChoosePassword extends PureComponent { - static navigationOptions = ({ navigation }) => getOnboardingNavbarOptions(navigation); + static navigationOptions = ({ navigation, route }) => getOnboardingNavbarOptions(navigation, route); static propTypes = { /** @@ -228,7 +228,11 @@ class ChoosePassword extends PureComponent { /** * Action to reset the flag seedphraseBackedUp in redux */ - seedphraseNotBackedUp: PropTypes.func + seedphraseNotBackedUp: PropTypes.func, + /** + * Object that represents the current route info like params passed to it + */ + route: PropTypes.object }; state = { @@ -269,7 +273,7 @@ class ChoosePassword extends PureComponent { if (!prevLoading && loading) { // update navigationOptions navigation.setParams({ - headerLeft: + headerLeft: () => }); } } @@ -306,8 +310,7 @@ class ChoosePassword extends PureComponent { } try { this.setState({ loading: true }); - - const previous_screen = this.props.navigation.getParam(PREVIOUS_SCREEN); + const previous_screen = this.props.route.params?.[PREVIOUS_SCREEN]; if (previous_screen === ONBOARDING) { await this.createNewVaultAndKeychain(password); @@ -496,8 +499,11 @@ class ChoosePassword extends PureComponent { learnMore = () => { this.props.navigation.push('Webview', { - url: 'https://metamask.zendesk.com/hc/en-us/articles/360039616872-How-can-I-reset-my-password-', - title: 'metamask.zendesk.com' + screen: 'SimpleWebview', + params: { + url: 'https://metamask.zendesk.com/hc/en-us/articles/360039616872-How-can-I-reset-my-password-', + title: 'metamask.zendesk.com' + } }); }; @@ -516,7 +522,7 @@ class ChoosePassword extends PureComponent { } = this.state; const passwordsMatch = password !== '' && password === confirmPassword; const canSubmit = passwordsMatch && isSelected; - const previousScreen = this.props.navigation.getParam(PREVIOUS_SCREEN); + const previousScreen = this.props.route.params?.[PREVIOUS_SCREEN]; const passwordStrengthWord = getPasswordStrengthWord(passwordStrength); return ( diff --git a/app/components/Views/ChoosePassword/index.test.js b/app/components/Views/ChoosePassword/index.test.js index 2e837ab2d17..dc5586b4b69 100644 --- a/app/components/Views/ChoosePassword/index.test.js +++ b/app/components/Views/ChoosePassword/index.test.js @@ -24,17 +24,9 @@ describe('ChoosePassword', () => { } }; - const wrapper = shallow( - [ONBOARDING, PROTECT], - state: { params: {} } - }} - />, - { - context: { store: mockStore(initialState) } - } - ); + const wrapper = shallow(, { + context: { store: mockStore(initialState) } + }); expect(wrapper.dive()).toMatchSnapshot(); }); }); diff --git a/app/components/Views/ChoosePasswordSimple/index.js b/app/components/Views/ChoosePasswordSimple/index.js index e5491e0606f..f60a1b17e1e 100644 --- a/app/components/Views/ChoosePasswordSimple/index.js +++ b/app/components/Views/ChoosePasswordSimple/index.js @@ -125,7 +125,11 @@ class ChoosePasswordSimple extends PureComponent { /** * The navigator object */ - navigation: PropTypes.object + navigation: PropTypes.object, + /** + * Object that represents the current route info like params passed to it + */ + route: PropTypes.object }; state = { @@ -170,7 +174,7 @@ class ChoosePasswordSimple extends PureComponent { if (error) { Alert.alert('Error', error); } else { - this.props.navigation.state.params.onPasswordSet(this.state.password); + this.props.route.params.onPasswordSet(this.state.password); this.props.navigation.pop(); return; } diff --git a/app/components/Views/ChoosePasswordSimple/index.test.js b/app/components/Views/ChoosePasswordSimple/index.test.js index baefc32e319..e6e6458ca29 100644 --- a/app/components/Views/ChoosePasswordSimple/index.test.js +++ b/app/components/Views/ChoosePasswordSimple/index.test.js @@ -16,16 +16,9 @@ describe('ChoosePasswordSimple', () => { } }; - const wrapper = shallow( - , - { - context: { store: mockStore(initialState) } - } - ); + const wrapper = shallow(, { + context: { store: mockStore(initialState) } + }); expect(wrapper.dive()).toMatchSnapshot(); }); }); diff --git a/app/components/Views/Collectible/__snapshots__/index.test.js.snap b/app/components/Views/Collectible/__snapshots__/index.test.js.snap index ea2870580d1..1729a9031fc 100644 --- a/app/components/Views/Collectible/__snapshots__/index.test.js.snap +++ b/app/components/Views/Collectible/__snapshots__/index.test.js.snap @@ -33,15 +33,6 @@ exports[`Collectible should render correctly 1`] = ` "address": "0x1", } } - navigation={ - Object { - "state": Object { - "params": Object { - "address": "0x1", - }, - }, - } - } ownerOf={0} /> @@ -60,15 +51,6 @@ exports[`Collectible should render correctly 1`] = ` } } collectibles={Array []} - navigation={ - Object { - "state": Object { - "params": Object { - "address": "0x1", - }, - }, - } - } /> @@ -118,15 +100,6 @@ exports[`Collectible should render correctly 1`] = ` "address": "0x1", } } - navigation={ - Object { - "state": Object { - "params": Object { - "address": "0x1", - }, - }, - } - } onClose={[Function]} /> diff --git a/app/components/Views/Collectible/index.js b/app/components/Views/Collectible/index.js index 24014ae0f5a..f21964f7d3f 100644 --- a/app/components/Views/Collectible/index.js +++ b/app/components/Views/Collectible/index.js @@ -42,7 +42,11 @@ class Collectible extends PureComponent { /** * Whether collectible contract information is visible */ - collectibleContractModalVisible: PropTypes.bool + collectibleContractModalVisible: PropTypes.bool, + /** + * Object that represents the current route info like params passed to it + */ + route: PropTypes.object }; state = { @@ -50,8 +54,8 @@ class Collectible extends PureComponent { collectibles: [] }; - static navigationOptions = ({ navigation }) => - getNetworkNavbarOptions(navigation.getParam('name', ''), false, navigation); + static navigationOptions = ({ navigation, route }) => + getNetworkNavbarOptions(route.params?.name ?? '', false, navigation); onRefresh = async () => { this.setState({ refreshing: true }); @@ -66,9 +70,7 @@ class Collectible extends PureComponent { render = () => { const { - navigation: { - state: { params } - }, + route: { params }, navigation, collectibleContractModalVisible } = this.props; diff --git a/app/components/Views/Collectible/index.test.js b/app/components/Views/Collectible/index.test.js index daabdef34ae..97b9f8332f2 100644 --- a/app/components/Views/Collectible/index.test.js +++ b/app/components/Views/Collectible/index.test.js @@ -20,7 +20,7 @@ describe('Collectible', () => { } }; - const wrapper = shallow(, { + const wrapper = shallow(, { context: { store: mockStore(initialState) } }); expect(wrapper.dive()).toMatchSnapshot(); diff --git a/app/components/Views/CollectibleView/index.js b/app/components/Views/CollectibleView/index.js new file mode 100644 index 00000000000..c333e71644a --- /dev/null +++ b/app/components/Views/CollectibleView/index.js @@ -0,0 +1,113 @@ +import React, { PureComponent } from 'react'; +import { ScrollView, View, StyleSheet, Text, SafeAreaView } from 'react-native'; +import PropTypes from 'prop-types'; +import CollectibleOverview from '../../UI/CollectibleOverview'; +import { getNetworkNavbarOptions } from '../../UI/Navbar'; +import StyledButton from '../../UI/StyledButton'; +import { strings } from '../../../../locales/i18n'; +import { colors, fontStyles } from '../../../styles/common'; +import { connect } from 'react-redux'; +import collectiblesTransferInformation from '../../../util/collectibles-transfer'; +import { newAssetTransaction } from '../../../actions/transaction'; + +const styles = StyleSheet.create({ + root: { + flex: 1, + backgroundColor: colors.white + }, + wrapper: { + flex: 0.9 + }, + buttons: { + paddingVertical: 15, + flex: 0.1, + height: 4 + }, + button: { + marginHorizontal: 16, + flexDirection: 'row' + }, + buttonText: { + marginLeft: 8, + fontSize: 15, + color: colors.white, + ...fontStyles.bold + } +}); + +/** + * View that displays a specific collectible asset + */ +class CollectibleView extends PureComponent { + static propTypes = { + /** + /* navigation object required to access the props + /* passed by the parent component + */ + navigation: PropTypes.object, + /** + * Start transaction with asset + */ + newAssetTransaction: PropTypes.func, + /** + * Object that represents the current route info like params passed to it + */ + route: PropTypes.object + }; + + static navigationOptions = ({ navigation, route }) => + getNetworkNavbarOptions(route.params?.contractName ?? '', false, navigation); + + onSend = async () => { + const { + route: { params } + } = this.props; + this.props.newAssetTransaction(params); + this.props.navigation.navigate('SendFlowView'); + }; + + render() { + const { + route: { params }, + navigation + } = this.props; + const collectible = params; + const lowerAddress = collectible.address.toLowerCase(); + const tradable = + lowerAddress in collectiblesTransferInformation + ? collectiblesTransferInformation[lowerAddress].tradable + : true; + + return ( + + + + + + + {tradable && ( + + + {strings('asset_overview.send_button').toUpperCase()} + + + )} + + ); + } +} + +const mapDispatchToProps = dispatch => ({ + newAssetTransaction: selectedAsset => dispatch(newAssetTransaction(selectedAsset)) +}); + +export default connect( + null, + mapDispatchToProps +)(CollectibleView); diff --git a/app/components/Views/CreateWallet/__snapshots__/index.test.js.snap b/app/components/Views/CreateWallet/__snapshots__/index.test.js.snap index 717171232d3..3306be1cf2c 100644 --- a/app/components/Views/CreateWallet/__snapshots__/index.test.js.snap +++ b/app/components/Views/CreateWallet/__snapshots__/index.test.js.snap @@ -1,3 +1,3 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`CreateWallet should render correctly 1`] = ``; +exports[`CreateWallet should render correctly 1`] = ``; diff --git a/app/components/Views/CreateWallet/index.js b/app/components/Views/CreateWallet/index.js index 2d01dd0ea00..b90041f7e0e 100644 --- a/app/components/Views/CreateWallet/index.js +++ b/app/components/Views/CreateWallet/index.js @@ -20,7 +20,8 @@ import { passwordUnset, seedphraseNotBackedUp } from '../../../actions/user'; import { setLockTime } from '../../../actions/settings'; import { connect } from 'react-redux'; import setOnboardingWizardStep from '../../../actions/wizard'; -import { withNavigationFocus } from 'react-navigation'; +// eslint-disable-next-line import/named +import { withNavigationFocus } from '@react-navigation/compat'; import OnboardingScreenWithBg from '../../UI/OnboardingScreenWithBg'; import Device from '../../../util/Device'; import { @@ -145,7 +146,7 @@ class CreateWallet extends PureComponent { this.props.navigation.navigate('HomeNav'); } else { this.props.setOnboardingWizardStep(1); - this.props.navigation.navigate('WalletView'); + this.props.navigation.navigate('HomeNav', { screen: 'WalletView' }); } }, 1000); }); diff --git a/app/components/Views/EnterPasswordSimple/index.js b/app/components/Views/EnterPasswordSimple/index.js index 892fba5ba6a..975d01e88a1 100644 --- a/app/components/Views/EnterPasswordSimple/index.js +++ b/app/components/Views/EnterPasswordSimple/index.js @@ -45,7 +45,11 @@ export default class EnterPasswordSimple extends PureComponent { /** * The navigator object */ - navigation: PropTypes.object + navigation: PropTypes.object, + /** + * Object that represents the current route info like params passed to it + */ + route: PropTypes.object }; state = { @@ -65,7 +69,7 @@ export default class EnterPasswordSimple extends PureComponent { if (!passwordRequirementsMet(this.state.password)) { Alert.alert(strings('enter_password.error'), strings('choose_password.password_length_error')); } else { - this.props.navigation.state.params.onPasswordSet(this.state.password); + this.props.route.params.onPasswordSet(this.state.password); this.props.navigation.pop(); return; } diff --git a/app/components/Views/EnterPasswordSimple/index.test.js b/app/components/Views/EnterPasswordSimple/index.test.js index a8985c71ad5..abff329f0a5 100644 --- a/app/components/Views/EnterPasswordSimple/index.test.js +++ b/app/components/Views/EnterPasswordSimple/index.test.js @@ -6,14 +6,7 @@ import EnterPasswordSimple from './'; describe('EnterPasswordSimple', () => { it('should render correctly', () => { - const wrapper = shallow( - , - {} - ); + const wrapper = shallow(, {}); expect(wrapper.dive()).toMatchSnapshot(); }); }); diff --git a/app/components/Views/Entry/index.js b/app/components/Views/Entry/index.js index e17c980d116..2ef70c4d34f 100644 --- a/app/components/Views/Entry/index.js +++ b/app/components/Views/Entry/index.js @@ -1,5 +1,5 @@ import React, { useState, useRef, useEffect, useCallback } from 'react'; -import { withNavigation } from 'react-navigation'; +import { withNavigation } from '@react-navigation/compat'; import PropTypes from 'prop-types'; import { Animated, Dimensions, StyleSheet, View } from 'react-native'; import AsyncStorage from '@react-native-community/async-storage'; @@ -85,7 +85,12 @@ const Entry = props => { useNativeDriver: true, isInteraction: false }).start(() => { - if (viewToGo && (viewToGo !== 'WalletView' || viewToGo !== 'Onboarding')) { + if (viewToGo === 'OptinMetrics') { + props.navigation.navigate('OnboardingRootNav', { + screen: 'OnboardingNav', + params: { screen: 'OptinMetrics' } + }); + } else if (viewToGo && (viewToGo !== 'WalletView' || viewToGo !== 'Onboarding')) { props.navigation.navigate(viewToGo); } else if (viewToGo === 'Onboarding') { props.navigation.navigate('OnboardingRootNav'); diff --git a/app/components/Views/GasEducationCarousel/index.js b/app/components/Views/GasEducationCarousel/index.js index b2dae0f98d3..c2f9e3ef72a 100644 --- a/app/components/Views/GasEducationCarousel/index.js +++ b/app/components/Views/GasEducationCarousel/index.js @@ -112,7 +112,7 @@ const carousel_images = [gas_education_carousel_1, gas_education_carousel_2, gas /** * View that is displayed to first time (new) users */ -const GasEducationCarousel = ({ navigation, conversionRate, currentCurrency }) => { +const GasEducationCarousel = ({ navigation, route, conversionRate, currentCurrency }) => { const [currentTab, setCurrentTab] = useState(1); const [gasFiat, setGasFiat] = useState(null); @@ -126,9 +126,8 @@ const GasEducationCarousel = ({ navigation, conversionRate, currentCurrency }) = }, [conversionRate, currentCurrency]); const onPresGetStarted = () => { - navigation.dismiss(); - const navigateTo = navigation && navigation.getParam('navigateTo'); - navigateTo(); + navigation.pop(); + route?.params?.navigateTo?.(); }; const renderTabBar = () => ; @@ -139,7 +138,8 @@ const GasEducationCarousel = ({ navigation, conversionRate, currentCurrency }) = const openLink = () => navigation.navigate('Webview', { - url: 'https://community.metamask.io/t/what-is-gas-why-do-transactions-take-so-long/3172' + screen: 'SimpleWebview', + params: { url: 'https://community.metamask.io/t/what-is-gas-why-do-transactions-take-so-long/3172' } }); const renderText = key => { @@ -275,7 +275,11 @@ GasEducationCarousel.propTypes = { /** /* Selected currency */ - currentCurrency: PropTypes.string + currentCurrency: PropTypes.string, + /** + * Object that represents the current route info like params passed to it + */ + route: PropTypes.object }; const mapStateToProps = state => ({ diff --git a/app/components/Views/ImportFromSeed/__snapshots__/index.test.js.snap b/app/components/Views/ImportFromSeed/__snapshots__/index.test.js.snap index f26e20f3084..c8a8f4b0e6c 100644 --- a/app/components/Views/ImportFromSeed/__snapshots__/index.test.js.snap +++ b/app/components/Views/ImportFromSeed/__snapshots__/index.test.js.snap @@ -457,13 +457,6 @@ exports[`ImportFromSeed should render correctly 1`] = ` > diff --git a/app/components/Views/ImportFromSeed/index.js b/app/components/Views/ImportFromSeed/index.js index ca1c7a20e2f..a93afdd4e90 100644 --- a/app/components/Views/ImportFromSeed/index.js +++ b/app/components/Views/ImportFromSeed/index.js @@ -177,7 +177,7 @@ const PASSCODE_NOT_SET_ERROR = 'Error: Passcode not set.'; * using a seed phrase */ class ImportFromSeed extends PureComponent { - static navigationOptions = ({ navigation }) => getOnboardingNavbarOptions(navigation); + static navigationOptions = ({ navigation, route }) => getOnboardingNavbarOptions(navigation, route); static propTypes = { /** @@ -292,7 +292,7 @@ class ImportFromSeed extends PureComponent { this.props.navigation.navigate('ManualBackupStep3'); } else { this.props.setOnboardingWizardStep(1); - this.props.navigation.navigate('WalletView'); + this.props.navigation.navigate('HomeNav', { screen: 'WalletView' }); } await importAdditionalAccounts(); } catch (error) { diff --git a/app/components/Views/ImportFromSeed/index.test.js b/app/components/Views/ImportFromSeed/index.test.js index 8ae876b69bd..3eef21f0301 100644 --- a/app/components/Views/ImportFromSeed/index.test.js +++ b/app/components/Views/ImportFromSeed/index.test.js @@ -14,16 +14,9 @@ describe('ImportFromSeed', () => { } }; - const wrapper = shallow( - , - { - context: { store: mockStore(initialState) } - } - ); + const wrapper = shallow(, { + context: { store: mockStore(initialState) } + }); expect(wrapper.dive()).toMatchSnapshot(); }); }); diff --git a/app/components/Views/ImportPrivateKey/index.js b/app/components/Views/ImportPrivateKey/index.js index 2b9dbfef0bf..92a5b98c3bc 100644 --- a/app/components/Views/ImportPrivateKey/index.js +++ b/app/components/Views/ImportPrivateKey/index.js @@ -167,8 +167,11 @@ export default class ImportPrivateKey extends PureComponent { learnMore = () => this.props.navigation.navigate('Webview', { - url: 'https://metamask.zendesk.com/hc/en-us/articles/360015289932-What-are-imported-accounts-', - title: strings('drawer.metamask_support') + screen: 'SimpleWebview', + params: { + url: 'https://metamask.zendesk.com/hc/en-us/articles/360015289932-What-are-imported-accounts-', + title: strings('drawer.metamask_support') + } }); onInputChange = value => { diff --git a/app/components/Views/ImportPrivateKey/index.test.js b/app/components/Views/ImportPrivateKey/index.test.js index a14593501e0..4ea7982f1f3 100644 --- a/app/components/Views/ImportPrivateKey/index.test.js +++ b/app/components/Views/ImportPrivateKey/index.test.js @@ -4,7 +4,7 @@ import ImportPrivateKey from './'; describe('ImportPrivateKey', () => { it('should render correctly', () => { - const wrapper = shallow( null, state: { params: {} } }} />); + const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); }); }); diff --git a/app/components/Views/ImportPrivateKeySuccess/__snapshots__/index.test.js.snap b/app/components/Views/ImportPrivateKeySuccess/__snapshots__/index.test.js.snap index a2280185ea7..eed52b04b60 100644 --- a/app/components/Views/ImportPrivateKeySuccess/__snapshots__/index.test.js.snap +++ b/app/components/Views/ImportPrivateKeySuccess/__snapshots__/index.test.js.snap @@ -2,12 +2,9 @@ exports[`ImportPrivateKeySuccess should render correctly 1`] = ` diff --git a/app/components/Views/ImportPrivateKeySuccess/index.test.js b/app/components/Views/ImportPrivateKeySuccess/index.test.js index 19a51e40a1c..fd2672e8b2c 100644 --- a/app/components/Views/ImportPrivateKeySuccess/index.test.js +++ b/app/components/Views/ImportPrivateKeySuccess/index.test.js @@ -11,7 +11,7 @@ describe('ImportPrivateKeySuccess', () => { it('should render correctly', () => { const wrapper = shallow( - null, state: { params: {} } }} /> + ); expect(wrapper).toMatchSnapshot(); diff --git a/app/components/Views/LockScreen/index.js b/app/components/Views/LockScreen/index.js index 6a49b9044d0..9aae314eae2 100644 --- a/app/components/Views/LockScreen/index.js +++ b/app/components/Views/LockScreen/index.js @@ -119,7 +119,10 @@ class LockScreen extends PureComponent { } else if (this.props.passwordSet) { this.props.navigation.navigate('Login'); } else { - this.props.navigation.navigate('Onboarding'); + this.props.navigation.navigate('OnboardingRootNav', { + screen: 'OnboardingNav', + params: { screen: 'Onboarding' } + }); } } catch (error) { if (this.unlockAttempts <= 3) { diff --git a/app/components/Views/Login/index.js b/app/components/Views/Login/index.js index c317b78af09..346c3e0e18f 100644 --- a/app/components/Views/Login/index.js +++ b/app/components/Views/Login/index.js @@ -302,7 +302,10 @@ class Login extends PureComponent { // Check if user passed through metrics opt-in screen const metricsOptIn = await AsyncStorage.getItem(METRICS_OPT_IN); if (!metricsOptIn) { - this.props.navigation.navigate('OptinMetrics'); + this.props.navigation.navigate('OnboardingRootNav', { + screen: 'OnboardingNav', + params: { screen: 'OptinMetrics' } + }); } else if (onboardingWizard) { this.props.navigation.navigate('HomeNav'); } else { @@ -355,7 +358,10 @@ class Login extends PureComponent { deleteExistingUser = async () => { try { await AsyncStorage.removeItem(EXISTING_USER); - this.props.navigation.navigate('Onboarding', { delete: true }); + this.props.navigation.navigate('OnboardingRootNav', { + screen: 'OnboardingNav', + params: { screen: 'Onboarding', params: { delete: true } } + }); } catch (error) { Logger.log(error, `Failed to remove key: ${EXISTING_USER} from AsyncStorage`); } diff --git a/app/components/Views/ManualBackupStep1/index.js b/app/components/Views/ManualBackupStep1/index.js index 414eaabd154..8daee184561 100644 --- a/app/components/Views/ManualBackupStep1/index.js +++ b/app/components/Views/ManualBackupStep1/index.js @@ -215,13 +215,17 @@ const styles = StyleSheet.create({ * the backup seed phrase flow */ export default class ManualBackupStep1 extends PureComponent { - static navigationOptions = ({ navigation }) => getOnboardingNavbarOptions(navigation); + static navigationOptions = ({ navigation, route }) => getOnboardingNavbarOptions(navigation, route); static propTypes = { /** /* navigation object required to push and pop other views */ - navigation: PropTypes.object + navigation: PropTypes.object, + /** + * Object that represents the current route info like params passed to it + */ + route: PropTypes.object }; steps = MANUAL_BACKUP_STEPS; @@ -236,7 +240,8 @@ export default class ManualBackupStep1 extends PureComponent { }; async componentDidMount() { - this.words = this.props.navigation.getParam('words', []); + this.words = this.props.route.params?.words ?? []; + if (!this.words.length) { try { const credentials = await SecureKeychain.getGenericPassword(); diff --git a/app/components/Views/ManualBackupStep1/index.test.js b/app/components/Views/ManualBackupStep1/index.test.js index 7df8816a963..d9f63dee9b7 100644 --- a/app/components/Views/ManualBackupStep1/index.test.js +++ b/app/components/Views/ManualBackupStep1/index.test.js @@ -6,22 +6,23 @@ describe('ManualBackupStep1', () => { it('should render correctly', () => { const wrapper = shallow( [ - 'abstract', - 'accident', - 'acoustic', - 'announce', - 'artefact', - 'attitude', - 'bachelor', - 'broccoli', - 'business', - 'category', - 'champion', - 'cinnamon' - ], - state: { params: {} } + route={{ + params: { + words: [ + 'abstract', + 'accident', + 'acoustic', + 'announce', + 'artefact', + 'attitude', + 'bachelor', + 'broccoli', + 'business', + 'category', + 'champion', + 'cinnamon' + ] + } }} /> ); diff --git a/app/components/Views/ManualBackupStep2/index.js b/app/components/Views/ManualBackupStep2/index.js index fa0107706fd..a7881fd52ce 100644 --- a/app/components/Views/ManualBackupStep2/index.js +++ b/app/components/Views/ManualBackupStep2/index.js @@ -149,7 +149,7 @@ const styles = StyleSheet.create({ * the backup seed phrase flow */ class ManualBackupStep2 extends PureComponent { - static navigationOptions = ({ navigation }) => getOnboardingNavbarOptions(navigation); + static navigationOptions = ({ navigation, route }) => getOnboardingNavbarOptions(navigation, route); static propTypes = { /** @@ -160,18 +160,22 @@ class ManualBackupStep2 extends PureComponent { * The action to update the seedphrase backed up flag * in the redux store */ - seedphraseBackedUp: PropTypes.func + seedphraseBackedUp: PropTypes.func, + /** + * Object that represents the current route info like params passed to it + */ + route: PropTypes.object }; constructor(props) { super(props); - const words = props.navigation.getParam('words'); + const words = props.route.params?.words; if (process.env.JEST_WORKER_ID === undefined) { this.words = [...words].sort(() => 0.5 - Math.random()); } else { this.words = words; } - this.steps = props.navigation.getParam('steps'); + this.steps = props.route.params?.steps; } state = { @@ -183,8 +187,8 @@ class ManualBackupStep2 extends PureComponent { }; componentDidMount = () => { - const { navigation } = this.props; - const words = navigation.getParam('words', []); + const { route } = this.props; + const words = route.params?.words ?? []; this.setState( { confirmedWords: Array(words.length).fill({ word: undefined, originalPosition: undefined }) @@ -247,11 +251,11 @@ class ManualBackupStep2 extends PureComponent { }; goNext = () => { - const { seedphraseBackedUp, navigation } = this.props; + const { seedphraseBackedUp, route, navigation } = this.props; if (this.validateWords()) { seedphraseBackedUp(); InteractionManager.runAfterInteractions(() => { - const words = navigation.getParam('words'); + const words = route.params?.words; navigation.navigate('ManualBackupStep3', { steps: this.steps, words }); }); } else { @@ -260,7 +264,7 @@ class ManualBackupStep2 extends PureComponent { }; validateWords = () => { - const words = this.props.navigation.getParam('words', []); + const words = this.props.route.params?.words ?? []; const { confirmedWords: wordMap } = this.state; const confirmedWords = wordMap.map(confirmedWord => confirmedWord.word); if (words.join('') === confirmedWords.join('')) { diff --git a/app/components/Views/ManualBackupStep2/index.test.js b/app/components/Views/ManualBackupStep2/index.test.js index 08c850d2e4b..aaff97ac6d4 100644 --- a/app/components/Views/ManualBackupStep2/index.test.js +++ b/app/components/Views/ManualBackupStep2/index.test.js @@ -16,28 +16,24 @@ describe('ManualBackupStep2', () => { const wrapper = shallow( { - const params = { - words: [ - 'abstract', - 'accident', - 'acoustic', - 'announce', - 'artefact', - 'attitude', - 'bachelor', - 'broccoli', - 'business', - 'category', - 'champion', - 'cinnamon' - ], - steps: ['one', 'two', 'three'] - }; - return params[param]; - }, - state: { params: {} } + route={{ + params: { + words: [ + 'abstract', + 'accident', + 'acoustic', + 'announce', + 'artefact', + 'attitude', + 'bachelor', + 'broccoli', + 'business', + 'category', + 'champion', + 'cinnamon' + ], + steps: ['one', 'two', 'three'] + } }} />, { diff --git a/app/components/Views/ManualBackupStep3/index.js b/app/components/Views/ManualBackupStep3/index.js index 61da2be1f6f..fa06ff166c7 100644 --- a/app/components/Views/ManualBackupStep3/index.js +++ b/app/components/Views/ManualBackupStep3/index.js @@ -75,7 +75,7 @@ class ManualBackupStep3 extends PureComponent { constructor(props) { super(props); - this.steps = props.navigation.getParam('steps', undefined); + this.steps = props.route.params?.steps; } state = { @@ -88,7 +88,11 @@ class ManualBackupStep3 extends PureComponent { /** /* navigation object required to push and pop other views */ - navigation: PropTypes.object + navigation: PropTypes.object, + /** + * Object that represents the current route info like params passed to it + */ + route: PropTypes.object }; componentWillUnmount = () => { @@ -111,12 +115,15 @@ class ManualBackupStep3 extends PureComponent { learnMore = () => this.props.navigation.navigate('Webview', { - url: 'https://support.metamask.io', - title: strings('drawer.metamask_support') + screen: 'SimpleWebview', + params: { + url: 'https://support.metamask.io', + title: strings('drawer.metamask_support') + } }); isHintSeedPhrase = hintText => { - const words = this.props.navigation.getParam('words'); + const words = this.props.route.params?.words; if (words) { const lower = string => String(string).toLowerCase(); return lower(hintText) === lower(words.join(' ')); @@ -144,11 +151,9 @@ class ManualBackupStep3 extends PureComponent { if (!metricsOptIn) { this.props.navigation.navigate('OptinMetrics'); } else if (onboardingWizard) { - this.props.navigation.navigate('HomeNav'); - this.props.navigation.popToTop(); - this.props.navigation.goBack(null); + this.props.navigation.reset({ routes: [{ name: 'HomeNav' }] }); } else { - this.props.navigation.navigate('HomeNav'); + this.props.navigation.reset({ routes: [{ name: 'HomeNav' }] }); } }; diff --git a/app/components/Views/NavigationUnitTest/TestScreen1.test.js b/app/components/Views/NavigationUnitTest/TestScreen1.test.js new file mode 100644 index 00000000000..40daac0b8ad --- /dev/null +++ b/app/components/Views/NavigationUnitTest/TestScreen1.test.js @@ -0,0 +1,10 @@ +import React from 'react'; +import NavigationUnitTest from '.'; +import { render } from 'enzyme'; + +describe('NavigationUnitTest', () => { + it('should render correctly', () => { + const wrapper = render(); + expect(wrapper).toMatchSnapshot(); + }); +}); diff --git a/app/components/Views/NavigationUnitTest/TestScreen2.test.js b/app/components/Views/NavigationUnitTest/TestScreen2.test.js new file mode 100644 index 00000000000..acfed8f8cfd --- /dev/null +++ b/app/components/Views/NavigationUnitTest/TestScreen2.test.js @@ -0,0 +1,10 @@ +import React from 'react'; +import NavigationUnitTest from '.'; +import { render } from 'enzyme'; + +describe('NavigationUnitTest', () => { + it('should render correctly', () => { + const wrapper = render(); + expect(wrapper).toMatchSnapshot(); + }); +}); diff --git a/app/components/Views/NavigationUnitTest/TestScreen3.test.js b/app/components/Views/NavigationUnitTest/TestScreen3.test.js new file mode 100644 index 00000000000..188048d9c55 --- /dev/null +++ b/app/components/Views/NavigationUnitTest/TestScreen3.test.js @@ -0,0 +1,10 @@ +import React from 'react'; +import NavigationUnitTest from '.'; +import { render } from 'enzyme'; + +describe('NavigationUnitTest', () => { + it('should render correctly', () => { + const wrapper = render(); + expect(wrapper).toMatchSnapshot(); + }); +}); diff --git a/app/components/Views/NavigationUnitTest/__snapshots__/TestScreen1.test.js.snap b/app/components/Views/NavigationUnitTest/__snapshots__/TestScreen1.test.js.snap new file mode 100644 index 00000000000..1614ab55df9 --- /dev/null +++ b/app/components/Views/NavigationUnitTest/__snapshots__/TestScreen1.test.js.snap @@ -0,0 +1,106 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`NavigationUnitTest should render correctly 1`] = ` + + + + + + + + + + + + + TestScreen1 + + + + + + + + + + + + + + + + + + TestScreen1 THIS SHOULD NOT HAVE CHANGED, take a deeper look + + + + + + + + + + + +`; diff --git a/app/components/Views/NavigationUnitTest/__snapshots__/TestScreen2.test.js.snap b/app/components/Views/NavigationUnitTest/__snapshots__/TestScreen2.test.js.snap new file mode 100644 index 00000000000..e090dffe19e --- /dev/null +++ b/app/components/Views/NavigationUnitTest/__snapshots__/TestScreen2.test.js.snap @@ -0,0 +1,201 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`NavigationUnitTest should render correctly 1`] = ` + + + + + + + + + + + + + TestStack + + + + + + + + + + + + + + + + + + + + + + + + + + + + TestScreen2 + + + + + + + + + + + + + + + + + + TestScreen2 THIS SHOULD NOT HAVE CHANGED, take a deeper look + + + + + + + + + + + + + + + + + + + + +`; diff --git a/app/components/Views/NavigationUnitTest/__snapshots__/TestScreen3.test.js.snap b/app/components/Views/NavigationUnitTest/__snapshots__/TestScreen3.test.js.snap new file mode 100644 index 00000000000..14ba4791ba9 --- /dev/null +++ b/app/components/Views/NavigationUnitTest/__snapshots__/TestScreen3.test.js.snap @@ -0,0 +1,296 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`NavigationUnitTest should render correctly 1`] = ` + + + + + + + + + + + + + TestStack + + + + + + + + + + + + + + + + + + + + + + + + + + + + TestSubStack + + + + + + + + + + + + + + + + + + + + + + + + + + + + TestScreen3 + + + + + + + + + + + + + + + + + + TestScreen3 THIS SHOULD NOT HAVE CHANGED, take a deeper look + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +`; diff --git a/app/components/Views/NavigationUnitTest/index.js b/app/components/Views/NavigationUnitTest/index.js new file mode 100644 index 00000000000..0e8fcd00b70 --- /dev/null +++ b/app/components/Views/NavigationUnitTest/index.js @@ -0,0 +1,53 @@ +/** + * This view was created in order to test the navigation api since it's possible it can change even with minor upgrades. + * For reference see: https://reactnavigation.org/docs/navigation-prop/#dangerouslygetstate + */ + +/* eslint-disable react/prop-types */ +import React from 'react'; +import { createStackNavigator } from '@react-navigation/stack'; +import { NavigationContainer, useNavigationState } from '@react-navigation/native'; +import { findRouteNameFromNavigatorState } from '../../../util/general'; +import { Text } from 'react-native'; + +const Stack = createStackNavigator(); + +const NavigationUnitTestFactory = ({ firstRoute, secondRoute }) => { + const TestScreen = ({ route }) => { + const routes = useNavigationState(state => state.routes); + + const name = findRouteNameFromNavigatorState(routes); + + if (name !== route.params.screenName) + throw new Error( + 'Error, react navigation api changed: https://reactnavigation.org/docs/navigation-prop/#dangerouslygetstate' + ); + + return {name} THIS SHOULD NOT HAVE CHANGED, take a deeper look; + }; + + const TestSubStack = () => ( + + + + ); + + const TestStack = () => ( + + + + + ); + + const NavigationUnitTest = () => ( + + + + + + + ); + + return ; +}; +export default NavigationUnitTestFactory; diff --git a/app/components/Views/OfflineMode/index.js b/app/components/Views/OfflineMode/index.js index b3d747bbcd7..b8b389f2cf5 100644 --- a/app/components/Views/OfflineMode/index.js +++ b/app/components/Views/OfflineMode/index.js @@ -58,7 +58,10 @@ const OfflineMode = ({ navigation, infuraBlocked }) => { }; const learnMore = () => { - navigation.navigate('SimpleWebview', { url: AppConstants.URLS.CONNECTIVITY_ISSUES }); + navigation.navigate('Webview', { + screen: 'SimpleWebview', + params: { url: AppConstants.URLS.CONNECTIVITY_ISSUES } + }); }; const action = () => { diff --git a/app/components/Views/OfflineMode/index.test.js b/app/components/Views/OfflineMode/index.test.js index ab24f9fbf8c..1b6f663c5fd 100644 --- a/app/components/Views/OfflineMode/index.test.js +++ b/app/components/Views/OfflineMode/index.test.js @@ -12,7 +12,7 @@ describe('OfflineMode', () => { isBlocked: false } }; - const wrapper = shallow( false }} />, { + const wrapper = shallow(, { context: { store: mockStore(initialState) } }); expect(wrapper.dive()).toMatchSnapshot(); diff --git a/app/components/Views/Onboarding/index.js b/app/components/Views/Onboarding/index.js index d83e83674dc..f6e3dba346b 100644 --- a/app/components/Views/Onboarding/index.js +++ b/app/components/Views/Onboarding/index.js @@ -169,8 +169,8 @@ const createStep = step => ({ * View that is displayed to first time (new) users */ class Onboarding extends PureComponent { - static navigationOptions = ({ navigation }) => - navigation.getParam('delete', null) + static navigationOptions = ({ navigation, route }) => + route.params?.delete ? getTransparentOnboardingNavbarOptions(navigation) : getTransparentBackOnboardingNavbarOptions(navigation); @@ -221,7 +221,11 @@ class Onboarding extends PureComponent { /** * loadings msg */ - loadingMsg: PropTypes.string + loadingMsg: PropTypes.string, + /** + * Object that represents the current route info like params passed to it + */ + route: PropTypes.object }; notificationAnimated = new Animated.Value(100); @@ -275,7 +279,7 @@ class Onboarding extends PureComponent { this.checkIfExistingUser(); InteractionManager.runAfterInteractions(() => { PreventScreenshot.forbid(); - if (this.props.navigation.getParam('delete', false)) { + if (this.props.route.params?.delete) { this.props.setLoading(strings('onboarding.delete_current')); setTimeout(() => { this.showNotification(); @@ -337,7 +341,6 @@ class Onboarding extends PureComponent { } catch (e) { this.props.unsetLoading(); if (!firstAttempt) { - this.props.navigation.goBack(); if (e.message === 'Sync::timeout') { Alert.alert( strings('sync_with_extension.outdated_qr_code'), @@ -622,7 +625,7 @@ class Onboarding extends PureComponent { } handleSimpleNotification = () => { - if (!this.props.navigation.getParam('delete', false)) return; + if (!this.props.route.params?.delete) return; return ( { it('should render correctly', () => { - const wrapper = shallow( false }} />); + const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); }); }); diff --git a/app/components/Views/QRScanner/index.js b/app/components/Views/QRScanner/index.js index c92a05dc490..f6d90664779 100644 --- a/app/components/Views/QRScanner/index.js +++ b/app/components/Views/QRScanner/index.js @@ -57,7 +57,11 @@ export default class QrScanner extends PureComponent { /** * Object that represents the navigator */ - navigation: PropTypes.object + navigation: PropTypes.object, + /** + * Object that represents the current route info like params passed to it + */ + route: PropTypes.object }; mounted = false; @@ -69,16 +73,16 @@ export default class QrScanner extends PureComponent { goBack = () => { this.props.navigation.goBack(); - if (this.props.navigation.state.params.onScanError) { - this.props.navigation.state.params.onScanError('USER_CANCELLED'); + if (this.props.route.params.onScanError) { + this.props.route.params.onScanError('USER_CANCELLED'); } }; end = (data, content) => { - const { navigation } = this.props; + const { navigation, route } = this.props; this.mounted = false; navigation.goBack(); - navigation.state.params.onScanSuccess(data, content); + route.params.onScanSuccess(data, content); }; onBarCodeRead = response => { @@ -92,16 +96,14 @@ export default class QrScanner extends PureComponent { if (content.split('metamask-sync:').length > 1) { this.shouldReadBarCode = false; data = { content }; - if (this.props.navigation.state.params.onStartScan) { - this.props.navigation.state.params.onStartScan(data).then(() => { - this.props.navigation.state.params.onScanSuccess(data); + if (this.props.route.params.onStartScan) { + this.props.route.params.onStartScan(data).then(() => { + this.props.route.params.onScanSuccess(data); }); this.mounted = false; - this.props.navigation.goBack(); } else { Alert.alert(strings('qr_scanner.error'), strings('qr_scanner.attempting_sync_from_wallet_error')); this.mounted = false; - this.props.navigation.goBack(); } } else { if (!failedSeedPhraseRequirements(content) && isValidMnemonic(content)) { @@ -128,7 +130,7 @@ export default class QrScanner extends PureComponent { data = { ...data, action }; this.mounted = false; this.props.navigation.goBack(); - this.props.navigation.state.params.onScanSuccess(data, content); + this.props.route.params.onScanSuccess(data, content); return; } @@ -165,8 +167,8 @@ export default class QrScanner extends PureComponent { onError = error => { this.props.navigation.goBack(); InteractionManager.runAfterInteractions(() => { - if (this.props.navigation.state.params.onScanError && error) { - this.props.navigation.state.params.onScanError(error.message); + if (this.props.route.params.onScanError && error) { + this.props.route.params.onScanError(error.message); } }); }; diff --git a/app/components/Views/ResetPassword/index.js b/app/components/Views/ResetPassword/index.js index 0a9eb1ce249..9a27ff14c87 100644 --- a/app/components/Views/ResetPassword/index.js +++ b/app/components/Views/ResetPassword/index.js @@ -273,7 +273,11 @@ class ResetPassword extends PureComponent { /** * A string representing the selected address => account */ - selectedAddress: PropTypes.string + selectedAddress: PropTypes.string, + /** + * Object that represents the current route info like params passed to it + */ + route: PropTypes.object }; state = { @@ -321,7 +325,7 @@ class ResetPassword extends PureComponent { if (!prevLoading && loading) { // update navigationOptions navigation.setParams({ - headerLeft: + headerLeft: () => }); } } @@ -563,8 +567,11 @@ class ResetPassword extends PureComponent { learnMore = () => { this.props.navigation.push('Webview', { - url: 'https://metamask.zendesk.com/hc/en-us/articles/360039616872-How-can-I-reset-my-password-', - title: 'metamask.zendesk.com' + screen: 'SimpleWebview', + params: { + url: 'https://metamask.zendesk.com/hc/en-us/articles/360039616872-How-can-I-reset-my-password-', + title: 'metamask.zendesk.com' + } }); }; @@ -631,7 +638,7 @@ class ResetPassword extends PureComponent { } = this.state; const passwordsMatch = password !== '' && password === confirmPassword; const canSubmit = passwordsMatch && isSelected; - const previousScreen = this.props.navigation.getParam(PREVIOUS_SCREEN); + const previousScreen = this.props.route.params?.[PREVIOUS_SCREEN]; const passwordStrengthWord = getPasswordStrengthWord(passwordStrength); return ( diff --git a/app/components/Views/ResetPassword/index.test.js b/app/components/Views/ResetPassword/index.test.js index 2e837ab2d17..995c5e86c26 100644 --- a/app/components/Views/ResetPassword/index.test.js +++ b/app/components/Views/ResetPassword/index.test.js @@ -4,7 +4,6 @@ import React from 'react'; import { shallow } from 'enzyme'; import ChoosePassword from './'; import configureMockStore from 'redux-mock-store'; -import { ONBOARDING, PROTECT } from '../../../constants/navigation'; describe('ChoosePassword', () => { const mockStore = configureMockStore(); @@ -24,17 +23,9 @@ describe('ChoosePassword', () => { } }; - const wrapper = shallow( - [ONBOARDING, PROTECT], - state: { params: {} } - }} - />, - { - context: { store: mockStore(initialState) } - } - ); + const wrapper = shallow(, { + context: { store: mockStore(initialState) } + }); expect(wrapper.dive()).toMatchSnapshot(); }); }); diff --git a/app/components/Views/RevealPrivateCredential/index.js b/app/components/Views/RevealPrivateCredential/index.js index 428c10a2a90..647cbb875e4 100644 --- a/app/components/Views/RevealPrivateCredential/index.js +++ b/app/components/Views/RevealPrivateCredential/index.js @@ -141,9 +141,9 @@ class RevealPrivateCredential extends PureComponent { warningIncorrectPassword: '' }; - static navigationOptions = ({ navigation }) => + static navigationOptions = ({ navigation, route }) => getNavigationOptionsTitle( - strings(`reveal_credential.${navigation.getParam('privateCredentialName', '')}_title`), + strings(`reveal_credential.${route.params?.privateCredentialName ?? ''}_title`), navigation ); static propTypes = { @@ -170,7 +170,11 @@ class RevealPrivateCredential extends PureComponent { /** * Cancel function to be called when cancel button is clicked. If not provided, we go to previous screen on cancel */ - cancel: PropTypes.func + cancel: PropTypes.func, + /** + * Object that represents the current route info like params passed to it + */ + route: PropTypes.object }; async componentDidMount() { @@ -209,8 +213,7 @@ class RevealPrivateCredential extends PureComponent { const { KeyringController } = Engine.context; const { selectedAddress } = this.props; - const privateCredentialName = - this.props.privateCredentialName || this.props.navigation.state.params.privateCredentialName; + const privateCredentialName = this.props.privateCredentialName || this.props.route.params.privateCredentialName; try { if (privateCredentialName === 'seed_phrase') { @@ -245,8 +248,7 @@ class RevealPrivateCredential extends PureComponent { copyPrivateCredentialToClipboard = async () => { const { privateCredential } = this.state; - const privateCredentialName = - this.props.privateCredentialName || this.props.navigation.state.params.privateCredentialName; + const privateCredentialName = this.props.privateCredentialName || this.props.route.params.privateCredentialName; await Clipboard.setString(privateCredential); this.props.showAlert({ @@ -272,8 +274,7 @@ class RevealPrivateCredential extends PureComponent { render = () => { const { unlocked, privateCredential } = this.state; - const privateCredentialName = - this.props.privateCredentialName || this.props.navigation.state.params.privateCredentialName; + const privateCredentialName = this.props.privateCredentialName || this.props.route.params.privateCredentialName; return ( diff --git a/app/components/Views/RevealPrivateCredential/index.test.js b/app/components/Views/RevealPrivateCredential/index.test.js index 8e7177a8bc0..c2b8e7b5a9b 100644 --- a/app/components/Views/RevealPrivateCredential/index.test.js +++ b/app/components/Views/RevealPrivateCredential/index.test.js @@ -20,7 +20,7 @@ describe('RevealPrivateCredential', () => { } }; const wrapper = shallow( - , + , { context: { store: mockStore(initialState) } } ); expect(wrapper.dive()).toMatchSnapshot(); diff --git a/app/components/Views/Send/index.js b/app/components/Views/Send/index.js index b195ffcbfd2..e82b0d7acc4 100644 --- a/app/components/Views/Send/index.js +++ b/app/components/Views/Send/index.js @@ -51,7 +51,7 @@ const styles = StyleSheet.create({ * View that wraps the wraps the "Send" screen */ class Send extends PureComponent { - static navigationOptions = ({ navigation }) => getTransactionOptionsTitle('send.confirm', navigation); + static navigationOptions = ({ navigation, route }) => getTransactionOptionsTitle('send.confirm', navigation, route); static propTypes = { /** @@ -113,7 +113,11 @@ class Send extends PureComponent { /** * A list of custom RPCs to provide the user */ - frequentRpcList: PropTypes.array + frequentRpcList: PropTypes.array, + /** + * Object that represents the current route info like params passed to it + */ + route: PropTypes.object }; state = { @@ -151,8 +155,8 @@ class Send extends PureComponent { * Check if view is called with txMeta object for a deeplink */ checkForDeeplinks() { - const { navigation } = this.props; - const txMeta = navigation && navigation.getParam('txMeta', null); + const { route } = this.props; + const txMeta = route.params?.txMeta; if (txMeta) { this.handleNewTxMeta(txMeta); } else { @@ -197,15 +201,16 @@ class Send extends PureComponent { } componentDidUpdate(prevProps) { - const prevNavigation = prevProps.navigation; + const prevRoute = prevProps.route; const { - navigation, + route, transaction: { assetType, selectedAsset }, - contractBalances + contractBalances, + navigation } = this.props; - if (prevNavigation && navigation) { - const prevTxMeta = prevNavigation.getParam('txMeta', null); - const currentTxMeta = navigation.getParam('txMeta', null); + if (prevRoute && route) { + const prevTxMeta = prevRoute.params?.txMeta; + const currentTxMeta = route.params?.txMeta; if ( currentTxMeta && currentTxMeta.source && diff --git a/app/components/Views/SendFlow/Amount/index.js b/app/components/Views/SendFlow/Amount/index.js index 92fc0f5e4f7..67a8c050934 100644 --- a/app/components/Views/SendFlow/Amount/index.js +++ b/app/components/Views/SendFlow/Amount/index.js @@ -289,8 +289,7 @@ const styles = StyleSheet.create({ * View that wraps the wraps the "Send" screen */ class Amount extends PureComponent { - static navigationOptions = ({ navigation, screenProps }) => - getSendFlowTitle('send.amount', navigation, screenProps); + static navigationOptions = ({ navigation, route }) => getSendFlowTitle('send.amount', navigation, route); static propTypes = { /** @@ -372,7 +371,11 @@ class Amount extends PureComponent { /** * function to call when the 'Next' button is clicked */ - onConfirm: PropTypes.func + onConfirm: PropTypes.func, + /** + * Indicates whether the current transaction is a deep link transaction + */ + isPaymentRequest: PropTypes.bool }; state = { @@ -397,10 +400,11 @@ class Amount extends PureComponent { transactionState: { readableValue }, navigation, providerType, - selectedAsset + selectedAsset, + isPaymentRequest } = this.props; // For analytics - navigation.setParams({ providerType }); + navigation.setParams({ providerType, isPaymentRequest }); this.tokens = [getEther(ticker), ...tokens]; this.collectibles = this.processCollectibles(); @@ -1076,7 +1080,8 @@ const mapStateToProps = (state, ownProps) => ({ ticker: state.engine.backgroundState.NetworkController.provider.ticker, tokens: state.engine.backgroundState.AssetsController.tokens, transactionState: ownProps.transaction || state.transaction, - selectedAsset: state.transaction.selectedAsset + selectedAsset: state.transaction.selectedAsset, + isPaymentRequest: state.transaction.paymentRequest }); const mapDispatchToProps = dispatch => ({ diff --git a/app/components/Views/SendFlow/Confirm/index.js b/app/components/Views/SendFlow/Confirm/index.js index 743a5423601..27906cfc80b 100644 --- a/app/components/Views/SendFlow/Confirm/index.js +++ b/app/components/Views/SendFlow/Confirm/index.js @@ -220,8 +220,7 @@ const styles = StyleSheet.create({ * View that wraps the wraps the "Send" screen */ class Confirm extends PureComponent { - static navigationOptions = ({ navigation, screenProps }) => - getSendFlowTitle('send.confirm', navigation, screenProps); + static navigationOptions = ({ navigation, route }) => getSendFlowTitle('send.confirm', navigation, route); static propTypes = { /** @@ -315,7 +314,11 @@ class Confirm extends PureComponent { /** * Set proposed nonce (from network) */ - setProposedNonce: PropTypes.func + setProposedNonce: PropTypes.func, + /** + * Indicates whether the current transaction is a deep link transaction + */ + isPaymentRequest: PropTypes.bool }; state = { @@ -392,10 +395,10 @@ class Confirm extends PureComponent { // For analytics AnalyticsV2.trackEvent(AnalyticsV2.ANALYTICS_EVENTS.SEND_TRANSACTION_STARTED, this.getAnalyticsParams()); - const { showCustomNonce, navigation, providerType } = this.props; + const { showCustomNonce, navigation, providerType, isPaymentRequest } = this.props; await this.handleFetchBasicEstimates(); showCustomNonce && (await this.setNetworkNonce()); - navigation.setParams({ providerType }); + navigation.setParams({ providerType, isPaymentRequest }); this.handleConfusables(); this.parseTransactionData(); this.prepareTransaction(); @@ -755,7 +758,7 @@ class Confirm extends PureComponent { this.getAnalyticsParams() ); resetTransaction(); - navigation && navigation.dismiss(); + navigation && navigation.dangerouslyGetParent()?.pop(); }); } catch (error) { Alert.alert(strings('transactions.transaction_error'), error && error.message, [ @@ -917,7 +920,7 @@ class Confirm extends PureComponent { buyEth = () => { const { navigation } = this.props; - navigation.navigate('PaymentMethodSelector'); + navigation.navigate('FiatOnRamp'); InteractionManager.runAfterInteractions(() => { Analytics.trackEvent(ANALYTICS_EVENT_OPTS.RECEIVE_OPTIONS_PAYMENT_REQUEST); }); @@ -1114,7 +1117,8 @@ const mapStateToProps = state => ({ transaction: getNormalizedTxState(state), selectedAsset: state.transaction.selectedAsset, transactionState: state.transaction, - primaryCurrency: state.settings.primaryCurrency + primaryCurrency: state.settings.primaryCurrency, + isPaymentRequest: state.transaction.paymentRequest }); const mapDispatchToProps = dispatch => ({ diff --git a/app/components/Views/SendFlow/SendTo/index.js b/app/components/Views/SendFlow/SendTo/index.js index bd0f0b7bd42..4497ff843c4 100644 --- a/app/components/Views/SendFlow/SendTo/index.js +++ b/app/components/Views/SendFlow/SendTo/index.js @@ -171,8 +171,7 @@ const dummy = () => true; * View that wraps the wraps the "Send" screen */ class SendFlow extends PureComponent { - static navigationOptions = ({ navigation, screenProps }) => - getSendFlowTitle('send.send_to', navigation, screenProps); + static navigationOptions = ({ navigation, route }) => getSendFlowTitle('send.send_to', navigation, route); static propTypes = { /** @@ -222,7 +221,15 @@ class SendFlow extends PureComponent { /** * Network provider type as mainnet */ - providerType: PropTypes.string + providerType: PropTypes.string, + /** + * Object that represents the current route info like params passed to it + */ + route: PropTypes.object, + /** + * Indicates whether the current transaction is a deep link transaction + */ + isPaymentRequest: PropTypes.bool }; addressToInputRef = React.createRef(); @@ -246,10 +253,20 @@ class SendFlow extends PureComponent { }; componentDidMount = async () => { - const { addressBook, selectedAddress, accounts, ticker, network, navigation, providerType } = this.props; + const { + addressBook, + selectedAddress, + accounts, + ticker, + network, + navigation, + providerType, + route, + isPaymentRequest + } = this.props; const { fromAccountName } = this.state; // For analytics - navigation.setParams({ providerType }); + navigation.setParams({ providerType, isPaymentRequest }); const networkAddressBook = addressBook[network] || {}; const ens = await doENSReverseLookup(selectedAddress, network); const fromAccountBalance = `${renderFromWei(accounts[selectedAddress].balance)} ${getTicker(ticker)}`; @@ -263,10 +280,12 @@ class SendFlow extends PureComponent { }); }, 100); if (!Object.keys(networkAddressBook).length) { - this.addressToInputRef && this.addressToInputRef.current && this.addressToInputRef.current.focus(); + setTimeout(() => { + this.addressToInputRef && this.addressToInputRef.current && this.addressToInputRef.current.focus(); + }, 500); } //Fills in to address and sets the transaction if coming from QR code scan - const targetAddress = navigation.getParam('txMeta', null)?.target_address; + const targetAddress = route.params?.txMeta?.target_address; if (targetAddress) { this.props.newAssetTransaction(getEther(ticker)); this.onToSelectedAddressChange(targetAddress); @@ -530,7 +549,7 @@ class SendFlow extends PureComponent { }; goToBuy = () => { - this.props.navigation.navigate('PaymentMethodSelector'); + this.props.navigation.navigate('FiatOnRamp'); InteractionManager.runAfterInteractions(() => { Analytics.trackEvent(ANALYTICS_EVENT_OPTS.WALLET_BUY_ETH); }); @@ -701,7 +720,8 @@ const mapStateToProps = state => ({ keyrings: state.engine.backgroundState.KeyringController.keyrings, ticker: state.engine.backgroundState.NetworkController.provider.ticker, network: state.engine.backgroundState.NetworkController.network, - providerType: state.engine.backgroundState.NetworkController.provider.type + providerType: state.engine.backgroundState.NetworkController.provider.type, + isPaymentRequest: state.transaction.paymentRequest }); const mapDispatchToProps = dispatch => ({ diff --git a/app/components/Views/Settings/AdvancedSettings/index.test.js b/app/components/Views/Settings/AdvancedSettings/index.test.js index b6d33c019da..3b9f993dbb9 100644 --- a/app/components/Views/Settings/AdvancedSettings/index.test.js +++ b/app/components/Views/Settings/AdvancedSettings/index.test.js @@ -18,16 +18,9 @@ describe('AdvancedSettings', () => { } }; - const wrapper = shallow( - , - { - context: { store: mockStore(initialState) } - } - ); + const wrapper = shallow(, { + context: { store: mockStore(initialState) } + }); expect(wrapper.dive()).toMatchSnapshot(); }); }); diff --git a/app/components/Views/Settings/AppInformation/__snapshots__/index.test.js.snap b/app/components/Views/Settings/AppInformation/__snapshots__/index.test.js.snap index 4d1713a6e22..e8a8c72fc99 100644 --- a/app/components/Views/Settings/AppInformation/__snapshots__/index.test.js.snap +++ b/app/components/Views/Settings/AppInformation/__snapshots__/index.test.js.snap @@ -2,11 +2,9 @@ exports[`AppInformation should render correctly 1`] = ` diff --git a/app/components/Views/Settings/AppInformation/index.js b/app/components/Views/Settings/AppInformation/index.js index 1a81896c065..13c4d942de0 100644 --- a/app/components/Views/Settings/AppInformation/index.js +++ b/app/components/Views/Settings/AppInformation/index.js @@ -97,8 +97,11 @@ export default class AppInformation extends PureComponent { goTo = (url, title) => { InteractionManager.runAfterInteractions(() => { this.props.navigation.navigate('Webview', { - url, - title + screen: 'SimpleWebview', + params: { + url, + title + } }); }); }; diff --git a/app/components/Views/Settings/AppInformation/index.test.js b/app/components/Views/Settings/AppInformation/index.test.js index 036c1e9d36e..43d84ca9946 100644 --- a/app/components/Views/Settings/AppInformation/index.test.js +++ b/app/components/Views/Settings/AppInformation/index.test.js @@ -11,7 +11,7 @@ describe('AppInformation', () => { it('should render correctly', () => { const wrapper = shallow( - + ); expect(wrapper).toMatchSnapshot(); diff --git a/app/components/Views/Settings/Contacts/ContactForm/index.js b/app/components/Views/Settings/Contacts/ContactForm/index.js index e4082c92eb6..bfb612fc60b 100644 --- a/app/components/Views/Settings/Contacts/ContactForm/index.js +++ b/app/components/Views/Settings/Contacts/ContactForm/index.js @@ -92,8 +92,8 @@ const EDIT = 'edit'; * View that contains app information */ class ContactForm extends PureComponent { - static navigationOptions = ({ navigation }) => - getEditableOptions(strings(`address_book.${navigation.getParam('mode', ADD)}_contact_title`), navigation); + static navigationOptions = ({ navigation, route }) => + getEditableOptions(strings(`address_book.${route.params?.mode ?? ADD}_contact_title`), navigation, route); static propTypes = { /** @@ -111,7 +111,11 @@ class ContactForm extends PureComponent { /** * Map representing the address book */ - addressBook: PropTypes.object + addressBook: PropTypes.object, + /** + * Object that represents the current route info like params passed to it + */ + route: PropTypes.object }; state = { @@ -120,7 +124,7 @@ class ContactForm extends PureComponent { addressError: undefined, toEnsName: undefined, addressReady: false, - mode: this.props.navigation.getParam('mode', ADD), + mode: this.props.route.params?.mode ?? ADD, memo: undefined, editable: true, inputWidth: Platform.OS === 'android' ? '99%' : undefined @@ -141,7 +145,7 @@ class ContactForm extends PureComponent { if (mode === EDIT) { const { addressBook, network, identities } = this.props; const networkAddressBook = addressBook[network] || {}; - const address = this.props.navigation.getParam('address', ''); + const address = this.props.route.params?.address ?? ''; const contact = networkAddressBook[address] || identities[address]; this.setState({ address, name: contact.name, memo: contact.memo, addressReady: true, editable: false }); navigation && navigation.setParams({ dispatch: this.onEdit, mode: EDIT }); @@ -229,9 +233,9 @@ class ContactForm extends PureComponent { deleteContact = () => { const { AddressBookController } = Engine.context; - const { network, navigation } = this.props; + const { network, navigation, route } = this.props; AddressBookController.delete(network, this.contactAddressToRemove); - navigation.state.params.onDelete(); + route.params.onDelete(); navigation.pop(); }; diff --git a/app/components/Views/Settings/Contacts/ContactForm/index.test.js b/app/components/Views/Settings/Contacts/ContactForm/index.test.js index 7403de8272b..96cb7fe7598 100644 --- a/app/components/Views/Settings/Contacts/ContactForm/index.test.js +++ b/app/components/Views/Settings/Contacts/ContactForm/index.test.js @@ -34,16 +34,9 @@ describe('ContactForm', () => { } } }; - const wrapper = shallow( - 'add' - }} - />, - { - context: { store: mockStore(initialState) } - } - ); + const wrapper = shallow(, { + context: { store: mockStore(initialState) } + }); expect(wrapper.dive()).toMatchSnapshot(); }); }); diff --git a/app/components/Views/Settings/Contacts/index.test.js b/app/components/Views/Settings/Contacts/index.test.js index 8a6ad400a50..84d1b7ccc30 100644 --- a/app/components/Views/Settings/Contacts/index.test.js +++ b/app/components/Views/Settings/Contacts/index.test.js @@ -26,16 +26,9 @@ describe('Contacts', () => { } } }; - const wrapper = shallow( - , - { - context: { store: mockStore(initialState) } - } - ); + const wrapper = shallow(, { + context: { store: mockStore(initialState) } + }); expect(wrapper.dive()).toMatchSnapshot(); }); }); diff --git a/app/components/Views/Settings/ExperimentalSettings/index.test.js b/app/components/Views/Settings/ExperimentalSettings/index.test.js index 5c186d0fd6b..b8ff602efe8 100644 --- a/app/components/Views/Settings/ExperimentalSettings/index.test.js +++ b/app/components/Views/Settings/ExperimentalSettings/index.test.js @@ -19,16 +19,9 @@ describe('ExperimentalSettings', () => { } }; - const wrapper = shallow( - , - { - context: { store: mockStore(initialState) } - } - ); + const wrapper = shallow(, { + context: { store: mockStore(initialState) } + }); expect(wrapper.dive()).toMatchSnapshot(); }); }); diff --git a/app/components/Views/Settings/GeneralSettings/index.test.js b/app/components/Views/Settings/GeneralSettings/index.test.js index d54d26c5a83..c5e8d9fb8de 100644 --- a/app/components/Views/Settings/GeneralSettings/index.test.js +++ b/app/components/Views/Settings/GeneralSettings/index.test.js @@ -21,16 +21,9 @@ describe('GeneralSettings', () => { } } }; - const wrapper = shallow( - , - { - context: { store: mockStore(initialState) } - } - ); + const wrapper = shallow(, { + context: { store: mockStore(initialState) } + }); expect(wrapper.dive()).toMatchSnapshot(); }); }); diff --git a/app/components/Views/Settings/NetworksSettings/NetworkSettings/index.js b/app/components/Views/Settings/NetworksSettings/NetworkSettings/index.js index a4042a18b69..5b186f19f17 100644 --- a/app/components/Views/Settings/NetworksSettings/NetworkSettings/index.js +++ b/app/components/Views/Settings/NetworksSettings/NetworkSettings/index.js @@ -95,7 +95,11 @@ class NetworkSettings extends PureComponent { /** * Object that represents the navigator */ - navigation: PropTypes.object + navigation: PropTypes.object, + /** + * Object that represents the current route info like params passed to it + */ + route: PropTypes.object }; static navigationOptions = ({ navigation }) => @@ -126,8 +130,8 @@ class NetworkSettings extends PureComponent { getOtherNetworks = () => allNetworks.slice(1); componentDidMount = () => { - const { navigation, frequentRpcList } = this.props; - const network = navigation.getParam('network', undefined); + const { route, frequentRpcList } = this.props; + const network = route.params?.network; let blockExplorerUrl, chainId, nickname, ticker, editable, rpcUrl; // If no navigation param, user clicked on add network if (network) { diff --git a/app/components/Views/Settings/NetworksSettings/NetworkSettings/index.test.js b/app/components/Views/Settings/NetworksSettings/NetworkSettings/index.test.js index 1d3d78b0161..9ccb20d77ce 100644 --- a/app/components/Views/Settings/NetworksSettings/NetworkSettings/index.test.js +++ b/app/components/Views/Settings/NetworksSettings/NetworkSettings/index.test.js @@ -14,19 +14,9 @@ describe('NetworkSettings', () => { } } }; - const wrapper = shallow( - { - 'network'; - } - }} - />, - { - context: { store: mockStore(initialState) } - } - ); + const wrapper = shallow(, { + context: { store: mockStore(initialState) } + }); expect(wrapper.dive()).toMatchSnapshot(); }); }); diff --git a/app/components/Views/Settings/NetworksSettings/index.test.js b/app/components/Views/Settings/NetworksSettings/index.test.js index 4db91f3c326..8a43e2c7133 100644 --- a/app/components/Views/Settings/NetworksSettings/index.test.js +++ b/app/components/Views/Settings/NetworksSettings/index.test.js @@ -18,19 +18,9 @@ describe('NetworksSettings', () => { thirdPartyApiMode: true } }; - const wrapper = shallow( - { - 'network'; - } - }} - />, - { - context: { store: mockStore(initialState) } - } - ); + const wrapper = shallow(, { + context: { store: mockStore(initialState) } + }); expect(wrapper.dive()).toMatchSnapshot(); }); }); diff --git a/app/components/Views/Settings/SecuritySettings/index.test.js b/app/components/Views/Settings/SecuritySettings/index.test.js index 90621104616..dbafe9ff15c 100644 --- a/app/components/Views/Settings/SecuritySettings/index.test.js +++ b/app/components/Views/Settings/SecuritySettings/index.test.js @@ -21,16 +21,9 @@ describe('SecuritySettings', () => { } }; - const wrapper = shallow( - , - { - context: { store: mockStore(initialState) } - } - ); + const wrapper = shallow(, { + context: { store: mockStore(initialState) } + }); expect(wrapper.dive()).toMatchSnapshot(); }); }); diff --git a/app/components/Views/Settings/index.test.js b/app/components/Views/Settings/index.test.js index 4d9f309c406..7933b37ee09 100644 --- a/app/components/Views/Settings/index.test.js +++ b/app/components/Views/Settings/index.test.js @@ -23,16 +23,9 @@ describe('Settings', () => { } } }; - const wrapper = shallow( - , - { - context: { store: mockStore(initialState) } - } - ); + const wrapper = shallow(, { + context: { store: mockStore(initialState) } + }); expect(wrapper.dive()).toMatchSnapshot(); }); }); diff --git a/app/components/Views/SimpleWebview/__snapshots__/index.test.js.snap b/app/components/Views/SimpleWebview/__snapshots__/index.test.js.snap index a98271893bc..157a89b97fa 100644 --- a/app/components/Views/SimpleWebview/__snapshots__/index.test.js.snap +++ b/app/components/Views/SimpleWebview/__snapshots__/index.test.js.snap @@ -19,10 +19,7 @@ exports[`SimpleWebview should render correctly 1`] = ` } source={ Object { - "uri": Object { - "title": "etherscan", - "url": "https://etherscan.io", - }, + "uri": "https://etherscan.io", } } useSharedProcessPool={true} diff --git a/app/components/Views/SimpleWebview/index.js b/app/components/Views/SimpleWebview/index.js index 666976cfe55..d037c603e6c 100644 --- a/app/components/Views/SimpleWebview/index.js +++ b/app/components/Views/SimpleWebview/index.js @@ -8,13 +8,17 @@ import Logger from '../../../util/Logger'; import { baseStyles } from '../../../styles/common'; export default class SimpleWebview extends PureComponent { - static navigationOptions = ({ navigation }) => getWebviewNavbar(navigation); + static navigationOptions = ({ navigation, route }) => getWebviewNavbar(navigation, route); static propTypes = { /** * react-navigation object used to switch between screens */ - navigation: PropTypes.object + navigation: PropTypes.object, + /** + * Object that represents the current route info like params passed to it + */ + route: PropTypes.object }; componentDidMount = () => { @@ -23,8 +27,8 @@ export default class SimpleWebview extends PureComponent { }; share = () => { - const { navigation } = this.props; - const url = navigation && navigation.getParam('url', null); + const { route } = this.props; + const url = route.params?.url; if (url) { Share.open({ url @@ -35,7 +39,7 @@ export default class SimpleWebview extends PureComponent { }; render() { - const uri = this.props.navigation.getParam('url', null); + const uri = this.props.route.params?.url; if (uri) { return ( diff --git a/app/components/Views/SimpleWebview/index.test.js b/app/components/Views/SimpleWebview/index.test.js index b4bd3ea0134..e0bc4bb59ec 100644 --- a/app/components/Views/SimpleWebview/index.test.js +++ b/app/components/Views/SimpleWebview/index.test.js @@ -7,11 +7,11 @@ describe('SimpleWebview', () => { const wrapper = shallow( ({ url: 'https://etherscan.io', title: 'etherscan' }), setParams: () => { ''; } }} + route={{ params: { url: 'https://etherscan.io', title: 'etherscan' } }} /> ); expect(wrapper).toMatchSnapshot(); diff --git a/app/components/Views/SyncWithExtensionSuccess/index.js b/app/components/Views/SyncWithExtensionSuccess/index.js index 915fc043bbc..7a4d72b56fc 100644 --- a/app/components/Views/SyncWithExtensionSuccess/index.js +++ b/app/components/Views/SyncWithExtensionSuccess/index.js @@ -109,9 +109,9 @@ class SyncWithExtensionSuccess extends PureComponent { setOnboardingWizardStep: PropTypes.func }; - static navigationOptions = ({ navigation }) => ({ - ...getOnboardingNavbarOptions(navigation), - headerLeft: + static navigationOptions = ({ navigation, route }) => ({ + ...getOnboardingNavbarOptions(navigation, route), + headerLeft: () => }); iconSpringVal = new Animated.Value(0.4); @@ -140,19 +140,22 @@ class SyncWithExtensionSuccess extends PureComponent { // Check if user passed through metrics opt-in screen const metricsOptIn = await AsyncStorage.getItem(METRICS_OPT_IN); if (!metricsOptIn) { - this.props.navigation.navigate('OptinMetrics'); + this.props.navigation.navigate('OnboardingNav', { screen: 'OptinMetrics' }); } else if (onboardingWizard) { this.props.navigation.navigate('HomeNav'); } else { this.props.setOnboardingWizardStep(1); - this.props.navigation.navigate('WalletView'); + this.props.navigation.navigate('HomeNav', { screen: 'WalletView' }); } }; learnMore = () => { this.props.navigation.navigate('Webview', { - url: 'https://metamask.zendesk.com/hc/en-us/articles/360015489591-Basic-Safety-Tips', - title: strings('drawer.metamask_support') + screen: 'SimpleWebview', + params: { + url: 'https://metamask.zendesk.com/hc/en-us/articles/360015489591-Basic-Safety-Tips', + title: strings('drawer.metamask_support') + } }); }; diff --git a/app/components/Views/TermsAndConditions/index.js b/app/components/Views/TermsAndConditions/index.js index 0d3fee372f6..6ba4fc7c223 100644 --- a/app/components/Views/TermsAndConditions/index.js +++ b/app/components/Views/TermsAndConditions/index.js @@ -31,8 +31,11 @@ export default class TermsAndConditions extends PureComponent { press = () => { const { navigation } = this.props; navigation.navigate('Webview', { - url: AppConstants.URLS.TERMS_AND_CONDITIONS, - title: strings('terms_and_conditions.title') + screen: 'SimpleWebview', + params: { + url: AppConstants.URLS.TERMS_AND_CONDITIONS, + title: strings('terms_and_conditions.title') + } }); }; diff --git a/app/components/Views/TransactionsView/index.js b/app/components/Views/TransactionsView/index.js index 22692b517ee..b3b189d5295 100644 --- a/app/components/Views/TransactionsView/index.js +++ b/app/components/Views/TransactionsView/index.js @@ -2,7 +2,7 @@ import React, { useState, useEffect, useCallback } from 'react'; import { StyleSheet, View, InteractionManager } from 'react-native'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; -import { withNavigation } from 'react-navigation'; +import { withNavigation } from '@react-navigation/compat'; import Engine from '../../../core/Engine'; import { showAlert } from '../../../actions/alert'; import Transactions from '../../UI/Transactions'; diff --git a/app/core/DeeplinkManager.js b/app/core/DeeplinkManager.js index cb025a67fa3..6d38b6cd7e3 100644 --- a/app/core/DeeplinkManager.js +++ b/app/core/DeeplinkManager.js @@ -38,15 +38,17 @@ class DeeplinkManager { const txMeta = { ...ethUrl, source: url }; if (ethUrl.parameters?.value) { this.navigation.navigate('SendView', { - txMeta: { ...txMeta, action: 'send-eth' } + screen: 'Send', + params: { txMeta: { ...txMeta, action: 'send-eth' } } }); } else { - this.navigation.navigate('SendFlowView', { txMeta }); + this.navigation.navigate('SendFlowView', { screen: 'SendTo', params: { txMeta } }); } } else if (functionName === 'transfer') { const txMeta = { ...ethUrl, source: url }; this.navigation.navigate('SendView', { - txMeta: { ...txMeta, action: 'send-token' } + screen: 'Send', + params: { txMeta: { ...txMeta, action: 'send-token' } } }); } else if (functionName === 'approve') { // add approve transaction diff --git a/app/util/general.js b/app/util/general.js index 6cc9e75f505..7bb6d945ca0 100644 --- a/app/util/general.js +++ b/app/util/general.js @@ -16,22 +16,24 @@ export function timeoutFetch(url, options, timeout = 500) { ]); } -export function findBottomTabRouteNameFromNavigatorState({ routes }) { +export function findRouteNameFromNavigatorState(routes) { let route = routes?.[routes.length - 1]; - let routeName; - while (route.index !== undefined) { - routeName = route?.routeName; - route = route?.routes?.[route.index]; + if (route.state) { + route = route.state; } - return routeName; -} - -export function findRouteNameFromNavigatorState({ routes }) { - let route = routes?.[routes.length - 1]; - while (route.index !== undefined) { + while (route !== undefined && route.index !== undefined) { route = route?.routes?.[route.index]; + if (route.state) { + route = route.state; + } } - return route?.routeName; + + let name = route?.name; + + // For compatibility with the previous way on react navigation 4 + if (name === 'Main' || name === 'WalletTabHome' || name === 'Home') name = 'WalletView'; + + return name; } export const capitalize = str => (str && str.charAt(0).toUpperCase() + str.slice(1)) || false; diff --git a/index.js b/index.js index 45e25584a01..abf68682f05 100644 --- a/index.js +++ b/index.js @@ -53,7 +53,8 @@ LogBox.ignoreLogs([ 'componentWillUpdate', 'componentWillReceiveProps', 'getNode()', - 'VirtualizedLists should never be nested inside plain ScrollViews' + 'VirtualizedLists should never be nested inside plain ScrollViews', + 'Non-serializable values were found in the navigation state.' // We are not saving navigation state so we can ignore this ]); /** diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 7d316fffffa..108291bb549 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -391,8 +391,9 @@ PODS: - React - RNReanimated (1.13.2): - React-Core - - RNScreens (2.16.1): + - RNScreens (3.4.0): - React-Core + - React-RCTImage - RNSensors (5.3.0): - React - RNSentry (2.4.2): @@ -715,7 +716,7 @@ SPEC CHECKSUMS: RNKeychain: b8e0711b959a19c5b057d1e970d3c83d159b6da5 RNOS: 6f2f9a70895bbbfbdad7196abd952e7b01d45027 RNReanimated: e03f7425cb7a38dcf1b644d680d1bfc91c3337ad - RNScreens: 45c457af3d2ee9e08fc01e70da87e653d46b1198 + RNScreens: 21b73c94c9117e1110a79ee0ee80c93ccefed8ce RNSensors: c363d486c879e181905dea84a2535e49af1c2d25 RNSentry: e86fb2e2fec0365644f4b582938bf66be515acce RNShare: 5cfe16bfd42cd2c4869a7692462181ac8cc15a6d @@ -726,6 +727,6 @@ SPEC CHECKSUMS: Yoga: 4bd86afe9883422a7c4028c00e34790f560923d6 YogaKit: f782866e155069a2cca2517aafea43200b01fd5a -PODFILE CHECKSUM: 434a61298c023532f98cdb537dcdecda8aae27b7 +PODFILE CHECKSUM: 5bb88f2ae6014e7faa76d3850e213eac8427ec6b COCOAPODS: 1.10.1 diff --git a/package.json b/package.json index d5be0bf4ee8..788d5b33d86 100644 --- a/package.json +++ b/package.json @@ -90,6 +90,11 @@ "@react-native-community/netinfo": "6.0.0", "@react-native-community/picker": "^1.8.1", "@react-native-community/viewpager": "^3.3.0", + "@react-navigation/bottom-tabs": "^5.11.11", + "@react-navigation/compat": "^5.3.15", + "@react-navigation/drawer": "^5.12.5", + "@react-navigation/native": "^5.9.4", + "@react-navigation/stack": "^5.14.5", "@rnhooks/keyboard": "^0.0.3", "@sentry/integrations": "6.3.1", "@sentry/react-native": "2.4.2", @@ -173,8 +178,9 @@ "react-native-qrcode-svg": "5.1.2", "react-native-randombytes": "^3.5.3", "react-native-reanimated": "^1.13.2", + "react-native-redash": "14.1.1", "react-native-safe-area-context": "^3.1.9", - "react-native-screens": "^2.16.1", + "react-native-screens": "^3.3.0", "react-native-scrollable-tab-view": "^1.0.0", "react-native-search-api": "ombori/react-native-search-api#8/head", "react-native-sensors": "5.3.0", @@ -190,10 +196,6 @@ "react-native-video": "^5.1.1", "react-native-view-shot": "^3.1.2", "react-native-webview": "^11.0.2", - "react-navigation": "^4.4.3", - "react-navigation-drawer": "1.4.0", - "react-navigation-stack": "1.7.3", - "react-navigation-tabs": "2.5.5", "react-redux": "5.1.1", "readable-stream": "1.0.33", "redux": "4.0.1", @@ -217,6 +219,8 @@ "@babel/runtime": "^7.8.4", "@metamask/mobile-provider": "^2.1.0", "@react-native-community/eslint-config": "^1.1.0", + "@types/react": "^17.0.11", + "@types/react-native": "^0.64.10", "assert": "1.4.1", "babel-core": "7.0.0-bridge.0", "babel-eslint": "10.1.0", @@ -228,6 +232,7 @@ "enzyme-to-json": "3.3.5", "eslint": "^7.14.0", "eslint-config-react-native": "4.0.0", + "eslint-import-resolver-typescript": "^2.4.0", "eslint-plugin-import": "2.18.2", "eslint-plugin-prettier": "^3.3.1", "eslint-plugin-react": "7.16.0", diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000000..6927955c75c --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,60 @@ +{ + "compilerOptions": { + /* Basic Options */ + "target": "esnext" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */, + "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */, + "lib": ["es2017"] /* Specify library files to be included in the compilation. */, + "allowJs": true /* Allow javascript files to be compiled. */, + // "checkJs": true, /* Report errors in .js files. */ + "jsx": "react-native" /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */, + // "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + // "outDir": "./", /* Redirect output structure to the directory. */ + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "removeComments": true, /* Do not emit comments to output. */ + "noEmit": true /* Do not emit outputs. */, + // "incremental": true, /* Enable incremental compilation */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + "isolatedModules": true /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */, + + /* Strict Type-Checking Options */ + "strict": true /* Enable all strict type-checking options. */, + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + + /* Module Resolution Options */ + "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */, + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + "allowSyntheticDefaultImports": true /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */, + "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + "skipLibCheck": false /* Skip type checking of declaration files. */ + + /* Source Map Options */ + // "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + }, + "exclude": ["node_modules", "babel.config.js", "metro.config.js", "jest.config.js"] +} diff --git a/yarn.lock b/yarn.lock index d32dbd23ec0..15ad3dfc474 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1824,9 +1824,9 @@ integrity sha512-W/J0fNYVO01tioHjvYWQ9m6RgndVtbElzYozBq1ZPrHO/iCzlqoySHl4gO/fpCl9QEFjvJfjPgtPMTMlsoq5DQ== "@react-native-community/masked-view@^0.1.10": - version "0.1.10" - resolved "https://registry.yarnpkg.com/@react-native-community/masked-view/-/masked-view-0.1.10.tgz#5dda643e19e587793bc2034dd9bf7398ad43d401" - integrity sha512-rk4sWFsmtOw8oyx8SD3KSvawwaK7gRBSEIy2TAwURyGt+3TizssXP1r8nx3zY+R7v2vYYHXZ+k2/GULAT/bcaQ== + version "0.1.11" + resolved "https://registry.yarnpkg.com/@react-native-community/masked-view/-/masked-view-0.1.11.tgz#2f4c6e10bee0786abff4604e39a37ded6f3980ce" + integrity sha512-rQfMIGSR/1r/SyN87+VD8xHHzDYeHaJq6elOSCAD+0iLagXkSI2pfA0LmSXP21uw5i3em7GkkRjfJ8wpqWXZNw== "@react-native-community/netinfo@6.0.0": version "6.0.0" @@ -1848,23 +1848,61 @@ resolved "https://registry.yarnpkg.com/@react-native-community/viewpager/-/viewpager-3.3.0.tgz#e613747a43a31a6f3278f817ba96fdaaa7941f23" integrity sha512-tyzh79l4t/hxiyS9QD3LRmWMs8KVkZzjrkQ8U8+8To1wmvVCBtp8BenvNsDLTBO7CpO/YmiThpmIdEZMr1WuVw== -"@react-navigation/core@^3.7.9": - version "3.7.9" - resolved "https://registry.yarnpkg.com/@react-navigation/core/-/core-3.7.9.tgz#3f7ba0fcb6c8d74a77a057382af198d84c7c4e3b" - integrity sha512-EknbzM8OI9A5alRxXtQRV5Awle68B+z1QAxNty5DxmlS3BNfmduWNGnim159ROyqxkuDffK9L/U/Tbd45mx+Jg== +"@react-navigation/bottom-tabs@^5.11.11": + version "5.11.11" + resolved "https://registry.yarnpkg.com/@react-navigation/bottom-tabs/-/bottom-tabs-5.11.11.tgz#ad4dfee4316522d8c05b5a8ad460f597bddb9e3c" + integrity sha512-hThj6Vfw+ITzAVj5TgLEoxkVEcBD+gYeieWOe6FryBRgokgKNCzFQzqArJ5UCmNMxklNH0rstJfcdyHflLuPtw== dependencies: - hoist-non-react-statics "^3.3.2" - path-to-regexp "^1.8.0" + color "^3.1.3" + react-native-iphone-x-helper "^1.3.0" + +"@react-navigation/compat@^5.3.15": + version "5.3.15" + resolved "https://registry.yarnpkg.com/@react-navigation/compat/-/compat-5.3.15.tgz#bdfc4a41be44aefa3ffb3ba5ec0307e40e97ab99" + integrity sha512-Gh5O6Ng3z0/qSBhuuWuPSi24+RrkgNy5hzvEke8qjS+kbrPfSFLD6aq9B2Fdfndp/dAyfxgTi6aULUsnv847Hw== + +"@react-navigation/core@^5.15.3": + version "5.15.3" + resolved "https://registry.yarnpkg.com/@react-navigation/core/-/core-5.15.3.tgz#dce7090bf3ea0d302993d742c706825e495b812e" + integrity sha512-3ZdyDInh8qg1kygCNkmh9lFgpDf29lTvPsaMe2mm/qvmxLKSgttWBz07P2fc181aV9jTdgQpzYfWZ5KWT036zw== + dependencies: + "@react-navigation/routers" "^5.7.2" + escape-string-regexp "^4.0.0" + nanoid "^3.1.15" query-string "^6.13.6" react-is "^16.13.0" -"@react-navigation/native@^3.8.3": - version "3.8.3" - resolved "https://registry.yarnpkg.com/@react-navigation/native/-/native-3.8.3.tgz#acaca7239059465220955767dafafb17f7b47e5e" - integrity sha512-1yLd2pi8SK3wPC58mWZ5fjW5uYr1gmMN8YwjkA2qVjyVYfzzctRkoFDu8poO5UzxEIgf/4ns6ezBtKY1Q601UQ== +"@react-navigation/drawer@^5.12.5": + version "5.12.5" + resolved "https://registry.yarnpkg.com/@react-navigation/drawer/-/drawer-5.12.5.tgz#463bd33b29bfcefaa474207b50c2bd3bac6dae79" + integrity sha512-WMfz/tKg/K7QBb5rhjXW/pho4zXh3OoHXnHETk5SuVzHlDPM7r84uvAeC5l+ySp5jmipLrJn3zL+kfv9+KKHZQ== dependencies: - hoist-non-react-statics "^3.3.2" - react-native-safe-area-view "^0.14.9" + color "^3.1.3" + react-native-iphone-x-helper "^1.3.0" + +"@react-navigation/native@^5.9.4": + version "5.9.4" + resolved "https://registry.yarnpkg.com/@react-navigation/native/-/native-5.9.4.tgz#414c044423c58aa1cdde1b6494309e0b51da08b8" + integrity sha512-BUCrOXfZDdKWBqM8OhOKQhCX5we4HUo5XG6tCQtVqQAep+7UcApZmMUuemUXDxVe8NPESUpoUlB0RaEpyIdfTQ== + dependencies: + "@react-navigation/core" "^5.15.3" + escape-string-regexp "^4.0.0" + nanoid "^3.1.15" + +"@react-navigation/routers@^5.7.2": + version "5.7.2" + resolved "https://registry.yarnpkg.com/@react-navigation/routers/-/routers-5.7.2.tgz#069d0a707b30ba2e27a32b6482531d0ff7317136" + integrity sha512-BxNSMLHpU+oS37Xok0ql6rc9U7IC8aUD4+U5ZPbjDJ0pwzZxGGh0YOEBzfV4k/Ig3cbPdvVWbc1C9HHbCVr2oQ== + dependencies: + nanoid "^3.1.15" + +"@react-navigation/stack@^5.14.5": + version "5.14.5" + resolved "https://registry.yarnpkg.com/@react-navigation/stack/-/stack-5.14.5.tgz#dc615cd7d270ba79e3330dcb50c2819d0e1f3850" + integrity sha512-hpdn1SS0tc3/3atkV2Q2y++n5B4e0rUcCj4W43PODMu72yX2m0LkKAAcpkPDCWAvwnLLIoLAEl5BEifZigl/6A== + dependencies: + color "^3.1.3" + react-native-iphone-x-helper "^1.3.0" "@rnhooks/keyboard@^0.0.3": version "0.0.3" @@ -2101,9 +2139,9 @@ "@types/node" "*" "@types/hammerjs@^2.0.36": - version "2.0.36" - resolved "https://registry.yarnpkg.com/@types/hammerjs/-/hammerjs-2.0.36.tgz#17ce0a235e9ffbcdcdf5095646b374c2bf615a4c" - integrity sha512-7TUK/k2/QGpEAv/BCwSHlYu3NXZhQ9ZwBYpzr9tjlPIL2C5BeGhH3DmVavRx3ZNyELX5TLC91JTz/cen6AAtIQ== + version "2.0.39" + resolved "https://registry.yarnpkg.com/@types/hammerjs/-/hammerjs-2.0.39.tgz#4be64bbacf3813c79c0dab895c6b0fdc7d5e513f" + integrity sha512-lYR2Y/tV2ujpk/WyUc7S0VLI0a9hrtVIN9EwnrNo5oSEJI2cK2/XrgwOQmXLL3eTulOESvh9qP6si9+DWM9cOA== "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.2" @@ -2137,6 +2175,11 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339" integrity sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA== +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= + "@types/node@*": version "14.0.5" resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.5.tgz#3d03acd3b3414cf67faf999aed11682ed121f22b" @@ -2164,6 +2207,32 @@ resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-1.19.1.tgz#33509849f8e679e4add158959fdb086440e9553f" integrity sha512-5qOlnZscTn4xxM5MeGXAMOsIOIKIbh9e85zJWfBRVPlRMEVawzoPhINYbRGkBZCI8LxvBe7tJCdWiarA99OZfQ== +"@types/prop-types@*": + version "15.7.3" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" + integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== + +"@types/react-native@^0.64.10": + version "0.64.10" + resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.64.10.tgz#5eb6a72c77ce0f7e6e14b19c61a6bc585975eef5" + integrity sha512-3Kb9QM5/WZ6p58yZ7VPbvjvi6Wc/ZkESgJhKso1gKkNuHBe/4WL6586R2JRDiz9Tsxal9lMnbj3fligBVGl8PA== + dependencies: + "@types/react" "*" + +"@types/react@*", "@types/react@^17.0.11": + version "17.0.11" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.11.tgz#67fcd0ddbf5a0b083a0f94e926c7d63f3b836451" + integrity sha512-yFRQbD+whVonItSk7ZzP/L+gPTJVBkL/7shLEF+i9GC/1cV3JmUxEQz6+9ylhUpWSDuqo1N9qEvqS6vTj4USUA== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + +"@types/scheduler@*": + version "0.16.1" + resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.1.tgz#18845205e86ff0038517aab7a18a62a6b9f71275" + integrity sha512-EaCxbanVeyxDRTQBkdLb3Bvl/HK7PBK6UJjsSixB0iHKoWxE5uu2Q/DgtpOhPIojN0Zl1whvOd7PoHs2P0s5eA== + "@types/secp256k1@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@types/secp256k1/-/secp256k1-4.0.1.tgz#fb3aa61a1848ad97d7425ff9dcba784549fca5a4" @@ -2331,6 +2400,11 @@ abort-controller@^3.0.0: dependencies: event-target-shim "^5.0.0" +abs-svg-path@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/abs-svg-path/-/abs-svg-path-0.1.1.tgz#df601c8e8d2ba10d4a76d625e236a9a39c2723bf" + integrity sha1-32Acjo0roQ1KdtYl4japo5wnI78= + absolute-path@^0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/absolute-path/-/absolute-path-0.0.0.tgz#a78762fbdadfb5297be99b15d35a785b2f095bf7" @@ -3803,6 +3877,14 @@ color-string@^1.5.2: color-name "^1.0.0" simple-swizzle "^0.2.2" +color-string@^1.5.4: + version "1.5.5" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.5.tgz#65474a8f0e7439625f3d27a6a19d89fc45223014" + integrity sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg== + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + color-support@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" @@ -3816,6 +3898,14 @@ color@^3.0.0: color-convert "^1.9.1" color-string "^1.5.2" +color@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/color/-/color-3.1.3.tgz#ca67fb4e7b97d611dcde39eceed422067d91596e" + integrity sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ== + dependencies: + color-convert "^1.9.1" + color-string "^1.5.4" + colorette@^1.0.7: version "1.2.0" resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.0.tgz#45306add826d196e8c87236ac05d797f25982e63" @@ -4062,9 +4152,9 @@ cross-fetch@^2.1.0: whatwg-fetch "2.0.4" cross-fetch@^3.0.4: - version "3.0.6" - resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.0.6.tgz#3a4040bc8941e653e0e9cf17f29ebcd177d3365c" - integrity sha512-KBPUbqgFjzWlVcURG+Svp9TlhA5uliYtiNx/0r8nv0pdypeQCRJ9IaSIc3q/x3q8t3F75cHuwxVql1HFGHCNJQ== + version "3.1.4" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.4.tgz#9723f3a3a247bf8b89039f3a380a9244e8fa2f39" + integrity sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ== dependencies: node-fetch "2.6.1" @@ -4165,6 +4255,11 @@ cssstyle@^2.0.0: dependencies: cssom "~0.3.6" +csstype@^3.0.2: + version "3.0.8" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.8.tgz#d2266a792729fb227cd216fb572f43728e1ad340" + integrity sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw== + cycle@1.0.x: version "1.0.3" resolved "https://registry.yarnpkg.com/cycle/-/cycle-1.0.3.tgz#21e80b2be8580f98b468f379430662b046c34ad2" @@ -4833,6 +4928,11 @@ escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + escodegen@1.x.x, escodegen@^1.11.1: version "1.14.1" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.1.tgz#ba01d0c8278b5e95a9a45350142026659027a457" @@ -4880,6 +4980,17 @@ eslint-import-resolver-node@^0.3.2: debug "^2.6.9" resolve "^1.13.1" +eslint-import-resolver-typescript@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.4.0.tgz#ec1e7063ebe807f0362a7320543aaed6fe1100e1" + integrity sha512-useJKURidCcldRLCNKWemr1fFQL1SzB3G4a0li6lFGvlc5xGe1hY343bvG07cbpCzPuM/lK19FIJB3XGFSkplA== + dependencies: + debug "^4.1.1" + glob "^7.1.6" + is-glob "^4.0.1" + resolve "^1.17.0" + tsconfig-paths "^3.9.0" + eslint-module-utils@^2.4.0, eslint-module-utils@^2.4.1: version "2.6.0" resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz#579ebd094f56af7797d19c9866c9c9486629bfa6" @@ -6899,11 +7010,6 @@ hmac-drbg@^1.0.1: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" -hoist-non-react-statics@^2.3.1: - version "2.5.5" - resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47" - integrity sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw== - hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" @@ -8331,6 +8437,13 @@ json-stringify-safe@~5.0.1: resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= +json5@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" + integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== + dependencies: + minimist "^1.2.0" + json5@^2.1.2: version "2.1.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43" @@ -9654,6 +9767,11 @@ nanoid@^3.1.12: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788" integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw== +nanoid@^3.1.15: + version "3.1.23" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.23.tgz#f744086ce7c2bc47ee0a8472574d5c78e4183a81" + integrity sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw== + nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -9810,6 +9928,13 @@ normalize-path@^3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== +normalize-svg-path@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/normalize-svg-path/-/normalize-svg-path-1.1.0.tgz#0e614eca23c39f0cffe821d6be6cd17e569a766c" + integrity sha512-r9KHKG2UUeB5LoTouwDzBy2VxXlHsiM6fyLQvnJa0S5hrhzqElH/CH7TUGhT1fVvIYBIKf3OpY4YJ4CK+iaqHg== + dependencies: + svg-arc-to-cubic-bezier "^3.0.0" + npm-run-path@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" @@ -10267,6 +10392,11 @@ parse-node-version@^1.0.0: resolved "https://registry.yarnpkg.com/parse-node-version/-/parse-node-version-1.0.1.tgz#e2b5dbede00e7fa9bc363607f53327e8b073189b" integrity sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA== +parse-svg-path@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/parse-svg-path/-/parse-svg-path-0.1.2.tgz#7a7ec0d1eb06fa5325c7d3e009b859a09b5d49eb" + integrity sha1-en7A0esG+lMlx9PgCbhZoJtdSes= + parse5@5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.0.tgz#c59341c9723f414c452975564c7c00a68d58acd2" @@ -10342,13 +10472,6 @@ path-parse@^1.0.6: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== -path-to-regexp@^1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" - integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA== - dependencies: - isarray "0.0.1" - path-type@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" @@ -10906,7 +11029,7 @@ react-is@^16.12.0, react-is@^16.13.0, react-is@^16.6.0, react-is@^16.7.0, react- resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== -react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.4: +react-lifecycles-compat@^3.0.0: version "3.0.4" resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== @@ -11073,6 +11196,11 @@ react-native-iphone-x-helper@^1.0.3, react-native-iphone-x-helper@^1.2.0: resolved "https://registry.yarnpkg.com/react-native-iphone-x-helper/-/react-native-iphone-x-helper-1.2.1.tgz#645e2ffbbb49e80844bb4cbbe34a126fda1e6772" integrity sha512-/VbpIEp8tSNNHIvstuA3Swx610whci1Zpc9mqNkqn14DkMbw+ORviln2u0XyHG1kPvvwTNGZY6QpeFwxYaSdbQ== +react-native-iphone-x-helper@^1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/react-native-iphone-x-helper/-/react-native-iphone-x-helper-1.3.1.tgz#20c603e9a0e765fd6f97396638bdeb0e5a60b010" + integrity sha512-HOf0jzRnq2/aFUcdCJ9w9JGzN3gdEg0zFE4FyYlp4jtidqU03D5X7ZegGKfT1EWteR0gPBGp9ye5T5FvSWi9Yg== + react-native-jazzicon@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/react-native-jazzicon/-/react-native-jazzicon-0.1.2.tgz#af0bd041685644ce283c0a330e3cf639d470720a" @@ -11150,23 +11278,25 @@ react-native-randombytes@^3.5.3: sjcl "^1.0.3" react-native-reanimated@^1.13.2: - version "1.13.2" - resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-1.13.2.tgz#1ae5457b24b4913d173a5a064bb28eae7783d293" - integrity sha512-O+WhgxSjOIzcVdAAvx+h2DY331Ek1knKlaq+jsNLpC1fhRy9XTdOObovgob/aF2ve9uJfPEawCx8381g/tUJZQ== + version "1.13.3" + resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-1.13.3.tgz#fb0e1d582c8866015140f2159e147ffe6e798a3a" + integrity sha512-i714H24dv6ncpFO7/SZ0PfAMbvjgVbF8Ow2NPtowoZAz8osS54DmTMrkgJ9Za+uEku/s0AEaxqiXG2Xgntvv2g== dependencies: fbjs "^1.0.0" -react-native-safe-area-context@^3.1.9: - version "3.1.9" - resolved "https://registry.yarnpkg.com/react-native-safe-area-context/-/react-native-safe-area-context-3.1.9.tgz#48864ea976b0fa57142a2cc523e1fd3314e7247e" - integrity sha512-wmcGbdyE/vBSL5IjDPReoJUEqxkZsywZw5gPwsVUV1NBpw5eTIdnL6Y0uNKHE25Z661moxPHQz6kwAkYQyorxA== - -react-native-safe-area-view@^0.14.6, react-native-safe-area-view@^0.14.9: - version "0.14.9" - resolved "https://registry.yarnpkg.com/react-native-safe-area-view/-/react-native-safe-area-view-0.14.9.tgz#90ee8383037010d9a5055a97cf97e4c1da1f0c3d" - integrity sha512-WII/ulhpVyL/qbYb7vydq7dJAfZRBcEhg4/UWt6F6nAKpLa3gAceMOxBxI914ppwSP/TdUsandFy6lkJQE0z4A== +react-native-redash@14.1.1: + version "14.1.1" + resolved "https://registry.yarnpkg.com/react-native-redash/-/react-native-redash-14.1.1.tgz#b3a45652160746df8766bcb7251f7747c5863934" + integrity sha512-xjTmEEnDKXa7DlGhKpL7HUksSGS6RFY8duig1YWrADnatTKBiwRBTymLJplqs9jE9rSOEeqISRYhtkB9iRlfRA== dependencies: - hoist-non-react-statics "^2.3.1" + abs-svg-path "^0.1.1" + normalize-svg-path "^1.0.1" + parse-svg-path "^0.1.2" + +react-native-safe-area-context@^3.1.9: + version "3.2.0" + resolved "https://registry.yarnpkg.com/react-native-safe-area-context/-/react-native-safe-area-context-3.2.0.tgz#06113c6b208f982d68ab5c3cebd199ca93db6941" + integrity sha512-k2Nty4PwSnrg9HwrYeeE+EYqViYJoOFwEy9LxL5RIRfoqxAq/uQXNGwpUg2/u4gnKpBbEPa9eRh15KKMe/VHkA== react-native-safe-module@^1.1.0: version "1.2.0" @@ -11175,10 +11305,12 @@ react-native-safe-module@^1.1.0: dependencies: dedent "^0.6.0" -react-native-screens@^2.16.1: - version "2.16.1" - resolved "https://registry.yarnpkg.com/react-native-screens/-/react-native-screens-2.16.1.tgz#b105a127378d90018a46daf0c2f6518fca60c06f" - integrity sha512-WZ7m0sBDVaHbBnlHxwQnUlI6KNfQKHq+Unfw+VBuAlnSXvT+aw6Bb/K2bUlHzBgvrPjwY3Spc7ZERFuTwRLLwg== +react-native-screens@^3.3.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/react-native-screens/-/react-native-screens-3.4.0.tgz#91deeac7630db9f3984053e2ab146d71bba7af4e" + integrity sha512-cg+q9MRnVdeOcJyvJtqffoXLur/C2wHA/7IO2+FAipzTlgHbbM1mTuSM7qG+SeiQjoIs4mHOEf7A0ziPKW04sA== + dependencies: + warn-once "^0.1.0" react-native-scrollable-tab-view@^1.0.0: version "1.0.0" @@ -11228,18 +11360,6 @@ react-native-swipe-gestures@1.0.3: resolved "https://registry.yarnpkg.com/react-native-swipe-gestures/-/react-native-swipe-gestures-1.0.3.tgz#4160f8d459627323f3a3d2770af4bcd82fd054f5" integrity sha512-KOouRzPB2fHFjVombsSdRfYo8SFeNVa4Ho4B5il87DuuF26sPNOtb3je+qaT/xVptedOsCzRPJGbWFMsaBApgg== -react-native-tab-view@^1.2.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/react-native-tab-view/-/react-native-tab-view-1.4.1.tgz#f113cd87485808f0c991abec937f70fa380478b9" - integrity sha512-Bke8KkDcDhvB/z0AS7MnQKMD2p6Kwfc1rSKlMOvg9CC5CnClQ2QEnhPSbwegKDYhUkBI92iH/BYy7hNSm5kbUQ== - dependencies: - prop-types "^15.6.1" - -react-native-tab-view@^2.9.0: - version "2.14.2" - resolved "https://registry.yarnpkg.com/react-native-tab-view/-/react-native-tab-view-2.14.2.tgz#5f1e2063f632ec7f8ab27374ecaf72677788b445" - integrity sha512-BkQ9htmUM3iroMTgIdCSP+1On8bRHkgFHiOCAnk9OhoFlQOvwZebSU6k705QBDeuuwEVIIT4+C+nT2MvXI/FEQ== - react-native-tcp@aprock/react-native-tcp#11/head: version "4.0.0" resolved "https://codeload.github.com/aprock/react-native-tcp/tar.gz/98fbc801f0586297f16730b2f4c75eef15dfabcd" @@ -11329,38 +11449,6 @@ react-native@^0.63.4: use-subscription "^1.0.0" whatwg-fetch "^3.0.0" -react-navigation-drawer@1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/react-navigation-drawer/-/react-navigation-drawer-1.4.0.tgz#70f3dd83e3da9cd4ea6e2739526502c823d466b9" - integrity sha512-ZyWBozcjB2aZ7vwCALv90cYA2NpDjM+WALaiYRshvPvue8l7cqynePbHK8GhlMGyJDwZqp4MxQmu8u1XAKp3Bw== - dependencies: - react-native-tab-view "^1.2.0" - -react-navigation-stack@1.7.3: - version "1.7.3" - resolved "https://registry.yarnpkg.com/react-navigation-stack/-/react-navigation-stack-1.7.3.tgz#2dce28bdc80bbd2bf09755a6aa7200055a907504" - integrity sha512-wOt7T5NkIFInnFw+cxkUHUbNrXbPqascScia6azMWSdHhx+gvz3uW4Ubyw+2NULHcoshU53boUuQ8lUmUrJdhg== - dependencies: - prop-types "^15.7.2" - -react-navigation-tabs@2.5.5: - version "2.5.5" - resolved "https://registry.yarnpkg.com/react-navigation-tabs/-/react-navigation-tabs-2.5.5.tgz#f651355b140b35ef5753aac434da5e1943abdd26" - integrity sha512-oIL5V4agCxcqbWNZzF1h/cm1bxKXNUeGrWaRQEEnuN3TXTEj1SVRz33CnKYg30pVvgF5L2p28sOk15Z4Ao01NQ== - dependencies: - hoist-non-react-statics "^3.3.0" - react-lifecycles-compat "^3.0.4" - react-native-safe-area-view "^0.14.6" - react-native-tab-view "^2.9.0" - -react-navigation@^4.4.3: - version "4.4.3" - resolved "https://registry.yarnpkg.com/react-navigation/-/react-navigation-4.4.3.tgz#41a83583e7ee07c9c9e0fb8ee0508261f239086c" - integrity sha512-tNBQQzbw0PVo9FLypQUUCISMcXW0wCW8oQeHtY0spWf35KC3IZHq/WcBm4E956wFsaqrDMGCUnyaVrxZNSuUGg== - dependencies: - "@react-navigation/core" "^3.7.9" - "@react-navigation/native" "^3.8.3" - react-redux@5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.1.1.tgz#88e368682c7fa80e34e055cd7ac56f5936b0f52f" @@ -12842,6 +12930,11 @@ supports-hyperlinks@^2.0.0: has-flag "^4.0.0" supports-color "^7.0.0" +svg-arc-to-cubic-bezier@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/svg-arc-to-cubic-bezier/-/svg-arc-to-cubic-bezier-3.2.0.tgz#390c450035ae1c4a0104d90650304c3bc814abe6" + integrity sha512-djbJ/vZKZO+gPoSDThGNpKDO+o+bAeA4XQKovvkNCqnIS2t+S4qnLAGQhyyrulhCFRl1WWzAp0wUDV8PpTVU3g== + symbol-observable@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.1.tgz#8340fc4702c3122df5d22288f88283f513d3fdd4" @@ -13069,6 +13162,16 @@ truncate-utf8-bytes@^1.0.0: dependencies: utf8-byte-length "^1.0.1" +tsconfig-paths@^3.9.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b" + integrity sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw== + dependencies: + "@types/json5" "^0.0.29" + json5 "^1.0.1" + minimist "^1.2.0" + strip-bom "^3.0.0" + tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.13.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" @@ -13460,6 +13563,11 @@ walker@^1.0.7, walker@~1.0.5: dependencies: makeerror "1.0.x" +warn-once@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/warn-once/-/warn-once-0.1.0.tgz#4f58d89b84f968d0389176aa99e0cf0f14ffd4c8" + integrity sha512-recZTSvuaH/On5ZU5ywq66y99lImWqzP93+AiUo9LUwG8gXHW+LJjhOd6REJHm7qb0niYqrEQJvbHSQfuJtTqA== + wcwidth@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8"