diff --git a/.changeset/many-hats-push.md b/.changeset/many-hats-push.md
new file mode 100644
index 000000000000..9a9ad22ed1a2
--- /dev/null
+++ b/.changeset/many-hats-push.md
@@ -0,0 +1,5 @@
+---
+"live-mobile": patch
+---
+
+Add LedgerSync status banner + hook
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
new file mode 100644
index 000000000000..d6f803417a64
--- /dev/null
+++ b/apps/ledger-live-mobile/src/newArch/features/WalletSync/__integrations__/walletSyncStatus.integration.test.tsx
@@ -0,0 +1,56 @@
+import React from "react";
+import { screen } from "@testing-library/react-native";
+import { render } from "@tests/test-renderer";
+import { WalletSyncSettingsNavigator } from "./shared";
+import { State } from "~/reducers/types";
+
+jest.mock("../hooks/useLedgerSyncStatus", () => ({
+ useLedgerSyncStatus: () => ({
+ error: new Error(),
+ isError: true,
+ }),
+}));
+
+describe("WalletSyncStatus", () => {
+ it("Should display warning banner when LedgerSync is down", async () => {
+ const { user } = render(, {
+ overrideInitialState: (state: State) => ({
+ ...state,
+ settings: {
+ ...state.settings,
+ readOnlyModeEnabled: false,
+ overriddenFeatureFlags: {
+ llmWalletSync: {
+ enabled: true,
+ params: {
+ environment: "STAGING",
+ watchConfig: {
+ pollingInterval: 10000,
+ initialTimeout: 5000,
+ userIntentDebounce: 1000,
+ },
+ },
+ },
+ },
+ },
+ trustchain: {
+ ...state.trustchain,
+ trustchain: {
+ rootId: "rootId",
+ applicationPath: "applicationPath",
+ walletSyncEncryptionKey: "walletSyncEncryptionKey",
+ },
+ },
+ }),
+ });
+
+ // Check if the ledger sync row is visible
+ await expect(await screen.findByText(/ledger sync/i)).toBeVisible();
+
+ // On Press the ledger sync row
+ await user.press(await screen.findByText(/ledger sync/i));
+
+ // Check if the activation screen is visible
+ expect(await screen.findByText(/Ledger Sync is currently unavailable./i)).toBeVisible();
+ });
+});
diff --git a/apps/ledger-live-mobile/src/newArch/features/WalletSync/components/AlertLedgerSyncDown.tsx b/apps/ledger-live-mobile/src/newArch/features/WalletSync/components/AlertLedgerSyncDown.tsx
new file mode 100644
index 000000000000..dc0e0779464f
--- /dev/null
+++ b/apps/ledger-live-mobile/src/newArch/features/WalletSync/components/AlertLedgerSyncDown.tsx
@@ -0,0 +1,13 @@
+import React from "react";
+import { useTranslation } from "react-i18next";
+import { Alert } from "@ledgerhq/native-ui";
+
+export function AlertLedgerSyncDown() {
+ const { t } = useTranslation();
+ return (
+
+ );
+}
diff --git a/apps/ledger-live-mobile/src/newArch/features/WalletSync/hooks/type.hooks.ts b/apps/ledger-live-mobile/src/newArch/features/WalletSync/hooks/type.hooks.ts
index 90f47ff861d1..eaca56be58ea 100644
--- a/apps/ledger-live-mobile/src/newArch/features/WalletSync/hooks/type.hooks.ts
+++ b/apps/ledger-live-mobile/src/newArch/features/WalletSync/hooks/type.hooks.ts
@@ -1,6 +1,8 @@
export enum QueryKey {
getMembers = "useGetMembers",
destroyTrustchain = "useDestroyTrustchain",
+ fetchTrustchainStatus = "useFetchTrustchainStatus",
+ fetchCloudSyncStatus = "useFetchCloudSyncStatus",
}
export enum ErrorType {
diff --git a/apps/ledger-live-mobile/src/newArch/features/WalletSync/hooks/useLedgerSyncStatus.ts b/apps/ledger-live-mobile/src/newArch/features/WalletSync/hooks/useLedgerSyncStatus.ts
new file mode 100644
index 000000000000..36f69b03e807
--- /dev/null
+++ b/apps/ledger-live-mobile/src/newArch/features/WalletSync/hooks/useLedgerSyncStatus.ts
@@ -0,0 +1,38 @@
+import { useQueries, UseQueryResult } from "@tanstack/react-query";
+import { QueryKey } from "./type.hooks";
+import getTrustchainApi, { StatusAPIResponse as TrustchainStatus } from "@ledgerhq/trustchain/api";
+import getCloudSyncApi, {
+ StatusAPIResponse as CloudSyncStatus,
+} from "@ledgerhq/live-wallet/cloudsync/api";
+import { useFeature } from "@ledgerhq/live-common/featureFlags/index";
+import getWalletSyncEnvironmentParams from "@ledgerhq/live-common/walletSync/getEnvironmentParams";
+
+export function useLedgerSyncStatus() {
+ const featureWalletSync = useFeature("llmWalletSync");
+ const { trustchainApiBaseUrl, cloudSyncApiBaseUrl } = getWalletSyncEnvironmentParams(
+ featureWalletSync?.params?.environment,
+ );
+ const QUERIES = [
+ {
+ queryKey: [QueryKey.fetchTrustchainStatus],
+ queryFn: () => getTrustchainApi(trustchainApiBaseUrl).fetchStatus(),
+ },
+ {
+ queryKey: [QueryKey.fetchCloudSyncStatus],
+ queryFn: () => getCloudSyncApi(cloudSyncApiBaseUrl).fetchStatus(),
+ },
+ ];
+
+ return useQueries({
+ queries: QUERIES,
+ combine: combineData,
+ });
+}
+
+function combineData(results: UseQueryResult[]) {
+ return {
+ error: results.find(result => result.isError)?.error || null,
+ isLoading: results.some(result => result.isLoading),
+ isError: results.some(result => result.isError),
+ };
+}
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 e41f5a953dbf..4ac54b862850 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
@@ -1,5 +1,5 @@
import { Box, Flex, Text, Icons, InfiniteLoader, Alert } from "@ledgerhq/native-ui";
-import React, { useState } from "react";
+import React, { useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { Option, OptionProps } from "./Option";
import styled from "styled-components";
@@ -17,6 +17,9 @@ import ManageInstanceDrawer from "../ManageInstances/ManageInstancesDrawer";
import { useManageInstancesDrawer } from "../ManageInstances/useManageInstanceDrawer";
import ActivationDrawer from "../Activation/ActivationDrawer";
import { Steps } from "../../types/Activation";
+import { TrackScreen } from "~/analytics";
+import { AlertLedgerSyncDown } from "../../components/AlertLedgerSyncDown";
+import { useLedgerSyncStatus } from "../../hooks/useLedgerSyncStatus";
const WalletSyncManage = () => {
const { t } = useTranslation();
@@ -24,7 +27,9 @@ const WalletSyncManage = () => {
const manageKeyHook = useManageKeyDrawer();
const manageInstancesHook = useManageInstancesDrawer();
- const { data, isLoading, isError, error } = manageInstancesHook.memberHook;
+ const { error: ledgerSyncError, isError: isLedgerSyncError } = useLedgerSyncStatus();
+
+ const { data, isLoading, isError, error: manageInstancesError } = manageInstancesHook.memberHook;
const { onClickTrack } = useLedgerSyncAnalytics();
@@ -67,17 +72,29 @@ const WalletSyncManage = () => {
},
];
- function getError(error: Error) {
- // if (error instanceof UnavailableServerError) { DO SOMETHING}
+ const error = useMemo(
+ () => ledgerSyncError || manageInstancesError,
+ [ledgerSyncError, manageInstancesError],
+ );
- return ;
- }
+ const getTopContent = () => {
+ if (error) {
+ if (isLedgerSyncError) {
+ return ;
+ }
+ return ;
+ } else {
+ return ;
+ }
+ };
+
+ const hasError = isLedgerSyncError || isError;
return (
- {/* */}
+
- {isError ? getError(error) : }
+ {getTopContent()}
{Options.map((props, index) => (