Skip to content

Handle bluetooth connection error scenarios #279

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 21 commits into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
4687d1c
WIP Somewhat working except for connect fail when no device is selected
microbit-grace Jul 25, 2024
70a5f69
Fix setting bluetooth connection name filter
microbit-grace Jul 25, 2024
ed001d5
Working version
microbit-grace Jul 26, 2024
54e47a5
Remove status from ConnectionStage in favour of using connection stat…
microbit-grace Jul 26, 2024
9e99e04
Remove reconnectFailStreak
microbit-grace Jul 26, 2024
0849565
Move connection status to connect-status-hooks.tsx and document each …
microbit-grace Jul 26, 2024
26f3124
Merge branch 'react' into bt-error
microbit-grace Jul 26, 2024
d8977d8
Generalise bluetooth-specific parts
microbit-grace Jul 26, 2024
60393b2
Fix lint
microbit-grace Jul 26, 2024
9fa55f7
Add reconnect troubleshoot link for whatYouWillNeedDialog
microbit-grace Jul 26, 2024
d24802d
Rename connection flow steps to connection loss and reconnect failed
microbit-grace Jul 26, 2024
eaca13d
Update how the user has not selected a device is determined to use de…
microbit-grace Jul 26, 2024
fd1381b
Remove debugging console logs
microbit-grace Jul 26, 2024
a5a12db
Upgrade connection lib to ^0.0.0-alpha.14
microbit-grace Jul 29, 2024
3099ff3
Remove no longer needed TODO comment
microbit-grace Jul 29, 2024
2e41c8f
Fix failed twice connection dialog flow so that going back will show …
microbit-grace Jul 29, 2024
7e2d785
Rearrange failed to conenct status change to be above reconnect
microbit-grace Jul 29, 2024
b913c9c
Remove not needed "?" in connect-actions
microbit-grace Jul 29, 2024
9a18509
Use radio connecting stage instead of bt
microbit-grace Jul 29, 2024
9ecd2f4
Calculate isConnected without using useMemo hook
microbit-grace Jul 29, 2024
b9120c3
Fix radio connection happy flow
microbit-grace Jul 29, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
"@chakra-ui/react": "^2.8.2",
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.5",
"@microbit/microbit-connection": "^0.0.0-alpha.13",
"@microbit/microbit-connection": "^0.0.0-alpha.14",
"@tensorflow/tfjs": "^4.4.0",
"@types/w3c-web-serial": "^1.0.6",
"@types/w3c-web-usb": "^1.0.6",
Expand Down
17 changes: 10 additions & 7 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { GesturesProvider } from "./gestures-hooks";
import { MlStatusProvider } from "./ml-status-hooks";
import { ConnectionStageProvider } from "./connection-stage-hooks";
import { ConnectProvider } from "./connect-actions-hooks";
import { ConnectStatusProvider } from "./connect-status-hooks";

