diff --git a/src/store/app/app.effects.ts b/src/store/app/app.effects.ts index 1d7717512e..8339610b41 100644 --- a/src/store/app/app.effects.ts +++ b/src/store/app/app.effects.ts @@ -618,6 +618,43 @@ export const startInAppNotification = dispatch(AppActions.showInAppNotification(context, _message, request)); }; +/** + * Dismiss the native InAppBrowser, but only if we actually opened one via + * openUrlWithInAppBrowser (tracked in APP.inAppBrowserOpen). Some flows (e.g. + * Buy/Sell) render their own in-app WebView modal + * instead of the native IAB. Calling InAppBrowser.close() unconditionally + * (e.g. whenever a bitpay:// deep link is received) can dismiss whatever modal happens to be + * presented at that moment - including that unrelated WebView modal - since + * the underlying native module simply dismisses the topmost presented + * screen rather than a specific tracked IAB session. + */ +export const dismissInAppBrowserIfOpen = + (): Effect => async (dispatch, getState) => { + const {APP} = getState(); + if (!APP.inAppBrowserOpen) { + logManager.debug( + '[dismissInAppBrowserIfOpen] IAB not open, skipping close', + ); + return; + } + logManager.debug( + '[dismissInAppBrowserIfOpen] IAB is open, attempting close...', + ); + try { + const isAvailable = await InAppBrowser.isAvailable(); + if (isAvailable) { + await InAppBrowser.close(); + } + } catch (err) { + const errStr = err instanceof Error ? err.message : JSON.stringify(err); + logManager.error( + '[dismissInAppBrowserIfOpen] Error closing IAB: ' + errStr, + ); + } finally { + dispatch(AppActions.setInAppBrowserOpen(false)); + } + }; + /** * Open a URL with the InAppBrowser if available, else lets the device handle the URL. * @param url diff --git a/src/utils/hooks/useDeeplinks.ts b/src/utils/hooks/useDeeplinks.ts index 9f0cfd365b..139cfbf847 100644 --- a/src/utils/hooks/useDeeplinks.ts +++ b/src/utils/hooks/useDeeplinks.ts @@ -7,7 +7,6 @@ import { import {useMemo, useRef} from 'react'; import {DeviceEventEmitter, Linking, NativeModules} from 'react-native'; import AppsFlyer from 'react-native-appsflyer'; -import InAppBrowser from 'react-native-inappbrowser-reborn'; import { APP_CRYPTO_PREFIX, APP_DEEPLINK_PREFIX, @@ -21,8 +20,10 @@ import {navigationRef, RootStackParamList, RootStacks} from '../../Root'; import {TabsScreens, TabsStackParamList} from '../../navigation/tabs/TabsStack'; import {incomingData} from '../../store/scan/scan.effects'; import {showBlur} from '../../store/app/app.actions'; -import {AppActions} from '../../store/app'; -import {incomingLink} from '../../store/app/app.effects'; +import { + dismissInAppBrowserIfOpen, + incomingLink, +} from '../../store/app/app.effects'; import useAppDispatch from './useAppDispatch'; import {useLogger} from './useLogger'; import {DebugScreens} from '../../navigation/Debug'; @@ -126,18 +127,7 @@ export const useUrlEventHandler = () => { } } - try { - // clicking a deeplink from the IAB in iOS doesn't auto-close the IAB, so do it manually - InAppBrowser.isAvailable().then(isAvailable => { - if (isAvailable) { - InAppBrowser.close(); - dispatch(AppActions.setInAppBrowserOpen(false)); - } - }); - } catch (err) { - const errStr = err instanceof Error ? err.message : JSON.stringify(err); - logger.error('[deeplink] not available from IAB: ' + errStr); - } + dispatch(dismissInAppBrowserIfOpen()); return handled; }