fix: go up one folder on Android back in the Files tab

Back inside a Files subfolder switched to the Mail tab. It now closes an
open preview, then leaves selection mode, then goes up one folder, and
only switches tabs at the root. `filesBackStep` holds that order.
This commit is contained in:
Linus Rath
2026-09-24 19:28:37 +02:00
parent 409cdb139e
commit 4fdf39aa08
3 changed files with 57 additions and 4 deletions
+23
View File
@@ -0,0 +1,23 @@
import { describe, it, expect } from 'vitest';
import { filesBackStep } from '../files-back';
describe('filesBackStep', () => {
it('leaves the tab only at the root with nothing open', () => {
expect(filesBackStep({ previewOpen: false, selecting: false, folderDepth: 0 })).toBe('leave');
});
it('goes up one folder inside a subfolder', () => {
expect(filesBackStep({ previewOpen: false, selecting: false, folderDepth: 1 })).toBe('folder-up');
expect(filesBackStep({ previewOpen: false, selecting: false, folderDepth: 3 })).toBe('folder-up');
});
it('ends selection mode before changing folders', () => {
expect(filesBackStep({ previewOpen: false, selecting: true, folderDepth: 2 })).toBe('clear-selection');
expect(filesBackStep({ previewOpen: false, selecting: true, folderDepth: 0 })).toBe('clear-selection');
});
it('closes an open preview before anything else', () => {
expect(filesBackStep({ previewOpen: true, selecting: true, folderDepth: 2 })).toBe('close-preview');
expect(filesBackStep({ previewOpen: true, selecting: false, folderDepth: 0 })).toBe('close-preview');
});
});
+19
View File
@@ -0,0 +1,19 @@
/** What Android back does in the Files tab. */
export type FilesBackStep = 'close-preview' | 'clear-selection' | 'folder-up' | 'leave';
/**
* Back works through the tab's own layers, innermost first: an open preview
* closes, then selection mode ends (like the header's X), then the folder
* goes up one level. Only at the root with nothing open does it `leave`,
* i.e. fall through to navigation, which switches to the Mail tab.
*/
export function filesBackStep(state: {
previewOpen: boolean;
selecting: boolean;
folderDepth: number;
}): FilesBackStep {
if (state.previewOpen) return 'close-preview';
if (state.selecting) return 'clear-selection';
if (state.folderDepth > 0) return 'folder-up';
return 'leave';
}
+15 -4
View File
@@ -53,6 +53,7 @@ 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 { filesBackStep } from '../lib/files-back';
import type { FileNode } from '../api/types';
import { spacing, radius, typography, type ThemePalette } from '../theme/tokens';
import { useColors } from '../theme/colors';
@@ -325,10 +326,6 @@ 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) => {
@@ -459,6 +456,20 @@ export default function FilesScreen() {
clearSelection();
}, [clearSelection]);
// Android back closes the preview, then leaves selection mode, then goes
// up one folder; only at the root does it switch to the Mail tab.
const backStep = filesBackStep({
previewOpen: previewTarget != null,
selecting: selectionMode,
folderDepth: path.length,
});
const handleBack = useCallback(() => {
if (backStep === 'close-preview') setPreviewTarget(null);
else if (backStep === 'clear-selection') clearSelection();
else if (backStep === 'folder-up') goBack();
}, [backStep, clearSelection, goBack]);
useBackWhileFocused(backStep !== 'leave', handleBack);
const toggleView = useCallback(() => {
const next: FilesViewMode = viewMode === 'list' ? 'grid' : 'list';
setViewOverride(next);