mirror of
https://github.com/pybricks/pybricks-code.git
synced 2026-09-12 01:23:52 +00:00
explorer: move export from fileStorage
This commit is contained in:
@@ -6,13 +6,14 @@ import { cleanup } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
import { testRender, uuid } from '../../test';
|
||||
import {
|
||||
FileMetadata,
|
||||
fileStorageArchiveAllFiles,
|
||||
fileStorageExportFile,
|
||||
} from '../fileStorage/actions';
|
||||
import { FileMetadata, fileStorageArchiveAllFiles } from '../fileStorage/actions';
|
||||
import Explorer from './Explorer';
|
||||
import { explorerDeleteFile, explorerImportFiles, explorerRenameFile } from './actions';
|
||||
import {
|
||||
explorerDeleteFile,
|
||||
explorerExportFile,
|
||||
explorerImportFiles,
|
||||
explorerRenameFile,
|
||||
} from './actions';
|
||||
|
||||
afterEach(async () => {
|
||||
cleanup();
|
||||
@@ -153,7 +154,7 @@ describe('tree item', () => {
|
||||
|
||||
userEvent.click(button);
|
||||
|
||||
expect(dispatch).toHaveBeenCalledWith(fileStorageExportFile('test.file'));
|
||||
expect(dispatch).toHaveBeenCalledWith(explorerExportFile('test.file'));
|
||||
});
|
||||
|
||||
it('should dispatch export action when key is pressed', async () => {
|
||||
@@ -166,6 +167,6 @@ describe('tree item', () => {
|
||||
userEvent.click(treeItem);
|
||||
userEvent.keyboard('{ctrl}e');
|
||||
|
||||
expect(dispatch).toHaveBeenCalledWith(fileStorageExportFile('test.file'));
|
||||
expect(dispatch).toHaveBeenCalledWith(explorerExportFile('test.file'));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -23,15 +23,17 @@ import {
|
||||
useTreeEnvironment,
|
||||
} from 'react-complex-tree';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import {
|
||||
fileStorageArchiveAllFiles,
|
||||
fileStorageExportFile,
|
||||
} from '../fileStorage/actions';
|
||||
import { fileStorageArchiveAllFiles } from '../fileStorage/actions';
|
||||
import { useSelector } from '../reducers';
|
||||
import { isMacOS } from '../utils/os';
|
||||
import { preventBrowserNativeContextMenu } from '../utils/react';
|
||||
import { TreeItemContext, TreeItemData, renderers } from '../utils/tree-renderer';
|
||||
import { explorerDeleteFile, explorerImportFiles, explorerRenameFile } from './actions';
|
||||
import {
|
||||
explorerDeleteFile,
|
||||
explorerExportFile,
|
||||
explorerImportFiles,
|
||||
explorerRenameFile,
|
||||
} from './actions';
|
||||
import { I18nId } from './i18n';
|
||||
import NewFileWizard from './newFileWizard/NewFileWizard';
|
||||
import RenameFileDialog from './renameFileDialog/RenameFileDialog';
|
||||
@@ -110,7 +112,7 @@ const FileActionButtonGroup: React.VoidFunctionComponent<ActionButtonGroupProps>
|
||||
icon="import"
|
||||
tooltip={i18n.translate(I18nId.TreeItemExportTooltip, { fileName })}
|
||||
focusable={false}
|
||||
onClick={() => dispatch(fileStorageExportFile(fileName))}
|
||||
onClick={() => dispatch(explorerExportFile(fileName))}
|
||||
/>
|
||||
<ActionButton
|
||||
icon="trash"
|
||||
@@ -236,7 +238,7 @@ const renderTreeContainer: typeof renderers.renderTreeContainer = (props) => {
|
||||
const handleExportKeyDown = useCallback(() => {
|
||||
if (focusedItem !== undefined) {
|
||||
const fileName = environment.getItemTitle(environment.items[focusedItem]);
|
||||
dispatch(fileStorageExportFile(fileName));
|
||||
dispatch(explorerExportFile(fileName));
|
||||
}
|
||||
}, [environment]);
|
||||
|
||||
|
||||
@@ -86,6 +86,37 @@ export const explorerRenameFile = createAction((fileName: string) => ({
|
||||
fileName,
|
||||
}));
|
||||
|
||||
/**
|
||||
* Request to export (download) a file.
|
||||
* @param fileName The file name.
|
||||
*/
|
||||
export const explorerExportFile = createAction((fileName: string) => ({
|
||||
type: 'explorer.action.exportFile',
|
||||
fileName,
|
||||
}));
|
||||
|
||||
/**
|
||||
* Indicates that explorerExportFile(fileName) succeeded.
|
||||
* @param fileName The file name.
|
||||
*/
|
||||
export const explorerDidExportFile = createAction((fileName: string) => ({
|
||||
type: 'explorer.action.didExportFile',
|
||||
fileName,
|
||||
}));
|
||||
|
||||
/**
|
||||
* Indicates that explorerExportFile(fileName) failed.
|
||||
* @param fileName The file name.
|
||||
* @param error The error that was raised.
|
||||
*/
|
||||
export const explorerDidFailToExportFile = createAction(
|
||||
(fileName: string, error: Error) => ({
|
||||
type: 'explorer.action.didFailToExportFile',
|
||||
fileName,
|
||||
error,
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* Action that indicates that {@link explorerRenameFile} succeeded.
|
||||
*/
|
||||
|
||||
@@ -6,11 +6,15 @@ import { FileWithHandle } from 'browser-fs-access';
|
||||
import { mock } from 'jest-mock-extended';
|
||||
import { AsyncSaga, uuid } from '../../test';
|
||||
import {
|
||||
fileStorageDidFailToOpenFile,
|
||||
fileStorageDidFailToReadFile,
|
||||
fileStorageDidFailToRenameFile,
|
||||
fileStorageDidOpenFile,
|
||||
fileStorageDidReadFile,
|
||||
fileStorageDidRenameFile,
|
||||
fileStorageDidWriteFile,
|
||||
fileStorageOpenFile,
|
||||
fileStorageReadFile,
|
||||
fileStorageRenameFile,
|
||||
fileStorageWriteFile,
|
||||
} from '../fileStorage/actions';
|
||||
@@ -19,10 +23,13 @@ import {
|
||||
Hub,
|
||||
explorerCreateNewFile,
|
||||
explorerDidCreateNewFile,
|
||||
explorerDidExportFile,
|
||||
explorerDidFailToExportFile,
|
||||
explorerDidFailToImportFiles,
|
||||
explorerDidFailToRenameFile,
|
||||
explorerDidImportFiles,
|
||||
explorerDidRenameFile,
|
||||
explorerExportFile,
|
||||
explorerImportFiles,
|
||||
explorerRenameFile,
|
||||
} from './actions';
|
||||
@@ -159,3 +166,70 @@ describe('handleExplorerRenameFile', () => {
|
||||
await saga.end();
|
||||
});
|
||||
});
|
||||
|
||||
describe('handleExplorerExportFile', () => {
|
||||
let saga: AsyncSaga;
|
||||
const testFile = 'test.file';
|
||||
const testFileId = uuid(0);
|
||||
const testFileContents = '# test file contents';
|
||||
const testError = new Error('test error');
|
||||
|
||||
beforeEach(async () => {
|
||||
saga = new AsyncSaga(explorer);
|
||||
|
||||
saga.put(explorerExportFile(testFile));
|
||||
|
||||
await expect(saga.take()).resolves.toEqual(fileStorageOpenFile(testFile));
|
||||
});
|
||||
|
||||
it('should fail if file does not exist', async () => {
|
||||
saga.put(fileStorageDidFailToOpenFile(testFile, testError));
|
||||
|
||||
await expect(saga.take()).resolves.toEqual(
|
||||
explorerDidFailToExportFile(testFile, testError),
|
||||
);
|
||||
});
|
||||
|
||||
describe('should read file', () => {
|
||||
beforeEach(async () => {
|
||||
saga.put(fileStorageDidOpenFile(testFile, testFileId));
|
||||
|
||||
await expect(saga.take()).resolves.toEqual(fileStorageReadFile(testFileId));
|
||||
});
|
||||
|
||||
it('should catch read error', async () => {
|
||||
saga.put(fileStorageDidFailToReadFile(testFileId, testError));
|
||||
|
||||
await expect(saga.take()).resolves.toEqual(
|
||||
explorerDidFailToExportFile(testFile, testError),
|
||||
);
|
||||
});
|
||||
|
||||
describe('should read file', () => {
|
||||
it('should export file', async () => {
|
||||
jest.spyOn(browserFsAccess, 'fileSave').mockResolvedValue(null);
|
||||
|
||||
saga.put(fileStorageDidReadFile(testFileId, testFileContents));
|
||||
|
||||
await expect(saga.take()).resolves.toEqual(
|
||||
explorerDidExportFile('test.file'),
|
||||
);
|
||||
expect(browserFsAccess.fileSave).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should catch error', async () => {
|
||||
jest.spyOn(browserFsAccess, 'fileSave').mockRejectedValue(testError);
|
||||
|
||||
saga.put(fileStorageDidReadFile(testFileId, testFileContents));
|
||||
|
||||
await expect(saga.take()).resolves.toEqual(
|
||||
explorerDidFailToExportFile('test.file', testError),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await saga.end();
|
||||
});
|
||||
});
|
||||
|
||||
+63
-1
@@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2022 The Pybricks Authors
|
||||
|
||||
import { fileOpen } from 'browser-fs-access';
|
||||
import { fileOpen, fileSave } from 'browser-fs-access';
|
||||
import {
|
||||
call,
|
||||
put,
|
||||
@@ -14,12 +14,15 @@ import {
|
||||
import { getPybricksMicroPythonFileTemplate } from '../editor/pybricksMicroPython';
|
||||
import {
|
||||
fileStorageDidFailToOpenFile,
|
||||
fileStorageDidFailToReadFile,
|
||||
fileStorageDidFailToRenameFile,
|
||||
fileStorageDidFailToWriteFile,
|
||||
fileStorageDidOpenFile,
|
||||
fileStorageDidReadFile,
|
||||
fileStorageDidRenameFile,
|
||||
fileStorageDidWriteFile,
|
||||
fileStorageOpenFile,
|
||||
fileStorageReadFile,
|
||||
fileStorageRenameFile,
|
||||
fileStorageWriteFile,
|
||||
} from '../fileStorage/actions';
|
||||
@@ -35,11 +38,14 @@ import { defined, ensureError } from '../utils';
|
||||
import {
|
||||
explorerCreateNewFile,
|
||||
explorerDidCreateNewFile,
|
||||
explorerDidExportFile,
|
||||
explorerDidFailToCreateNewFile,
|
||||
explorerDidFailToExportFile,
|
||||
explorerDidFailToImportFiles,
|
||||
explorerDidFailToRenameFile,
|
||||
explorerDidImportFiles,
|
||||
explorerDidRenameFile,
|
||||
explorerExportFile,
|
||||
explorerImportFiles,
|
||||
explorerRenameFile,
|
||||
} from './actions';
|
||||
@@ -207,6 +213,61 @@ function* handleExplorerRenameFile(
|
||||
yield* put(explorerDidRenameFile());
|
||||
}
|
||||
|
||||
function* handleExplorerExportFile(
|
||||
action: ReturnType<typeof explorerExportFile>,
|
||||
): Generator {
|
||||
try {
|
||||
yield* put(fileStorageOpenFile(action.fileName));
|
||||
|
||||
const { didOpen, didFailToOpen } = yield* race({
|
||||
didOpen: take(
|
||||
fileStorageDidOpenFile.when((a) => a.path === action.fileName),
|
||||
),
|
||||
didFailToOpen: take(
|
||||
fileStorageDidFailToOpenFile.when((a) => a.path === action.fileName),
|
||||
),
|
||||
});
|
||||
|
||||
if (didFailToOpen) {
|
||||
throw didFailToOpen.error;
|
||||
}
|
||||
|
||||
defined(didOpen);
|
||||
|
||||
yield* put(fileStorageReadFile(didOpen.id));
|
||||
|
||||
const { didRead, didFailToRead } = yield* race({
|
||||
didRead: take(fileStorageDidReadFile.when((a) => a.id === didOpen.id)),
|
||||
didFailToRead: take(
|
||||
fileStorageDidFailToReadFile.when((a) => a.id === didOpen.id),
|
||||
),
|
||||
});
|
||||
|
||||
if (didFailToRead) {
|
||||
throw didFailToRead.error;
|
||||
}
|
||||
|
||||
defined(didRead);
|
||||
|
||||
const blob = new Blob([didRead.contents], { type: `${pythonFileMimeType}` });
|
||||
|
||||
yield* call(() =>
|
||||
fileSave(blob, {
|
||||
id: 'pybricksCodeFileStorageExport',
|
||||
fileName: didOpen.path,
|
||||
extensions: [pythonFileExtension],
|
||||
mimeTypes: [pythonFileMimeType],
|
||||
// TODO: translate description
|
||||
description: 'Python Files',
|
||||
}),
|
||||
);
|
||||
|
||||
yield* put(explorerDidExportFile(action.fileName));
|
||||
} catch (err) {
|
||||
yield* put(explorerDidFailToExportFile(action.fileName, ensureError(err)));
|
||||
}
|
||||
}
|
||||
|
||||
export default function* (): Generator {
|
||||
yield* takeEvery(explorerImportFiles, handleExplorerImportFiles);
|
||||
yield* takeEvery(explorerCreateNewFile, handleExplorerCreateNewFile);
|
||||
@@ -214,4 +275,5 @@ export default function* (): Generator {
|
||||
// previous one is finished, the old one will be canceled. We don't expect
|
||||
// this to happen in practice though.
|
||||
yield* takeLatest(explorerRenameFile, handleExplorerRenameFile);
|
||||
yield* takeLatest(explorerExportFile, handleExplorerExportFile);
|
||||
}
|
||||
|
||||
@@ -231,37 +231,6 @@ export const fileStorageDidFailToRenameFile = createAction(
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* Request to export (download) a file.
|
||||
* @param id The file handle UUID.
|
||||
*/
|
||||
export const fileStorageExportFile = createAction((fileName: string) => ({
|
||||
type: 'fileStorage.action.exportFile',
|
||||
fileName,
|
||||
}));
|
||||
|
||||
/**
|
||||
* Indicates that fileStorageExportFile(fileName) succeeded.
|
||||
* @param fileName The file handle UUID.
|
||||
*/
|
||||
export const fileStorageDidExportFile = createAction((fileName: string) => ({
|
||||
type: 'fileStorage.action.didExportFile',
|
||||
fileName,
|
||||
}));
|
||||
|
||||
/**
|
||||
* Indicates that fileStorageExportFile(fileName) failed.
|
||||
* @param fileName The file name.
|
||||
* @param error The error that was raised.
|
||||
*/
|
||||
export const fileStorageDidFailToExportFile = createAction(
|
||||
(fileName: string, error: Error) => ({
|
||||
type: 'fileStorage.action.didFailToExportFile',
|
||||
fileName,
|
||||
error,
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* Request to archive (download) all files in the store.
|
||||
*/
|
||||
|
||||
@@ -15,9 +15,7 @@ import {
|
||||
fileStorageDidArchiveAllFiles,
|
||||
fileStorageDidChangeItem,
|
||||
fileStorageDidDeleteFile,
|
||||
fileStorageDidExportFile,
|
||||
fileStorageDidFailToArchiveAllFiles,
|
||||
fileStorageDidFailToExportFile,
|
||||
fileStorageDidFailToReadFile,
|
||||
fileStorageDidInitialize,
|
||||
fileStorageDidOpenFile,
|
||||
@@ -25,7 +23,6 @@ import {
|
||||
fileStorageDidRemoveItem,
|
||||
fileStorageDidRenameFile,
|
||||
fileStorageDidWriteFile,
|
||||
fileStorageExportFile,
|
||||
fileStorageOpenFile,
|
||||
fileStorageReadFile,
|
||||
fileStorageRenameFile,
|
||||
@@ -219,59 +216,6 @@ describe('rename', () => {
|
||||
await saga.end();
|
||||
});
|
||||
});
|
||||
|
||||
describe('export', () => {
|
||||
it('should fail if file does not exist', async () => {
|
||||
const testFile = 'test.file';
|
||||
|
||||
const saga = new AsyncSaga(fileStorage);
|
||||
|
||||
await expect(saga.take()).resolves.toEqual(fileStorageDidInitialize([]));
|
||||
|
||||
saga.put(fileStorageExportFile(testFile));
|
||||
|
||||
await expect(saga.take()).resolves.toEqual(
|
||||
fileStorageDidFailToExportFile(testFile, new Error('file does not exist')),
|
||||
);
|
||||
|
||||
await saga.end();
|
||||
});
|
||||
|
||||
it('should export file', async () => {
|
||||
const saga = new AsyncSaga(fileStorage);
|
||||
|
||||
const [testFile] = await setUpTestFile(saga);
|
||||
|
||||
jest.spyOn(browserFsAccess, 'fileSave');
|
||||
|
||||
saga.put(fileStorageExportFile(testFile.path));
|
||||
|
||||
await expect(saga.take()).resolves.toEqual(
|
||||
fileStorageDidExportFile(testFile.path),
|
||||
);
|
||||
expect(browserFsAccess.fileSave).toHaveBeenCalled();
|
||||
|
||||
await saga.end();
|
||||
});
|
||||
|
||||
it('should catch error', async () => {
|
||||
const saga = new AsyncSaga(fileStorage);
|
||||
|
||||
const [testFile] = await setUpTestFile(saga);
|
||||
|
||||
const testError = new Error('test error');
|
||||
jest.spyOn(browserFsAccess, 'fileSave').mockRejectedValue(testError);
|
||||
|
||||
saga.put(fileStorageExportFile(testFile.path));
|
||||
|
||||
await expect(saga.take()).resolves.toEqual(
|
||||
fileStorageDidFailToExportFile(testFile.path, testError),
|
||||
);
|
||||
|
||||
await saga.end();
|
||||
});
|
||||
});
|
||||
|
||||
describe('archive', () => {
|
||||
it('should archive file', async () => {
|
||||
const saga = new AsyncSaga(fileStorage);
|
||||
|
||||
@@ -13,7 +13,6 @@ import 'dexie-observable';
|
||||
import JSZip from 'jszip';
|
||||
import { eventChannel } from 'redux-saga';
|
||||
import { call, fork, put, take, takeEvery } from 'typed-redux-saga/macro';
|
||||
import { pythonFileExtension, pythonFileMimeType } from '../pybricksMicropython/lib';
|
||||
import { ensureError, timestamp } from '../utils';
|
||||
import { sha256Digest } from '../utils/crypto';
|
||||
import {
|
||||
@@ -25,10 +24,8 @@ import {
|
||||
fileStorageDidArchiveAllFiles,
|
||||
fileStorageDidChangeItem,
|
||||
fileStorageDidDeleteFile,
|
||||
fileStorageDidExportFile,
|
||||
fileStorageDidFailToArchiveAllFiles,
|
||||
fileStorageDidFailToDeleteFile,
|
||||
fileStorageDidFailToExportFile,
|
||||
fileStorageDidFailToInitialize,
|
||||
fileStorageDidFailToOpenFile,
|
||||
fileStorageDidFailToReadFile,
|
||||
@@ -40,7 +37,6 @@ import {
|
||||
fileStorageDidRemoveItem,
|
||||
fileStorageDidRenameFile,
|
||||
fileStorageDidWriteFile,
|
||||
fileStorageExportFile,
|
||||
fileStorageOpenFile,
|
||||
fileStorageReadFile,
|
||||
fileStorageRenameFile,
|
||||
@@ -256,55 +252,6 @@ function* handleWriteFile(
|
||||
}
|
||||
}
|
||||
|
||||
function* handleExportFile(
|
||||
db: FileStorageDb,
|
||||
action: ReturnType<typeof fileStorageExportFile>,
|
||||
): Generator {
|
||||
const file = yield* call(() =>
|
||||
db.transaction('r', db.metadata, db._contents, async () => {
|
||||
const metadata = await db.metadata
|
||||
.where('path')
|
||||
.equals(action.fileName)
|
||||
.first();
|
||||
|
||||
if (!metadata) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return await db._contents.get(metadata.path);
|
||||
}),
|
||||
);
|
||||
|
||||
if (!file) {
|
||||
yield* put(
|
||||
fileStorageDidFailToExportFile(
|
||||
action.fileName,
|
||||
new Error('file does not exist'),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const blob = new Blob([file.contents], { type: `${pythonFileMimeType}` });
|
||||
|
||||
try {
|
||||
yield* call(() =>
|
||||
fileSave(blob, {
|
||||
id: 'pybricksCodeFileStorageExport',
|
||||
fileName: file.path,
|
||||
extensions: [pythonFileExtension],
|
||||
mimeTypes: [pythonFileMimeType],
|
||||
// TODO: translate description
|
||||
description: 'Python Files',
|
||||
}),
|
||||
);
|
||||
|
||||
yield* put(fileStorageDidExportFile(action.fileName));
|
||||
} catch (err) {
|
||||
yield* put(fileStorageDidFailToExportFile(action.fileName, ensureError(err)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a file from storage.
|
||||
* @param db The database instance.
|
||||
@@ -466,7 +413,6 @@ function* initialize(): Generator {
|
||||
yield* takeEvery(fileStorageWriteFile, handleWriteFile, db);
|
||||
yield* takeEvery(fileStorageDeleteFile, handleDeleteFile, db);
|
||||
yield* takeEvery(fileStorageRenameFile, handleRenameFile, db);
|
||||
yield* takeEvery(fileStorageExportFile, handleExportFile, db);
|
||||
yield* takeEvery(fileStorageArchiveAllFiles, handleArchiveAllFiles, db);
|
||||
|
||||
const files = yield* call(() => db.metadata.toArray());
|
||||
|
||||
@@ -16,11 +16,10 @@ export enum I18nId {
|
||||
ExplorerDeleteFileMessage = 'explorer.deleteFile.message',
|
||||
ExplorerDeleteFileAction = 'explorer.deleteFile.action',
|
||||
ExplorerFailedToImportFiles = 'explorer.failedToImportFiles',
|
||||
ExplorerFailedToCreate = 'explorer.failedToCreate',
|
||||
ExplorerFailedToExport = 'explorer.failedToExport',
|
||||
FileStorageFailedToInitialize = 'fileStorage.failedToInitialize',
|
||||
FileStorageFailedToRead = 'fileStorage.failedToRead',
|
||||
FileStorageFailedToWrite = 'fileStorage.failedToWrite',
|
||||
FileStorageFailedToDelete = 'fileStorage.failedToDelete',
|
||||
FileStorageFailedToExport = 'fileStorage.failedToExport',
|
||||
FileStorageFailedToArchive = 'fileStorage.failedToArchive',
|
||||
FlashFirmwareTimedOut = 'flashFirmware.timedOut',
|
||||
FlashFirmwareBleError = 'flashFirmware.bleError',
|
||||
FlashFirmwareDisconnected = 'flashFirmware.disconnected',
|
||||
|
||||
@@ -19,12 +19,12 @@ import {
|
||||
import {
|
||||
explorerDeleteFile,
|
||||
explorerDidFailToCreateNewFile,
|
||||
explorerDidFailToExportFile,
|
||||
explorerDidFailToImportFiles,
|
||||
} from '../explorer/actions';
|
||||
import {
|
||||
fileStorageDeleteFile,
|
||||
fileStorageDidFailToArchiveAllFiles,
|
||||
fileStorageDidFailToExportFile,
|
||||
fileStorageDidFailToInitialize,
|
||||
fileStorageDidRemoveItem,
|
||||
} from '../fileStorage/actions';
|
||||
@@ -112,10 +112,10 @@ test.each([
|
||||
appDidCheckForUpdate(false),
|
||||
bleDIServiceDidReceiveFirmwareRevision('3.0.0'),
|
||||
fileStorageDidFailToInitialize(new Error('test error')),
|
||||
fileStorageDidFailToExportFile('test.file', new Error('test error')),
|
||||
fileStorageDidFailToArchiveAllFiles(new Error('test error')),
|
||||
explorerDidFailToImportFiles(new Error('test error')),
|
||||
explorerDidFailToCreateNewFile(new Error('test error')),
|
||||
explorerDidFailToExportFile('test.file', new Error('test error')),
|
||||
])('actions that should show notification: %o', async (action: AnyAction) => {
|
||||
const { toaster, saga } = createTestToasterSaga();
|
||||
|
||||
@@ -135,12 +135,12 @@ test.each([
|
||||
serviceWorkerDidSucceed(),
|
||||
appDidCheckForUpdate(true),
|
||||
bleDIServiceDidReceiveFirmwareRevision(firmwareVersion),
|
||||
fileStorageDidFailToExportFile(
|
||||
fileStorageDidFailToArchiveAllFiles(new DOMException('test message', 'AbortError')),
|
||||
explorerDidFailToImportFiles(new DOMException('test message', 'AbortError')),
|
||||
explorerDidFailToExportFile(
|
||||
'test.file',
|
||||
new DOMException('test message', 'AbortError'),
|
||||
),
|
||||
fileStorageDidFailToArchiveAllFiles(new DOMException('test message', 'AbortError')),
|
||||
explorerDidFailToImportFiles(new DOMException('test message', 'AbortError')),
|
||||
])('actions that should not show a notification: %o', async (action: AnyAction) => {
|
||||
const { toaster, saga } = createTestToasterSaga();
|
||||
|
||||
|
||||
+15
-15
@@ -20,12 +20,12 @@ import {
|
||||
import {
|
||||
explorerDeleteFile,
|
||||
explorerDidFailToCreateNewFile,
|
||||
explorerDidFailToExportFile,
|
||||
explorerDidFailToImportFiles,
|
||||
} from '../explorer/actions';
|
||||
import {
|
||||
fileStorageDeleteFile,
|
||||
fileStorageDidFailToArchiveAllFiles,
|
||||
fileStorageDidFailToExportFile,
|
||||
fileStorageDidFailToInitialize,
|
||||
fileStorageDidRemoveItem,
|
||||
} from '../fileStorage/actions';
|
||||
@@ -387,17 +387,6 @@ function* showFileStorageFailToInitialize(
|
||||
yield* showUnexpectedError(I18nId.FileStorageFailedToInitialize, action.error);
|
||||
}
|
||||
|
||||
function* showFileStorageFailToExport(
|
||||
action: ReturnType<typeof fileStorageDidFailToExportFile>,
|
||||
): Generator {
|
||||
if (action.error.name === 'AbortError') {
|
||||
// user clicked cancel button - not an error
|
||||
return;
|
||||
}
|
||||
|
||||
yield* showUnexpectedError(I18nId.FileStorageFailedToExport, action.error);
|
||||
}
|
||||
|
||||
function* showFileStorageFailToArchive(
|
||||
action: ReturnType<typeof fileStorageDidFailToArchiveAllFiles>,
|
||||
): Generator {
|
||||
@@ -406,7 +395,7 @@ function* showFileStorageFailToArchive(
|
||||
return;
|
||||
}
|
||||
|
||||
yield* showUnexpectedError(I18nId.FileStorageFailedToExport, action.error);
|
||||
yield* showUnexpectedError(I18nId.FileStorageFailedToArchive, action.error);
|
||||
}
|
||||
|
||||
function* showDeleteFileWarning(action: ReturnType<typeof explorerDeleteFile>) {
|
||||
@@ -458,7 +447,18 @@ function* showExplorerFailToImportFiles(
|
||||
function* showExplorerFailToCreateFile(
|
||||
action: ReturnType<typeof explorerDidFailToCreateNewFile>,
|
||||
): Generator {
|
||||
yield* showUnexpectedError(I18nId.FileStorageFailedToDelete, action.error);
|
||||
yield* showUnexpectedError(I18nId.ExplorerFailedToCreate, action.error);
|
||||
}
|
||||
|
||||
function* showExplorerFailToExport(
|
||||
action: ReturnType<typeof explorerDidFailToExportFile>,
|
||||
): Generator {
|
||||
if (action.error.name === 'AbortError') {
|
||||
// user clicked cancel button - not an error
|
||||
return;
|
||||
}
|
||||
|
||||
yield* showUnexpectedError(I18nId.ExplorerFailedToExport, action.error);
|
||||
}
|
||||
|
||||
export default function* (): Generator {
|
||||
@@ -472,9 +472,9 @@ export default function* (): Generator {
|
||||
yield* takeEvery(appDidCheckForUpdate, showNoUpdateInfo);
|
||||
yield* takeEvery(bleDIServiceDidReceiveFirmwareRevision, checkVersion);
|
||||
yield* takeEvery(fileStorageDidFailToInitialize, showFileStorageFailToInitialize);
|
||||
yield* takeEvery(fileStorageDidFailToExportFile, showFileStorageFailToExport);
|
||||
yield* takeEvery(fileStorageDidFailToArchiveAllFiles, showFileStorageFailToArchive);
|
||||
yield* takeEvery(explorerDeleteFile, showDeleteFileWarning);
|
||||
yield* takeEvery(explorerDidFailToImportFiles, showExplorerFailToImportFiles);
|
||||
yield* takeEvery(explorerDidFailToCreateNewFile, showExplorerFailToCreateFile);
|
||||
yield* takeEvery(explorerDidFailToExportFile, showExplorerFailToExport);
|
||||
}
|
||||
|
||||
@@ -19,14 +19,13 @@
|
||||
"message": "The file {fileName} will be permanently deleted. This cannot be undone.",
|
||||
"action": "Delete"
|
||||
},
|
||||
"failedToImportFiles": "Failed to import file(s)"
|
||||
"failedToImportFiles": "Failed to import file(s).",
|
||||
"failedToCreate": "Failed to create file.",
|
||||
"failedToExport": "Failed to export file."
|
||||
},
|
||||
"fileStorage": {
|
||||
"failedToInitialize": "Failed to initial file storage. Changes will not be automatically saved.",
|
||||
"failedToRead": "Failed to read file.",
|
||||
"failedToWrite": "Failed to write file.",
|
||||
"failedToDelete": "Failed to delete file.",
|
||||
"failedToExport": "Failed to export file.'"
|
||||
"failedToArchive": "Failed to archive files.'"
|
||||
},
|
||||
"flashFirmware": {
|
||||
"timedOut": "The hub took too long to respond. Restart the hub and try again.",
|
||||
|
||||
Reference in New Issue
Block a user