From bc7a8401f8ef7cc00a53805d8520a314af71534d Mon Sep 17 00:00:00 2001 From: Szymon Lesisz Date: Fri, 22 Feb 2019 17:39:51 +0100 Subject: [PATCH 01/12] open modal on UI.REQUEST_CONFIRMATION event --- src/actions/constants/modal.js | 1 + src/reducers/ModalReducer.js | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/actions/constants/modal.js b/src/actions/constants/modal.js index 3383e91a..d7ac2cb9 100644 --- a/src/actions/constants/modal.js +++ b/src/actions/constants/modal.js @@ -7,3 +7,4 @@ export const CONTEXT_DEVICE: 'modal_ctx_device' = 'modal_ctx_device'; export const CONTEXT_EXTERNAL_WALLET: 'modal_ctx_external-wallet' = 'modal_ctx_external-wallet'; export const OPEN_SCAN_QR: 'modal__open_scan_qr' = 'modal__open_scan_qr'; export const CONTEXT_SCAN_QR: 'modal__ctx_scan_qr' = 'modal__ctx_scan_qr'; +export const CONTEXT_CONFIRMATION: 'modal__ctx_confirmation' = 'modal__ctx_confirmation'; diff --git a/src/reducers/ModalReducer.js b/src/reducers/ModalReducer.js index 266ced8b..70e534bf 100644 --- a/src/reducers/ModalReducer.js +++ b/src/reducers/ModalReducer.js @@ -20,7 +20,10 @@ export type State = { windowType?: string; } | { context: typeof MODAL.CONTEXT_SCAN_QR, -} +} | { + context: typeof MODAL.CONTEXT_CONFIRMATION, + windowType: string; +}; const initialState: State = { context: MODAL.CONTEXT_NONE, @@ -98,6 +101,12 @@ export default function modal(state: State = initialState, action: Action): Stat context: MODAL.CONTEXT_SCAN_QR, }; + case UI.REQUEST_CONFIRMATION: + return { + context: MODAL.CONTEXT_CONFIRMATION, + windowType: action.payload.view, + }; + default: return state; } From 397fd0fac7ba854213022f0babf94cf6ec2d1c8a Mon Sep 17 00:00:00 2001 From: Szymon Lesisz Date: Fri, 22 Feb 2019 17:40:12 +0100 Subject: [PATCH 02/12] add NoBackup modal --- .../modals/confirm/NoBackup/index.js | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 src/components/modals/confirm/NoBackup/index.js diff --git a/src/components/modals/confirm/NoBackup/index.js b/src/components/modals/confirm/NoBackup/index.js new file mode 100644 index 00000000..814cb92f --- /dev/null +++ b/src/components/modals/confirm/NoBackup/index.js @@ -0,0 +1,82 @@ +/* @flow */ + +import React from 'react'; +import PropTypes from 'prop-types'; +import styled from 'styled-components'; + +import icons from 'config/icons'; +import colors from 'config/colors'; + +import { H2 } from 'components/Heading'; +import P from 'components/Paragraph'; +import Icon from 'components/Icon'; +import Button from 'components/Button'; +import Link from 'components/Link'; + +import type { Props as BaseProps } from '../../Container'; + +type Props = { + onReceiveConfirmation: $ElementType<$ElementType, 'onReceiveConfirmation'>; +} + +const Wrapper = styled.div` + max-width: 370px; + padding: 30px 48px; +`; + +const StyledLink = styled(Link)` + position: absolute; + right: 15px; + top: 15px; +`; + +const BackupButton = styled(Button)` + width: 100%; + margin-bottom: 10px; +`; + +const ProceedButton = styled(Button)` + background: transparent; + border-color: ${colors.WARNING_PRIMARY}; + color: ${colors.WARNING_PRIMARY}; + + &:focus, + &:hover, + &:active { + color: ${colors.WHITE}; + background: ${colors.WARNING_PRIMARY}; + box-shadow: none; + } +`; + +const StyledP = styled(P)` + padding-bottom: 20px; +`; + +const Row = styled.div` + display: flex; + flex-direction: column; +`; + +const Confirmation = (props: Props) => ( + + props.onReceiveConfirmation(false)}> + + +

Your Trezor is not backed up!

