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