mirror of
https://github.com/pybricks/pybricks-code.git
synced 2026-09-14 18:46:17 +00:00
ble/alerts: move and improve no web bluetooth message
This commit is contained in:
@@ -3,12 +3,14 @@
|
||||
|
||||
import { IToastProps } from '@blueprintjs/core';
|
||||
import alerts from './alerts/alerts';
|
||||
import ble from './ble/alerts';
|
||||
import explorer from './explorer/alerts';
|
||||
import { CreateToast } from './i18nToaster';
|
||||
|
||||
/** This collects alerts from all of the subsystems of the app */
|
||||
const alertDomains = {
|
||||
alerts,
|
||||
ble,
|
||||
explorer,
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2022 The Pybricks Authors
|
||||
|
||||
import { Button, Intent } from '@blueprintjs/core';
|
||||
import React from 'react';
|
||||
import { CreateToast } from '../../i18nToaster';
|
||||
import { isIOS, isLinux } from '../../utils/os';
|
||||
import { I18nId, useI18n } from './i18n';
|
||||
|
||||
const NoWebBluetooth: React.VoidFunctionComponent = () => {
|
||||
const i18n = useI18n();
|
||||
return (
|
||||
<>
|
||||
<p>{i18n.translate(I18nId.NoWebBluetoothMessage)}</p>
|
||||
{!isLinux() && !isIOS() && (
|
||||
<p>{i18n.translate(I18nId.NoWebBluetoothSuggestion)}</p>
|
||||
)}
|
||||
{isLinux() && (
|
||||
<>
|
||||
<p>{i18n.translate(I18nId.NoWebBluetoothLinux)}</p>
|
||||
<p>
|
||||
<code>
|
||||
chrome://flags/#enable-experimental-web-platform-features
|
||||
</code>
|
||||
<Button
|
||||
icon="duplicate"
|
||||
small={true}
|
||||
minimal={true}
|
||||
onClick={() =>
|
||||
navigator.clipboard.writeText(
|
||||
'chrome://flags/#enable-experimental-web-platform-features',
|
||||
)
|
||||
}
|
||||
/>
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const noWebBluetooth: CreateToast = (onAction) => {
|
||||
return {
|
||||
message: <NoWebBluetooth />,
|
||||
icon: 'error',
|
||||
intent: Intent.DANGER,
|
||||
onDismiss: () => onAction('dismiss'),
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2022 The Pybricks Authors
|
||||
|
||||
import { lookup } from '../../../test';
|
||||
import { I18nId } from './i18n';
|
||||
import en from './translations/en.json';
|
||||
|
||||
describe('Ensure .json file has matches for I18nId', () => {
|
||||
test.each(Object.values(I18nId))('%s', (id) => {
|
||||
expect(lookup(en, id)).toBeDefined();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2022 The Pybricks Authors
|
||||
|
||||
import { I18n, useI18n as useShopifyI18n } from '@shopify/react-i18n';
|
||||
|
||||
export function useI18n(): I18n {
|
||||
// istanbul ignore next: babel-loader rewrites this line
|
||||
const [i18n] = useShopifyI18n();
|
||||
return i18n;
|
||||
}
|
||||
|
||||
export enum I18nId {
|
||||
NoWebBluetoothMessage = 'noWebBluetooth.message',
|
||||
NoWebBluetoothSuggestion = 'noWebBluetooth.suggestion',
|
||||
NoWebBluetoothLinux = 'noWebBluetooth.linux',
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2022 The Pybricks Authors
|
||||
|
||||
import { noWebBluetooth } from './NoWebBluetooth';
|
||||
|
||||
// gathers all of the alert creation functions for passing up to the top level
|
||||
export default { noWebBluetooth };
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"noWebBluetooth": {
|
||||
"message": "This browser does not support Web Bluetooth or it is not enabled.",
|
||||
"suggestion": "Use a supported browser such as Google Chrome or Microsoft Edge.",
|
||||
"linux": "Web Bluetooth is experimental on Linux and must be manually enabled. Copy the link below and paste it in the address bar.",
|
||||
"action": "More Info"
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
import { HubType } from '@pybricks/firmware';
|
||||
import { MockProxy, mock } from 'jest-mock-extended';
|
||||
import { AsyncSaga } from '../../test';
|
||||
import { alertsShowAlert } from '../alerts/actions';
|
||||
import {
|
||||
bleDIServiceDidReceiveFirmwareRevision,
|
||||
bleDIServiceDidReceivePnPId,
|
||||
@@ -241,6 +242,9 @@ describe('connect action is dispatched', () => {
|
||||
it('should fail if no web bluetooth', async () => {
|
||||
await runConnectUntil(saga, ConnectRunPoint.Connect);
|
||||
|
||||
await expect(saga.take()).resolves.toEqual(
|
||||
alertsShowAlert('ble', 'noWebBluetooth'),
|
||||
);
|
||||
await expect(saga.take()).resolves.toEqual(
|
||||
bleDidFailToConnectPybricks({
|
||||
reason: BleDeviceFailToConnectReasonType.NoWebBluetooth,
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
takeEvery,
|
||||
takeMaybe,
|
||||
} from 'typed-redux-saga/macro';
|
||||
import { alertsShowAlert } from '../alerts/actions';
|
||||
import {
|
||||
bleDIServiceDidReceiveFirmwareRevision,
|
||||
bleDIServiceDidReceivePnPId,
|
||||
@@ -101,6 +102,7 @@ function* handleWriteUart(
|
||||
|
||||
function* handleBleConnectPybricks(): Generator {
|
||||
if (navigator.bluetooth === undefined) {
|
||||
yield* put(alertsShowAlert('ble', 'noWebBluetooth'));
|
||||
yield* put(bleDidFailToConnectPybricks({ reason: Reason.NoWebBluetooth }));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
import { END, eventChannel } from 'redux-saga';
|
||||
import { call, cancel, put, spawn, takeEvery, takeMaybe } from 'typed-redux-saga/macro';
|
||||
import { alertsShowAlert } from '../alerts/actions';
|
||||
import { ensureError } from '../utils';
|
||||
import {
|
||||
BootloaderConnectionFailureReason as Reason,
|
||||
@@ -42,6 +43,7 @@ function* write(
|
||||
|
||||
function* handleConnect(): Generator {
|
||||
if (navigator.bluetooth === undefined) {
|
||||
yield* put(alertsShowAlert('ble', 'noWebBluetooth'));
|
||||
yield* put(didFailToConnect(Reason.NoWebBluetooth));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ export enum I18nId {
|
||||
BleUnexpectedError = 'ble.unexpectedError',
|
||||
BleGattPermission = 'ble.gattPermission',
|
||||
BleGattServiceNotFound = 'ble.gattServiceNotFound',
|
||||
BleNoWebBluetooth = 'ble.noWebBluetooth',
|
||||
BleNoBluetooth = 'ble.noBluetooth',
|
||||
EditorFailedToOpenFile = 'editor.failedToOpenFile',
|
||||
EditorFailedToSaveFile = 'editor.failedToSaveFile',
|
||||
|
||||
@@ -61,9 +61,6 @@ function createTestToasterSaga(): { toaster: IToaster; saga: AsyncSaga } {
|
||||
}
|
||||
|
||||
test.each([
|
||||
bleDidFailToConnectPybricks({
|
||||
reason: BleDeviceFailToConnectReasonType.NoWebBluetooth,
|
||||
}),
|
||||
bleDidFailToConnectPybricks({
|
||||
reason: BleDeviceFailToConnectReasonType.NoBluetooth,
|
||||
}),
|
||||
@@ -81,7 +78,6 @@ test.each([
|
||||
bootloaderDidFailToConnect(BootloaderConnectionFailureReason.Unknown, <Error>{
|
||||
message: 'test',
|
||||
}),
|
||||
bootloaderDidFailToConnect(BootloaderConnectionFailureReason.NoWebBluetooth),
|
||||
bootloaderDidFailToConnect(BootloaderConnectionFailureReason.NoBluetooth),
|
||||
bootloaderDidFailToConnect(BootloaderConnectionFailureReason.GattServiceNotFound),
|
||||
didFailToCompile(['reason']),
|
||||
@@ -135,7 +131,11 @@ test.each([
|
||||
});
|
||||
|
||||
test.each([
|
||||
bleDidFailToConnectPybricks({
|
||||
reason: BleDeviceFailToConnectReasonType.NoWebBluetooth,
|
||||
}),
|
||||
bleDidFailToConnectPybricks({ reason: BleDeviceFailToConnectReasonType.Canceled }),
|
||||
bootloaderDidFailToConnect(BootloaderConnectionFailureReason.NoWebBluetooth),
|
||||
bootloaderDidFailToConnect(BootloaderConnectionFailureReason.Canceled),
|
||||
didFailToFinish(FailToFinishReasonType.FailedToConnect),
|
||||
serviceWorkerDidSucceed(),
|
||||
|
||||
@@ -193,16 +193,6 @@ function* showBleDeviceDidFailToConnectError(
|
||||
case BleDeviceFailToConnectReasonType.NoBluetooth:
|
||||
yield* showSingleton(Level.Error, I18nId.BleNoBluetooth);
|
||||
break;
|
||||
case BleDeviceFailToConnectReasonType.NoWebBluetooth:
|
||||
yield* showSingleton(
|
||||
Level.Error,
|
||||
I18nId.BleNoWebBluetooth,
|
||||
undefined,
|
||||
helpAction(
|
||||
'https://github.com/WebBluetoothCG/web-bluetooth/blob/master/implementation-status.md',
|
||||
),
|
||||
);
|
||||
break;
|
||||
case BleDeviceFailToConnectReasonType.Unknown:
|
||||
yield* showUnexpectedError(I18nId.BleUnexpectedError, action.err);
|
||||
break;
|
||||
@@ -219,16 +209,6 @@ function* showBootloaderDidFailToConnectError(
|
||||
hubName: 'LEGO Bootloader',
|
||||
});
|
||||
break;
|
||||
case BootloaderConnectionFailureReason.NoWebBluetooth:
|
||||
yield* showSingleton(
|
||||
Level.Error,
|
||||
I18nId.BleNoWebBluetooth,
|
||||
undefined,
|
||||
helpAction(
|
||||
'https://github.com/WebBluetoothCG/web-bluetooth/blob/master/implementation-status.md',
|
||||
),
|
||||
);
|
||||
break;
|
||||
case BootloaderConnectionFailureReason.NoBluetooth:
|
||||
yield* showSingleton(Level.Error, I18nId.BleNoBluetooth);
|
||||
break;
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
"ble": {
|
||||
"gattPermission": "The web browser did not give permission to use Bluetooth Low Energy",
|
||||
"gattServiceNotFound": "Connected to hub but failed to get {serviceName} service.\nEnsure that you are using the most recent firmware.\nIf the problem persists, try removing the \"{hubName}\" device in your OS Bluetooth settings, then try connecting again.",
|
||||
"noWebBluetooth": "This web browser does not support Web Bluetooth or it is not enabled.",
|
||||
"noBluetooth": "No Bluetooth adapter could be found. Please connect or enable a Bluetooth Low Energy adapter and restart the browser.",
|
||||
"unexpectedError": "Unexpected error while trying to connect: {errorMessage}"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user