Implement ledger unlock redesign

This commit is contained in:
james-prado 2018-07-12 20:08:20 +01:00
parent e9dfcfbbd2
commit cf62870f55
No known key found for this signature in database
GPG Key ID: 313ACB2286229FD0
20 changed files with 356 additions and 148 deletions

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="99px" height="87px" viewBox="0 0 99 87" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 50.2 (55047) - http://www.bohemiancoding.com/sketch -->
<title>icn-ledger-nano</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="New-Onboarding-Modal" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="New-Modal-Wireframe-v3-04" transform="translate(-860.000000, -349.000000)" stroke="#005268" stroke-width="2.70990156">
<g id="icn-ledger-nano" transform="translate(905.000000, 399.000000) rotate(17.000000) translate(-905.000000, -399.000000) translate(855.000000, 355.000000)">
<path d="M1.35495078,8.7984121 L1.35495078,33.9907753 L94.1201623,33.9907753 L94.1201623,8.7984121 L1.35495078,8.7984121 Z M79.0296391,29.950129 C74.2491153,29.950129 70.3655887,26.1242595 70.3655887,21.3945937 C70.3655887,16.6649279 74.2491153,12.8390585 79.0296391,12.8390585 C83.8101629,12.8390585 87.6936894,16.6649279 87.6936894,21.3945937 C87.6936894,26.1242595 83.8101629,29.950129 79.0296391,29.950129 Z" id="Combined-Shape" fill="#077493"></path>
<rect id="Rectangle-14" fill="#1097C0" x="15.8345888" y="16.8990696" width="38.0140803" height="8.99104819" rx="0.903300521"></rect>
<path d="M9.50883116,31.5342916 L9.50883116,56.7266548 L89.6778611,56.7266548 C96.6345401,56.7266548 102.274043,51.0871522 102.274043,44.1304732 C102.274043,37.1737942 96.6345401,31.5342916 89.6778611,31.5342916 L9.50883116,31.5342916 Z M88.0971569,53.5860815 C82.8120451,53.5860815 78.5194691,49.3572356 78.5194691,44.1304732 C78.5194691,38.9037108 82.8120451,34.6748649 88.0971569,34.6748649 C93.3822687,34.6748649 97.6748447,38.9037108 97.6748447,44.1304732 C97.6748447,49.3572356 93.3822687,53.5860815 88.0971569,53.5860815 Z" id="Combined-Shape" fill="#1097C0" transform="translate(55.891437, 44.130473) rotate(-45.000000) translate(-55.891437, -44.130473) "></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="109px" height="100px" viewBox="0 0 109 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 50.2 (55047) - http://www.bohemiancoding.com/sketch -->
<title>icn-trezor-new</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="New-Onboarding-Modal" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="New-Modal-Wireframe-v3-04" transform="translate(-696.000000, -342.000000)">
<g id="icn-trezor-new" transform="translate(693.000000, 340.000000)">
<path d="" id="Path-3" stroke="#979797" stroke-width="0.5"></path>
<path d="M43.2727273,84.9452076 C46.7771363,93.147951 50.889609,97.9639661 55.6101455,99.3932528 C62.6909503,101.537183 71.8111154,98.0882172 73.8963575,87.6295735 C75.9815995,77.1709297 75.9815995,71.212401 79.3451993,67.3643417 C81.5875991,64.7989688 83.9876842,63.3632965 86.5454545,63.0573248" id="Path-4" stroke="#005268" stroke-width="2.59872611"></path>
<path d="M87.208454,60.0072793 L87.208454,61.4121929 L90.423765,61.4121929 L90.423765,66.2165605 L107.518819,66.2165605 L107.518819,54.8025478 L90.423765,54.8025478 L90.423765,60.0072793 L87.208454,60.0072793 Z" id="Combined-Shape" stroke="#005268" stroke-width="2.59872611" fill="#1097C0" transform="translate(97.363636, 60.509554) rotate(-11.000000) translate(-97.363636, -60.509554) "></path>
<g id="Group-4" transform="translate(31.818182, 46.815287) rotate(-16.000000) translate(-31.818182, -46.815287) translate(11.454545, 4.458599)" stroke="#005268" stroke-width="2.59872611">
<path d="M26.0728804,80.5685991 L26.1087761,80.7240803 C26.0889515,80.6382104 26.1424917,80.5525281 26.2283615,80.5327035 L26.0728804,80.5685991 Z M19.8746456,81.9995744 L19.7191645,82.0354701 C19.8050343,82.0156454 19.8907166,82.0691857 19.9105412,82.1550555 L19.8746456,81.9995744 Z M16.3165441,66.5877437 L16.2806485,66.4322626 C16.3004731,66.5181325 16.2469329,66.6038148 16.161063,66.6236394 L16.3165441,66.5877437 Z M16.9232255,63.9312597 L16.6153412,62.655564 L15.644537,64.2264682 L16.9232255,63.9312597 Z M20.7403295,63.0500118 L21.9938867,62.7606053 L20.4384963,61.7993886 L20.7403295,63.0500118 Z M22.514779,65.1567685 L22.6702601,65.1208728 C22.5843902,65.1406974 22.4987079,65.0871572 22.4788833,65.0012873 L22.514779,65.1567685 Z" id="Combined-Shape" fill="#1097C0" transform="translate(21.194712, 72.412897) rotate(-348.000000) translate(-21.194712, -72.412897) "></path>
<path d="M10.2581294,1.29936306 C5.31033937,1.29936306 1.29936306,5.31033937 1.29936306,10.2581294 L1.29936306,43.4845358 C1.29936306,45.4128002 1.9215143,47.2896043 3.07335934,48.8360378 L10.2908976,58.5260936 C11.9815582,60.795925 14.6453907,62.1333579 17.4756677,62.1333579 L23.2516051,62.1333579 C26.0818821,62.1333579 28.7457146,60.795925 30.4363751,58.5260936 L37.6539134,48.8360378 C38.8057584,47.2896043 39.4279097,45.4128002 39.4279097,43.4845358 L39.4279097,10.2581294 C39.4279097,5.31033937 35.4169334,1.29936306 30.4691433,1.29936306 L10.2581294,1.29936306 Z" id="Rectangle-8" fill="#1097C0"></path>
<rect id="Rectangle-10" fill="#077493" x="5.77936306" y="10.7119604" width="29.1685466" height="28.094526" rx="2.27958431"></rect>
</g>
<rect id="Rectangle-17" fill="#005268" transform="translate(109.136364, 57.961783) rotate(-11.000000) translate(-109.136364, -57.961783) " x="107.545455" y="54.1401274" width="3.18181818" height="7.6433121"></rect>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -1,6 +1,6 @@
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import NavigationLink from 'components/NavigationLink'; import NavigationLink from 'components/NavigationLink';
import { navigationLinks, knowledgeBaseURL } from 'config'; import { navigationLinks } from 'config';
import './Navigation.scss'; import './Navigation.scss';
interface Props { interface Props {

View File

@ -19,7 +19,6 @@ import { Link } from 'react-router-dom';
import { TSetGasPriceField, setGasPriceField } from 'actions/transaction'; import { TSetGasPriceField, setGasPriceField } from 'actions/transaction';
import { ANNOUNCEMENT_MESSAGE, ANNOUNCEMENT_TYPE, languages } from 'config'; import { ANNOUNCEMENT_MESSAGE, ANNOUNCEMENT_TYPE, languages } from 'config';
import Navigation from './components/Navigation'; import Navigation from './components/Navigation';
import { knowledgeBaseURL } from 'config/data';
import NetworkDropdown from './components/NetworkDropdown'; import NetworkDropdown from './components/NetworkDropdown';
import CustomNodeModal from 'components/CustomNodeModal'; import CustomNodeModal from 'components/CustomNodeModal';
import { getKeyByValue } from 'utils/helpers'; import { getKeyByValue } from 'utils/helpers';
@ -34,7 +33,6 @@ import {
import { NetworkConfig } from 'types/network'; import { NetworkConfig } from 'types/network';
import { connect, MapStateToProps } from 'react-redux'; import { connect, MapStateToProps } from 'react-redux';
import './index.scss'; import './index.scss';
import translate from 'translations';
interface OwnProps { interface OwnProps {
networkParam: string | null; networkParam: string | null;

View File

@ -0,0 +1,53 @@
@import 'common/sass/variables';
@import 'common/sass/mixins';
.PrimaryButton {
@include reset-button;
display: block;
color: white;
font-size: 1rem;
font-weight: 400;
background-color: $brand-primary;
padding: 0.75rem 1rem;
border-radius: 3px;
box-shadow: 0 1px 3px rgba(#32325d, 0.1), 0 1px 1px rgba(#32325d, 0.05),
0 0 1px rgba(#32325d, 0.03);
transition: box-shadow 150ms ease, transform 150ms ease, background-color 150ms ease;
&[disabled] {
cursor: not-allowed;
opacity: 0.5;
}
&.loading {
transform: translateY(-1px);
background-color: darken($brand-primary, 7%);
box-shadow: 0 1px 3px rgba(#32325d, 0.1), 0 1px 1px rgba(#32325d, 0.05),
0 0 1px rgba(#32325d, 0.03);
}
&:not([disabled]):not(.loading) {
&:hover,
&:focus {
transform: translateY(-2px);
z-index: 2;
box-shadow: 0 7px 14px rgba(232, 234, 237, 0.5), 0 3px 6px rgba(232, 234, 237, 0.5);
}
&:active {
transform: translateY(-1px);
background-color: darken($brand-primary, 3.5%);
box-shadow: 0 1px 3px rgba(#32325d, 0.1), 0 1px 1px rgba(#32325d, 0.05),
0 0 1px rgba(#32325d, 0.03);
}
}
&-spinner-wrapper {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
align-items: center;
> .Spinner {
margin-right: 1rem;
}
}
}

View File

@ -0,0 +1,34 @@
import React from 'react';
import { Spinner } from 'components/ui';
import './PrimaryButton.scss';
interface Props {
className?: string;
disabled?: boolean;
text?: string;
loading?: boolean;
loadingTxt?: string;
onClick(): void;
}
export default class PrimaryButton extends React.Component<Props> {
public render() {
const { text, disabled, className, loading, loadingTxt, onClick } = this.props;
return (
<button
className={`PrimaryButton ${loading ? 'loading' : ''} ${className}`}
disabled={disabled}
onClick={onClick}
>
{loading ? (
<div className="PrimaryButton-spinner-wrapper">
<Spinner light={true} />
<span>{loadingTxt}</span>
</div>
) : (
text
)}
</button>
);
}
}

View File

@ -0,0 +1,32 @@
@import 'common/sass/variables';
@import 'common/sass/mixins';
.SecondaryButton {
@include reset-button;
display: block;
color: #333;
font-size: 1rem;
font-weight: 400;
background-color: white;
padding: 0.75rem 1rem;
border-radius: 3px;
box-shadow: 0 1px 3px rgba(#32325d, 0.1), 0 1px 1px rgba(#32325d, 0.05),
0 0 1px rgba(#32325d, 0.03);
transition: box-shadow 150ms ease, transform 150ms ease, background-color 150ms ease;
&:not(.is-disabled) {
&:hover,
&:focus {
transform: translateY(-2px);
z-index: 2;
box-shadow: 0 7px 14px rgba(232, 234, 237, 0.5), 0 3px 6px rgba(232, 234, 237, 0.5);
}
}
&:active {
transform: translateY(-1px);
background-color: darken(white, 3.5%);
box-shadow: 0 1px 3px rgba(#32325d, 0.1), 0 1px 1px rgba(#32325d, 0.05),
0 0 1px rgba(#32325d, 0.03);
}
}

View File

@ -0,0 +1,22 @@
import React from 'react';
import { Spinner } from 'components/ui';
import './SecondaryButton.scss';
interface Props {
className?: string;
disabled?: boolean;
text?: string;
loading?: boolean;
onClick(): void;
}
export default class SecondaryButton extends React.Component<Props> {
public render() {
const { text, disabled, className, loading, onClick } = this.props;
return (
<button className={className + ' SecondaryButton'} disabled={disabled} onClick={onClick}>
{loading ? <Spinner light={true} /> : text}
</button>
);
}
}

View File

@ -14,20 +14,30 @@ $speed: 500ms;
} }
} }
@mixin decrypt-title {
text-align: center;
line-height: 1;
margin: 0 0 3rem;
font-weight: normal;
animation: decrypt-enter $speed ease 1;
}
.WalletDecrypt { .WalletDecrypt {
position: relative; position: relative;
&-header {
margin-bottom: 1.5rem;
}
&-wallets { &-wallets {
&-title { &-title {
@include decrypt-title; text-align: center;
font-size: 24px;
line-height: 1;
margin: 0;
font-weight: normal;
animation: decrypt-enter $speed ease 1;
}
&-desc {
text-align: center;
font-size: 16px;
line-height: 1;
margin: 0;
margin-top: 0.5rem;
font-weight: 400;
animation: decrypt-enter $speed ease 1;
} }
&-row { &-row {
@ -54,11 +64,23 @@ $speed: 500ms;
&-decrypt { &-decrypt {
position: relative; position: relative;
text-align: center;
padding-bottom: $space;
@media (max-width: $screen-md) { &-title {
padding-bottom: $space * 2; text-align: center;
font-size: 24px;
line-height: 1;
margin: 0;
font-weight: normal;
animation: decrypt-enter $speed ease 1;
}
&-desc {
text-align: center;
font-size: 16px;
line-height: 1;
margin: 0;
margin-top: 0.5rem;
font-weight: 400;
animation: decrypt-enter $speed ease 1;
} }
&-back { &-back {
@ -93,13 +115,9 @@ $speed: 500ms;
} }
} }
&-title {
@include decrypt-title;
}
&-form { &-form {
max-width: 360px;
margin: 0 auto; margin: 0 auto;
width: 516px;
} }
&-label { &-label {
opacity: 0.8; opacity: 0.8;

View File

@ -51,6 +51,7 @@ import { wikiLink as paritySignerHelpLink } from 'libs/wallet/non-deterministic/
import './WalletDecrypt.scss'; import './WalletDecrypt.scss';
import { withRouter, RouteComponentProps } from 'react-router'; import { withRouter, RouteComponentProps } from 'react-router';
import { Errorable } from 'components'; import { Errorable } from 'components';
import { getNetworkConfig } from 'selectors/config';
interface OwnProps { interface OwnProps {
hidden?: boolean; hidden?: boolean;
@ -72,6 +73,7 @@ interface StateProps {
computedDisabledWallets: DisabledWallets; computedDisabledWallets: DisabledWallets;
isWalletPending: AppState['wallet']['isWalletPending']; isWalletPending: AppState['wallet']['isWalletPending'];
isPasswordPending: AppState['wallet']['isPasswordPending']; isPasswordPending: AppState['wallet']['isPasswordPending'];
networkName: string;
} }
type Props = OwnProps & StateProps & DispatchProps & RouteComponentProps<{}>; type Props = OwnProps & StateProps & DispatchProps & RouteComponentProps<{}>;
@ -248,22 +250,28 @@ const WalletDecrypt = withRouter<Props>(
return ( return (
<div className="WalletDecrypt-decrypt"> <div className="WalletDecrypt-decrypt">
<button className="WalletDecrypt-decrypt-back" onClick={this.clearWalletChoice}> <div className="WalletDecrypt-header">
<i className="fa fa-arrow-left" /> {translate('CHANGE_WALLET')} <h2 className="WalletDecrypt-decrypt-title">
</button> {translate('UNLOCK_DEVICE', { $device: translateRaw(selectedWallet.lid) })}
<h2 className="WalletDecrypt-decrypt-title"> </h2>
{!selectedWallet.isReadOnly && 'Unlock your'} {translate(selectedWallet.lid)} {this.props.showGenerateLink && (
</h2> <p className="WalletDecrypt-decrypt-desc">
{translate('UNLOCK_DEVICE_NEXT_STEP', { $network: this.props.networkName })}{' '}
</p>
)}
</div>
<section className="WalletDecrypt-decrypt-form"> <section className="WalletDecrypt-decrypt-form">
<Errorable <Errorable
errorMessage={`Oops, looks like ${translateRaw( errorMessage={`${translate('ERROR_DEVICE_NOT_SUPPORTED', {
selectedWallet.lid $device: translateRaw(selectedWallet.lid)
)} is not supported by your browser`} })}`}
onError={this.clearWalletChoice} onError={this.clearWalletChoice}
shouldCatch={selectedWallet.lid === this.WALLETS.paritySigner.lid} shouldCatch={selectedWallet.lid === this.WALLETS.paritySigner.lid}
> >
<selectedWallet.component <selectedWallet.component
value={this.state.value} value={this.state.value}
clearWalletChoice={this.clearWalletChoice}
onChange={this.onChange} onChange={this.onChange}
onUnlock={(value: any) => { onUnlock={(value: any) => {
if (selectedWallet.redirect) { if (selectedWallet.redirect) {
@ -299,7 +307,17 @@ const WalletDecrypt = withRouter<Props>(
return ( return (
<div className="WalletDecrypt-wallets"> <div className="WalletDecrypt-wallets">
<h2 className="WalletDecrypt-wallets-title">{translate('DECRYPT_ACCESS')}</h2> <div className="WalletDecrypt-header">
<h2 className="WalletDecrypt-wallets-title">{translate('DECRYPT_ACCESS')}</h2>
{this.props.showGenerateLink && (
<p className="WalletDecrypt-wallets-desc">
{translate('DONT_HAVE_WALLET_PROMPT')}{' '}
<span>
<Link to="/generate">{translate('Create One.')}</Link>
</span>
</p>
)}
</div>
<div className="WalletDecrypt-wallets-row"> <div className="WalletDecrypt-wallets-row">
{SECURE_WALLETS.map((walletType: SecureWalletName) => { {SECURE_WALLETS.map((walletType: SecureWalletName) => {
@ -355,12 +373,6 @@ const WalletDecrypt = withRouter<Props>(
); );
})} })}
</div> </div>
{this.props.showGenerateLink && (
<div className="WalletDecrypt-wallets-generate">
<Link to="/generate">{translate('DONT_HAVE_WALLET_PROMPT')}</Link>
</div>
)}
</div> </div>
); );
} }
@ -437,8 +449,8 @@ const WalletDecrypt = withRouter<Props>(
return; return;
} }
// some components (TrezorDecrypt) don't take an onChange prop, and thus // some components (TrezorDecrypt) don't take an onChange prop, so
// this.state.value will remain unpopulated. in this case, we can expect // this.state.value will remain unpopulated. In this case, we can expect
// the payload to contain the unlocked wallet info. // the payload to contain the unlocked wallet info.
const unlockValue = value && !isEmpty(value) ? value : payload; const unlockValue = value && !isEmpty(value) ? value : payload;
this.WALLETS[selectedWalletKey].unlock(unlockValue); this.WALLETS[selectedWalletKey].unlock(unlockValue);
@ -468,7 +480,8 @@ function mapStateToProps(state: AppState, ownProps: Props) {
return { return {
computedDisabledWallets, computedDisabledWallets,
isWalletPending: state.wallet.isWalletPending, isWalletPending: state.wallet.isWalletPending,
isPasswordPending: state.wallet.isPasswordPending isPasswordPending: state.wallet.isPasswordPending,
networkName: getNetworkConfig(state).name
}; };
} }

View File

@ -9,6 +9,8 @@
&-error { &-error {
opacity: 0; opacity: 0;
transition: none; transition: none;
border-radius: 3px;
margin-bottom: 2.5rem;
&.is-showing { &.is-showing {
opacity: 1; opacity: 1;
@ -28,4 +30,38 @@
margin-right: 16px; margin-right: 16px;
} }
} }
&-illustration {
margin-bottom: 2rem;
width: 128px;
}
&-tip {
&-wrapper {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
margin: auto;
margin-bottom: 3rem;
max-width: 360px;
> p {
font-size: 1rem;
font-weight: 400;
color: #a8adb3;
text-align: left;
&:first-child {
color: #333;
margin-right: 0.5rem;
}
}
}
}
&-btn {
&-wrapper {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
}
}
} }

View File

@ -1,20 +1,23 @@
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import ledger from 'ledgerco'; import ledger from 'ledgerco';
import translate, { translateRaw } from 'translations'; import { translateRaw } from 'translations';
import DeterministicWalletsModal from './DeterministicWalletsModal'; import DeterministicWalletsModal from './DeterministicWalletsModal';
import UnsupportedNetwork from './UnsupportedNetwork'; import UnsupportedNetwork from './UnsupportedNetwork';
import { LedgerWallet } from 'libs/wallet'; import { LedgerWallet } from 'libs/wallet';
import { Spinner, NewTabLink } from 'components/ui'; import { NewTabLink } from 'components/ui';
import { PrimaryButton, SecondaryButton } from 'components';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { AppState } from 'reducers'; import { AppState } from 'reducers';
import { SecureWalletName, ledgerReferralURL } from 'config'; import { SecureWalletName } from 'config';
import { getPaths, getSingleDPath } from 'selectors/config/wallet'; import { getPaths, getSingleDPath } from 'selectors/config/wallet';
import { getNetworkConfig } from 'selectors/config'; import { getNetworkConfig } from 'selectors/config';
import { NetworkConfig } from 'types/network'; import { NetworkConfig } from 'types/network';
import img from 'assets/images/ledger-nano-illustration.svg';
import './LedgerNano.scss'; import './LedgerNano.scss';
interface OwnProps { interface OwnProps {
onUnlock(param: any): void; onUnlock(param: any): void;
clearWalletChoice(): void;
} }
interface StateProps { interface StateProps {
@ -57,8 +60,7 @@ class LedgerNanoSDecryptClass extends PureComponent<Props, State> {
} }
public render() { public render() {
const { network } = this.props; const { dPath, publicKey, chainCode, error, isLoading } = this.state;
const { dPath, publicKey, chainCode, error, isLoading, showTip } = this.state;
const showErr = error ? 'is-showing' : ''; const showErr = error ? 'is-showing' : '';
if (!dPath) { if (!dPath) {
@ -78,37 +80,29 @@ class LedgerNanoSDecryptClass extends PureComponent<Props, State> {
return ( return (
<div className="LedgerDecrypt"> <div className="LedgerDecrypt">
<button {error && (
className="LedgerDecrypt-decrypt btn btn-primary btn-lg btn-block" <div className={`LedgerDecrypt-error alert alert-danger ${showErr}`}>{error}</div>
onClick={this.handleNullConnect}
disabled={isLoading}
>
{isLoading ? (
<div className="LedgerDecrypt-message">
<Spinner light={true} />
{translate('WALLET_UNLOCKING')}
</div>
) : (
translate('ADD_LEDGER_SCAN')
)}
</button>
<NewTabLink className="LedgerDecrypt-buy btn btn-sm btn-default" href={ledgerReferralURL}>
{translate('LEDGER_REFERRAL_2')}
</NewTabLink>
<div className={`LedgerDecrypt-error alert alert-danger ${showErr}`}>{error || '-'}</div>
{showTip && (
<p className="LedgerDecrypt-tip">{translate('LEDGER_TIP', { $network: network.unit })}</p>
)} )}
<img src={img} alt="Ledger nano illustration" className="LedgerDecrypt-illustration" />
<div className="LedgerDecrypt-help"> <div className="LedgerDecrypt-tip-wrapper">
<NewTabLink href="https://support.ledgerwallet.com/hc/en-us/articles/115005200009"> <p>Tip: </p>
{translate('HELP_ARTICLE_1')} <p>Make sure youve enabled browser support in settings on your device.</p>
</NewTabLink> </div>
<div className="LedgerDecrypt-btn-wrapper">
<SecondaryButton
text="Back"
onClick={this.props.clearWalletChoice}
className="LedgerDecrypt-btn"
/>
<div className="flex-spacer" />
<PrimaryButton
text="Connect"
onClick={this.handleNullConnect}
loading={isLoading}
loadingTxt={translateRaw('WALLET_UNLOCKING')}
className="LedgerDecrypt-btn"
/>
</div> </div>
<DeterministicWalletsModal <DeterministicWalletsModal
isOpen={!!publicKey && !!chainCode} isOpen={!!publicKey && !!chainCode}
publicKey={publicKey} publicKey={publicKey}

View File

@ -15,18 +15,17 @@
.WalletButton { .WalletButton {
position: relative; position: relative;
flex: 1; flex: 1;
height: 155px; min-width: 180px;
max-width: 230px; padding: 1.5rem;
min-width: 200px;
padding: 25px 15px;
margin: 0 $space-md $space; margin: 0 $space-md $space;
background: #fff; background: #fff;
box-shadow: 0 1px 4px rgba(#000, 0.2); border: 1px solid #e8eaed;
border-radius: 8px; border-radius: 6px;
box-shadow: 0 2px 4px rgba(#e8eaed, 0.5), 0 1px 1px rgba(#e8eaed, 0.5);
text-align: center; text-align: center;
cursor: pointer; cursor: pointer;
transition: transform 150ms ease, box-shadow 150ms ease; transition: transform 150ms ease, box-shadow 150ms ease, background-color 150ms ease;
animation: wallet-button-enter 400ms ease 1; animation: wallet-button-enter 400ms ease 1;
animation-fill-mode: backwards; animation-fill-mode: backwards;
@ -41,11 +40,9 @@
&:focus { &:focus {
transform: translateY(-2px); transform: translateY(-2px);
z-index: 2; z-index: 2;
box-shadow: 0 1px 4px rgba(#000, 0.12), 0 4px 6px rgba(#000, 0.12); box-shadow: 0 7px 14px rgba(232, 234, 237, 0.5), 0 3px 6px rgba(232, 234, 237, 0.5);
.WalletButton-title { .WalletButton-title {
color: $brand-primary;
&-icon { &-icon {
opacity: 1; opacity: 1;
} }
@ -54,7 +51,8 @@
&:active { &:active {
transform: translateY(-1px); transform: translateY(-1px);
box-shadow: 0 1px 2px rgba(#000, 0.2), 0 3px 4px rgba(#000, 0.2); background-color: #fafbfc;
box-shadow: 0 2px 4px rgba(#e8eaed, 0.5), 0 1px 1px rgba(#e8eaed, 0.5);
} }
} }
@ -76,12 +74,11 @@
justify-content: center; justify-content: center;
align-items: center; align-items: center;
font-size: $font-size-medium; font-size: $font-size-medium;
margin-bottom: $space * 1.25;
transition: color 150ms ease; transition: color 150ms ease;
&-icon { &-icon {
margin-right: 8px; max-height: 60px;
max-height: 26px; margin-bottom: 1rem;
opacity: 0.8; opacity: 0.8;
} }
} }
@ -100,8 +97,8 @@
&-icons { &-icons {
position: absolute; position: absolute;
bottom: 5px; top: 0.5rem;
right: 5px; right: 0.5rem;
&-icon { &-icon {
position: relative; position: relative;
@ -141,13 +138,16 @@
height: 105px; height: 105px;
max-width: 180px; max-width: 180px;
min-width: 160px; min-width: 160px;
margin: 0 $space-sm $space-md; padding: 1.5rem;
padding: 20px 15px;
.WalletButton { .WalletButton {
&-title { &-title {
font-size: $font-size-bump; font-size: $font-size-base;
margin-bottom: $space-sm; top: 50%;
left: 50%;
position: absolute;
transform: translate(-50%, -50%);
white-space: nowrap;
} }
&-icons { &-icons {

View File

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import classnames from 'classnames'; import classnames from 'classnames';
import { NewTabLink, Tooltip } from 'components/ui'; import { Tooltip } from 'components/ui';
import './WalletButton.scss'; import './WalletButton.scss';
import { WalletName } from 'config'; import { WalletName } from 'config';
@ -35,17 +35,7 @@ type Props = OwnProps & StateProps;
export class WalletButton extends React.PureComponent<Props> { export class WalletButton extends React.PureComponent<Props> {
public render() { public render() {
const { const { name, icon, helpLink, isSecure, isReadOnly, isDisabled, disableReason } = this.props;
name,
description,
example,
icon,
helpLink,
isSecure,
isReadOnly,
isDisabled,
disableReason
} = this.props;
const icons: Icon[] = []; const icons: Icon[] = [];
if (isReadOnly) { if (isReadOnly) {
@ -90,32 +80,26 @@ export class WalletButton extends React.PureComponent<Props> {
aria-disabled={isDisabled} aria-disabled={isDisabled}
> >
<div className="WalletButton-inner"> <div className="WalletButton-inner">
{icon && <img className="WalletButton-title-icon" src={icon} alt={name + ' logo'} />}
<div className="WalletButton-title"> <div className="WalletButton-title">
{icon && <img className="WalletButton-title-icon" src={icon} alt={name + ' logo'} />}
<span>{name}</span> <span>{name}</span>
</div> </div>
{description && ( {/* {description && (
<div className="WalletButton-description" aria-label="description"> <div className="WalletButton-description" aria-label="description">
{description} {description}
</div> </div>
)} )} */}
{example && ( {/* {example && (
<div className="WalletButton-example" aria-label="example" aria-hidden={true}> <div className="WalletButton-example" aria-label="example" aria-hidden={true}>
{example} {example}
</div> </div>
)} )} */}
<div className="WalletButton-icons"> <div className="WalletButton-icons">
{icons.map(i => ( {icons.map(i => (
<span className="WalletButton-icons-icon" key={i.icon} onClick={this.stopPropogation}> <span className="WalletButton-icons-icon" key={i.icon} onClick={this.stopPropogation}>
{i.href ? ( {!i.href && <i className={`fa fa-${i.icon}`} aria-label={i.arialabel} />}
<NewTabLink href={i.href} onClick={this.stopPropogation} aria-label={i.arialabel}>
<i className={`fa fa-${i.icon}`} />
</NewTabLink>
) : (
<i className={`fa fa-${i.icon}`} aria-label={i.arialabel} />
)}
{!isDisabled && <Tooltip size="sm">{i.tooltip}</Tooltip>} {!isDisabled && <Tooltip size="sm">{i.tooltip}</Tooltip>}
</span> </span>
))} ))}

View File

@ -25,3 +25,5 @@ export { default as ElectronNav } from './ElectronNav';
export { default as AddressBookTable } from './AddressBookTable'; export { default as AddressBookTable } from './AddressBookTable';
export { default as Errorable } from './Errorable'; export { default as Errorable } from './Errorable';
export { default as AppAlphaNotice } from './AppAlphaNotice'; export { default as AppAlphaNotice } from './AppAlphaNotice';
export { default as PrimaryButton } from './PrimaryButton';
export { default as SecondaryButton } from './SecondaryButton';

View File

@ -1,9 +1,8 @@
import React from 'react'; // For ANNOUNCEMENT_MESSAGE jsx // import React from 'react'; // For ANNOUNCEMENT_MESSAGE jsx
import { getValues } from '../utils/helpers'; import { getValues } from '../utils/helpers';
import packageJson from '../../package.json'; import packageJson from '../../package.json';
import { GasPriceSetting } from 'types/network'; import { GasPriceSetting } from 'types/network';
import { makeExplorer } from 'utils/helpers'; import { makeExplorer } from 'utils/helpers';
import NewTabLink from 'components/ui/NewTabLink';
export const languages = require('./languages.json'); export const languages = require('./languages.json');
export const discordURL = 'https://discord.gg/VSaTXEA'; export const discordURL = 'https://discord.gg/VSaTXEA';

View File

@ -43,7 +43,7 @@ class SendTransaction extends React.Component<Props> {
public render() { public render() {
const { wallet, match, location, history, network } = this.props; const { wallet, match, location, history, network } = this.props;
const currentPath = match.url; const currentPath = match.url;
const EthTabs: Tab[] = [ const Tabs: Tab[] = [
{ {
path: 'send', path: 'send',
name: translate('NAV_SENDETHER'), name: translate('NAV_SENDETHER'),
@ -67,21 +67,6 @@ class SendTransaction extends React.Component<Props> {
name: translate('NAV_ADDRESS_BOOK') name: translate('NAV_ADDRESS_BOOK')
} }
]; ];
const XmrTabs: Tab[] = [
{
path: 'send',
name: translate('NAV_SEND'),
disabled: !!wallet && !!wallet.isReadOnly
},
{
path: 'receive',
name: translate('NAV_REQUESTPAYMENT')
},
{
path: 'swap',
name: translate('NAV_SWAP')
}
];
return ( return (
<TabSection> <TabSection>
@ -90,12 +75,7 @@ class SendTransaction extends React.Component<Props> {
{wallet && ( {wallet && (
<div className="SubTabs row"> <div className="SubTabs row">
<div className="col-sm-8"> <div className="col-sm-8">
<SubTabs <SubTabs tabs={Tabs} match={match} location={location} history={history} />
tabs={network.id === 'XMR' ? XmrTabs : EthTabs}
match={match}
location={location}
history={history}
/>
</div> </div>
<div className="col-sm-8"> <div className="col-sm-8">
<Switch> <Switch>

View File

@ -1,4 +1,5 @@
$ether-navy: #163151; $ether-navy: #163151;
$other-blue: #1eb8e7;
$ether-blue: #0e97c0; $ether-blue: #0e97c0;
$gray-base: #000; $gray-base: #000;
@ -12,7 +13,7 @@ $gray-lightest: #fafafa;
$border-idle: #e5ecf3; $border-idle: #e5ecf3;
$border-disabled: #e6e6e6; $border-disabled: #e6e6e6;
$brand-primary: #007896; $brand-primary: #1097c0;
$brand-success: #5dba5a; $brand-success: #5dba5a;
$brand-info: $ether-navy; $brand-info: $ether-navy;
$brand-warning: #ff9800; $brand-warning: #ff9800;

View File

@ -69,7 +69,7 @@
"SIDEBAR_DONATE": "Donate ", "SIDEBAR_DONATE": "Donate ",
"SIDEBAR_THANKS": "THANK YOU!!! ", "SIDEBAR_THANKS": "THANK YOU!!! ",
"SIDEBAR_DISPLAY_ADDR": "Display address on $wallet", "SIDEBAR_DISPLAY_ADDR": "Display address on $wallet",
"DECRYPT_ACCESS": "How would you like to access your wallet? ", "DECRYPT_ACCESS": "Select a Method to Unlock Your Wallet",
"X_LEDGER": "Ledger ", "X_LEDGER": "Ledger ",
"ADD_LEDGER_SCAN": "Connect to Ledger Wallet ", "ADD_LEDGER_SCAN": "Connect to Ledger Wallet ",
"ADD_METAMASK": "Connect to MetaMask ", "ADD_METAMASK": "Connect to MetaMask ",
@ -613,6 +613,10 @@
"PAYMENT_ID_TITLE": "Payment ID", "PAYMENT_ID_TITLE": "Payment ID",
"PAYMENT_ID_PLACEHOLDER": "Enter a Payment ID", "PAYMENT_ID_PLACEHOLDER": "Enter a Payment ID",
"PRIVACY_RADIO": "Privacy", "PRIVACY_RADIO": "Privacy",
"PRIORITY_RADIO": "Priority" "PRIORITY_RADIO": "Priority",
"UNLOCK_DEVICE":"Unlock Your $device",
"ERROR_DEVICE_NOT_SUPPORTED": "$device is not supported by your browser",
"CONNECT_TO_DEVICE": "Connect to $device",
"UNLOCK_DEVICE_NEXT_STEP": "After, open the $network App on your device."
} }
} }

View File

@ -27,9 +27,9 @@
"./common/", "./common/",
"./electron-app/", "./electron-app/",
"./shared/", "./shared/",
"spec", "spec"
"./node_modules/types-rlp/index.d.ts", // "./node_modules/types-rlp/index.d.ts",
"./node_modules/mycrypto-shepherd/dist/lib/types/btoa.d.ts" // "./node_modules/mycrypto-shepherd/dist/lib/types/btoa.d.ts"
], ],
"awesomeTypescriptLoaderOptions": { "awesomeTypescriptLoaderOptions": {
"transpileOnly": true "transpileOnly": true