fix: stop the cold start flashing white and the mail header jumping in

On a cold start the splash was followed by about a second of blank
white, then mail rows whose header sat under the status bar until it
popped in and pushed the list down.

- `AppTheme` had no `android:windowBackground`, so while JS loaded the
  window showed the platform default (near-white, or grey at night).
  It is now the app's own background, #ffffff by day and #09090b at
  night (`values-night`), matching `src/theme/tokens.ts`.
- `NavigationContainer` had no theme, so React Navigation painted its
  light default behind and between screens even in dark mode. It now
  uses the palette's background.
- The mail list took its top inset from the native `SafeAreaView`,
  which on Fabric only learns the insets a few frames after mounting.
  It now pads with `useSafeAreaInsets()`, which the navigator's
  safe-area provider fills from the initial window metrics on the first
  render; the value is the same, it just arrives in time.
This commit is contained in:
Linus Rath
2026-09-24 19:35:47 +02:00
parent 82ca359df7
commit b9db3764ff
5 changed files with 23 additions and 6 deletions
+7 -3
View File
@@ -1,6 +1,6 @@
import React from 'react';
import { View, Text, StyleSheet, FlatList, Pressable, TextInput, Image, ActivityIndicator, Modal, Platform, ScrollView, TouchableWithoutFeedback, Alert } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import DateTimePicker, { type DateTimePickerEvent } from '@react-native-community/datetimepicker';
import * as DocumentPicker from 'expo-document-picker';
import {
@@ -295,6 +295,10 @@ interface EmailListScreenProps {
export default function EmailListScreen({ onEmailPress, onComposePress }: EmailListScreenProps) {
const c = useColors();
const styles = React.useMemo(() => makeStyles(c), [c]);
// The navigator's safe-area provider knows the insets on the first render;
// the native SafeAreaView only learned them a few frames later on Fabric,
// so the header first drew under the status bar and then jumped down.
const insets = useSafeAreaInsets();
const { t } = useLocaleStore();
const [drawerOpen, setDrawerOpen] = React.useState(false);
const [filterMenuOpen, setFilterMenuOpen] = React.useState(false);
@@ -917,7 +921,7 @@ export default function EmailListScreen({ onEmailPress, onComposePress }: EmailL
}, [mailboxes, currentMailboxId, selectMailbox]);
return (
<SafeAreaView style={styles.container} edges={['top']}>
<View style={[styles.container, { paddingTop: insets.top }]}>
{/* Header */}
{selectionMode ? (
<View style={styles.header}>
@@ -1592,7 +1596,7 @@ export default function EmailListScreen({ onEmailPress, onComposePress }: EmailL
/>
<UndoSnackbar />
</SafeAreaView>
</View>
);
}