Skip to content

Commit

Permalink
Merge pull request #5547 from LedgerHQ/feat/LIVE-9823/LLDPostOnboardi…
Browse files Browse the repository at this point in the history
…ngItems

feat(LLD) Post Onboarding New Items
  • Loading branch information
valpinkman authored Nov 28, 2023
2 parents 113d57c + 5d03bf5 commit 57d63f7
Show file tree
Hide file tree
Showing 9 changed files with 151 additions and 50 deletions.
6 changes: 6 additions & 0 deletions .changeset/poor-rats-repeat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@ledgerhq/types-live": patch
"ledger-live-desktop": patch
---

Adding new items to LLD post onboarding
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import React, { useCallback } from "react";
import { Flex, Icons, Tag, Text } from "@ledgerhq/react-ui";
import { useTranslation } from "react-i18next";
// import { useHistory } from "react-router-dom";
import { PostOnboardingActionState, PostOnboardingAction } from "@ledgerhq/types-live";
import { track } from "~/renderer/analytics/segment";
import styled from "styled-components";
import { useDispatch } from "react-redux";
import { openModal } from "~/renderer/actions/modals";
import { AllModalNames } from "~/renderer/modals/types";
import { useHistory } from "react-router";

export type Props = PostOnboardingAction & PostOnboardingActionState;

