Browse Source

Simple implementation of Macbook TouchBar

master
Loëck Vézien 7 years ago
parent
commit
2421e5713b
No known key found for this signature in database GPG Key ID: CBCDCE384E853AC4
  1. 2
      package.json
  2. 13
      src/actions/accounts.js
  3. 31
      src/components/AccountPage/index.js
  4. 11
      src/components/BalanceSummary/index.js
  5. 16
      src/components/CalculateBalance.js
  6. 28
      src/components/DashboardPage/index.js
  7. 4
      src/components/DeviceConnect/index.js
  8. 4
      src/components/ManagerPage/index.js
  9. 2
      src/components/OperationsList/index.js
  10. 4
      src/components/ReceiveBox.js
  11. 8
      src/components/SideBar/Item.js
  12. 4
      src/components/TopBar.js
  13. 8
      src/components/base/GrowScroll/index.js
  14. 16
      src/components/base/Search/index.js
  15. 4
      src/components/base/Tabs/index.js
  16. 7
      src/components/layout/Default.js
  17. 4
      src/helpers/balance.js
  18. 4
      src/helpers/staticPath.js
  19. 5
      src/index.ejs
  20. 78
      src/main/app.js
  21. 9
      src/reducers/modals.js
  22. 2
      src/renderer/events.js
  23. 1
      src/renderer/index.js
  24. 2
      src/types/common.js
  25. 6
      yarn.lock

2
package.json

@ -140,7 +140,7 @@
"js-yaml": "^3.10.0",
"lint-staged": "^7.0.4",
"node-loader": "^0.6.0",
"prettier": "^1.11.1",
"prettier": "^1.12.0",
"react-hot-loader": "^4.0.1",
"react-test-renderer": "^16.3.1",
"webpack": "^4.5.0",

13
src/actions/accounts.js

