From 76935209f9f0ae7f7bbe7d1cc1453710b77f7043 Mon Sep 17 00:00:00 2001 From: JoeGruff Date: Tue, 16 May 2023 17:31:34 +0900 Subject: [PATCH] multi: Add ledger ui. --- .../CreateLedgerWalletForm.jsx | 79 +++++++++++++++++++ .../CreateLedgerWalletForm.module.css | 59 ++++++++++++++ .../CreateLedgerWalletForm/index.js | 1 + .../LedgerLoaderBarContainer.jsx | 33 ++++++++ .../LedgerLoaderBarContainer.module.css | 63 +++++++++++++++ .../LedgerLoaderBarContainer/hooks.js | 10 +++ .../LedgerLoaderBarContainer/index.js | 1 + .../PreCreateWallet/PreCreateWallet.jsx | 54 +++++++++++-- .../GetStartedPage/PreCreateWallet/hooks.js | 42 +++++++++- app/components/views/GetStartedPage/hooks.js | 29 ++++--- .../GetStartedPage/messages/messages.jsx | 9 +++ .../LedgerPage/NoDevicePage/NoDevicePage.jsx | 21 +++++ .../NoDevicePage/NoDevicePage.module.css | 29 +++++++ .../views/LedgerPage/NoDevicePage/index.js | 1 + app/hooks/useDaemonStartup.js | 20 +++++ app/hooks/useLedger.js | 34 ++++++++ app/reducers/ledger.js | 71 +++++++++++++++++ app/selectors.js | 12 ++- 18 files changed, 549 insertions(+), 19 deletions(-) create mode 100644 app/components/views/GetStartedPage/PreCreateWallet/CreateLedgerWalletForm/CreateLedgerWalletForm.jsx create mode 100644 app/components/views/GetStartedPage/PreCreateWallet/CreateLedgerWalletForm/CreateLedgerWalletForm.module.css create mode 100644 app/components/views/GetStartedPage/PreCreateWallet/CreateLedgerWalletForm/index.js create mode 100644 app/components/views/GetStartedPage/PreCreateWallet/LedgerLoaderBarContainer/LedgerLoaderBarContainer.jsx create mode 100644 app/components/views/GetStartedPage/PreCreateWallet/LedgerLoaderBarContainer/LedgerLoaderBarContainer.module.css create mode 100644 app/components/views/GetStartedPage/PreCreateWallet/LedgerLoaderBarContainer/hooks.js create mode 100644 app/components/views/GetStartedPage/PreCreateWallet/LedgerLoaderBarContainer/index.js create mode 100644 app/components/views/LedgerPage/NoDevicePage/NoDevicePage.jsx create mode 100644 app/components/views/LedgerPage/NoDevicePage/NoDevicePage.module.css create mode 100644 app/components/views/LedgerPage/NoDevicePage/index.js create mode 100644 app/hooks/useLedger.js create mode 100644 app/reducers/ledger.js diff --git a/app/components/views/GetStartedPage/PreCreateWallet/CreateLedgerWalletForm/CreateLedgerWalletForm.jsx b/app/components/views/GetStartedPage/PreCreateWallet/CreateLedgerWalletForm/CreateLedgerWalletForm.jsx new file mode 100644 index 0000000000..79929b4798 --- /dev/null +++ b/app/components/views/GetStartedPage/PreCreateWallet/CreateLedgerWalletForm/CreateLedgerWalletForm.jsx @@ -0,0 +1,79 @@ +import { FormattedMessage as T, defineMessages } from "react-intl"; +import { TextInput } from "inputs"; +import { KeyBlueButton, InvisibleButton } from "buttons"; +import styles from "./CreateLedgerWalletForm.module.css"; +import { Documentation } from "shared"; + +const messages = defineMessages({ + walletNameInputPlaceholder: { + id: "createLedgerWallet.walletNameInput.placeholder", + defaultMessage: "Choose a name for your Ledger Wallet" + }, + walletNameInputLabel: { + id: "createLedgerWallet.walletNameInput.label", + defaultMessage: "Wallet Name" + }, + messageWalletDupeNameError: { + id: "createLedgerWallet.dupeWalletName.error", + defaultMessage: "Please choose an unused wallet name" + } +}); + +const CreateLedgerWalletForm = ({ + createWallet, + hideCreateWalletForm, + newWalletName, + walletNameError, + onChangeCreateWalletName, + hasFailedAttemptName, + intl +}) => { + return ( +
+
+ +
+
+ + + +
+ +
+ onChangeCreateWalletName(e.target.value)} + label={intl.formatMessage(messages.walletNameInputLabel)} + placeholder={intl.formatMessage(messages.walletNameInputPlaceholder)} + showErrors={hasFailedAttemptName} + className={styles.walletNameInput} + /> + +
+ + + + + + +
+
+
+ ); +}; + +export default CreateLedgerWalletForm; diff --git a/app/components/views/GetStartedPage/PreCreateWallet/CreateLedgerWalletForm/CreateLedgerWalletForm.module.css b/app/components/views/GetStartedPage/PreCreateWallet/CreateLedgerWalletForm/CreateLedgerWalletForm.module.css new file mode 100644 index 0000000000..10040539b1 --- /dev/null +++ b/app/components/views/GetStartedPage/PreCreateWallet/CreateLedgerWalletForm/CreateLedgerWalletForm.module.css @@ -0,0 +1,59 @@ +.container { + width: 764px; +} + +.grid { + display: grid; + grid-template-columns: repeat(2, 30rem); + grid-column-gap: 76px; + font-size: 13px; + line-height: 17px; + color: var(--grey-7); + margin: 38px 0 32px 0; +} + +.title { + font-size: 27px; + line-height: 34px; + color: var(--grey-7); + width: 32rem; +} + +.textToggle { + font-size: 13px; + line-height: 16px; + height: 29px; + grid-column: 1; + margin-top: 20px; +} + +.textToggleChild { + padding: 6px 31px; +} + +.walletNameInput, +.walletNameInput input { + width: 279px !important; +} + +.buttonContrainer { + width: 279px; + display: flex; + justify-content: flex-end; + margin-top: 20px; +} + +.cancelBt, +.createWalletBt { + height: 44px; +} + +@media screen and (max-width: 768px) { + .container { + width: 355px; + } + + .grid { + grid-template-columns: 30rem; + } +} diff --git a/app/components/views/GetStartedPage/PreCreateWallet/CreateLedgerWalletForm/index.js b/app/components/views/GetStartedPage/PreCreateWallet/CreateLedgerWalletForm/index.js new file mode 100644 index 0000000000..b2674514b5 --- /dev/null +++ b/app/components/views/GetStartedPage/PreCreateWallet/CreateLedgerWalletForm/index.js @@ -0,0 +1 @@ +export { default } from "./CreateLedgerWalletForm"; diff --git a/app/components/views/GetStartedPage/PreCreateWallet/LedgerLoaderBarContainer/LedgerLoaderBarContainer.jsx b/app/components/views/GetStartedPage/PreCreateWallet/LedgerLoaderBarContainer/LedgerLoaderBarContainer.jsx new file mode 100644 index 0000000000..b13aa11451 --- /dev/null +++ b/app/components/views/GetStartedPage/PreCreateWallet/LedgerLoaderBarContainer/LedgerLoaderBarContainer.jsx @@ -0,0 +1,33 @@ +import { FormattedMessage as T } from "react-intl"; +import styles from "./LedgerLoaderBarContainer.module.css"; +import { useLedgerLoaderBarContainer } from "./hooks"; + +const LedgerLoaderBarContainer = ({ loaderBar }) => { + const { ledgerDevice, deviceLabel } = useLedgerLoaderBarContainer(); + return ledgerDevice ? ( +
+
+ + + ) + }} + /> + + | + + + +
+
{loaderBar}
+
+ ) : null; +}; + +export default LedgerLoaderBarContainer; diff --git a/app/components/views/GetStartedPage/PreCreateWallet/LedgerLoaderBarContainer/LedgerLoaderBarContainer.module.css b/app/components/views/GetStartedPage/PreCreateWallet/LedgerLoaderBarContainer/LedgerLoaderBarContainer.module.css new file mode 100644 index 0000000000..db06fe3a11 --- /dev/null +++ b/app/components/views/GetStartedPage/PreCreateWallet/LedgerLoaderBarContainer/LedgerLoaderBarContainer.module.css @@ -0,0 +1,63 @@ +.loaderBarContainer { + position: fixed; + bottom: 0; + left: 0; + right: 0; + box-shadow: 0px -4px 8px rgba(0, 0, 0, 0.1); + display: grid; + grid-template-columns: 1fr 1fr; + align-items: center; + background-size: 14px; + background-repeat: no-repeat; + background-position: 25px center; + background-image: var(--tz-create-bt); +} + +.loaderBar div { + border-radius: 0; +} + +.loaderBar, +.loaderBar > div { + height: 60px; + background: linear-gradient( + 270deg, + var(--display-wallet-gradient-selected-right), + var(--input-color) + ); + color: var(--white-button-text); + padding-left: 1rem; + font-size: 1.1rem; +} + +.loaderBar > div > div:nth-of-type(1) { + grid-column: 2; + grid-row: 1; + align-items: center; + display: flex; + padding-left: 1rem; +} +.loaderBar > div > div:nth-of-type(2n) { + grid-column: 1; + grid-row: 1; +} + +.loaderBar > div > div:nth-of-type(2n) > div { + background-size: 2rem; + background-position: center; +} + +.deviceStatus { + font-size: 13px; + line-height: 16px; + padding-left: 55px; +} + +.deviceLabel { + font-weight: 600; + color: var(--main-dark-blue); +} + +.connected { + color: var(--accent-blue); +} diff --git a/app/components/views/GetStartedPage/PreCreateWallet/LedgerLoaderBarContainer/hooks.js b/app/components/views/GetStartedPage/PreCreateWallet/LedgerLoaderBarContainer/hooks.js new file mode 100644 index 0000000000..be2089fb69 --- /dev/null +++ b/app/components/views/GetStartedPage/PreCreateWallet/LedgerLoaderBarContainer/hooks.js @@ -0,0 +1,10 @@ +import { useSelector } from "react-redux"; +import { useDaemonStartup } from "hooks"; +import * as sel from "selectors"; + +export const useLedgerLoaderBarContainer = () => { + const { ledgerDevice } = useDaemonStartup(); + const deviceLabel = useSelector(sel.ledgerLabel); + + return { ledgerDevice, deviceLabel }; +}; diff --git a/app/components/views/GetStartedPage/PreCreateWallet/LedgerLoaderBarContainer/index.js b/app/components/views/GetStartedPage/PreCreateWallet/LedgerLoaderBarContainer/index.js new file mode 100644 index 0000000000..0f8e9e04c6 --- /dev/null +++ b/app/components/views/GetStartedPage/PreCreateWallet/LedgerLoaderBarContainer/index.js @@ -0,0 +1 @@ +export { default } from "./LedgerLoaderBarContainer"; diff --git a/app/components/views/GetStartedPage/PreCreateWallet/PreCreateWallet.jsx b/app/components/views/GetStartedPage/PreCreateWallet/PreCreateWallet.jsx index 545a208529..3e5de52790 100644 --- a/app/components/views/GetStartedPage/PreCreateWallet/PreCreateWallet.jsx +++ b/app/components/views/GetStartedPage/PreCreateWallet/PreCreateWallet.jsx @@ -1,9 +1,11 @@ import { FormattedMessage as T } from "react-intl"; import CreateWalletForm from "./CreateWalletForm"; import CreateTrezorWalletForm from "./CreateTrezorWalletForm"; +import CreateLedgerWalletForm from "./CreateLedgerWalletForm"; import { usePreCreateWallet } from "./hooks"; import { injectIntl } from "react-intl"; import NoDevicePage from "views/TrezorPage/NoDevicePage"; +import { NoDevicePage as NoDevicePageLedger } from "views/LedgerPage/NoDevicePage"; import styles from "./PreCreateWallet.module.css"; import { InvisibleButton } from "buttons"; @@ -15,7 +17,8 @@ const PreCreateWallet = ({ isCreateNewWallet, onShowCreateWallet, isTrezor, - creatingWallet + creatingWallet, + isLedger }) => { const { availableWallets, @@ -38,14 +41,16 @@ const PreCreateWallet = ({ toggleDisableCoinTypeUpgrades, gapLimit, setGapLimit, - connectTrezor + connectTrezor, + connectLedger, } = usePreCreateWallet({ onSendContinue, onSendBack, onSendError, isCreateNewWallet, onShowCreateWallet, - isTrezor + isTrezor, + isLedger }); return isTrezor && !trezorDevice ? ( @@ -81,7 +86,45 @@ const PreCreateWallet = ({ disableCoinTypeUpgrades, toggleDisableCoinTypeUpgrades, gapLimit, - setGapLimit + setGapLimit, + isLedger + }} + /> + ) : isLedger && !ledgerDevice ? ( +
+ + + + +
+ ) : isLedger ? ( + ) : ( @@ -110,7 +153,8 @@ const PreCreateWallet = ({ disableCoinTypeUpgrades, toggleDisableCoinTypeUpgrades, gapLimit, - setGapLimit + setGapLimit, + isLedger }} /> ); diff --git a/app/components/views/GetStartedPage/PreCreateWallet/hooks.js b/app/components/views/GetStartedPage/PreCreateWallet/hooks.js index 41824abf41..17eb074a88 100644 --- a/app/components/views/GetStartedPage/PreCreateWallet/hooks.js +++ b/app/components/views/GetStartedPage/PreCreateWallet/hooks.js @@ -2,6 +2,7 @@ import { useState, useCallback, useEffect } from "react"; import { useDispatch } from "react-redux"; import { useDaemonStartup } from "hooks"; import * as trza from "actions/TrezorActions"; +import * as ldgr from "actions/LedgerActions"; export const usePreCreateWallet = ({ onSendContinue, @@ -9,7 +10,8 @@ export const usePreCreateWallet = ({ onSendError, isCreateNewWallet, onShowCreateWallet, - isTrezor + isTrezor, + isLedger }) => { const { isSPV, @@ -22,7 +24,12 @@ export const usePreCreateWallet = ({ trezorGetWalletCreationMasterPubKey, onCreateWallet, trezorEnable, - validateMasterPubKey + validateMasterPubKey, + ledgerDevice, + ledgerEnable, + ledgerDisable, + ledgerAlertNoConnectedDevice, + ledgerGetWalletCreationMasterPubKey } = useDaemonStartup(); const [newWalletName, setNewWalletName] = useState(""); const [isWatchingOnly, setIsWatchingOnly] = useState(false); @@ -38,8 +45,11 @@ export const usePreCreateWallet = ({ if (isTrezor) { trezorDisable(); } + if (isLedger) { + ledgerDisable(); + } onSendBack(); - }, [isTrezor, trezorDisable, onSendBack]); + }, [isTrezor, trezorDisable, onSendBack, isLedger, ledgerDisable]); const onChangeCreateWalletName = useCallback( (newWalletName) => { @@ -83,7 +93,12 @@ export const usePreCreateWallet = ({ } else { trezorDisable(); } - }, [isTrezor, trezorEnable, trezorDisable]); + if (isLedger) { + ledgerEnable(); + } else { + ledgerDisable(); + } + }, [isTrezor, trezorEnable, trezorDisable, isLedger, ledgerEnable, ledgerDisable]); const toggleDisableCoinTypeUpgrades = () => setDisableCoinTypeUpgrades((value) => !value); @@ -115,6 +130,10 @@ export const usePreCreateWallet = ({ trezorAlertNoConnectedDevice(); return; } + if (isLedger && !ledgerDevice) { + ledgerAlertNoConnectedDevice(); + return; + } // onSendContinue action so getStartedStateMachine can go to // creatingWallet state. onSendContinue(); @@ -130,6 +149,18 @@ export const usePreCreateWallet = ({ ) ); } + if (isLedger) { + walletSelected.isWatchingOnly = true; + return ledgerGetWalletCreationMasterPubKey().then((walletMasterPubKey) => + onCreateWallet(walletSelected).then(() => + onShowCreateWallet({ + isNew, + walletMasterPubKey, + isLedger: true + }) + ) + ); + } return onCreateWallet(walletSelected) .then(() => onShowCreateWallet({ isNew, walletMasterPubKey })) @@ -147,6 +178,9 @@ export const usePreCreateWallet = ({ trezorAlertNoConnectedDevice, trezorDevice, trezorGetWalletCreationMasterPubKey, + ledgerAlertNoConnectedDevice, + ledgerDevice, + ledgerGetWalletCreationMasterPubKey, walletMasterPubKey, walletNameError, gapLimit, diff --git a/app/components/views/GetStartedPage/hooks.js b/app/components/views/GetStartedPage/hooks.js index 6d6f6bfbc5..0dbd355492 100644 --- a/app/components/views/GetStartedPage/hooks.js +++ b/app/components/views/GetStartedPage/hooks.js @@ -26,6 +26,7 @@ import styles from "./GetStarted.module.css"; import { isObject } from "lodash"; import { wallet } from "wallet-preload-shim"; import TrezorLoaderBarContainer from "views/GetStartedPage/PreCreateWallet/TrezorLoaderBarContainer"; +import LedgerLoaderBarContainer from "views/GetStartedPage/PreCreateWallet/LedgerLoaderBarContainer"; import { LoaderBarContainer } from "./helpers"; export const useGetStarted = () => { @@ -164,7 +165,7 @@ export const useGetStarted = () => { isAtStartWallet: (context) => { const { selectedWallet } = context; const { passPhrase } = context; - const { isWatchingOnly, isTrezor } = selectedWallet.value; + const { isWatchingOnly, isTrezor, isLedger } = selectedWallet.value; const hasPassPhrase = !!passPhrase; onStartWallet(selectedWallet, hasPassPhrase) .then((discoverAccountsComplete) => { @@ -174,7 +175,8 @@ export const useGetStarted = () => { !discoverAccountsComplete && !passPhrase && !isWatchingOnly && - !isTrezor + !isTrezor && + !isLedger ) { // Need to discover accounts and the passphrase isn't stored in // context, so ask for the private passphrase before continuing. @@ -373,7 +375,7 @@ export const useGetStarted = () => { ); const onSendCreateWallet = useCallback( - (isNew, isTrezor) => send({ type: "CREATE_WALLET", isNew, isTrezor }), + (isNew, isTrezor, isLedger) => send({ type: "CREATE_WALLET", isNew, isTrezor, isLedger }), [send] ); @@ -409,12 +411,13 @@ export const useGetStarted = () => { ); const onShowCreateWallet = useCallback( - ({ isNew, walletMasterPubKey, isTrezor }) => + ({ isNew, walletMasterPubKey, isTrezor, isLedger }) => send({ type: "SHOW_CREATE_WALLET", isNew, walletMasterPubKey, - isTrezor + isTrezor, + isLedger }), [send] ); @@ -489,7 +492,8 @@ export const useGetStarted = () => { isTrezor, isSPV, createWalletRef, - settingUpWalletRef + settingUpWalletRef, + isLedger } = state.context; let component, text, animationType, PageComponent; @@ -560,9 +564,15 @@ export const useGetStarted = () => { m="Create a trezor wallet..." /> ); - hideHeader = isTrezor; - showLoaderBar = isTrezor; - loaderBarContainer = isTrezor ? TrezorLoaderBarContainer : null; + text = isLedger && ( + + ); + hideHeader = isTrezor || isLedger; + showLoaderBar = isTrezor || isLedger; + loaderBarContainer = isTrezor ? TrezorLoaderBarContainer : isLedger ? TrezorLoaderBarContainer : null; component = h(PreCreateWalletForm, { onShowCreateWallet, onSendContinue, @@ -570,6 +580,7 @@ export const useGetStarted = () => { onSendError, isCreateNewWallet, isTrezor, + isLedger, error }); break; diff --git a/app/components/views/GetStartedPage/messages/messages.jsx b/app/components/views/GetStartedPage/messages/messages.jsx index df9a250106..1fe4f2a005 100644 --- a/app/components/views/GetStartedPage/messages/messages.jsx +++ b/app/components/views/GetStartedPage/messages/messages.jsx @@ -81,6 +81,10 @@ export const messages = defineMessages({ id: "getStarted.trezor", defaultMessage: "Setup a Trezor Wallet" }, + ledgerTabMsg: { + id: "getStarted.ledger", + defaultMessage: "Setup a Ledger Wallet" + }, closeEditWallets: { id: "getStarted.closeEditWallets", defaultMessage: "Close" @@ -108,6 +112,11 @@ export const messages = defineMessages({ defaultMessage: "Trezor is a hardware wallet. For more information, visit {link}" }, + messageWalletLedgerDescription: { + id: "createwallet.ledger.description", + defaultMessage: + "Ledger is a hardware wallet. For more information, visit {link}" + }, messageWalletMasterPubKey: { id: "createwallet.walletpubkey.placeholder", defaultMessage: "Master Pub Key" diff --git a/app/components/views/LedgerPage/NoDevicePage/NoDevicePage.jsx b/app/components/views/LedgerPage/NoDevicePage/NoDevicePage.jsx new file mode 100644 index 0000000000..cb02b5c3c0 --- /dev/null +++ b/app/components/views/LedgerPage/NoDevicePage/NoDevicePage.jsx @@ -0,0 +1,21 @@ +import styles from "./NoDevicePage.module.css"; +import { FormattedMessage as T } from "react-intl"; +import { KeyBlueButton } from "buttons"; + +const NoDevicePage = ({ onConnect }) => { + return ( +
+
+ +
+ + + +
+ ); +}; + +export default NoDevicePage; diff --git a/app/components/views/LedgerPage/NoDevicePage/NoDevicePage.module.css b/app/components/views/LedgerPage/NoDevicePage/NoDevicePage.module.css new file mode 100644 index 0000000000..ad8d45215e --- /dev/null +++ b/app/components/views/LedgerPage/NoDevicePage/NoDevicePage.module.css @@ -0,0 +1,29 @@ +.container { + display: flex; + flex-direction: column; + align-items: center; +} + +.desc { + font-size: 27px; + line-height: 34px; + color: var(--grey-7); + width: 522px; + text-align: center; + margin-bottom: 40px; +} + +.button { + height: 29px; + font-size: 13px; + line-height: 16.34px; + padding: 6px 10px; +} + +@media screen and (max-width: 768px) { + .desc { + width: 80%; + font-size: 22px; + line-height: 23px; + } +} diff --git a/app/components/views/LedgerPage/NoDevicePage/index.js b/app/components/views/LedgerPage/NoDevicePage/index.js new file mode 100644 index 0000000000..ef97d62181 --- /dev/null +++ b/app/components/views/LedgerPage/NoDevicePage/index.js @@ -0,0 +1 @@ +export { default } from "./NoDevicePage"; diff --git a/app/hooks/useDaemonStartup.js b/app/hooks/useDaemonStartup.js index 676aea1f0f..7b5da04b2f 100644 --- a/app/hooks/useDaemonStartup.js +++ b/app/hooks/useDaemonStartup.js @@ -6,6 +6,7 @@ import * as da from "actions/DaemonActions"; import * as ca from "actions/ClientActions"; import * as ctrla from "actions/ControlActions"; import * as trza from "actions/TrezorActions"; +import * as ldgr from "actions/LedgerActions"; import * as ama from "actions/AccountMixerActions"; import * as va from "actions/VSPActions"; @@ -38,7 +39,9 @@ const useDaemonStartup = () => { const getDaemonStarted = useSelector(sel.getDaemonStarted); const getEstimatedTimeLeft = useSelector(sel.getEstimatedTimeLeft); const trezorDevice = useSelector(sel.trezorDevice); + const ledgerDevice = useSelector(sel.ledgerDevice); const isTrezor = useSelector(sel.isTrezor); + const isLedger = useSelector(sel.isLedger); const syncAttemptRequest = useSelector(sel.getSyncAttemptRequest); const daemonWarning = useSelector(sel.daemonWarning); const isSettingAccountsPassphrase = useSelector( @@ -230,6 +233,22 @@ const useDaemonStartup = () => { () => dispatch(trza.getWalletCreationMasterPubKey()), [dispatch] ); + const ledgerEnable = useCallback( + () => dispatch(ldgr.enableLedger()), + [dispatch] + ); + const ledgerDisable = useCallback( + () => dispatch(ldgr.disableLedger()), + [dispatch] + ); + const ledgerAlertNoConnectedDevice = useCallback( + () => dispatch(ldgr.alertNoConnectedDevice()), + [dispatch] + ); + const ledgerGetWalletCreationMasterPubKey = useCallback( + () => dispatch(ldgr.getWalletCreationMasterPubKey()), + [dispatch] + ); const validateMasterPubKey = useCallback( (masterPubKey) => dispatch(ctrla.validateMasterPubKey(masterPubKey)), [dispatch] @@ -325,6 +344,7 @@ const useDaemonStartup = () => { getEstimatedTimeLeft, trezorDevice, isTrezor, + isLedger, peerCount, synced, syncFetchMissingCfiltersAttempt, diff --git a/app/hooks/useLedger.js b/app/hooks/useLedger.js new file mode 100644 index 0000000000..0f8a2053a6 --- /dev/null +++ b/app/hooks/useLedger.js @@ -0,0 +1,34 @@ +import { useSelector, useDispatch } from "react-redux"; +import { useCallback } from "react"; +import * as sel from "selectors"; +import * as ldgr from "actions/LedgerActions"; + +const useLedger = () => { + const isLedger = useSelector(sel.isLedger); + const performingOperation = useSelector(sel.ledgerPerformingOperation); + const isGetStarted = useSelector(sel.isGetStarted); + const device = useSelector(sel.ledgerDevice); + const walletCreationMasterPubkeyAttempt = useSelector( + sel.ledgerWalletCreationMasterPubkeyAttempt + ); + + const dispatch = useDispatch(); + + const onConnect = useCallback(() => dispatch(ldgr.connect()), [dispatch]); + const onEnableLedger = useCallback( + () => dispatch(ldgr.enableLedger()), + [dispatch] + ); + + return { + isLedger, + performingOperation, + isGetStarted, + device, + walletCreationMasterPubkeyAttempt, + onConnect, + onEnableLedger + }; +}; + +export default useLedger; diff --git a/app/reducers/ledger.js b/app/reducers/ledger.js new file mode 100644 index 0000000000..2f36a20722 --- /dev/null +++ b/app/reducers/ledger.js @@ -0,0 +1,71 @@ +import { + LDG_WALLET_CLOSED, + LDG_LEDGER_ENABLED, + LDG_LEDGER_DISABLED, + LDG_CONNECT_ATTEMPT, + LDG_CONNECT_FAILED, + LDG_CONNECT_SUCCESS, + LDG_NOCONNECTEDDEVICE, + LDG_LOADDEVICE, + LDG_GETWALLETCREATIONMASTERPUBKEY_ATTEMPT, + LDG_GETWALLETCREATIONMASTERPUBKEY_FAILED, + LDG_GETWALLETCREATIONMASTERPUBKEY_SUCCESS +} from "actions/TrezorActions"; +import { + SIGNTX_ATTEMPT, + SIGNTX_FAILED, + SIGNTX_SUCCESS +} from "actions/ControlActions"; +import { CLOSEWALLET_SUCCESS } from "actions/WalletLoaderActions"; + +export default function trezor(state = {}, action) { + switch (action.type) { + case LDG_LEDGER_ENABLED: + return { ...state, enabled: true }; + case LDG_LEDGER_DISABLED: + return { ...state, enabled: false }; + case LDG_CONNECT_ATTEMPT: + return { + ...state, + connectAttempt: true + }; + case LDG_CONNECT_SUCCESS: + return { + ...state, + // Ledger does not keep a constant connection. Device is set to true on + // the first successful attempt and left true until the wallet is closed. + device: true, + connectAttempt: false + }; + case LDG_CONNECT_FAILED: + return { + ...state, + connectError: action.error, + connectAttempt: false + }; + case LDG_NOCONNECTEDDEVICE: + // We don't currently listen for reconnect so not deleting the device. + return { + ...state + }; + case LDG_WALLET_CLOSED: + return { + ...state, + device: null, + performingOperation: false, + connected: false + }; + case LDG_GETWALLETCREATIONMASTERPUBKEY_ATTEMPT: + return { ...state, walletCreationMasterPubkeyAttempt: true }; + case LDG_GETWALLETCREATIONMASTERPUBKEY_SUCCESS: + case LDG_GETWALLETCREATIONMASTERPUBKEY_FAILED: + return { ...state, walletCreationMasterPubkeyAttempt: false }; + case SIGNTX_ATTEMPT: + case SIGNTX_FAILED: + case SIGNTX_SUCCESS: + case CLOSEWALLET_SUCCESS: + return { ...state, enabled: false }; + default: + return state; + } +} diff --git a/app/selectors.js b/app/selectors.js index 8a287b5591..ea4d6c3f1b 100644 --- a/app/selectors.js +++ b/app/selectors.js @@ -1104,6 +1104,8 @@ export const confirmationDialogModalVisible = bool( export const isTrezor = get(["trezor", "enabled"]); export const isPerformingTrezorUpdate = get(["trezor", "performingUpdate"]); +export const isLedger = get(["ledger", "enabled"]); + export const isSignMessageDisabled = and(isWatchingOnly, not(isTrezor)); export const isChangePassPhraseDisabled = isWatchingOnly; export const isTransactionsSendTabDisabled = not(isTrezor); @@ -1313,6 +1315,14 @@ export const trezorWalletCreationMasterPubkeyAttempt = get([ "walletCreationMasterPubkeyAttempt" ]); +export const ledgerPerformingOperation = get(["ledger", "performingOperation"]); +export const ledgerDevice = get(["ledger", "device"]); +export const ledgerLabel = get(["ledger", "deviceLabel"]); +export const ledgerWalletCreationMasterPubkeyAttempt = get([ + "ledger", + "walletCreationMasterPubkeyAttempt" +]); + // selectors for checking if decrediton can be closed. // getRunningIndicator is a indicator for indicate something is runnning on // decrediton, like the ticket auto buyer or the mixer. @@ -1329,7 +1339,7 @@ export const loggedInDex = bool(get(["dex", "loggedIn"])); // ln selectors -export const lnEnabled = bool(and(not(isWatchingOnly), not(isTrezor))); +export const lnEnabled = bool(and(not(isWatchingOnly), not(isTrezor), not(isLedger))); export const lnActive = bool(get(["ln", "active"])); export const lnStartupStage = get(["ln", "startupStage"]); export const lnStartAttempt = bool(get(["ln", "startAttempt"]));