Skip to content

Commit

Permalink
Merge pull request #6802 from LedgerHQ/feat/post-onboarding-deeplink
Browse files Browse the repository at this point in the history
LLM / LLD - Post Onboarding Deeplink
  • Loading branch information
cgrellard-ledger committed May 3, 2024
2 parents 1cee8ff + 463ab46 commit d9ddfcd
Show file tree
Hide file tree
Showing 11 changed files with 205 additions and 1 deletion.
7 changes: 7 additions & 0 deletions .changeset/nice-guests-wave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"ledger-live-desktop": minor
"live-mobile": minor
"@ledgerhq/live-common": minor
---

LLM / LLD - Add a deeplink redirecting to the post onboarding
14 changes: 13 additions & 1 deletion apps/ledger-live-desktop/src/renderer/hooks/useDeeplinking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import { setTrackingSource } from "../analytics/TrackPage";
import { CryptoOrTokenCurrency, Currency } from "@ledgerhq/types-cryptoassets";
import { Account, SubAccount } from "@ledgerhq/types-live";
import { useStorylyContext } from "~/storyly/StorylyProvider";
import { useNavigateToPostOnboardingHubCallback } from "~/renderer/components/PostOnboardingHub/logic/useNavigateToPostOnboardingHubCallback";
import { usePostOnboardingDeeplinkHandler } from "@ledgerhq/live-common/postOnboarding/hooks/index";

