mirror of
https://github.com/bulwarkmail/native.git
synced 2026-11-04 16:06:41 +00:00
fix: put the mail search in the header row on a phone in landscape
This commit is contained in:
@@ -388,7 +388,7 @@ export default function EmailListScreen({
|
|||||||
const resolvedTheme = useResolvedTheme();
|
const resolvedTheme = useResolvedTheme();
|
||||||
// The undo bar floats over the list's bottom right on a phone (on a split
|
// The undo bar floats over the list's bottom right on a phone (on a split
|
||||||
// layout it sits over the reading pane instead).
|
// layout it sits over the reading pane instead).
|
||||||
const { isSplit } = useLayout();
|
const { isSplit, shortHeight } = useLayout();
|
||||||
const listUndo = useEmailStore((s) => s.pendingUndo != null);
|
const listUndo = useEmailStore((s) => s.pendingUndo != null);
|
||||||
const sendUndo = useSendUndoStore((s) => s.pending != null);
|
const sendUndo = useSendUndoStore((s) => s.pending != null);
|
||||||
const undoBarUp = !isSplit && (listUndo || sendUndo);
|
const undoBarUp = !isSplit && (listUndo || sendUndo);
|
||||||
@@ -1066,6 +1066,90 @@ export default function EmailListScreen({
|
|||||||
}
|
}
|
||||||
}, [mailboxes, currentMailboxId, selectMailbox]);
|
}, [mailboxes, currentMailboxId, selectMailbox]);
|
||||||
|
|
||||||
|
const renderSearchBar = (inHeader: boolean) => (
|
||||||
|
<View style={[styles.searchBar, inHeader && styles.searchBarInHeader]}>
|
||||||
|
<Pressable
|
||||||
|
style={styles.checkboxButton}
|
||||||
|
onPress={toggleSelectAllVisible}
|
||||||
|
hitSlop={6}
|
||||||
|
accessibilityRole="checkbox"
|
||||||
|
accessibilityLabel={t('email_list.batch_actions.select_all', 'Select all')}
|
||||||
|
accessibilityState={{ checked: allSelected ? true : selectionMode ? 'mixed' : false }}
|
||||||
|
>
|
||||||
|
{allSelected ? (
|
||||||
|
<SquareCheck size={18} color={c.primary} />
|
||||||
|
) : selectionMode ? (
|
||||||
|
<View style={styles.checkboxIndeterminate}>
|
||||||
|
<Minus size={14} color={c.background} />
|
||||||
|
</View>
|
||||||
|
) : (
|
||||||
|
<Square size={18} color={c.textMuted} />
|
||||||
|
)}
|
||||||
|
</Pressable>
|
||||||
|
<View style={styles.searchInputArea}>
|
||||||
|
<Search size={16} color={c.textMuted} />
|
||||||
|
<TextInput
|
||||||
|
// No full-screen keyboard in landscape: it would hide the suggestions
|
||||||
|
// and the text around the field.
|
||||||
|
disableFullscreenUI
|
||||||
|
ref={searchInputRef}
|
||||||
|
style={styles.searchInput}
|
||||||
|
placeholder={t('email_list.search_placeholder', 'Search mail...')}
|
||||||
|
placeholderTextColor={c.textMuted}
|
||||||
|
value={searchInput}
|
||||||
|
onChangeText={setSearchInput}
|
||||||
|
onFocus={() => setSearchFocused(true)}
|
||||||
|
// Delay so a tap on a recent-search row lands before the list hides.
|
||||||
|
onBlur={() => { setTimeout(() => setSearchFocused(false), 150); }}
|
||||||
|
onSubmitEditing={() => submitSearch(searchInput)}
|
||||||
|
autoCapitalize="none"
|
||||||
|
autoCorrect={false}
|
||||||
|
returnKeyType="search"
|
||||||
|
/>
|
||||||
|
{searchInput.length > 0 && (
|
||||||
|
<Pressable
|
||||||
|
onPress={() => { setSearchInput(''); setSearchQuery(''); }}
|
||||||
|
hitSlop={8}
|
||||||
|
style={styles.searchClearButton}
|
||||||
|
accessibilityRole="button"
|
||||||
|
accessibilityLabel={t('contacts.clear_search', 'Clear search')}
|
||||||
|
>
|
||||||
|
<X size={14} color={c.textMuted} />
|
||||||
|
</Pressable>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
<Pressable
|
||||||
|
style={styles.filterButton}
|
||||||
|
onPress={() => setSortAscending(!sortAscending)}
|
||||||
|
accessibilityRole="button"
|
||||||
|
accessibilityLabel={sortAscending
|
||||||
|
? t('settings.appearance.message_list_order.direction.oldest_first', 'Oldest first')
|
||||||
|
: t('settings.appearance.message_list_order.direction.newest_first', 'Newest first')}
|
||||||
|
accessibilityHint={t('email_list.sort_toggle_hint', 'Reverses the mail sort order')}
|
||||||
|
>
|
||||||
|
{sortAscending ? (
|
||||||
|
<ArrowUpNarrowWide size={18} color={c.primary} />
|
||||||
|
) : (
|
||||||
|
<ArrowDownWideNarrow size={18} color={c.textMuted} />
|
||||||
|
)}
|
||||||
|
</Pressable>
|
||||||
|
<Pressable
|
||||||
|
style={[styles.filterButton, activeFilterCount > 0 && styles.filterButtonActive]}
|
||||||
|
onPress={() => setFilterMenuOpen(true)}
|
||||||
|
accessibilityRole="button"
|
||||||
|
accessibilityLabel={t('advanced_search.advanced_filters_tooltip', 'Advanced search filters')}
|
||||||
|
accessibilityValue={activeFilterCount > 0 ? { text: String(activeFilterCount) } : undefined}
|
||||||
|
>
|
||||||
|
<Filter size={18} color={activeFilterCount > 0 ? c.primary : c.textMuted} />
|
||||||
|
{activeFilterCount > 0 && (
|
||||||
|
<View style={styles.filterBadge}>
|
||||||
|
<Text style={styles.filterBadgeText}>{activeFilterCount}</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</Pressable>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.container, { paddingTop: insets.top }]}>
|
<View style={[styles.container, { paddingTop: insets.top }]}>
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
@@ -1176,10 +1260,12 @@ export default function EmailListScreen({
|
|||||||
<Menu size={20} color={c.textMuted} />
|
<Menu size={20} color={c.textMuted} />
|
||||||
</Pressable>
|
</Pressable>
|
||||||
)}
|
)}
|
||||||
<Text style={styles.headerTitle} numberOfLines={1}>
|
<Text style={[styles.headerTitle, shortHeight && styles.headerTitleShort]} numberOfLines={1}>
|
||||||
{headerTitle}
|
{headerTitle}
|
||||||
</Text>
|
</Text>
|
||||||
<View style={{ flex: 1 }} />
|
{/* A phone in landscape has room for a few rows only: the search
|
||||||
|
shares the header row instead of taking its own. */}
|
||||||
|
{shortHeight ? renderSearchBar(true) : <View style={{ flex: 1 }} />}
|
||||||
<Pressable
|
<Pressable
|
||||||
onPress={() => { void handleImport(); }}
|
onPress={() => { void handleImport(); }}
|
||||||
style={styles.headerButton}
|
style={styles.headerButton}
|
||||||
@@ -1203,88 +1289,8 @@ export default function EmailListScreen({
|
|||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Search bar (always visible) */}
|
{/* Search bar (always visible; inside the header in a short window) */}
|
||||||
<View style={styles.searchBar}>
|
{!shortHeight && renderSearchBar(false)}
|
||||||
<Pressable
|
|
||||||
style={styles.checkboxButton}
|
|
||||||
onPress={toggleSelectAllVisible}
|
|
||||||
hitSlop={6}
|
|
||||||
accessibilityRole="checkbox"
|
|
||||||
accessibilityLabel={t('email_list.batch_actions.select_all', 'Select all')}
|
|
||||||
accessibilityState={{ checked: allSelected ? true : selectionMode ? 'mixed' : false }}
|
|
||||||
>
|
|
||||||
{allSelected ? (
|
|
||||||
<SquareCheck size={18} color={c.primary} />
|
|
||||||
) : selectionMode ? (
|
|
||||||
<View style={styles.checkboxIndeterminate}>
|
|
||||||
<Minus size={14} color={c.background} />
|
|
||||||
</View>
|
|
||||||
) : (
|
|
||||||
<Square size={18} color={c.textMuted} />
|
|
||||||
)}
|
|
||||||
</Pressable>
|
|
||||||
<View style={styles.searchInputArea}>
|
|
||||||
<Search size={16} color={c.textMuted} />
|
|
||||||
<TextInput
|
|
||||||
// No full-screen keyboard in landscape: it would hide the suggestions
|
|
||||||
// and the text around the field.
|
|
||||||
disableFullscreenUI
|
|
||||||
ref={searchInputRef}
|
|
||||||
style={styles.searchInput}
|
|
||||||
placeholder={t('email_list.search_placeholder', 'Search mail...')}
|
|
||||||
placeholderTextColor={c.textMuted}
|
|
||||||
value={searchInput}
|
|
||||||
onChangeText={setSearchInput}
|
|
||||||
onFocus={() => setSearchFocused(true)}
|
|
||||||
// Delay so a tap on a recent-search row lands before the list hides.
|
|
||||||
onBlur={() => { setTimeout(() => setSearchFocused(false), 150); }}
|
|
||||||
onSubmitEditing={() => submitSearch(searchInput)}
|
|
||||||
autoCapitalize="none"
|
|
||||||
autoCorrect={false}
|
|
||||||
returnKeyType="search"
|
|
||||||
/>
|
|
||||||
{searchInput.length > 0 && (
|
|
||||||
<Pressable
|
|
||||||
onPress={() => { setSearchInput(''); setSearchQuery(''); }}
|
|
||||||
hitSlop={8}
|
|
||||||
style={styles.searchClearButton}
|
|
||||||
accessibilityRole="button"
|
|
||||||
accessibilityLabel={t('contacts.clear_search', 'Clear search')}
|
|
||||||
>
|
|
||||||
<X size={14} color={c.textMuted} />
|
|
||||||
</Pressable>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
<Pressable
|
|
||||||
style={styles.filterButton}
|
|
||||||
onPress={() => setSortAscending(!sortAscending)}
|
|
||||||
accessibilityRole="button"
|
|
||||||
accessibilityLabel={sortAscending
|
|
||||||
? t('settings.appearance.message_list_order.direction.oldest_first', 'Oldest first')
|
|
||||||
: t('settings.appearance.message_list_order.direction.newest_first', 'Newest first')}
|
|
||||||
accessibilityHint={t('email_list.sort_toggle_hint', 'Reverses the mail sort order')}
|
|
||||||
>
|
|
||||||
{sortAscending ? (
|
|
||||||
<ArrowUpNarrowWide size={18} color={c.primary} />
|
|
||||||
) : (
|
|
||||||
<ArrowDownWideNarrow size={18} color={c.textMuted} />
|
|
||||||
)}
|
|
||||||
</Pressable>
|
|
||||||
<Pressable
|
|
||||||
style={[styles.filterButton, activeFilterCount > 0 && styles.filterButtonActive]}
|
|
||||||
onPress={() => setFilterMenuOpen(true)}
|
|
||||||
accessibilityRole="button"
|
|
||||||
accessibilityLabel={t('advanced_search.advanced_filters_tooltip', 'Advanced search filters')}
|
|
||||||
accessibilityValue={activeFilterCount > 0 ? { text: String(activeFilterCount) } : undefined}
|
|
||||||
>
|
|
||||||
<Filter size={18} color={activeFilterCount > 0 ? c.primary : c.textMuted} />
|
|
||||||
{activeFilterCount > 0 && (
|
|
||||||
<View style={styles.filterBadge}>
|
|
||||||
<Text style={styles.filterBadgeText}>{activeFilterCount}</Text>
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
</Pressable>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{searchFocused && ((!searchInput.trim() && recentSearches.length > 0) || contactSuggestions.length > 0) && (
|
{searchFocused && ((!searchInput.trim() && recentSearches.length > 0) || contactSuggestions.length > 0) && (
|
||||||
<View style={styles.recentSearches}>
|
<View style={styles.recentSearches}>
|
||||||
@@ -1967,6 +1973,8 @@ function makeStyles(c: ThemePalette) {
|
|||||||
marginVertical: spacing.sm,
|
marginVertical: spacing.sm,
|
||||||
gap: spacing.sm,
|
gap: spacing.sm,
|
||||||
},
|
},
|
||||||
|
searchBarInHeader: { flex: 1, marginHorizontal: 0, marginVertical: 0 },
|
||||||
|
headerTitleShort: { flexShrink: 1, maxWidth: 180 },
|
||||||
checkboxButton: {
|
checkboxButton: {
|
||||||
width: componentSizes.buttonMd, height: componentSizes.buttonMd,
|
width: componentSizes.buttonMd, height: componentSizes.buttonMd,
|
||||||
alignItems: 'center', justifyContent: 'center',
|
alignItems: 'center', justifyContent: 'center',
|
||||||
|
|||||||
Reference in New Issue
Block a user