Browse Source

Re-add Print and Copy for CurrentAddress

master
Loëck Vézien 7 years ago
parent
commit
25918249b7
No known key found for this signature in database GPG Key ID: CBCDCE384E853AC4
  1. 2
      package.json
  2. 116
      src/components/CurrentAddress/index.js
  3. 5
      src/components/CurrentAddress/stories.js
  4. 14
      src/components/base/CopyToClipboard.js
  5. 32
      src/components/base/Print.js
  6. 52
      src/components/layout/Print.js
  7. 7
      src/components/modals/Receive/04-step-receive-funds.js
  8. 1
      src/components/modals/Receive/index.js
  9. 12
      src/icons/Copy.js
  10. 12
      src/icons/Print.js
  11. 12
      src/icons/Share.js
  12. 49
      src/icons/Shield.js
  13. 2
      src/internals/usb/manager/helpers.js
  14. 11
      yarn.lock

2
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",

116
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,
}) => (
<FooterButtonWrapper onClick={onClick}>
{icon}
<Box fontSize={3} ff="Museo Sans|Bold" mt={1}>
{label}
</Box>
</FooterButtonWrapper>
)
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<Props> {
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 (
<Container withQRCode={withQRCode} notValid={notValid}>
<WrapperAddress notValid={notValid}>
<Container withQRCode={withQRCode} notValid={notValid} {...props}>
<WrapperAddress notValid={notValid} grow>
{withQRCode && (
<Box mb={4}>
<QRCode
@ -94,6 +167,41 @@ class CurrentAddress extends PureComponent<Props> {
{account.address}
</Address>
</WrapperAddress>
{withBadge && (
<Box horizontal flow={2} mt={2} alignItems="center">
<Box color={notValid ? 'alertRed' : 'wallet'}>
<IconShield height={32} width={28} />
</Box>
<Box shrink fontSize={12} color={notValid ? 'alertRed' : 'dark'} ff="Open Sans">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam blandit velit egestas leo
tincidunt
</Box>
</Box>
)}
{withFooter && (
<Footer>
{withVerify && (
<FooterButton icon={<IconCheck size={16} />} label="Verify" onClick={onVerify} />
)}
<CopyToClipboard
data={account.address}
render={copy => (
<FooterButton icon={<IconCopy size={16} />} label="Copy" onClick={copy} />
)}
/>
<Print
data={{ account, amount }}
render={(print, isLoading) => (
<FooterButton
icon={<IconPrint size={16} />}
label={isLoading ? '...' : 'Print'}
onClick={print}
/>
)}
/>
<FooterButton icon={<IconShare size={16} />} label="Share" onClick={onShare} />
</Footer>
)}
</Container>
)
}

5
src/components/CurrentAddress/stories.js

@ -12,8 +12,11 @@ const stories = storiesOf('Components', module)
stories.add('CurrentAddress', () => (
<CurrentAddress
addressVerified={boolean('addressVerified', true)}
account={accounts[0]}
addressVerified={boolean('addressVerified', true)}
withBadge={boolean('withBadge', false)}
withFooter={boolean('withFooter', false)}
withQRCode={boolean('withQRCode', false)}
withVerify={boolean('withVerify', false)}
/>
))

14
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

32
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<Props, State> {
}
handlePrint = () => {
if (BrowserWindow === null) {
return
}
const { data } = this.props
this.setState({ isLoading: true })
@ -33,22 +41,18 @@ class Print extends PureComponent<Props, State> {
},
})
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() {

52
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<any, State> {
state = {
data: null,
}
componentWillMount() {
this.setState({
data: queryString.parse(this.props.location.search),
})
}
import CurrentAddress from 'components/CurrentAddress'
class Print extends PureComponent<any> {
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 (
<Box p={3} flow={3}>
<QRCode size={150} data={`bitcoin:${address}${amount ? `?amount=${amount}` : ''}`} />
<AddressBox>{address}</AddressBox>
{amount && <AddressBox>{amount}</AddressBox>}
</Box>
<CurrentAddress
innerRef={n => (this._node = n)}
amount={amount}
account={account}
withQRCode
/>
)
}
}

7
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) => (
/>
</Box>
<CurrentAddress
account={props.account}
addressVerified={props.addressVerified}
amount={props.amount}
account={props.account}
onVerify={props.onVerify}
withBadge
withFooter
withQRCode
withVerify={props.addressVerified === false}
/>
</Box>
)

1
src/components/modals/Receive/index.js

@ -207,6 +207,7 @@ class ReceiveModal extends PureComponent<Props, State> {
addressVerified,
amount,
onChangeAmount: this.handleChangeAmount,
onVerify: this.handlePrevStep,
}),
}

