diff --git a/CHANGELOG.md b/CHANGELOG.md index 37311cf7..08821870 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ ## [Unreleased] +### Fixed +- Fixed starting DFU flash while another was still in progress ([support#1146]). + +[support#1146]: https://github.com/pybricks/support/issues/1146 ## [2.2.0-beta.8] - 2023-07-07 ### Changed diff --git a/src/firmware/installPybricksDialog/InstallPybricksDialog.tsx b/src/firmware/installPybricksDialog/InstallPybricksDialog.tsx index 355b0977..b5643ab6 100644 --- a/src/firmware/installPybricksDialog/InstallPybricksDialog.tsx +++ b/src/firmware/installPybricksDialog/InstallPybricksDialog.tsx @@ -450,6 +450,11 @@ const BootloaderModePanel: React.FunctionComponent = ( export const InstallPybricksDialog: React.FunctionComponent = () => { const { isOpen } = useSelector((s) => s.firmware.installPybricksDialog); + const inProgress = useSelector( + (s) => + s.firmware.isFirmwareFlashUsbDfuInProgress || + s.firmware.isFirmwareRestoreOfficialDfuInProgress, + ); const dispatch = useDispatch(); const [hubName, setHubName] = useState(''); const [licenseAccepted, setLicenseAccepted] = useState(false); @@ -476,6 +481,7 @@ export const InstallPybricksDialog: React.FunctionComponent = () => { nextButtonProps={{ text: i18n.translate('nextButton.label') }} finalButtonProps={{ text: i18n.translate('flashFirmwareButton.label'), + disabled: inProgress, onClick: () => dispatch( firmwareInstallPybricksDialogAccept( diff --git a/src/firmware/reducers.test.ts b/src/firmware/reducers.test.ts index 76ccc47a..1e93e6c3 100644 --- a/src/firmware/reducers.test.ts +++ b/src/firmware/reducers.test.ts @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// Copyright (c) 2021-2022 The Pybricks Authors +// Copyright (c) 2021-2023 The Pybricks Authors import { AnyAction } from 'redux'; import { @@ -23,6 +23,8 @@ test('initial state', () => { "installPybricksDialog": { "isOpen": false, }, + "isFirmwareFlashUsbDfuInProgress": false, + "isFirmwareRestoreOfficialDfuInProgress": false, "progress": null, "restoreOfficialDialog": { "isOpen": false, diff --git a/src/firmware/reducers.ts b/src/firmware/reducers.ts index 323dcf15..fd73fb65 100644 --- a/src/firmware/reducers.ts +++ b/src/firmware/reducers.ts @@ -1,8 +1,19 @@ // SPDX-License-Identifier: MIT -// Copyright (c) 2021-2022 The Pybricks Authors +// Copyright (c) 2021-2023 The Pybricks Authors import { Reducer, combineReducers } from 'redux'; -import { didFailToFinish, didFinish, didProgress, didStart } from './actions'; +import { + didFailToFinish, + didFinish, + didProgress, + didStart, + firmwareDidFailToFlashUsbDfu, + firmwareDidFailToRestoreOfficialDfu, + firmwareDidFlashUsbDfu, + firmwareDidRestoreOfficialDfu, + firmwareFlashUsbDfu, + firmwareRestoreOfficialDfu, +} from './actions'; import dfuWindowsDriverInstallDialog from './dfuWindowsDriverInstallDialog/reducers'; import installPybricksDialog from './installPybricksDialog/reducers'; import restoreOfficialDialog from './restoreOfficialDialog/reducers'; @@ -31,10 +42,47 @@ const progress: Reducer = (state = null, action) => { return state; }; +const isFirmwareFlashUsbDfuInProgress: Reducer = (state = false, action) => { + if (firmwareFlashUsbDfu.matches(action)) { + return true; + } + + if (firmwareDidFlashUsbDfu.matches(action)) { + return false; + } + + if (firmwareDidFailToFlashUsbDfu.matches(action)) { + return false; + } + + return state; +}; + +const isFirmwareRestoreOfficialDfuInProgress: Reducer = ( + state = false, + action, +) => { + if (firmwareRestoreOfficialDfu.matches(action)) { + return true; + } + + if (firmwareDidRestoreOfficialDfu.matches(action)) { + return false; + } + + if (firmwareDidFailToRestoreOfficialDfu.matches(action)) { + return false; + } + + return state; +}; + export default combineReducers({ dfuWindowsDriverInstallDialog, installPybricksDialog, restoreOfficialDialog, flashing, progress, + isFirmwareFlashUsbDfuInProgress, + isFirmwareRestoreOfficialDfuInProgress, }); diff --git a/src/firmware/restoreOfficialDialog/RestoreOfficialDialog.tsx b/src/firmware/restoreOfficialDialog/RestoreOfficialDialog.tsx index 64cb9cb3..cd048355 100644 --- a/src/firmware/restoreOfficialDialog/RestoreOfficialDialog.tsx +++ b/src/firmware/restoreOfficialDialog/RestoreOfficialDialog.tsx @@ -9,7 +9,7 @@ import { MultistepDialog, } from '@blueprintjs/core'; import classNames from 'classnames'; -import React from 'react'; +import React, { useCallback } from 'react'; import { useDispatch } from 'react-redux'; import { legoEducationSpikeRegisteredTrademark, @@ -46,6 +46,15 @@ const RestoreFirmwarePanel: React.FunctionComponent = () => { const [hubType] = useHubPickerSelectedHub(); const dispatch = useDispatch(); const i18n = useI18n(); + const inProgress = useSelector( + (s) => + s.firmware.isFirmwareFlashUsbDfuInProgress || + s.firmware.isFirmwareRestoreOfficialDfuInProgress, + ); + + const handleRestoreButtonClick = useCallback(() => { + dispatch(firmwareRestoreOfficialDfu(hubType)); + }, [dispatch, hubType]); return (
@@ -72,7 +81,8 @@ const RestoreFirmwarePanel: React.FunctionComponent = () => {