diff --git a/src/actions/LoginActions.tsx b/src/actions/LoginActions.tsx index cc6673b4032..669d82a4982 100644 --- a/src/actions/LoginActions.tsx +++ b/src/actions/LoginActions.tsx @@ -86,7 +86,10 @@ export function initializeAccount(navigation: NavigationBase, account: EdgeAccou // during account creation await readLocalAccountSettings(account) - const newAccountFlow = async (navigation: NavigationProp<'createWalletSelectCrypto'>, items: WalletCreateItem[]) => { + const newAccountFlow = async ( + navigation: NavigationProp<'createWalletSelectCrypto' | 'createWalletSelectCryptoNewAccount'>, + items: WalletCreateItem[] + ) => { navigation.replace('edgeTabs', { screen: 'home' }) const createWalletsPromise = createCustomWallets(account, fiatCurrencyCode, items, dispatch).catch(error => showError(error)) @@ -114,7 +117,13 @@ export function initializeAccount(navigation: NavigationBase, account: EdgeAccou performance.mark('loginEnd', { detail: { isNewAccount: newAccount } }) } else { - rootNavigation.replace('edgeApp', {}) + rootNavigation.replace('edgeApp', { + screen: 'edgeAppStack', + params: { + screen: 'edgeTabs', + params: { screen: 'home' } + } + }) referralPromise.catch(() => console.log(`Failed to load account referral info`)) performance.mark('loginEnd', { detail: { isNewAccount: newAccount } }) diff --git a/src/components/Main.tsx b/src/components/Main.tsx index e69c3409ad2..bc7d9a9a609 100644 --- a/src/components/Main.tsx +++ b/src/components/Main.tsx @@ -20,7 +20,17 @@ import { RewardsCardDashboardScene as RewardsCardListSceneComponent } from '../p import { RewardsCardWelcomeScene as RewardsCardWelcomeSceneComponent } from '../plugins/gui/scenes/RewardsCardWelcomeScene' import { SepaFormScene } from '../plugins/gui/scenes/SepaFormScene' import { useDispatch, useSelector } from '../types/reactRedux' -import { AppParamList, EdgeSceneProps, NavigationBase } from '../types/routerTypes' +import { + BuyTabParamList, + DrawerParamList, + EdgeAppStackParamList, + EdgeSceneProps, + EdgeTabsParamList, + NavigationBase, + RootParamList, + SwapTabParamList, + WalletsTabParamList +} from '../types/routerTypes' import { isMaestro } from '../util/maestro' import { logEvent } from '../util/tracking' import { ifLoggedIn } from './hoc/IfLoggedIn' @@ -209,9 +219,13 @@ const WcDisconnectScene = ifLoggedIn(WcDisconnectSceneComponent) const WebViewScene = ifLoggedIn(WebViewSceneComponent) const HomeScene = ifLoggedIn(HomeSceneComponent) -const Drawer = createDrawerNavigator() -const Stack = createStackNavigator() -const Tab = createBottomTabNavigator() +const RootStack = createStackNavigator() +const Drawer = createDrawerNavigator() +const AppStack = createStackNavigator() +const Tabs = createBottomTabNavigator() +const SwapStack = createStackNavigator() +const BuyStack = createStackNavigator() +const WalletsStack = createStackNavigator() const headerMode = isMaestro() && Platform.OS === 'android' ? 'float' : undefined @@ -238,29 +252,29 @@ const firstSceneScreenOptions: StackNavigationOptions & BottomTabNavigationOptio const EdgeWalletsTabScreen = () => { return ( - - + }} /> - - + fromParams={params => params.walletName} /> }} /> - + ) } const EdgeBuyTabScreen = () => { return ( - - - + + { headerLeft: () => }} /> - null }} /> - null }} /> - null }} /> - null }} /> - - - - + + + + ) } const EdgeSellTabScreen = () => { return ( - - - + + { headerLeft: () => }} /> - null }} /> - null }} /> - null }} /> - null }} /> - - - - + + + + ) } const EdgeSwapTabScreen = () => { return ( - - + { title: lstrings.title_exchange }} /> - - + { headerRight: () => null }} /> - + ) } @@ -381,21 +395,21 @@ const EdgeTabs = () => { const initialRouteName = defaultScreen === 'assets' ? 'walletsTab' : 'home' return ( - } screenOptions={{ headerShown: false }} > - - - - - - - - + + + + + + + + ) } @@ -406,8 +420,8 @@ const EdgeTabs = () => { const EdgeAppStack = () => { return ( - - + { }} /> - }} /> - { headerRight: () => null }} /> - { headerRight: () => null }} /> - - - - + + + { headerRight: () => }} /> - { headerRight: () => }} /> - { headerRight: () => null }} /> - null }} /> - null }} /> - - + { headerLeft: () => null }} /> - - + { headerRight: () => null }} /> - { headerRight: () => null }} /> - { headerRight: () => null }} /> - null }} /> - - + null }} /> - { headerRight: () => null }} /> - null }} /> - }} /> - - - - + + + null }} /> - { headerLeft: () => null }} /> - - - + + - null }} /> - - + { headerRight: () => null }} /> - - + null }} /> - - - + + null }} /> - - - - - - - - - - + + + + + + + + + { }} /> - - - - + + + { headerRight: () => null }} /> - - + { headerRight: () => null }} /> - - + { headerRight: () => null }} /> - - + { headerRight: () => null }} /> - { headerRight: () => null }} /> - - { headerLeft: () => }} /> - { headerRight: () => null }} /> - - - - + + + - { headerRight: () => null }} /> - - - - + + + }} /> - { headerRight: () => null }} /> - - - - + + + fromParams={params => params.title} /> }} /> - + ) } @@ -827,28 +841,28 @@ export const Main = () => { ) : ( - - + - + {(props: EdgeSceneProps<'gettingStarted'>) => { - if (navigation == null) setTimeout(() => setNavigation(props.navigation), 0) + if (navigation == null) setTimeout(() => setNavigation(props.navigation as NavigationBase), 0) return }} - + - + {(props: EdgeSceneProps<'login'>) => { - if (navigation == null) setTimeout(() => setNavigation(props.navigation), 0) + if (navigation == null) setTimeout(() => setNavigation(props.navigation as NavigationBase), 0) return }} - - + + {navigation == null ? null : } )} diff --git a/src/components/scenes/GuiPluginViewScene.tsx b/src/components/scenes/GuiPluginViewScene.tsx index 243e000b1d2..35dacfddd68 100644 --- a/src/components/scenes/GuiPluginViewScene.tsx +++ b/src/components/scenes/GuiPluginViewScene.tsx @@ -3,7 +3,7 @@ import * as React from 'react' import { checkAndShowLightBackupModal } from '../../actions/BackupModalActions' import { GuiPlugin } from '../../types/GuiPluginTypes' import { useSelector } from '../../types/reactRedux' -import { EdgeSceneProps } from '../../types/routerTypes' +import { NavigationProp } from '../../types/routerTypes' import { UriQueryMap } from '../../types/WebTypes' import { SceneWrapper } from '../common/SceneWrapper' import { EdgeProviderComponent } from '../themed/EdgeProviderComponent' @@ -17,7 +17,12 @@ export interface PluginViewParams { deepQuery?: UriQueryMap } -interface Props extends EdgeSceneProps<'pluginView' | 'pluginViewBuy' | 'pluginViewSell'> {} +interface Props { + navigation: NavigationProp<'pluginView' | 'pluginViewBuy' | 'pluginViewSell'> + + // Work around an extremely strange & hard-to-debug type error: + route: any // RouteProp<'pluginView' | 'pluginViewBuy' | 'pluginViewSell'> +} export function GuiPluginViewScene(props: Props): JSX.Element { const { route, navigation } = props diff --git a/src/components/scenes/HomeScene.tsx b/src/components/scenes/HomeScene.tsx index 7a95e418812..9ff93e8d899 100644 --- a/src/components/scenes/HomeScene.tsx +++ b/src/components/scenes/HomeScene.tsx @@ -76,16 +76,16 @@ export const HomeScene = (props: Props) => { // const handleBuyPress = useHandler(() => { - navigation.navigate('buyTab', {}) + navigation.navigate('buyTab') }) const handleSellPress = useHandler(() => { - navigation.navigate('sellTab', {}) + navigation.navigate('sellTab') }) const handleFioPress = useHandler(() => { navigation.navigate('fioAddressList') }) const handleSwapPress = useHandler(() => { - navigation.navigate('swapTab', {}) + navigation.navigate('swapTab') }) const handleViewAssetsPress = useHandler(() => { navigation.navigate('walletsTab', { screen: 'walletList' }) diff --git a/src/hooks/useAsyncNavigation.tsx b/src/hooks/useAsyncNavigation.tsx index 582bfff09f4..4939b46888d 100644 --- a/src/hooks/useAsyncNavigation.tsx +++ b/src/hooks/useAsyncNavigation.tsx @@ -1,14 +1,14 @@ import { NavigationProp as NavigationCoreProp, StackActionHelpers } from '@react-navigation/native' import * as React from 'react' -import { AppParamList, NavigationBase, RouteParamList } from '../types/routerTypes' +import { AppParamList, NavigationBase } from '../types/routerTypes' /** * Use this in place of NavigationProp/NavigationBase methods to prevent * multiple navigations from rapid tapping. Navigation calls are only executed * if there isn't already another one in flight */ -export const useAsyncNavigation = ( +export const useAsyncNavigation = ( navigation: NavigationBase | (NavigationCoreProp & StackActionHelpers) ): NavigationBase & (NavigationCoreProp & StackActionHelpers) => { const [isNavigating, setIsNavigating] = React.useState(false) diff --git a/src/plugins/gui/fiatPlugin.tsx b/src/plugins/gui/fiatPlugin.tsx index 7847584b378..d7809514470 100644 --- a/src/plugins/gui/fiatPlugin.tsx +++ b/src/plugins/gui/fiatPlugin.tsx @@ -86,7 +86,7 @@ export const executePlugin = async (params: { function maybeNavigateToCorrectTabScene() { const navPath = getNavigationAbsolutePath(navigation) if (!navPath.includes(`/edgeTabs/${tabSceneKey}`)) { - navigation.navigate(tabSceneKey, {}) + navigation.navigate(tabSceneKey) navigation.navigate(listSceneKey, {}) } } diff --git a/src/types/routerTypes.tsx b/src/types/routerTypes.tsx index 81a12c782cd..2cbf067a0e8 100644 --- a/src/types/routerTypes.tsx +++ b/src/types/routerTypes.tsx @@ -1,3 +1,4 @@ +import type { NavigatorScreenParams } from '@react-navigation/core' import * as NavigationCore from '@react-navigation/core' import type { StackActionHelpers } from '@react-navigation/native' @@ -68,20 +69,20 @@ import type { FiatPluginOpenWebViewParams } from '../plugins/gui/scenes/FiatPlug import type { RewardsCardDashboardParams } from '../plugins/gui/scenes/RewardsCardDashboardScene' import type { RewardsCardWelcomeParams } from '../plugins/gui/scenes/RewardsCardWelcomeScene' -/** - * Defines the acceptable route parameters for each scene key. - */ -export interface RouteParamList { - // ------------------------------------------------------------------------- - // Tab router - // ------------------------------------------------------------------------- +// ------------------------------------------------------------------------- +// Router types +// +// These must all be `type`, not `interface`, because of +// https://reactnavigation.org/docs/typescript#type-checking-the-navigator +// ------------------------------------------------------------------------- - // `walletsTab`: +export type WalletsTabParamList = {} & { walletList: undefined transactionList: TransactionListParams transactionDetails: TransactionDetailsParams +} - // `buyTab` / `sellTab`: +export type BuyTabParamList = {} & { pluginListBuy: GuiPluginListParams | undefined pluginListSell: GuiPluginListParams | undefined pluginViewBuy: PluginViewParams @@ -93,27 +94,28 @@ export interface RouteParamList { guiPluginWebView: FiatPluginOpenWebViewParams rewardsCardDashboard: RewardsCardDashboardParams rewardsCardWelcome: RewardsCardWelcomeParams +} - // `swapTab`: +export type SwapTabParamList = {} & { swapCreate: SwapCreateParams | undefined swapConfirmation: SwapConfirmationParams swapProcessing: SwapProcessingParams +} - // `edgeTabs`: +export type EdgeTabsParamList = {} & { home: undefined - walletsTab: {} - buyTab: {} - sellTab: {} - swapTab: {} + walletsTab: NavigatorScreenParams | undefined + buyTab: NavigatorScreenParams | undefined + sellTab: NavigatorScreenParams | undefined + swapTab: NavigatorScreenParams | undefined extraTab: undefined devTab: undefined +} - // ------------------------------------------------------------------------- - // Main `edgeAppStack` - // The tabs live inside this stack, as well as most app scenes. - // ------------------------------------------------------------------------- +export type EdgeAppStackParamList = {} & { + // We nest the tabs inside this master stack: + edgeTabs: NavigatorScreenParams - edgeTabs: {} // Tab navigator assetSettings: undefined changeMiningFee2: ChangeMiningFeeParams changePassword: undefined @@ -134,6 +136,7 @@ export interface RouteParamList { defaultFiatSetting: undefined edgeLogin: EdgeLoginParams editToken: EditTokenParams + extraTab: undefined fioAddressDetails: FioAddressDetailsParams fioAddressList: undefined fioAddressRegister: undefined @@ -185,32 +188,46 @@ export interface RouteParamList { sweepPrivateKeyProcessing: SweepPrivateKeyProcessingParams sweepPrivateKeySelectCrypto: SweepPrivateKeySelectCryptoParams testScene: undefined - // transactionDetails is copied here + transactionDetails: TransactionDetailsParams transactionsExport: TransactionsExportParams upgradeUsername: undefined wcConnect: WcConnectParams - wcConnections: WcConnectionsParams | undefined + wcConnections: WcConnectionsParams wcDisconnect: WcDisconnectParams webView: WebViewSceneParams +} - // ------------------------------------------------------------------------- - // Root router - // ------------------------------------------------------------------------- - - // `edgeApp`: - edgeAppStack: {} +// A drawer router that contains the main `edgeAppStack` +export type DrawerParamList = {} & { + edgeAppStack: NavigatorScreenParams | undefined +} - // Root routes: - edgeApp: {} // A drawer router that contains the main `edgeAppStack` +export type RootParamList = {} & { + edgeApp: NavigatorScreenParams | undefined gettingStarted: GettingStartedParams login: LoginParams } -export type RouteSceneKey = keyof RouteParamList - -export type AppParamList = { - [key in RouteSceneKey]: RouteParamList[key] -} +// ------------------------------------------------------------------------- +// Legacy types +// +// These are a giant hack to smooth away the differences +// between different navigation objects. +// They pretend that any navigator to visit any scene, +// as if the whole app were flat. That's not how react-navigation works, +// but it's "close enough" until we can utilize the proper types +// defined above. +// ------------------------------------------------------------------------- + +export type AppParamList = RootParamList & + DrawerParamList & + EdgeAppStackParamList & + EdgeTabsParamList & + SwapTabParamList & + BuyTabParamList & + WalletsTabParamList + +export type RouteSceneKey = keyof AppParamList /** * The of the `navigation` prop passed to each scene, diff --git a/src/util/DeepLinkParser.ts b/src/util/DeepLinkParser.ts index f9ada6afb49..8c2d8f4a949 100644 --- a/src/util/DeepLinkParser.ts +++ b/src/util/DeepLinkParser.ts @@ -5,7 +5,7 @@ import { guiPlugins } from '../constants/plugins/GuiPlugins' import { ENV } from '../env' import { asFiatDirection, asFiatPaymentType } from '../plugins/gui/fiatPluginTypes' import { asModalNames, DeepLink, PromotionLink } from '../types/DeepLinkTypes' -import { RouteParamList } from '../types/routerTypes' +import { AppParamList } from '../types/routerTypes' import { parseQuery, stringifyQuery } from './WebUtils' /** @@ -146,7 +146,7 @@ function parseEdgeProtocol(url: URL): DeepLink { case 'scene': { const sceneName = url.pathname.replace('/', '') - return { type: 'scene', sceneName: sceneName as keyof RouteParamList, query: parseQuery(url.query) } + return { type: 'scene', sceneName: sceneName as keyof AppParamList, query: parseQuery(url.query) } } case 'swap': {