Skip to content

Commit

Permalink
ledger webusb transport test implementation #224
Browse files Browse the repository at this point in the history
  • Loading branch information
TobiaszCudnik committed Apr 16, 2019
1 parent f5be315 commit 4fe7e7c
Show file tree
Hide file tree
Showing 10 changed files with 230 additions and 340 deletions.
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
"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
51 changes: 12 additions & 39 deletions src/components/ConfirmTxStatusFooter.tsx
Original file line number Diff line number Diff line change
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 @@ -189,40 +184,18 @@ class ConfirmTxStatusFooter extends React.Component<DecoratedProps> {
}
/>
) : 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
12 changes: 10 additions & 2 deletions src/components/Link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ type BaseProps = Overwrite<
>;

interface Props extends BaseProps {
onClick?(ev: React.MouseEvent<HTMLAnchorElement>): void;
children: React.ReactElement<
React.AnchorHTMLAttributes<HTMLAnchorElement> & {
component: React.ReactType;
Expand Down Expand Up @@ -70,7 +71,7 @@ class Link extends React.Component<Props> {
ev.preventDefault();
store.navigateTo(routeLink);
}
}
};

render() {
const {
Expand All @@ -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 => {
onClick(e);
this.handleClick(e);
}
: this.handleClick
};
}

Expand Down
15 changes: 6 additions & 9 deletions src/containers/onboarding/AddAccountPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,16 +65,12 @@ class AddAccountPage extends React.Component<Props> {
handleBeforeNavigate = () => {
const { onboardingStore } = this.injected;
onboardingStore.reset();
}

componentWillMount() {
// establish communication with a ledger
this.injected.ledgerStore.open();
}
};

componentWillUnmount() {
this.injected.ledgerStore.close();
}
preserveLedgerContext = (event: React.MouseEvent<HTMLAnchorElement>) => {
this.injected.ledgerStore.open()
// this.injected.ledgerStore.eventContext = event;
};

