diff --git a/.storybook/preview-head.html b/.storybook/preview-head.html
index 9e769352..52da7032 100644
--- a/.storybook/preview-head.html
+++ b/.storybook/preview-head.html
@@ -1,2 +1,3 @@
+
\ No newline at end of file
diff --git a/package.json b/package.json
index dd55ec56..c3c4d732 100644
--- a/package.json
+++ b/package.json
@@ -65,7 +65,7 @@
"ethereumjs-abi": "^0.6.5",
"extract-text-webpack-plugin": "^4.0.0-beta.0",
"file-loader": "^1.1.11",
- "flow-bin": "^0.66.0",
+ "flow-bin": "^0.79.1",
"fs-extra": "^5.0.0",
"html-loader": "^0.5.5",
"html-webpack-plugin": "^3.0.4",
@@ -102,8 +102,8 @@
},
"dependencies": {
"@gnosis.pm/util-contracts": "^0.2.14",
- "@material-ui/core": "^1.2.1",
- "@material-ui/icons": "^1.1.0",
+ "@material-ui/core": "^3.0.1",
+ "@material-ui/icons": "^3.0.1",
"final-form": "^4.2.1",
"history": "^4.7.2",
"react-final-form": "^3.1.2",
diff --git a/public/favicon.ico b/public/favicon.ico
index 9cb8601e..7b9d1cd1 100644
Binary files a/public/favicon.ico and b/public/favicon.ico differ
diff --git a/public/index.html b/public/index.html
index 832dad97..9e3064f4 100644
--- a/public/index.html
+++ b/public/index.html
@@ -5,6 +5,7 @@
+
Multisig Safe
diff --git a/src/components/Footer/index.jsx b/src/components/Footer/index.jsx
index eb1e52ed..2c5882c5 100644
--- a/src/components/Footer/index.jsx
+++ b/src/components/Footer/index.jsx
@@ -2,16 +2,17 @@
import React from 'react'
import Block from '~/components/layout/Block'
import Link from '~/components/layout/Link'
+import Paragraph from '~/components/layout/Paragraph'
import { WELCOME_ADDRESS, SAFELIST_ADDRESS } from '~/routes/routes'
import styles from './index.scss'
const Footer = () => (
-
- Welcome
+
+ Welcome
- Safe List
+ Safe List
)
diff --git a/src/components/Footer/index.scss b/src/components/Footer/index.scss
index c5036983..91887ad4 100644
--- a/src/components/Footer/index.scss
+++ b/src/components/Footer/index.scss
@@ -1,7 +1,12 @@
.footer {
+ font-size: $smallFontSize;
display: grid;
- grid-template-columns: 1fr auto auto;
- justify-items: end;
+ grid-template-columns: 100px 100px 1fr;
+ grid-template-rows: 36px;
+ justify-items: center;
+ align-items: center;
+ border: solid 0.5px $border;
+ background-color: white;
}
@media only screen and (max-width: $(screenXs)px) {
diff --git a/src/components/Footer/index.stories.js b/src/components/Footer/index.stories.js
index 57711930..1af60805 100644
--- a/src/components/Footer/index.stories.js
+++ b/src/components/Footer/index.stories.js
@@ -11,6 +11,6 @@ const FrameDecorator = story => (
)
-storiesOf('Components', module)
+storiesOf('Components /Footer', module)
.addDecorator(FrameDecorator)
- .add('Footer', () => )
+ .add('Loaded', () => )
diff --git a/src/components/Header/actions.js b/src/components/Header/actions.js
index 32069043..e96a2379 100644
--- a/src/components/Header/actions.js
+++ b/src/components/Header/actions.js
@@ -1,6 +1,12 @@
// @flow
-import { fetchProvider } from '~/logic/wallets/store/actions'
+import { fetchProvider, removeProvider } from '~/logic/wallets/store/actions'
+
+export type Actions = {
+ fetchProvider: typeof fetchProvider,
+ removeProvider: typeof removeProvider,
+}
export default {
fetchProvider,
+ removeProvider,
}
diff --git a/src/components/Header/assets/connect-wallet.svg b/src/components/Header/assets/connect-wallet.svg
new file mode 100644
index 00000000..a04fe2bd
--- /dev/null
+++ b/src/components/Header/assets/connect-wallet.svg
@@ -0,0 +1,15 @@
+
diff --git a/src/components/Header/assets/connected-error.svg b/src/components/Header/assets/connected-error.svg
new file mode 100644
index 00000000..8c351246
--- /dev/null
+++ b/src/components/Header/assets/connected-error.svg
@@ -0,0 +1,6 @@
+
diff --git a/src/components/Header/assets/connected.svg b/src/components/Header/assets/connected.svg
new file mode 100644
index 00000000..1309cf1c
--- /dev/null
+++ b/src/components/Header/assets/connected.svg
@@ -0,0 +1,6 @@
+
diff --git a/src/components/Header/assets/dotRinkeby.svg b/src/components/Header/assets/dotRinkeby.svg
new file mode 100644
index 00000000..8b415e70
--- /dev/null
+++ b/src/components/Header/assets/dotRinkeby.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/components/Header/assets/gnosis-safe-logo.svg b/src/components/Header/assets/gnosis-safe-logo.svg
new file mode 100644
index 00000000..f125e106
--- /dev/null
+++ b/src/components/Header/assets/gnosis-safe-logo.svg
@@ -0,0 +1,22 @@
+
diff --git a/src/components/Header/assets/gnosis_logo.svg b/src/components/Header/assets/gnosis_logo.svg
deleted file mode 100644
index 32b13cfd..00000000
--- a/src/components/Header/assets/gnosis_logo.svg
+++ /dev/null
@@ -1,49 +0,0 @@
-
-
-
diff --git a/src/components/Header/assets/icon_metamask.svg b/src/components/Header/assets/icon_metamask.svg
deleted file mode 100644
index 7a96204f..00000000
--- a/src/components/Header/assets/icon_metamask.svg
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
diff --git a/src/components/Header/assets/icon_parity.svg b/src/components/Header/assets/icon_parity.svg
deleted file mode 100644
index 63da56db..00000000
--- a/src/components/Header/assets/icon_parity.svg
+++ /dev/null
@@ -1,72 +0,0 @@
-
-
-
diff --git a/src/components/Header/assets/metamask.svg b/src/components/Header/assets/metamask.svg
new file mode 100644
index 00000000..5c45d278
--- /dev/null
+++ b/src/components/Header/assets/metamask.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/components/Header/component/Connected/index.jsx b/src/components/Header/component/Connected/index.jsx
deleted file mode 100644
index 73c7b09e..00000000
--- a/src/components/Header/component/Connected/index.jsx
+++ /dev/null
@@ -1,40 +0,0 @@
-// @flow
-import * as React from 'react'
-import Img from '~/components/layout/Img'
-import Span from '~/components/layout/Span'
-import { upperFirst } from '~/utils/css'
-
-const IconParity = require('~/components/Header/assets/icon_parity.svg')
-const IconMetamask = require('~/components/Header/assets/icon_metamask.svg')
-
-type Props = {
- provider: string,
-}
-
-const PROVIDER_METAMASK = 'METAMASK'
-const PROVIDER_PARITY = 'PARITY'
-
-const PROVIDER_ICONS = {
- [PROVIDER_METAMASK]: IconMetamask,
- [PROVIDER_PARITY]: IconParity,
-}
-
-const Connected = ({ provider }: Props) => {
- const msg = `You are using ${upperFirst(provider.toLowerCase())} to connect to your Safe`
-
- return (
-
- { PROVIDER_ICONS[provider] &&
-
- }
- Connected
-
- )
-}
-
-export default Connected
diff --git a/src/components/Header/component/Layout.jsx b/src/components/Header/component/Layout.jsx
index 7afbb088..224a62cc 100644
--- a/src/components/Header/component/Layout.jsx
+++ b/src/components/Header/component/Layout.jsx
@@ -1,30 +1,77 @@
// @flow
-import React from 'react'
+import * as React from 'react'
+import { withStyles } from '@material-ui/core/styles'
+import Grow from '@material-ui/core/Grow'
+import ClickAwayListener from '@material-ui/core/ClickAwayListener'
+import Popper from '@material-ui/core/Popper'
+import List from '@material-ui/core/List'
+import Divider from '~/components/layout/Divider'
+import openHoc, { type Open } from '~/components/hoc/OpenHoc'
import Col from '~/components/layout/Col'
import Img from '~/components/layout/Img'
-import Refresh from '~/components/Refresh'
import Row from '~/components/layout/Row'
+import Spacer from '~/components/Spacer'
+import { border, sm, md } from '~/theme/variables'
+import Provider from './Provider'
-import Connected from './Connected'
-import NotConnected from './NotConnected'
+const logo = require('../assets/gnosis-safe-logo.svg')
-const logo = require('../assets/gnosis_logo.svg')
-
-type Props = {
- provider: string,
- reloadWallet: Function,
+type Props = Open & {
+ classes: Object,
+ providerDetails: React$Node,
+ providerInfo: React$Node,
}
-const Header = ({ provider, reloadWallet }: Props) => (
-
-
-
-
-
- { provider ? : }
-
-
-
-)
+const styles = () => ({
+ root: {
+ backgroundColor: 'white',
+ padding: 0,
+ boxShadow: '0 0 10px 0 rgba(33, 48, 77, 0.1)',
+ minWidth: '280px',
+ left: '4px',
+ },
+ summary: {
+ borderBottom: `solid 2px ${border}`,
+ alignItems: 'center',
+ height: '52px',
+ backgroundColor: 'white',
+ },
+ logo: {
+ padding: `${sm} ${md}`,
+ flexBasis: '95px',
+ },
+})
-export default Header
+const Layout = openHoc(({
+ open, toggle, classes, providerInfo, providerDetails,
+}: Props) => (
+
+
+
+
+
+
+
+
+
+ {providerRef => (
+
+ {({ TransitionProps }) => (
+
+
+
+ {providerDetails}
+
+
+
+ )}
+
+ )}
+
+
+
+))
+
+export default withStyles(styles)(Layout)
diff --git a/src/components/Header/component/Layout.stories.js b/src/components/Header/component/Layout.stories.js
index 778744d4..354ee223 100644
--- a/src/components/Header/component/Layout.stories.js
+++ b/src/components/Header/component/Layout.stories.js
@@ -1,9 +1,12 @@
// @flow
-import { select } from '@storybook/addon-knobs'
import { storiesOf } from '@storybook/react'
import * as React from 'react'
import styles from '~/components/layout/PageFrame/index.scss'
-import Component from './Layout'
+import Layout from './Layout'
+import ProviderInfo from './ProviderInfo'
+import ProviderDetails from './ProviderInfo/UserDetails'
+import ProviderDisconnected from './ProviderDisconnected'
+import ConnectDetails from './ProviderDisconnected/ConnectDetails'
const FrameDecorator = story => (
@@ -11,10 +14,34 @@ const FrameDecorator = story => (
)
-storiesOf('Components', module)
+storiesOf('Components /Header', module)
.addDecorator(FrameDecorator)
- .add('Header', () => {
- // https://github.com/storybooks/storybook/tree/master/addons/knobs#select
- const provider = select('Status by Provider', ['', 'UNKNOWN', 'METAMASK', 'PARITY'], 'METAMASK')
- return {}} />
+ .add('Connected', () => {
+ const provider = 'Metamask'
+ const userAddress = '0x873faa4cddd5b157e8e5a57e7a5479afc5d30moe'
+ const network = 'RINKEBY'
+ const info =
+ const details =
+
+ return
+ })
+ .add('Disconnected', () => {
+ const info =
+ const details =
+
+ return
+ })
+ .add('Connection Error', () => {
+ const provider = 'Metamask'
+ const userAddress = '0x873faa4cddd5b157e8e5a57e7a5479afc5d30moe'
+ const network = 'RINKEBY'
+ const info =
+ const details = ()
+
+ return
})
diff --git a/src/components/Header/component/NotConnected/index.jsx b/src/components/Header/component/NotConnected/index.jsx
deleted file mode 100644
index f2c75156..00000000
--- a/src/components/Header/component/NotConnected/index.jsx
+++ /dev/null
@@ -1,5 +0,0 @@
-// @flow
-import React from 'react'
-import Span from '~/components/layout/Span'
-
-export default () => Not Connected
diff --git a/src/components/Header/component/Provider.jsx b/src/components/Header/component/Provider.jsx
new file mode 100644
index 00000000..ca18ae35
--- /dev/null
+++ b/src/components/Header/component/Provider.jsx
@@ -0,0 +1,74 @@
+// @flow
+import * as React from 'react'
+import { withStyles } from '@material-ui/core/styles'
+import IconButton from '@material-ui/core/IconButton'
+import ExpandLess from '@material-ui/icons/ExpandLess'
+import ExpandMore from '@material-ui/icons/ExpandMore'
+import Col from '~/components/layout/Col'
+import { type Open } from '~/components/hoc/OpenHoc'
+import { sm, md } from '~/theme/variables'
+
+type Props = Open & {
+ classes: Object,
+ popupDetails: React$Node,
+ info: React$Node,
+ children: Function,
+}
+
+const styles = () => ({
+ root: {
+ height: '100%',
+ display: 'flex',
+ alignItems: 'center',
+ flexBasis: '250px',
+ },
+ provider: {
+ padding: `${sm} ${md}`,
+ alignItems: 'center',
+ flex: '1 1 auto',
+ display: 'flex',
+ cursor: 'pointer',
+ },
+ expand: {
+ width: '30px',
+ height: '30px',
+ },
+})
+
+type ProviderRef = { current: null | HTMLDivElement }
+
+class Provider extends React.Component {
+ constructor(props: Props) {
+ super(props)
+
+ this.myRef = React.createRef()
+ }
+
+ myRef: ProviderRef
+
+ render() {
+ const {
+ open, toggle, children, classes, info,
+ } = this.props
+
+ return (
+
+
+
+ { info }
+
+ { open ? : }
+
+
+
+ { children(this.myRef) }
+
+ )
+ }
+}
+
+export default withStyles(styles)(Provider)
diff --git a/src/components/Header/component/ProviderDisconnected/ConnectDetails.jsx b/src/components/Header/component/ProviderDisconnected/ConnectDetails.jsx
new file mode 100644
index 00000000..f885f162
--- /dev/null
+++ b/src/components/Header/component/ProviderDisconnected/ConnectDetails.jsx
@@ -0,0 +1,64 @@
+// @flow
+import * as React from 'react'
+import { withStyles } from '@material-ui/core/styles'
+import Paragraph from '~/components/layout/Paragraph'
+import Button from '~/components/layout/Button'
+import Img from '~/components/layout/Img'
+import Row from '~/components/layout/Row'
+import { md, lg } from '~/theme/variables'
+
+const connectedLogo = require('../../assets/connect-wallet.svg')
+
+type Props = {
+ classes: Object,
+ onConnect: Function,
+}
+
+const styles = () => ({
+ container: {
+ padding: `${md} 12px`,
+ },
+ logo: {
+ justifyContent: 'center',
+ },
+ text: {
+ letterSpacing: '-0.6px',
+ flexGrow: 1,
+ textAlign: 'center',
+ },
+ connect: {
+ padding: `${md} ${lg}`,
+ },
+ connectText: {
+ letterSpacing: '1px',
+ },
+ img: {
+ margin: '0px 2px',
+ },
+})
+
+const ConnectDetails = ({ classes, onConnect }: Props) => (
+
+
+
+
+
+
+
+
+
+)
+
+export default withStyles(styles)(ConnectDetails)
diff --git a/src/components/Header/component/ProviderDisconnected/index.jsx b/src/components/Header/component/ProviderDisconnected/index.jsx
new file mode 100644
index 00000000..9c3bc715
--- /dev/null
+++ b/src/components/Header/component/ProviderDisconnected/index.jsx
@@ -0,0 +1,43 @@
+// @flow
+import * as React from 'react'
+import { withStyles } from '@material-ui/core/styles'
+import Paragraph from '~/components/layout/Paragraph'
+import Col from '~/components/layout/Col'
+import Img from '~/components/layout/Img'
+import { type Open } from '~/components/hoc/OpenHoc'
+import { md } from '~/theme/variables'
+
+const connectWallet = require('../../assets/connect-wallet.svg')
+
+type Props = Open & {
+ classes: Object,
+ children: Function,
+}
+
+const styles = () => ({
+ network: {
+ fontFamily: 'Montserrat, sans-serif',
+ },
+ account: {
+ padding: `0 ${md}`,
+ display: 'flex',
+ flexDirection: 'column',
+ justifyContent: 'center',
+ flexGrow: 1,
+ },
+ connect: {
+ letterSpacing: '-0.5px',
+ },
+})
+
+const ProviderDesconnected = ({ classes }: Props) => (
+
+
+
+ Not Connected
+ Connect Wallet
+
+
+)
+
+export default withStyles(styles)(ProviderDesconnected)
diff --git a/src/components/Header/component/ProviderInfo/UserDetails.jsx b/src/components/Header/component/ProviderInfo/UserDetails.jsx
new file mode 100644
index 00000000..4559e8ea
--- /dev/null
+++ b/src/components/Header/component/ProviderInfo/UserDetails.jsx
@@ -0,0 +1,151 @@
+// @flow
+import * as React from 'react'
+import OpenInNew from '@material-ui/icons/OpenInNew'
+import { withStyles } from '@material-ui/core/styles'
+import Paragraph from '~/components/layout/Paragraph'
+import Button from '~/components/layout/Button'
+import Identicon from '~/components/Identicon'
+import Bold from '~/components/layout/Bold'
+import Hairline from '~/components/layout/Hairline'
+import Img from '~/components/layout/Img'
+import Row from '~/components/layout/Row'
+import Block from '~/components/layout/Block'
+import Spacer from '~/components/Spacer'
+import { xs, sm, md, lg, background } from '~/theme/variables'
+import { upperFirst } from '~/utils/css'
+import { shortVersionOf } from '~/logic/wallets/ethAddresses'
+import { openInEtherScan } from '~/logic/wallets/getWeb3'
+
+const metamask = require('../../assets/metamask.svg')
+const connectedLogo = require('../../assets/connected.svg')
+const connectedWarning = require('../../assets/connected-error.svg')
+const dot = require('../../assets/dotRinkeby.svg')
+
+type Props = {
+ provider: string,
+ connected: boolean,
+ network: string,
+ userAddress: string,
+ classes: Object,
+ onDisconnect: Function,
+}
+
+const openIconStyle = {
+ height: '16px',
+ color: '#467ee5',
+}
+
+const styles = () => ({
+ container: {
+ padding: `${md} 12px`,
+ display: 'flex',
+ flexDirection: 'column',
+ },
+ identicon: {
+ justifyContent: 'center',
+ padding: `0 ${md}`,
+ },
+ user: {
+ borderRadius: '3px',
+ backgroundColor: background,
+ margin: '0 auto',
+ padding: sm,
+ },
+ details: {
+ padding: `0 ${lg}`,
+ height: '20px',
+ alignItems: 'center',
+ },
+ address: {
+ flexGrow: 1,
+ textAlign: 'center',
+ letterSpacing: '-0.5px',
+ },
+ open: {
+ paddingLeft: sm,
+ width: 'auto',
+ '&:hover': {
+ cursor: 'pointer',
+ },
+ },
+ disconnect: {
+ padding: `${md} ${lg}`,
+ },
+ disconnectText: {
+ letterSpacing: '1px',
+ },
+ logo: {
+ margin: `0px ${xs}`,
+ },
+})
+
+const UserDetails = ({
+ provider, connected, network, userAddress, classes, onDisconnect,
+}: Props) => {
+ const status = connected ? 'Connected' : 'Connection error'
+ const address = userAddress ? shortVersionOf(userAddress, 6) : 'Address not available'
+ const identiconAddress = userAddress || 'random'
+ const connectionLogo = connected ? connectedLogo : connectedWarning
+ const color = connected ? 'primary' : 'warning'
+
+ return (
+
+
+
+
+
+
+ {address}
+ { userAddress &&
+
+ }
+
+
+
+
+ Status
+
+
+
+
+ {status}
+
+
+
+
+
+ Client
+
+
+
+
+ {upperFirst(provider)}
+
+
+
+
+
+ Network
+
+
+
+ {upperFirst(network)}
+
+
+
+
+
+
+
+ )
+}
+
+export default withStyles(styles)(UserDetails)
diff --git a/src/components/Header/component/ProviderInfo/index.jsx b/src/components/Header/component/ProviderInfo/index.jsx
new file mode 100644
index 00000000..dd675235
--- /dev/null
+++ b/src/components/Header/component/ProviderInfo/index.jsx
@@ -0,0 +1,64 @@
+// @flow
+import * as React from 'react'
+import { withStyles } from '@material-ui/core/styles'
+import Paragraph from '~/components/layout/Paragraph'
+import Col from '~/components/layout/Col'
+import Img from '~/components/layout/Img'
+import { sm } from '~/theme/variables'
+import Identicon from '~/components/Identicon'
+import { shortVersionOf } from '~/logic/wallets/ethAddresses'
+
+const connectedLogo = require('../../assets/connected.svg')
+const connectedWarning = require('../../assets/connected-error.svg')
+
+type Props = {
+ provider: string,
+ network: string,
+ classes: Object,
+ userAddress: string,
+ connected: boolean,
+}
+
+const styles = () => ({
+ network: {
+ fontFamily: 'Montserrat, sans-serif',
+ },
+ logo: {
+ top: '10px',
+ position: 'relative',
+ right: '13px',
+ },
+ account: {
+ paddingRight: sm,
+ display: 'flex',
+ flexDirection: 'column',
+ justifyContent: 'center',
+ flexGrow: 1,
+ },
+ address: {
+ letterSpacing: '-0.5px',
+ },
+})
+
+const ProviderInfo = ({
+ provider, network, userAddress, connected, classes,
+}: Props) => {
+ const providerText = `${provider} [${network}]`
+ const cutAddress = connected ? shortVersionOf(userAddress, 6) : 'Connection Error'
+ const color = connected ? 'primary' : 'warning'
+ const logo = connected ? connectedLogo : connectedWarning
+ const identiconAddress = userAddress || 'random'
+
+ return (
+
+
+
+
+ {providerText}
+ {cutAddress}
+
+
+ )
+}
+
+export default withStyles(styles)(ProviderInfo)
diff --git a/src/components/Header/index.jsx b/src/components/Header/index.jsx
index 83cefe88..f6ccca5d 100644
--- a/src/components/Header/index.jsx
+++ b/src/components/Header/index.jsx
@@ -1,29 +1,81 @@
// @flow
import * as React from 'react'
import { connect } from 'react-redux'
+import { logComponentStack, type Info } from '~/utils/logBoundaries'
+import ProviderInfo from './component/ProviderInfo'
+import ProviderDetails from './component/ProviderInfo/UserDetails'
+import ProviderDisconnected from './component/ProviderDisconnected'
+import ConnectDetails from './component/ProviderDisconnected/ConnectDetails'
import Layout from './component/Layout'
-import actions from './actions'
-import selector from './selector'
+import actions, { type Actions } from './actions'
+import selector, { type SelectorProps } from './selector'
-type Props = {
- provider: string,
- fetchProvider: Function,
+type Props = Actions & SelectorProps
+
+type State = {
+ hasError: boolean,
}
-class Header extends React.PureComponent {
+class Header extends React.PureComponent {
+ state = {
+ hasError: false,
+ }
+
componentDidMount() {
this.props.fetchProvider()
}
- reloadWallet = () => {
+ componentDidCatch(error: Error, info: Info) {
+ this.setState({ hasError: true })
+
+ logComponentStack(error, info)
+ }
+
+ onDisconnect = () => {
+ this.props.removeProvider()
+ }
+
+ onConnect = () => {
this.props.fetchProvider()
}
+ getProviderInfoBased = () => {
+ const { hasError } = this.state
+ const {
+ loaded, available, provider, network, userAddress,
+ } = this.props
+
+ if (hasError || !loaded) {
+ return
+ }
+
+ return
+ }
+
+ getProviderDetailsBased = () => {
+ const { hasError } = this.state
+ const {
+ loaded, available, provider, network, userAddress,
+ } = this.props
+
+ if (hasError || !loaded) {
+ return
+ }
+
+ return ()
+ }
+
render() {
- const { provider } = this.props
- return (
-
- )
+ const info = this.getProviderInfoBased()
+ const details = this.getProviderDetailsBased()
+
+ return
}
}
diff --git a/src/components/Header/selector.js b/src/components/Header/selector.js
index 11b9dd34..76b98813 100644
--- a/src/components/Header/selector.js
+++ b/src/components/Header/selector.js
@@ -1,7 +1,19 @@
// @flow
import { createStructuredSelector } from 'reselect'
-import { providerNameSelector } from '~/logic/wallets/store/selectors'
+import { providerNameSelector, userAccountSelector, networkSelector, availableSelector, loadedSelector } from '~/logic/wallets/store/selectors'
+
+export type SelectorProps = {
+ provider: string,
+ userAddress: string,
+ network: string,
+ loaded: boolean,
+ available: boolean,
+}
export default createStructuredSelector({
provider: providerNameSelector,
+ userAddress: userAccountSelector,
+ network: networkSelector,
+ loaded: loadedSelector,
+ available: availableSelector,
})
diff --git a/src/components/Identicon/blockies.js b/src/components/Identicon/blockies.js
new file mode 100644
index 00000000..1123ab5e
--- /dev/null
+++ b/src/components/Identicon/blockies.js
@@ -0,0 +1,366 @@
+/* eslint-disable */
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
+ typeof define === 'function' && define.amd ? define(['exports'], factory) :
+ (factory((global.blockies = {})));
+}(this, (function (exports) {
+ 'use strict';
+
+ /**
+ * A handy class to calculate color values.
+ *
+ * @version 1.0
+ * @author Robert Eisele
+ * @copyright Copyright (c) 2010, Robert Eisele
+ * @link http://www.xarg.org/2010/03/generate-client-side-png-files-using-javascript/
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
+ *
+ */
+
+
+ // helper functions for that ctx
+ function write(buffer, offs) {
+ for (var i = 2; i < arguments.length; i++) {
+ for (var j = 0; j < arguments[i].length; j++) {
+ buffer[offs++] = arguments[i].charAt(j);
+ }
+ }
+ }
+
+ function byte2(w) {
+ return String.fromCharCode((w >> 8) & 255, w & 255);
+ }
+
+ function byte4(w) {
+ return String.fromCharCode((w >> 24) & 255, (w >> 16) & 255, (w >> 8) & 255, w & 255);
+ }
+
+ function byte2lsb(w) {
+ return String.fromCharCode(w & 255, (w >> 8) & 255);
+ }
+
+ var PNG = function (width, height, depth) {
+
+ this.width = width;
+ this.height = height;
+ this.depth = depth;
+
+ // pixel data and row filter identifier size
+ this.pix_size = height * (width + 1);
+
+ // deflate header, pix_size, block headers, adler32 checksum
+ this.data_size = 2 + this.pix_size + 5 * Math.floor((0xfffe + this.pix_size) / 0xffff) + 4;
+
+ // offsets and sizes of Png chunks
+ this.ihdr_offs = 0; // IHDR offset and size
+ this.ihdr_size = 4 + 4 + 13 + 4;
+ this.plte_offs = this.ihdr_offs + this.ihdr_size; // PLTE offset and size
+ this.plte_size = 4 + 4 + 3 * depth + 4;
+ this.trns_offs = this.plte_offs + this.plte_size; // tRNS offset and size
+ this.trns_size = 4 + 4 + depth + 4;
+ this.idat_offs = this.trns_offs + this.trns_size; // IDAT offset and size
+ this.idat_size = 4 + 4 + this.data_size + 4;
+ this.iend_offs = this.idat_offs + this.idat_size; // IEND offset and size
+ this.iend_size = 4 + 4 + 4;
+ this.buffer_size = this.iend_offs + this.iend_size; // total PNG size
+
+ this.buffer = new Array();
+ this.palette = new Object();
+ this.pindex = 0;
+
+ var _crc32 = new Array();
+
+ // initialize buffer with zero bytes
+ for (var i = 0; i < this.buffer_size; i++) {
+ this.buffer[i] = "\x00";
+ }
+
+ // initialize non-zero elements
+ write(this.buffer, this.ihdr_offs, byte4(this.ihdr_size - 12), 'IHDR', byte4(width), byte4(height), "\x08\x03");
+ write(this.buffer, this.plte_offs, byte4(this.plte_size - 12), 'PLTE');
+ write(this.buffer, this.trns_offs, byte4(this.trns_size - 12), 'tRNS');
+ write(this.buffer, this.idat_offs, byte4(this.idat_size - 12), 'IDAT');
+ write(this.buffer, this.iend_offs, byte4(this.iend_size - 12), 'IEND');
+
+ // initialize deflate header
+ var header = ((8 + (7 << 4)) << 8) | (3 << 6);
+ header += 31 - (header % 31);
+
+ write(this.buffer, this.idat_offs + 8, byte2(header));
+
+ // initialize deflate block headers
+ for (var i = 0; (i << 16) - 1 < this.pix_size; i++) {
+ var size, bits;
+ if (i + 0xffff < this.pix_size) {
+ size = 0xffff;
+ bits = "\x00";
+ } else {
+ size = this.pix_size - (i << 16) - i;
+ bits = "\x01";
+ }
+ write(this.buffer, this.idat_offs + 8 + 2 + (i << 16) + (i << 2), bits, byte2lsb(size), byte2lsb(~size));
+ }
+
+ /* Create crc32 lookup table */
+ for (var i = 0; i < 256; i++) {
+ var c = i;
+ for (var j = 0; j < 8; j++) {
+ if (c & 1) {
+ c = -306674912 ^ ((c >> 1) & 0x7fffffff);
+ } else {
+ c = (c >> 1) & 0x7fffffff;
+ }
+ }
+ _crc32[i] = c;
+ }
+
+ // compute the index into a png for a given pixel
+ this.index = function (x, y) {
+ var i = y * (this.width + 1) + x + 1;
+ var j = this.idat_offs + 8 + 2 + 5 * Math.floor((i / 0xffff) + 1) + i;
+ return j;
+ };
+
+ // convert a color and build up the palette
+ this.color = function (red, green, blue, alpha) {
+
+ alpha = alpha >= 0 ? alpha : 255;
+ var color = (((((alpha << 8) | red) << 8) | green) << 8) | blue;
+
+ if (typeof this.palette[color] == "undefined") {
+ if (this.pindex == this.depth) return "\x00";
+
+ var ndx = this.plte_offs + 8 + 3 * this.pindex;
+
+ this.buffer[ndx + 0] = String.fromCharCode(red);
+ this.buffer[ndx + 1] = String.fromCharCode(green);
+ this.buffer[ndx + 2] = String.fromCharCode(blue);
+ this.buffer[this.trns_offs + 8 + this.pindex] = String.fromCharCode(alpha);
+
+ this.palette[color] = String.fromCharCode(this.pindex++);
+ }
+ return this.palette[color];
+ };
+
+ // output a PNG string, Base64 encoded
+ this.getBase64 = function () {
+
+ var s = this.getDump();
+
+ var ch = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+ var c1, c2, c3, e1, e2, e3, e4;
+ var l = s.length;
+ var i = 0;
+ var r = "";
+
+ do {
+ c1 = s.charCodeAt(i);
+ e1 = c1 >> 2;
+ c2 = s.charCodeAt(i + 1);
+ e2 = ((c1 & 3) << 4) | (c2 >> 4);
+ c3 = s.charCodeAt(i + 2);
+ if (l < i + 2) { e3 = 64; } else { e3 = ((c2 & 0xf) << 2) | (c3 >> 6); }
+ if (l < i + 3) { e4 = 64; } else { e4 = c3 & 0x3f; }
+ r += ch.charAt(e1) + ch.charAt(e2) + ch.charAt(e3) + ch.charAt(e4);
+ } while ((i += 3) < l);
+ return r;
+ };
+
+ // output a PNG string
+ this.getDump = function () {
+
+ // compute adler32 of output pixels + row filter bytes
+ var BASE = 65521; /* largest prime smaller than 65536 */
+ var NMAX = 5552; /* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
+ var s1 = 1;
+ var s2 = 0;
+ var n = NMAX;
+
+ for (var y = 0; y < this.height; y++) {
+ for (var x = -1; x < this.width; x++) {
+ s1 += this.buffer[this.index(x, y)].charCodeAt(0);
+ s2 += s1;
+ if ((n -= 1) == 0) {
+ s1 %= BASE;
+ s2 %= BASE;
+ n = NMAX;
+ }
+ }
+ }
+ s1 %= BASE;
+ s2 %= BASE;
+ write(this.buffer, this.idat_offs + this.idat_size - 8, byte4((s2 << 16) | s1));
+
+ // compute crc32 of the PNG chunks
+ function crc32(png, offs, size) {
+ var crc = -1;
+ for (var i = 4; i < size - 4; i += 1) {
+ crc = _crc32[(crc ^ png[offs + i].charCodeAt(0)) & 0xff] ^ ((crc >> 8) & 0x00ffffff);
+ }
+ write(png, offs + size - 4, byte4(crc ^ -1));
+ }
+
+ crc32(this.buffer, this.ihdr_offs, this.ihdr_size);
+ crc32(this.buffer, this.plte_offs, this.plte_size);
+ crc32(this.buffer, this.trns_offs, this.trns_size);
+ crc32(this.buffer, this.idat_offs, this.idat_size);
+ crc32(this.buffer, this.iend_offs, this.iend_size);
+
+ // convert PNG to string
+ return "\x89PNG\r\n\x1A\n" + this.buffer.join('');
+ };
+
+ this.fillRect = function (x, y, w, h, color) {
+ for (var i = 0; i < w; i++) {
+ for (var j = 0; j < h; j++) {
+ this.buffer[this.index(x + i, y + j)] = color;
+ }
+ }
+ };
+ };
+
+ // https://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion
+ /**
+ * Converts an HSL color value to RGB. Conversion formula
+ * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
+ * Assumes h, s, and l are contained in the set [0, 1] and
+ * returns r, g, and b in the set [0, 255].
+ *
+ * @param {number} h The hue
+ * @param {number} s The saturation
+ * @param {number} l The lightness
+ * @return {Array} The RGB representation
+ */
+
+ function hue2rgb(p, q, t) {
+ if (t < 0) t += 1;
+ if (t > 1) t -= 1;
+ if (t < 1 / 6) return p + (q - p) * 6 * t;
+ if (t < 1 / 2) return q;
+ if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
+ return p;
+ }
+
+ function hsl2rgb(h, s, l) {
+ var r, g, b;
+
+ if (s == 0) {
+ r = g = b = l; // achromatic
+ } else {
+ var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
+ var p = 2 * l - q;
+ r = hue2rgb(p, q, h + 1 / 3);
+ g = hue2rgb(p, q, h);
+ b = hue2rgb(p, q, h - 1 / 3);
+ }
+
+ return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255), 255];
+ }
+
+ // The random number is a js implementation of the Xorshift PRNG
+ var randseed = new Array(4); // Xorshift: [x, y, z, w] 32 bit values
+
+ function seedrand(seed) {
+ for (var i = 0; i < randseed.length; i++) {
+ randseed[i] = 0;
+ }
+ for (var i = 0; i < seed.length; i++) {
+ randseed[i % 4] = (randseed[i % 4] << 5) - randseed[i % 4] + seed.charCodeAt(i);
+ }
+ }
+
+ function rand() {
+ // based on Java's String.hashCode(), expanded to 4 32bit values
+ var t = randseed[0] ^ (randseed[0] << 11);
+
+ randseed[0] = randseed[1];
+ randseed[1] = randseed[2];
+ randseed[2] = randseed[3];
+ randseed[3] = randseed[3] ^ (randseed[3] >> 19) ^ t ^ (t >> 8);
+
+ return (randseed[3] >>> 0) / (1 << 31 >>> 0);
+ }
+
+ function createColor() {
+ //saturation is the whole color spectrum
+ var h = Math.floor(rand() * 360);
+ //saturation goes from 40 to 100, it avoids greyish colors
+ var s = rand() * 60 + 40;
+ //lightness can be anything from 0 to 100, but probabilities are a bell curve around 50%
+ var l = (rand() + rand() + rand() + rand()) * 25;
+
+ return [h / 360, s / 100, l / 100];
+ }
+
+ function createImageData(size) {
+ var width = size; // Only support square icons for now
+ var height = size;
+
+ var dataWidth = Math.ceil(width / 2);
+ var mirrorWidth = width - dataWidth;
+
+ var data = [];
+ for (var y = 0; y < height; y++) {
+ var row = [];
+ for (var x = 0; x < dataWidth; x++) {
+ // this makes foreground and background color to have a 43% (1/2.3) probability
+ // spot color has 13% chance
+ row[x] = Math.floor(rand() * 2.3);
+ }
+ var r = row.slice(0, mirrorWidth);
+ r.reverse();
+ row = row.concat(r);
+
+ for (var i = 0; i < row.length; i++) {
+ data.push(row[i]);
+ }
+ }
+
+ return data;
+ }
+
+ function buildOpts(opts) {
+ if (!opts.seed) {
+ throw 'No seed provided'
+ }
+
+ seedrand(opts.seed);
+
+ return Object.assign({
+ size: 8,
+ scale: 16,
+ color: createColor(),
+ bgcolor: createColor(),
+ spotcolor: createColor(),
+ }, opts)
+ }
+
+ function toDataUrl(address) {
+ const opts = buildOpts({ seed: address.toLowerCase() });
+
+ const imageData = createImageData(opts.size);
+ const width = Math.sqrt(imageData.length);
+
+ const p = new PNG(opts.size * opts.scale, opts.size * opts.scale, 3);
+ const bgcolor = p.color(...hsl2rgb(...opts.bgcolor));
+ const color = p.color(...hsl2rgb(...opts.color));
+ const spotcolor = p.color(...hsl2rgb(...opts.spotcolor));
+
+ for (var i = 0; i < imageData.length; i++) {
+ var row = Math.floor(i / width);
+ var col = i % width;
+ // if data is 0, leave the background
+ if (imageData[i]) {
+ // if data is 2, choose spot color, if 1 choose foreground
+ const pngColor = imageData[i] == 1 ? color : spotcolor;
+ p.fillRect(col * opts.scale, row * opts.scale, opts.scale, opts.scale, pngColor);
+ }
+ }
+ return `data:image/png;base64,${p.getBase64()}`;
+ }
+
+ exports.toDataUrl = toDataUrl;
+
+ Object.defineProperty(exports, '__esModule', { value: true });
+
+})));
diff --git a/src/components/Identicon/index.jsx b/src/components/Identicon/index.jsx
new file mode 100644
index 00000000..a6b3fecd
--- /dev/null
+++ b/src/components/Identicon/index.jsx
@@ -0,0 +1,67 @@
+// @flow
+import * as React from 'react'
+import { toDataUrl } from './blockies'
+
+type Props = {
+ address: string,
+ diameter: number,
+}
+
+type IdenticonRef = { current: null | HTMLDivElement }
+
+export default class Identicon extends React.PureComponent {
+ constructor(props: Props) {
+ super(props)
+
+ this.identicon = React.createRef()
+ }
+
+ componentDidMount = () => {
+ const { address, diameter } = this.props
+ const image = this.generateBlockieIdenticon(address, diameter)
+ if (this.identicon.current) {
+ this.identicon.current.appendChild(image)
+ }
+ }
+
+ componentDidUpdate = () => {
+ const { address, diameter } = this.props
+ const image = this.generateBlockieIdenticon(address, diameter)
+
+ if (!this.identicon.current) {
+ return
+ }
+
+ const { children } = this.identicon.current
+ for (let i = 0; i < children.length; i += 1) {
+ this.identicon.current.removeChild(children[i])
+ }
+
+ this.identicon.current.appendChild(image)
+ }
+
+ getStyleFrom = (diameter: number) => ({
+ width: diameter,
+ height: diameter,
+ })
+
+ generateBlockieIdenticon = (address: string, diameter: number) => {
+ const image = new window.Image()
+ image.src = toDataUrl(address)
+ image.height = diameter
+ image.width = diameter
+ image.style.borderRadius = `${diameter / 2}px`
+
+ return image
+ }
+
+ identicon: IdenticonRef
+
+ render() {
+ const style = this.getStyleFrom(this.props.diameter)
+
+ return (
+
+ )
+ }
+}
diff --git a/src/components/Spacer/index.jsx b/src/components/Spacer/index.jsx
new file mode 100644
index 00000000..114a94f1
--- /dev/null
+++ b/src/components/Spacer/index.jsx
@@ -0,0 +1,8 @@
+// @flow
+import * as React from 'react'
+
+const style = {
+ flexGrow: 1,
+}
+
+export default () =>
diff --git a/src/components/Stepper/index.jsx b/src/components/Stepper/index.jsx
index 20328615..5dc21fb6 100644
--- a/src/components/Stepper/index.jsx
+++ b/src/components/Stepper/index.jsx
@@ -164,6 +164,7 @@ class GnoStepper extends React.PureComponent {
const styles = {
root: {
flex: '1 1 auto',
+ backgroundColor: 'transparent',
},
}
diff --git a/src/components/layout/Block/index.jsx b/src/components/layout/Block/index.jsx
index a92dd060..d399bb82 100644
--- a/src/components/layout/Block/index.jsx
+++ b/src/components/layout/Block/index.jsx
@@ -2,12 +2,11 @@
import classNames from 'classnames/bind'
import React, { PureComponent } from 'react'
import { capitalize } from '~/utils/css'
+import { type Size } from '~/theme/size'
import styles from './index.scss'
const cx = classNames.bind(styles)
-type Size = 'sm' | 'md' | 'lg' | 'xl'
-
type Props = {
margin?: Size,
padding?: Size,
diff --git a/src/components/layout/Block/index.scss b/src/components/layout/Block/index.scss
index 62a278cb..8abbbde4 100644
--- a/src/components/layout/Block/index.scss
+++ b/src/components/layout/Block/index.scss
@@ -1,6 +1,9 @@
.block {
- width: 100%;
- overflow: hidden;
+
+}
+
+.xs {
+ margin-bottom: $xs;
}
.sm {
@@ -19,6 +22,10 @@
margin-bottom: $xl;
}
+.paddingXs {
+ padding-top: $xs;
+}
+
.paddingSm {
padding-top: $sm;
}
diff --git a/src/components/layout/Button/index.jsx b/src/components/layout/Button/index.jsx
index f0ee9a37..1c236242 100644
--- a/src/components/layout/Button/index.jsx
+++ b/src/components/layout/Button/index.jsx
@@ -1,4 +1,26 @@
// @flow
+import * as React from 'react'
import Button from '@material-ui/core/Button'
+import { withStyles } from '@material-ui/core/styles'
-export default Button
+const styles = {
+ root: {
+ borderRadius: 0,
+ },
+}
+
+type Props = {
+ minWidth?: number
+}
+
+const calculateStyleBased = minWidth => ({
+ minWidth: `${minWidth}px`,
+})
+
+const GnoButton = ({ minWidth, ...props }: Props) => {
+ const style = minWidth ? calculateStyleBased(minWidth) : undefined
+
+ return
+}
+
+export default withStyles(styles)(GnoButton)
diff --git a/src/components/layout/Col/index.jsx b/src/components/layout/Col/index.jsx
index f9ae8d87..bdb36df5 100644
--- a/src/components/layout/Col/index.jsx
+++ b/src/components/layout/Col/index.jsx
@@ -39,8 +39,8 @@ const Col = ({
}: Props) => {
const colClassNames = cx(
'col',
- start ? capitalize(start, 'start') : undefined,
center ? capitalize(center, 'center') : undefined,
+ start ? capitalize(start, 'start') : undefined,
end ? capitalize(end, 'end') : undefined,
top ? capitalize(top, 'top') : undefined,
middle ? capitalize(middle, 'middle') : undefined,
diff --git a/src/components/layout/Divider/index.js b/src/components/layout/Divider/index.js
new file mode 100644
index 00000000..7962d569
--- /dev/null
+++ b/src/components/layout/Divider/index.js
@@ -0,0 +1,14 @@
+// @flow
+import * as React from 'react'
+import { border } from '~/theme/variables'
+
+const style = {
+ height: '100%',
+ border: `solid 1px ${border}`,
+}
+
+const Divider = () => (
+
+)
+
+export default Divider
diff --git a/src/components/layout/Hairline/index.js b/src/components/layout/Hairline/index.js
index 99c4912d..8bebc215 100644
--- a/src/components/layout/Hairline/index.js
+++ b/src/components/layout/Hairline/index.js
@@ -1,15 +1,23 @@
// @flow
import * as React from 'react'
+import { type Size, getSize } from '~/theme/size'
+import { border } from '~/theme/variables'
-const hairlineStyle = {
+const calculateStyleFrom = (margin: Size) => ({
width: '100%',
- height: '2px',
- backgroundColor: '#d5d4d6',
- margin: '20px 0px',
+ height: '1px',
+ backgroundColor: border,
+ margin: `${getSize(margin)} 0px`,
+})
+
+type Props = {
+ margin?: Size,
}
-const Hairline = () => (
-
-)
+const Hairline = ({ margin = 'md' }: Props) => {
+ const style = calculateStyleFrom(margin)
+
+ return
+}
export default Hairline
diff --git a/src/components/layout/Heading/index.jsx b/src/components/layout/Heading/index.jsx
index 68676ae1..286e47e6 100644
--- a/src/components/layout/Heading/index.jsx
+++ b/src/components/layout/Heading/index.jsx
@@ -10,7 +10,7 @@ type HeadingTag = 'h1' | 'h2' | 'h3' | 'h4';
type Props = {
align?: 'left' | 'center' | 'right',
- margin?: 'sm' | 'md' | 'lg',
+ margin?: 'sm' | 'md' | 'lg' | 'xl',
tag: HeadingTag,
truncate?: boolean,
children: React$Node,
diff --git a/src/components/layout/Heading/index.scss b/src/components/layout/Heading/index.scss
index 32a232c4..982f49e6 100644
--- a/src/components/layout/Heading/index.scss
+++ b/src/components/layout/Heading/index.scss
@@ -1,22 +1,31 @@
.heading {
- font-weight: $boldFontWeight;
+ font-weight: normal;
line-height: normal;
margin: 0;
}
.h1 {
+ line-height: 36px;
+ font-weight: 500;
+ letter-spacing: -1px;
font-size: $(fontSizeHeadingLg)px;
}
.h2 {
+ line-height: 28px;
font-size: $(fontSizeHeadingMd)px;
}
.h3 {
+ line-height: 21px;
+ font-weight: bold;
+ font-family: 'Roboto Mono', monospace;
font-size: $(fontSizeHeadingSm)px;
}
.h4 {
+ line-height: 21px;
+ font-family: 'Roboto Mono', monospace;
font-size: $(fontSizeHeadingXs)px;
}
@@ -45,3 +54,7 @@
.marginLg {
margin: 0 0 $lg 0;
}
+
+.marginXl {
+ margin: 0 0 $xl 0;
+}
diff --git a/src/components/layout/Page/index.scss b/src/components/layout/Page/index.scss
index 845e9468..b50999b6 100644
--- a/src/components/layout/Page/index.scss
+++ b/src/components/layout/Page/index.scss
@@ -1,7 +1,8 @@
.page {
display: flex;
- flex-direction: column;
flex: 1 0 auto;
+ flex-direction: column;
+ padding: $xl;
}
.center {
diff --git a/src/components/layout/PageFrame/index.scss b/src/components/layout/PageFrame/index.scss
index 70e667f9..9727c40e 100644
--- a/src/components/layout/PageFrame/index.scss
+++ b/src/components/layout/PageFrame/index.scss
@@ -1,7 +1,6 @@
.frame {
display: flex;
flex-direction: column;
- flex: 1 0 auto;
- background-color: white;
- padding: $xl;
+ flex: 1 1 auto;
+ max-width: 100%;
}
diff --git a/src/components/layout/Paragraph/index.js b/src/components/layout/Paragraph/index.js
index 5de1372f..a9f08efc 100644
--- a/src/components/layout/Paragraph/index.js
+++ b/src/components/layout/Paragraph/index.js
@@ -8,20 +8,25 @@ const cx = classNames.bind(styles)
type Props = {
align?: 'right' | 'center' | 'left',
noMargin?: boolean,
- bold?: boolean,
+ weight?: 'light' | 'regular' | 'bolder' | 'bold',
size?: 'sm' | 'md' | 'lg' | 'xl',
- color?: 'soft' | 'medium' | 'dark' | 'primary',
- children: React$Node
+ color?: 'soft' | 'medium' | 'dark' | 'white' | 'fancy' | 'primary' | 'warning',
+ transform?: 'capitalize' | 'lowercase' | 'uppercase',
+ children: React$Node,
+ className?: string,
}
class Paragraph extends React.PureComponent {
render() {
const {
- bold, children, color, align, size, noMargin, ...props
+ weight, children, color, align, size, transform, noMargin, className, ...props
} = this.props
return (
-
+
{ children }
)
diff --git a/src/components/layout/Paragraph/index.scss b/src/components/layout/Paragraph/index.scss
index 3ddb5960..fcbadac8 100644
--- a/src/components/layout/Paragraph/index.scss
+++ b/src/components/layout/Paragraph/index.scss
@@ -1,6 +1,6 @@
.paragraph {
text-overflow: ellipsis;
- overflow-x: inherit;
+ overflow-x: inherit;
}
.soft {
@@ -15,8 +15,31 @@
color: black;
}
+.fancy {
+ color: $fancy;
+}
+
+.warning {
+ color: $warning;
+}
.primary {
- color: #00a6c4;
+ color: $fontColor;
+}
+
+.white {
+ color: white;
+}
+
+.capitalize {
+ text-transform: capitalize
+}
+
+.lowercase {
+ text-transform: lowercase
+}
+
+.uppercase {
+ text-transform: uppercase
}
.noMargin{
@@ -47,10 +70,22 @@
font-size: $largeFontSize;
}
-.lg {
+.xl {
font-size: $extraLargeFontSize;
}
+.light {
+ font-weight: $lightFont;
+}
+
+.regular {
+ font-weight: $regularFont;
+}
+
+.bolder {
+ font-weight: $bolderFont;
+}
+
.bold {
- font-weight: bold;
+ font-weight: $boldFont;
}
\ No newline at end of file
diff --git a/src/components/layout/Row/index.scss b/src/components/layout/Row/index.scss
index de411935..519162c3 100644
--- a/src/components/layout/Row/index.scss
+++ b/src/components/layout/Row/index.scss
@@ -16,7 +16,7 @@
}
.marginLg {
- margin-bottom: $xl;
+ margin-bottom: $lg;
}
.marginXl {
diff --git a/src/index.scss b/src/index.scss
index 5b54a6b7..2948797d 100644
--- a/src/index.scss
+++ b/src/index.scss
@@ -10,19 +10,16 @@ body {
left: 0;
right: 0;
overflow-x: hidden;
- color: #1f5f76;
- font-family: 'Montserrat', sans-serif;
+ color: $fontColor;
+ font-family: 'Roboto Mono', monospace;
font-size: $mediumFontSize;
- margin: 0;
+ margin: 0;
+ background-color: $background;
}
body>div:first-child {
display: flex;
- flex: 1 1 auto;
- flex-direction: column;
- min-height: calc(100% - (2 * $xl));
- padding: $xl;
- background-image: linear-gradient(to bottom, $primary, #1a829d, #1a829d, #1f5f76);
+ min-height: 100%;
}
h1, h2, h3 {
diff --git a/src/logic/safe/safeBlockchainOperations.js b/src/logic/safe/safeBlockchainOperations.js
index efa2d531..d71be6c7 100644
--- a/src/logic/safe/safeBlockchainOperations.js
+++ b/src/logic/safe/safeBlockchainOperations.js
@@ -118,8 +118,14 @@ export const executeDailyLimit = async (
const gas = await calculateGasOf(dailyLimitData, sender, dailyLimitModule.address)
const gasPrice = await calculateGasPrice()
- const txReceipt = await dailyLimitModule.executeDailyLimit(0, to, valueInWei, { from: sender, gas, gasPrice })
- checkReceiptStatus(txReceipt.tx)
+ try {
+ const txReceipt = await dailyLimitModule.executeDailyLimit(0, to, valueInWei, { from: sender, gas, gasPrice })
+ await checkReceiptStatus(txReceipt.tx)
+
+ return Promise.resolve(txReceipt.tx)
+ } catch (err) {
+ return Promise.reject(new Error(err))
+ }
/*
// Temporarily disabled for daily limit operations
@@ -128,5 +134,4 @@ export const executeDailyLimit = async (
await submitOperation(safeAddress, to, Number(valueInWei), data, operation, nonce, txReceipt.tx, sender, 'execution')
*/
- return txReceipt.tx
}
diff --git a/src/logic/wallets/ethAddresses.js b/src/logic/wallets/ethAddresses.js
index a84ebf46..df1ec918 100644
--- a/src/logic/wallets/ethAddresses.js
+++ b/src/logic/wallets/ethAddresses.js
@@ -10,3 +10,10 @@ export const sameAddress = (firstAddress: string, secondAddress: string): boolea
return firstAddress.toLowerCase() === secondAddress.toLowerCase()
}
+
+export const shortVersionOf = (address: string, cut: number) => {
+ const initial = cut
+ const final = 42 - cut
+
+ return `${address.substring(0, initial)}...${address.substring(final)}`
+}
diff --git a/src/logic/wallets/ethTransactions.js b/src/logic/wallets/ethTransactions.js
index a16dc121..d7fecb2e 100644
--- a/src/logic/wallets/ethTransactions.js
+++ b/src/logic/wallets/ethTransactions.js
@@ -9,7 +9,7 @@ export const EMPTY_DATA = '0x'
export const checkReceiptStatus = async (hash: string) => {
if (!hash) {
- throw new Error('No valid Tx hash to get receipt from')
+ return Promise.reject(new Error('No valid Tx hash to get receipt from'))
}
const web3 = getWeb3()
@@ -17,13 +17,15 @@ export const checkReceiptStatus = async (hash: string) => {
const { status } = txReceipt
if (!status) {
- throw new Error('No status found on this transaction receipt')
+ return Promise.reject(new Error('No status found on this transaction receipt'))
}
const hasError = status === '0x0'
if (hasError) {
- throw new Error('Obtained a transaction failure in the receipt')
+ return Promise.reject(new Error('Obtained a transaction failure in the receipt'))
}
+
+ return Promise.resolve()
}
export const calculateGasPrice = async () => {
@@ -50,7 +52,11 @@ export const calculateGasPrice = async () => {
export const calculateGasOf = async (data: Object, from: string, to: string) => {
const web3 = getWeb3()
- const gas = await promisify(cb => web3.eth.estimateGas({ data, from, to }, cb))
+ try {
+ const gas = await promisify(cb => web3.eth.estimateGas({ data, from, to }, cb))
- return gas * 2
+ return gas * 2
+ } catch (err) {
+ return Promise.reject(new Error(err))
+ }
}
diff --git a/src/logic/wallets/getWeb3.js b/src/logic/wallets/getWeb3.js
index 9ffdcba1..2dd0a794 100644
--- a/src/logic/wallets/getWeb3.js
+++ b/src/logic/wallets/getWeb3.js
@@ -4,6 +4,39 @@ import Web3 from 'web3'
import type { ProviderProps } from '~/logic/wallets/store/model/provider'
import { promisify } from '~/utils/promisify'
+export const ETHEREUM_NETWORK = {
+ MAIN: 'MAIN',
+ MORDEN: 'MORDEN',
+ ROPSTEN: 'ROPSTEN',
+ RINKEBY: 'RINKEBY',
+ KOVAN: 'KOVAN',
+ UNKNOWN: 'UNKNOWN',
+}
+
+export const WALLET_PROVIDER = {
+ METAMASK: 'METAMASK',
+ PARITY: 'PARITY',
+ REMOTE: 'REMOTE',
+ UPORT: 'UPORT',
+}
+
+export const ETHEREUM_NETWORK_IDS = {
+ // $FlowFixMe
+ 1: ETHEREUM_NETWORK.MAIN,
+ // $FlowFixMe
+ 2: ETHEREUM_NETWORK.MORDEN,
+ // $FlowFixMe
+ 3: ETHEREUM_NETWORK.ROPSTEN,
+ // $FlowFixMe
+ 4: ETHEREUM_NETWORK.RINKEBY,
+ // $FlowFixMe
+ 42: ETHEREUM_NETWORK.KOVAN,
+}
+
+export const openInEtherScan = (address: string, network: string) => () => {
+ window.open(`https://${network}.etherscan.io/address/${address}`)
+}
+
let web3
export const getWeb3 = () => web3 || new Web3(window.web3.currentProvider)
@@ -19,10 +52,16 @@ const getAccountFrom: Function = async (web3Provider): Promise =>
return accounts && accounts.length > 0 ? accounts[0] : null
}
+const getNetworkIdFrom = async (web3Provider) => {
+ const networkId = await promisify(cb => web3Provider.version.getNetwork(cb))
+
+ return networkId
+}
+
export const getProviderInfo: Function = async (): Promise => {
if (typeof window.web3 === 'undefined') {
return {
- name: '', available: false, loaded: false, account: '',
+ name: '', available: false, loaded: false, account: '', network: 0,
}
}
@@ -34,8 +73,10 @@ export const getProviderInfo: Function = async (): Promise => {
console.log('Injected web3 detected.')
}
- const name = isMetamask(web3) ? 'METAMASK' : 'UNKNOWN'
+ const name = isMetamask(web3) ? WALLET_PROVIDER.METAMASK : 'UNKNOWN'
const account = await getAccountFrom(web3)
+ const network = await getNetworkIdFrom(web3)
+
const available = account !== null
return {
@@ -43,6 +84,7 @@ export const getProviderInfo: Function = async (): Promise => {
available,
loaded: true,
account,
+ network,
}
}
diff --git a/src/logic/wallets/store/actions/fetchProvider.js b/src/logic/wallets/store/actions/fetchProvider.js
index 2fdb2962..3d523e46 100644
--- a/src/logic/wallets/store/actions/fetchProvider.js
+++ b/src/logic/wallets/store/actions/fetchProvider.js
@@ -7,11 +7,11 @@ import addProvider from './addProvider'
export const processProviderResponse = (dispatch: ReduxDispatch<*>, response: ProviderProps) => {
const {
- name, available, loaded, account,
+ name, available, loaded, account, network,
} = response
const walletRecord = makeProvider({
- name, available, loaded, account,
+ name, available, loaded, account, network,
})
dispatch(addProvider(walletRecord))
diff --git a/src/logic/wallets/store/actions/index.js b/src/logic/wallets/store/actions/index.js
index db7cb3e2..b4493b0c 100644
--- a/src/logic/wallets/store/actions/index.js
+++ b/src/logic/wallets/store/actions/index.js
@@ -3,3 +3,4 @@ export * from './addProvider'
export * from './fetchProvider'
export { default as addProvider } from './addProvider'
export { default as fetchProvider } from './fetchProvider'
+export { default as removeProvider } from './removeProvider'
diff --git a/src/logic/wallets/store/actions/removeProvider.js b/src/logic/wallets/store/actions/removeProvider.js
new file mode 100644
index 00000000..c721cd32
--- /dev/null
+++ b/src/logic/wallets/store/actions/removeProvider.js
@@ -0,0 +1,17 @@
+// @flow
+import type { Dispatch as ReduxDispatch } from 'redux'
+import { makeProvider, type ProviderProps, type Provider } from '~/logic/wallets/store/model/provider'
+import addProvider from './addProvider'
+
+export default () => async (dispatch: ReduxDispatch<*>) => {
+ const providerProps: ProviderProps = {
+ name: '',
+ available: false,
+ loaded: false,
+ account: '',
+ network: 0,
+ }
+
+ const provider: Provider = makeProvider(providerProps)
+ dispatch(addProvider(provider))
+}
diff --git a/src/logic/wallets/store/model/provider.js b/src/logic/wallets/store/model/provider.js
index 753cb974..a62e4082 100644
--- a/src/logic/wallets/store/model/provider.js
+++ b/src/logic/wallets/store/model/provider.js
@@ -7,6 +7,7 @@ export type ProviderProps = {
loaded: boolean,
available: boolean,
account: string,
+ network: number,
}
export const makeProvider: RecordFactory = Record({
@@ -14,6 +15,7 @@ export const makeProvider: RecordFactory = Record({
loaded: false,
available: false,
account: '',
+ network: 0,
})
export type Provider = RecordOf
diff --git a/src/logic/wallets/store/selectors/index.js b/src/logic/wallets/store/selectors/index.js
index 208acf3a..17fdf1f3 100644
--- a/src/logic/wallets/store/selectors/index.js
+++ b/src/logic/wallets/store/selectors/index.js
@@ -2,6 +2,7 @@
import { createSelector } from 'reselect'
import type { Provider } from '~/logic/wallets/store/model/provider'
import { PROVIDER_REDUCER_ID } from '~/logic/wallets/store/reducer/provider'
+import { ETHEREUM_NETWORK_IDS, ETHEREUM_NETWORK } from '~/logic/wallets/getWeb3'
const providerSelector = (state: any): Provider => state[PROVIDER_REDUCER_ID]
@@ -18,9 +19,27 @@ export const providerNameSelector = createSelector(
providerSelector,
(provider: Provider) => {
const name = provider.get('name')
- const loaded = provider.get('loaded')
- const available = provider.get('available')
- return loaded && available ? name : undefined
+ return name ? name.toLowerCase() : undefined
},
)
+
+export const networkSelector = createSelector(
+ providerSelector,
+ (provider: Provider) => {
+ const networkId = provider.get('network')
+ const network = ETHEREUM_NETWORK_IDS[networkId] || ETHEREUM_NETWORK.UNKNOWN
+
+ return network
+ },
+)
+
+export const loadedSelector = createSelector(
+ providerSelector,
+ (provider: Provider) => provider.get('loaded'),
+)
+
+export const availableSelector = createSelector(
+ providerSelector,
+ (provider: Provider) => provider.get('available'),
+)
diff --git a/src/logic/wallets/store/test/name.selector.js b/src/logic/wallets/store/test/name.selector.js
index 212242a6..04838e7a 100644
--- a/src/logic/wallets/store/test/name.selector.js
+++ b/src/logic/wallets/store/test/name.selector.js
@@ -16,7 +16,7 @@ const providerReducerTests = () => {
expect(providerName).toEqual(undefined)
})
- it('should return undefined when Metamask is loaded but not available', () => {
+ it('should return metamask when Metamask is loaded but not available', () => {
// GIVEN
const reduxStore = { [PROVIDER_REDUCER_ID]: ProviderFactory.metamaskLoaded }
@@ -24,7 +24,7 @@ const providerReducerTests = () => {
const providerName = providerNameSelector(reduxStore)
// THEN
- expect(providerName).toEqual(undefined)
+ expect(providerName).toEqual('metamask')
})
it('should return METAMASK when Metamask is loaded and available', () => {
@@ -35,7 +35,7 @@ const providerReducerTests = () => {
const providerName = providerNameSelector(reduxStore)
// THEN
- expect(providerName).toEqual('METAMASK')
+ expect(providerName).toEqual('metamask')
})
})
}
diff --git a/src/logic/wallets/store/test/provider.reducer.js b/src/logic/wallets/store/test/provider.reducer.js
index e9791bb3..1d3a9e38 100644
--- a/src/logic/wallets/store/test/provider.reducer.js
+++ b/src/logic/wallets/store/test/provider.reducer.js
@@ -25,7 +25,7 @@ const providerReducerTests = () => {
it('reducer should return default Provider record when no Metamask is loaded', () => {
// GIVEN
const emptyResponse: ProviderProps = {
- name: '', loaded: false, available: false, account: '',
+ name: '', loaded: false, available: false, account: '', network: 0,
}
// WHEN
@@ -39,7 +39,7 @@ const providerReducerTests = () => {
it('reducer should return avaiable with its default value when is loaded but not available', () => {
// GIVEN
const metamaskLoaded: ProviderProps = {
- name: 'METAMASK', loaded: true, available: false, account: '',
+ name: 'METAMASK', loaded: true, available: false, account: '', network: 0,
}
// WHEN
@@ -53,7 +53,7 @@ const providerReducerTests = () => {
it('reducer should return metamask provider when it is loaded and available', () => {
// GIVEN
const metamask: ProviderProps = {
- name: 'METAMASK', loaded: true, available: true, account: '',
+ name: 'METAMASK', loaded: true, available: true, account: '', network: 0,
}
// WHEN
diff --git a/src/routes/open/components/SafeForm/Owners/index.jsx b/src/routes/open/components/SafeForm/Owners/index.jsx
index fa51f1b3..e5f395c5 100644
--- a/src/routes/open/components/SafeForm/Owners/index.jsx
+++ b/src/routes/open/components/SafeForm/Owners/index.jsx
@@ -54,7 +54,7 @@ const Owners = (props: Props) => {
- Owner Nº {index + 1}
+ Owner Nº {index + 1}
{
+class GnoTransaction extends React.PureComponent {
onProccesClick = () => this.props.onProcessTx(this.props.transaction, this.props.confirmed)
hasConfirmed = (userAddress: string, confirmations: List): boolean =>
@@ -102,10 +102,10 @@ class GnoTransaction extends React.PureComponent {
destination={transaction.get('destination')}
threshold={threshold}
/> }
-
+
)
}
}
-export default connect(selector)(openHoc(GnoTransaction))
+export default openHoc(connect(selector)(GnoTransaction))
diff --git a/src/routes/welcome/assets/new.svg b/src/routes/welcome/assets/new.svg
new file mode 100644
index 00000000..f308edc1
--- /dev/null
+++ b/src/routes/welcome/assets/new.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/routes/welcome/assets/safe.svg b/src/routes/welcome/assets/safe.svg
new file mode 100644
index 00000000..43ec9182
--- /dev/null
+++ b/src/routes/welcome/assets/safe.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/routes/welcome/assets/vault.svg b/src/routes/welcome/assets/vault.svg
deleted file mode 100644
index 46b00a87..00000000
--- a/src/routes/welcome/assets/vault.svg
+++ /dev/null
@@ -1,86 +0,0 @@
-
diff --git a/src/routes/welcome/components/Layout.jsx b/src/routes/welcome/components/Layout.jsx
index fa42d03d..7766d5f3 100644
--- a/src/routes/welcome/components/Layout.jsx
+++ b/src/routes/welcome/components/Layout.jsx
@@ -1,13 +1,16 @@
// @flow
import * as React from 'react'
import Block from '~/components/layout/Block'
+import Heading from '~/components/layout/Heading'
import Img from '~/components/layout/Img'
import Button from '~/components/layout/Button'
import Link from '~/components/layout/Link'
import { OPEN_ADDRESS } from '~/routes/routes'
+import { sm } from '~/theme/variables'
import styles from './Layout.scss'
-const vault = require('../assets/vault.svg')
+const safe = require('../assets/safe.svg')
+const plus = require('../assets/new.svg')
type Props = {
provider: string
@@ -15,26 +18,63 @@ type Props = {
type SafeProps = {
provider: string,
- size?: 'small' | 'medium',
+ size?: 'small' | 'medium' | 'large',
+}
+
+const buttonStyle = {
+ marginLeft: sm,
}
export const CreateSafe = ({ size, provider }: SafeProps) => (
+)
+
+export const LoadSafe = ({ size, provider }: SafeProps) => (
+
)
const Welcome = ({ provider }: Props) => (
-
+
+ Welcome to the Gnosis
+
+
+ Safe Team Edition
+
+
+ The Gnosis Safe Team Edition is geared towards teams managing
+ shared crypto funds. It is an improvement of the existing Gnosis
+ MultiSig wallet with redesigned smart contracts, cheaper setup and
+ transaction costs as well as an enhanced user experience.
+
-
+
+
+
+
)
diff --git a/src/routes/welcome/components/Layout.scss b/src/routes/welcome/components/Layout.scss
index 681515f7..aeb5a001 100644
--- a/src/routes/welcome/components/Layout.scss
+++ b/src/routes/welcome/components/Layout.scss
@@ -1,18 +1,15 @@
.safe {
- display: grid;
justify-content: center;
- grid-row-gap: $xl;
+ justify-items: center;
+ margin-top: $xl;
}
-.safeActions {
+.summary {
display: flex;
justify-content: space-around;
}
-@media(max-width: $(screenXsMax)px) {
- .safeActions {
- grid-row-gap: $md;
- display: grid;
- justify-items: center;
- }
+.safeActions {
+ display: flex;
+ justify-content: center;
}
diff --git a/src/test/dailyLimit.blockchain.test.js b/src/test/dailyLimit.blockchain.test.js
index fcb2bc9e..a4e8082a 100644
--- a/src/test/dailyLimit.blockchain.test.js
+++ b/src/test/dailyLimit.blockchain.test.js
@@ -28,6 +28,6 @@ describe('Safe Blockchain Test', () => {
await executeWithdrawOn(safe, value)
// THEN
- expect(executeWithdrawOn(safe, value)).rejects.toThrow('VM Exception while processing transaction: revert')
+ expect(executeWithdrawOn(safe, value)).rejects.toThrow('VM Exception while processing transaction: revert Daily limit has been reached')
})
})
diff --git a/src/theme/mui.js b/src/theme/mui.js
index 325d2da9..300cf447 100644
--- a/src/theme/mui.js
+++ b/src/theme/mui.js
@@ -1,7 +1,7 @@
// @flow
import red from '@material-ui/core/colors/red'
import { createMuiTheme } from '@material-ui/core/styles'
-import { primary, secondary } from './variables'
+import { mediumFontSize, primary, secondary, md, lg } from './variables'
export type WithStyles = {
classes: Object,
@@ -25,5 +25,21 @@ export default createMuiTheme({
typography: {
fontFamily: 'Montserrat,sans-serif',
},
+ overrides: {
+ MuiButton: {
+ root: {
+ fontFamily: 'Roboto Mono, monospace',
+ letterSpacing: '1px',
+ },
+ containedPrimary: {
+ backgroundColor: '#467ee5',
+ },
+ sizeLarge: {
+ padding: `${md} ${lg}`,
+ minHeight: '52px',
+ fontSize: mediumFontSize,
+ },
+ },
+ },
palette,
})
diff --git a/src/theme/size.js b/src/theme/size.js
new file mode 100644
index 00000000..13e11797
--- /dev/null
+++ b/src/theme/size.js
@@ -0,0 +1,21 @@
+// @flow
+import { xs, sm, md, lg, xl } from '~/theme/variables'
+
+export type Size = 'xs' | 'sm' | 'md' | 'lg' | 'xl'
+
+export const getSize = (size: Size) => {
+ switch (size) {
+ case 'xs':
+ return xs
+ case 'sm':
+ return sm
+ case 'md':
+ return md
+ case 'lg':
+ return lg
+ case 'xl':
+ return xl
+ default:
+ return md
+ }
+}
diff --git a/src/theme/variables.js b/src/theme/variables.js
index 52c9a40d..94185584 100644
--- a/src/theme/variables.js
+++ b/src/theme/variables.js
@@ -1,31 +1,45 @@
// @flow
-const primary = '#1798cc'
+const border = '#eaebef'
+const background = '#f4f4f9'
+const primary = '#4a5579'
const secondary = '#13222b'
const tertiary = '#f6f9fc'
+const fontColor = '#4a5579'
+const fancyColor = '#fd7890'
+const warningColor = '#c97c05'
const xs = '4px'
const sm = '8px'
const md = '16px'
const lg = '24px'
-const xl = '42px'
+const xl = '32px'
+const xxl = '40px'
module.exports = Object.assign({}, {
primary,
secondary,
tertiary,
+ background,
+ fontColor,
+ fancy: fancyColor,
+ warning: warningColor,
xs,
sm,
md,
lg,
xl,
- fontSizeHeadingXs: 16,
+ xxl,
+ border,
+ fontSizeHeadingXs: 13,
fontSizeHeadingSm: 18,
fontSizeHeadingMd: 21,
- fontSizeHeadingLg: 28,
- regularFontWeight: 400,
- boldFontWeight: 700,
- smallFontSize: '12px',
- mediumFontSize: '14px',
- largeFontSize: '18px',
+ fontSizeHeadingLg: 32,
+ lightFont: 300,
+ regularFont: 400,
+ bolderFont: 500,
+ boldFont: 700,
+ smallFontSize: '11px',
+ mediumFontSize: '13px',
+ largeFontSize: '15px',
extraLargeFontSize: '24px',
screenXs: 480,
screenXsMax: 767,
diff --git a/src/utils/css.js b/src/utils/css.js
index 3c556b23..34dddbe7 100644
--- a/src/utils/css.js
+++ b/src/utils/css.js
@@ -1,5 +1,5 @@
// @flow
-export const upperFirst = (value: string) => value.charAt(0).toUpperCase() + value.slice(1)
+export const upperFirst = (value: string) => value.charAt(0).toUpperCase() + value.toLowerCase().slice(1)
type Value = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'center' | 'end' | 'start' | number | boolean
diff --git a/src/utils/logBoundaries.js b/src/utils/logBoundaries.js
new file mode 100644
index 00000000..9db3a6a6
--- /dev/null
+++ b/src/utils/logBoundaries.js
@@ -0,0 +1,12 @@
+// @flow
+export type Info = {
+ componentStack: string,
+}
+
+export const logComponentStack = (error: Error, info: Info) => {
+ // eslint-disable-next-line
+ console.log(error)
+ // eslint-disable-next-line
+ console.log(info.componentStack)
+}
+
diff --git a/yarn.lock b/yarn.lock
index 9264d541..af0f8771 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -860,12 +860,17 @@
"@babel/plugin-syntax-dynamic-import" "7.0.0-beta.51"
"@babel/plugin-syntax-import-meta" "7.0.0-beta.51"
-"@babel/runtime@^7.0.0-beta.42":
- version "7.0.0-beta.51"
- resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.0.0-beta.51.tgz#48b8ed18307034c6620f643514650ca2ccc0165a"
+"@babel/runtime@7.0.0", "@babel/runtime@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.0.0.tgz#adeb78fedfc855aa05bc041640f3f6f98e85424c"
dependencies:
- core-js "^2.5.7"
- regenerator-runtime "^0.11.1"
+ regenerator-runtime "^0.12.0"
+
+"@babel/runtime@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.0.0-rc.1.tgz#42f36fc5817911c89ea75da2b874054922967616"
+ dependencies:
+ regenerator-runtime "^0.12.0"
"@babel/template@7.0.0-beta.44":
version "7.0.0-beta.44"
@@ -935,11 +940,11 @@
version "0.2.14"
resolved "https://registry.yarnpkg.com/@gnosis.pm/util-contracts/-/util-contracts-0.2.14.tgz#587cd6268a7d08dbc0d08b1c7bd375e19549d833"
-"@material-ui/core@^1.2.1":
- version "1.2.2"
- resolved "https://registry.yarnpkg.com/@material-ui/core/-/core-1.2.2.tgz#b074bdaa679d68af235b4d3f108f828ddcf6c1bc"
+"@material-ui/core@^3.0.1":
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/@material-ui/core/-/core-3.0.1.tgz#e8476394a42d89ae404355ddbc093db4d044b225"
dependencies:
- "@babel/runtime" "^7.0.0-beta.42"
+ "@babel/runtime" "7.0.0"
"@types/jss" "^9.5.3"
"@types/react-transition-group" "^2.0.8"
brcast "^3.0.1"
@@ -949,6 +954,7 @@
deepmerge "^2.0.1"
dom-helpers "^3.2.1"
hoist-non-react-statics "^2.5.0"
+ is-plain-object "^2.0.4"
jss "^9.3.3"
jss-camel-case "^6.0.0"
jss-default-unit "^8.0.2"
@@ -958,20 +964,20 @@
jss-vendor-prefixer "^7.0.0"
keycode "^2.1.9"
normalize-scroll-left "^0.1.2"
+ popper.js "^1.14.1"
prop-types "^15.6.0"
- react-event-listener "^0.6.0"
+ react-event-listener "^0.6.2"
react-jss "^8.1.0"
- react-popper "^1.0.0"
react-transition-group "^2.2.1"
- recompose "^0.27.0"
- scroll "^2.0.3"
+ recompose "^0.29.0"
warning "^4.0.1"
-"@material-ui/icons@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@material-ui/icons/-/icons-1.1.0.tgz#4d025df7b0ba6ace8d6710079ed76013a4d26595"
+"@material-ui/icons@^3.0.1":
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/@material-ui/icons/-/icons-3.0.1.tgz#671fb3d04dcaf9351dbbd2bf82ae2ae72e3d93cd"
dependencies:
- recompose "^0.26.0 || ^0.27.0"
+ "@babel/runtime" "7.0.0"
+ recompose "^0.29.0"
"@mrmlnc/readdir-enhanced@^2.2.1":
version "2.2.1"
@@ -3963,13 +3969,6 @@ create-react-class@^15.5.2, create-react-class@^15.6.0, create-react-class@^15.6
loose-envify "^1.3.1"
object-assign "^4.1.1"
-create-react-context@^0.2.1:
- version "0.2.2"
- resolved "https://registry.yarnpkg.com/create-react-context/-/create-react-context-0.2.2.tgz#9836542f9aaa22868cd7d4a6f82667df38019dca"
- dependencies:
- fbjs "^0.8.0"
- gud "^1.0.0"
-
cross-spawn@5.1.0, cross-spawn@^5.0.1, cross-spawn@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
@@ -5370,7 +5369,7 @@ fb-watchman@^2.0.0:
dependencies:
bser "^2.0.0"
-fbjs@^0.8.0, fbjs@^0.8.1, fbjs@^0.8.12, fbjs@^0.8.16, fbjs@^0.8.9:
+fbjs@^0.8.1, fbjs@^0.8.12, fbjs@^0.8.16, fbjs@^0.8.9:
version "0.8.17"
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd"
dependencies:
@@ -5519,9 +5518,9 @@ flatten@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782"
-flow-bin@^0.66.0:
- version "0.66.0"
- resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.66.0.tgz#a96dde7015dc3343fd552a7b4963c02be705ca26"
+flow-bin@^0.79.1:
+ version "0.79.1"
+ resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.79.1.tgz#01c9f427baa6556753fa878c192d42e1ecb764b6"
flow-parser@^0.*:
version "0.74.0"
@@ -5968,10 +5967,6 @@ growly@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
-gud@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/gud/-/gud-1.0.0.tgz#a489581b17e6a70beca9abe3ae57de7a499852c0"
-
gzip-size@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-3.0.0.tgz#546188e9bdc337f673772f81660464b389dce520"
@@ -9670,12 +9665,6 @@ radium@^0.19.0, radium@^0.19.4:
inline-style-prefixer "^2.0.5"
prop-types "^15.5.8"
-rafl@~1.2.1:
- version "1.2.2"
- resolved "https://registry.yarnpkg.com/rafl/-/rafl-1.2.2.tgz#fe930f758211020d47e38815f5196a8be4150740"
- dependencies:
- global "~4.3.0"
-
ramda@^0.24.1:
version "0.24.1"
resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.24.1.tgz#c3b7755197f35b8dc3502228262c4c91ddb6b857"
@@ -9790,11 +9779,11 @@ react-error-overlay@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-4.0.0.tgz#d198408a85b4070937a98667f500c832f86bd5d4"
-react-event-listener@^0.6.0:
- version "0.6.1"
- resolved "https://registry.yarnpkg.com/react-event-listener/-/react-event-listener-0.6.1.tgz#41c7a80a66b398c27dd511e22712b02f3d4eccca"
+react-event-listener@^0.6.2:
+ version "0.6.3"
+ resolved "https://registry.yarnpkg.com/react-event-listener/-/react-event-listener-0.6.3.tgz#8eab88129a76e095ed8aa684c29679eded1e843d"
dependencies:
- "@babel/runtime" "^7.0.0-beta.42"
+ "@babel/runtime" "7.0.0-rc.1"
prop-types "^15.6.0"
warning "^4.0.1"
@@ -9867,17 +9856,6 @@ react-onclickoutside@^6.5.0:
version "6.7.1"
resolved "https://registry.yarnpkg.com/react-onclickoutside/-/react-onclickoutside-6.7.1.tgz#6a5b5b8b4eae6b776259712c89c8a2b36b17be93"
-react-popper@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-1.0.0.tgz#b99452144e8fe4acc77fa3d959a8c79e07a65084"
- dependencies:
- babel-runtime "6.x.x"
- create-react-context "^0.2.1"
- popper.js "^1.14.1"
- prop-types "^15.6.1"
- typed-styles "^0.0.5"
- warning "^3.0.0"
-
react-redux@^5.0.7:
version "5.0.7"
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.0.7.tgz#0dc1076d9afb4670f993ffaef44b8f8c1155a4c8"
@@ -10132,7 +10110,7 @@ rechoir@^0.6.2:
dependencies:
resolve "^1.1.6"
-"recompose@^0.26.0 || ^0.27.0", recompose@^0.27.0, recompose@^0.27.1:
+recompose@^0.27.1:
version "0.27.1"
resolved "https://registry.yarnpkg.com/recompose/-/recompose-0.27.1.tgz#1a49e931f183634516633bbb4f4edbfd3f38a7ba"
dependencies:
@@ -10143,6 +10121,17 @@ rechoir@^0.6.2:
react-lifecycles-compat "^3.0.2"
symbol-observable "^1.0.4"
+recompose@^0.29.0:
+ version "0.29.0"
+ resolved "https://registry.yarnpkg.com/recompose/-/recompose-0.29.0.tgz#f1a4e20d5f24d6ef1440f83924e821de0b1bccef"
+ dependencies:
+ "@babel/runtime" "^7.0.0"
+ change-emitter "^0.1.2"
+ fbjs "^0.8.1"
+ hoist-non-react-statics "^2.3.1"
+ react-lifecycles-compat "^3.0.2"
+ symbol-observable "^1.0.4"
+
recursive-readdir@2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.1.tgz#90ef231d0778c5ce093c9a48d74e5c5422d13a99"
@@ -10214,6 +10203,10 @@ regenerator-runtime@^0.11.0, regenerator-runtime@^0.11.1:
version "0.11.1"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
+regenerator-runtime@^0.12.0:
+ version "0.12.1"
+ resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de"
+
regenerator-transform@^0.10.0:
version "0.10.1"
resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.10.1.tgz#1e4996837231da8b7f3cf4114d71b5691a0680dd"
@@ -10655,12 +10648,6 @@ scoped-regex@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/scoped-regex/-/scoped-regex-1.0.0.tgz#a346bb1acd4207ae70bd7c0c7ca9e566b6baddb8"
-scroll@^2.0.3:
- version "2.0.3"
- resolved "https://registry.yarnpkg.com/scroll/-/scroll-2.0.3.tgz#0951b785544205fd17753bc3d294738ba16fc2ab"
- dependencies:
- rafl "~1.2.1"
-
scrypt.js@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/scrypt.js/-/scrypt.js-0.2.0.tgz#af8d1465b71e9990110bedfc593b9479e03a8ada"
@@ -11820,10 +11807,6 @@ type-is@~1.6.15, type-is@~1.6.16:
media-typer "0.3.0"
mime-types "~2.1.18"
-typed-styles@^0.0.5:
- version "0.0.5"
- resolved "https://registry.yarnpkg.com/typed-styles/-/typed-styles-0.0.5.tgz#a60df245d482a9b1adf9c06c078d0f06085ed1cf"
-
typedarray@^0.0.6, typedarray@~0.0.5:
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"