mirror of
https://github.com/bulwarkmail/native.git
synced 2026-11-04 08:07:29 +00:00
fix: keep screens clear of the camera cutout and side navigation bar in landscape
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import { Animated, Easing, Pressable, StyleSheet, Text, View } from 'react-native';
|
||||
import { CalendarPlus, CircleCheck, Plus } from 'lucide-react-native';
|
||||
import { componentSizes, radius, spacing, typography, type ThemePalette } from '../../theme/tokens';
|
||||
@@ -39,6 +40,8 @@ export function CalendarFab({ onNewEvent, onNewTask }: CalendarFabProps) {
|
||||
action();
|
||||
};
|
||||
|
||||
const insets = useSafeAreaInsets();
|
||||
|
||||
const onMainPress = () => {
|
||||
if (!onNewTask) {
|
||||
onNewEvent();
|
||||
@@ -63,7 +66,8 @@ export function CalendarFab({ onNewEvent, onNewTask }: CalendarFabProps) {
|
||||
/>
|
||||
</Animated.View>
|
||||
)}
|
||||
<View style={styles.anchor} pointerEvents="box-none">
|
||||
{/* Clear of a side inset (camera cutout, navigation bar) in landscape. */}
|
||||
<View style={[styles.anchor, { right: spacing.lg + insets.right }]} pointerEvents="box-none">
|
||||
{open && onNewTask && (
|
||||
<Animated.View style={[styles.option, { opacity: progress, transform: [{ translateY: lift }] }]}>
|
||||
<Text style={styles.optionLabel}>{t('calendar.fab.task', 'Task')}</Text>
|
||||
|
||||
@@ -988,7 +988,7 @@ export default function CalendarScreen() {
|
||||
const titleDate = (monthPanelOpen && panelMonth) || visibleDate || focus.date;
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.container} edges={['top']}>
|
||||
<SafeAreaView style={styles.container} edges={['top', 'left', 'right']}>
|
||||
<View style={styles.header}>
|
||||
<Pressable
|
||||
onPress={() => setSidebarVisible(true)}
|
||||
|
||||
@@ -361,7 +361,7 @@ export default function ContactDetailScreen() {
|
||||
].filter(Boolean) as MoreItem[];
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.container} edges={['top']}>
|
||||
<SafeAreaView style={styles.container} edges={['top', 'left', 'right']}>
|
||||
<View style={[styles.header, inPane && styles.headerInPane]}>
|
||||
{/* In a split view's pane the list is right next to it: no back. */}
|
||||
{!inPane && (
|
||||
|
||||
@@ -1070,7 +1070,7 @@ export default function ContactFormScreen() {
|
||||
if (asGroup) {
|
||||
// Groups are a name plus a member list - none of the person fields apply.
|
||||
return (
|
||||
<SafeAreaView style={styles.container} edges={['top']}>
|
||||
<SafeAreaView style={styles.container} edges={['top', 'left', 'right']}>
|
||||
{header}
|
||||
<KeyboardAvoidingView
|
||||
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
|
||||
@@ -1131,7 +1131,7 @@ export default function ContactFormScreen() {
|
||||
}
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.container} edges={['top']}>
|
||||
<SafeAreaView style={styles.container} edges={['top', 'left', 'right']}>
|
||||
{header}
|
||||
|
||||
<KeyboardAvoidingView
|
||||
|
||||
@@ -381,7 +381,7 @@ export default function ContactsScreen({ onOpenContact, activeContactId = null }
|
||||
}, [addressBooks, groups, selectedCategory, t]);
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.container} edges={['top']}>
|
||||
<SafeAreaView style={styles.container} edges={['top', 'left', 'right']}>
|
||||
<ContactsSidebarDrawer visible={drawerOpen} onClose={() => setDrawerOpen(false)} />
|
||||
|
||||
<View style={styles.header}>
|
||||
|
||||
@@ -1151,7 +1151,7 @@ export default function EmailListScreen({
|
||||
);
|
||||
|
||||
return (
|
||||
<View style={[styles.container, { paddingTop: insets.top }]}>
|
||||
<View style={[styles.container, { paddingTop: insets.top, paddingLeft: insets.left, paddingRight: insets.right }]}>
|
||||
{/* Header */}
|
||||
{selectionMode ? (
|
||||
<View style={styles.header}>
|
||||
|
||||
@@ -65,7 +65,7 @@ export default function EmailSourceScreen({ route, navigation }: Props) {
|
||||
};
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.container} edges={['top']}>
|
||||
<SafeAreaView style={styles.container} edges={['top', 'left', 'right']}>
|
||||
<View style={styles.header}>
|
||||
<Pressable
|
||||
onPress={() => navigation.goBack()}
|
||||
|
||||
@@ -689,7 +689,7 @@ function EmailViewer({ route, navigation, embedded = false, onActiveChange }: Pr
|
||||
: false;
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.container} edges={['top']}>
|
||||
<SafeAreaView style={styles.container} edges={['top', 'left', 'right']}>
|
||||
{/* Toolbar */}
|
||||
<View style={[styles.toolbar, embedded && styles.toolbarEmbedded]}>
|
||||
{!embedded && (
|
||||
@@ -827,7 +827,7 @@ function EmailViewer({ route, navigation, embedded = false, onActiveChange }: Pr
|
||||
|
||||
{/* Bottom action bar (hidden while keyboard is up so quick reply stays visible) */}
|
||||
{kbObstruction === 0 && (
|
||||
<View style={[styles.bottomBar, { paddingBottom: Math.max(insets.bottom, 4) }]}>
|
||||
<View style={[styles.bottomBar, { paddingBottom: Math.max(insets.bottom, 4), paddingLeft: insets.left, paddingRight: insets.right }]}>
|
||||
<BottomBarButton
|
||||
icon={<ChevronLeft size={20} color={c.textMuted} />}
|
||||
label={t('email_viewer.previous', 'Prev')}
|
||||
|
||||
@@ -168,14 +168,10 @@ export default function FilesScreen() {
|
||||
const styles = React.useMemo(() => makeStyles(c), [c]);
|
||||
const t = useLocaleStore((s) => s.t);
|
||||
const formatModified = useFormatModified();
|
||||
// Clear of the status bar and, in landscape, the side insets; the header
|
||||
// used a fixed 60 px top padding instead.
|
||||
// Clear of the status bar (the header used a fixed 60 px top padding); the
|
||||
// whole screen keeps clear of the side insets in landscape.
|
||||
const insets = useSafeAreaInsets();
|
||||
const headerInsets = {
|
||||
paddingTop: insets.top + spacing.sm,
|
||||
paddingLeft: spacing.md + insets.left,
|
||||
paddingRight: spacing.md + insets.right,
|
||||
};
|
||||
const headerInsets = { paddingTop: insets.top + spacing.sm, paddingHorizontal: spacing.md };
|
||||
|
||||
// Folder navigation stack. Hierarchy is real parentId nesting (#379) — each
|
||||
// entry is the folder's node id (namespaced "accountId:nodeId" inside a
|
||||
@@ -1035,7 +1031,7 @@ export default function FilesScreen() {
|
||||
// On a wide window an open file shows beside the list instead of covering it.
|
||||
const previewDocked = isSplit && previewTarget != null;
|
||||
return (
|
||||
<View style={[styles.container, previewDocked && styles.splitRow]}>
|
||||
<View style={[styles.container, previewDocked && styles.splitRow, { paddingLeft: insets.left, paddingRight: insets.right }]}>
|
||||
<View style={styles.listColumn}>
|
||||
{renderHeader()}
|
||||
{body}
|
||||
|
||||
@@ -62,7 +62,7 @@ export default function GroupDetailScreen() {
|
||||
|
||||
if (!group) {
|
||||
return (
|
||||
<SafeAreaView style={styles.container} edges={['top']}>
|
||||
<SafeAreaView style={styles.container} edges={['top', 'left', 'right']}>
|
||||
<View style={styles.header}>
|
||||
<Pressable
|
||||
onPress={() => navigation.goBack()}
|
||||
@@ -133,7 +133,7 @@ export default function GroupDetailScreen() {
|
||||
};
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.container} edges={['top']}>
|
||||
<SafeAreaView style={styles.container} edges={['top', 'left', 'right']}>
|
||||
<View style={[styles.header, inPane && styles.headerInPane]}>
|
||||
{/* In a split view's pane the list is right next to it: no back. */}
|
||||
{!inPane && (
|
||||
|
||||
Reference in New Issue
Block a user