mirror of
https://github.com/pybricks/pybricks-code.git
synced 2026-09-14 10:35:11 +00:00
ble/alerts: move old firmware from notifications
also add button to open new install firmware dialog
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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'),
|
||||
};
|
||||
};
|
||||
@@ -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;
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
);
|
||||
|
||||
@@ -39,5 +39,4 @@ export enum I18nId {
|
||||
ServiceWorkerUpdateMessage = 'serviceWorker.update.message',
|
||||
ServiceWorkerUpdateAction = 'serviceWorker.update.action',
|
||||
MpyError = 'mpy.error',
|
||||
CheckFirmwareTooOld = 'check.firmwareTooOld',
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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."
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user