export interface ProviderLayoutProps {
children: ReactNode;
Expand All @@ -43,13 +44,15 @@ const Providers = ({ children }: ProviderLayoutProps) => {
<SettingsProvider>
<GesturesProvider>
<MlStatusProvider>
<ConnectProvider>
<ConnectionStageProvider>
<TranslationProvider>
<ErrorBoundary>{children}</ErrorBoundary>
</TranslationProvider>
</ConnectionStageProvider>
</ConnectProvider>
<ConnectStatusProvider>
<ConnectProvider>
<ConnectionStageProvider>
<TranslationProvider>
<ErrorBoundary>{children}</ErrorBoundary>
</TranslationProvider>
</ConnectionStageProvider>
</ConnectProvider>
</ConnectStatusProvider>
</MlStatusProvider>
</GesturesProvider>
</SettingsProvider>
Expand Down
14 changes: 10 additions & 4 deletions src/components/ConnectCableDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Image, Text, VStack } from "@chakra-ui/react";
import { Button, Image, Text, VStack } from "@chakra-ui/react";
import { FormattedMessage } from "react-intl";
import connectCableImage from "../images/connect-cable.gif";
import ConnectContainerDialog, {
Expand Down Expand Up @@ -51,17 +51,23 @@ const ConnectCableDialog = ({
onSwitch,
...props
}: ConnectCableDialogProps) => {
const { subtitleId, onLink, ...typeProps } = configs[type];
const { subtitleId, onLink, linkTextId, headingId } = configs[type];
const linkConfig = {
[LinkType.None]: undefined,
[LinkType.Skip]: onSkip,
[LinkType.Switch]: onSwitch,
};
return (
<ConnectContainerDialog
{...typeProps}
headingId={headingId}
{...props}
onLinkClick={linkConfig[onLink]}
footerLeft={
linkTextId && (
<Button onClick={linkConfig[onLink]} variant="link" size="lg">
<FormattedMessage id={linkTextId} />
</Button>
)
}
>
<VStack gap={5}>
<Text width="100%">
Expand Down
14 changes: 4 additions & 10 deletions src/components/ConnectContainerDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ export interface ConnectContainerDialogProps {
isOpen: boolean;
onClose: () => void;
headingId: string;
onLinkClick?: () => void;
linkTextId?: string;
footerLeft?: ReactNode;
onNextClick?: () => void;
children: ReactNode;
onBackClick?: () => void;
Expand All @@ -30,8 +29,7 @@ const ConnectContainerDialog = ({
isOpen,
onClose,
headingId,
onLinkClick,
linkTextId,
footerLeft,
onNextClick,
onBackClick,
children,
Expand All @@ -57,14 +55,10 @@ const ConnectContainerDialog = ({
</VStack>
</ModalBody>
<ModalFooter
justifyContent={onLinkClick && linkTextId ? "space-between" : "end"}
justifyContent={footerLeft ? "space-between" : "end"}
p={0}
>
{onLinkClick && linkTextId && (
<Button onClick={onLinkClick} variant="link" size="lg">
<FormattedMessage id={linkTextId} />
</Button>
)}
{footerLeft && footerLeft}
<HStack gap={5}>
{onBackClick && (
<Button onClick={onBackClick} variant="secondary" size="lg">
Expand Down
5 changes: 2 additions & 3 deletions src/components/ConnectionFlowDialogs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -235,9 +235,8 @@ const ConnectionDialogs = () => {
case ConnectionFlowStep.WebUsbBluetoothUnsupported: {
return <WebUsbBluetoothUnsupportedDialog {...dialogCommonProps} />;
}
// TODO: Reconnect dialogs
case ConnectionFlowStep.ReconnectManualFail:
case ConnectionFlowStep.ReconnectAutoFail: {
case ConnectionFlowStep.ReconnectFailed:
case ConnectionFlowStep.ConnectionLost: {
return (
<ReconnectErrorDialog
{...dialogCommonProps}
Expand Down
44 changes: 21 additions & 23 deletions src/components/LiveGraphPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,30 @@ import { Button, HStack, Portal, Text } from "@chakra-ui/react";
import { useMemo, useRef } from "react";
import { MdBolt } from "react-icons/md";
import { FormattedMessage } from "react-intl";
import {
ConnectionStatus,
useConnectionStage,
} from "../connection-stage-hooks";
import { useConnectionStage } from "../connection-stage-hooks";
import InfoToolTip from "./InfoToolTip";
import LiveGraph from "./LiveGraph";
import { ConnectionStatus, useConnectStatus } from "../connect-status-hooks";

const LiveGraphPanel = () => {
const { actions } = useConnectionStage();
const { stage } = useConnectionStage();
const status = useConnectStatus();
const parentPortalRef = useRef(null);

const connectBtnConfig = useMemo(
() =>
stage.status === ConnectionStatus.None ||
stage.status === ConnectionStatus.Connecting
? {
textId: "footer.connectButton",
onClick: actions.start,
}
: {
textId: "actions.reconnect",
onClick: actions.reconnect,
},
[actions.reconnect, actions.start, stage]
);
const connectBtnConfig = useMemo(() => {
return status === ConnectionStatus.NotConnected ||
status === ConnectionStatus.Connecting ||
status === ConnectionStatus.FailedToConnect ||
status === ConnectionStatus.FailedToReconnectTwice
? {
textId: "footer.connectButton",
onClick: actions.start,
}
: {
textId: "actions.reconnect",
onClick: actions.reconnect,
};
}, [actions.reconnect, actions.start, status]);

return (
<HStack
Expand All @@ -49,7 +47,7 @@ const LiveGraphPanel = () => {
>
<HStack gap={4}>
<LiveIndicator />
{stage.status === ConnectionStatus.Connected ? (
{status === ConnectionStatus.Connected ? (
<Button variant="primary" size="sm" onClick={actions.disconnect}>
<FormattedMessage id="footer.disconnectButton" />
</Button>
Expand All @@ -58,15 +56,15 @@ const LiveGraphPanel = () => {
variant="primary"
size="sm"
isDisabled={
stage.status === ConnectionStatus.Reconnecting ||
stage.status === ConnectionStatus.Connecting
status === ConnectionStatus.Reconnecting ||
status === ConnectionStatus.Connecting
}
onClick={connectBtnConfig.onClick}
>
<FormattedMessage id={connectBtnConfig.textId} />
</Button>
)}
{stage.status === ConnectionStatus.Reconnecting && (
{status === ConnectionStatus.Reconnecting && (
<Text rounded="4xl" bg="white" py="1px" fontWeight="bold">
<FormattedMessage id="connectMB.reconnecting" />
</Text>
Expand Down
8 changes: 4 additions & 4 deletions src/components/ReconnectErrorDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ interface ReconnectErrorDialogProps {
onReconnect: () => void;
flowType: ConnectionFlowType;
errorStep:
| ConnectionFlowStep.ReconnectManualFail
| ConnectionFlowStep.ReconnectAutoFail;
| ConnectionFlowStep.ReconnectFailed
| ConnectionFlowStep.ConnectionLost;
}

const contentConfig = {
Expand Down Expand Up @@ -55,8 +55,8 @@ const contentConfig = {
};

const errorTextIdPrefixConfig = {
[ConnectionFlowStep.ReconnectAutoFail]: "disconnectedWarning",
[ConnectionFlowStep.ReconnectManualFail]: "reconnectFailed",
[ConnectionFlowStep.ConnectionLost]: "disconnectedWarning",
[ConnectionFlowStep.ReconnectFailed]: "reconnectFailed",
};

const ReconnectErrorDialog = ({
Expand Down
29 changes: 24 additions & 5 deletions src/components/WhatYouWillNeedDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Grid, GridItem, Image, Text, VStack } from "@chakra-ui/react";
import { Button, Grid, GridItem, Image, Text, VStack } from "@chakra-ui/react";
import { FormattedMessage } from "react-intl";
import batteryPackImage from "../images/stylised-battery-pack.svg";
import microbitImage from "../images/stylised-microbit-black.svg";
Expand All @@ -9,6 +9,7 @@ import computerBluetoothImage from "../images/stylised_computer_w_bluetooth.svg"
import ConnectContainerDialog, {
ConnectContainerDialogProps,
} from "./ConnectContainerDialog";
import ExternalLink from "./ExternalLink";

const itemsConfig = {
radio: [
Expand Down Expand Up @@ -60,24 +61,42 @@ export interface WhatYouWillNeedDialogProps
> {
reconnect: boolean;
type: "radio" | "bluetooth";
onLinkClick: (() => void) | undefined;
}

const WhatYouWillNeedDialog = ({
reconnect,
type,
onLinkClick,
...props
}: WhatYouWillNeedDialogProps) => {
return (
<ConnectContainerDialog
{...props}
linkTextId={`connectMB.${type}Start.switch${
type === "bluetooth" ? "Radio" : "Bluetooth"
}`}
headingId={
reconnect
? `connectMB.${type}Start.heading`
? `reconnectFailed.${type}Heading`
: `connectMB.${type}Start.heading`
}
footerLeft={
<VStack alignItems="start">
{onLinkClick && (
<Button onClick={onLinkClick} variant="link" size="lg">
<FormattedMessage
id={`connectMB.${type}Start.switch${
type === "bluetooth" ? "Radio" : "Bluetooth"
}`}
/>
</Button>
)}
{reconnect && (
<ExternalLink
href="https://support.microbit.org/a/solutions/articles/19000157495"
textId="connectMB.troubleshoot"
/>
)}
</VStack>
}
>
{reconnect && (
<Text>
Expand Down
23 changes: 16 additions & 7 deletions src/connect-actions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
AccelerometerDataEvent,
ConnectionStatusEvent,
ButtonEvent,
ConnectionStatus as DeviceConnectionStatus,
DeviceError,
Expand Down Expand Up @@ -78,7 +79,7 @@ export class ConnectActions {
try {
await this.usb.flash(data, {
partial: true,
progress: (v) => progress(v ?? 100),
progress: (v: number | undefined) => progress(v ?? 100),
});
return ConnectAndFlashResult.Success;
} catch (e) {
Expand Down Expand Up @@ -118,16 +119,16 @@ export class ConnectActions {
};

connectBluetooth = async (
name: string | undefined
): Promise<ConnectResult> => {
name: string | undefined,
clearDevice: boolean
): Promise<void> => {
if (clearDevice) {
await this.bluetooth.clearDevice();
}
if (name) {
this.bluetooth.setNameFilter(name);
}
await this.bluetooth.connect();
if (this.bluetooth.status === DeviceConnectionStatus.CONNECTED) {
return ConnectResult.Success;
}
return ConnectResult.ManualConnectFailed;
};

addAccelerometerListener = (
Expand Down Expand Up @@ -166,4 +167,12 @@ export class ConnectActions {
await this.bluetooth.disconnect();
await this.radioBridge.disconnect();
};

addStatusListener = (listener: (e: ConnectionStatusEvent) => void) => {
this.bluetooth.addEventListener("status", listener);
};

removeStatusListener = (listener: (e: ConnectionStatusEvent) => void) => {
this.bluetooth.removeEventListener("status", listener);
};
}
Loading
Loading