diff --git a/src/connect-status-hooks.tsx b/src/connect-status-hooks.tsx index 1cd51f172..f54c6c3a5 100644 --- a/src/connect-status-hooks.tsx +++ b/src/connect-status-hooks.tsx @@ -113,15 +113,6 @@ export const useConnectStatusUpdater = ( useState(true); useEffect(() => { - if ( - !isBrowserTabVisible && - currConnType === "radio" && - connectionStatus === ConnectionStatus.Connected - ) { - // Show reconnecting when user hides browser tab and is radio connected - setConnectionStatus(ConnectionStatus.ReconnectingAutomatically); - return; - } const listener: StatusListener = ({ status: deviceStatus, type }) => { const nextState = getNextConnectionState({ currConnType, @@ -133,9 +124,10 @@ export const useConnectStatusUpdater = ( setHasAttemptedReconnect, onFirstConnectAttempt, setOnFirstConnectAttempt, + isBrowserTabVisible, }); prevDeviceStatus.current = deviceStatus; - if (nextState && isBrowserTabVisible) { + if (nextState) { handleStatus && handleStatus(nextState.status, nextState.flowType); setConnectionStatus(nextState.status); } diff --git a/src/get-next-connection-state.test.ts b/src/get-next-connection-state.test.ts index 8e99ddc18..f0c8c1093 100644 --- a/src/get-next-connection-state.test.ts +++ b/src/get-next-connection-state.test.ts @@ -28,6 +28,7 @@ const testGetNextConnectionState = ({ expectedNextConnectionState, expectedHasAttemptedReconnect, expectedOnFirstConnectAttempt, + isBrowserTabVisible, }: { input: Input; initialHasAttemptedReconnect: boolean; @@ -35,6 +36,7 @@ const testGetNextConnectionState = ({ expectedHasAttemptedReconnect: boolean; initialOnFirstConnectAttempt: boolean; expectedOnFirstConnectAttempt: boolean; + isBrowserTabVisible?: boolean; }) => { let hasAttempedReconnect = initialHasAttemptedReconnect; let onFirstConnectAttempt = initialOnFirstConnectAttempt; @@ -48,6 +50,7 @@ const testGetNextConnectionState = ({ setHasAttemptedReconnect: (val: boolean) => { hasAttempedReconnect = val; }, + isBrowserTabVisible: isBrowserTabVisible ?? true, }); expect(result).toEqual(expectedNextConnectionState); expect(hasAttempedReconnect).toEqual(expectedHasAttemptedReconnect); @@ -154,7 +157,7 @@ describe("getNextConnectionState for radio connection", () => { type: "usb", }, initialOnFirstConnectAttempt: false, - expectedOnFirstConnectAttempt: false, + expectedOnFirstConnectAttempt: true, initialHasAttemptedReconnect: false, expectedHasAttemptedReconnect: false, expectedNextConnectionState: undefined, @@ -233,6 +236,26 @@ describe("getNextConnectionState for radio connection", () => { }, }); }); + test("radio bridge device showing reconnecting automatically because tab is no longer visible", () => { + testGetNextConnectionState({ + input: { + currConnType: "radio", + currStatus: ConnectionStatus.Connected, + deviceStatus: DeviceConnectionStatus.DISCONNECTED, + prevDeviceStatus: DeviceConnectionStatus.NO_AUTHORIZED_DEVICE, + type: "usb", + }, + initialOnFirstConnectAttempt: false, + expectedOnFirstConnectAttempt: false, + initialHasAttemptedReconnect: false, + isBrowserTabVisible: false, + expectedHasAttemptedReconnect: false, + expectedNextConnectionState: { + status: ConnectionStatus.ReconnectingAutomatically, + flowType: ConnectionFlowType.ConnectRadioBridge, + }, + }); + }); test("radio bridge device reconnect fail", () => { testGetNextConnectionState({ input: { @@ -264,7 +287,7 @@ describe("getNextConnectionState for radio connection", () => { initialOnFirstConnectAttempt: false, expectedOnFirstConnectAttempt: false, initialHasAttemptedReconnect: true, - expectedHasAttemptedReconnect: false, + expectedHasAttemptedReconnect: true, expectedNextConnectionState: { status: ConnectionStatus.FailedToReconnectTwice, flowType: ConnectionFlowType.ConnectRadioRemote, @@ -321,7 +344,7 @@ describe("getNextConnectionState for radio connection", () => { initialOnFirstConnectAttempt: false, expectedOnFirstConnectAttempt: false, initialHasAttemptedReconnect: true, - expectedHasAttemptedReconnect: false, + expectedHasAttemptedReconnect: true, expectedNextConnectionState: { status: ConnectionStatus.FailedToReconnectTwice, flowType: ConnectionFlowType.ConnectRadioRemote, @@ -527,7 +550,7 @@ describe("getNextConnectionState for bluetooth connection", () => { initialOnFirstConnectAttempt: false, expectedOnFirstConnectAttempt: false, initialHasAttemptedReconnect: true, - expectedHasAttemptedReconnect: false, + expectedHasAttemptedReconnect: true, expectedNextConnectionState: { status: ConnectionStatus.FailedToReconnectTwice, flowType: ConnectionFlowType.ConnectBluetooth, diff --git a/src/get-next-connection-state.ts b/src/get-next-connection-state.ts index 4ebfc96c0..4bd0e6a89 100644 --- a/src/get-next-connection-state.ts +++ b/src/get-next-connection-state.ts @@ -13,6 +13,7 @@ export interface GetNextConnectionStateInput { setHasAttemptedReconnect: (val: boolean) => void; onFirstConnectAttempt: boolean; setOnFirstConnectAttempt: (val: boolean) => void; + isBrowserTabVisible: boolean; } export type NextConnectionState = @@ -29,6 +30,7 @@ export const getNextConnectionState = ({ setHasAttemptedReconnect, onFirstConnectAttempt, setOnFirstConnectAttempt, + isBrowserTabVisible, }: GetNextConnectionStateInput): NextConnectionState => { if (currStatus === ConnectionStatus.Disconnected) { // Do not update connection status when user explicitly disconnected connection @@ -42,6 +44,27 @@ export const getNextConnectionState = ({ ? ConnectionFlowType.ConnectRadioRemote : ConnectionFlowType.ConnectBluetooth; + // Don't update connection status when hiding browser tab if connection + // status is already set to an error case. + if ( + !isBrowserTabVisible && + (currStatus === ConnectionStatus.ConnectionLost || + currStatus === ConnectionStatus.FailedToReconnect || + currStatus === ConnectionStatus.FailedToReconnectTwice) + ) { + return undefined; + } + + const hasStartedOver = + currStatus === ConnectionStatus.NotConnected || + currStatus === ConnectionStatus.FailedToConnect || + currStatus === ConnectionStatus.FailedToReconnectTwice; + + if (hasStartedOver) { + setHasAttemptedReconnect(false); + setOnFirstConnectAttempt(true); + } + // We use usb status to infer the radio bridge device status for handling error. if (type === "usb") { if ( @@ -59,11 +82,14 @@ export const getNextConnectionState = ({ ) { return undefined; } + // Show reconnecting automatically when user hides browser tab and there is a connection error. + if (!isBrowserTabVisible) { + return { status: ConnectionStatus.ReconnectingAutomatically, flowType }; + } if ( // If bridge micro:bit causes radio bridge reconnect to fail twice hasAttempedReconnect ) { - setHasAttemptedReconnect(false); return { status: ConnectionStatus.FailedToReconnectTwice, flowType: ConnectionFlowType.ConnectRadioRemote, @@ -78,15 +104,6 @@ export const getNextConnectionState = ({ return { status, flowType }; } - const hasStartedOver = - currStatus === ConnectionStatus.NotConnected || - currStatus === ConnectionStatus.FailedToConnect; - - if (hasStartedOver) { - setHasAttemptedReconnect(false); - setOnFirstConnectAttempt(true); - } - if ( // If user starts or restarts connection flow. // Disconnection happens for newly started / restarted @@ -126,7 +143,6 @@ export const getNextConnectionState = ({ hasAttempedReconnect && deviceStatus === DeviceConnectionStatus.DISCONNECTED ) { - setHasAttemptedReconnect(false); return { status: ConnectionStatus.FailedToReconnectTwice, flowType }; } if (