const getAccountsOrSubAccountsByCurrency = (
currency: CryptoOrTokenCurrency,
Expand Down Expand Up @@ -43,6 +45,12 @@ export function useDeepLinkHandler() {
const location = useLocation();
const history = useHistory();
const { setUrl } = useStorylyContext();
const navigateToHome = () => history.push("/");
const navigateToPostOnboardingHub = useNavigateToPostOnboardingHubCallback();
const postOnboardingDeeplinkHandler = usePostOnboardingDeeplinkHandler(
navigateToHome,
navigateToPostOnboardingHub,
);

const navigate = useCallback(
(
Expand Down Expand Up @@ -334,13 +342,17 @@ export function useDeepLinkHandler() {
case "storyly":
setUrl(deeplink);
break;
case "post-onboarding": {
postOnboardingDeeplinkHandler(query.device);
break;
}
case "portfolio":
default:
navigate("/");
break;
}
},
[accounts, dispatch, location.pathname, navigate, setUrl],
[accounts, dispatch, location.pathname, navigate, postOnboardingDeeplinkHandler, setUrl],
);
return {
handler,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { PostOnboardingNavigatorParamList } from "./types/PostOnboardingNavigato
import { NavigationHeaderBackButton } from "../NavigationHeaderBackButton";
import { NavigationHeaderCloseButton } from "../NavigationHeaderCloseButton";
import { useCompletePostOnboarding } from "~/logic/postOnboarding/useCompletePostOnboarding";
import PostOnboardingDeeplinkHandler from "~/screens/PostOnboarding/PostOnboardingDeeplinkHandler";

const Stack = createStackNavigator<PostOnboardingNavigatorParamList>();

Expand Down Expand Up @@ -42,6 +43,10 @@ const PostOnboardingNavigator = () => {
name={ScreenName.PostOnboardingDebugScreen}
component={PostOnboardingDebugScreen}
/>
<Stack.Screen
name={ScreenName.PostOnboardingDeeplinkHandler}
component={PostOnboardingDeeplinkHandler}
/>
<Stack.Screen
name={ScreenName.PostOnboardingMockActionScreen}
component={PostOnboardingMockActionScreen}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ export type PostOnboardingNavigatorParamList = {
[ScreenName.PostOnboardingHub]: Record<string, never> | undefined;
[ScreenName.PostOnboardingDebugScreen]: Record<string, never>;
[ScreenName.PostOnboardingMockActionScreen]: { id: PostOnboardingActionId };
[ScreenName.PostOnboardingDeeplinkHandler]: { device: string };
};
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 @@ -494,6 +494,7 @@ export enum ScreenName {
PostOnboardingHub = "PostOnboardingHub",
PostOnboardingDebugScreen = "PostOnboardingDebugScreen",
PostOnboardingMockActionScreen = "PostOnboardingMockActionScreen",
PostOnboardingDeeplinkHandler = "PostOnboardingDeeplinkHandler",
WalletNftGallery = "WalletNftGallery",
NoFunds = "NoFunds",
Stake = "Stake",
Expand Down
12 changes: 12 additions & 0 deletions apps/ledger-live-mobile/src/navigation/DeeplinksProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,18 @@ const linkingOptions = () => ({
},
},
},
[NavigatorName.PostOnboarding]: {
screens: {
/**
* ie: "ledgerlive://post-onboarding" will open the home page as the device parameter is not provided
*
* @params ?device: string
* ie: "ledgerlive://post-onboarding?device=stax" will open post-onboarding for the stax device (it should be a device model id)
*
*/
[ScreenName.PostOnboardingDeeplinkHandler]: "post-onboarding",
},
},
[NavigatorName.ReceiveFunds]: {
screens: {
/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React, { useEffect } from "react";
import { NavigatorName, ScreenName } from "~/const";
import { BaseComposite, StackNavigatorProps } from "~/components/RootNavigator/types/helpers";
import { PostOnboardingNavigatorParamList } from "~/components/RootNavigator/types/PostOnboardingNavigator";
import { usePostOnboardingDeeplinkHandler } from "@ledgerhq/live-common/postOnboarding/hooks/index";
import { useNavigateToPostOnboardingHubCallback } from "~/logic/postOnboarding/useNavigateToPostOnboardingHubCallback";
import { Loading } from "~/components/Loading";

type Props = BaseComposite<
StackNavigatorProps<PostOnboardingNavigatorParamList, ScreenName.PostOnboardingDeeplinkHandler>
>;
const PostOnboardingDeeplinkHandler = ({ route, navigation }: Props) => {
const navigateToHome = () => {
navigation.navigate(NavigatorName.Base, {
screen: NavigatorName.Main,
});
};
const navigateToPostOnboardingHub = useNavigateToPostOnboardingHubCallback();
const postOnboardingDeeplinkHandler = usePostOnboardingDeeplinkHandler(
navigateToHome,
navigateToPostOnboardingHub,
);

useEffect(() => {
postOnboardingDeeplinkHandler(route.params?.device);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

return <Loading />;
};

export default PostOnboardingDeeplinkHandler;
2 changes: 2 additions & 0 deletions libs/ledger-live-common/.unimportedrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -2151,6 +2151,7 @@
"lib/postOnboarding/hooks/usePostOnboardingHubState.test.d.ts.map",
"lib/postOnboarding/hooks/usePostOnboardingHubState.test.js",
"lib/postOnboarding/hooks/usePostOnboardingHubState.test.js.map",
"src/postOnboarding/hooks/usePostOnboardingDeeplinkHandler.test.ts",
"lib/postOnboarding/reducer.test.d.ts",
"lib/postOnboarding/reducer.test.d.ts.map",
"lib/postOnboarding/reducer.test.js",
Expand Down Expand Up @@ -2705,6 +2706,7 @@
"src/postOnboarding/hooks/usePostOnboardingEntryPointVisibleOnWallet.test.ts",
"src/postOnboarding/hooks/usePostOnboardingHubState.test.ts",
"src/postOnboarding/hooks/useAutoDismissPostOnboardingEntryPoint.test.ts",
"src/postOnboarding/hooks/usePostOnboardingDeeplinkHandler.test.ts",
"src/postOnboarding/mock.ts",
"src/postOnboarding/reducer.test.ts",
"src/reactNativeSvg.d.ts",
Expand Down
1 change: 1 addition & 0 deletions libs/ledger-live-common/src/postOnboarding/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from "./usePostOnboardingEntryPointVisibleOnWallet";
export * from "./usePostOnboardingHubState";
export * from "./useStartPostOnboardingCallback";
export * from "./useAutoDismissPostOnboardingEntryPoint";
export * from "./usePostOnboardingDeeplinkHandler";
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/**
* @jest-environment jsdom
*/

import { renderHook, act } from "@testing-library/react";
import { usePostOnboardingDeeplinkHandler } from "./usePostOnboardingDeeplinkHandler";

import { PostOnboardingHubState } from "@ledgerhq/types-live";
import { DeviceModelId } from "@ledgerhq/devices";

import {
postOnboardingDeviceModelIdSelector,
walletPostOnboardingEntryPointDismissedSelector,
} from "../reducer";
import { useAllPostOnboardingActionsCompleted } from "./useAllPostOnboardingActionsCompleted";
import { usePostOnboardingHubState } from "./usePostOnboardingHubState";
import { claimTestMock, personalizeTestMock, migrateAssetsTestMock } from "../mock";

const mockedDispatch = jest.fn();
jest.mock("react-redux", () => ({
useSelector: fun => fun(),
useDispatch: () => mockedDispatch,
}));

jest.mock("../reducer");
jest.mock("./useAllPostOnboardingActionsCompleted");
jest.mock("./usePostOnboardingHubState");

const mockedUseAllCompleted = jest.mocked(useAllPostOnboardingActionsCompleted);
const mockedDismissedSelector = jest.mocked(walletPostOnboardingEntryPointDismissedSelector);
const mockedDeviceModelIdSelector = jest.mocked(postOnboardingDeviceModelIdSelector);
const mockedUsePostOnboardingHubState = jest.mocked(usePostOnboardingHubState);

const mockedStateNoActionsCompleted: PostOnboardingHubState = {
deviceModelId: DeviceModelId.stax,
lastActionCompleted: null,
actionsState: [
{ ...claimTestMock, completed: false },
{ ...personalizeTestMock, completed: false },
{ ...migrateAssetsTestMock, completed: false },
],
postOnboardingInProgress: false,
};

describe("usePostOnboardingDeeplinkHandler", () => {
beforeEach(() => {
mockedUseAllCompleted.mockClear();
mockedDismissedSelector.mockClear();
mockedDeviceModelIdSelector.mockClear();
mockedDispatch.mockClear();
mockedUsePostOnboardingHubState.mockClear();
});

test("should navigate to the post onboarding hub when called with a stax device model id", () => {
mockedDismissedSelector.mockReturnValue(false);
mockedUseAllCompleted.mockReturnValue(false);
mockedDeviceModelIdSelector.mockReturnValue(DeviceModelId.stax);
mockedUsePostOnboardingHubState.mockReturnValue(mockedStateNoActionsCompleted);

const navigateToHome = jest.fn();
const navigateToPostOnboardingHub = jest.fn();
const { result } = renderHook(() =>
usePostOnboardingDeeplinkHandler(navigateToHome, navigateToPostOnboardingHub),
);

act(() => {
result.current("stax");
});

expect(navigateToPostOnboardingHub).toHaveBeenCalled();
expect(navigateToHome).not.toHaveBeenCalled();
});

test("should navigate to the home when called with no device parameter", () => {
mockedDismissedSelector.mockReturnValue(false);
mockedUseAllCompleted.mockReturnValue(false);
mockedDeviceModelIdSelector.mockReturnValue(DeviceModelId.nanoSP);
mockedUsePostOnboardingHubState.mockReturnValue(mockedStateNoActionsCompleted);

const navigateToHome = jest.fn();
const navigateToPostOnboardingHub = jest.fn();
const { result } = renderHook(() =>
usePostOnboardingDeeplinkHandler(navigateToHome, navigateToPostOnboardingHub),
);

act(() => {
result.current();
});

expect(navigateToPostOnboardingHub).not.toHaveBeenCalled();
expect(navigateToHome).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { useCallback } from "react";
import { DeviceModelId } from "@ledgerhq/devices";
import { useStartPostOnboardingCallback } from "./useStartPostOnboardingCallback";
import { usePostOnboardingEntryPointVisibleOnWallet } from "./usePostOnboardingEntryPointVisibleOnWallet";
import { usePostOnboardingHubState } from "./usePostOnboardingHubState";

export function usePostOnboardingDeeplinkHandler(navigateToHome, navigateToPostOnboardingHub) {
const handleInitPostOnboarding = useStartPostOnboardingCallback();
const isPostOnboardingVisible = usePostOnboardingEntryPointVisibleOnWallet();
const { deviceModelId: postOnboardingDeviceModelId } = usePostOnboardingHubState();

return useCallback(
(device?: string) => {
if (!device) {
navigateToHome();
return;
}
if (isPostOnboardingVisible && device === postOnboardingDeviceModelId) {
navigateToPostOnboardingHub();
} else if (device in DeviceModelId) {
handleInitPostOnboarding({
deviceModelId: device as DeviceModelId,
mock: false,
fallbackIfNoAction: () => navigateToHome(),
});
} else {
navigateToHome();
}
},
[
handleInitPostOnboarding,
isPostOnboardingVisible,
navigateToHome,
navigateToPostOnboardingHub,
postOnboardingDeviceModelId,
],
);
}

0 comments on commit d9ddfcd

Please sign in to comment.