mirror of
https://github.com/minibits-cash/minibits_wallet.git
synced 2026-10-05 11:18:24 +00:00
Render BottomModal in-tree via portal to fix edge-to-edge nav bar
Keep edgeToEdgeEnabled=true so the themed header draws behind the status
bar on pre-Android-15 devices (e.g. Moto G4). Android 15+ is edge-to-edge
OS-enforced regardless.
That flag also routes react-native Modal's native dialog window through RN
core's enableEdgeToEdge() (WindowUtil.kt), which hardcodes nav-bar contrast
enforcement and derives appearance from device dark mode, painting a grey
(white-in-dark) scrim on modal open/close. That code ships in the prebuilt
RN Android AAR and can't be patched.
Work around it without disabling the flag: on Android, BottomModal renders
in the app's React tree via @gorhom/portal (coverScreen={false}) instead of
as a native window, so it never hits that code path. A PortalProvider at the
app root keeps the modal overlaying the bottom tab bar. iOS keeps the native
modal path unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
e36bd02bf8
commit
cafef4f462
@@ -41,5 +41,19 @@ hermesEnabled=true
|
|||||||
# Use this property to enable edge-to-edge display support.
|
# Use this property to enable edge-to-edge display support.
|
||||||
# This allows your app to draw behind system bars for an immersive UI.
|
# This allows your app to draw behind system bars for an immersive UI.
|
||||||
# Note: Only works with ReactActivity and should not be used with custom Activity.
|
# Note: Only works with ReactActivity and should not be used with custom Activity.
|
||||||
|
#
|
||||||
|
# Kept TRUE. This makes the themed header draw behind the status bar on pre-Android-15
|
||||||
|
# devices (e.g. Moto G4) too (Android 15+ is edge-to-edge OS-enforced regardless).
|
||||||
|
#
|
||||||
|
# Heads-up: TRUE also forces react-native Modal's separate dialog *window* through RN
|
||||||
|
# core's enableEdgeToEdge() (WindowUtil.kt), which hardcodes isNavigationBarContrast-
|
||||||
|
# Enforced=true and derives nav-bar appearance from DEVICE dark mode — painting a grey
|
||||||
|
# (or white-in-dark) scrim behind the nav bar on modal open/close. That code is in the
|
||||||
|
# prebuilt RN Android AAR (unpatchable without building RN from source). We worked
|
||||||
|
# around it WITHOUT turning this flag off: BottomModal renders in-tree via @gorhom/portal
|
||||||
|
# (coverScreen={false}) on Android instead of as a native window, so it never hits that
|
||||||
|
# code path. Any NEW Android modal should use BottomModal / the same portal pattern
|
||||||
|
# rather than a raw native react-native Modal, or the scrim will come back.
|
||||||
|
# See project memory: android-edge-to-edge-modal-tradeoff.
|
||||||
edgeToEdgeEnabled=true
|
edgeToEdgeEnabled=true
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
"@gandlaf21/bc-ur": "^1.1.12",
|
"@gandlaf21/bc-ur": "^1.1.12",
|
||||||
"@gandlaf21/bolt11-decode": "^3.1.1",
|
"@gandlaf21/bolt11-decode": "^3.1.1",
|
||||||
"@gocodingnow/rn-size-matters": "^0.0.4",
|
"@gocodingnow/rn-size-matters": "^0.0.4",
|
||||||
|
"@gorhom/portal": "^1.0.14",
|
||||||
"@hot-updater/react-native": "0.32.0",
|
"@hot-updater/react-native": "0.32.0",
|
||||||
"@notifee/react-native": "^9.1.8",
|
"@notifee/react-native": "^9.1.8",
|
||||||
"@op-engineering/op-sqlite": "^16.2.0",
|
"@op-engineering/op-sqlite": "^16.2.0",
|
||||||
|
|||||||
+9
-4
@@ -9,6 +9,7 @@ import {
|
|||||||
} from '@gocodingnow/rn-size-matters'
|
} from '@gocodingnow/rn-size-matters'
|
||||||
import RNExitApp from 'react-native-exit-app'
|
import RNExitApp from 'react-native-exit-app'
|
||||||
import { changeIcon } from 'react-native-change-icon'
|
import { changeIcon } from 'react-native-change-icon'
|
||||||
|
import { PortalProvider } from '@gorhom/portal'
|
||||||
import {AppNavigator} from './navigation'
|
import {AppNavigator} from './navigation'
|
||||||
import {useInitialRootStore, useStores} from './models'
|
import {useInitialRootStore, useStores} from './models'
|
||||||
import {KeyChain, WalletKeys} from './services'
|
import {KeyChain, WalletKeys} from './services'
|
||||||
@@ -242,10 +243,14 @@ function App() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaProvider>
|
<SafeAreaProvider>
|
||||||
<ErrorBoundary catchErrors={Config.catchErrors}>
|
{/* PortalProvider hosts in-tree modals (BottomModal on Android) above the
|
||||||
<AppNavigator />
|
navigator + bottom tab bar, avoiding react-native Modal's native window. */}
|
||||||
<FlashMessage position="bottom" />
|
<PortalProvider>
|
||||||
</ErrorBoundary>
|
<ErrorBoundary catchErrors={Config.catchErrors}>
|
||||||
|
<AppNavigator />
|
||||||
|
<FlashMessage position="bottom" />
|
||||||
|
</ErrorBoundary>
|
||||||
|
</PortalProvider>
|
||||||
</SafeAreaProvider>
|
</SafeAreaProvider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import React, { ComponentType, Fragment, ReactElement, useEffect } from "react"
|
import React, { ComponentType, Fragment, ReactElement, useEffect } from "react"
|
||||||
import Modal from 'react-native-modal'
|
import Modal from 'react-native-modal'
|
||||||
|
import { Portal } from '@gorhom/portal'
|
||||||
import {
|
import {
|
||||||
StyleProp,
|
StyleProp,
|
||||||
TextStyle,
|
TextStyle,
|
||||||
@@ -169,11 +170,20 @@ export function BottomModal(props: ModalProps) {
|
|||||||
// const keyboard = useAnimatedKeyboard()
|
// const keyboard = useAnimatedKeyboard()
|
||||||
//log.trace({keyboard})
|
//log.trace({keyboard})
|
||||||
|
|
||||||
|
// On Android, render the modal inside the app's React tree via a Portal instead of as a
|
||||||
|
// native Dialog window. The native-window path forces RN core's enableEdgeToEdge() (when
|
||||||
|
// the edge-to-edge flag is on), which paints a buggy nav-bar contrast scrim on open/close.
|
||||||
|
// coverScreen={false} keeps the modal in-tree; the Portal hoists it above the bottom tab
|
||||||
|
// bar so the backdrop still covers the whole screen. iOS keeps the native modal path
|
||||||
|
// (no such issue there, and its keyboard avoidance depends on it).
|
||||||
|
const ModalWrapper = Platform.OS === 'android' ? Portal : View
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View>
|
<ModalWrapper>
|
||||||
<Modal
|
<Modal
|
||||||
isVisible={isVisible}
|
isVisible={isVisible}
|
||||||
statusBarTranslucent={Platform.OS === 'android'}
|
statusBarTranslucent={Platform.OS === 'android'}
|
||||||
|
coverScreen={Platform.OS !== 'android'}
|
||||||
avoidKeyboard={Platform.OS === 'ios'}
|
avoidKeyboard={Platform.OS === 'ios'}
|
||||||
onBackdropPress={onBackdropPress}
|
onBackdropPress={onBackdropPress}
|
||||||
onBackButtonPress={onBackButtonPress}
|
onBackButtonPress={onBackButtonPress}
|
||||||
@@ -240,7 +250,7 @@ export function BottomModal(props: ModalProps) {
|
|||||||
</View>
|
</View>
|
||||||
</KeyboardAvoidingView>
|
</KeyboardAvoidingView>
|
||||||
</Modal>
|
</Modal>
|
||||||
</View>
|
</ModalWrapper>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4439,6 +4439,18 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@gorhom/portal@npm:^1.0.14":
|
||||||
|
version: 1.0.14
|
||||||
|
resolution: "@gorhom/portal@npm:1.0.14"
|
||||||
|
dependencies:
|
||||||
|
nanoid: ^3.3.1
|
||||||
|
peerDependencies:
|
||||||
|
react: "*"
|
||||||
|
react-native: "*"
|
||||||
|
checksum: 227bb96a2db854ab29bb9da8d4f3823c7f7448358de459709dd1b78522110da564c9a8734c6bc7d7153ed7c99320e0fb5d60b420c2ebb75ecaf2f0d757f410f9
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@grpc/grpc-js@npm:^1.10.9, @grpc/grpc-js@npm:^1.12.6":
|
"@grpc/grpc-js@npm:^1.10.9, @grpc/grpc-js@npm:^1.12.6":
|
||||||
version: 1.14.2
|
version: 1.14.2
|
||||||
resolution: "@grpc/grpc-js@npm:1.14.2"
|
resolution: "@grpc/grpc-js@npm:1.14.2"
|
||||||
@@ -14821,6 +14833,7 @@ __metadata:
|
|||||||
"@gandlaf21/bc-ur": ^1.1.12
|
"@gandlaf21/bc-ur": ^1.1.12
|
||||||
"@gandlaf21/bolt11-decode": ^3.1.1
|
"@gandlaf21/bolt11-decode": ^3.1.1
|
||||||
"@gocodingnow/rn-size-matters": ^0.0.4
|
"@gocodingnow/rn-size-matters": ^0.0.4
|
||||||
|
"@gorhom/portal": ^1.0.14
|
||||||
"@hot-updater/bare": 0.32.0
|
"@hot-updater/bare": 0.32.0
|
||||||
"@hot-updater/firebase": 0.32.0
|
"@hot-updater/firebase": 0.32.0
|
||||||
"@hot-updater/react-native": 0.32.0
|
"@hot-updater/react-native": 0.32.0
|
||||||
@@ -15228,6 +15241,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"nanoid@npm:^3.3.1":
|
||||||
|
version: 3.3.12
|
||||||
|
resolution: "nanoid@npm:3.3.12"
|
||||||
|
bin:
|
||||||
|
nanoid: bin/nanoid.cjs
|
||||||
|
checksum: 38699257447dc59e21e73e0510d0dfb16b7a610d9ca80633d5c3a68f9b4298c990513d30404ca8f163c2d03225ee01695ff8898bea6179183f38f0477b7635ac
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"nanoid@npm:^3.3.11":
|
"nanoid@npm:^3.3.11":
|
||||||
version: 3.3.11
|
version: 3.3.11
|
||||||
resolution: "nanoid@npm:3.3.11"
|
resolution: "nanoid@npm:3.3.11"
|
||||||
|
|||||||
Reference in New Issue
Block a user