+ + If your device is ever lost or damaged, your funds will be lost. Backup your device first, to protect your coins against such events. + + + props.onReceiveConfirmation(false)}>Create a backup in 3 minutes. + + props.onReceiveConfirmation(true)}>Show address, I will take the risk + +
+); + +Confirmation.propTypes = { + onReceiveConfirmation: PropTypes.func.isRequired, +}; + +export default Confirmation; \ No newline at end of file From e336a4f05f49d6f32c84946346e85e14971e8191 Mon Sep 17 00:00:00 2001 From: Szymon Lesisz Date: Fri, 22 Feb 2019 17:40:40 +0100 Subject: [PATCH 03/12] add "onReceiveConfirmation" method to ModalActions --- src/actions/ModalActions.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/actions/ModalActions.js b/src/actions/ModalActions.js index 47f98776..fe2cd7af 100644 --- a/src/actions/ModalActions.js +++ b/src/actions/ModalActions.js @@ -59,6 +59,17 @@ export const onPassphraseSubmit = (passphrase: string): AsyncAction => async (di }); }; +export const onReceiveConfirmation = (confirmation: any): AsyncAction => async (dispatch: Dispatch): Promise => { + await TrezorConnect.uiResponse({ + type: UI.RECEIVE_CONFIRMATION, + payload: confirmation, + }); + + dispatch({ + type: MODAL.CLOSE, + }); +}; + export const onRememberDevice = (device: TrezorDevice): Action => ({ type: CONNECT.REMEMBER, device, @@ -173,6 +184,7 @@ export const onQrScan = (parsedUri: parsedURI, networkType: string): ThunkAction export default { onPinSubmit, onPassphraseSubmit, + onReceiveConfirmation, onRememberDevice, onForgetDevice, onForgetSingleDevice, From 3ac1b207472f16f62fbcd26f847854f64133e2fa Mon Sep 17 00:00:00 2001 From: Szymon Lesisz Date: Fri, 22 Feb 2019 17:40:58 +0100 Subject: [PATCH 04/12] display NoBackup modal --- src/components/modals/index.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/components/modals/index.js b/src/components/modals/index.js index 4db54d9e..7fcb70db 100644 --- a/src/components/modals/index.js +++ b/src/components/modals/index.js @@ -19,6 +19,7 @@ import PassphraseType from 'components/modals/passphrase/Type'; import ConfirmSignTx from 'components/modals/confirm/SignTx'; import ConfirmAction from 'components/modals/confirm/Action'; import ConfirmUnverifiedAddress from 'components/modals/confirm/UnverifiedAddress'; +import ConfirmNoBackup from 'components/modals/confirm/NoBackup'; import ForgetDevice from 'components/modals/device/Forget'; import RememberDevice from 'components/modals/device/Remember'; import DuplicateDevice from 'components/modals/device/Duplicate'; @@ -189,6 +190,19 @@ const getQrModal = (props: Props) => { ); }; +const getConfirmationModal = (props: Props) => { + const { modal, modalActions } = props; + + if (modal.context !== MODAL.CONTEXT_CONFIRMATION) return null; + + switch (modal.windowType) { + case 'no-backup': + return (); + default: + return null; + } +}; + // modal container component const Modal = (props: Props) => { const { modal } = props; @@ -205,6 +219,9 @@ const Modal = (props: Props) => { case MODAL.CONTEXT_SCAN_QR: component = getQrModal(props); break; + case MODAL.CONTEXT_CONFIRMATION: + component = getConfirmationModal(props); + break; default: break; } From 40790470c2b92f8bc5460bc74b446ca4dc240491 Mon Sep 17 00:00:00 2001 From: Szymon Lesisz Date: Fri, 22 Feb 2019 17:42:07 +0100 Subject: [PATCH 05/12] ignore error code in showaddress method 403: permissions not granted from TrezorConnect can be safely ignored --- src/actions/ReceiveActions.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/actions/ReceiveActions.js b/src/actions/ReceiveActions.js index 116a5c15..0503fdf7 100644 --- a/src/actions/ReceiveActions.js +++ b/src/actions/ReceiveActions.js @@ -94,6 +94,10 @@ export const showAddress = (path: Array): AsyncAction => async (dispatch type: RECEIVE.HIDE_ADDRESS, }); + // special case: device no-backup permissions not granted + // $FlowIssue: remove this after trezor-connect@7.0.0 release + if (response.payload.code === 403) return; + dispatch({ type: NOTIFICATION.ADD, payload: { From 19dbf88d3c24330c5aa0009137db3c337ccde9e7 Mon Sep 17 00:00:00 2001 From: slowbackspace Date: Sun, 24 Feb 2019 22:51:51 +0100 Subject: [PATCH 06/12] add NoBackup notification --- .../App/components/NoBackup/index.js | 25 +++++++++++++++++++ src/components/notifications/App/index.js | 2 ++ 2 files changed, 27 insertions(+) create mode 100644 src/components/notifications/App/components/NoBackup/index.js diff --git a/src/components/notifications/App/components/NoBackup/index.js b/src/components/notifications/App/components/NoBackup/index.js new file mode 100644 index 00000000..494bb9d9 --- /dev/null +++ b/src/components/notifications/App/components/NoBackup/index.js @@ -0,0 +1,25 @@ +/* @flow */ +import * as React from 'react'; +import Notification from 'components/Notification'; + +import type { Props } from '../../index'; + +export default (props: Props) => { + const { selectedDevice } = props.wallet; + const needsBackup = selectedDevice && selectedDevice.features && selectedDevice.features.needs_backup; + if (!needsBackup) return null; + return ( + {}, //TODO + }] + } + /> + ); +}; \ No newline at end of file diff --git a/src/components/notifications/App/index.js b/src/components/notifications/App/index.js index 6167cfcf..d7c91180 100644 --- a/src/components/notifications/App/index.js +++ b/src/components/notifications/App/index.js @@ -12,6 +12,7 @@ import * as RouterActions from 'actions/RouterActions'; import OnlineStatus from './components/OnlineStatus'; import UpdateBridge from './components/UpdateBridge'; import UpdateFirmware from './components/UpdateFirmware'; +import NoBackup from './components/NoBackup'; export type StateProps = { connect: $ElementType; @@ -33,6 +34,7 @@ const Notifications = (props: Props) => ( + ); From b0d67bd6672c852b757ed994f090eadffb448874 Mon Sep 17 00:00:00 2001 From: slowbackspace Date: Mon, 25 Feb 2019 13:28:41 +0100 Subject: [PATCH 07/12] add gotoBackup action --- src/actions/RouterActions.js | 11 +++++++++++ .../notifications/App/components/NoBackup/index.js | 4 ++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/actions/RouterActions.js b/src/actions/RouterActions.js index 3d501714..7fdeb835 100644 --- a/src/actions/RouterActions.js +++ b/src/actions/RouterActions.js @@ -347,6 +347,17 @@ export const gotoFirmwareUpdate = (): ThunkAction => (dispatch: Dispatch, getSta dispatch(goto(`/device/${devUrl}/firmware-update`)); }; +/* +* Go to NoBackup page +*/ +export const gotoBackup = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { + const { selectedDevice } = getState().wallet; + if (!selectedDevice || !selectedDevice.features) return; + const devUrl: string = `${selectedDevice.features.device_id}${selectedDevice.instance ? `:${selectedDevice.instance}` : ''}`; + dispatch(goto(`/device/${devUrl}/backup`)); +}; + + /* * Try to redirect to initial url */ diff --git a/src/components/notifications/App/components/NoBackup/index.js b/src/components/notifications/App/components/NoBackup/index.js index 494bb9d9..5b7f2082 100644 --- a/src/components/notifications/App/components/NoBackup/index.js +++ b/src/components/notifications/App/components/NoBackup/index.js @@ -10,14 +10,14 @@ export default (props: Props) => { if (!needsBackup) return null; return ( {}, //TODO + callback: props.routerActions.gotoBackup, }] } /> From bb4cd79004c192961bfee3debfb991749b079bfc Mon Sep 17 00:00:00 2001 From: slowbackspace Date: Mon, 25 Feb 2019 13:28:58 +0100 Subject: [PATCH 08/12] add NoBackup component --- src/support/routes.js | 5 +++ src/views/Wallet/views/NoBackup/index.js | 57 ++++++++++++++++++++++++ src/views/index.js | 2 + 3 files changed, 64 insertions(+) create mode 100644 src/views/Wallet/views/NoBackup/index.js diff --git a/src/support/routes.js b/src/support/routes.js index 717bed03..1ebe97c2 100644 --- a/src/support/routes.js +++ b/src/support/routes.js @@ -62,6 +62,11 @@ export const routes: Array = [ pattern: '/device/:device/firmware-update', fields: ['device', 'firmware-update'], }, + { + name: 'wallet-backup', + pattern: '/device/:device/backup', + fields: ['device', 'backup'], + }, { name: 'wallet-device-settings', pattern: '/device/:device/settings', diff --git a/src/views/Wallet/views/NoBackup/index.js b/src/views/Wallet/views/NoBackup/index.js new file mode 100644 index 00000000..53c27cf9 --- /dev/null +++ b/src/views/Wallet/views/NoBackup/index.js @@ -0,0 +1,57 @@ +/* @flow */ + +import React from 'react'; +import styled from 'styled-components'; +import { H1 } from 'components/Heading'; +import P from 'components/Paragraph'; +import Link from 'components/Link'; +import Button from 'components/Button'; +import Icon from 'components/Icon'; + +import { FONT_SIZE } from 'config/variables'; +import colors from 'config/colors'; +import icons from 'config/icons'; + +const Wrapper = styled.section` + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + padding: 90px 35px 40px 35px; +`; + +const StyledNavLink = styled(Link)` + color: ${colors.TEXT_SECONDARY}; + padding-top: 20px; + font-size: ${FONT_SIZE.BASE}; +`; + +const StyledH1 = styled(H1)` + text-align: center; +`; + +const Message = styled.div` + text-align: center; + padding: 0 0 15px 0; +`; + +const FirmwareUpdate = () => ( + + + Your Trezor is not backed up! + +

If your device is ever lost or damaged, your funds will be lost. Backup your device first, to protect your coins against such events.

+

Please use Bitcoin wallet interface to create a backup.

+
+ + + + I’ll do that later. +
+); + +export default FirmwareUpdate; \ No newline at end of file diff --git a/src/views/index.js b/src/views/index.js index 1b0a8963..184b4133 100644 --- a/src/views/index.js +++ b/src/views/index.js @@ -27,6 +27,7 @@ import WalletDeviceSettings from 'views/Wallet/views/DeviceSettings'; import WalletSettings from 'views/Wallet/views/WalletSettings'; import WalletBootloader from 'views/Wallet/views/Bootloader'; import WalletFirmwareUpdate from 'views/Wallet/views/FirmwareUpdate'; +import WalletNoBackup from 'views/Wallet/views/NoBackup'; import WalletInitialize from 'views/Wallet/views/Initialize'; import WalletSeedless from 'views/Wallet/views/Seedless'; import WalletAcquire from 'views/Wallet/views/Acquire'; @@ -54,6 +55,7 @@ const App = () => ( + From 752943ff4e0e40966bd1b4beef2b2f99f3a3ae25 Mon Sep 17 00:00:00 2001 From: slowbackspace Date: Mon, 25 Feb 2019 16:12:26 +0100 Subject: [PATCH 09/12] remove punctuation marks from button and title --- src/components/modals/confirm/NoBackup/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/modals/confirm/NoBackup/index.js b/src/components/modals/confirm/NoBackup/index.js index 814cb92f..205a9947 100644 --- a/src/components/modals/confirm/NoBackup/index.js +++ b/src/components/modals/confirm/NoBackup/index.js @@ -63,12 +63,12 @@ const Confirmation = (props: Props) => ( props.onReceiveConfirmation(false)}> -

Your Trezor is not backed up!

+

Your Trezor is not backed up

If your device is ever lost or damaged, your funds will be lost. Backup your device first, to protect your coins against such events. - props.onReceiveConfirmation(false)}>Create a backup in 3 minutes. + props.onReceiveConfirmation(false)}>Create a backup in 3 minutes props.onReceiveConfirmation(true)}>Show address, I will take the risk From a01ca0fb78159914a6ae44071204c4953b6b7009 Mon Sep 17 00:00:00 2001 From: slowbackspace Date: Mon, 25 Feb 2019 16:19:13 +0100 Subject: [PATCH 10/12] show warning when device needs backup before showing full address --- .../modals/confirm/UnverifiedAddress/index.js | 74 +++++++++++++++---- 1 file changed, 61 insertions(+), 13 deletions(-) diff --git a/src/components/modals/confirm/UnverifiedAddress/index.js b/src/components/modals/confirm/UnverifiedAddress/index.js index e6f26592..230671ac 100644 --- a/src/components/modals/confirm/UnverifiedAddress/index.js +++ b/src/components/modals/confirm/UnverifiedAddress/index.js @@ -31,11 +31,23 @@ const StyledLink = styled(Link)` const Wrapper = styled.div` max-width: 370px; - padding: 30px 48px; + padding: 30px 0px; + +`; + +const Content = styled.div` + padding: 0px 48px; `; const StyledP = styled(P)` - padding: 20px 0px; + padding-bottom: 20px; +`; + +const Divider = styled.div` + width: 100%; + height: 1px; + background: ${colors.DIVIDER}; + margin: 20px 0px; `; const Row = styled.div` @@ -47,6 +59,20 @@ const Row = styled.div` } `; +const WarnButton = styled(Button)` + background: transparent; + border-color: ${colors.WARNING_PRIMARY}; + color: ${colors.WARNING_PRIMARY}; + + &:focus, + &:hover, + &:active { + color: ${colors.WHITE}; + background: ${colors.WARNING_PRIMARY}; + box-shadow: none; + } +`; + class ConfirmUnverifiedAddress extends PureComponent { componentDidMount(): void { this.keyboardHandler = this.keyboardHandler.bind(this); @@ -86,26 +112,48 @@ class ConfirmUnverifiedAddress extends PureComponent { let claim: string; if (!device.connected) { - deviceStatus = `${device.label} is not connected`; + deviceStatus = `Device ${device.label} is not connected`; claim = 'Please connect your device'; } else { // corner-case where device is connected but it is unavailable because it was created with different "passphrase_protection" settings const enable: string = device.features && device.features.passphrase_protection ? 'enable' : 'disable'; - deviceStatus = `${device.label} is unavailable`; + deviceStatus = `Device ${device.label} is unavailable`; claim = `Please ${enable} passphrase settings`; } + const needsBackup = device.features && device.features.needs_backup; + return ( - - - -

{ deviceStatus }

- To prevent phishing attacks, you should verify the address on your Trezor first. { claim } to continue with the verification process. - - - - + + + + +

{ deviceStatus }

+ To prevent phishing attacks, you should verify the address on your Trezor first. { claim } to continue with the verification process. +
+ + + + this.showUnverifiedAddress()}>Show unverified address + + + {needsBackup && } + {needsBackup && ( + <> + +

Device {device.label} is not backed up

+ If your device is ever lost or damaged, your funds will be lost. Backup your device first, to protect your coins against such events. +
+ + + + + + + + + )}
); } From cf86dc93a28cab51f2f5abcc1942c1a30281fe0c Mon Sep 17 00:00:00 2001 From: slowbackspace Date: Mon, 25 Feb 2019 16:28:41 +0100 Subject: [PATCH 11/12] fix width of the backup button --- src/components/modals/confirm/UnverifiedAddress/index.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/modals/confirm/UnverifiedAddress/index.js b/src/components/modals/confirm/UnverifiedAddress/index.js index 230671ac..dec2098f 100644 --- a/src/components/modals/confirm/UnverifiedAddress/index.js +++ b/src/components/modals/confirm/UnverifiedAddress/index.js @@ -59,6 +59,10 @@ const Row = styled.div` } `; +const BackupButton = styled(Button)` + width: 100%; +`; + const WarnButton = styled(Button)` background: transparent; border-color: ${colors.WARNING_PRIMARY}; @@ -148,7 +152,7 @@ class ConfirmUnverifiedAddress extends PureComponent { - + Create a backup in 3 minutes From f502f55a4dad0fb8f954a11ac089d66d9a565920 Mon Sep 17 00:00:00 2001 From: Vladimir Volek Date: Tue, 26 Feb 2019 13:10:21 +0100 Subject: [PATCH 12/12] Added max width for paragraph --- src/views/Wallet/views/NoBackup/index.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/views/Wallet/views/NoBackup/index.js b/src/views/Wallet/views/NoBackup/index.js index 53c27cf9..62d2b982 100644 --- a/src/views/Wallet/views/NoBackup/index.js +++ b/src/views/Wallet/views/NoBackup/index.js @@ -30,6 +30,11 @@ const StyledH1 = styled(H1)` text-align: center; `; +const StyledP = styled(P)` + max-width: 550px; + padding-bottom: 15px; +`; + const Message = styled.div` text-align: center; padding: 0 0 15px 0; @@ -44,7 +49,7 @@ const FirmwareUpdate = () => ( /> Your Trezor is not backed up! -

If your device is ever lost or damaged, your funds will be lost. Backup your device first, to protect your coins against such events.

+ If your device is ever lost or damaged, your funds will be lost. Backup your device first, to protect your coins against such events.

Please use Bitcoin wallet interface to create a backup.