diff --git a/.changeset/dry-cheetahs-leave.md b/.changeset/dry-cheetahs-leave.md new file mode 100644 index 000000000000..957f5b1b9d00 --- /dev/null +++ b/.changeset/dry-cheetahs-leave.md @@ -0,0 +1,5 @@ +--- +"ledger-live-desktop": patch +--- + +Fix a crash with Europa in My Ledger when opening an app diff --git a/.changeset/polite-ladybugs-prove.md b/.changeset/polite-ladybugs-prove.md new file mode 100644 index 000000000000..a5ccaf721534 --- /dev/null +++ b/.changeset/polite-ladybugs-prove.md @@ -0,0 +1,5 @@ +--- +"@ledgerhq/devices": patch +--- + +Add type narrowing helper isDeviceModelId diff --git a/apps/ledger-live-desktop/src/renderer/screens/manager/Disconnected.tsx b/apps/ledger-live-desktop/src/renderer/screens/manager/Disconnected.tsx index bcac47f815b4..62262e100a8e 100644 --- a/apps/ledger-live-desktop/src/renderer/screens/manager/Disconnected.tsx +++ b/apps/ledger-live-desktop/src/renderer/screens/manager/Disconnected.tsx @@ -1,6 +1,7 @@ import React, { useEffect, useState, useCallback } from "react"; import { useSelector } from "react-redux"; -import { lastSeenDeviceSelector } from "~/renderer/reducers/settings"; +import { DeviceModelId } from "@ledgerhq/devices"; +import { isDeviceModelId } from "@ledgerhq/devices/helpers"; import { withDevice } from "@ledgerhq/live-common/hw/deviceAccess"; import getAppAndVersion from "@ledgerhq/live-common/hw/getAppAndVersion"; import { Subscription, from } from "rxjs"; @@ -15,6 +16,7 @@ import { Trans } from "react-i18next"; import Box from "~/renderer/components/Box"; import Button from "~/renderer/components/Button"; import { useHistory } from "react-router-dom"; +import { lastSeenDeviceSelector } from "~/renderer/reducers/settings"; import { getCurrentDevice } from "~/renderer/reducers/devices"; import nanoS from "./assets/nanoS.png"; import blue from "./assets/blue.png"; @@ -25,8 +27,12 @@ import blueDark from "./assets/blue_dark.png"; import nanoXDark from "./assets/nanoX_dark.png"; import nanoSPDark from "./assets/nanoSP_dark.png"; import stax from "./assets/stax.png"; +import europa from "./assets/europa.png"; -const illustrations = { +const illustrations: Record< + DeviceModelId, + { light: string; dark: string; width: number; height: number } +> = { nanoX: { light: nanoX, dark: nanoXDark, @@ -51,6 +57,12 @@ const illustrations = { width: 141, height: 223, }, + europa: { + light: europa, + dark: europa, + width: 141, + height: 223, + }, blue: { light: blue, dark: blueDark, @@ -58,8 +70,9 @@ const illustrations = { height: 64, }, }; + const Illustration = styled.div<{ - modelId: string; + modelId: DeviceModelId; }>` // prettier-ignore background: url('${p => @@ -73,7 +86,10 @@ const Illustration = styled.div<{ `; const Disconnected = ({ onTryAgain }: { onTryAgain: (a: boolean) => void }) => { const lastSeenDevice = useSelector(lastSeenDeviceSelector); - const modelId = process.env.OVERRIDE_MODEL_ID || lastSeenDevice?.modelId || "nanoS"; + const overriddenModelId = process.env.OVERRIDE_MODELID; + const modelId: DeviceModelId = isDeviceModelId(overriddenModelId) + ? overriddenModelId + : lastSeenDevice?.modelId || DeviceModelId.nanoS; const [readyToDecide, setReadyToDecide] = useState(false); const [showSpinner, setShowSpinner] = useState(true); const device = useSelector(getCurrentDevice); diff --git a/apps/ledger-live-desktop/src/renderer/screens/manager/assets/europa.png b/apps/ledger-live-desktop/src/renderer/screens/manager/assets/europa.png new file mode 100644 index 000000000000..4b87b6bff921 Binary files /dev/null and b/apps/ledger-live-desktop/src/renderer/screens/manager/assets/europa.png differ diff --git a/libs/ledgerjs/packages/devices/src/helpers.test.ts b/libs/ledgerjs/packages/devices/src/helpers.test.ts index b7c78a96f184..a1d45b699089 100644 --- a/libs/ledgerjs/packages/devices/src/helpers.test.ts +++ b/libs/ledgerjs/packages/devices/src/helpers.test.ts @@ -1,5 +1,24 @@ import { DeviceModelId } from "."; -import { stringToDeviceModelId } from "./helpers"; +import { stringToDeviceModelId, isDeviceModelId } from "./helpers"; + +const validDeviceModelsIds = ["nanoS", "nanoX", "blue", "nanoSP", "stax", "europa"]; +const invalidDeviceModelsIds = ["does-not-exist", "", null, undefined]; + +describe("isDeviceModelId", () => { + validDeviceModelsIds.forEach(potentialModelId => { + test(`Input: ${potentialModelId} -> Expected output: true`, () => { + const result = isDeviceModelId(potentialModelId); + expect(result).toEqual(true); + }); + }); + + invalidDeviceModelsIds.forEach(potentialModelId => { + test(`Input: ${potentialModelId} -> Expected output: false`, () => { + const result = isDeviceModelId(potentialModelId); + expect(result).toEqual(false); + }); + }); +}); type Test = { input: [string, DeviceModelId]; diff --git a/libs/ledgerjs/packages/devices/src/helpers.ts b/libs/ledgerjs/packages/devices/src/helpers.ts index ef6b50bbc3d4..5007ca1b86a6 100644 --- a/libs/ledgerjs/packages/devices/src/helpers.ts +++ b/libs/ledgerjs/packages/devices/src/helpers.ts @@ -1,12 +1,14 @@ import { DeviceModelId } from "."; +export function isDeviceModelId(val: string | undefined | null): val is DeviceModelId { + if (!val) return false; + return Object.values(DeviceModelId).includes(val as DeviceModelId); +} + export const stringToDeviceModelId = ( strDeviceModelId: string, defaultDeviceModelId: DeviceModelId, ): DeviceModelId => { - if (Object.values(DeviceModelId)?.includes(strDeviceModelId as DeviceModelId)) { - return DeviceModelId[strDeviceModelId as DeviceModelId]; - } - - return defaultDeviceModelId; + if (!isDeviceModelId(strDeviceModelId)) return defaultDeviceModelId; + return DeviceModelId[strDeviceModelId]; };