接入移动壳安全区布局

Expo 移动壳使用 SafeAreaProvider 和 SafeAreaView 包裹 WebView

新增安全区边界配置和测试

补充移动壳安全区依赖检查和架构文档
This commit is contained in:
2026-06-18 08:14:27 +08:00
parent 94046153c6
commit 94a866b48b
9 changed files with 76 additions and 27 deletions

View File

@@ -7,8 +7,8 @@ import {
Linking,
Platform,
StyleSheet,
View,
} from 'react-native';
import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context';
import type { WebViewMessageEvent } from 'react-native-webview';
import { WebView } from 'react-native-webview';
@@ -24,6 +24,7 @@ import {
shouldOpenInMobileShellWebView,
} from './src/mobileShellNavigation';
import { subscribeMobileNetworkStatus } from './src/mobileShellNetwork';
import { MOBILE_SHELL_SAFE_AREA_EDGES } from './src/mobileShellSafeArea';
import { buildMobileShellUrl } from './src/mobileShellUrl';
const defaultWebUrl = 'http://127.0.0.1:3000/';
@@ -165,32 +166,34 @@ export default function App() {
};
return (
<View style={styles.root}>
<StatusBar style="auto" />
<WebView
ref={webViewRef}
source={{ uri: webUrl }}
javaScriptEnabled
domStorageEnabled
originWhitelist={[allowedWebOrigin]}
onMessage={handleMessage}
onShouldStartLoadWithRequest={handleShouldStartLoad}
onNavigationStateChange={(event) => {
setCanGoBack(event.canGoBack);
webViewRef.current?.injectJavaScript(
buildHostBridgeMessageScript({
bridge: 'GenarrativeHostBridge',
version: 1,
event: 'navigation.canGoBack',
payload: {
canGoBack: event.canGoBack,
},
}),
);
}}
setSupportMultipleWindows={false}
/>
</View>
<SafeAreaProvider>
<SafeAreaView style={styles.root} edges={MOBILE_SHELL_SAFE_AREA_EDGES}>
<StatusBar style="auto" />
<WebView
ref={webViewRef}
source={{ uri: webUrl }}
javaScriptEnabled
domStorageEnabled
originWhitelist={[allowedWebOrigin]}
onMessage={handleMessage}
onShouldStartLoadWithRequest={handleShouldStartLoad}
onNavigationStateChange={(event) => {
setCanGoBack(event.canGoBack);
webViewRef.current?.injectJavaScript(
buildHostBridgeMessageScript({
bridge: 'GenarrativeHostBridge',
version: 1,
event: 'navigation.canGoBack',
payload: {
canGoBack: event.canGoBack,
},
}),
);
}}
setSupportMultipleWindows={false}
/>
</SafeAreaView>
</SafeAreaProvider>
);
}

View File

@@ -26,6 +26,7 @@
"expo-status-bar": "^56.0.4",
"react": "^19.0.0",
"react-native": "^0.86.0",
"react-native-safe-area-context": "^5.8.0",
"react-native-webview": "^13.16.1"
},
"devDependencies": {

View File

@@ -124,6 +124,9 @@ for (const snippet of [
'subscribeMobileNetworkStatus',
'navigation.canGoBack',
'buildHostBridgeMessageScript',
'SafeAreaProvider',
'SafeAreaView',
'MOBILE_SHELL_SAFE_AREA_EDGES',
]) {
if (!appSource.includes(snippet)) {
throw new Error(`mobile shell App missing ${snippet}`);
@@ -137,6 +140,7 @@ for (const dependency of [
'expo-network',
'expo-notifications',
'expo-sharing',
'react-native-safe-area-context',
]) {
if (!packageConfig.dependencies?.[dependency]) {
throw new Error(`mobile shell package missing ${dependency}`);

View File

@@ -0,0 +1,14 @@
import { describe, expect, test } from 'vitest';
import { MOBILE_SHELL_SAFE_AREA_EDGES } from './mobileShellSafeArea';
describe('mobile shell safe area', () => {
test('protects the WebView from every device edge', () => {
expect(MOBILE_SHELL_SAFE_AREA_EDGES).toEqual([
'top',
'right',
'bottom',
'left',
]);
});
});

View File

@@ -0,0 +1,6 @@
export const MOBILE_SHELL_SAFE_AREA_EDGES = [
'top',
'right',
'bottom',
'left',
] as const;