mirror of
https://github.com/pybricks/pybricks-code.git
synced 2026-09-12 01:23:52 +00:00
explorer: move files state from fileStorage
This state should be exclusivly used to for displaying files in the explorer, so the state should be in the explorer branch of the reducer tree.
This commit is contained in:
@@ -31,7 +31,7 @@ const testFile: FileMetadata = {
|
||||
describe('archive button', () => {
|
||||
it('should be enabled if there are files', () => {
|
||||
const [explorer, dispatch] = testRender(<Explorer />, {
|
||||
fileStorage: { files: [testFile] },
|
||||
explorer: { files: [testFile] },
|
||||
});
|
||||
|
||||
const button = explorer.getByTitle('Backup all files');
|
||||
@@ -43,7 +43,7 @@ describe('archive button', () => {
|
||||
|
||||
it('should be disabled if there are no files', () => {
|
||||
const [explorer, dispatch] = testRender(<Explorer />, {
|
||||
fileStorage: { files: [] },
|
||||
explorer: { files: [] },
|
||||
});
|
||||
|
||||
const button = explorer.getByTitle('Backup all files');
|
||||
@@ -84,7 +84,7 @@ describe('new file button', () => {
|
||||
describe('tree item', () => {
|
||||
it('should dispatch action when button is clicked', async () => {
|
||||
const [explorer, dispatch] = testRender(<Explorer />, {
|
||||
fileStorage: { files: [testFile] },
|
||||
explorer: { files: [testFile] },
|
||||
});
|
||||
|
||||
expect(
|
||||
@@ -102,7 +102,7 @@ describe('tree item', () => {
|
||||
|
||||
it('should dispatch action when key is pressed', async () => {
|
||||
const [explorer, dispatch] = testRender(<Explorer />, {
|
||||
fileStorage: { files: [testFile] },
|
||||
explorer: { files: [testFile] },
|
||||
});
|
||||
|
||||
expect(
|
||||
@@ -119,7 +119,7 @@ describe('tree item', () => {
|
||||
|
||||
it('should dispatch delete action when button is clicked', async () => {
|
||||
const [explorer, dispatch] = testRender(<Explorer />, {
|
||||
fileStorage: { files: [testFile] },
|
||||
explorer: { files: [testFile] },
|
||||
});
|
||||
|
||||
// NB: this button is intentionally not accessible (by role) since
|
||||
@@ -133,7 +133,7 @@ describe('tree item', () => {
|
||||
|
||||
it('should dispatch delete action when key is pressed', async () => {
|
||||
const [explorer, dispatch] = testRender(<Explorer />, {
|
||||
fileStorage: { files: [testFile] },
|
||||
explorer: { files: [testFile] },
|
||||
});
|
||||
|
||||
const treeItem = explorer.getByRole('treeitem', { name: 'test.file' });
|
||||
@@ -146,7 +146,7 @@ describe('tree item', () => {
|
||||
|
||||
it('should dispatch export action when button is clicked', async () => {
|
||||
const [explorer, dispatch] = testRender(<Explorer />, {
|
||||
fileStorage: { files: [testFile] },
|
||||
explorer: { files: [testFile] },
|
||||
});
|
||||
|
||||
// NB: this button is intentionally not accessible (by role) since
|
||||
@@ -160,7 +160,7 @@ describe('tree item', () => {
|
||||
|
||||
it('should dispatch export action when key is pressed', async () => {
|
||||
const [explorer, dispatch] = testRender(<Explorer />, {
|
||||
fileStorage: { files: [testFile] },
|
||||
explorer: { files: [testFile] },
|
||||
});
|
||||
|
||||
const treeItem = explorer.getByRole('treeitem', { name: 'test.file' });
|
||||
|
||||
@@ -132,7 +132,7 @@ type HeaderProps = {
|
||||
const Header: React.VoidFunctionComponent<HeaderProps> = ({ i18n }) => {
|
||||
const [isNewFileWizardOpen, setIsNewFileWizardOpen] = useState(false);
|
||||
const dispatch = useDispatch();
|
||||
const files = useSelector((s) => s.fileStorage.files);
|
||||
const files = useSelector((s) => s.explorer.files);
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
|
||||
@@ -281,7 +281,7 @@ type FileTreeProps = {
|
||||
|
||||
const FileTree: React.VoidFunctionComponent<FileTreeProps> = ({ i18n }) => {
|
||||
const [focusedItem, setFocusedItem] = useState<TreeItemIndex>();
|
||||
const files = useSelector((s) => s.fileStorage.files);
|
||||
const files = useSelector((s) => s.explorer.files);
|
||||
const liveDescriptors = useLiveDescriptors(i18n);
|
||||
|
||||
const rootItemIndex = 'root';
|
||||
|
||||
@@ -41,7 +41,7 @@ const NewFileWizard: React.VoidFunctionComponent<NewFileWizardProps> = ({
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const [fileName, setFileName] = useState('');
|
||||
const files = useSelector((s) => s.fileStorage.files);
|
||||
const files = useSelector((s) => s.explorer.files);
|
||||
const fileNameValidation = validateFileName(
|
||||
fileName,
|
||||
pythonFileExtension,
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2022 The Pybricks Authors
|
||||
|
||||
import { uuid } from '../../test';
|
||||
import {
|
||||
FileMetadata,
|
||||
fileStorageDidAddItem,
|
||||
fileStorageDidChangeItem,
|
||||
fileStorageDidInitialize,
|
||||
fileStorageDidRemoveItem,
|
||||
} from '../fileStorage/actions';
|
||||
import reducers from './reducers';
|
||||
|
||||
type State = ReturnType<typeof reducers>;
|
||||
|
||||
test('files', () => {
|
||||
const testFile: FileMetadata = {
|
||||
uuid: uuid(0),
|
||||
path: 'test.file',
|
||||
sha256: '',
|
||||
};
|
||||
|
||||
const modifiedFile: FileMetadata = { ...testFile, path: 'modified.file' };
|
||||
|
||||
expect(testFile).not.toEqual(modifiedFile);
|
||||
|
||||
// initialization populates file list
|
||||
expect(
|
||||
reducers(
|
||||
{ files: [] as readonly FileMetadata[] } as State,
|
||||
fileStorageDidInitialize([testFile]),
|
||||
).files,
|
||||
).toEqual([testFile]);
|
||||
|
||||
// adding appends an item
|
||||
expect(
|
||||
reducers(
|
||||
{ files: [] as readonly FileMetadata[] } as State,
|
||||
fileStorageDidAddItem(testFile),
|
||||
).files,
|
||||
).toEqual([testFile]);
|
||||
|
||||
// changing replaces an item
|
||||
expect(
|
||||
reducers(
|
||||
{ files: [testFile] as readonly FileMetadata[] } as State,
|
||||
fileStorageDidChangeItem(testFile, modifiedFile),
|
||||
).files,
|
||||
).toEqual([modifiedFile]);
|
||||
|
||||
// removing deletes an item
|
||||
expect(
|
||||
reducers(
|
||||
{ files: [testFile] as readonly FileMetadata[] } as State,
|
||||
fileStorageDidRemoveItem(testFile),
|
||||
).files,
|
||||
).not.toContain(testFile);
|
||||
});
|
||||
@@ -1,8 +1,35 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2022 The Pybricks Authors
|
||||
|
||||
import { combineReducers } from 'redux';
|
||||
import { Reducer, combineReducers } from 'redux';
|
||||
import {
|
||||
FileMetadata,
|
||||
fileStorageDidAddItem,
|
||||
fileStorageDidChangeItem,
|
||||
fileStorageDidInitialize,
|
||||
fileStorageDidRemoveItem,
|
||||
} from '../fileStorage/actions';
|
||||
|
||||
import renameFileDialog from './renameFileDialog/reducers';
|
||||
|
||||
export default combineReducers({ renameFileDialog });
|
||||
const files: Reducer<readonly FileMetadata[]> = (state = [], action) => {
|
||||
if (fileStorageDidInitialize.matches(action)) {
|
||||
return [...action.files];
|
||||
}
|
||||
|
||||
if (fileStorageDidAddItem.matches(action)) {
|
||||
return [...state, action.file];
|
||||
}
|
||||
|
||||
if (fileStorageDidChangeItem.matches(action)) {
|
||||
return [...state].map((f) => (f.uuid === action.file.uuid ? action.file : f));
|
||||
}
|
||||
|
||||
if (fileStorageDidRemoveItem.matches(action)) {
|
||||
return [...state].filter((value) => value.uuid !== action.file.uuid);
|
||||
}
|
||||
|
||||
return state;
|
||||
};
|
||||
|
||||
export default combineReducers({ files, renameFileDialog });
|
||||
|
||||
@@ -24,7 +24,7 @@ const RenameFileDialog: React.VFC = () => {
|
||||
const [baseName, extension] = oldName.split(/(\.\w+)$/);
|
||||
|
||||
const [newName, setNewName] = useState(baseName);
|
||||
const files = useSelector((s) => s.fileStorage.files);
|
||||
const files = useSelector((s) => s.explorer.files);
|
||||
const result = validateFileName(
|
||||
newName,
|
||||
extension,
|
||||
|
||||
@@ -120,7 +120,7 @@ function* handleExplorerImportFiles(): Generator {
|
||||
const text = yield* call(() => file.text());
|
||||
|
||||
const [baseName] = file.name.split(pythonFileExtensionRegex);
|
||||
const existingFiles = yield* select((s: RootState) => s.fileStorage.files);
|
||||
const existingFiles = yield* select((s: RootState) => s.explorer.files);
|
||||
|
||||
const result = validateFileName(
|
||||
baseName,
|
||||
|
||||
@@ -2,14 +2,7 @@
|
||||
// Copyright (c) 2022 The Pybricks Authors
|
||||
|
||||
import { AnyAction } from 'redux';
|
||||
import { uuid } from '../../test';
|
||||
import {
|
||||
FileMetadata,
|
||||
fileStorageDidAddItem,
|
||||
fileStorageDidChangeItem,
|
||||
fileStorageDidInitialize,
|
||||
fileStorageDidRemoveItem,
|
||||
} from './actions';
|
||||
import { fileStorageDidInitialize } from './actions';
|
||||
import reducers from './reducers';
|
||||
|
||||
type State = ReturnType<typeof reducers>;
|
||||
@@ -17,7 +10,6 @@ type State = ReturnType<typeof reducers>;
|
||||
test('initial state', () => {
|
||||
expect(reducers(undefined, {} as AnyAction)).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"files": Array [],
|
||||
"isInitialized": false,
|
||||
}
|
||||
`);
|
||||
@@ -29,47 +21,3 @@ test('isInitialized', () => {
|
||||
.isInitialized,
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
test('fileNames', () => {
|
||||
const testFile: FileMetadata = {
|
||||
uuid: uuid(0),
|
||||
path: 'test.file',
|
||||
sha256: '',
|
||||
};
|
||||
|
||||
const modifiedFile: FileMetadata = { ...testFile, path: 'modified.file' };
|
||||
|
||||
expect(testFile).not.toEqual(modifiedFile);
|
||||
|
||||
// initialization populates file list
|
||||
expect(
|
||||
reducers(
|
||||
{ files: [] as readonly FileMetadata[] } as State,
|
||||
fileStorageDidInitialize([testFile]),
|
||||
).files,
|
||||
).toEqual([testFile]);
|
||||
|
||||
// adding appends an item
|
||||
expect(
|
||||
reducers(
|
||||
{ files: [] as readonly FileMetadata[] } as State,
|
||||
fileStorageDidAddItem(testFile),
|
||||
).files,
|
||||
).toEqual([testFile]);
|
||||
|
||||
// changing replaces an item
|
||||
expect(
|
||||
reducers(
|
||||
{ files: [testFile] as readonly FileMetadata[] } as State,
|
||||
fileStorageDidChangeItem(testFile, modifiedFile),
|
||||
).files,
|
||||
).toEqual([modifiedFile]);
|
||||
|
||||
// removing deletes an item
|
||||
expect(
|
||||
reducers(
|
||||
{ files: [testFile] as readonly FileMetadata[] } as State,
|
||||
fileStorageDidRemoveItem(testFile),
|
||||
).files,
|
||||
).not.toContain(testFile);
|
||||
});
|
||||
|
||||
@@ -2,13 +2,7 @@
|
||||
// Copyright (c) 2022 The Pybricks Authors
|
||||
|
||||
import { Reducer, combineReducers } from 'redux';
|
||||
import {
|
||||
FileMetadata,
|
||||
fileStorageDidAddItem,
|
||||
fileStorageDidChangeItem,
|
||||
fileStorageDidInitialize,
|
||||
fileStorageDidRemoveItem,
|
||||
} from './actions';
|
||||
import { fileStorageDidInitialize } from './actions';
|
||||
|
||||
const isInitialized: Reducer<boolean> = (state = false, action) => {
|
||||
if (fileStorageDidInitialize.matches(action)) {
|
||||
@@ -18,24 +12,4 @@ const isInitialized: Reducer<boolean> = (state = false, action) => {
|
||||
return state;
|
||||
};
|
||||
|
||||
const files: Reducer<readonly FileMetadata[]> = (state = [], action) => {
|
||||
if (fileStorageDidInitialize.matches(action)) {
|
||||
return [...action.files];
|
||||
}
|
||||
|
||||
if (fileStorageDidAddItem.matches(action)) {
|
||||
return [...state, action.file];
|
||||
}
|
||||
|
||||
if (fileStorageDidChangeItem.matches(action)) {
|
||||
return [...state].map((f) => (f.uuid === action.file.uuid ? action.file : f));
|
||||
}
|
||||
|
||||
if (fileStorageDidRemoveItem.matches(action)) {
|
||||
return [...state].filter((value) => value.uuid !== action.file.uuid);
|
||||
}
|
||||
|
||||
return state;
|
||||
};
|
||||
|
||||
export default combineReducers({ isInitialized, files });
|
||||
export default combineReducers({ isInitialized });
|
||||
|
||||
Reference in New Issue
Block a user