Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

webusb transport for Ledger Nano #227

Merged
merged 10 commits into from Jun 13, 2019
2 changes: 1 addition & 1 deletion desktop/package.json
@@ -1,7 +1,7 @@
{
"name": "rise-wallet-desktop",
"description": "Rise Wallet",
"version": "1.1.3",
"version": "1.2.0-beta2",
"author": "rise.vision",
"engines": {
"yarn": "^1.10.1"
Expand Down
6 changes: 4 additions & 2 deletions package.json
@@ -1,15 +1,17 @@
{
"name": "rise-wallet",
"version": "1.1.3",
"version": "1.2.0-beta2",
"engines": {
"yarn": "^1.10.1"
},
"license": "GPL-3.0-only",
"dependencies": {
"@babel/polyfill": "^7.0.0",
"@ledgerhq/hw-transport-u2f": "^4.48.0",
"@ledgerhq/hw-transport-webusb": "^4.48.0",
"@material-ui/core": "^3.7.0",
"@material-ui/icons": "^3.0.1",
"@types/ledgerhq__hw-transport": "^4.21.1",
"@types/w3c-web-usb": "^1.0.3",
"async-mutex": "^0.1.3",
"babel-cli": "^6.26.0",
"babel-plugin-syntax-dynamic-import": "^6.18.0",
Expand Down
55 changes: 14 additions & 41 deletions src/components/ConfirmTxStatusFooter.tsx
Expand Up @@ -73,11 +73,6 @@ const messages = defineMessages({
id: 'confirm-tx-status-footer.success-icon-aria',
description: 'Success status icon label for accessibility',
defaultMessage: 'Success indicator icon'
},
ledgerDoubleConfirmLink: {
id: 'confirm-tx-status-footer.ledger-dobule-confirm-link',
description: 'text content for the ledger issue link',
defaultMessage: 'known issue'
}
});

Expand Down Expand Up @@ -181,48 +176,26 @@ class ConfirmTxStatusFooter extends React.Component<DecoratedProps> {
/>
) : type === 'ledger-not-connected' ? (
<FormattedMessage
id="confirm-tx-status-footer.ledger-not-connected-msg"
id="confirm-tx-status-footer.ledger-not-connected-msg-v2"
description="Message for when the Ledger device isn't connected."
defaultMessage={
'Please connect your Ledger and open the RISE app on it. ' +
'Connect your Ledger, open the RISE app and click Discover Device below.' +
'Waiting for Ledger...'
}
/>
) : type === 'ledger-confirming' ? (
<React.Fragment>
<FormattedMessage
id="confirm-tx-status-footer.ledger-confirming-msg"
description="Message for when the user needs to confirm the transaction on Ledger."
defaultMessage={
'Please confirm the transaction on your Ledger. Waiting for confirmation... ' +
'({seconds} {seconds, plural,' +
' one {second}' +
' other {seconds}' +
'} remaining)'
}
values={{ seconds: timeout || 0 }}
/>
<br />
<br />
<FormattedMessage
id="confirm-tx-status-footer.ledger-confirming-msg-twice"
description="Double confirmation issue notification."
defaultMessage={
'You may have to confirm twice. Its a {link}.'}
values={{
seconds: timeout || 0,

link: (
<a
href="https://support.ledger.com/hc/en-us/articles/360018810413-U2F-timeout-in-Chrome-browser"
target="_blank"
>
{intl.formatMessage(messages.ledgerDoubleConfirmLink)}
</a>
)
}}
/>
</React.Fragment>
<FormattedMessage
id="confirm-tx-status-footer.ledger-confirming-msg"
description="Message for when the user needs to confirm the transaction on Ledger."
defaultMessage={
'Please confirm the transaction on your Ledger. Waiting for confirmation... ' +
'({seconds} {seconds, plural,' +
' one {second}' +
' other {seconds}' +
'} remaining)'
}
values={{ seconds: timeout || 0 }}
/>
) : null}
</Typography>
</Grid>
Expand Down
127 changes: 71 additions & 56 deletions src/components/LedgerConnectIllustration.tsx
@@ -1,3 +1,4 @@
import { Button } from '@material-ui/core';
import { createStyles, withStyles, WithStyles } from '@material-ui/core/styles';
import * as React from 'react';

Expand Down Expand Up @@ -34,85 +35,99 @@ const styles = createStyles({
}
});

interface Props extends WithStyles<typeof styles> {}
interface Props extends WithStyles<typeof styles> {
onClick(): void;
}

const stylesDecorator = withStyles(styles, {
name: 'LedgerConnectIllustration'
});

class LedgerConnectIllustration extends React.Component<Props> {
render() {
const { classes } = this.props;
const { classes, onClick } = this.props;

// Source SVG optimised with https://jakearchibald.github.io/svgomg/
// and relevant animation groups injected by hand

// tslint:disable:max-line-length
return (
<svg
className={classes.root}
height="90px"
viewBox="160 55 240 90"
aria-hidden={true}
>
<g fill="none" fillRule="evenodd">
<g className={classes.usbCable}>
<g transform="translate(-96 91)" fill="#2F2F2F" stroke="#434242">
<path d="M.5 3.5h199v11H.5z" />
<rect x="187.5" y=".5" width="29" height="17" rx="4" />
<React.Fragment>
<svg
className={classes.root}
height="90px"
viewBox="160 55 240 90"
aria-hidden={true}
>
<g fill="none" fillRule="evenodd">
<g className={classes.usbCable}>
<g transform="translate(-96 91)" fill="#2F2F2F" stroke="#434242">
<path d="M.5 3.5h199v11H.5z" />
<rect x="187.5" y=".5" width="29" height="17" rx="4" />
</g>
<g transform="translate(171 88)" fill="#D8D8D8" stroke="#979797">
<rect x=".5" y=".5" width="24" height="23" rx="2" />
<path d="M.5 3.5h24v17H.5z" />
</g>
<path
d="M176 82.5h-60c-6.351 0-11.5 5.149-11.5 11.5v12c0 6.351 5.149 11.5 11.5 11.5h60a1.5 1.5 0 0 0 1.5-1.5V84a1.5 1.5 0 0 0-1.5-1.5z"
stroke="#434242"
fill="#2F2F2F"
/>
</g>
<rect
fill="#4C575B"
x="212"
y="83"
width="130"
height="34"
rx="1"
/>
<g className={classes.homeScreen} fill="#A7CAED">
<path d="M269.613 109.918h-.933V112h-1.172v-5.688h2.113c.672 0 1.19.15 1.555.45.364.3.547.722.547 1.27 0 .387-.084.71-.252.97-.168.26-.423.465-.764.62l1.23 2.323V112h-1.257l-1.067-2.082zm-.933-.95h.945c.294 0 .522-.074.684-.224.161-.15.242-.356.242-.619 0-.268-.076-.48-.229-.633-.152-.153-.386-.23-.7-.23h-.942v1.707zm6.094 3.032h-1.172v-5.688h1.172V112zm4.95-1.492c0-.222-.079-.391-.235-.51-.157-.118-.438-.243-.844-.375a5.981 5.981 0 0 1-.965-.389c-.646-.349-.969-.819-.969-1.41 0-.307.087-.581.26-.822.173-.24.422-.43.746-.565a2.808 2.808 0 0 1 1.092-.203c.406 0 .768.074 1.086.221.318.147.564.355.74.623.176.268.264.573.264.914h-1.172c0-.26-.082-.463-.246-.607-.164-.145-.395-.217-.691-.217-.287 0-.51.06-.668.182a.573.573 0 0 0-.239.478c0 .185.093.34.28.465.186.125.46.242.822.352.666.2 1.152.449 1.457.746.305.297.457.666.457 1.109 0 .492-.186.878-.559 1.158-.372.28-.873.42-1.504.42-.437 0-.836-.08-1.195-.24-.36-.16-.633-.38-.822-.658a1.686 1.686 0 0 1-.283-.97h1.175c0 .629.375.942 1.125.942.279 0 .496-.056.653-.17a.553.553 0 0 0 .234-.474zm6.304-.973h-2.25v1.524h2.64V112h-3.812v-5.687h3.805v.949h-2.633v1.355h2.25v.918zM274 87h6a5 5 0 0 1 5 5v6a5 5 0 0 1-5 5h-6a5 5 0 0 1-5-5v-6a5 5 0 0 1 5-5zm0 3v2h2v-2h-2zm4 8v2h2v-2h-2zm0-7v1h1v-1h-1zm1-1v1h1v-1h-1zm-2 2v1h1v-1h-1zm2 0v1h1v-1h-1zm-1 1v1h1v-1h-1zm1 1v1h1v-1h-1zm-1 1v1h1v-1h-1zm-1-1v1h1v-1h-1zm-1-1v1h1v-1h-1zm-1 1v1h1v-1h-1zm1 1v1h1v-1h-1zm1 1v1h1v-1h-1zm-1 1v1h1v-1h-1zm-1-1v1h1v-1h-1zm-1-1v1h1v-1h-1zm0 2v1h1v-1h-1zm1 1v1h1v-1h-1zm-1 1v1h1v-1h-1z" />
</g>
<g transform="translate(171 88)" fill="#D8D8D8" stroke="#979797">
<rect x=".5" y=".5" width="24" height="23" rx="2" />
<path d="M.5 3.5h24v17H.5z" />
<g className={classes.rightButton}>
<rect
stroke="#434242"
strokeWidth="2"
fill="#2F2F2F"
x="331"
y="62"
width="18"
height="8"
rx="2"
/>
</g>
<g className={classes.leftButton}>
<rect
stroke="#434242"
strokeWidth="2"
fill="#2F2F2F"
x="205"
y="62"
width="18"
height="8"
rx="2"
/>
</g>
<path
d="M176 82.5h-60c-6.351 0-11.5 5.149-11.5 11.5v12c0 6.351 5.149 11.5 11.5 11.5h60a1.5 1.5 0 0 0 1.5-1.5V84a1.5 1.5 0 0 0-1.5-1.5z"
stroke="#434242"
fill="#2F2F2F"
/>
</g>
<rect fill="#4C575B" x="212" y="83" width="130" height="34" rx="1" />
<g className={classes.homeScreen} fill="#A7CAED">
<path d="M269.613 109.918h-.933V112h-1.172v-5.688h2.113c.672 0 1.19.15 1.555.45.364.3.547.722.547 1.27 0 .387-.084.71-.252.97-.168.26-.423.465-.764.62l1.23 2.323V112h-1.257l-1.067-2.082zm-.933-.95h.945c.294 0 .522-.074.684-.224.161-.15.242-.356.242-.619 0-.268-.076-.48-.229-.633-.152-.153-.386-.23-.7-.23h-.942v1.707zm6.094 3.032h-1.172v-5.688h1.172V112zm4.95-1.492c0-.222-.079-.391-.235-.51-.157-.118-.438-.243-.844-.375a5.981 5.981 0 0 1-.965-.389c-.646-.349-.969-.819-.969-1.41 0-.307.087-.581.26-.822.173-.24.422-.43.746-.565a2.808 2.808 0 0 1 1.092-.203c.406 0 .768.074 1.086.221.318.147.564.355.74.623.176.268.264.573.264.914h-1.172c0-.26-.082-.463-.246-.607-.164-.145-.395-.217-.691-.217-.287 0-.51.06-.668.182a.573.573 0 0 0-.239.478c0 .185.093.34.28.465.186.125.46.242.822.352.666.2 1.152.449 1.457.746.305.297.457.666.457 1.109 0 .492-.186.878-.559 1.158-.372.28-.873.42-1.504.42-.437 0-.836-.08-1.195-.24-.36-.16-.633-.38-.822-.658a1.686 1.686 0 0 1-.283-.97h1.175c0 .629.375.942 1.125.942.279 0 .496-.056.653-.17a.553.553 0 0 0 .234-.474zm6.304-.973h-2.25v1.524h2.64V112h-3.812v-5.687h3.805v.949h-2.633v1.355h2.25v.918zM274 87h6a5 5 0 0 1 5 5v6a5 5 0 0 1-5 5h-6a5 5 0 0 1-5-5v-6a5 5 0 0 1 5-5zm0 3v2h2v-2h-2zm4 8v2h2v-2h-2zm0-7v1h1v-1h-1zm1-1v1h1v-1h-1zm-2 2v1h1v-1h-1zm2 0v1h1v-1h-1zm-1 1v1h1v-1h-1zm1 1v1h1v-1h-1zm-1 1v1h1v-1h-1zm-1-1v1h1v-1h-1zm-1-1v1h1v-1h-1zm-1 1v1h1v-1h-1zm1 1v1h1v-1h-1zm1 1v1h1v-1h-1zm-1 1v1h1v-1h-1zm-1-1v1h1v-1h-1zm-1-1v1h1v-1h-1zm0 2v1h1v-1h-1zm1 1v1h1v-1h-1zm-1 1v1h1v-1h-1z" />
</g>
<g className={classes.rightButton}>
<rect
d="M184 67a3 3 0 0 0-3 3v60a3 3 0 0 0 3 3h246a3 3 0 0 0 3-3V70a3 3 0 0 0-3-3H184zm30 16h126a2 2 0 0 1 2 2v30a2 2 0 0 1-2 2H214a2 2 0 0 1-2-2V85a2 2 0 0 1 2-2zm186 35c-9.941 0-18-8.059-18-18s8.059-18 18-18 18 8.059 18 18-8.059 18-18 18z"
stroke="#434242"
strokeWidth="2"
fill="#2F2F2F"
x="331"
y="62"
width="18"
height="8"
rx="2"
/>
</g>
<g className={classes.leftButton}>
<rect
stroke="#434242"
<path
d="M400 132h222a3 3 0 0 0 3-3V71a3 3 0 0 0-3-3H400c-17.673 0-32 14.327-32 32 0 17.673 14.327 32 32 32zm0-9c-12.703 0-23-10.297-23-23s10.297-23 23-23 23 10.297 23 23-10.297 23-23 23z"
stroke="#AEAEAE"
strokeWidth="2"
fill="#2F2F2F"
x="205"
y="62"
width="18"
height="8"
rx="2"
fill="#C7C7C7"
/>
</g>
<path
d="M184 67a3 3 0 0 0-3 3v60a3 3 0 0 0 3 3h246a3 3 0 0 0 3-3V70a3 3 0 0 0-3-3H184zm30 16h126a2 2 0 0 1 2 2v30a2 2 0 0 1-2 2H214a2 2 0 0 1-2-2V85a2 2 0 0 1 2-2zm186 35c-9.941 0-18-8.059-18-18s8.059-18 18-18 18 8.059 18 18-8.059 18-18 18z"
stroke="#434242"
strokeWidth="2"
fill="#2F2F2F"
/>
<path
d="M400 132h222a3 3 0 0 0 3-3V71a3 3 0 0 0-3-3H400c-17.673 0-32 14.327-32 32 0 17.673 14.327 32 32 32zm0-9c-12.703 0-23-10.297-23-23s10.297-23 23-23 23 10.297 23 23-10.297 23-23 23z"
stroke="#AEAEAE"
strokeWidth="2"
fill="#C7C7C7"
/>
</g>
</svg>
</svg>
<Button onClick={onClick} color="secondary" fullWidth={true}>
Discover Device
</Button>
</React.Fragment>
);
// tslint:enable:max-line-length
}
Expand Down
10 changes: 9 additions & 1 deletion src/components/Link.tsx
Expand Up @@ -16,6 +16,7 @@ interface Props extends BaseProps {
component: React.ReactType;
}
>;
onClick?(ev: React.MouseEvent<HTMLAnchorElement>): void;
}

interface PropsInjected extends Props {
Expand Down Expand Up @@ -79,6 +80,7 @@ class Link extends React.Component<Props> {
queryParams,
onBeforeNavigate,
onAfterNavigate,
onClick,
children,
store,
...passthroughProps
Expand All @@ -91,7 +93,13 @@ class Link extends React.Component<Props> {
overrideProps = {
component: 'a',
href: store.linkUrl(routeLink),
onClick: this.handleClick
// compose onClick if a handler passed
onClick: onClick
? (e: React.MouseEvent<HTMLAnchorElement>) => {
onClick(e);
this.handleClick(e);
}
: this.handleClick
};
}

Expand Down
2 changes: 1 addition & 1 deletion src/components/content/SendCoinsDialogContent.tsx
Expand Up @@ -227,7 +227,7 @@ class SendCoinsDialogContent extends React.Component<DecoratedProps, State> {
handleAmountBlur = () => {
const amountInvalid = !!this.state.amount && !!this.amountError();
const amount = this.state.parsedAmount
? this.state.parsedAmount.unit.toNumber().toString()
? this.state.parsedAmount.unit.toString()
: '';
this.setState({
amountInvalid,
Expand Down
1 change: 1 addition & 0 deletions src/components/content/VoteDelegateDialogContent.tsx
Expand Up @@ -85,6 +85,7 @@ type BaseProps = WithStyles<typeof styles> & DialogContentProps;
interface Props extends BaseProps, ICloseInterruptFormProps {
query: string;
onQueryChange: (query: string) => void;
// TODO rename to onSubmit
onSelect: (delegate: Delegate) => void;
isLoading: boolean;
votedDelegate: null | Delegate;
Expand Down
8 changes: 2 additions & 6 deletions src/containers/onboarding/AddAccountPage.tsx
Expand Up @@ -67,15 +67,10 @@ class AddAccountPage extends React.Component<Props> {
onboardingStore.reset();
}

componentWillMount() {
// establish communication with a ledger
handleAddLedgerClick = (event: React.MouseEvent<HTMLAnchorElement>) => {
this.injected.ledgerStore.open();
}

componentWillUnmount() {
this.injected.ledgerStore.close();
}

render() {
const { classes, langStore, walletStore } = this.injected;
const showClose = [...walletStore.accounts.keys()].length > 0;
Expand Down Expand Up @@ -191,6 +186,7 @@ class AddAccountPage extends React.Component<Props> {
<Link
route={onboardingLedgerAccount}
onBeforeNavigate={this.handleBeforeNavigate}
onClick={this.handleAddLedgerClick}
>
<ListItem button={true}>
<ListItemText
Expand Down