diff --git a/package.json b/package.json index 5a3d2865..4a035bca 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,7 @@ "moment": "^2.22.0", "object-path": "^0.11.4", "qrcode": "^1.2.0", - "query-string": "^6.0.0", + "qs": "^6.5.1", "raven": "^2.5.0", "raven-js": "^3.24.1", "react": "^16.3.1", diff --git a/src/components/CurrentAddress/index.js b/src/components/CurrentAddress/index.js index 86ce82d8..eb3347fd 100644 --- a/src/components/CurrentAddress/index.js +++ b/src/components/CurrentAddress/index.js @@ -4,21 +4,31 @@ import React, { PureComponent } from 'react' import { translate } from 'react-i18next' import styled from 'styled-components' +import noop from 'lodash/noop' + import type { Account } from '@ledgerhq/wallet-common/lib/types' import type { T } from 'types/common' import { rgba } from 'styles/helpers' import Box from 'components/base/Box' +import CopyToClipboard from 'components/base/CopyToClipboard' +import Print from 'components/base/Print' import QRCode from 'components/base/QRCode' +import IconCheck from 'icons/Check' +import IconCopy from 'icons/Copy' import IconInfoCircle from 'icons/InfoCircle' +import IconPrint from 'icons/Print' +import IconShare from 'icons/Share' +import IconShield from 'icons/Shield' const Container = styled(Box).attrs({ borderRadius: 1, alignItems: 'center', bg: p => (p.withQRCode ? 'lightGrey' : 'transparent'), - p: 5, + py: 5, + px: 7, })`` const WrapperAddress = styled(Box).attrs({ @@ -29,6 +39,7 @@ const WrapperAddress = styled(Box).attrs({ })` background: ${p => (p.notValid ? rgba(p.theme.colors.alertRed, 0.05) : 'transparent')}; border: ${p => (p.notValid ? `1px dashed ${rgba(p.theme.colors.alertRed, 0.26)}` : 'none')}; + width: 100%; ` const Address = styled(Box).attrs({ @@ -55,29 +66,91 @@ const Label = styled(Box).attrs({ horizontal: true, })`` +const Footer = styled(Box).attrs({ + horizontal: true, + mt: 5, +})` + text-transform: uppercase; + width: 100%; +` + +const FooterButtonWrapper = styled(Box).attrs({ + color: 'grey', + alignItems: 'center', + justifyContent: 'center', + grow: true, +})` + cursor: pointer; +` + +const FooterButton = ({ + icon, + label, + onClick, +}: { + icon: any, + label: string, + onClick: Function, +}) => ( + + {icon} + + {label} + + +) + type Props = { account: Account, addressVerified: null | boolean, amount: null | string, + onCopy: Function, + onPrint: Function, + onShare: Function, + onVerify: Function, t: T, + withBadge: boolean, + withFooter: boolean, withQRCode: boolean, + withVerify: boolean, } class CurrentAddress extends PureComponent { static defaultProps = { addressVerified: null, amount: null, + onCopy: noop, + onPrint: noop, + onShare: noop, + onVerify: noop, + withBadge: false, + withFooter: false, withQRCode: false, + withVerify: false, } render() { - const { amount, account, t, addressVerified, withQRCode } = this.props + const { + account, + addressVerified, + amount, + onCopy, + onPrint, + onShare, + onVerify, + t, + withBadge, + withFooter, + withQRCode, + withVerify, + ...props + } = this.props const notValid = addressVerified === false return ( - - + + {withQRCode && ( { {account.address} + {withBadge && ( + + + + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam blandit velit egestas leo + tincidunt + + + )} + {withFooter && ( +
+ {withVerify && ( + } label="Verify" onClick={onVerify} /> + )} + ( + } label="Copy" onClick={copy} /> + )} + /> + ( + } + label={isLoading ? '...' : 'Print'} + onClick={print} + /> + )} + /> + } label="Share" onClick={onShare} /> +
+ )}
) } diff --git a/src/components/CurrentAddress/stories.js b/src/components/CurrentAddress/stories.js index 69837cae..5504bc4a 100644 --- a/src/components/CurrentAddress/stories.js +++ b/src/components/CurrentAddress/stories.js @@ -12,8 +12,11 @@ const stories = storiesOf('Components', module) stories.add('CurrentAddress', () => ( )) diff --git a/src/components/base/CopyToClipboard.js b/src/components/base/CopyToClipboard.js index 5f965de7..24d8542c 100644 --- a/src/components/base/CopyToClipboard.js +++ b/src/components/base/CopyToClipboard.js @@ -1,6 +1,11 @@ // @flow -import { clipboard } from 'electron' +let clipboard = null + +if (!process.env.STORYBOOK_ENV) { + const electron = require('electron') + clipboard = electron.clipboard // eslint-disable-line +} type Props = { data: string, @@ -9,7 +14,12 @@ type Props = { function CopyToClipboard(props: Props) { const { render, data } = props - return render(() => clipboard.writeText(data)) + + if (clipboard === null) { + return render() + } + + return render(() => clipboard && clipboard.writeText(data)) } export default CopyToClipboard diff --git a/src/components/base/Print.js b/src/components/base/Print.js index 1c1224e0..bc2f9ceb 100644 --- a/src/components/base/Print.js +++ b/src/components/base/Print.js @@ -1,10 +1,14 @@ // @flow import { PureComponent } from 'react' -import { remote } from 'electron' -import queryString from 'query-string' +import qs from 'qs' -const { BrowserWindow } = remote +let BrowserWindow = null + +if (!process.env.STORYBOOK_ENV) { + const { remote } = require('electron') + BrowserWindow = remote.BrowserWindow // eslint-disable-line +} type Props = { render: Function, @@ -21,6 +25,10 @@ class Print extends PureComponent { } handlePrint = () => { + if (BrowserWindow === null) { + return + } + const { data } = this.props this.setState({ isLoading: true }) @@ -33,22 +41,18 @@ class Print extends PureComponent { }, }) - w.webContents.openDevTools() - const url = __DEV__ ? `http://localhost:${process.env.ELECTRON_WEBPACK_WDS_PORT || ''}` : `file://${__dirname}/index.html` - w.loadURL(`${url}/#/print?${queryString.stringify(data)}`) + w.loadURL(`${url}/#/print?${qs.stringify(data)}`) - w.webContents.on('did-finish-load', () => { - w.on('minimize', () => { - w.webContents.print({}, () => { - w.destroy() - this.setState({ isLoading: false }) - }) - }) - }) + w.webContents.on('did-finish-load', () => + w.on('print-ready', () => { + w.webContents.print({}, () => w.destroy()) + this.setState({ isLoading: false }) + }), + ) } render() { diff --git a/src/components/layout/Print.js b/src/components/layout/Print.js index 53501912..1a92646e 100644 --- a/src/components/layout/Print.js +++ b/src/components/layout/Print.js @@ -2,49 +2,43 @@ import React, { PureComponent } from 'react' import { remote } from 'electron' -import queryString from 'query-string' +import qs from 'qs' -import QRCode from 'components/base/QRCode' -import Box from 'components/base/Box' -import { AddressBox } from 'components/ReceiveBox' - -type State = { - data: Object | null, -} - -class Print extends PureComponent { - state = { - data: null, - } - - componentWillMount() { - this.setState({ - data: queryString.parse(this.props.location.search), - }) - } +import CurrentAddress from 'components/CurrentAddress' +class Print extends PureComponent { componentDidMount() { window.requestAnimationFrame(() => setTimeout(() => { - // hacky way to detect that render is ready - // from the parent window - remote.getCurrentWindow().minimize() + if (!this._node) { + return + } + + const { height, width } = this._node.getBoundingClientRect() + const currentWindow = remote.getCurrentWindow() + + currentWindow.setContentSize(width, height) + currentWindow.emit('print-ready') }, 300), ) } + _node = null + render() { - const { data } = this.state + const data = qs.parse(this.props.location.search, { ignoreQueryPrefix: true }) + if (!data) { return null } - const { address, amount } = data + const { account, amount } = data return ( - - - {address} - {amount && {amount}} - + (this._node = n)} + amount={amount} + account={account} + withQRCode + /> ) } } diff --git a/src/components/modals/Receive/04-step-receive-funds.js b/src/components/modals/Receive/04-step-receive-funds.js index a3af6516..fed9282c 100644 --- a/src/components/modals/Receive/04-step-receive-funds.js +++ b/src/components/modals/Receive/04-step-receive-funds.js @@ -16,6 +16,7 @@ type Props = { addressVerified: null | boolean, amount: string | number, onChangeAmount: Function, + onVerify: Function, t: T, } @@ -35,10 +36,14 @@ export default (props: Props) => ( /> ) diff --git a/src/components/modals/Receive/index.js b/src/components/modals/Receive/index.js index c059a886..859b2580 100644 --- a/src/components/modals/Receive/index.js +++ b/src/components/modals/Receive/index.js @@ -207,6 +207,7 @@ class ReceiveModal extends PureComponent { addressVerified, amount, onChangeAmount: this.handleChangeAmount, + onVerify: this.handlePrevStep, }), } diff --git a/src/icons/Copy.js b/src/icons/Copy.js new file mode 100644 index 00000000..61777f5a --- /dev/null +++ b/src/icons/Copy.js @@ -0,0 +1,12 @@ +// @flow + +import React from 'react' + +export default ({ size, ...p }: { size: number }) => ( + + + +) diff --git a/src/icons/Print.js b/src/icons/Print.js new file mode 100644 index 00000000..551b969e --- /dev/null +++ b/src/icons/Print.js @@ -0,0 +1,12 @@ +// @flow + +import React from 'react' + +export default ({ size, ...p }: { size: number }) => ( + + + +) diff --git a/src/icons/Share.js b/src/icons/Share.js new file mode 100644 index 00000000..a25e1ee1 --- /dev/null +++ b/src/icons/Share.js @@ -0,0 +1,12 @@ +// @flow + +import React from 'react' + +export default ({ size, ...p }: { size: number }) => ( + + + +) diff --git a/src/icons/Shield.js b/src/icons/Shield.js new file mode 100644 index 00000000..8ce93f98 --- /dev/null +++ b/src/icons/Shield.js @@ -0,0 +1,49 @@ +// @flow + +import React from 'react' + +export default (p: Object) => ( + + + + + + + + + + + + + + + + + + + + + +) diff --git a/src/internals/usb/manager/helpers.js b/src/internals/usb/manager/helpers.js index 4f24e222..28890fb9 100644 --- a/src/internals/usb/manager/helpers.js +++ b/src/internals/usb/manager/helpers.js @@ -3,7 +3,7 @@ import CommNodeHid from '@ledgerhq/hw-transport-node-hid' import chalk from 'chalk' import Websocket from 'ws' -import qs from 'query-string' +import qs from 'qs' import noop from 'lodash/noop' import type Transport from '@ledgerhq/hw-transport' diff --git a/yarn.lock b/yarn.lock index c0f84b72..c751b08d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9959,13 +9959,6 @@ query-string@^5.0.1: object-assign "^4.1.0" strict-uri-encode "^1.0.0" -query-string@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.0.0.tgz#8b8f39447b73e8290d6f5e3581779218e9171142" - dependencies: - decode-uri-component "^0.2.0" - strict-uri-encode "^2.0.0" - querystring-es3@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" @@ -11544,10 +11537,6 @@ strict-uri-encode@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" -strict-uri-encode@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" - string-argv@^0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.0.2.tgz#dac30408690c21f3c3630a3ff3a05877bdcbd736"