Skip to content

Commit 9c121df

Browse files
Unsupported V1 micro:bit dialog for radio bridge flow (#215)
We might support this one day but for now the C++ project is V2 only.
1 parent 516916a commit 9c121df

File tree

7 files changed

+113
-17
lines changed

7 files changed

+113
-17
lines changed

src/components/HtmlFormattedMessage.svelte

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,24 @@
1313
-->
1414

1515
<script lang="ts" context="module">
16+
interface Options {
17+
className?: string;
18+
tag?: string;
19+
}
1620
export const linkWithProps =
17-
(props: Partial<HTMLAnchorElement>) => (chunks: string[]) => {
21+
(props: Partial<HTMLAnchorElement>, options: Options = {}) =>
22+
(chunks: string[]) => {
1823
{
19-
const a = document.createElement('a');
20-
Object.assign(a, {
24+
const tag = options.tag ?? 'a';
25+
const element = document.createElement(tag);
26+
Object.assign(element, {
2127
className:
22-
'text-link outline-none focus-visible:ring-4 focus-visible:ring-offset-1 focus-visible:ring-ring',
28+
'text-link outline-none focus-visible:ring-4 focus-visible:ring-offset-1 focus-visible:ring-ring ' +
29+
(options.className ?? ''),
2330
innerText: chunks.join(''),
2431
...props,
2532
});
26-
return a;
33+
return element;
2734
}
2835
};
2936
</script>
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<!--
2+
(c) 2023, Center for Computational Thinking and Design at Aarhus University and contributors
3+
4+
SPDX-License-Identifier: MIT
5+
-->
6+
7+
<script lang="ts">
8+
import { t } from '../i18n';
9+
import DialogHeading from './DialogHeading.svelte';
10+
import HtmlFormattedMessage, { linkWithProps } from './HtmlFormattedMessage.svelte';
11+
import StandardButton from './StandardButton.svelte';
12+
13+
export let onClose: () => void;
14+
export let onStartBluetoothClick: () => void;
15+
16+
const linkWithPropsForMicrobitVersionSupport = linkWithProps({
17+
href: 'https://support.microbit.org/support/solutions/articles/19000119162',
18+
target: '_blank',
19+
rel: 'noopener',
20+
});
21+
const linkToBluetoothFlowId = 'link-to-bluetooth';
22+
const linkToBluetoothFlow = linkWithProps(
23+
{
24+
id: linkToBluetoothFlowId,
25+
},
26+
{ tag: 'button' },
27+
);
28+
// The elements in translated strings don't support Svelte event handlers
29+
// so we wire it up ourselves.
30+
const wireLinkToBluetoothFlow = (node: Element) => {
31+
const link = node.querySelector('#' + linkToBluetoothFlowId);
32+
link?.addEventListener('click', onStartBluetoothClick);
33+
return {
34+
destroy() {
35+
link?.removeEventListener('click', onStartBluetoothClick);
36+
},
37+
};
38+
};
39+
</script>
40+
41+
<div class="w-175">
42+
<DialogHeading>{$t('connectMB.unsupportedMicrobit.header')}</DialogHeading>
43+
<div class="space-y-5">
44+
<div class="space-y-2">
45+
<p>
46+
<HtmlFormattedMessage
47+
id={$t('connectMB.unsupportedMicrobit.explain')}
48+
options={{
49+
values: {
50+
link: linkWithPropsForMicrobitVersionSupport,
51+
},
52+
}} />
53+
</p>
54+
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
55+
<p use:wireLinkToBluetoothFlow>
56+
<HtmlFormattedMessage
57+
id={$t('connectMB.unsupportedMicrobit.advice')}
58+
options={{
59+
values: {
60+
link1: linkToBluetoothFlow,
61+
link2: linkWithPropsForMicrobitVersionSupport,
62+
},
63+
}} />
64+
</p>
65+
</div>
66+
<div class="flex justify-end">
67+
<StandardButton onClick={onClose} type="primary"
68+
>{$t('actions.close')}</StandardButton>
69+
</div>
70+
</div>
71+
</div>

src/components/connection-prompt/ConnectDialogContainer.svelte

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import Microbits, {
1515
FlashStage,
1616
HexType,
17+
getHexFileUrl,
1718
} from '../../script/microbit-interfacing/Microbits';
1819
import {
1920
ConnectDialogStates,
@@ -40,6 +41,7 @@
4041
import DownloadingDialog from './usb/DownloadingDialog.svelte';
4142
import SelectMicrobitDialogUsb from './usb/SelectMicrobitDialogUsb.svelte';
4243
import ManualInstallTutorial from './usb/manual/ManualInstallTutorial.svelte';
44+
import UnsupportedMicrobitWarningDialog from '../UnsupportedMicrobitWarningDialog.svelte';
4345
4446
const { bluetooth, usb } = get(compatibility);
4547
let endOfFlow = false;
@@ -118,7 +120,13 @@
118120
try {
119121
const deviceId = await usb.getDeviceId();
120122
const hexForStage = stageToHex(flashStage);
121-
await usb.flashHex(hexForStage, progress => {
123+
const hexUrl = getHexFileUrl(usb.getModelNumber(), hexForStage);
124+
if (hexUrl === undefined) {
125+
$connectionDialogState.connectionState = ConnectDialogStates.MICROBIT_UNSUPPORTED;
126+
return;
127+
}
128+
129+
await usb.flashHex(hexUrl, progress => {
122130
// Flash hex
123131
// Send users to download screen
124132
if (
@@ -405,6 +413,12 @@
405413
onTryAgain={() => {
406414
$connectionDialogState.connectionState = ConnectDialogStates.BLUETOOTH;
407415
}} />
416+
{:else if $connectionDialogState.connectionState === ConnectDialogStates.MICROBIT_UNSUPPORTED}
417+
<UnsupportedMicrobitWarningDialog
418+
onStartBluetoothClick={() => {
419+
$connectionDialogState.connectionState = ConnectDialogStates.START_BLUETOOTH;
420+
}}
421+
onClose={endFlow} />
408422
{/if}
409423
</StandardDialog>
410424
</div>

src/messages/ui.en.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,10 @@
139139
"connectMB.usbTryAgain.replugMicrobit3": "check that this micro:bit does not have a battery pack connected",
140140
"connectMB.usbTryAgain.replugMicrobit4": "unplug and replug the USB cable",
141141

142+
"connectMB.unsupportedMicrobit.header": "micro:bit V1 is not supported for connecting two micro:bits",
143+
"connectMB.unsupportedMicrobit.explain": "Unfortunately, we only support using micro:bit V2 when connecting two micro:bits. You have connected a <link>micro:bit V1</link>.",
144+
"connectMB.unsupportedMicrobit.advice": "If your computer supports Web Bluetooth then you can use the <link1>alternative method</link1> that uses one micro:bit with your <link2>micro:bit V1</link2>.",
145+
142146
"connectMB.radioStart.heading": "What you will need to get started:",
143147
"connectMB.radioStart.requirements1": "2 micro:bits",
144148
"connectMB.radioStart.requirements2": "Computer",

src/script/microbit-interfacing/MicrobitUSB.ts

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@
55
*/
66

77
import { CortexM, DAPLink, WebUSB } from 'dapjs';
8-
import MBSpecs from './MBSpecs';
9-
import { HexType, getHexFileUrl } from './Microbits';
108
import { logError } from '../utils/logging';
9+
import MBSpecs from './MBSpecs';
1110
import { CortexSpecialReg } from './constants';
1211

1312
const baudRate = 115200;
@@ -112,18 +111,15 @@ class MicrobitUSB {
112111

113112
/**
114113
* Flashes a .hex file to the micro:bit.
115-
* @param {string} hex The hex file to flash. (As a link)
114+
* @param {string} url The hex file to flash. (As a link)
116115
* @param {(progress: number) => void} progressCallback A callback for progress.
117116
*/
118117
public async flashHex(
119-
hexType: HexType,
118+
url: string,
120119
progressCallback: (progress: number) => void,
121120
): Promise<void> {
122-
const version = this.getModelNumber();
123-
const hex = getHexFileUrl(version, hexType);
124-
const hexFile: Response = await fetch(hex);
125-
const buffer: ArrayBuffer = await hexFile.arrayBuffer();
126-
121+
const hexFile = await fetch(url);
122+
const buffer = await hexFile.arrayBuffer();
127123
const target = new DAPLink(this.transport);
128124

129125
target.on(DAPLink.EVENT_PROGRESS, (progress: number) => {

src/script/microbit-interfacing/Microbits.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ export type HexType =
1919

2020
export type UARTMessageType = 'g' | 's';
2121

22-
export const getHexFileUrl = (version: 1 | 2 | 'universal', type: HexType) => {
22+
export const getHexFileUrl = (
23+
version: 1 | 2 | 'universal',
24+
type: HexType,
25+
): string | undefined => {
2326
if (type === 'bluetooth') {
2427
return {
2528
1: 'firmware/ml-microbit-cpp-version-combined.hex',
@@ -28,7 +31,7 @@ export const getHexFileUrl = (version: 1 | 2 | 'universal', type: HexType) => {
2831
}[version];
2932
}
3033
if (version !== 2) {
31-
throw new Error('Only V2 is supported');
34+
return undefined;
3235
}
3336
return {
3437
'radio-remote-dev': 'firmware/radio-remote-v0.2.0-dev.hex',

src/script/stores/connectDialogStore.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export enum ConnectDialogStates {
2727
MANUAL_TUTORIAL, // Prompt with tutorial gif for manual installation (and downloading of program)
2828
USB_TRY_AGAIN, // Prompt user to try connecting via WebUSB again
2929
BLUETOOTH_TRY_AGAIN, // Prompt user to try connecting via WebBluetooth again
30+
MICROBIT_UNSUPPORTED, // Warn user that micro:bit V1 is not supported
3031
BROWSER_DIALOG, // Awaiting user interaction with browser dialog
3132
}
3233

0 commit comments

Comments
 (0)