mirror of
https://github.com/trezor/trezor-wallet
synced 2025-01-07 14:50:52 +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 => ({
|
||||
type: CONNECT.REMEMBER,
|
||||
device,
|
||||
@ -173,6 +184,7 @@ export const onQrScan = (parsedUri: parsedURI, networkType: string): ThunkAction
|
||||
export default {
|
||||
onPinSubmit,
|
||||
onPassphraseSubmit,
|
||||
onReceiveConfirmation,
|
||||
onRememberDevice,
|
||||
onForgetDevice,
|
||||
onForgetSingleDevice,
|
||||
|
@ -95,6 +95,7 @@ export const showAddress = (path: Array<number>): AsyncAction => async (dispatch
|
||||
});
|
||||
|
||||
// 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({
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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';
|
||||
|
@ -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,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> {
|
||||
componentDidMount(): void {
|
||||
this.keyboardHandler = this.keyboardHandler.bind(this);
|
||||
@ -86,26 +116,48 @@ class ConfirmUnverifiedAddress extends PureComponent<Props> {
|
||||
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 (
|
||||
<Wrapper>
|
||||
<StyledLink onClick={onCancel}>
|
||||
<Icon size={24} color={colors.TEXT_SECONDARY} icon={icons.CLOSE} />
|
||||
</StyledLink>
|
||||
<H2>{ deviceStatus }</H2>
|
||||
<StyledP isSmaller>To prevent phishing attacks, you should verify the address on your Trezor first. { claim } to continue with the verification process.</StyledP>
|
||||
<Row>
|
||||
<Button onClick={() => (!account ? this.verifyAddress() : 'false')}>Try again</Button>
|
||||
<Button isWhite onClick={() => this.showUnverifiedAddress()}>Show unverified address</Button>
|
||||
</Row>
|
||||
<Content>
|
||||
<StyledLink onClick={onCancel}>
|
||||
<Icon size={24} color={colors.TEXT_SECONDARY} icon={icons.CLOSE} />
|
||||
</StyledLink>
|
||||
<H2>{ deviceStatus }</H2>
|
||||
<StyledP isSmaller>To prevent phishing attacks, you should verify the address on your Trezor first. { claim } to continue with the verification process.</StyledP>
|
||||
</Content>
|
||||
<Content>
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
@ -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 (<ConfirmNoBackup onReceiveConfirmation={modalActions.onReceiveConfirmation} />);
|
||||
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;
|
||||
}
|
||||
|
@ -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 UpdateBridge from './components/UpdateBridge';
|
||||
import UpdateFirmware from './components/UpdateFirmware';
|
||||
import NoBackup from './components/NoBackup';
|
||||
|
||||
export type StateProps = {
|
||||
connect: $ElementType<State, 'connect'>;
|
||||
@ -33,6 +34,7 @@ const Notifications = (props: Props) => (
|
||||
<OnlineStatus {...props} />
|
||||
<UpdateBridge {...props} />
|
||||
<UpdateFirmware {...props} />
|
||||
<NoBackup {...props} />
|
||||
</React.Fragment>
|
||||
);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -62,6 +62,11 @@ export const routes: Array<Route> = [
|
||||
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',
|
||||
|
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 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 = () => (
|
||||
<Route exact path={getPattern('wallet-initialize')} component={WalletInitialize} />
|
||||
<Route exact path={getPattern('wallet-seedless')} component={WalletSeedless} />
|
||||
<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-account-summary')} component={AccountSummary} />
|
||||
<Route path={getPattern('wallet-account-send')} component={AccountSend} />
|
||||
|
Loading…
Reference in New Issue
Block a user