diff --git a/.eslintrc b/.eslintrc index 176ff361..9d0b1450 100644 --- a/.eslintrc +++ b/.eslintrc @@ -10,6 +10,7 @@ "__SENTRY_URL__": false, "__PRINT_MODE__": false, "__GLOBAL_STYLES__": false, + "__APP_VERSION__": false, "__static": false, "window": false, "document": false, @@ -20,6 +21,8 @@ "test": false, "it": false, "expect": false, + "requestAnimationFrame": false, + "cancelAnimationFrame": false }, "rules": { "camelcase": 0, diff --git a/flow-defs/globals.js b/flow-defs/globals.js index db928b57..c35461a8 100644 --- a/flow-defs/globals.js +++ b/flow-defs/globals.js @@ -7,6 +7,7 @@ declare var __ENV__: string declare var __PRINT_MODE__: string declare var __SENTRY_URL__: string declare var __GLOBAL_STYLES__: string +declare var __APP_VERSION__: string declare var __static: string declare var describe: Function declare var test: Function diff --git a/src/components/QRCodeExporter.js b/src/components/QRCodeExporter.js new file mode 100644 index 00000000..21ee4f90 --- /dev/null +++ b/src/components/QRCodeExporter.js @@ -0,0 +1,85 @@ +// @flow + +import React, { PureComponent } from 'react' + +import { connect } from 'react-redux' +import type { State } from 'reducers' +import { getVisibleAccounts } from 'reducers/accounts' +import QRCode from './base/QRCode' + +// encode the app state to export into an array of chunks for the mobile app to understand. +// returned data frames are json stringified array with format: [ datalength, index, type, ...rest ] +// NB as soon as we have common types we'll move this in a ledgerhq/common project +function makeChunks(state: State): Array { + const chunksFormatVersion = 1 + const desktopVersion = __APP_VERSION__ + const data = [ + ['meta', chunksFormatVersion, 'desktop', desktopVersion], + ...getVisibleAccounts(state).map(account => [ + 'account', + account.id, + account.name, + account.coinType, + ]), + ] + return data.map((arr, i) => JSON.stringify([data.length, i, ...arr])) +} + +const mapStateToProps = (state: State) => ({ chunks: makeChunks(state) }) + +class QRCodeExporter extends PureComponent< + { + chunks: string[], + fps: number, + size: number, + }, + { + frame: number, + }, +> { + static defaultProps = { + fps: 10, + size: 480, + } + + state = { + frame: 0, + } + + componentDidMount() { + const nextFrame = ({ frame }, { chunks }) => ({ + frame: (frame + 1) % chunks.length, + }) + let lastT + const loop = t => { + this._raf = requestAnimationFrame(loop) + if (!lastT) lastT = t + if ((t - lastT) * this.props.fps < 1000) return + lastT = t + this.setState(nextFrame) + } + this._raf = requestAnimationFrame(loop) + } + + componentWillUnmount() { + cancelAnimationFrame(this._raf) + } + + _raf: * + + render() { + const { frame } = this.state + const { chunks, size } = this.props + return ( +
+ {chunks.map((chunk, i) => ( +
+ +
+ ))} +
+ ) + } +} + +export default connect(mapStateToProps)(QRCodeExporter) diff --git a/src/components/SettingsPage/Tools.js b/src/components/SettingsPage/Tools.js new file mode 100644 index 00000000..6a64ba87 --- /dev/null +++ b/src/components/SettingsPage/Tools.js @@ -0,0 +1,53 @@ +// @flow + +import React, { PureComponent } from 'react' + +import Box, { Card } from 'components/base/Box' +import Modal, { ModalBody } from 'components/base/Modal' +import Button from 'components/base/Button' +import QRCodeExporter from 'components/QRCodeExporter' + +class TabProfile extends PureComponent<*, *> { + state = { + qrcodeMobileExportModal: false, + } + + onQRCodeMobileExport = () => { + this.setState({ qrcodeMobileExportModal: true }) + } + + onRequestClose = () => { + this.setState({ qrcodeMobileExportModal: false }) + } + + renderQRCodeModal = ({ onClose }: *) => ( + +

+ {/* TODO translate */} + Open Ledger Wallet Mobile App, go to Settings {'>'} Import Accounts +

+ +
+ ) + + render() { + const { qrcodeMobileExportModal } = this.state + return ( + + + + + + + + ) + } +} + +export default TabProfile diff --git a/src/components/SettingsPage/index.js b/src/components/SettingsPage/index.js index 3d4d0c65..826455a7 100644 --- a/src/components/SettingsPage/index.js +++ b/src/components/SettingsPage/index.js @@ -18,6 +18,7 @@ import Tabs from 'components/base/Tabs' import TabDisplay from './Display' import TabProfile from './Profile' +import TabTools from './Tools' import TabMoney from './Money' const mapStateToProps = state => ({ @@ -103,9 +104,8 @@ class SettingsPage extends PureComponent { }, { key: 'tools', - isDisabled: true, title: t('settings:tabs.tools'), - render: () =>
{'Outils'}
, + render: () => , }, { key: 'blockchain', diff --git a/src/components/base/QRCode/index.js b/src/components/base/QRCode/index.js index 148b9ffc..543dacc0 100644 --- a/src/components/base/QRCode/index.js +++ b/src/components/base/QRCode/index.js @@ -5,7 +5,7 @@ import qrcode from 'qrcode' type Props = { data: string, - size?: number, + size: number, } class QRCode extends PureComponent { diff --git a/webpack/plugins.js b/webpack/plugins.js index 347460bd..af1b4b64 100644 --- a/webpack/plugins.js +++ b/webpack/plugins.js @@ -1,6 +1,6 @@ const webpack = require('webpack') const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer') - +const pkg = require('../package.json') require('../src/globals') const { BUNDLE_ANALYZER } = process.env @@ -8,6 +8,7 @@ const { BUNDLE_ANALYZER } = process.env module.exports = type => { const plugins = [ new webpack.DefinePlugin({ + __APP_VERSION__: JSON.stringify(pkg.version), __GLOBAL_STYLES__: JSON.stringify(__GLOBAL_STYLES__), __DEV__, __PROD__,