diff --git a/src/ble/alerts/NoGatt.tsx b/src/ble/alerts/NoGatt.tsx
new file mode 100644
index 00000000..78649013
--- /dev/null
+++ b/src/ble/alerts/NoGatt.tsx
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2022 The Pybricks Authors
+
+import { Intent } from '@blueprintjs/core';
+import React from 'react';
+import { CreateToast } from '../../i18nToaster';
+import { I18nId, useI18n } from './i18n';
+
+const NoGatt: React.VoidFunctionComponent = () => {
+ const i18n = useI18n();
+ return
{i18n.translate(I18nId.NoGattMessage)}
;
+};
+
+export const noGatt: CreateToast = (onAction) => {
+ return {
+ message: ,
+ icon: 'error',
+ intent: Intent.DANGER,
+ onDismiss: () => onAction('dismiss'),
+ };
+};
diff --git a/src/ble/alerts/i18n.ts b/src/ble/alerts/i18n.ts
index 192a6ca8..c48428f5 100644
--- a/src/ble/alerts/i18n.ts
+++ b/src/ble/alerts/i18n.ts
@@ -15,4 +15,5 @@ export enum I18nId {
NoWebBluetoothLinux = 'noWebBluetooth.linux',
BluetoothNotAvailableMessage = 'bluetoothNotAvailable.message',
BluetoothNotAvailableSuggestion = 'bluetoothNotAvailable.suggestion',
+ NoGattMessage = 'noGatt.message',
}
diff --git a/src/ble/alerts/index.ts b/src/ble/alerts/index.ts
index 53b93b33..d3f08281 100644
--- a/src/ble/alerts/index.ts
+++ b/src/ble/alerts/index.ts
@@ -2,7 +2,8 @@
// Copyright (c) 2022 The Pybricks Authors
import { bluetoothNotAvailable } from './BluetoothNotAvailable';
+import { noGatt } from './NoGatt';
import { noWebBluetooth } from './NoWebBluetooth';
// gathers all of the alert creation functions for passing up to the top level
-export default { bluetoothNotAvailable, noWebBluetooth };
+export default { bluetoothNotAvailable, noGatt, noWebBluetooth };
diff --git a/src/ble/alerts/translations/en.json b/src/ble/alerts/translations/en.json
index 22090626..c1acbf34 100644
--- a/src/ble/alerts/translations/en.json
+++ b/src/ble/alerts/translations/en.json
@@ -8,5 +8,8 @@
"bluetoothNotAvailable": {
"message": "No Bluetooth adapter could be found.",
"suggestion": "Please connect or enable a Bluetooth Low Energy adapter and restart the browser."
+ },
+ "noGatt": {
+ "message": "The web browser did not give permission to use Bluetooth Low Energy."
}
}
diff --git a/src/ble/sagas.test.ts b/src/ble/sagas.test.ts
index 2ee72c6d..ddccf7b3 100644
--- a/src/ble/sagas.test.ts
+++ b/src/ble/sagas.test.ts
@@ -314,6 +314,9 @@ describe('connect action is dispatched', () => {
await runConnectUntil(saga, ConnectRunPoint.Connect);
+ await expect(saga.take()).resolves.toEqual(
+ alertsShowAlert('ble', 'noGatt'),
+ );
await expect(saga.take()).resolves.toEqual(
bleDidFailToConnectPybricks({
reason: BleDeviceFailToConnectReasonType.NoGatt,
diff --git a/src/ble/sagas.ts b/src/ble/sagas.ts
index e6622b18..f9549b59 100644
--- a/src/ble/sagas.ts
+++ b/src/ble/sagas.ts
@@ -149,6 +149,7 @@ function* handleBleConnectPybricks(): Generator {
const gatt = device.gatt;
if (!gatt) {
+ yield* put(alertsShowAlert('ble', 'noGatt'));
yield* put(bleDidFailToConnectPybricks({ reason: Reason.NoGatt }));
return;
}
diff --git a/src/notifications/i18n.ts b/src/notifications/i18n.ts
index c6354e29..e8fd902d 100644
--- a/src/notifications/i18n.ts
+++ b/src/notifications/i18n.ts
@@ -14,7 +14,6 @@ export function useI18n(): I18n {
export enum I18nId {
AppNoUpdateFound = 'app.noUpdateFound',
BleUnexpectedError = 'ble.unexpectedError',
- BleGattPermission = 'ble.gattPermission',
BleGattServiceNotFound = 'ble.gattServiceNotFound',
EditorFailedToOpenFile = 'editor.failedToOpenFile',
EditorFailedToSaveFile = 'editor.failedToSaveFile',
diff --git a/src/notifications/sagas.test.ts b/src/notifications/sagas.test.ts
index 0d84a6e1..bc1543e2 100644
--- a/src/notifications/sagas.test.ts
+++ b/src/notifications/sagas.test.ts
@@ -61,7 +61,6 @@ function createTestToasterSaga(): { toaster: IToaster; saga: AsyncSaga } {
}
test.each([
- bleDidFailToConnectPybricks({ reason: BleDeviceFailToConnectReasonType.NoGatt }),
bleDidFailToConnectPybricks({
reason: BleDeviceFailToConnectReasonType.NoDeviceInfoService,
}),
@@ -130,6 +129,7 @@ test.each([
reason: BleDeviceFailToConnectReasonType.NoBluetooth,
}),
bleDidFailToConnectPybricks({ reason: BleDeviceFailToConnectReasonType.Canceled }),
+ bleDidFailToConnectPybricks({ reason: BleDeviceFailToConnectReasonType.NoGatt }),
bleDidFailToConnectPybricks({
reason: BleDeviceFailToConnectReasonType.Unknown,
err: { name: 'test', message: 'unknown' },
diff --git a/src/notifications/sagas.ts b/src/notifications/sagas.ts
index 002b0a19..e7c3a09a 100644
--- a/src/notifications/sagas.ts
+++ b/src/notifications/sagas.ts
@@ -174,10 +174,6 @@ function* showBleDeviceDidFailToConnectError(
action: ReturnType,
): Generator {
switch (action.reason) {
- case BleDeviceFailToConnectReasonType.NoGatt:
- yield* showSingleton(Level.Error, I18nId.BleGattPermission);
- break;
-
case BleDeviceFailToConnectReasonType.NoPybricksService:
yield* showSingleton(Level.Error, I18nId.BleGattServiceNotFound, {
serviceName: 'Pybricks',
diff --git a/src/notifications/translations/en.json b/src/notifications/translations/en.json
index 798a726f..f2d956aa 100644
--- a/src/notifications/translations/en.json
+++ b/src/notifications/translations/en.json
@@ -3,7 +3,6 @@
"noUpdateFound": "{appName} is already up to date."
},
"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.",
"unexpectedError": "Unexpected error while trying to connect: {errorMessage}"
},