@ -43,7 +43,10 @@ export const updateOrderAccounts: UpdateOrderAccounts = (orderAccounts: string)
export type AddAccount = Account => (Function, Function) => void
export const addAccount: AddAccount = payload => (dispatch, getState) => {
const { settings: { counterValue, orderAccounts }, accounts } = getState()
const {
settings: { counterValue, orderAccounts },
accounts,
} = getState()
dispatch({ type: 'ADD_ACCOUNT', payload })
dispatch(updateOrderAccounts(orderAccounts))
@ -63,7 +66,9 @@ export const removeAccount: RemoveAccount = payload => ({
export type FetchAccounts = () => (Function, Function) => Promise<*, *>
export const fetchAccounts: FetchAccounts = () => (dispatch, getState) => {
const { settings: { orderAccounts } } = getState()
const {
settings: { orderAccounts },
} = getState()
const accounts = db.get('accounts')
dispatch({
type: 'SET_ACCOUNTS',
@ -74,7 +79,9 @@ export const fetchAccounts: FetchAccounts = () => (dispatch, getState) => {
export type UpdateAccount = Account => (Function, Function) => void
export const updateAccount: UpdateAccount = payload => (dispatch, getState) => {
const { settings: { orderAccounts } } = getState()
const {
settings: { orderAccounts },
} = getState()
dispatch({
type: 'UPDATE_ACCOUNT',
payload,

31
src/components/AccountPage/index.js

@ -1,11 +1,14 @@
// @flow
import React, { PureComponent } from 'react'
import { ipcRenderer } from 'electron'
import { compose } from 'redux'
import { connect } from 'react-redux'
import { translate } from 'react-i18next'
import { Redirect } from 'react-router'
import styled from 'styled-components'
import { formatCurrencyUnit, getFiatUnit } from '@ledgerhq/currencies'
import type { Account } from '@ledgerhq/wallet-common/lib/types'
import { MODAL_SEND, MODAL_RECEIVE, MODAL_SETTINGS_ACCOUNT } from 'config/constants'
@ -75,6 +78,27 @@ class AccountPage extends PureComponent<Props, State> {
daysCount: 7,
}
handleCalculateBalance = data => {
const { counterValue, account } = this.props
if (!account) {
return
}
ipcRenderer.send('touch-bar-update', {
text: account.name,
color: account.currency.color,
balance: {
currency: formatCurrencyUnit(account.unit, account.balance, {
showCode: true,
}),
counterValue: formatCurrencyUnit(getFiatUnit(counterValue), data.totalBalance, {
showCode: true,
}),
},
})
}
handleChangeSelectedTime = item =>
this.setState({
selectedTime: item.key,
@ -116,12 +140,13 @@ class AccountPage extends PureComponent<Props, State> {
</Box>
<Box mb={7}>
<BalanceSummary
counterValue={counterValue}
accounts={[account]}
chartColor={account.currency.color}
chartId={`account-chart-${account.id}`}
accounts={[account]}
selectedTime={selectedTime}
counterValue={counterValue}
daysCount={daysCount}
onCalculate={this.handleCalculateBalance}
selectedTime={selectedTime}
renderHeader={({ totalBalance, sinceBalance, refBalance }) => (
<Box flow={4} mb={2}>
<Box horizontal>

11
src/components/BalanceSummary/index.js

@ -10,6 +10,7 @@ import CalculateBalance from 'components/CalculateBalance'
import FormattedVal from 'components/base/FormattedVal'
type Props = {
onCalculate: Function,
counterValue: string,
chartColor: string,
chartId: string,
@ -20,21 +21,23 @@ type Props = {
}
const BalanceSummary = ({
counterValue,
accounts,
chartColor,
chartId,
accounts,
selectedTime,
counterValue,
daysCount,
onCalculate,
renderHeader,
selectedTime,
}: Props) => {
const unit = getFiatUnit(counterValue)
return (
<Card p={0} py={6}>
<CalculateBalance
counterValue={counterValue}
accounts={accounts}
counterValue={counterValue}
daysCount={daysCount}
onCalculate={onCalculate}
render={({ allBalances, totalBalance, sinceBalance, refBalance }) => (
<Fragment>
{renderHeader !== null && (

16
src/components/CalculateBalance.js

@ -2,6 +2,9 @@
import { PureComponent } from 'react'
import { connect } from 'react-redux'
import noop from 'lodash/noop'
import type { Account } from '@ledgerhq/wallet-common/lib/types'
import calculateBalance from 'helpers/balance'
@ -14,6 +17,7 @@ type Props = {
accounts: Account[],
counterValues: Object,
daysCount: number,
onCalculate: Function,
render: Function,
}
@ -33,14 +37,24 @@ function calculateBalanceToState(props: Object) {
}
class CalculateBalance extends PureComponent<Props, State> {
static defaultProps = {
onCalculate: noop,
}
state = calculateBalanceToState(this.props)
componentDidMount() {
this.props.onCalculate(this.state)
}
componentWillReceiveProps(nextProps) {
const sameAccounts = this.props.accounts === nextProps.accounts
const sameCounterValues = this.props.counterValues === nextProps.counterValues
const sameDaysCount = this.props.daysCount === nextProps.daysCount
if (!sameAccounts || !sameCounterValues || !sameDaysCount) {
this.setState(calculateBalanceToState(nextProps))
const state = calculateBalanceToState(nextProps)
nextProps.onCalculate(state)
this.setState(state)
}
}

28
src/components/DashboardPage/index.js

@ -1,16 +1,21 @@
// @flow
import React, { PureComponent, Fragment } from 'react'
import { ipcRenderer } from 'electron'
import { compose } from 'redux'
import { translate } from 'react-i18next'
import { connect } from 'react-redux'
import { push } from 'react-router-redux'
import { formatCurrencyUnit, getFiatUnit } from '@ledgerhq/currencies'
import type { Account } from '@ledgerhq/wallet-common/lib/types'
import chunk from 'lodash/chunk'
import type { T } from 'types/common'
import { colors } from 'styles/theme'
import { getVisibleAccounts } from 'reducers/accounts'
import { getCounterValueCode } from 'reducers/settings'
@ -77,12 +82,32 @@ class DashboardPage extends PureComponent<Props, State> {
}
}
handleCalculateBalance = data => {
const { counterValue } = this.props
if (this._cacheBalance !== data.totalBalance) {
this._cacheBalance = data.totalBalance
ipcRenderer.send('touch-bar-update', {
text: 'Total balance',
color: colors.wallet,
balance: {
counterValue: formatCurrencyUnit(getFiatUnit(counterValue), data.totalBalance, {
showCode: true,
}),
},
})
}
}
handleChangeSelectedTime = item =>
this.setState({
selectedTime: item.key,
daysCount: item.value,
})
_cacheBalance = null
render() {
const { push, accounts, t, counterValue } = this.props
const { accountsChunk, selectedTime, daysCount } = this.state
@ -109,9 +134,10 @@ class DashboardPage extends PureComponent<Props, State> {
{totalAccounts > 0 && (
<Fragment>
<BalanceSummary
onCalculate={this.handleCalculateBalance}
counterValue={counterValue}
chartId="dashboard-chart"
chartColor="#5286f7"
chartColor={colors.wallet}
accounts={accounts}
selectedTime={selectedTime}
daysCount={daysCount}

4
src/components/DeviceConnect/index.js

@ -29,7 +29,9 @@ const Step = styled(Box).attrs({
${p =>
p.validated
? p.theme.colors.wallet
: p.hasErrors ? p.theme.colors.alertRed : p.theme.colors.fog};
: p.hasErrors
? p.theme.colors.alertRed
: p.theme.colors.fog};
`
const StepIcon = styled(Box).attrs({
alignItems: 'center',

4
src/components/ManagerPage/index.js

@ -83,7 +83,9 @@ class ManagerPage extends PureComponent<Props, State> {
this.setState({ status: 'busy' })
try {
const { job, successResponse, errorResponse } = options
const { device: { path: devicePath } } = this.props
const {
device: { path: devicePath },
} = this.props
const data = { appParams, devicePath }
await runJob({ channel: 'usb', job, successResponse, errorResponse, data })
this.setState({ status: 'success' })

2
src/components/OperationsList/index.js

@ -293,7 +293,7 @@ export class OperationsList extends PureComponent<Props> {
}
return (
<Operation
key={op.hash}
key={`${account.id}-${op.hash}`}
account={account}
minConfirmations={account.minConfirmations}
onAccountClick={onAccountClick}

4
src/components/ReceiveBox.js

@ -144,7 +144,9 @@ class ReceiveBox extends PureComponent<Props, State> {
isVerified:{' '}
{isVerified === null
? 'not yet...'
: isVerified === true ? 'ok!' : '/!\\ contact support'}
: isVerified === true
? 'ok!'
: '/!\\ contact support'}
</Box>
<Box alignItems="center">
<QRCode size={150} data={`bitcoin:${address}${amount ? `?amount=${amount}` : ''}`} />

8
src/components/SideBar/Item.js

@ -85,8 +85,12 @@ function Item({
isActive={isActive}
onClick={
linkTo
? isActive ? undefined : () => push(linkTo)
: modal ? () => openModal(modal) : void 0
? isActive
? undefined
: () => push(linkTo)
: modal
? () => openModal(modal)
: void 0
}
>
{icon && <Box color={isActive ? iconActiveColor : void 0}>{icon}</Box>}

4
src/components/TopBar.js

@ -52,7 +52,9 @@ const Activity = styled.div`
background: ${p =>
p.progress === true
? p.theme.colors.wallet
: p.fail === true ? p.theme.colors.alertRed : p.theme.colors.positiveGreen};
: p.fail === true
? p.theme.colors.alertRed
: p.theme.colors.positiveGreen};
border-radius: 50%;
bottom: 20px;
height: 4px;

8
src/components/base/GrowScroll/index.js

@ -32,16 +32,16 @@ class GrowScroll extends PureComponent<Props> {
}
}
componentWillReceiveProps(nextProps: Props) {
this.handleUpdate(nextProps)
}
componentWillUnmount() {
if (this._scrollbar) {
this._scrollbar.removeListener(this.props.onScroll)
}
}
componenDidUpdate() {
this.handleUpdate(this.props)
}
handleUpdate = (props: Props) => {
if (this._scrollbar) {
props.onUpdate(this._scrollbar)

16
src/components/base/Search/index.js

@ -39,18 +39,18 @@ class Search extends PureComponent<Props, State> {
this.initFuse(this.props)
}
componentWillReceiveProps(nextProps: Props) {
if (nextProps.value !== this.props.value) {
componentDidUpdate(prevProps: Props) {
if (prevProps.value !== this.props.value) {
if (this._fuse) {
const results = this._fuse.search(nextProps.value)
this.formatResults(results, nextProps)
const results = this._fuse.search(this.props.value)
this.formatResults(results, this.props)
}
}
if (nextProps.highlight !== this.props.highlight) {
this.initFuse(nextProps)
if (prevProps.highlight !== this.props.highlight) {
this.initFuse(this.props)
}
if (nextProps.items !== this.props.items) {
this.initFuse(nextProps)
if (prevProps.items !== this.props.items) {
this.initFuse(this.props)
}
}

4
src/components/base/Tabs/index.js

@ -25,7 +25,9 @@ const Tab = styled(Tabbable).attrs({
color: ${p =>
p.isActive
? p.theme.colors.wallet
: p.isDisabled ? p.theme.colors.grey : p.theme.colors.graphite};
: p.isDisabled
? p.theme.colors.grey
: p.theme.colors.graphite};
margin-bottom: -1px;
outline: none;
cursor: ${p => (p.isActive ? 'default' : p.isDisabled ? 'not-allowed' : 'pointer')};

7
src/components/layout/Default.js

@ -2,6 +2,7 @@
import React, { Fragment, Component } from 'react'
import { compose } from 'redux'
import { ipcRenderer } from 'electron'
import styled from 'styled-components'
import { Route, withRouter } from 'react-router'
import { translate } from 'react-i18next'
@ -37,6 +38,12 @@ class Default extends Component<Props> {
window.requestAnimationFrame(() => (this._timeout = setTimeout(() => window.onAppReady(), 300)))
}
componentWillReceiveProps(nextProps: Props) {
if (process.platform === 'darwin' && nextProps.location !== this.props.location) {
ipcRenderer.send('touch-bar-update', { clear: true })
}
}
componentDidUpdate(prevProps) {
if (this.props.location !== prevProps.location) {
if (this._scrollContainer) {

4
src/helpers/balance.js

@ -135,7 +135,9 @@ export function getBalanceHistoryForAccounts({
}
return { ...item, balance: b }
})
: balances.length > 0 ? balances[0] : []
: balances.length > 0
? balances[0]
: []
}
export default function calculateBalance(props: CalculateBalance) {

4
src/helpers/staticPath.js

@ -7,4 +7,6 @@ export default (__DEV__ && !STORYBOOK_ENV && NODE_ENV !== 'test'
? __static
: isRunningInAsar
? __dirname.replace(/app\.asar$/, 'static')
: !STORYBOOK_ENV ? `${__dirname}/../../static` : 'static')
: !STORYBOOK_ENV
? `${__dirname}/../../static`
: 'static')

5
src/index.ejs

@ -45,7 +45,8 @@
</div>
<div id="app"></div>
<script>
const { name } = require('electron').remote.getCurrentWindow()
const { remote, ipcRenderer } = require('electron')
const { name } = remote.getCurrentWindow()
const preloadEl = document.getElementById('preload')
const appEl = document.getElementById('app')
@ -60,6 +61,8 @@
preloadEl.addEventListener('transitionend', () => preloadEl.remove())
}
ipcRenderer.emit('touch-bar-init')
}
if (name === 'MainWindow') {

78
src/main/app.js

@ -1,11 +1,15 @@
// @flow
import { app, BrowserWindow, Menu, screen } from 'electron'
import { app, BrowserWindow, Menu, screen, TouchBar, ipcMain } from 'electron'
import debounce from 'lodash/debounce'
import menu from 'main/menu'
import db from 'helpers/db'
import { MODAL_RECEIVE, MODAL_SEND } from 'config/constants'
const { TouchBarButton, TouchBarGroup, TouchBarLabel } = TouchBar
// necessary to prevent win from being garbage collected
let mainWindow = null
@ -61,6 +65,70 @@ const saveWindowSettings = window => {
)
}
function configureTouchBar(w) {
const defaultItems = [
new TouchBarButton({
label: 'Send funds',
click: () =>
w.webContents.send('msg', {
type: 'dispatch',
data: { type: 'MODAL_OPEN', payload: { name: MODAL_SEND } },
}),
}),
new TouchBarButton({
label: 'Receive funds',
click: () =>
w.webContents.send('msg', {
type: 'dispatch',
data: { type: 'MODAL_OPEN', payload: { name: MODAL_RECEIVE } },
}),
}),
]
ipcMain.on('touch-bar-init', () => w.setTouchBar(new TouchBar(defaultItems)))
ipcMain.on('touch-bar-update', (e, d) => {
if (d.clear) {
w.setTouchBar(new TouchBar(defaultItems))
return
}
const items = [
new TouchBarLabel({
textColor: d.color,
label: d.text,
}),
]
if (d.balance.currency) {
items.push(
new TouchBarLabel({
textColor: d.color,
label: d.balance.currency,
}),
)
}
if (d.balance.counterValue) {
items.push(
new TouchBarLabel({
textColor: d.color,
label: d.balance.counterValue,
}),
)
}
w.setTouchBar(
new TouchBar([
...defaultItems,
new TouchBarGroup({
items,
}),
]),
)
})
}
const defaultWindowOptions = {
backgroundColor: '#fff',
webPreferences: {
@ -129,6 +197,10 @@ function createMainWindow() {
})
})
if (process.platform === 'darwin') {
configureTouchBar(window)
}
return window
}
@ -169,9 +241,7 @@ function createDevWindow() {
window.on('close', handleCloseWindow(window))
window.on('ready-to-show', () => {
window.show()
})
window.on('ready-to-show', () => window.show())
// Don't want to use HTML <title>
window.on('page-title-updated', e => e.preventDefault())

9
src/reducers/modals.js

@ -24,7 +24,14 @@ const handlers = {
MODAL_OPEN: (state, { payload }: { payload: OpenPayload }) => {
const { name, data } = payload
return {
...state,
// Close all modal before
...Object.keys(state).reduce((result, key) => {
result[key] = {
isOpened: false,
data: undefined,
}
return result
}, {}),
[name]: {
isOpened: true,
data,

2
src/renderer/events.js

@ -127,7 +127,7 @@ export function checkUpdates() {
export default ({ store, locked }: { store: Object, locked: boolean }) => {
const handlers = {
dispatch: (type, payload) => store.dispatch({ type, payload }),
dispatch: ({ type, payload }) => store.dispatch({ type, payload }),
application: {
changeLanguage: lang => i18n.changeLanguage(lang),
},

1
src/renderer/index.js

@ -1,4 +1,5 @@
require('@babel/polyfill')
const Raven = require('raven-js')
require('../env')

2
src/types/common.js

@ -29,4 +29,4 @@ export type SettingsMoney = {
export type Settings = SettingsProfile & SettingsDisplay & SettingsMoney
export type T = (string, ?Object) => string
export type T = (?string, ?Object) => string

6
yarn.lock

@ -9762,7 +9762,11 @@ preserve@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
prettier@^1.11.1, prettier@^1.5.3:
prettier@^1.12.0:
version "1.12.0"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.12.0.tgz#d26fc5894b9230de97629b39cae225b503724ce8"
prettier@^1.5.3:
version "1.11.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.11.1.tgz#61e43fc4cd44e68f2b0dfc2c38cd4bb0fccdcc75"

Loading…
Cancel
Save