9202864846
* fix(library): request storage permission when saving to a custom folder On Android a custom library folder on shared storage needs All Files Access. The import/settings/migrate paths request it, but the library-save path (updateBook/updateBooks -> saveLibraryBooks) did not, so on a device without the permission every save failed with EACCES; because callers (sync, imports) don't await/catch it, it surfaced as an unhandled-rejection crash (Sentry READEST-A). saveLibraryBooks now catches a storage-permission error, requests the permission through the existing flow, and retries once. It prompts at most once per session so background saves don't repeatedly open system settings; a still-denied save is logged rather than crashing (the user was already shown the All Files Access screen). Fixes READEST-A Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(sentry): drop only the benign hidden-tab View Transition error The View Transition API skips a transition when the tab is hidden; that unhandled rejection is expected browser behavior and pure noise, so it is dropped in before_send. A transition timeout abort (READEST-9) is NOT dropped: a slow DOM update can signal a real performance problem, so it stays visible. Fixes READEST-7 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
24 lines
888 B
TypeScript
24 lines
888 B
TypeScript
import { describe, test, expect } from 'vitest';
|
|
import { isStoragePermissionError } from '@/utils/permission';
|
|
|
|
describe('isStoragePermissionError', () => {
|
|
test('detects Android EACCES / permission-denied save failures', () => {
|
|
expect(
|
|
isStoragePermissionError(
|
|
new Error(
|
|
'Failed to save library.json: failed to open file at path: ' +
|
|
'/storage/emulated/0/Readest/Books/library.json.bak with error: ' +
|
|
'Permission denied (os error 13)',
|
|
),
|
|
),
|
|
).toBe(true);
|
|
expect(isStoragePermissionError('EACCES: permission denied')).toBe(true);
|
|
});
|
|
|
|
test('ignores unrelated errors', () => {
|
|
expect(isStoragePermissionError(new Error('disk full'))).toBe(false);
|
|
expect(isStoragePermissionError(new Error('JSON parse error'))).toBe(false);
|
|
expect(isStoragePermissionError(undefined)).toBe(false);
|
|
});
|
|
});
|