mirror of
https://github.com/pybricks/pybricks-code.git
synced 2026-09-12 01:23:52 +00:00
make license dialog state local
This commit is contained in:
@@ -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
@@ -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;
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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,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,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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user