ble/alerts: move old firmware from notifications

also add button to open new install firmware dialog
This commit is contained in:
David Lechner
2022-07-20 17:26:08 -05:00
parent 25a0ce7633
commit 1d2c55eba6
12 changed files with 97 additions and 37 deletions
+2 -2
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2022 The Pybricks Authors
import './noHub.scss';
import './index.scss';
import { AnchorButton, Button, Intent } from '@blueprintjs/core';
import React from 'react';
import { appName, pybricksBluetoothTroubleshootingUrl } from '../../app/constants';
@@ -30,7 +30,7 @@ const NoHub: React.VoidFunctionComponent<NoHubProps> = ({ onFlashFirmware }) =>
})}
</p>
<p>{i18n.translate(I18nId.NoHubSuggestion2)}</p>
<div className="pb-ble-alerts-noHub-buttons">
<div className="pb-ble-alerts-buttons">
<Button icon="download" onClick={onFlashFirmware}>
{i18n.translate(I18nId.NoHubFlashFirmwareButton)}
</Button>
+40
View File
@@ -0,0 +1,40 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2022 The Pybricks Authors
import './index.scss';
import { Button, Intent } from '@blueprintjs/core';
import React from 'react';
import { CreateToast } from '../../i18nToaster';
import { I18nId, useI18n } from './i18n';
type OldFirmwareProps = {
onFlashFirmware: () => void;
};
const OldFirmware: React.VoidFunctionComponent<OldFirmwareProps> = ({
onFlashFirmware,
}) => {
const i18n = useI18n();
return (
<>
<p>{i18n.translate(I18nId.OldFirmwareMessage)}</p>
<div className="pb-ble-alerts-buttons">
<Button icon="download" onClick={onFlashFirmware}>
{i18n.translate(I18nId.OldFirmwareFlashFirmwareLabel)}
</Button>
</div>
</>
);
};
export const oldFirmware: CreateToast<never, 'dismiss' | 'flashFirmware'> = (
onAction,
) => {
return {
message: <OldFirmware onFlashFirmware={() => onAction('flashFirmware')} />,
icon: 'info-sign',
intent: Intent.PRIMARY,
onDismiss: () => onAction('dismiss'),
};
};
+2
View File
@@ -24,4 +24,6 @@ export enum I18nId {
NoHubSuggestion2 = 'noHub.suggestion2',
NoHubFlashFirmwareButton = 'noHub.flashFirmwareButton',
NoHubTroubleshootButton = 'noHub.troubleshootButton',
OldFirmwareMessage = 'oldFirmware.message',
OldFirmwareFlashFirmwareLabel = 'oldFirmware.flashFirmware.label',
}
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2022 The Pybricks Authors
.pb-ble-alerts-noHub {
.pb-ble-alerts {
&-buttons {
display: flex;
gap: 10px;
+9 -1
View File
@@ -6,6 +6,14 @@ import { missingService } from './MissingService';
import { noGatt } from './NoGatt';
import { noHub } from './NoHub';
import { noWebBluetooth } from './NoWebBluetooth';
import { oldFirmware } from './OldFirmware';
// gathers all of the alert creation functions for passing up to the top level
export default { bluetoothNotAvailable, missingService, noGatt, noHub, noWebBluetooth };
export default {
bluetoothNotAvailable,
missingService,
noGatt,
noHub,
noWebBluetooth,
oldFirmware,
};
+6
View File
@@ -23,5 +23,11 @@
"suggestion2": "If you have flashed the Pybricks firmware to the hub already and you are still having problems connecting, please visit the troubleshooting guide.",
"flashFirmwareButton": "Flash Firmware",
"troubleshootButton": "Troubleshooting Tips"
},
"oldFirmware": {
"message": "A new firmware version is available for this hub. Please install the latest version to use all new features.",
"flashFirmware": {
"label": "Flash firmware now"
}
}
}
+3 -1
View File
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2022 The Pybricks Authors
import { HubType } from '@pybricks/firmware';
import { MockProxy, mock } from 'jest-mock-extended';
import { AsyncSaga } from '../../test';
import { alertsDidShowAlert, alertsShowAlert } from '../alerts/actions';
@@ -17,6 +16,7 @@ import {
softwareRevisionStringUUID,
} from '../ble-device-info-service/protocol';
import { encodeInfo } from '../ble-device-info-service/protocol.test';
import { HubType } from '../ble-lwp3-service/protocol';
import {
nordicUartRxCharUUID,
nordicUartServiceUUID,
@@ -204,6 +204,8 @@ async function runConnectUntil(saga: AsyncSaga, point: ConnectRunPoint): Promise
bleDIServiceDidReceiveFirmwareRevision('3.2.0b2'),
);
await expect(saga.take()).resolves.toEqual(alertsShowAlert('ble', 'oldFirmware'));
if (point === ConnectRunPoint.DidReceiveFirmwareRevision) {
return;
}
+33
View File
@@ -6,7 +6,9 @@
// TODO: this file needs to be combined with the firmware BLE connection management
// to reduce duplicated code
import { firmwareVersion } from '@pybricks/firmware';
import { Task, buffers, eventChannel } from 'redux-saga';
import { satisfies } from 'semver';
import {
call,
cancel,
@@ -14,6 +16,7 @@ import {
fork,
put,
select,
spawn,
take,
takeEvery,
} from 'typed-redux-saga/macro';
@@ -54,6 +57,7 @@ import {
import { firmwareInstallPybricks } from '../firmware/actions';
import { RootState } from '../reducers';
import { ensureError } from '../utils';
import { pythonVersionToSemver } from '../utils/version';
import {
bleConnectPybricks as bleConnectPybricks,
bleDidConnectPybricks,
@@ -219,6 +223,35 @@ function* handleBleConnectPybricks(): Generator {
);
yield* put(bleDIServiceDidReceiveFirmwareRevision(firmwareRevision));
// notify user if old firmware
if (
satisfies(
pythonVersionToSemver(firmwareRevision),
`<${pythonVersionToSemver(firmwareVersion)}`,
)
) {
yield* put(alertsShowAlert('ble', 'oldFirmware'));
// initiate flashing firmware if user requested
const flashIfRequested = function* () {
const { action } = yield* take<
ReturnType<typeof alertsDidShowAlert<'ble', 'oldFirmware'>>
>(
alertsDidShowAlert.when(
(a) => a.domain === 'ble' && a.specific === 'oldFirmware',
),
);
if (action === 'flashFirmware') {
yield* put(firmwareInstallPybricks());
}
};
// have to spawn so that we don't block the task and it still works
// if parent task ends
yield* spawn(flashIfRequested);
}
const softwareVersionChar = yield* call(() =>
deviceInfoService.getCharacteristic(softwareRevisionStringUUID),
);
-1
View File
@@ -39,5 +39,4 @@ export enum I18nId {
ServiceWorkerUpdateMessage = 'serviceWorker.update.message',
ServiceWorkerUpdateAction = 'serviceWorker.update.action',
MpyError = 'mpy.error',
CheckFirmwareTooOld = 'check.firmwareTooOld',
}
+1 -8
View File
@@ -2,16 +2,11 @@
// Copyright (c) 2021-2022 The Pybricks Authors
import { IToaster } from '@blueprintjs/core';
import {
FirmwareReaderError,
FirmwareReaderErrorCode,
firmwareVersion,
} from '@pybricks/firmware';
import { FirmwareReaderError, FirmwareReaderErrorCode } from '@pybricks/firmware';
import { I18nManager } from '@shopify/react-i18n';
import { AnyAction } from 'redux';
import { AsyncSaga, uuid } from '../../test';
import { appDidCheckForUpdate } from '../app/actions';
import { bleDIServiceDidReceiveFirmwareRevision } from '../ble-device-info-service/actions';
import { editorDidFailToOpenFile } from '../editor/actions';
import { EditorError } from '../editor/error';
import {
@@ -91,7 +86,6 @@ test.each([
didFailToFinish(FailToFinishReasonType.FirmwareSize),
didFailToFinish(FailToFinishReasonType.Unknown, new Error('test error')),
appDidCheckForUpdate(false),
bleDIServiceDidReceiveFirmwareRevision('3.0.0'),
fileStorageDidFailToInitialize(new Error('test error')),
explorerDidFailToImportFiles(new Error('test error')),
explorerDidFailToCreateNewFile(new Error('test error')),
@@ -118,7 +112,6 @@ test.each([
didFailToFinish(FailToFinishReasonType.FailedToConnect),
serviceWorkerDidSucceed(),
appDidCheckForUpdate(true),
bleDIServiceDidReceiveFirmwareRevision(firmwareVersion),
explorerDidFailToImportFiles(new DOMException('test message', 'AbortError')),
explorerDidFailToCreateNewFile(new DOMException('test message', 'AbortError')),
explorerDidFailToDuplicateFile(
-20
View File
@@ -4,16 +4,13 @@
// Saga for managing notifications (toasts)
import { ActionProps, IToaster, IconName, Intent, LinkProps } from '@blueprintjs/core';
import { firmwareVersion } from '@pybricks/firmware';
import { Replacements } from '@shopify/react-i18n';
import React from 'react';
import { channel } from 'redux-saga';
import * as semver from 'semver';
import { delay, getContext, put, take, takeEvery } from 'typed-redux-saga/macro';
import { getAlertProps } from '../alerts';
import { appDidCheckForUpdate, appReload } from '../app/actions';
import { appName } from '../app/constants';
import { bleDIServiceDidReceiveFirmwareRevision } from '../ble-device-info-service/actions';
import { editorDidFailToOpenFile } from '../editor/actions';
import { EditorError } from '../editor/error';
import {
@@ -31,7 +28,6 @@ import {
} from '../lwp3-bootloader/actions';
import { didCompile, didFailToCompile } from '../mpy/actions';
import { serviceWorkerDidUpdate } from '../service-worker/actions';
import { pythonVersionToSemver } from '../utils/version';
import NotificationAction from './NotificationAction';
import NotificationMessage from './NotificationMessage';
import { add as addNotification } from './actions';
@@ -309,21 +305,6 @@ function* showNoUpdateInfo(action: ReturnType<typeof appDidCheckForUpdate>): Gen
});
}
function* checkVersion(
action: ReturnType<typeof bleDIServiceDidReceiveFirmwareRevision>,
): Generator {
// ensure the actual hub firmware version is the same as the shipped
// firmware version or newer
if (
!semver.satisfies(
pythonVersionToSemver(action.version),
`>=${pythonVersionToSemver(firmwareVersion)}`,
)
) {
yield* showSingleton(Level.Error, I18nId.CheckFirmwareTooOld);
}
}
function* showFileStorageFailToInitialize(
action: ReturnType<typeof fileStorageDidFailToInitialize>,
): Generator {
@@ -405,7 +386,6 @@ export default function* (): Generator {
yield* takeEvery(addNotification, handleAddNotification);
yield* takeEvery(serviceWorkerDidUpdate, showServiceWorkerUpdate);
yield* takeEvery(appDidCheckForUpdate, showNoUpdateInfo);
yield* takeEvery(bleDIServiceDidReceiveFirmwareRevision, checkVersion);
yield* takeEvery(fileStorageDidFailToInitialize, showFileStorageFailToInitialize);
yield* takeEvery(explorerDidFailToImportFiles, showExplorerFailToImportFiles);
yield* takeEvery(explorerDidFailToCreateNewFile, showExplorerFailToCreateFile);
-3
View File
@@ -43,8 +43,5 @@
"message": "A new version of {appName} is available. Click {action} to start using the new version.",
"action": "Restart"
}
},
"check": {
"firmwareTooOld": "A new firmware version is available for this hub. Please install the latest version to use all new features."
}
}