12
src/icons/Copy.js

@ -0,0 +1,12 @@
// @flow
import React from 'react'
export default ({ size, ...p }: { size: number }) => (
<svg viewBox="0 0 16 16" height={size} width={size} {...p}>
<path
fill="currentColor"
d="M8 6.75c-.69035594 0-1.25.55964406-1.25 1.25v4.6666667c0 .6903559.55964406 1.25 1.25 1.25h4.6666667c.6903559 0 1.25-.5596441 1.25-1.25V8c0-.69035594-.5596441-1.25-1.25-1.25H8zm0-1.5h4.6666667c1.518783 0 2.75 1.23121694 2.75 2.75v4.6666667c0 1.518783-1.231217 2.75-2.75 2.75H8c-1.51878306 0-2.75-1.231217-2.75-2.75V8c0-1.51878306 1.23121694-2.75 2.75-2.75zm-4.66666667 4c.41421357 0 .75.33578644.75.75 0 .4142136-.33578643.75-.75.75h-.66666666c-1.15059323 0-2.08333334-.9327401-2.08333334-2.08333333v-6c0-1.15059323.93274011-2.08333334 2.08333334-2.08333334h6C9.8172599.58333333 10.75 1.51607344 10.75 2.66666667v.66666666c0 .41421357-.3357864.75-.75.75-.41421356 0-.75-.33578643-.75-.75v-.66666666c0-.32216611-.26116723-.58333334-.58333333-.58333334h-6c-.32216611 0-.58333334.26116723-.58333334.58333334v6c0 .3221661.26116723.58333333.58333334.58333333h.66666666z"
/>
</svg>
)

12
src/icons/Print.js

@ -0,0 +1,12 @@
// @flow
import React from 'react'
export default ({ size, ...p }: { size: number }) => (
<svg viewBox="0 0 16 16" height={size} width={size} {...p}>
<path
fill="currentColor"
d="M4.75 2.08333333V6c0 .41421356-.33578644.75-.75.75s-.75-.33578644-.75-.75V1.33333333c0-.41421356.33578644-.75.75-.75h8c.4142136 0 .75.33578644.75.75V6c0 .41421356-.3357864.75-.75.75s-.75-.33578644-.75-.75V2.08333333h-6.5zM4 11.25c.41421356 0 .75.3357864.75.75s-.33578644.75-.75.75H2.66666667c-1.15059323 0-2.08333334-.9327401-2.08333334-2.0833333V7.33333333C.58333333 6.1827401 1.51607344 5.25 2.66666667 5.25H13.3333333c1.1505933 0 2.0833334.9327401 2.0833334 2.08333333v3.33333337c0 1.1505932-.9327401 2.0833333-2.0833334 2.0833333H12c-.4142136 0-.75-.3357864-.75-.75s.3357864-.75.75-.75h1.3333333c.3221661 0 .5833334-.2611672.5833334-.5833333V7.33333333c0-.3221661-.2611673-.58333333-.5833334-.58333333H2.66666667c-.32216611 0-.58333334.26116723-.58333334.58333333v3.33333337c0 .3221661.26116723.5833333.58333334.5833333H4zm.75 2.6666667h6.5v-3.8333334h-6.5v3.8333334zM4 8.58333333h8c.4142136 0 .75.33578644.75.75v5.33333337c0 .4142135-.3357864.75-.75.75H4c-.41421356 0-.75-.3357865-.75-.75V9.33333333c0-.41421356.33578644-.75.75-.75z"
/>
</svg>
)

12
src/icons/Share.js

@ -0,0 +1,12 @@
// @flow
import React from 'react'
export default ({ size, ...p }: { size: number }) => (
<svg viewBox="0 0 16 16" height={size} width={size} {...p}>
<path
fill="currentColor"
d="M2.58333333 8c0-.41421356.33578644-.75.75-.75.41421357 0 .75.33578644.75.75v4.6666667c0 .2301186.18654802.4166666.41666667.4166666h7c.2301186 0 .4166667-.186548.4166667-.4166666V8c0-.41421356.3357864-.75.75-.75.4142135 0 .75.33578644.75.75v4.6666667c0 1.0585457-.8581209 1.9166666-1.9166667 1.9166666h-7c-1.05854577 0-1.91666667-.8581209-1.91666667-1.9166666V8zm3.61366342-2.96966991c-.29289322.29289321-.76776695.29289321-1.06066017 0-.29289322-.29289322-.29289322-.76776696 0-1.06066018l2.33333333-2.33333333c.29289322-.29289322.76776696-.29289322 1.06066018 0l2.33333331 2.33333333c.2928932.29289322.2928932.76776696 0 1.06066018-.2928932.29289321-.7677669.29289321-1.06066015 0L8 3.22732684 6.19699675 5.03033009zM7.25 2.16666667c0-.41421357.33578644-.75.75-.75s.75.33578643.75.75V9.75c0 .4142136-.33578644.75-.75.75s-.75-.3357864-.75-.75V2.16666667z"
/>
</svg>
)

