mirror of
https://github.com/zimplexing/OrionTV.git
synced 2026-02-23 19:54:45 +08:00
fix(ui): resolve status bar overlay issue across all screens
Add SafeAreaProvider to root layout and implement proper safe area handling: - Wrap app in SafeAreaProvider in _layout.tsx - Update HomeScreen to use safe area insets for proper top padding - Fix SettingsScreen safe area handling for all device types - Update ResponsiveHeader to use SafeAreaContext instead of manual calculation This ensures content is not covered by the status bar on mobile and tablet devices while maintaining TV compatibility.
This commit is contained in:
@@ -5,6 +5,7 @@ import * as SplashScreen from "expo-splash-screen";
|
|||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { Platform, View, StyleSheet } from "react-native";
|
import { Platform, View, StyleSheet } from "react-native";
|
||||||
import Toast from "react-native-toast-message";
|
import Toast from "react-native-toast-message";
|
||||||
|
import { SafeAreaProvider } from "react-native-safe-area-context";
|
||||||
|
|
||||||
import { useSettingsStore } from "@/stores/settingsStore";
|
import { useSettingsStore } from "@/stores/settingsStore";
|
||||||
import { useRemoteControlStore } from "@/stores/remoteControlStore";
|
import { useRemoteControlStore } from "@/stores/remoteControlStore";
|
||||||
@@ -76,23 +77,25 @@ export default function RootLayout() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeProvider value={colorScheme === "dark" ? DarkTheme : DefaultTheme}>
|
<SafeAreaProvider>
|
||||||
<View style={styles.container}>
|
<ThemeProvider value={colorScheme === "dark" ? DarkTheme : DefaultTheme}>
|
||||||
<Stack>
|
<View style={styles.container}>
|
||||||
<Stack.Screen name="index" options={{ headerShown: false }} />
|
<Stack>
|
||||||
<Stack.Screen name="detail" options={{ headerShown: false }} />
|
<Stack.Screen name="index" options={{ headerShown: false }} />
|
||||||
{Platform.OS !== "web" && <Stack.Screen name="play" options={{ headerShown: false }} />}
|
<Stack.Screen name="detail" options={{ headerShown: false }} />
|
||||||
<Stack.Screen name="search" options={{ headerShown: false }} />
|
{Platform.OS !== "web" && <Stack.Screen name="play" options={{ headerShown: false }} />}
|
||||||
<Stack.Screen name="live" options={{ headerShown: false }} />
|
<Stack.Screen name="search" options={{ headerShown: false }} />
|
||||||
<Stack.Screen name="settings" options={{ headerShown: false }} />
|
<Stack.Screen name="live" options={{ headerShown: false }} />
|
||||||
<Stack.Screen name="favorites" options={{ headerShown: false }} />
|
<Stack.Screen name="settings" options={{ headerShown: false }} />
|
||||||
<Stack.Screen name="+not-found" />
|
<Stack.Screen name="favorites" options={{ headerShown: false }} />
|
||||||
</Stack>
|
<Stack.Screen name="+not-found" />
|
||||||
</View>
|
</Stack>
|
||||||
<Toast />
|
</View>
|
||||||
<LoginModal />
|
<Toast />
|
||||||
<UpdateModal />
|
<LoginModal />
|
||||||
</ThemeProvider>
|
<UpdateModal />
|
||||||
|
</ThemeProvider>
|
||||||
|
</SafeAreaProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ export default function HomeScreen() {
|
|||||||
const dynamicStyles = StyleSheet.create({
|
const dynamicStyles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
paddingTop: deviceType === "mobile" ? 0 : 40,
|
paddingTop: deviceType === "mobile" ? insets.top : deviceType === "tablet" ? insets.top + 20 : 40,
|
||||||
},
|
},
|
||||||
headerContainer: {
|
headerContainer: {
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import React, { useState, useEffect, useRef } from "react";
|
import React, { useState, useEffect, useRef } from "react";
|
||||||
import { View, StyleSheet, FlatList, Alert, KeyboardAvoidingView, Platform } from "react-native";
|
import { View, StyleSheet, FlatList, Alert, KeyboardAvoidingView, Platform } from "react-native";
|
||||||
import { useTVEventHandler } from "react-native";
|
import { useTVEventHandler } from "react-native";
|
||||||
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||||
import { ThemedText } from "@/components/ThemedText";
|
import { ThemedText } from "@/components/ThemedText";
|
||||||
import { ThemedView } from "@/components/ThemedView";
|
import { ThemedView } from "@/components/ThemedView";
|
||||||
import { StyledButton } from "@/components/StyledButton";
|
import { StyledButton } from "@/components/StyledButton";
|
||||||
@@ -24,6 +25,7 @@ export default function SettingsScreen() {
|
|||||||
const { loadSettings, saveSettings, setApiBaseUrl, setM3uUrl } = useSettingsStore();
|
const { loadSettings, saveSettings, setApiBaseUrl, setM3uUrl } = useSettingsStore();
|
||||||
const { lastMessage, targetPage, clearMessage } = useRemoteControlStore();
|
const { lastMessage, targetPage, clearMessage } = useRemoteControlStore();
|
||||||
const backgroundColor = useThemeColor({}, "background");
|
const backgroundColor = useThemeColor({}, "background");
|
||||||
|
const insets = useSafeAreaInsets();
|
||||||
|
|
||||||
// 响应式布局配置
|
// 响应式布局配置
|
||||||
const responsiveConfig = useResponsiveLayout();
|
const responsiveConfig = useResponsiveLayout();
|
||||||
@@ -162,7 +164,7 @@ export default function SettingsScreen() {
|
|||||||
useTVEventHandler(deviceType === "tv" ? handleTVEvent : () => {});
|
useTVEventHandler(deviceType === "tv" ? handleTVEvent : () => {});
|
||||||
|
|
||||||
// 动态样式
|
// 动态样式
|
||||||
const dynamicStyles = createResponsiveStyles(deviceType, spacing);
|
const dynamicStyles = createResponsiveStyles(deviceType, spacing, insets);
|
||||||
|
|
||||||
const renderSettingsContent = () => (
|
const renderSettingsContent = () => (
|
||||||
<KeyboardAvoidingView style={{ flex: 1, backgroundColor }} behavior={Platform.OS === "ios" ? "padding" : "height"}>
|
<KeyboardAvoidingView style={{ flex: 1, backgroundColor }} behavior={Platform.OS === "ios" ? "padding" : "height"}>
|
||||||
@@ -214,7 +216,7 @@ export default function SettingsScreen() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const createResponsiveStyles = (deviceType: string, spacing: number) => {
|
const createResponsiveStyles = (deviceType: string, spacing: number, insets: any) => {
|
||||||
const isMobile = deviceType === "mobile";
|
const isMobile = deviceType === "mobile";
|
||||||
const isTablet = deviceType === "tablet";
|
const isTablet = deviceType === "tablet";
|
||||||
const isTV = deviceType === "tv";
|
const isTV = deviceType === "tv";
|
||||||
@@ -224,7 +226,7 @@ const createResponsiveStyles = (deviceType: string, spacing: number) => {
|
|||||||
container: {
|
container: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
padding: spacing,
|
padding: spacing,
|
||||||
paddingTop: isTV ? spacing * 2 : 0,
|
paddingTop: isTV ? spacing * 2 : isMobile ? insets.top + spacing : insets.top + spacing * 1.5,
|
||||||
},
|
},
|
||||||
header: {
|
header: {
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { ArrowLeft } from 'lucide-react-native';
|
|||||||
import { ThemedText } from '@/components/ThemedText';
|
import { ThemedText } from '@/components/ThemedText';
|
||||||
import { useResponsiveLayout } from '@/hooks/useResponsiveLayout';
|
import { useResponsiveLayout } from '@/hooks/useResponsiveLayout';
|
||||||
import { DeviceUtils } from '@/utils/DeviceUtils';
|
import { DeviceUtils } from '@/utils/DeviceUtils';
|
||||||
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
|
|
||||||
interface ResponsiveHeaderProps {
|
interface ResponsiveHeaderProps {
|
||||||
title?: string;
|
title?: string;
|
||||||
@@ -21,6 +22,7 @@ const ResponsiveHeader: React.FC<ResponsiveHeaderProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { deviceType, spacing } = useResponsiveLayout();
|
const { deviceType, spacing } = useResponsiveLayout();
|
||||||
|
const insets = useSafeAreaInsets();
|
||||||
|
|
||||||
// TV端不显示Header,使用现有的页面内导航
|
// TV端不显示Header,使用现有的页面内导航
|
||||||
if (deviceType === 'tv') {
|
if (deviceType === 'tv') {
|
||||||
@@ -35,7 +37,7 @@ const ResponsiveHeader: React.FC<ResponsiveHeaderProps> = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const dynamicStyles = createStyles(spacing, deviceType);
|
const dynamicStyles = createStyles(spacing, deviceType, insets);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -74,14 +76,13 @@ const ResponsiveHeader: React.FC<ResponsiveHeaderProps> = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const createStyles = (spacing: number, deviceType: string) => {
|
const createStyles = (spacing: number, deviceType: string, insets: any) => {
|
||||||
const minTouchTarget = DeviceUtils.getMinTouchTargetSize();
|
const minTouchTarget = DeviceUtils.getMinTouchTargetSize();
|
||||||
const statusBarHeight = Platform.OS === 'ios' ? 44 : StatusBar.currentHeight || 24;
|
|
||||||
|
|
||||||
return StyleSheet.create({
|
return StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
backgroundColor: '#1c1c1e',
|
backgroundColor: '#1c1c1e',
|
||||||
paddingTop: statusBarHeight,
|
paddingTop: insets.top,
|
||||||
borderBottomWidth: 1,
|
borderBottomWidth: 1,
|
||||||
borderBottomColor: '#333',
|
borderBottomColor: '#333',
|
||||||
shadowColor: '#000',
|
shadowColor: '#000',
|
||||||
|
|||||||
Reference in New Issue
Block a user