mirror of
https://github.com/trezor/trezor-wallet
synced 2025-01-09 07:30:58 +00:00
merge
This commit is contained in:
commit
f4685961ec
@ -59,6 +59,17 @@ export const onPassphraseSubmit = (passphrase: string): AsyncAction => async (di
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const onReceiveConfirmation = (confirmation: any): AsyncAction => async (dispatch: Dispatch): Promise<void> => {
|
||||||
|
await TrezorConnect.uiResponse({
|
||||||
|
type: UI.RECEIVE_CONFIRMATION,
|
||||||
|
payload: confirmation,
|
||||||
|
});
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: MODAL.CLOSE,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const onRememberDevice = (device: TrezorDevice): Action => ({
|
export const onRememberDevice = (device: TrezorDevice): Action => ({
|
||||||
type: CONNECT.REMEMBER,
|
type: CONNECT.REMEMBER,
|
||||||
device,
|
device,
|
||||||
@ -173,6 +184,7 @@ export const onQrScan = (parsedUri: parsedURI, networkType: string): ThunkAction
|
|||||||
export default {
|
export default {
|
||||||
onPinSubmit,
|
onPinSubmit,
|
||||||
onPassphraseSubmit,
|
onPassphraseSubmit,
|
||||||
|
onReceiveConfirmation,
|
||||||
onRememberDevice,
|
onRememberDevice,
|
||||||
onForgetDevice,
|
onForgetDevice,
|
||||||
onForgetSingleDevice,
|
onForgetSingleDevice,
|
||||||
|
@ -95,6 +95,7 @@ export const showAddress = (path: Array<number>): AsyncAction => async (dispatch
|
|||||||
});
|
});
|
||||||
|
|
||||||
// special case: device no-backup permissions not granted
|
// special case: device no-backup permissions not granted
|
||||||
|
// $FlowIssue: remove this after trezor-connect@7.0.0 release
|
||||||
if (response.payload.code === 403) return;
|
if (response.payload.code === 403) return;
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
|
@ -347,6 +347,17 @@ export const gotoFirmwareUpdate = (): ThunkAction => (dispatch: Dispatch, getSta
|
|||||||
dispatch(goto(`/device/${devUrl}/firmware-update`));
|
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
|
* Try to redirect to initial url
|
||||||
*/
|
*/
|
||||||
|
@ -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 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 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_SCAN_QR: 'modal__ctx_scan_qr' = 'modal__ctx_scan_qr';
|
||||||
|
export const CONTEXT_CONFIRMATION: 'modal__ctx_confirmation' = 'modal__ctx_confirmation';
|
||||||
|
@ -31,11 +31,23 @@ const StyledLink = styled(Link)`
|
|||||||
|
|
||||||
const Wrapper = styled.div`
|
const Wrapper = styled.div`
|
||||||
max-width: 370px;
|
max-width: 370px;
|
||||||
padding: 30px 48px;
|
padding: 30px 0px;
|
||||||
|
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Content = styled.div`
|
||||||
|
padding: 0px 48px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledP = styled(P)`
|
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`
|
const Row = styled.div`
|
||||||
@ -47,6 +59,24 @@ const Row = styled.div`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const BackupButton = styled(Button)`
|
||||||
|
width: 100%;
|
||||||
|
`;
|
||||||
|
|
||||||
|
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<Props> {
|
class ConfirmUnverifiedAddress extends PureComponent<Props> {
|
||||||
componentDidMount(): void {
|
componentDidMount(): void {
|
||||||
this.keyboardHandler = this.keyboardHandler.bind(this);
|
this.keyboardHandler = this.keyboardHandler.bind(this);
|
||||||
@ -86,26 +116,48 @@ class ConfirmUnverifiedAddress extends PureComponent<Props> {
|
|||||||
let claim: string;
|
let claim: string;
|
||||||
|
|
||||||
if (!device.connected) {
|
if (!device.connected) {
|
||||||
deviceStatus = `${device.label} is not connected`;
|
deviceStatus = `Device ${device.label} is not connected`;
|
||||||
claim = 'Please connect your device';
|
claim = 'Please connect your device';
|
||||||
} else {
|
} else {
|
||||||
// corner-case where device is connected but it is unavailable because it was created with different "passphrase_protection" settings
|
// 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';
|
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`;
|
claim = `Please ${enable} passphrase settings`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const needsBackup = device.features && device.features.needs_backup;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<StyledLink onClick={onCancel}>
|
<Content>
|
||||||
<Icon size={24} color={colors.TEXT_SECONDARY} icon={icons.CLOSE} />
|
<StyledLink onClick={onCancel}>
|
||||||
</StyledLink>
|
<Icon size={24} color={colors.TEXT_SECONDARY} icon={icons.CLOSE} />
|
||||||
<H2>{ deviceStatus }</H2>
|
</StyledLink>
|
||||||
<StyledP isSmaller>To prevent phishing attacks, you should verify the address on your Trezor first. { claim } to continue with the verification process.</StyledP>
|
<H2>{ deviceStatus }</H2>
|
||||||
<Row>
|
<StyledP isSmaller>To prevent phishing attacks, you should verify the address on your Trezor first. { claim } to continue with the verification process.</StyledP>
|
||||||
<Button onClick={() => (!account ? this.verifyAddress() : 'false')}>Try again</Button>
|
</Content>
|
||||||
<Button isWhite onClick={() => this.showUnverifiedAddress()}>Show unverified address</Button>
|
<Content>
|
||||||
</Row>
|
<Row>
|
||||||
|
<Button onClick={() => (!account ? this.verifyAddress() : 'false')}>Try again</Button>
|
||||||
|
<WarnButton isWhite onClick={() => this.showUnverifiedAddress()}>Show unverified address</WarnButton>
|
||||||
|
</Row>
|
||||||
|
</Content>
|
||||||
|
{needsBackup && <Divider />}
|
||||||
|
{needsBackup && (
|
||||||
|
<>
|
||||||
|
<Content>
|
||||||
|
<H2>Device {device.label} is not backed up</H2>
|
||||||
|
<StyledP isSmaller>If your device is ever lost or damaged, your funds will be lost. Backup your device first, to protect your coins against such events.</StyledP>
|
||||||
|
</Content>
|
||||||
|
<Content>
|
||||||
|
<Row>
|
||||||
|
<Link href="https://wallet.trezor.io/?backup">
|
||||||
|
<BackupButton>Create a backup in 3 minutes</BackupButton>
|
||||||
|
</Link>
|
||||||
|
</Row>
|
||||||
|
</Content>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ import PassphraseType from 'components/modals/passphrase/Type';
|
|||||||
import ConfirmSignTx from 'components/modals/confirm/SignTx';
|
import ConfirmSignTx from 'components/modals/confirm/SignTx';
|
||||||
import ConfirmAction from 'components/modals/confirm/Action';
|
import ConfirmAction from 'components/modals/confirm/Action';
|
||||||
import ConfirmUnverifiedAddress from 'components/modals/confirm/UnverifiedAddress';
|
import ConfirmUnverifiedAddress from 'components/modals/confirm/UnverifiedAddress';
|
||||||
|
import ConfirmNoBackup from 'components/modals/confirm/NoBackup';
|
||||||
import ForgetDevice from 'components/modals/device/Forget';
|
import ForgetDevice from 'components/modals/device/Forget';
|
||||||
import RememberDevice from 'components/modals/device/Remember';
|
import RememberDevice from 'components/modals/device/Remember';
|
||||||
import DuplicateDevice from 'components/modals/device/Duplicate';
|
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 (<ConfirmNoBackup onReceiveConfirmation={modalActions.onReceiveConfirmation} />);
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// modal container component
|
// modal container component
|
||||||
const Modal = (props: Props) => {
|
const Modal = (props: Props) => {
|
||||||
const { modal } = props;
|
const { modal } = props;
|
||||||
@ -205,6 +219,9 @@ const Modal = (props: Props) => {
|
|||||||
case MODAL.CONTEXT_SCAN_QR:
|
case MODAL.CONTEXT_SCAN_QR:
|
||||||
component = getQrModal(props);
|
component = getQrModal(props);
|
||||||
break;
|
break;
|
||||||
|
case MODAL.CONTEXT_CONFIRMATION:
|
||||||
|
component = getConfirmationModal(props);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -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 (
|
||||||
|
<Notification
|
||||||
|
key="no-backup"
|
||||||
|
type="warning"
|
||||||
|
title="Your Trezor is not backed up!"
|
||||||
|
message="If your device is ever lost or damaged, your funds will be lost. Backup your device first, to protect your coins against such events."
|
||||||
|
actions={
|
||||||
|
[{
|
||||||
|
label: 'Create a backup',
|
||||||
|
callback: props.routerActions.gotoBackup,
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
@ -12,6 +12,7 @@ import * as RouterActions from 'actions/RouterActions';
|
|||||||
import OnlineStatus from './components/OnlineStatus';
|
import OnlineStatus from './components/OnlineStatus';
|
||||||
import UpdateBridge from './components/UpdateBridge';
|
import UpdateBridge from './components/UpdateBridge';
|
||||||
import UpdateFirmware from './components/UpdateFirmware';
|
import UpdateFirmware from './components/UpdateFirmware';
|
||||||
|
import NoBackup from './components/NoBackup';
|
||||||
|
|
||||||
export type StateProps = {
|
export type StateProps = {
|
||||||
connect: $ElementType<State, 'connect'>;
|
connect: $ElementType<State, 'connect'>;
|
||||||
@ -33,6 +34,7 @@ const Notifications = (props: Props) => (
|
|||||||
<OnlineStatus {...props} />
|
<OnlineStatus {...props} />
|
||||||
<UpdateBridge {...props} />
|
<UpdateBridge {...props} />
|
||||||
<UpdateFirmware {...props} />
|
<UpdateFirmware {...props} />
|
||||||
|
<NoBackup {...props} />
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -20,7 +20,10 @@ export type State = {
|
|||||||
windowType?: string;
|
windowType?: string;
|
||||||
} | {
|
} | {
|
||||||
context: typeof MODAL.CONTEXT_SCAN_QR,
|
context: typeof MODAL.CONTEXT_SCAN_QR,
|
||||||
}
|
} | {
|
||||||
|
context: typeof MODAL.CONTEXT_CONFIRMATION,
|
||||||
|
windowType: string;
|
||||||
|
};
|
||||||
|
|
||||||
const initialState: State = {
|
const initialState: State = {
|
||||||
context: MODAL.CONTEXT_NONE,
|
context: MODAL.CONTEXT_NONE,
|
||||||
@ -98,6 +101,12 @@ export default function modal(state: State = initialState, action: Action): Stat
|
|||||||
context: MODAL.CONTEXT_SCAN_QR,
|
context: MODAL.CONTEXT_SCAN_QR,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
case UI.REQUEST_CONFIRMATION:
|
||||||
|
return {
|
||||||
|
context: MODAL.CONTEXT_CONFIRMATION,
|
||||||
|
windowType: action.payload.view,
|
||||||
|
};
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -62,6 +62,11 @@ export const routes: Array<Route> = [
|
|||||||
pattern: '/device/:device/firmware-update',
|
pattern: '/device/:device/firmware-update',
|
||||||
fields: ['device', 'firmware-update'],
|
fields: ['device', 'firmware-update'],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'wallet-backup',
|
||||||
|
pattern: '/device/:device/backup',
|
||||||
|
fields: ['device', 'backup'],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'wallet-device-settings',
|
name: 'wallet-device-settings',
|
||||||
pattern: '/device/:device/settings',
|
pattern: '/device/:device/settings',
|
||||||
|
62
src/views/Wallet/views/NoBackup/index.js
Normal file
62
src/views/Wallet/views/NoBackup/index.js
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
/* @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 StyledP = styled(P)`
|
||||||
|
max-width: 550px;
|
||||||
|
padding-bottom: 15px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Message = styled.div`
|
||||||
|
text-align: center;
|
||||||
|
padding: 0 0 15px 0;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const FirmwareUpdate = () => (
|
||||||
|
<Wrapper>
|
||||||
|
<Icon
|
||||||
|
size={128}
|
||||||
|
color={colors.WARNING_PRIMARY}
|
||||||
|
icon={icons.WARNING}
|
||||||
|
/>
|
||||||
|
<StyledH1>Your Trezor is not backed up!</StyledH1>
|
||||||
|
<Message>
|
||||||
|
<StyledP>If your device is ever lost or damaged, your funds will be lost. Backup your device first, to protect your coins against such events.</StyledP>
|
||||||
|
<P>Please use Bitcoin wallet interface to create a backup.</P>
|
||||||
|
</Message>
|
||||||
|
<Link href="https://wallet.trezor.io?backup=1">
|
||||||
|
<Button>Take me to the Bitcoin wallet</Button>
|
||||||
|
</Link>
|
||||||
|
<StyledNavLink to="/">I’ll do that later.</StyledNavLink>
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default FirmwareUpdate;
|
@ -27,6 +27,7 @@ import WalletDeviceSettings from 'views/Wallet/views/DeviceSettings';
|
|||||||
import WalletSettings from 'views/Wallet/views/WalletSettings';
|
import WalletSettings from 'views/Wallet/views/WalletSettings';
|
||||||
import WalletBootloader from 'views/Wallet/views/Bootloader';
|
import WalletBootloader from 'views/Wallet/views/Bootloader';
|
||||||
import WalletFirmwareUpdate from 'views/Wallet/views/FirmwareUpdate';
|
import WalletFirmwareUpdate from 'views/Wallet/views/FirmwareUpdate';
|
||||||
|
import WalletNoBackup from 'views/Wallet/views/NoBackup';
|
||||||
import WalletInitialize from 'views/Wallet/views/Initialize';
|
import WalletInitialize from 'views/Wallet/views/Initialize';
|
||||||
import WalletSeedless from 'views/Wallet/views/Seedless';
|
import WalletSeedless from 'views/Wallet/views/Seedless';
|
||||||
import WalletAcquire from 'views/Wallet/views/Acquire';
|
import WalletAcquire from 'views/Wallet/views/Acquire';
|
||||||
@ -54,6 +55,7 @@ const App = () => (
|
|||||||
<Route exact path={getPattern('wallet-initialize')} component={WalletInitialize} />
|
<Route exact path={getPattern('wallet-initialize')} component={WalletInitialize} />
|
||||||
<Route exact path={getPattern('wallet-seedless')} component={WalletSeedless} />
|
<Route exact path={getPattern('wallet-seedless')} component={WalletSeedless} />
|
||||||
<Route exact path={getPattern('wallet-firmware-update')} component={WalletFirmwareUpdate} />
|
<Route exact path={getPattern('wallet-firmware-update')} component={WalletFirmwareUpdate} />
|
||||||
|
<Route exact path={getPattern('wallet-backup')} component={WalletNoBackup} />
|
||||||
<Route exact path={getPattern('wallet-device-settings')} component={WalletDeviceSettings} />
|
<Route exact path={getPattern('wallet-device-settings')} component={WalletDeviceSettings} />
|
||||||
<Route exact path={getPattern('wallet-account-summary')} component={AccountSummary} />
|
<Route exact path={getPattern('wallet-account-summary')} component={AccountSummary} />
|
||||||
<Route path={getPattern('wallet-account-send')} component={AccountSend} />
|
<Route path={getPattern('wallet-account-send')} component={AccountSend} />
|
||||||
|
Loading…
Reference in New Issue
Block a user