diff --git a/.changeset/shiny-pants-refuse.md b/.changeset/shiny-pants-refuse.md
new file mode 100644
index 000000000000..39cc71c9e820
--- /dev/null
+++ b/.changeset/shiny-pants-refuse.md
@@ -0,0 +1,5 @@
+---
+"live-mobile": patch
+---
+
+Avoid blink when LLM is unsync
diff --git a/apps/ledger-live-mobile/__mocks__/api/LedgerSync/authenticate.json b/apps/ledger-live-mobile/__mocks__/api/LedgerSync/authenticate.json
new file mode 100644
index 000000000000..4d83737de88e
--- /dev/null
+++ b/apps/ledger-live-mobile/__mocks__/api/LedgerSync/authenticate.json
@@ -0,0 +1,8 @@
+{
+ "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJ0cnVzdGNoYWluLWJhY2tlbmQuYXBpLmF3cy5zdGcubGRnLXRlY2guY29tIiwic3ViIjoiMDMyNjk2ZWJkN2MyNzZjYjIwODM4MmNjYTk2Mzg4YWJjNWYxNTBkNDE1YjU5OWY3OTgzZmYzNTc1YmNlNDQxNDAxIiwiYXVkIjpbIkNsb3VkU3luYyIsInRydXN0Y2hhaW4iXSwiZXhwIjoxNzI2NjY1MjUwLCJpYXQiOjE3MjY2NjQ5NDksImp0aSI6IjhkM2JiYTVmLTg1NGYtNDkyNy1iZDFjLWUwMzE1NjE4Y2VmMCIsInBlcm1pc3Npb25zIjp7fSwiZGV2aWNlIjp0cnVlLCJyZWZyZXNoRXhwaXJhdGlvbiI6IjIwMjQtMDktMThUMTQ6MDk6MTBaIn0.wUyEXD488WSbKbdOM7Wk33SjKMzm7VGP3nCGRfTYhgNkI6vNdPrEQnrwKo6uloWsVvL5BrNpBCkd_8GPf2eyZw",
+ "permissions": {
+ "000c9ec1a1ab774f7eaeff2b0d4ad695f1fa07ea28d33f5d34126cb1152d6d83f6": {
+ "m/0'/16'/0'": ["owner"]
+ }
+ }
+}
diff --git a/apps/ledger-live-mobile/__mocks__/api/LedgerSync/challenge.json b/apps/ledger-live-mobile/__mocks__/api/LedgerSync/challenge.json
new file mode 100644
index 000000000000..f94954db0ba9
--- /dev/null
+++ b/apps/ledger-live-mobile/__mocks__/api/LedgerSync/challenge.json
@@ -0,0 +1,27 @@
+{
+ "json": {
+ "version": 0,
+ "challenge": {
+ "data": "f0c8ba737d490e16fb10dec07d2ae872",
+ "expiry": "2024-09-18T13:41:45Z"
+ },
+ "host": "trustchain-backend.api.aws.stg.ldg-tech.com",
+ "rp": [
+ {
+ "credential": {
+ "version": 0,
+ "curveId": 33,
+ "signAlgorithm": 1,
+ "publicKey": "03cb7628e7248ddf9c07da54b979f16bf081fb3d173aac0992ad2a44ef6a388ae2"
+ },
+ "signature": "3045022100fc6b314d0cfc74ccb3b3aa2ff0d5648c9b4e8ef997eae2370593f78223be0b1b02201e56255b3b3caf29a349ca8d69c0955b32f0eed54d70077d2b46f23b4fb19052"
+ }
+ ],
+ "protocolVersion": {
+ "major": 1,
+ "minor": 0,
+ "patch": 0
+ }
+ },
+ "tlv": "0101070201001210f0c8ba737d490e16fb10dec07d2ae87214010115473045022100fc6b314d0cfc74ccb3b3aa2ff0d5648c9b4e8ef997eae2370593f78223be0b1b02201e56255b3b3caf29a349ca8d69c0955b32f0eed54d70077d2b46f23b4fb19052160466ead899202b7472757374636861696e2d6261636b656e642e6170692e6177732e7374672e6c64672d746563682e636f6d320121332103cb7628e7248ddf9c07da54b979f16bf081fb3d173aac0992ad2a44ef6a388ae2600401000000"
+}
diff --git a/apps/ledger-live-mobile/__mocks__/api/LedgerSync/info.json b/apps/ledger-live-mobile/__mocks__/api/LedgerSync/info.json
new file mode 100644
index 000000000000..5d74c7a5f5cd
--- /dev/null
+++ b/apps/ledger-live-mobile/__mocks__/api/LedgerSync/info.json
@@ -0,0 +1 @@
+{ "name": "cloud-sync-backend-service", "version": "0.5.1-RC2" }
diff --git a/apps/ledger-live-mobile/__mocks__/api/LedgerSync/v1.json b/apps/ledger-live-mobile/__mocks__/api/LedgerSync/v1.json
new file mode 100644
index 000000000000..c2facb5138c8
--- /dev/null
+++ b/apps/ledger-live-mobile/__mocks__/api/LedgerSync/v1.json
@@ -0,0 +1,4 @@
+{
+ "m/": "01010102201415fb105d3e3cf464bcb7cce33380c480941659f7898948d35a5baf6c29b2930621032696ebd7c276cb208382cca96388abc5f150d415b599f7983ff3575bce44140101010110b005000102000006210306e3967662753cee32f227206f75327f0cc8fe0ca946ffd3f506fc4177b67fbe05100991dfda0c5bccdef61c983b284d4d0105508e4a84ab9ddedca1e31f3a7ee15f01666dd6e25f029527bc7901956aea2938a187cdfcf105932be5f8839d4722fd7e5bcde14afdd48622c9ce1d7049d48f8d6e23e352d05ace6494ec5e4090b0680807062102a9ae8ecdae5e00ff5a5f825c69d2994ce6f8833137f5df30eb8022db4f0ac2170346304402206c05e3366d190556062cc64a5cdcd294aa79e81259a1d82676d94dfe402243b4022043c6fede859beb41e0847905869b9ac4bc4050f25f507cf6324da360f24eb682",
+ "m/16'": "01010102200c9ec1a1ab774f7eaeff2b0d4ad695f1fa07ea28d33f5d34126cb1152d6d83f60621032696ebd7c276cb208382cca96388abc5f150d415b599f7983ff3575bce44140101010315b8050c800000008000001080000000062103183c0c8122c89dd59204919d434f08abf831e743fd826e4ce9bbdb9cad49d6710510b27a7045d2537beb7093c0f96459d9150550b356f67f576caaf6c57bec42cfff397a3ab970844fa380f0b186129155dd3973fa3ab445201dc7e0a5537e7ae3b175594dc6db58b34ef9b5162cfbdd78b4d72ea46189532bc4889ef46a35131467c5b8062103f1041567f9b8b0f658388e2d7f033239e2884c109fa4c1a751e7ae2e3d640e181137040c64656275672d643638326230062103d682b0be923a68e2aa077c3b49c79be57d447d8dca615628f5adceb2ccd175be0104ffffffff12aa0510b5fb0ca72e5c79d5b2229eba894ee85d05505807e4e403453725031e892885aba11acec0ea1f814b013a0e4841e24ab07cd4c1c9dc9c2829c992d3a0ae416a7db24e9b96cc73c2454c8b5849aaa3db2c80afa395bab59957e6b17d7fdbd5ec124476062103d682b0be923a68e2aa077c3b49c79be57d447d8dca615628f5adceb2ccd175be0621020918680272a96341dadeb69e43800618c739f150c066162065ff351d4298de3a0346304402202ffa1cb831595fe2594fd45c094987b32b49934bef9f285ff6086997a3ab46b3022043ba33239b19a0323788d29c2c638d4408097f144194219cc813ae4e31a476d6"
+}
diff --git a/apps/ledger-live-mobile/__tests__/handlers/index.ts b/apps/ledger-live-mobile/__tests__/handlers/index.ts
index cc6f31f32412..768cf0edb742 100644
--- a/apps/ledger-live-mobile/__tests__/handlers/index.ts
+++ b/apps/ledger-live-mobile/__tests__/handlers/index.ts
@@ -1,9 +1,12 @@
import marketHandlers from "./market";
+import ledgerSyncHandlers from "./ledgerSync";
export const ALLOWED_UNHANDLED_REQUESTS = [
"ledger.statuspage.io",
"cdn.live.ledger.com/announcements",
"swap.ledger.com/v5/currencies/all",
+ "https://cdn.live.ledger.com/swap-providers/data.json",
+ "https://crypto-assets-service.api.ledger.com/v1/partners?output=name,signature,public_key,public_key_curve&service_name=swap",
];
-export default [...marketHandlers];
+export default [...marketHandlers, ...ledgerSyncHandlers];
diff --git a/apps/ledger-live-mobile/__tests__/handlers/ledgerSync.ts b/apps/ledger-live-mobile/__tests__/handlers/ledgerSync.ts
new file mode 100644
index 000000000000..8b9b0f4ae864
--- /dev/null
+++ b/apps/ledger-live-mobile/__tests__/handlers/ledgerSync.ts
@@ -0,0 +1,27 @@
+import { http, HttpResponse } from "msw";
+import AuthenticateJson from "../../__mocks__/api/LedgerSync/authenticate.json";
+import ChallengeJson from "../../__mocks__/api/LedgerSync/challenge.json";
+import InfoJson from "../../__mocks__/api/LedgerSync/info.json";
+import v1Json from "../../__mocks__/api/LedgerSync/v1.json";
+const handlers = [
+ http.post("https://trustchain-backend.api.aws.stg.ldg-tech.com/v1/authenticate", () => {
+ return HttpResponse.json(AuthenticateJson);
+ }),
+ http.get("https://trustchain-backend.api.aws.stg.ldg-tech.com/v1/challenge", () => {
+ return HttpResponse.json(ChallengeJson);
+ }),
+ http.get("https://cloud-sync-backend.api.aws.stg.ldg-tech.com/_info", () => {
+ return HttpResponse.json(InfoJson);
+ }),
+ http.get("https://trustchain-backend.api.aws.stg.ldg-tech.com/_info", () => {
+ return HttpResponse.json(InfoJson);
+ }),
+ http.get(
+ "https://trustchain-backend.api.aws.stg.ldg-tech.com/v1/trustchain/000c9ec1a1ab774f7eaeff2b0d4ad695f1fa07ea28d33f5d34126cb1152d6d83f6",
+ () => {
+ return HttpResponse.json(v1Json);
+ },
+ ),
+];
+
+export default handlers;
diff --git a/apps/ledger-live-mobile/src/newArch/features/WalletSync/__integrations__/manageKey.integration.test.tsx b/apps/ledger-live-mobile/src/newArch/features/WalletSync/__integrations__/manageKey.integration.test.tsx
index 1928e0a8f62a..a229c96cd4ea 100644
--- a/apps/ledger-live-mobile/src/newArch/features/WalletSync/__integrations__/manageKey.integration.test.tsx
+++ b/apps/ledger-live-mobile/src/newArch/features/WalletSync/__integrations__/manageKey.integration.test.tsx
@@ -3,6 +3,7 @@ import { screen } from "@testing-library/react-native";
import { render } from "@tests/test-renderer";
import { WalletSyncSettingsNavigator } from "./shared";
import { State } from "~/reducers/types";
+import { crypto } from "@ledgerhq/hw-trustchain";
jest.mock("../hooks/useDestroyTrustchain", () => ({
useDestroyTrustchain: () => ({
@@ -16,6 +17,7 @@ jest.mock("../hooks/useDestroyTrustchain", () => ({
describe("ManageKey", () => {
it("Should open ManageKey flow and delete trustchain", async () => {
+ const keypair = await crypto.randomKeypair();
const { user } = render(, {
overrideInitialState: (state: State) => ({
...state,
@@ -39,13 +41,13 @@ describe("ManageKey", () => {
trustchain: {
...state.trustchain,
trustchain: {
- rootId: "rootId",
- applicationPath: "applicationPath",
- walletSyncEncryptionKey: "walletSyncEncryptionKey",
+ rootId: "000c9ec1a1ab774f7eaeff2b0d4ad695f1fa07ea28d33f5d34126cb1152d6d83f6",
+ applicationPath: "m/0'/16'/0'",
+ walletSyncEncryptionKey: crypto.to_hex(keypair.privateKey),
},
memberCredentials: {
- privatekey: "privatekey",
- pubkey: "pubkey",
+ privatekey: crypto.to_hex(keypair.privateKey),
+ pubkey: "03d682b0be923a68e2aa077c3b49c79be57d447d8dca615628f5adceb2ccd175be",
},
},
}),
diff --git a/apps/ledger-live-mobile/src/newArch/features/WalletSync/__integrations__/walletSyncActivated.integration.test.tsx b/apps/ledger-live-mobile/src/newArch/features/WalletSync/__integrations__/walletSyncActivated.integration.test.tsx
index dec5667286bf..a64a4185928c 100644
--- a/apps/ledger-live-mobile/src/newArch/features/WalletSync/__integrations__/walletSyncActivated.integration.test.tsx
+++ b/apps/ledger-live-mobile/src/newArch/features/WalletSync/__integrations__/walletSyncActivated.integration.test.tsx
@@ -3,8 +3,12 @@ import { screen } from "@testing-library/react-native";
import { render } from "@tests/test-renderer";
import { WalletSyncSettingsNavigator } from "./shared";
import { State } from "~/reducers/types";
+import { crypto } from "@ledgerhq/hw-trustchain";
+
describe("WalletSyncActivated", () => {
it("Should open WalletSyncActivated screen", async () => {
+ const keypair = await crypto.randomKeypair();
+
const { user } = render(, {
overrideInitialState: (state: State) => ({
...state,
@@ -28,9 +32,13 @@ describe("WalletSyncActivated", () => {
trustchain: {
...state.trustchain,
trustchain: {
- rootId: "rootId",
- applicationPath: "applicationPath",
- walletSyncEncryptionKey: "walletSyncEncryptionKey",
+ rootId: "000c9ec1a1ab774f7eaeff2b0d4ad695f1fa07ea28d33f5d34126cb1152d6d83f6",
+ applicationPath: "m/0'/16'/0'",
+ walletSyncEncryptionKey: crypto.to_hex(keypair.privateKey),
+ },
+ memberCredentials: {
+ privatekey: crypto.to_hex(keypair.privateKey),
+ pubkey: "03d682b0be923a68e2aa077c3b49c79be57d447d8dca615628f5adceb2ccd175be",
},
},
}),
diff --git a/apps/ledger-live-mobile/src/newArch/features/WalletSync/__integrations__/walletSyncStatus.integration.test.tsx b/apps/ledger-live-mobile/src/newArch/features/WalletSync/__integrations__/walletSyncStatus.integration.test.tsx
index d6f803417a64..a3563676c8f3 100644
--- a/apps/ledger-live-mobile/src/newArch/features/WalletSync/__integrations__/walletSyncStatus.integration.test.tsx
+++ b/apps/ledger-live-mobile/src/newArch/features/WalletSync/__integrations__/walletSyncStatus.integration.test.tsx
@@ -3,6 +3,8 @@ import { screen } from "@testing-library/react-native";
import { render } from "@tests/test-renderer";
import { WalletSyncSettingsNavigator } from "./shared";
import { State } from "~/reducers/types";
+import { http, HttpResponse } from "msw";
+import { server } from "@tests/server";
jest.mock("../hooks/useLedgerSyncStatus", () => ({
useLedgerSyncStatus: () => ({
@@ -44,6 +46,12 @@ describe("WalletSyncStatus", () => {
}),
});
+ server.use(
+ http.get("https://trustchain-backend.api.aws.stg.ldg-tech.com/v1/challenge", () => {
+ return HttpResponse.error();
+ }),
+ );
+
// Check if the ledger sync row is visible
await expect(await screen.findByText(/ledger sync/i)).toBeVisible();
diff --git a/apps/ledger-live-mobile/src/newArch/features/WalletSync/hooks/useCustomTimeOut.ts b/apps/ledger-live-mobile/src/newArch/features/WalletSync/hooks/useCustomTimeOut.ts
new file mode 100644
index 000000000000..420ad433aa57
--- /dev/null
+++ b/apps/ledger-live-mobile/src/newArch/features/WalletSync/hooks/useCustomTimeOut.ts
@@ -0,0 +1,17 @@
+import { useEffect, useState } from "react";
+
+export function useCustomTimeOut(timeout: number) {
+ const [isTimeout, setIsTimeout] = useState(false);
+
+ useEffect(() => {
+ const timer = setTimeout(() => {
+ setIsTimeout(true);
+ }, timeout);
+
+ return () => {
+ clearTimeout(timer);
+ };
+ }, [timeout]);
+
+ return isTimeout;
+}
diff --git a/apps/ledger-live-mobile/src/newArch/features/WalletSync/hooks/walletSync.hooks.ts b/apps/ledger-live-mobile/src/newArch/features/WalletSync/hooks/walletSync.hooks.ts
index 080c3b56f145..56c0f07b8c56 100644
--- a/apps/ledger-live-mobile/src/newArch/features/WalletSync/hooks/walletSync.hooks.ts
+++ b/apps/ledger-live-mobile/src/newArch/features/WalletSync/hooks/walletSync.hooks.ts
@@ -30,8 +30,6 @@ export const useLifeCycle = () => {
};
function handleError(error: Error) {
- console.error("GetMember :" + error);
-
if (error instanceof TrustchainEjected) reset();
if (error instanceof TrustchainNotAllowed) reset();
diff --git a/apps/ledger-live-mobile/src/newArch/features/WalletSync/screens/Manage/index.tsx b/apps/ledger-live-mobile/src/newArch/features/WalletSync/screens/Manage/index.tsx
index 461467e05f0e..7e31acc7a853 100644
--- a/apps/ledger-live-mobile/src/newArch/features/WalletSync/screens/Manage/index.tsx
+++ b/apps/ledger-live-mobile/src/newArch/features/WalletSync/screens/Manage/index.tsx
@@ -20,6 +20,7 @@ import { TrackScreen } from "~/analytics";
import { AlertLedgerSyncDown } from "../../components/AlertLedgerSyncDown";
import { useLedgerSyncStatus } from "../../hooks/useLedgerSyncStatus";
import { TrustchainNotFound } from "@ledgerhq/trustchain/errors";
+import { useCustomTimeOut } from "../../hooks/useCustomTimeOut";
const WalletSyncManage = () => {
const { t } = useTranslation();
@@ -29,7 +30,15 @@ const WalletSyncManage = () => {
const { error: ledgerSyncError, isError: isLedgerSyncError } = useLedgerSyncStatus();
- const { data, isLoading, isError, error: manageInstancesError } = manageInstancesHook.memberHook;
+ const {
+ data,
+ isLoading,
+ isError,
+ error: manageInstancesError,
+ isFetching,
+ isFetchedAfterMount,
+ isPending,
+ } = manageInstancesHook.memberHook;
const { onClickTrack } = useLedgerSyncAnalytics();
@@ -97,62 +106,76 @@ const WalletSyncManage = () => {
const hasError = isLedgerSyncError || isError;
+ const queryFetching = isPending || isFetching;
+ // the following checks if the instance has been deleted from another device to avoid a blink
+ // in dev on a real device it will blink since it's laggy
+ const unsynchronizedInstance = !data && !isFetchedAfterMount;
+
+ const forcedTimeLoaderOver = useCustomTimeOut(500);
+ const shouldDisplayLoader = !forcedTimeLoaderOver || unsynchronizedInstance || queryFetching;
+
return (
-
- {getTopContent()}
-
- {Options.map((props, index) => (
-
- ))}
-
-
-
- {isLoading ? (
-
- ) : (
-
- {isError
- ? t("walletSync.walletSyncActivated.synchronizedInstances.error")
- : t("walletSync.walletSyncActivated.synchronizedInstances.title", {
- count: data?.length,
- })}
-
- )}
-
-
-
- {t("walletSync.walletSyncActivated.synchronizedInstances.cta")}
-
-
-
-
-
-
-
-
-
+ {shouldDisplayLoader ? (
+
+
+
+ ) : (
+ <>
+ {getTopContent()}
+ {Options.map((props, index) => (
+
+ ))}
+
+
+
+ {isLoading ? (
+
+ ) : (
+
+ {isError
+ ? t("walletSync.walletSyncActivated.synchronizedInstances.error")
+ : t("walletSync.walletSyncActivated.synchronizedInstances.title", {
+ count: data?.length,
+ })}
+
+ )}
+
+
+
+ {t("walletSync.walletSyncActivated.synchronizedInstances.cta")}
+
+
+
+
+
+
+
+
+
+ >
+ )}
);
};