Expand All @@ -15,14 +18,25 @@ const ActionRowWrapper = styled(Flex)<{ completed: boolean }>`
const PostOnboardingActionRow: React.FC<Props> = props => {
const { id, Icon, title, description, tagLabel, buttonLabelForAnalyticsEvent, completed } = props;
const { t } = useTranslation();
const dispatch = useDispatch();
const history = useHistory();

const handleStartAction = useCallback(() => {
const openModalCallback = (modalName: AllModalNames) => {
dispatch(openModal(modalName, undefined));
};
const navigationCallback = (route: string) => {
history.push({
pathname: route,
});
};

if ("startAction" in props) {
props.startAction();
props.startAction({ openModalCallback, navigationCallback });
buttonLabelForAnalyticsEvent &&
track("button_clicked", { button: buttonLabelForAnalyticsEvent, flow: "post-onboarding" });
}
}, [props, buttonLabelForAnalyticsEvent]);
}, [props, dispatch, history, buttonLabelForAnalyticsEvent]);

return (
<ActionRowWrapper
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,29 @@ const customImage: PostOnboardingAction = {
buttonLabelForAnalyticsEvent: "Set lock screen picture",
};

const assetsTransfer: PostOnboardingAction = {
id: PostOnboardingActionId.assetsTransfer,
featureFlagId: "postOnboardingAssetsTransfer",
Icon: Icons.ArrowDown,
title: "postOnboarding.actions.assetsTransfer.title",
titleCompleted: "postOnboarding.actions.assetsTransfer.titleCompleted",
description: "postOnboarding.actions.assetsTransfer.description",
actionCompletedPopupLabel: "postOnboarding.actions.assetsTransfer.popupLabel",
buttonLabelForAnalyticsEvent: "Secure your assets on Ledger",
startAction: ({ openModalCallback }) => openModalCallback("MODAL_RECEIVE"),
};

const buyCrypto: PostOnboardingAction = {
id: PostOnboardingActionId.buyCrypto,
Icon: Icons.Plus,
title: "postOnboarding.actions.buyCrypto.title",
titleCompleted: "postOnboarding.actions.buyCrypto.titleCompleted",
description: "postOnboarding.actions.buyCrypto.description",
actionCompletedPopupLabel: "postOnboarding.actions.buyCrypto.popupLabel",
buttonLabelForAnalyticsEvent: "Buy Crypto",
startAction: ({ navigationCallback }) => navigationCallback("/exchange"),
};

/**
* All implemented post onboarding actions.
*/
Expand All @@ -64,6 +87,8 @@ const postOnboardingActions: { [id in PostOnboardingActionId]?: PostOnboardingAc
migrateAssetsMock,
personalizeMock,
customImage,
assetsTransfer,
buyCrypto,
};

/**
Expand Down Expand Up @@ -106,7 +131,7 @@ export function getPostOnboardingActionsForDevice(
* Set here the list of actions for the post onboarding of the
* DeviceModelId.stax
* */
return [customImage];
return [customImage, assetsTransfer, buyCrypto];
default:
return [];
}
Expand Down
117 changes: 76 additions & 41 deletions apps/ledger-live-desktop/src/renderer/modals/Receive/index.tsx
Original file line number Diff line number Diff line change
@@ -1,61 +1,96 @@
import React, { PureComponent } from "react";
import React, { useState, useCallback, useEffect } from "react";
import logger from "~/renderer/logger";
import Modal from "~/renderer/components/Modal";
import Body, { StepId } from "./Body";
import { useDispatch, useSelector } from "react-redux";
import { accountsSelector } from "~/renderer/reducers/accounts";
import { openModal, closeModal } from "~/renderer/actions/modals";

type State = {
stepId: StepId;
isAddressVerified: boolean | undefined | null;
verifyAddressError: Error | undefined | null;
};

const INITIAL_STATE = {
stepId: "account" as StepId,
isAddressVerified: null,
verifyAddressError: null,
};
class ReceiveModal extends PureComponent<{}, State> {
state = INITIAL_STATE;
handleReset = () =>
this.setState({
...INITIAL_STATE,
});

handleStepChange = (stepId: StepId) =>
this.setState({
stepId,
});

handleChangeAddressVerified = (isAddressVerified?: boolean | null, err?: Error | null) => {
const ReceiveModal = () => {
const [state, setState] = useState<State>(INITIAL_STATE);

const { stepId, isAddressVerified, verifyAddressError } = state;

const setStepId = (newStepId: State["stepId"]) => {
setState(prevState => ({ ...prevState, stepId: newStepId }));
};

const setIsAddressVerified = (newIsAddressVerified: State["isAddressVerified"]) => {
setState(prevState => ({ ...prevState, isAddressVerified: newIsAddressVerified }));
};

const setVerifyAddressError = (newVerifyAddressError: State["verifyAddressError"]) => {
setState(prevState => ({ ...prevState, verifyAddressError: newVerifyAddressError }));
};

const handleReset = () => {
setStepId(INITIAL_STATE.stepId);
setIsAddressVerified(INITIAL_STATE.isAddressVerified);
setVerifyAddressError(INITIAL_STATE.verifyAddressError);
};

const handleChangeAddressVerified = (isAddressVerified?: boolean | null, err?: Error | null) => {
if (err && err.name !== "UserRefusedAddress") {
logger.critical(err);
}
this.setState({
isAddressVerified,
verifyAddressError: err,
});
setIsAddressVerified(isAddressVerified);
setVerifyAddressError(err);
};

render() {
const { stepId, isAddressVerified, verifyAddressError } = this.state;
const isModalLocked = stepId === "receive" && isAddressVerified === null;
return (
<Modal
name="MODAL_RECEIVE"
centered
onHide={this.handleReset}
preventBackdropClick={isModalLocked}
render={({ data, onClose }) => (
<Body
onClose={onClose}
stepId={stepId}
isAddressVerified={isAddressVerified}
verifyAddressError={verifyAddressError}
onChangeAddressVerified={this.handleChangeAddressVerified}
onChangeStepId={this.handleStepChange}
params={data || {}}
/>
)}
/>
// Making sure at least one account exists, if not, redirecting to the add account modal
const accounts = useSelector(accountsSelector);

const dispatch = useDispatch();
const hasAccounts = !!accounts.length;

const openAddAccounts = useCallback(() => {
dispatch(closeModal("MODAL_RECEIVE"));
dispatch(
openModal("MODAL_ADD_ACCOUNTS", {
currency: null,
}),
);
}
}
}, [dispatch]);

useEffect(() => {
if (!hasAccounts) {
openAddAccounts();
}
}, [hasAccounts, openAddAccounts]);

if (!hasAccounts) return null;

const isModalLocked = stepId === "receive" && isAddressVerified === null;
return (
<Modal
name="MODAL_RECEIVE"
centered
onHide={handleReset}
preventBackdropClick={isModalLocked}
render={({ data, onClose }) => (
<Body
onClose={onClose}
stepId={stepId}
isAddressVerified={isAddressVerified}
verifyAddressError={verifyAddressError}
onChangeAddressVerified={handleChangeAddressVerified}
onChangeStepId={setStepId}
params={data || {}}
/>
)}
/>
);
};

export default ReceiveModal;
1 change: 1 addition & 0 deletions apps/ledger-live-desktop/src/renderer/modals/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,4 @@ export type GlobalModalData = {
* finally, we make a union with the coin modals data and we obtain the complete modal data type.
*/
export type ModalData = GlobalModalData & CoinModalsData;
export type AllModalNames = keyof ModalData;
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ const LiveAppExchange = ({ appId }: { appId: string }) => {
grow
style={{
overflow: "hidden",
height: "100%",
}}
>
{manifest ? (
Expand All @@ -103,8 +104,8 @@ export type ExchangeComponentParams = {
};

const Exchange = ({ match }: RouteComponentProps<ExchangeComponentParams>) => {
const { params } = match;
const appId = match?.params?.appId;

return <LiveAppExchange appId={params.appId || DEFAULT_MULTIBUY_APP_ID} />;
return <LiveAppExchange appId={appId || DEFAULT_MULTIBUY_APP_ID} />;
};
export default Exchange;
12 changes: 12 additions & 0 deletions apps/ledger-live-desktop/static/i18n/en/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -1300,6 +1300,18 @@
"title": "What's next for your Ledger Stax?",
"description": "Claim the Infinity NFT, customize it's lock screen, or\ntransfer your assets from other exchanges.",
"link": "See what's next"
},
"actions": {
"buyCrypto": {
"title": "Buy Crypto",
"description": "Choose from 500+ coins and the best quotes from buy-providers, tailored to your preferences.",
"actionCompletedPopupLabel": "Crypto bought"
},
"assetsTransfer": {
"title": "Transfer your assets to Ledger",
"description": "Easily transfer assets from Coinbase or any other exchange or wallet.",
"actionCompletedPopupLabel": "Assets transferred"
}
}
},
"hideNftCollection": {
Expand Down
7 changes: 6 additions & 1 deletion libs/ledgerjs/packages/types-live/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ Ledger Live main types.
* [PostOnboardingActionId](#postonboardingactionid)
* [navigationParams](#navigationparams)
* [startAction](#startaction)
* [](#)
* [PostOnboardingAction](#postonboardingaction)
* [disabled](#disabled)
* [featureFlagId](#featureflagid)
Expand Down Expand Up @@ -1194,7 +1195,11 @@ Type: [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global

The function to call when the user presses the button for this action

Type: function (): void
Type: function (openModalCallback: any, navigationCallback: any): void

###

Optional Redux dispatch function

### PostOnboardingAction

Expand Down
6 changes: 4 additions & 2 deletions libs/ledgerjs/packages/types-live/src/postOnboarding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,11 @@ type WithStartActionFunction = {
/**
* The function to call when the user presses the button for this action
*/
startAction: () => void;
startAction: (openModalCallback?: any, navigationCallback?: any) => void;
/**
* Optional Redux dispatch function
*/
};

/**
* All necessary information for complete integration of a post onboarding
* action.
Expand Down

3 comments on commit 57d63f7

@vercel
Copy link

@vercel vercel bot commented on 57d63f7 Nov 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Bot] Evm on Staging ($0.00) ⏲ 35.9s

What is the bot and how does it work? Everything is documented here!

6 critical spec errors

Spec Avalanche C-Chain failed!

LedgerAPI5xx: API HTTP 503

Spec Binance Smart Chain failed!

LedgerAPI5xx: API HTTP 503

Spec Ethereum failed!

LedgerAPI5xx: API HTTP 503

Spec Ethereum Classic failed!

LedgerAPI5xx: API HTTP 503

Spec Polygon failed!

LedgerAPI5xx: API HTTP 503

Spec Ethereum Goerli failed!

LedgerAPI5xx: API HTTP 503
Details of the 0 mutations

Spec Avalanche C-Chain (failed)


Spec Binance Smart Chain (failed)


Spec Ethereum (failed)


Spec Ethereum Classic (failed)


Spec Polygon (failed)


Spec Ethereum Goerli (failed)


Details of the 16 uncovered mutations

Spec Avalanche C-Chain (2)

  • move 50%:
  • send max:

Spec Binance Smart Chain (3)

  • move 50%:
  • send max:
  • move some ERC20 like (ERC20, BEP20, etc...):

Spec Ethereum (3)

  • move 50%:
  • send max:
  • move some ERC20 like (ERC20, BEP20, etc...):

Spec Ethereum Classic (2)

  • move 50%:
  • send max:

Spec Polygon (3)

  • move 50%:
  • send max:
  • move some ERC20 like (ERC20, BEP20, etc...):

Spec Ethereum Goerli (3)

  • move 50%:
  • send max:
  • move some ERC20 like (ERC20, BEP20, etc...):
Portfolio ($0.00) – Details of the 6 currencies
Spec (accounts) State Remaining Runs (est) funds?
Avalanche C-Chain (0) 0 ops , 🤷‍♂️ ``
Binance Smart Chain (0) 0 ops , 🤷‍♂️ ``
Ethereum (0) 0 ops , 🤷‍♂️ ``
Ethereum Classic (0) 0 ops , 🤷‍♂️ ``
Polygon (0) 0 ops , 🤷‍♂️ ``
Ethereum Goerli (0) 0 ops , 🤷‍♂️ ``

Performance ⏲ 35.9s

Time spent for each spec: (total across mutations)

Spec (accounts) preload scan re-sync tx status sign op broadcast test destination test
TOTAL 2619ms N/A N/A N/A N/A N/A N/A N/A
Avalanche C-Chain (0) 333ms N/A N/A N/A N/A N/A N/A N/A
Binance Smart Chain (0) 313ms N/A N/A N/A N/A N/A N/A N/A
Ethereum (0) 597ms N/A N/A N/A N/A N/A N/A N/A
Ethereum Classic (0) 903ms N/A N/A N/A N/A N/A N/A N/A
Polygon (0) 431ms N/A N/A N/A N/A N/A N/A N/A
Ethereum Goerli (0) 42ms N/A N/A N/A N/A N/A N/A N/A

What is the bot and how does it work? Everything is documented here!

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Bot] Bitcoin on Staging ($0.00) ⏲ 2min 51s

What is the bot and how does it work? Everything is documented here!

17 critical spec errors

Spec Bitcoin failed!

LedgerAPI5xx: API HTTP 503

Spec Bitcoin Testnet failed!

LedgerAPI5xx: API HTTP 503

Spec Bitcoin Cash failed!

LedgerAPI5xx: API HTTP 503

Spec Bitcoin Gold failed!

LedgerAPI5xx: API HTTP 503

Spec Dash failed!

LedgerAPI5xx: API HTTP 503

Spec Digibyte failed!

LedgerAPI5xx: API HTTP 503

Spec DogeCoin failed!

LedgerAPI5xx: API HTTP 503

Spec Komodo failed!

LedgerAPI5xx: API HTTP 503

Spec Litecoin failed!

LedgerAPI5xx: API HTTP 503

Spec Peercoin failed!

LedgerAPI5xx: API HTTP 503

Spec PivX failed!

LedgerAPI5xx: API HTTP 503

Spec Qtum failed!

LedgerAPI5xx: API HTTP 503

Spec Vertcoin failed!

LedgerAPI5xx: API HTTP 503

Spec Viacoin failed!

LedgerAPI5xx: API HTTP 503

Spec ZCash failed!

LedgerAPI5xx: API HTTP 503

Spec Horizen failed!

LedgerAPI5xx: API HTTP 503

Spec Decred failed!

LedgerAPI5xx: API HTTP 503
Details of the 0 mutations

Spec Bitcoin (failed)


Spec Bitcoin Testnet (failed)


Spec Bitcoin Cash (failed)


Spec Bitcoin Gold (failed)


Spec Dash (failed)


Spec Digibyte (failed)


Spec DogeCoin (failed)


Spec Komodo (failed)


Spec Litecoin (failed)


Spec Peercoin (failed)


Spec PivX (failed)


Spec Qtum (failed)


Spec Vertcoin (failed)


Spec Viacoin (failed)


Spec ZCash (failed)


Spec Horizen (failed)


Spec Decred (failed)


Details of the 85 uncovered mutations

Spec Bitcoin (5)

  • move ~50%:
  • optimize-size:
  • send 1 utxo:
  • send OP_RETURN transaction:
  • send max:

Spec Bitcoin Testnet (5)

  • move ~50%:
  • optimize-size:
  • send 1 utxo:
  • send OP_RETURN transaction:
  • send max:

Spec Bitcoin Cash (5)

  • move ~50%:
  • optimize-size:
  • send 1 utxo:
  • send OP_RETURN transaction:
  • send max:

Spec Bitcoin Gold (5)

  • move ~50%:
  • optimize-size:
  • send 1 utxo:
  • send OP_RETURN transaction:
  • send max:

Spec Dash (5)

  • move ~50%:
  • optimize-size:
  • send 1 utxo:
  • send OP_RETURN transaction:
  • send max:

Spec Digibyte (5)

  • move ~50%:
  • optimize-size:
  • send 1 utxo:
  • send OP_RETURN transaction:
  • send max:

Spec DogeCoin (5)

  • move ~50%:
  • optimize-size:
  • send 1 utxo:
  • send OP_RETURN transaction:
  • send max:

Spec Komodo (5)

  • move ~50%:
  • optimize-size:
  • send 1 utxo:
  • send OP_RETURN transaction:
  • send max:

Spec Litecoin (5)

  • move ~50%:
  • optimize-size:
  • send 1 utxo:
  • send OP_RETURN transaction:
  • send max:

Spec Peercoin (5)

  • move ~50%:
  • optimize-size:
  • send 1 utxo:
  • send OP_RETURN transaction:
  • send max:

Spec PivX (5)

  • move ~50%:
  • optimize-size:
  • send 1 utxo:
  • send OP_RETURN transaction:
  • send max:

Spec Qtum (5)

  • move ~50%:
  • optimize-size:
  • send 1 utxo:
  • send OP_RETURN transaction:
  • send max:

Spec Vertcoin (5)

  • move ~50%:
  • optimize-size:
  • send 1 utxo:
  • send OP_RETURN transaction:
  • send max:

Spec Viacoin (5)

  • move ~50%:
  • optimize-size:
  • send 1 utxo:
  • send OP_RETURN transaction:
  • send max:

Spec ZCash (5)

  • move ~50%:
  • optimize-size:
  • send 1 utxo:
  • send OP_RETURN transaction:
  • send max:

Spec Horizen (5)

  • move ~50%:
  • optimize-size:
  • send 1 utxo:
  • send OP_RETURN transaction:
  • send max:

Spec Decred (5)

  • move ~50%:
  • optimize-size:
  • send 1 utxo:
  • send OP_RETURN transaction:
  • send max:
Portfolio ($0.00) – Details of the 17 currencies
Spec (accounts) State Remaining Runs (est) funds?
Bitcoin (0) 0 ops , 🤷‍♂️ ``
Bitcoin Testnet (0) 0 ops , 🤷‍♂️ ``
Bitcoin Cash (0) 0 ops , 🤷‍♂️ ``
Bitcoin Gold (0) 0 ops , 🤷‍♂️ ``
Dash (0) 0 ops , 🤷‍♂️ ``
Digibyte (0) 0 ops , 🤷‍♂️ ``
DogeCoin (0) 0 ops , 🤷‍♂️ ``
Komodo (0) 0 ops , 🤷‍♂️ ``
Litecoin (0) 0 ops , 🤷‍♂️ ``
Peercoin (0) 0 ops , 🤷‍♂️ ``
PivX (0) 0 ops , 🤷‍♂️ ``
Qtum (0) 0 ops , 🤷‍♂️ ``
Vertcoin (0) 0 ops , 🤷‍♂️ ``
Viacoin (0) 0 ops , 🤷‍♂️ ``
ZCash (0) 0 ops , 🤷‍♂️ ``
Horizen (0) 0 ops , 🤷‍♂️ ``
Decred (0) 0 ops , 🤷‍♂️ ``

Performance ⏲ 2min 51s

Time spent for each spec: (total across mutations)

Spec (accounts) preload scan re-sync tx status sign op broadcast test destination test
TOTAL 10ms N/A N/A N/A N/A N/A N/A N/A
Bitcoin (0) 0.22ms N/A N/A N/A N/A N/A N/A N/A
Bitcoin Testnet (0) 0.21ms N/A N/A N/A N/A N/A N/A N/A
Bitcoin Cash (0) 0.23ms N/A N/A N/A N/A N/A N/A N/A
Bitcoin Gold (0) 0.19ms N/A N/A N/A N/A N/A N/A N/A
Dash (0) 2.36ms N/A N/A N/A N/A N/A N/A N/A
Digibyte (0) 0.63ms N/A N/A N/A N/A N/A N/A N/A
DogeCoin (0) 0.17ms N/A N/A N/A N/A N/A N/A N/A
Komodo (0) 0.24ms N/A N/A N/A N/A N/A N/A N/A
Litecoin (0) 0.83ms N/A N/A N/A N/A N/A N/A N/A
Peercoin (0) 0.18ms N/A N/A N/A N/A N/A N/A N/A
PivX (0) 0.52ms N/A N/A N/A N/A N/A N/A N/A
Qtum (0) 1.53ms N/A N/A N/A N/A N/A N/A N/A
Vertcoin (0) 1.52ms N/A N/A N/A N/A N/A N/A N/A
Viacoin (0) 0.26ms N/A N/A N/A N/A N/A N/A N/A
ZCash (0) 0.25ms N/A N/A N/A N/A N/A N/A N/A
Horizen (0) 0.89ms N/A N/A N/A N/A N/A N/A N/A
Decred (0) 0.24ms N/A N/A N/A N/A N/A N/A N/A

What is the bot and how does it work? Everything is documented here!

Please sign in to comment.