mirror of
https://github.com/bulwarkmail/native.git
synced 2026-10-31 08:06:36 +00:00
fix: clear the Files selection on Android back instead of switching to Mail
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
|
||||
const handlers: Array<() => boolean> = [];
|
||||
const remove = vi.fn();
|
||||
|
||||
vi.mock('react-native', () => ({
|
||||
BackHandler: {
|
||||
addEventListener: vi.fn((_event: string, handler: () => boolean) => {
|
||||
handlers.push(handler);
|
||||
return { remove };
|
||||
}),
|
||||
},
|
||||
}));
|
||||
|
||||
// Run the focus effect as if the screen had just gained focus and keep its
|
||||
// cleanup, which react-navigation calls on blur.
|
||||
let blur: void | (() => void);
|
||||
vi.mock('@react-navigation/native', () => ({
|
||||
useFocusEffect: (effect: () => void | (() => void)) => {
|
||||
blur = effect();
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('react', () => ({ useCallback: <T,>(fn: T) => fn }));
|
||||
|
||||
import { BackHandler } from 'react-native';
|
||||
import { useBackWhileFocused } from '../use-back-while-focused';
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
handlers.length = 0;
|
||||
blur = undefined;
|
||||
});
|
||||
|
||||
describe('useBackWhileFocused', () => {
|
||||
it('runs onBack and consumes the back press while active', () => {
|
||||
const onBack = vi.fn();
|
||||
useBackWhileFocused(true, onBack);
|
||||
|
||||
expect(BackHandler.addEventListener).toHaveBeenCalledWith('hardwareBackPress', expect.any(Function));
|
||||
expect(handlers[0]()).toBe(true);
|
||||
expect(onBack).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('leaves the back press to navigation while inactive', () => {
|
||||
useBackWhileFocused(false, vi.fn());
|
||||
|
||||
expect(BackHandler.addEventListener).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('stops listening when the screen loses focus', () => {
|
||||
useBackWhileFocused(true, vi.fn());
|
||||
|
||||
expect(typeof blur).toBe('function');
|
||||
(blur as () => void)();
|
||||
expect(remove).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
import { useCallback } from 'react';
|
||||
import { BackHandler } from 'react-native';
|
||||
import { useFocusEffect } from '@react-navigation/native';
|
||||
|
||||
/**
|
||||
* While `active` and the screen is focused, Android's hardware back runs
|
||||
* `onBack` instead of navigating (e.g. to leave a selection mode first).
|
||||
* Tab screens stay mounted while another tab is shown, so the listener is
|
||||
* tied to focus: a plain BackHandler listener would also swallow the back
|
||||
* presses meant for the other tabs.
|
||||
*/
|
||||
export function useBackWhileFocused(active: boolean, onBack: () => void): void {
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
if (!active) return undefined;
|
||||
const subscription = BackHandler.addEventListener('hardwareBackPress', () => {
|
||||
onBack();
|
||||
return true;
|
||||
});
|
||||
return () => subscription.remove();
|
||||
}, [active, onBack]),
|
||||
);
|
||||
}
|
||||
@@ -52,6 +52,7 @@ import { jmapClient } from '../api/jmap-client';
|
||||
import { downloadAttachment, shareAttachment } from '../lib/email-export';
|
||||
import { secureFetch } from '../lib/client-cert';
|
||||
import { getUniqueName } from '../lib/filenode-name';
|
||||
import { useBackWhileFocused } from '../lib/use-back-while-focused';
|
||||
import type { FileNode } from '../api/types';
|
||||
import { spacing, radius, typography, type ThemePalette } from '../theme/tokens';
|
||||
import { useColors } from '../theme/colors';
|
||||
@@ -321,6 +322,10 @@ export default function FilesScreen() {
|
||||
|
||||
const clearSelection = useCallback(() => setSelection(new Set()), []);
|
||||
|
||||
// Android back leaves selection mode first, like the header's X, instead
|
||||
// of switching to the Mail tab.
|
||||
useBackWhileFocused(selectionMode, clearSelection);
|
||||
|
||||
// Shared-with-me rows stay out of multi-select: batch delete routes to our
|
||||
// own account and would fail (or worse, mismatch) on namespaced ids.
|
||||
const toggleSelect = useCallback((row: FileRow) => {
|
||||
|
||||
Reference in New Issue
Block a user