diff --git a/apps/wallet-mobile/android/app/src/main/AndroidManifest.xml b/apps/wallet-mobile/android/app/src/main/AndroidManifest.xml index 2737d66c55..4b9dc445eb 100644 --- a/apps/wallet-mobile/android/app/src/main/AndroidManifest.xml +++ b/apps/wallet-mobile/android/app/src/main/AndroidManifest.xml @@ -21,15 +21,13 @@ - + - - - + diff --git a/apps/wallet-mobile/index.js b/apps/wallet-mobile/index.js index 6f7b0b0843..bf8437e34c 100644 --- a/apps/wallet-mobile/index.js +++ b/apps/wallet-mobile/index.js @@ -4,11 +4,13 @@ import 'react-native-gesture-handler' // required by react-navigation import './global' import './src/i18n/polyfills' // https://formatjs.io/docs/polyfills import BigNumber from 'bignumber.js' +import {enableMapSet} from 'immer' import {AppRegistry} from 'react-native' import {name} from './app.json' import {YoroiApp} from './src/YoroiApp' +enableMapSet() BigNumber.config({DECIMAL_PLACES: 19, EXPONENTIAL_AT: [-10, 40]}) AppRegistry.registerComponent(name, () => YoroiApp) diff --git a/apps/wallet-mobile/package.json b/apps/wallet-mobile/package.json index 92753fb9c5..10bef414ca 100644 --- a/apps/wallet-mobile/package.json +++ b/apps/wallet-mobile/package.json @@ -99,7 +99,7 @@ "@emurgo/csl-mobile-bridge": "6.0.0-alpha.4", "@emurgo/react-native-blockies-svg": "^0.0.2", "@emurgo/react-native-hid": "^5.15.6", - "@emurgo/yoroi-lib": "0.15.0", + "@emurgo/yoroi-lib": "0.15.1", "@formatjs/intl-datetimeformat": "^6.7.0", "@formatjs/intl-getcanonicallocales": "^2.1.0", "@formatjs/intl-locale": "^3.2.1", @@ -125,7 +125,7 @@ "@yoroi/api": "1.5.1", "@yoroi/common": "1.5.1", "@yoroi/exchange": "2.0.0", - "@yoroi/links": "1.5.1", + "@yoroi/links": "1.5.3", "@yoroi/resolver": "2.0.4", "@yoroi/staking": "1.5.1", "@yoroi/swap": "1.5.2", diff --git a/apps/wallet-mobile/src/AppNavigator.tsx b/apps/wallet-mobile/src/AppNavigator.tsx index ed0f138b04..d34c68b5d9 100644 --- a/apps/wallet-mobile/src/AppNavigator.tsx +++ b/apps/wallet-mobile/src/AppNavigator.tsx @@ -15,6 +15,7 @@ import {ModalProvider} from './components/Modal/ModalContext' import {ModalScreen} from './components/Modal/ModalScreen' import {AgreementChangedNavigator, InitializationNavigator} from './features/Initialization' import {LegalAgreement, useLegalAgreement} from './features/Initialization/common' +import {useDeepLinkWatcher} from './features/Links/common/useDeepLinkWatcher' import {CONFIG, LINKING_CONFIG, LINKING_PREFIXES} from './legacy/config' import {DeveloperScreen} from './legacy/DeveloperScreen' import {AppRoutes} from './navigation' @@ -27,9 +28,11 @@ const Stack = createStackNavigator() const navRef = React.createRef>() export const AppNavigator = () => { + useDeepLinkWatcher() const strings = useStrings() const [routeName, setRouteName] = React.useState() + // NOTE: mind some routes are handled by the event listener const enableDeepLink = routeName === 'history-list' const linking = { diff --git a/apps/wallet-mobile/src/IntialLinkManagerProvider.tsx b/apps/wallet-mobile/src/IntialLinkManagerProvider.tsx deleted file mode 100644 index 7acd78bb23..0000000000 --- a/apps/wallet-mobile/src/IntialLinkManagerProvider.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import * as React from 'react' -import {Linking} from 'react-native' - -const IntialLinkContext = React.createContext< - {initialUrl: string | null; setInitialUrl: (initialUrl: string | null) => void} | undefined ->(undefined) - -export const InitialLinkProvider: React.FC = ({children}) => { - const [initialUrl, setInitialUrl] = React.useState(null) - - // app is open - React.useEffect(() => { - const getUrl = ({url}: {url: string | null}) => { - if (url !== null) setInitialUrl(url) - } - Linking.addEventListener('url', getUrl) - return () => Linking.removeAllListeners('url') - }, []) - - // app is closed - React.useEffect(() => { - const getInitialURL = async () => { - const url = await Linking.getInitialURL() - if (url !== null) setInitialUrl(url) - } - - getInitialURL() - }, []) - - return {children} -} - -export const useInitialLink = () => React.useContext(IntialLinkContext) || missingProvider() - -const missingProvider = () => { - throw new Error('InitialLinkProvider is missing') -} diff --git a/apps/wallet-mobile/src/TxHistory/TxHistoryNavigator.tsx b/apps/wallet-mobile/src/TxHistory/TxHistoryNavigator.tsx index 9cae0ac17c..db4051dd6c 100644 --- a/apps/wallet-mobile/src/TxHistory/TxHistoryNavigator.tsx +++ b/apps/wallet-mobile/src/TxHistory/TxHistoryNavigator.tsx @@ -24,7 +24,6 @@ import {Boundary, Icon, Spacer} from '../components' import {claimApiMaker} from '../features/Claim/module/api' import {ClaimProvider} from '../features/Claim/module/ClaimProvider' import {ShowSuccessScreen} from '../features/Claim/useCases/ShowSuccessScreen' -import {useNavigateTo} from '../features/Exchange/common/useNavigateTo' import {CreateExchangeOrderScreen} from '../features/Exchange/useCases/CreateExchangeOrderScreen/CreateExchangeOrderScreen' import {SelectProviderFromListScreen} from '../features/Exchange/useCases/SelectProviderFromListScreen/SelectProviderFromListScreen' import {ShowExchangeResultOrderScreen} from '../features/Exchange/useCases/ShowExchangeResultOrderScreen/ShowExchangeResultOrderScreen' @@ -126,8 +125,6 @@ export const TxHistoryNavigator = () => { // navigator components const headerRightHistory = React.useCallback(() => , []) - const navigateTo = useNavigateTo() - // exchange const exchangeManager = React.useMemo(() => { const api = exchangeApiMaker({ @@ -254,9 +251,8 @@ export const TxHistoryNavigator = () => { headerShown: false, }} name="exchange-result" - > - {() => } - + component={ShowExchangeResultOrderScreen} + /> { const Stack = createStackNavigator() export const WalletNavigator = () => { - const {initialUrl} = useInitialLink() - const {resetToWalletSelection} = useWalletNavigation() + const initialRouteName = useInitialRouteName() + + // initialRoute doens't update the state of the navigator, only at first render + // https://reactnavigation.org/docs/auth-flow/ + if (initialRouteName === 'exchange-result') { + return ( + + + + ) + } return ( - - {() => } - - @@ -187,6 +198,13 @@ export const WalletNavigator = () => { ) } +const useInitialRouteName = () => { + const {action} = useLinks() + const routeName: keyof WalletStackRoutes = + action?.info.useCase === 'order/show-create-result' ? 'exchange-result' : 'wallet-selection' + return routeName +} + const messages = defineMessages({ transactionsButton: { id: 'components.common.navigation.transactionsButton', diff --git a/apps/wallet-mobile/src/YoroiApp.tsx b/apps/wallet-mobile/src/YoroiApp.tsx index cbaae8db6e..e117eca827 100644 --- a/apps/wallet-mobile/src/YoroiApp.tsx +++ b/apps/wallet-mobile/src/YoroiApp.tsx @@ -1,4 +1,5 @@ import {AsyncStorageProvider} from '@yoroi/common' +import {LinksProvider} from '@yoroi/links' import {ThemeProvider} from '@yoroi/theme' import React from 'react' import {LogBox, Platform, StyleSheet, UIManager} from 'react-native' @@ -14,7 +15,6 @@ import {ErrorBoundary} from './components/ErrorBoundary' import {CurrencyProvider} from './features/Settings/Currency/CurrencyContext' import {LanguageProvider} from './i18n' import {InitApp} from './InitApp' -import {InitialLinkProvider} from './IntialLinkManagerProvider' import {CONFIG} from './legacy/config' import {setLogLevel} from './legacy/logging' import {makeMetricsManager, MetricsProvider} from './metrics/metricsManager' @@ -62,9 +62,9 @@ export const YoroiApp = () => { - + - + diff --git a/apps/wallet-mobile/src/features/Exchange/common/useNavigateTo.tsx b/apps/wallet-mobile/src/features/Exchange/common/useNavigateTo.tsx index fea0659455..a8227d7193 100644 --- a/apps/wallet-mobile/src/features/Exchange/common/useNavigateTo.tsx +++ b/apps/wallet-mobile/src/features/Exchange/common/useNavigateTo.tsx @@ -35,13 +35,3 @@ export const useNavigateTo = () => { }), }).current } - -export type ExchangeInitRoutes = { - exchange: { - coin: string - coinAmount: number - fiat: number - fiatAmount: number - status: string - } -} diff --git a/apps/wallet-mobile/src/features/Exchange/useCases/ShowExchangeResultOrderScreen/ShowExchangeResultOrderScreen.stories.tsx b/apps/wallet-mobile/src/features/Exchange/useCases/ShowExchangeResultOrderScreen/ShowExchangeResultOrderScreen.stories.tsx index c037568dbb..e3cd5506b5 100644 --- a/apps/wallet-mobile/src/features/Exchange/useCases/ShowExchangeResultOrderScreen/ShowExchangeResultOrderScreen.stories.tsx +++ b/apps/wallet-mobile/src/features/Exchange/useCases/ShowExchangeResultOrderScreen/ShowExchangeResultOrderScreen.stories.tsx @@ -1,73 +1,54 @@ -import {NavigationRouteContext, RouteProp} from '@react-navigation/native' -import {actions} from '@storybook/addon-actions' import {storiesOf} from '@storybook/react-native' import {exchangeDefaultState, ExchangeProvider, successManagerMock} from '@yoroi/exchange' -import {produce} from 'immer' +import {LinksProvider} from '@yoroi/links' import React from 'react' import {ModalProvider} from '../../../../components' -import {InitialLinkProvider, useInitialLink} from '../../../../IntialLinkManagerProvider' import {SelectedWalletProvider} from '../../../../SelectedWallet' import {mocks as walletMocks} from '../../../../yoroi-wallets/mocks' -import {ExchangeInitRoutes} from '../../common/useNavigateTo' import {ShowExchangeResultOrderScreen} from './ShowExchangeResultOrderScreen' -storiesOf('Exchange ShowExchangeResultOrderScreen', module) - .addDecorator((story) => {story()}) - .add('no params', () => ) - .add('no info', () => actions('onClose')} />) - .add('with params', () => ) - -const Init = () => { - const {setInitialUrl} = useInitialLink() - const initialState = produce(exchangeDefaultState, (draft) => { - draft.orderType = 'buy' - }) - - React.useEffect(() => { - setInitialUrl( - 'yoroi://ramp-on-off/result?&status=success&fiatAmount=10000&coinAmount=5000&coin=ADA&fiat=USD&provider=encryptus', - ) - }, [setInitialUrl]) +storiesOf('Exchange ShowExchangeResultOrderScreen', module).add('with deep link data', () => ) +const WithDeepLinkData = () => { return ( - - actions('onClose')} /> - + + + + + ) } - -const WithParams = () => { - const {setInitialUrl} = useInitialLink() - const params: RouteProp['params'] = { - coin: 'ADA', - coinAmount: 100, - fiat: 990, - fiatAmount: 199, - status: 'success', - } - const initialState = produce(exchangeDefaultState, (draft) => { - draft.orderType = 'buy' - }) - - React.useEffect(() => { - setInitialUrl( - 'yoroi://ramp-on-off/result?&status=success&fiatAmount=10000&coinAmount=5000&coin=ADA&fiat=USD&provider=encryptus', - ) - }, [setInitialUrl]) - return ( - - - - - actions('onClose')} /> - - - - - ) -} diff --git a/apps/wallet-mobile/src/features/Exchange/useCases/ShowExchangeResultOrderScreen/ShowExchangeResultOrderScreen.tsx b/apps/wallet-mobile/src/features/Exchange/useCases/ShowExchangeResultOrderScreen/ShowExchangeResultOrderScreen.tsx index 7b62d7a7f8..162b01e922 100644 --- a/apps/wallet-mobile/src/features/Exchange/useCases/ShowExchangeResultOrderScreen/ShowExchangeResultOrderScreen.tsx +++ b/apps/wallet-mobile/src/features/Exchange/useCases/ShowExchangeResultOrderScreen/ShowExchangeResultOrderScreen.tsx @@ -1,3 +1,5 @@ +import {exchangeApiMaker, exchangeManagerMaker, ExchangeProvider} from '@yoroi/exchange' +import {LinksYoroiExchangeShowCreateResultParams, useLinks} from '@yoroi/links' import {useTheme} from '@yoroi/theme' import * as React from 'react' import {StyleSheet, TouchableOpacity, View} from 'react-native' @@ -5,7 +7,7 @@ import {SafeAreaView} from 'react-native-safe-area-context' import {Button, Icon, Spacer, Text, useModal} from '../../../../components' import {useStatusBar} from '../../../../components/hooks/useStatusBar' -import {useInitialLink} from '../../../../IntialLinkManagerProvider' +import {useWalletNavigation} from '../../../../navigation' import {DescribeAction} from '../../common/DescribeAction/DescribeAction' import {useStrings} from '../../common/useStrings' import {BanxaLogo} from '../../illustrations/BanxaLogo' @@ -13,82 +15,99 @@ import {EncryptusLogo} from '../../illustrations/EncryptusLogo' import {WalletAssetImage} from '../../illustrations/WalletAssetImage' import {ContentResult} from './ContentResult/ContentResult' -export const ShowExchangeResultOrderScreen = ({onClose}: {onClose: () => void}) => { +export const ShowExchangeResultOrderScreen = () => { const strings = useStrings() const styles = useStyles() const {openModal} = useModal() + const {resetToWalletSelection} = useWalletNavigation() + const {action, actionFinished} = useLinks() - const {setInitialUrl, initialUrl} = useInitialLink() + // exchange + const exchangeManager = React.useMemo(() => { + const api = exchangeApiMaker({ + isProduction: action?.info?.params?.isSandbox !== true, + partner: 'yoroi', + }) + + const manager = exchangeManagerMaker({api}) + return manager + }, [action?.info?.params?.isSandbox]) + + // NOTE: should never happen, caller should handle it + if (action == null || action.info.useCase !== 'order/show-create-result') return null + const params: LinksYoroiExchangeShowCreateResultParams = action.info.params const handleOnClose = () => { - setInitialUrl(null) - onClose() + actionFinished() + resetToWalletSelection() } const handleOnShowDetails = () => { openModal(strings.buySellCrypto, ) } - const {showOrderDetails, params, Logo, name, showProviderDetails} = sanitizeParams(initialUrl) + const {showOrderDetails, Logo, name, showProviderDetails} = sanitizeParams(params) return ( - - - + + + + + + + + + {strings.congrats} - + {showOrderDetails && ( + <> + + + + + + + )} + - - {strings.congrats} + {showOrderDetails && ( <> - + + {`${params?.coinAmount ?? 0} ${params?.coin ?? ''}`} + - - - + + + + {`${params?.fiatAmount ?? 0} ${params?.fiat ?? ''}`} + )} - - - - {showOrderDetails && ( - <> - - {`${params?.coinAmount ?? 0} ${params?.coin ?? ''}`} - - - - - - {`${params?.fiatAmount ?? 0} ${params?.fiat ?? ''}`} - - - )} + {showProviderDetails && ( + <> + - {showProviderDetails && ( - <> - + + + - - - + - + {name} + + + + )} + - {name} - - - - )} - - - -