49
src/icons/Shield.js

@ -0,0 +1,49 @@
// @flow
import React from 'react'
export default (p: Object) => (
<svg viewBox="0 0 28 32" height={32} width={28} {...p}>
<defs>
<filter
id="a"
width="163.6%"
height="153.8%"
x="-31.8%"
y="-23.1%"
filterUnits="objectBoundingBox"
>
<feOffset dy="1" in="SourceAlpha" result="shadowOffsetOuter1" />
<feGaussianBlur in="shadowOffsetOuter1" result="shadowBlurOuter1" stdDeviation="1.5" />
<feColorMatrix
in="shadowBlurOuter1"
result="shadowMatrixOuter1"
values="0 0 0 0 0.418261054 0 0 0 0 0.418261054 0 0 0 0 0.418261054 0 0 0 0.116196784 0"
/>
<feMerge>
<feMergeNode in="shadowMatrixOuter1" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
<path
id="b"
d="M11 25.5806452s11-3.7741936 11-11.9516129V4.08870968L11 0 0 4.08870968v9.54032262c0 8.1774193 11 11.9516129 11 11.9516129z"
/>
</defs>
<g fill="none" fillRule="evenodd" filter="url(#a)" transform="translate(3 2)">
<g strokeLinecap="round" strokeLinejoin="round">
<use fill="#FFF" stroke="currentColor" strokeWidth="1.5" xlinkHref="#b" />
<path
stroke="#FFF"
strokeWidth="2"
d="M10.6754629 26.5265181c-.1002644-.0344015-.2759634-.0990392-.5164692-.1943297-.39391814-.1560737-.83107448-.3435638-1.3008837-.5629688-1.3411669-.6263368-2.68247439-1.3856899-3.93963025-2.2843167C1.24412544 20.85844-1 17.5860312-1 13.6290323V3.39356402l12-4.46041056 12 4.46041056V13.6290323c0 3.9569989-2.2441254 7.2294077-5.9184797 9.8558706-1.2571559.8986268-2.5984634 1.6579799-3.9396303 2.2843167-.4698092.219405-.9069656.4068951-1.3008837.5629688-.2405058.0952905-.4162048.1599282-.5164692.1943297L11 26.6378696l-.3245371-.1113515z"
/>
</g>
<path
fill="currentColor"
fillRule="nonzero"
d="M14.4936649 8.7193017c-.3544349 0-.6426937.29661569-.6426937.66261516l-.0115902 2.97078244s.0011217.2071877-.1921725.2071877c-.1974068 0-.1919856-.2071877-.1919856-.2071877V8.06728684c0-.36580674-.28452-.64970207-.6391418-.64970207-.3549957 0-.6077362.28389533-.6077362.64970207v4.28521976s-.0216848.2095005-.2125488.2095005c-.1889945 0-.2033888-.2095005-.2033888-.2095005V7.35301931c0-.36561401-.2673217-.64334189-.6221304-.64334189-.3548088 0-.6243737.27772788-.6243737.64334189v4.99948729s-.0097208.200442-.2140443.200442c-.2007717 0-.2017064-.200442-.2017064-.200442V8.63854668c0-.36580674-.2777902-.5951587-.63241204-.5951587-.35499571 0-.61465292.22935196-.61465292.5951587v5.42793212s-.03477051.2413014-.37032465-.1572699c-.8604767-1.0209053-1.30931541-1.2238529-1.30931541-1.2238529s-.81673316-.412448-1.17845865.3322713c-.26208741.5400371.01551588.5691397.44416525 1.2323331.37911075.5872567 1.57831954 2.1318168 1.57831954 2.1318168s1.42241305 2.0687933 3.34189488 2.0687933c0 0 3.7595149.1653647 3.7595149-3.6694387l-.0130857-5.39921494c.0001869-.36599947-.2873241-.66261516-.6421328-.66261516"
/>
</g>
</svg>
)

2
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'

11
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"

Loading…
Cancel
Save