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) => (