Skip to content

Commit

Permalink
Feat/wallet sync loading screen on initialization (#7691)
Browse files Browse the repository at this point in the history
feat(llm/lld): wallet sync loading screen added on initialization
  • Loading branch information
cgrellard-ledger committed Aug 29, 2024
1 parent fb9466a commit ce18c9b
Show file tree
Hide file tree
Showing 25 changed files with 246 additions and 17 deletions.
9 changes: 9 additions & 0 deletions .changeset/good-boats-breathe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"ledger-live-desktop": minor
"live-mobile": minor
"@ledgerhq/live-wallet": minor
"@ledgerhq/trustchain": minor
"@ledgerhq/web-tools": minor
---

Ledger Sync - Added a Loading screen on LLM and LLD when initializing ledger sync while accounts are synchronizing
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,12 @@ export function useAddMember({ device }: { device: Device | null }) {
dispatch(
setFlow({
flow: Flow.Activation,
step:
step: Step.ActivationLoading,
nextStep:
trustchainResult.type === TrustchainResultType.created
? Step.ActivationFinal
: Step.SynchronizationFinal,
hasTrustchainBeenCreated: trustchainResult.type === TrustchainResultType.created,
}),
);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,19 @@ export const FlowOptions: Record<
1: Step.CreateOrSynchronize,
2: Step.DeviceAction,
3: Step.CreateOrSynchronizeTrustChain,
4: Step.ActivationFinal,
5: Step.SynchronizationFinal,
6: Step.SynchronizationError,
4: Step.ActivationLoading,
5: Step.ActivationFinal,
6: Step.SynchronizationFinal,
7: Step.SynchronizationError,
},
},
[Flow.Synchronize]: {
steps: {
1: Step.SynchronizeMode,
2: Step.SynchronizeWithQRCode,
3: Step.PinCode,
4: Step.Synchronized,
4: Step.SynchronizeLoading,
5: Step.Synchronized,
},
},
[Flow.ManageBackup]: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export enum AnalyticsPage {
DesktopSync = "Sync from a desktop",
KeyCreated = "Backup creation success",
KeyUpdated = "Sync apps success",
Loading = "Loading",

SyncWithQR = "Sync with QR code",
PinCode = "Pin code",
Expand Down Expand Up @@ -60,6 +61,7 @@ export const StepMappedToAnalytics: Record<Step, string> = {
[Step.CreateOrSynchronize]: AnalyticsPage.Activation,
[Step.DeviceAction]: AnalyticsPage.DeviceActionActivation,
[Step.CreateOrSynchronizeTrustChain]: AnalyticsPage.CreateOrSynchronizeTrustChain,
[Step.ActivationLoading]: AnalyticsPage.Loading,
[Step.ActivationFinal]: AnalyticsPage.KeyCreated,
[Step.SynchronizationFinal]: AnalyticsPage.KeyUpdated,
[Step.SynchronizationError]: AnalyticsPage.SynchronizationError,
Expand All @@ -69,6 +71,7 @@ export const StepMappedToAnalytics: Record<Step, string> = {
//Synchronize
[Step.SynchronizeMode]: AnalyticsPage.SyncMethod,
[Step.SynchronizeWithQRCode]: AnalyticsPage.SyncWithQR,
[Step.SynchronizeLoading]: AnalyticsPage.Loading,
[Step.PinCode]: AnalyticsPage.PinCode,
[Step.PinCodeError]: AnalyticsPage.PinCodeError,
[Step.Synchronized]: AnalyticsPage.KeyUpdated,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { setFlow } from "~/renderer/actions/walletSync";
import { walletSyncFlowSelector, walletSyncNextStepSelector } from "~/renderer/reducers/walletSync";
import { useWalletSyncUserState } from "../components/WalletSyncContext";
import { useFeature } from "@ledgerhq/live-common/featureFlags/index";

export function useLoadingStep() {
const dispatch = useDispatch();
const [waitedWatchLoop, setWaitedWatchLoop] = useState(false);
const { visualPending } = useWalletSyncUserState();
const nextStep = useSelector(walletSyncNextStepSelector);
const flow = useSelector(walletSyncFlowSelector);
const featureWalletSync = useFeature("lldWalletSync");
const initialTimeout = featureWalletSync?.params?.watchConfig?.initialTimeout || 1000;
const visualPendingTimeout = 1000;

useEffect(() => {
const timeout = setTimeout(
() => {
setWaitedWatchLoop(true);
},
initialTimeout + visualPendingTimeout + 500,
);

return () => {
clearTimeout(timeout);
};
}, [initialTimeout]);

useEffect(() => {
if (waitedWatchLoop && !visualPending && nextStep) {
dispatch(
setFlow({
flow,
step: nextStep,
nextStep: null,
hasTrustchainBeenCreated: null,
}),
);
}
}, [waitedWatchLoop, visualPending, dispatch, flow, nextStep]);
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,14 @@ export function useQRCode() {
if (newTrustchain) {
dispatch(setTrustchain(newTrustchain));
}
dispatch(setFlow({ flow: Flow.Synchronize, step: Step.Synchronized }));
dispatch(
setFlow({
flow: Flow.Synchronize,
step: Step.SynchronizeLoading,
nextStep: Step.Synchronized,
hasTrustchainBeenCreated: false,
}),
);
queryClient.invalidateQueries({ queryKey: [QueryKey.getMembers] });
setUrl(null);
dispatch(setQrCodePinCode(null));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,6 @@ export function useWatchWalletSync(): WalletSyncUserState {
// pull and push wallet sync loop
useEffect(() => {
const canNotRunWatchLoop = !featureWalletSync?.enabled || !trustchain || !memberCredentials;

if (canNotRunWatchLoop) {
onUserRefreshRef.current = noop;
setVisualPending(false);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from "react";
import Loading from "../../components/LoadingStep";
import { useTranslation } from "react-i18next";
import { AnalyticsPage } from "../../hooks/useLedgerSyncAnalytics";
import TrackPage from "~/renderer/analytics/TrackPage";
import { useLoadingStep } from "../../hooks/useLoadingStep";
import { walletSyncHasTrustchainBeenCreatedSelector } from "~/renderer/reducers/walletSync";
import { useSelector } from "react-redux";

export default function ActivationLoadingStep() {
useLoadingStep();
const hasTrustchainBeenCreated = useSelector(walletSyncHasTrustchainBeenCreatedSelector);
const { t } = useTranslation();
const title = "walletSync.loading.title";
const subtitle = hasTrustchainBeenCreated
? "walletSync.loading.activation"
: "walletSync.loading.synch";

return (
<>
<TrackPage category={String(AnalyticsPage.Loading)} />
<Loading title={t(title)} subtitle={t(subtitle)} />
</>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import { FlowOptions, useFlows } from "../../hooks/useFlows";
import CreateOrSynchronizeStep from "./01-CreateOrSynchronizeStep";
import DeviceActionStep from "./02-DeviceActionStep";
import ActivationOrSynchroWithTrustchain from "./03-ActivationOrSynchroWithTrustchain";
import ActivationFinalStep from "./04-ActivationFinalStep";
import LoadingStep from "./04-LoadingStep";
import ActivationFinalStep from "./05-ActivationFinalStep";
import { Device } from "@ledgerhq/live-common/hw/actions/types";
import ErrorStep from "./05-ActivationOrSyncError";
import ErrorStep from "./06-ActivationOrSyncError";
import {
AnalyticsPage,
useLedgerSyncAnalytics,
Expand Down Expand Up @@ -70,6 +71,8 @@ const WalletSyncActivation = forwardRef<BackRef, BackProps>((_props, ref) => {
return <DeviceActionStep goNext={goToActivationOrSynchroWithTrustchain} />;
case Step.CreateOrSynchronizeTrustChain:
return <ActivationOrSynchroWithTrustchain device={device} />;
case Step.ActivationLoading:
return <LoadingStep />;
case Step.ActivationFinal:
return <ActivationFinalStep isNewBackup={true} />;
case Step.SynchronizationFinal:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import UnbackedErrorStep from "./05-UnbackedError";
import { BackProps, BackRef } from "../router";
import AlreadyCreatedWithSameSeedStep from "./06-ActivationAlreadyCreatedSame";
import AlreadyCreatedOtherSeedStep from "./07-ActivationAlreadyCreatedOther";
import ActivationLoadingStep from "../Activation/04-LoadingStep";

const SynchronizeWallet = forwardRef<BackRef, BackProps>((_props, ref) => {
const dispatch = useDispatch();
Expand Down Expand Up @@ -80,6 +81,8 @@ const SynchronizeWallet = forwardRef<BackRef, BackProps>((_props, ref) => {
return <AlreadyCreatedWithSameSeedStep />;
case Step.AlreadySecuredOtherSeed:
return <AlreadyCreatedOtherSeedStep />;
case Step.SynchronizeLoading:
return <ActivationLoadingStep />;

case Step.Synchronized:
return <SyncFinalStep />;
Expand Down
4 changes: 2 additions & 2 deletions apps/ledger-live-desktop/src/renderer/actions/walletSync.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { TrustchainMember } from "@ledgerhq/trustchain/types";
import { Flow, Step } from "../reducers/walletSync";
import { ChangeFlowPayload } from "../reducers/walletSync";

export const setFlow = (payload: { flow: Flow; step: Step }) => ({
export const setFlow = (payload: ChangeFlowPayload) => ({
type: "WALLET_SYNC_CHANGE_FLOW",
payload,
});
Expand Down
26 changes: 24 additions & 2 deletions apps/ledger-live-desktop/src/renderer/reducers/walletSync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export enum Step {
CreateOrSynchronize = "CreateOrSynchronize",
DeviceAction = "DeviceAction",
CreateOrSynchronizeTrustChain = "CreateOrSynchronizeTrustChain",
ActivationLoading = "ActivationLoading",
ActivationFinal = "ActivationFinal",
SynchronizationFinal = "SynchronizationFinal",
SynchronizationError = "SynchronizationError",
Expand All @@ -32,6 +33,7 @@ export enum Step {
SynchronizeWithQRCode = "SynchronizeWithQRCode",
PinCode = "PinCode",
PinCodeError = "PinCodeError",
SynchronizeLoading = "SynchronizeLoading",
UnbackedError = "UnbackedError",
Synchronized = "Synchronized",

Expand All @@ -52,6 +54,8 @@ export type WalletSyncState = {
isDrawerOpen: boolean;
flow: Flow;
step: Step;
nextStep: Step | null;
hasTrustchainBeenCreated: boolean | null;
instances: TrustchainMember[];
hasBeenfaked: boolean;
qrCodeUrl: string | null;
Expand All @@ -62,15 +66,24 @@ export const initialStateWalletSync: WalletSyncState = {
isDrawerOpen: false,
flow: Flow.Activation,
step: Step.CreateOrSynchronize,
nextStep: null,
hasTrustchainBeenCreated: null,
instances: [],
hasBeenfaked: false,
qrCodePinCode: null,
qrCodeUrl: null,
};

export type ChangeFlowPayload = {
flow: Flow;
step: Step;
nextStep?: Step | null;
hasTrustchainBeenCreated?: boolean | null;
};

type HandlersPayloads = {
WALLET_SYNC_CHANGE_DRAWER_VISIBILITY: boolean;
WALLET_SYNC_CHANGE_FLOW: { flow: Flow; step: Step };
WALLET_SYNC_CHANGE_FLOW: ChangeFlowPayload;
WALLET_SYNC_CHANGE_ADD_INSTANCE: TrustchainMember;
WALLET_SYNC_CHANGE_REMOVE_INSTANCE: TrustchainMember;
WALLET_SYNC_CHANGE_CLEAN_INSTANCES: undefined;
Expand All @@ -96,11 +109,15 @@ const handlers: WalletSyncHandlers = {
}),
WALLET_SYNC_CHANGE_FLOW: (
state: WalletSyncState,
{ payload: { flow, step } }: { payload: { flow: Flow; step: Step } },
{
payload: { flow, step, nextStep = null, hasTrustchainBeenCreated = null },
}: { payload: ChangeFlowPayload },
) => ({
...state,
flow,
step,
nextStep,
hasTrustchainBeenCreated,
}),
WALLET_SYNC_CHANGE_ADD_INSTANCE: (
state: WalletSyncState,
Expand Down Expand Up @@ -151,6 +168,11 @@ export const walletSyncFlowSelector = (state: { walletSync: WalletSyncState }) =
state.walletSync.flow;
export const walletSyncStepSelector = (state: { walletSync: WalletSyncState }) =>
state.walletSync.step;
export const walletSyncNextStepSelector = (state: { walletSync: WalletSyncState }) =>
state.walletSync.nextStep;
export const walletSyncHasTrustchainBeenCreatedSelector = (state: {
walletSync: WalletSyncState;
}) => state.walletSync.hasTrustchainBeenCreated;
export const walletSyncInstancesSelector = (state: { walletSync: WalletSyncState }) =>
state.walletSync.instances;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ export type WalletSyncNavigatorStackParamList = {
created: boolean;
};

[ScreenName.WalletSyncLoading]: {
created: boolean;
};

[ScreenName.WalletSyncActivationProcess]: undefined;
[ScreenName.WalletSyncActivated]: undefined;

Expand Down
1 change: 1 addition & 0 deletions apps/ledger-live-mobile/src/const/navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,7 @@ export enum ScreenName {
WalletSyncActivationInit = "WalletSyncActivationInit",
WalletSyncActivationProcess = "WalletSyncActivationProcess",
WalletSyncSuccess = "WalletSyncSuccess",
WalletSyncLoading = "WalletSyncLoading",
WalletSyncActivated = "WalletSyncActivated",
WalletSyncManageKeyDeleteSuccess = "WalletSyncManageKeyDeleteSuccess",
WalletSyncUnSynchSuccess = "WalletSyncUnSynchSuccess",
Expand Down
5 changes: 5 additions & 0 deletions apps/ledger-live-mobile/src/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -6746,6 +6746,11 @@
"title": "Continue on your Ledger {{wording}}",
"description": "Follow the instruction which appear on your Ledger’s Trusted Display."
},
"loading": {
"title": "Hang tight...",
"activation": "Your data is being end-to-end encrypted... ",
"synch": "We are updating the synched instances..."
},
"success": {
"sync": "Synchronization successful!",
"syncDesc": "Changes in your accounts will now automatically appear across all apps and platforms.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ import { WalletSyncNavigatorStackParamList } from "../../../components/RootNavig
import WalletSyncActivation from "LLM/features/WalletSync/screens/Activation";
import { ActivationProcess } from "./screens/Activation/ActivationProcess";
import { ActivationSuccess } from "./screens/Activation/ActivationSuccess";
import { ActivationLoading } from "./screens/Activation/ActivationLoading";
import { useInitMemberCredentials } from "./hooks/useInitMemberCredentials";
import WalletSyncManage from "./screens/Manage";
import { useTranslation } from "react-i18next";
import { WalletSyncManageKeyDeletionSuccess } from "./screens/ManageKey/DeletionSuccess";
import { ManageInstancesProcess } from "./screens/ManageInstances/ManageInstancesProcess";
import { WalletSyncManageInstanceDeletionSuccess } from "./screens/ManageInstances/DeletionSuccess";
import { NavigationHeaderCloseButton } from "~/components/NavigationHeaderCloseButton";

const Stack = createStackNavigator<WalletSyncNavigatorStackParamList>();

Expand Down Expand Up @@ -40,6 +42,15 @@ export default function WalletSyncNavigator() {
headerRight: () => null,
}}
/>
<Stack.Screen
name={ScreenName.WalletSyncLoading}
component={ActivationLoading}
options={{
title: "",
headerLeft: () => null,
headerRight: () => <NavigationHeaderCloseButton />,
}}
/>

<Stack.Screen
name={ScreenName.WalletSyncSuccess}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export function useAddMember({ device }: { device: Device | null }): DrawerProps
const transitionToNextScreen = useCallback(
(trustchainResult: TrustchainResult) => {
dispatch(setTrustchain(trustchainResult.trustchain));
navigation.navigate(ScreenName.WalletSyncSuccess, {
navigation.navigate(ScreenName.WalletSyncLoading, {
created: trustchainResult.type === TrustchainResultType.created,
});
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export enum AnalyticsPage {
SyncWithQrCode = "Sync with QR code",
PinCode = "Pin code",
PinCodesDoNotMatch = "Pin codes don't match",
Loading = "Loading",
SettingsGeneral = "Settings General",
LedgerSyncSettings = "Ledger Sync Settings",
ManageSyncInstances = "Manage synchronized instances",
Expand Down
Loading

0 comments on commit ce18c9b

Please sign in to comment.