drop explicit state interfaces

This could cause subtle bugs like the one fixed here where license
was renamed to licenses.
This commit is contained in:
David Lechner
2021-02-01 10:26:05 -06:00
parent 650888bbf7
commit e5073399a0
13 changed files with 25 additions and 74 deletions
-12
View File
@@ -9,18 +9,6 @@ import { ServiceWorkerActionType } from '../service-worker/actions';
import { BeforeInstallPromptEvent } from '../utils/dom';
import { AppActionType } from './actions';
export interface AppState {
readonly showSettings: boolean;
readonly showAboutDialog: boolean;
readonly showLicenseDialog: boolean;
readonly serviceWorker: ServiceWorkerRegistration | null;
readonly checkingForUpdate: boolean;
readonly updateAvailable: boolean;
readonly beforeInstallPrompt: BeforeInstallPromptEvent;
readonly promptingInstall: boolean;
readonly readyForOfflineUse: boolean;
}
const showSettings: Reducer<boolean, Action> = (state = false, action) => {
switch (action.type) {
case AppActionType.OpenSettings:
-4
View File
@@ -49,8 +49,4 @@ const connection: Reducer<BleConnectionState, Action> = (
}
};
export interface BleState {
readonly connection: BleConnectionState;
}
export default combineReducers({ connection });
-3
View File
@@ -15,7 +15,4 @@ const current: Reducer<Ace.EditSession | null, Action> = (state = null, action)
}
};
export interface EditorState {
current: Ace.EditSession | null;
}
export default combineReducers({ current });
-7
View File
@@ -5,13 +5,6 @@ import { Reducer, combineReducers } from 'redux';
import { Action } from '../actions';
import { FlashFirmwareActionType } from './actions';
export interface FirmwareState {
/** The firmware is being erased/flashed right now. */
flashing: boolean;
/** The current progress (0 to 1) or null for unknown (e.g erasing) */
progress: number | null;
}
const flashing: Reducer<boolean, Action> = (state = false, action) => {
switch (action.type) {
case FlashFirmwareActionType.DidStart:
-4
View File
@@ -70,8 +70,4 @@ const runtime: Reducer<HubRuntimeState, Action> = (
}
};
export interface HubState {
readonly runtime: HubRuntimeState;
}
export default combineReducers({ runtime });
+1 -1
View File
@@ -13,7 +13,7 @@ import './index.scss';
import App from './app/App';
import { i18nManager } from './i18n';
import * as I18nToaster from './notifications/I18nToaster';
import rootReducer from './reducers';
import { rootReducer } from './reducers';
import reportWebVitals from './reportWebVitals';
import rootSaga from './sagas';
import { didSucceed, didUpdate } from './service-worker/actions';
+2 -2
View File
@@ -147,8 +147,8 @@ class LicenseDialog extends React.Component<LicenseDialogProps> {
const mapStateToProps = (state: RootState): StateProps => ({
showLicenseDialog: state.app.showLicenseDialog,
licenseList: state.license.list,
licenseInfo: state.license.selected,
licenseList: state.licenses.list,
licenseInfo: state.licenses.selected,
});
const mapDispatchToProps: DispatchProps = {
-5
View File
@@ -15,11 +15,6 @@ export interface LicenseInfo {
export type LicenseList = LicenseInfo[];
export interface LicenseState {
readonly list: LicenseList | null;
readonly selected: LicenseInfo | null;
}
const list: Reducer<LicenseList | null, Action> = (state = null, action) => {
switch (action.type) {
case LicenseActionType.DidFetchList:
+3 -3
View File
@@ -16,7 +16,7 @@ afterAll(() => {
describe('fetchLicenses', () => {
test('first call', async () => {
const testLicenseList: LicenseList = [];
const saga = new AsyncSaga(license, { license: { list: null } });
const saga = new AsyncSaga(license, { licenses: { list: null } });
jest.spyOn(globalThis, 'fetch').mockResolvedValue(
new Response(JSON.stringify(testLicenseList)),
@@ -33,7 +33,7 @@ describe('fetchLicenses', () => {
});
test('second call', async () => {
const testLicenseList: LicenseList = [];
const saga = new AsyncSaga(license, { license: { list: testLicenseList } });
const saga = new AsyncSaga(license, { licenses: { list: testLicenseList } });
jest.spyOn(globalThis, 'fetch').mockRejectedValue(
'fetch () should not have been called',
@@ -50,7 +50,7 @@ describe('fetchLicenses', () => {
});
test('failed fetch', async () => {
const failResponse = new Response(undefined, { status: 404 });
const saga = new AsyncSaga(license, { license: { list: null } });
const saga = new AsyncSaga(license, { licenses: { list: null } });
jest.spyOn(globalThis, 'fetch').mockResolvedValue(failResponse);
+1 -1
View File
@@ -7,7 +7,7 @@ import { RootState } from '../reducers';
import { didFailToFetchList, didFetchList } from './actions';
function* fetchLicenses(): Generator {
const licenses = yield* select((s: RootState) => s.license.list);
const licenses = yield* select((s: RootState) => s.licenses.list);
// if we already have license list, nothing to do
if (licenses !== null) {
-4
View File
@@ -47,8 +47,4 @@ const connection: Reducer<BootloaderConnectionState, Action> = (
}
};
export interface BootloaderState {
readonly connection: BootloaderConnectionState;
}
export default combineReducers({ connection });
+18 -22
View File
@@ -1,31 +1,20 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2020-2021 The Pybricks Authors
import { combineReducers } from 'redux';
import app, { AppState } from './app/reducers';
import ble, { BleState } from './ble/reducers';
import editor, { EditorState } from './editor/reducers';
import firmware, { FirmwareState } from './firmware/reducers';
import hub, { HubState } from './hub/reducers';
import licenses, { LicenseState } from './licenses/reducers';
import bootloader, { BootloaderState } from './lwp3-bootloader/reducers';
import settings, { SettingsState } from './settings/reducers';
import { Reducer, combineReducers } from 'redux';
import app from './app/reducers';
import ble from './ble/reducers';
import editor from './editor/reducers';
import firmware from './firmware/reducers';
import hub from './hub/reducers';
import licenses from './licenses/reducers';
import bootloader from './lwp3-bootloader/reducers';
import settings from './settings/reducers';
/**
* Root state for redux store.
* Root reducer for redux store.
*/
export interface RootState {
readonly app: AppState;
readonly bootloader: BootloaderState;
readonly ble: BleState;
readonly editor: EditorState;
readonly firmware: FirmwareState;
readonly hub: HubState;
readonly license: LicenseState;
readonly settings: SettingsState;
}
export default combineReducers({
export const rootReducer = combineReducers({
app,
bootloader,
ble,
@@ -35,3 +24,10 @@ export default combineReducers({
licenses,
settings,
});
/**
* Root state for redux store.
*/
type StateFromReducer<R> = R extends Reducer<infer S> ? S : never;
export type RootState = StateFromReducer<typeof rootReducer>;
-6
View File
@@ -6,12 +6,6 @@ import { Action } from '../actions';
import { SettingsActionType } from './actions';
import { SettingId, getDefaultBooleanValue } from './defaults';
export interface SettingsState {
readonly darkMode: boolean;
readonly showDocs: boolean;
readonly flashCurrentProgram: boolean;
}
const darkMode: Reducer<boolean, Action> = (
state = getDefaultBooleanValue(SettingId.DarkMode),
action,