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.
|
||||
# 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.
|
||||
#
|
||||
# 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
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
"@gandlaf21/bc-ur": "^1.1.12",
|
||||
"@gandlaf21/bolt11-decode": "^3.1.1",
|
||||
"@gocodingnow/rn-size-matters": "^0.0.4",
|
||||
"@gorhom/portal": "^1.0.14",
|
||||
"@hot-updater/react-native": "0.32.0",
|
||||
"@notifee/react-native": "^9.1.8",
|
||||
"@op-engineering/op-sqlite": "^16.2.0",
|
||||
|
||||
+9
-4
@@ -9,6 +9,7 @@ import {
|
||||
} from '@gocodingnow/rn-size-matters'
|
||||
import RNExitApp from 'react-native-exit-app'
|
||||
import { changeIcon } from 'react-native-change-icon'
|
||||
import { PortalProvider } from '@gorhom/portal'
|
||||
import {AppNavigator} from './navigation'
|
||||
import {useInitialRootStore, useStores} from './models'
|
||||
import {KeyChain, WalletKeys} from './services'
|
||||
@@ -242,10 +243,14 @@ function App() {
|
||||
|
||||
return (
|
||||
<SafeAreaProvider>
|
||||
<ErrorBoundary catchErrors={Config.catchErrors}>
|
||||
<AppNavigator />
|
||||
<FlashMessage position="bottom" />
|
||||
</ErrorBoundary>
|
||||
{/* PortalProvider hosts in-tree modals (BottomModal on Android) above the
|
||||
navigator + bottom tab bar, avoiding react-native Modal's native window. */}
|
||||
<PortalProvider>
|
||||
<ErrorBoundary catchErrors={Config.catchErrors}>
|
||||
<AppNavigator />
|
||||
<FlashMessage position="bottom" />
|
||||
</ErrorBoundary>
|
||||
</PortalProvider>
|
||||
</SafeAreaProvider>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { ComponentType, Fragment, ReactElement, useEffect } from "react"
|
||||
import Modal from 'react-native-modal'
|
||||
import { Portal } from '@gorhom/portal'
|
||||
import {
|
||||
StyleProp,
|
||||
TextStyle,
|
||||
@@ -169,11 +170,20 @@ export function BottomModal(props: ModalProps) {
|
||||
// const keyboard = useAnimatedKeyboard()
|
||||
//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 (
|
||||
<View>
|
||||
<ModalWrapper>
|
||||
<Modal
|
||||
isVisible={isVisible}
|
||||
statusBarTranslucent={Platform.OS === 'android'}
|
||||
coverScreen={Platform.OS !== 'android'}
|
||||
avoidKeyboard={Platform.OS === 'ios'}
|
||||
onBackdropPress={onBackdropPress}
|
||||
onBackButtonPress={onBackButtonPress}
|
||||
@@ -240,7 +250,7 @@ export function BottomModal(props: ModalProps) {
|
||||
</View>
|
||||
</KeyboardAvoidingView>
|
||||
</Modal>
|
||||
</View>
|
||||
</ModalWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -4439,6 +4439,18 @@ __metadata:
|
||||
languageName: node
|
||||
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":
|
||||
version: 1.14.2
|
||||
resolution: "@grpc/grpc-js@npm:1.14.2"
|
||||
@@ -14821,6 +14833,7 @@ __metadata:
|
||||
"@gandlaf21/bc-ur": ^1.1.12
|
||||
"@gandlaf21/bolt11-decode": ^3.1.1
|
||||
"@gocodingnow/rn-size-matters": ^0.0.4
|
||||
"@gorhom/portal": ^1.0.14
|
||||
"@hot-updater/bare": 0.32.0
|
||||
"@hot-updater/firebase": 0.32.0
|
||||
"@hot-updater/react-native": 0.32.0
|
||||
@@ -15228,6 +15241,15 @@ __metadata:
|
||||
languageName: node
|
||||
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":
|
||||
version: 3.3.11
|
||||
resolution: "nanoid@npm:3.3.11"
|
||||
|
||||
Reference in New Issue
Block a user