render() {
const { classes, langStore, walletStore } = this.injected;
Expand Down Expand Up @@ -191,6 +187,7 @@ class AddAccountPage extends React.Component<Props> {
<Link
route={onboardingLedgerAccount}
onBeforeNavigate={this.handleBeforeNavigate}
onClick={this.preserveLedgerContext}
>
<ListItem button={true}>
<ListItemText
Expand Down
57 changes: 36 additions & 21 deletions src/containers/onboarding/LedgerAccountPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,9 @@ const messages = defineMessages({
unsupportedBrowser: {
id: 'onboarding-ledger-account.unsupported-browser',
description:
'Message when trying to use a browser that doesn\'t support Ledger devices',
"Message when trying to use a browser that doesn't support Ledger devices",
defaultMessage:
'Your browser doesn\'t support using a Ledger device. If you wish to access this feature, ' +
"Your browser doesn't support using a Ledger device. If you wish to access this feature, " +
'you could try again with Google Chrome. It is a browser known to implement support for this.'
},
statusConnecting: {
Expand Down Expand Up @@ -117,6 +117,8 @@ class AccountData {
this.data = resp;
});
} catch (ex) {
// TODO debug remove
console.log(ex);
// Ignore failures
}
}
Expand All @@ -140,17 +142,20 @@ class LedgerAccountPage extends React.Component<DecoratedProps> {
return this.props as PropsInjected;
}

componentWillMount() {
this.injected.ledgerStore.open();
}
// async componentWillMount() {
// const { ledgerStore } = this.injected;
//
// // TODO call inside of a click handler
// await ledgerStore.open();
// }

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

render() {
const { intl, classes, ledgerStore } = this.injected;
const { deviceId } = ledgerStore;
const { transport } = ledgerStore;
const { selectedAccount, countdownSeconds } = this;

this.loadAccounts();
Expand All @@ -164,15 +169,15 @@ class LedgerAccountPage extends React.Component<DecoratedProps> {
defaultMessage="Import a Ledger account"
/>
</ModalPaperHeader>
{ledgerStore.hasSupport === false ? (
{/* NO SUPPORT */ ledgerStore.hasSupport === false ? (
<Grid container={true} className={classes.content} spacing={16}>
<Grid item={true} xs={12}>
<Typography
children={intl.formatMessage(messages.unsupportedBrowser)}
/>
</Grid>
</Grid>
) : deviceId === null ? (
) : /* SETUP THE DEVICE */ transport === null ? (
<Grid container={true} className={classes.content} spacing={16}>
<Grid item={true} xs={12}>
<Typography
Expand All @@ -191,7 +196,7 @@ class LedgerAccountPage extends React.Component<DecoratedProps> {
{this.getConnectingHelpMsg()}
</Grid>
</Grid>
) : selectedAccount !== null ? (
) : /* SELECT ACCOUNT */ selectedAccount !== null ? (
<React.Fragment>
<List>
<ListItem key={selectedAccount.slot} divider={true}>
Expand Down Expand Up @@ -237,7 +242,7 @@ class LedgerAccountPage extends React.Component<DecoratedProps> {
</Grid>
</React.Fragment>
) : (
<List>
/* CONFIRM IMPORT */ <List>
{this.accounts.map((acc, idx) => (
<ListItem
key={acc.slot}
Expand Down Expand Up @@ -313,11 +318,11 @@ class LedgerAccountPage extends React.Component<DecoratedProps> {
window.clearInterval(this.countdownId);
this.countdownId = null;
}
}
};

private async confirmImport(account: AccountData) {
const { walletStore, routerStore, ledgerStore } = this.injected;
const { deviceId } = ledgerStore;
const { device } = ledgerStore;

if (account.data !== null) {
const { address: accountAddress } = account.data;
Expand All @@ -344,7 +349,8 @@ class LedgerAccountPage extends React.Component<DecoratedProps> {
accountAddress,
{
type: AccountType.LEDGER,
hwId: deviceId,
// TODO check if `serialNumber` is ok
hwId: device!.serialNumber,
hwSlot: account.slot
},
true
Expand All @@ -364,30 +370,39 @@ class LedgerAccountPage extends React.Component<DecoratedProps> {
*/
@action
private loadAccounts = () => {
const accountsToLoad = 5;
const { walletStore, ledgerStore } = this.injected;
const { deviceId } = ledgerStore;

// wait for a read transport
if (!deviceId) {
if (!ledgerStore.transport) {
return;
}

// only one thread
if (this.loadingAccounts || this.accounts.length === accountsToLoad) {
if (this.loadingAccounts) {
return;
}

this.loadingAccounts = true;

debugger

// TODO tmp
// const accountsToLoad = 5;
const accountsToLoad = 1;
const { device } = ledgerStore;

this.selectedAccount = null;
// TODO dispose the previous one
this.accounts = observable.array();
if (device === null) {
return;
}

const importedAccounts = [...walletStore.accounts.values()]
.filter(({ type }) => type === AccountType.LEDGER)
.filter(({ hwId }) => hwId === deviceId);
// TODO compare account IDs
.filter(({ hwId }) => hwId === device.serialNumber);

for (let slot = 0; this.accounts.length < accountsToLoad; slot++) {
// TODO compare account IDs
const isImported =
importedAccounts.filter(({ hwSlot }) => hwSlot === slot).length > 0;

Expand All @@ -398,7 +413,7 @@ class LedgerAccountPage extends React.Component<DecoratedProps> {
}

this.loadingAccounts = false;
}
};
}

export default stylesDecorator(injectIntl(LedgerAccountPage));
5 changes: 1 addition & 4 deletions src/containers/wallet/AccountOverview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -340,10 +340,7 @@ class AccountOverview extends React.Component<DecoratedProps, State> {
)}
onExpand={this.handleExpand}
getSendLinkProps={this.getSendLinkProps}
key={
transaction.id +
(transaction.confirmations ? 'C' : 'U')
}
key={transaction.id}
tx={transaction}
explorerUrl={this.account.config.explorer_url}
handleContactEdit={this.handleContactEdit}
Expand Down
Loading

0 comments on commit 4fe7e7c

Please sign in to comment.