make license dialog state local

This commit is contained in:
David Lechner
2021-02-01 10:26:05 -06:00
parent e5073399a0
commit 89523b3915
8 changed files with 44 additions and 74 deletions
+14 -6
View File
@@ -7,7 +7,7 @@ import { AnchorButton, Button, Classes, Dialog } from '@blueprintjs/core';
import { WithI18nProps, withI18n } from '@shopify/react-i18n';
import React from 'react';
import { connect } from 'react-redux';
import { closeAboutDialog, openLicenseDialog } from '../app/actions';
import { closeAboutDialog } from '../app/actions';
import {
appName,
legoDisclaimer,
@@ -26,13 +26,17 @@ const version = process.env.REACT_APP_VERSION;
type StateProps = { showAboutDialog: boolean };
type DispatchProps = { onClose: () => void; onLicenseButtonClick: () => void };
type DispatchProps = { onClose: () => void };
type AboutDialogProps = StateProps & DispatchProps & WithI18nProps;
class AboutDialog extends React.Component<AboutDialogProps> {
public state = {
licenseDialogIsOpen: false,
};
render(): JSX.Element {
const { i18n, showAboutDialog, onClose, onLicenseButtonClick } = this.props;
const { i18n, showAboutDialog, onClose } = this.props;
return (
<Dialog
title={`${appName} v${version}`}
@@ -53,7 +57,9 @@ class AboutDialog extends React.Component<AboutDialogProps> {
<small>{legoDisclaimer}</small>
</p>
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
<Button onClick={() => onLicenseButtonClick()}>
<Button
onClick={() => this.setState({ licenseDialogIsOpen: true })}
>
{i18n.translate(AboutStringId.LicenseButtonLabel)}
</Button>
<AnchorButton href={pybricksWebsiteUrl} target="blank_">
@@ -62,7 +68,10 @@ class AboutDialog extends React.Component<AboutDialogProps> {
</AnchorButton>
</div>
</div>
<LicenseDialog />
<LicenseDialog
isOpen={this.state.licenseDialogIsOpen}
onClose={() => this.setState({ licenseDialogIsOpen: false })}
/>
</Dialog>
);
}
@@ -74,7 +83,6 @@ const mapStateToProps = (state: RootState): StateProps => ({
const mapDispatchToProps: DispatchProps = {
onClose: closeAboutDialog,
onLicenseButtonClick: openLicenseDialog,
};
export default connect(
+1 -23
View File
@@ -32,10 +32,6 @@ export enum AppActionType {
OpenAboutDialog = 'app.action.openAboutDialog',
/** Close about dialog. */
CloseAboutDialog = 'app.action.closeAboutDialog',
/** Open license dialog. */
OpenLicenseDialog = 'app.action.openLicenseDialog',
/** Close license dialog. */
CloseLicenseDialog = 'app.action.closeLicenseDialog',
}
/** Action that requests the app to reload. */
@@ -148,22 +144,6 @@ export function closeAboutDialog(): AppCloseAboutDialogAction {
return { type: AppActionType.CloseAboutDialog };
}
/** Action to open the license dialog. */
export type AppOpenLicenseDialogAction = Action<AppActionType.OpenLicenseDialog>;
/** Creates an action to open the license dialog. */
export function openLicenseDialog(): AppOpenLicenseDialogAction {
return { type: AppActionType.OpenLicenseDialog };
}
/** Action to close the license dialog. */
export type AppCloseLicenseDialogAction = Action<AppActionType.CloseLicenseDialog>;
/** Creates an action to close the license dialog. */
export function closeLicenseDialog(): AppCloseLicenseDialogAction {
return { type: AppActionType.CloseLicenseDialog };
}
/** common type for all app actions. */
export type AppAction =
| AppReloadAction
@@ -177,6 +157,4 @@ export type AppAction =
| AppOpenSettingsAction
| AppCloseSettingsAction
| AppOpenAboutDialogAction
| AppCloseAboutDialogAction
| AppOpenLicenseDialogAction
| AppCloseLicenseDialogAction;
| AppCloseAboutDialogAction;
-14
View File
@@ -7,7 +7,6 @@ import { BeforeInstallPromptEvent } from '../utils/dom';
import {
checkForUpdate,
closeAboutDialog,
closeLicenseDialog,
closeSettings,
didBeforeInstallPrompt,
didCheckForUpdate,
@@ -15,7 +14,6 @@ import {
didInstallPrompt,
installPrompt,
openAboutDialog,
openLicenseDialog,
openSettings,
} from './actions';
import reducers from './reducers';
@@ -31,7 +29,6 @@ test('initial state', () => {
"readyForOfflineUse": false,
"serviceWorker": null,
"showAboutDialog": false,
"showLicenseDialog": false,
"showSettings": false,
"updateAvailable": false,
}
@@ -58,17 +55,6 @@ test('showAboutDialog', () => {
).toBe(false);
});
test('showLicenseDialog', () => {
expect(
reducers({ showLicenseDialog: false } as State, openLicenseDialog())
.showLicenseDialog,
).toBe(true);
expect(
reducers({ showLicenseDialog: true } as State, closeLicenseDialog())
.showLicenseDialog,
).toBe(false);
});
test('serviceWorker', () => {
const registration = {} as ServiceWorkerRegistration;
expect(
-12
View File
@@ -31,17 +31,6 @@ const showAboutDialog: Reducer<boolean, Action> = (state = false, action) => {
}
};
const showLicenseDialog: Reducer<boolean, Action> = (state = false, action) => {
switch (action.type) {
case AppActionType.OpenLicenseDialog:
return true;
case AppActionType.CloseLicenseDialog:
return false;
default:
return state;
}
};
const serviceWorker: Reducer<ServiceWorkerRegistration | null, Action> = (
state = null,
action,
@@ -117,7 +106,6 @@ const readyForOfflineUse: Reducer<boolean, Action> = (state = false, action) =>
export default combineReducers({
showSettings,
showAboutDialog,
showLicenseDialog,
serviceWorker,
checkingForUpdate,
updateAvailable,
+15 -11
View File
@@ -15,10 +15,9 @@ import {
import { WithI18nProps, withI18n } from '@shopify/react-i18n';
import React from 'react';
import { connect } from 'react-redux';
import { closeLicenseDialog } from '../app/actions';
import { appName } from '../app/constants';
import { RootState } from '../reducers';
import { select } from './actions';
import { fetchList, select } from './actions';
import { LicenseStringId } from './i18n';
import en from './i18n.en.json';
import { LicenseInfo, LicenseList } from './reducers';
@@ -26,34 +25,40 @@ import { LicenseInfo, LicenseList } from './reducers';
import './license.scss';
type StateProps = {
showLicenseDialog: boolean;
licenseList: LicenseList | null;
licenseInfo: LicenseInfo | null;
};
type DispatchProps = {
onClose: () => void;
onOpening: () => void;
onSelectPackage: (info: LicenseInfo) => void;
};
type LicenseDialogProps = StateProps & DispatchProps & WithI18nProps;
type OwnProps = {
isOpen: boolean;
onClose(): void;
};
type LicenseDialogProps = StateProps & DispatchProps & OwnProps & WithI18nProps;
class LicenseDialog extends React.Component<LicenseDialogProps> {
render(): JSX.Element {
const infoDiv = React.createRef<HTMLDivElement>();
const {
i18n,
showLicenseDialog,
onClose,
licenseList,
licenseInfo,
onOpening,
onSelectPackage,
isOpen,
onClose,
i18n,
} = this.props;
return (
<Dialog
title={i18n.translate(LicenseStringId.Title)}
isOpen={showLicenseDialog}
isOpen={isOpen}
onOpening={() => onOpening()}
onClose={() => onClose()}
className="pb-license-dialog"
>
@@ -146,13 +151,12 @@ class LicenseDialog extends React.Component<LicenseDialogProps> {
}
const mapStateToProps = (state: RootState): StateProps => ({
showLicenseDialog: state.app.showLicenseDialog,
licenseList: state.licenses.list,
licenseInfo: state.licenses.selected,
});
const mapDispatchToProps: DispatchProps = {
onClose: closeLicenseDialog,
onOpening: fetchList,
onSelectPackage: select,
};
+8
View File
@@ -5,11 +5,18 @@ import { Action } from 'redux';
import { LicenseInfo, LicenseList } from './reducers';
export enum LicenseActionType {
FetchList = 'license.action.fetchList',
DidFetchList = 'license.action.didFetchList',
DidFailToFetchList = 'license.action.didFailToFetchList',
Select = 'license.action.select',
}
export type LicenseFetchListAction = Action<LicenseActionType.FetchList>;
export function fetchList(): LicenseFetchListAction {
return { type: LicenseActionType.FetchList };
}
export type LicenseDidFetchListAction = Action<LicenseActionType.DidFetchList> & {
list: LicenseList;
};
@@ -35,6 +42,7 @@ export function select(info: LicenseInfo): LicenseSelectAction {
}
export type LicenseAction =
| LicenseFetchListAction
| LicenseDidFetchListAction
| LicenseDidFailToFetchListAction
| LicenseSelectAction;
+4 -5
View File
@@ -4,8 +4,7 @@
// Tests for license sagas.
import { AsyncSaga, delay } from '../../test';
import { openLicenseDialog } from '../app/actions';
import { didFailToFetchList, didFetchList } from './actions';
import { didFailToFetchList, didFetchList, fetchList } from './actions';
import { LicenseList } from './reducers';
import license from './sagas';
@@ -24,7 +23,7 @@ describe('fetchLicenses', () => {
// initially, license list starts as null, so fetch is called to get
// the list
saga.put(openLicenseDialog());
saga.put(fetchList());
const action = await saga.take();
expect(action).toEqual(didFetchList(testLicenseList));
@@ -41,7 +40,7 @@ describe('fetchLicenses', () => {
// after we have the list, we don't fetch it again since it will
// always be the same list
saga.put(openLicenseDialog());
saga.put(fetchList());
// have to yield to be sure fetch call would have taken place on error
await delay(0);
@@ -54,7 +53,7 @@ describe('fetchLicenses', () => {
jest.spyOn(globalThis, 'fetch').mockResolvedValue(failResponse);
saga.put(openLicenseDialog());
saga.put(fetchList());
const action = await saga.take();
expect(action).toEqual(didFailToFetchList(failResponse));
+2 -3
View File
@@ -2,9 +2,8 @@
// Copyright (c) 2021 The Pybricks Authors
import { call, put, select, takeEvery } from 'typed-redux-saga/macro';
import { AppActionType } from '../app/actions';
import { RootState } from '../reducers';
import { didFailToFetchList, didFetchList } from './actions';
import { LicenseActionType, didFailToFetchList, didFetchList } from './actions';
function* fetchLicenses(): Generator {
const licenses = yield* select((s: RootState) => s.licenses.list);
@@ -25,5 +24,5 @@ function* fetchLicenses(): Generator {
}
export default function* (): Generator {
yield* takeEvery(AppActionType.OpenLicenseDialog, fetchLicenses);
yield* takeEvery(LicenseActionType.FetchList, fetchLicenses);
}