Meriadec Pillet
7 years ago
committed by
GitHub
32 changed files with 612 additions and 332 deletions
@ -0,0 +1,19 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import React from 'react' |
||||
|
import styled from 'styled-components' |
||||
|
|
||||
|
const TopGradientBox = styled.div` |
||||
|
width: 100%; |
||||
|
height: 80px; |
||||
|
position: absolute; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
background: linear-gradient(#ffffff 40%, rgba(255, 255, 255, 0)); |
||||
|
z-index: 2; |
||||
|
pointer-events: none; |
||||
|
` |
||||
|
|
||||
|
const el = <TopGradientBox /> |
||||
|
|
||||
|
export default () => el |
@ -0,0 +1,51 @@ |
|||||
|
// @flow
|
||||
|
import React from 'react' |
||||
|
import { translate } from 'react-i18next' |
||||
|
|
||||
|
import type { T, Device } from 'types/common' |
||||
|
|
||||
|
import Box from 'components/base/Box' |
||||
|
import Text from 'components/base/Text' |
||||
|
|
||||
|
import AppsList from './AppsList' |
||||
|
import FirmwareUpdate from './FirmwareUpdate' |
||||
|
|
||||
|
type DeviceInfo = { |
||||
|
targetId: number | string, |
||||
|
version: string, |
||||
|
final: boolean, |
||||
|
mcu: boolean, |
||||
|
} |
||||
|
|
||||
|
type Props = { |
||||
|
t: T, |
||||
|
device: Device, |
||||
|
deviceInfo: DeviceInfo, |
||||
|
} |
||||
|
|
||||
|
const Dashboard = ({ device, deviceInfo, t }: Props) => ( |
||||
|
<Box flow={4}> |
||||
|
<Box> |
||||
|
<Text ff="Museo Sans|Regular" fontSize={7} color="black"> |
||||
|
{t('app:manager.title')} |
||||
|
</Text> |
||||
|
<Text ff="Museo Sans|Light" fontSize={5}> |
||||
|
{t('app:manager.subtitle')} |
||||
|
</Text> |
||||
|
</Box> |
||||
|
<Box mt={7}> |
||||
|
<FirmwareUpdate |
||||
|
infos={{ |
||||
|
targetId: deviceInfo.targetId, |
||||
|
version: deviceInfo.version, |
||||
|
}} |
||||
|
device={device} |
||||
|
/> |
||||
|
</Box> |
||||
|
<Box> |
||||
|
<AppsList device={device} targetId={deviceInfo.targetId} /> |
||||
|
</Box> |
||||
|
</Box> |
||||
|
) |
||||
|
|
||||
|
export default translate()(Dashboard) |
@ -0,0 +1,187 @@ |
|||||
|
// @flow
|
||||
|
import React from 'react' |
||||
|
import { Trans, translate } from 'react-i18next' |
||||
|
import styled from 'styled-components' |
||||
|
import isNull from 'lodash/isNull' |
||||
|
import type { Device, T } from 'types/common' |
||||
|
|
||||
|
import { i } from 'helpers/staticPath' |
||||
|
|
||||
|
import Box from 'components/base/Box' |
||||
|
import Space from 'components/base/Space' |
||||
|
import Text from 'components/base/Text' |
||||
|
import Spinner from 'components/base/Spinner' |
||||
|
|
||||
|
import IconCheck from 'icons/Check' |
||||
|
import IconExclamationCircle from 'icons/ExclamationCircle' |
||||
|
import IconUsb from 'icons/Usb' |
||||
|
import IconHome from 'icons/Home' |
||||
|
|
||||
|
const WrapperIconCurrency = styled(Box).attrs({ |
||||
|
alignItems: 'center', |
||||
|
justifyContent: 'center', |
||||
|
})` |
||||
|
border: 1px solid ${p => p.theme.colors[p.color]}; |
||||
|
border-radius: 8px; |
||||
|
height: 24px; |
||||
|
width: 24px; |
||||
|
` |
||||
|
|
||||
|
const Step = styled(Box).attrs({ |
||||
|
borderRadius: 1, |
||||
|
justifyContent: 'center', |
||||
|
fontSize: 4, |
||||
|
})` |
||||
|
border: 1px solid |
||||
|
${p => |
||||
|
p.validated |
||||
|
? p.theme.colors.wallet |
||||
|
: p.hasErrors |
||||
|
? p.theme.colors.alertRed |
||||
|
: p.theme.colors.fog}; |
||||
|
` |
||||
|
|
||||
|
const StepIcon = styled(Box).attrs({ |
||||
|
alignItems: 'center', |
||||
|
justifyContent: 'center', |
||||
|
})` |
||||
|
width: 64px; |
||||
|
` |
||||
|
|
||||
|
const StepContent = styled(Box).attrs({ |
||||
|
color: 'dark', |
||||
|
horizontal: true, |
||||
|
alignItems: 'center', |
||||
|
})` |
||||
|
height: 60px; |
||||
|
line-height: 1.2; |
||||
|
|
||||
|
strong { |
||||
|
font-weight: 600; |
||||
|
} |
||||
|
` |
||||
|
|
||||
|
const StepCheck = ({ checked, hasErrors }: { checked: ?boolean, hasErrors?: boolean }) => ( |
||||
|
<Box pr={5}> |
||||
|
{checked ? ( |
||||
|
<Box color="wallet"> |
||||
|
<IconCheck size={16} /> |
||||
|
</Box> |
||||
|
) : hasErrors ? ( |
||||
|
<Box color="alertRed"> |
||||
|
<IconExclamationCircle size={16} /> |
||||
|
</Box> |
||||
|
) : ( |
||||
|
<Spinner size={16} /> |
||||
|
)} |
||||
|
</Box> |
||||
|
) |
||||
|
|
||||
|
StepCheck.defaultProps = { |
||||
|
hasErrors: false, |
||||
|
} |
||||
|
|
||||
|
type DeviceInfo = { |
||||
|
targetId: number | string, |
||||
|
version: string, |
||||
|
final: boolean, |
||||
|
mcu: boolean, |
||||
|
} |
||||
|
|
||||
|
type Error = { |
||||
|
message: string, |
||||
|
stack: string, |
||||
|
} |
||||
|
|
||||
|
type Props = { |
||||
|
t: T, |
||||
|
device: ?Device, |
||||
|
deviceInfo: ?DeviceInfo, |
||||
|
dashboardError: ?Error, |
||||
|
isGenuine: boolean, |
||||
|
} |
||||
|
|
||||
|
const WorkflowDefault = ({ device, deviceInfo, dashboardError, isGenuine, t }: Props) => ( |
||||
|
<Box align="center"> |
||||
|
<Space of={152} /> |
||||
|
<Box align="center" style={{ maxWidth: 460, padding: '0 10px' }}> |
||||
|
<img |
||||
|
src={i('logos/connectDevice.png')} |
||||
|
alt="connect your device" |
||||
|
style={{ marginBottom: 30, maxWidth: 362, width: '100%' }} |
||||
|
/> |
||||
|
<Text ff="Museo Sans|Regular" fontSize={7} color="black" style={{ marginBottom: 10 }}> |
||||
|
{t('app:manager.device.title')} |
||||
|
</Text> |
||||
|
<Text ff="Museo Sans|Light" fontSize={5} color="grey" align="center"> |
||||
|
{t('app:manager.device.desc')} |
||||
|
</Text> |
||||
|
</Box> |
||||
|
<Box flow={4} style={{ maxWidth: 460, padding: '60px 10px 0' }}> |
||||
|
{/* DEVICE CHECK */} |
||||
|
<Step validated={!!device}> |
||||
|
<StepContent> |
||||
|
<StepIcon> |
||||
|
<IconUsb size={36} /> |
||||
|
</StepIcon> |
||||
|
<Box grow shrink> |
||||
|
<Trans i18nKey="deviceConnect:step1.connect" parent="div"> |
||||
|
{'Connect your '} |
||||
|
<strong>Ledger device</strong> |
||||
|
{' to your computer and enter your '} |
||||
|
<strong>PIN code</strong> |
||||
|
{' on your device'} |
||||
|
</Trans> |
||||
|
</Box> |
||||
|
<StepCheck checked={!!device} /> |
||||
|
</StepContent> |
||||
|
</Step> |
||||
|
|
||||
|
{/* DASHBOARD CHECK */} |
||||
|
<Step validated={!!device && !!deviceInfo} hasErrors={!!device && !!dashboardError}> |
||||
|
<StepContent> |
||||
|
<StepIcon> |
||||
|
<WrapperIconCurrency> |
||||
|
<IconHome size={12} /> |
||||
|
</WrapperIconCurrency> |
||||
|
</StepIcon> |
||||
|
<Box grow shrink> |
||||
|
<Trans i18nKey="deviceConnect:dashboard.open" parent="div"> |
||||
|
{'Go to the '} |
||||
|
<strong>{'dashboard'}</strong> |
||||
|
{' on your device'} |
||||
|
</Trans> |
||||
|
</Box> |
||||
|
<StepCheck checked={!!device && !!deviceInfo} hasErrors={!!device && !!dashboardError} /> |
||||
|
</StepContent> |
||||
|
</Step> |
||||
|
|
||||
|
{/* GENUINE CHECK */} |
||||
|
<Step |
||||
|
validated={(!!device && !isNull(isGenuine) && isGenuine) || undefined} |
||||
|
hasErrors={(!!device && !isNull(isGenuine) && !isGenuine) || undefined} |
||||
|
> |
||||
|
<StepContent> |
||||
|
<StepIcon> |
||||
|
<WrapperIconCurrency> |
||||
|
<IconCheck size={12} /> |
||||
|
</WrapperIconCurrency> |
||||
|
</StepIcon> |
||||
|
<Box grow shrink> |
||||
|
<Trans i18nKey="deviceConnect:stepGenuine.open" parent="div"> |
||||
|
{'Confirm '} |
||||
|
<strong>{'authentication'}</strong> |
||||
|
{' on your device'} |
||||
|
</Trans> |
||||
|
</Box> |
||||
|
<StepCheck |
||||
|
checked={(!!device && !isNull(isGenuine) && isGenuine) || undefined} |
||||
|
hasErrors={(!!device && !isNull(isGenuine) && !isGenuine) || undefined} |
||||
|
/> |
||||
|
</StepContent> |
||||
|
</Step> |
||||
|
</Box> |
||||
|
</Box> |
||||
|
) |
||||
|
|
||||
|
export default translate()(WorkflowDefault) |
@ -0,0 +1,82 @@ |
|||||
|
// @flow
|
||||
|
import React, { Component } from 'react' |
||||
|
import styled, { keyframes } from 'styled-components' |
||||
|
|
||||
|
import Box from 'components/base/Box' |
||||
|
|
||||
|
const inifiteAnimation = keyframes` |
||||
|
0% { |
||||
|
left: 0 |
||||
|
} |
||||
|
100% { |
||||
|
left: 102% |
||||
|
} |
||||
|
` |
||||
|
|
||||
|
const fillInAnimation = keyframes` |
||||
|
0% { |
||||
|
transform: translate3d(-110%, 0, 0); |
||||
|
} |
||||
|
50% { |
||||
|
transform: translate3d(-30%, 0, 0); |
||||
|
} |
||||
|
100% { |
||||
|
transform: translate3d(0); |
||||
|
} |
||||
|
` |
||||
|
|
||||
|
const Bar = styled(Box).attrs({ |
||||
|
color: 'fog', |
||||
|
borderRadius: '2.5px', |
||||
|
})` |
||||
|
height: 5px; |
||||
|
width: 100%; |
||||
|
position: relative; |
||||
|
background-color: currentColor; |
||||
|
overflow: hidden; |
||||
|
` |
||||
|
|
||||
|
const Progression = styled(Bar).attrs({ |
||||
|
color: 'wallet', |
||||
|
})` |
||||
|
position: absolute; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
${p => |
||||
|
p.infinite |
||||
|
? ` |
||||
|
animation: 1000ms ${inifiteAnimation} ease-out infinite; |
||||
|
` |
||||
|
: ` |
||||
|
animation: ${p.timing}ms ${fillInAnimation} ease-out; |
||||
|
animation-fill-mode: forwards; |
||||
|
`};
|
||||
|
` |
||||
|
|
||||
|
type Props = { |
||||
|
infinite: boolean, |
||||
|
timing?: number, |
||||
|
color?: string, |
||||
|
} |
||||
|
|
||||
|
type State = {} |
||||
|
|
||||
|
class Progress extends Component<Props, State> { |
||||
|
static defaultProps = { |
||||
|
infinite: false, |
||||
|
timing: 3000, |
||||
|
color: 'wallet', |
||||
|
} |
||||
|
|
||||
|
render() { |
||||
|
const { infinite, color, timing } = this.props |
||||
|
const styles = infinite ? { width: '20%' } : { width: '100%' } |
||||
|
return ( |
||||
|
<Bar> |
||||
|
<Progression infinite={infinite} color={color} style={styles} timing={timing} /> |
||||
|
</Bar> |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export default Progress |
@ -0,0 +1,15 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import db from 'helpers/db' |
||||
|
import uuid from 'uuid/v4' |
||||
|
|
||||
|
// a user is an anonymous way to identify a same instance of the app
|
||||
|
|
||||
|
export default () => { |
||||
|
let user = db.get('user') |
||||
|
if (!user) { |
||||
|
user = { id: uuid() } |
||||
|
db.set('user', user) |
||||
|
} |
||||
|
return user |
||||
|
} |
@ -1,7 +0,0 @@ |
|||||
const { SENTRY_URL } = process.env |
|
||||
|
|
||||
if (__PROD__ && SENTRY_URL) { |
|
||||
const Raven = require('raven') |
|
||||
const ravenConfig = { captureUnhandledRejections: true } |
|
||||
Raven.config(SENTRY_URL, ravenConfig).install() |
|
||||
} |
|
@ -0,0 +1,14 @@ |
|||||
|
import { ipcRenderer } from 'electron' |
||||
|
import { sentryLogsBooleanSelector } from 'reducers/settings' |
||||
|
|
||||
|
let isSentryInstalled = false |
||||
|
|
||||
|
export default store => next => action => { |
||||
|
next(action) |
||||
|
const state = store.getState() |
||||
|
const sentryLogs = sentryLogsBooleanSelector(state) |
||||
|
if (sentryLogs !== isSentryInstalled) { |
||||
|
isSentryInstalled = sentryLogs |
||||
|
ipcRenderer.send('sentryLogsChanged', { value: sentryLogs }) |
||||
|
} |
||||
|
} |
@ -1,14 +1,3 @@ |
|||||
require('@babel/polyfill') |
require('@babel/polyfill') |
||||
|
|
||||
const Raven = require('raven-js') |
|
||||
|
|
||||
require('../env') |
require('../env') |
||||
|
|
||||
const { SENTRY_URL } = process.env |
|
||||
|
|
||||
if (__PROD__ && SENTRY_URL) { |
|
||||
Raven.config(SENTRY_URL, { allowSecretKey: true }).install() |
|
||||
window.addEventListener('unhandledrejection', event => Raven.captureException(event.reason)) |
|
||||
} |
|
||||
|
|
||||
require('./init') |
require('./init') |
||||
|
@ -0,0 +1,9 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import Raven from 'raven-js' |
||||
|
import user from 'helpers/user' |
||||
|
import install from './install' |
||||
|
|
||||
|
export default (shouldSendCallback: () => boolean) => { |
||||
|
install(Raven, shouldSendCallback, user().id) |
||||
|
} |
@ -0,0 +1,24 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
require('../env') |
||||
|
|
||||
|
export default (Raven: any, shouldSendCallback: () => boolean, userId: string) => { |
||||
|
if (!__SENTRY_URL__) return |
||||
|
let r = Raven.config(__SENTRY_URL__, { |
||||
|
captureUnhandledRejections: true, |
||||
|
allowSecretKey: true, |
||||
|
release: __APP_VERSION__, |
||||
|
environment: __DEV__ ? 'development' : 'production', |
||||
|
shouldSendCallback, |
||||
|
}) |
||||
|
const user = { |
||||
|
ip_address: null, |
||||
|
id: userId, |
||||
|
} |
||||
|
if (r.setUserContext) { |
||||
|
r = r.setUserContext(user) |
||||
|
} else if (r.setContext) { |
||||
|
r = r.setContext({ user }) |
||||
|
} |
||||
|
r.install() |
||||
|
} |
@ -0,0 +1,8 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import Raven from 'raven' |
||||
|
import install from './install' |
||||
|
|
||||
|
export default (shouldSendCallback: () => boolean, userId: string) => { |
||||
|
install(Raven, shouldSendCallback, userId) |
||||
|
} |
Loading…
Reference in new issue