diff --git a/package-lock.json b/package-lock.json
index e05b27df..bb544cd2 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3023,9 +3023,9 @@
"integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs="
},
"cryptiles": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz",
- "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=",
+ "version": "3.1.4",
+ "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.4.tgz",
+ "integrity": "sha512-8I1sgZHfVwcSOY6mSGpVU3lw/GSIZvusg8dD2+OGehCJpOhQRLNcH0qb9upQnOH4XhgxxFJSg6E2kx95deb1Tw==",
"requires": {
"boom": "5.x.x"
},
@@ -3377,6 +3377,89 @@
"es5-ext": "^0.10.9"
}
},
+ "d3-array": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz",
+ "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw=="
+ },
+ "d3-collection": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz",
+ "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A=="
+ },
+ "d3-color": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.2.3.tgz",
+ "integrity": "sha512-x37qq3ChOTLd26hnps36lexMRhNXEtVxZ4B25rL0DVdDsGQIJGB18S7y9XDwlDD6MD/ZBzITCf4JjGMM10TZkw=="
+ },
+ "d3-ease": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.5.tgz",
+ "integrity": "sha512-Ct1O//ly5y5lFM9YTdu+ygq7LleSgSE4oj7vUt9tPLHUi8VCV7QoizGpdWRWAwCO9LdYzIrQDg97+hGVdsSGPQ=="
+ },
+ "d3-format": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.3.2.tgz",
+ "integrity": "sha512-Z18Dprj96ExragQ0DeGi+SYPQ7pPfRMtUXtsg/ChVIKNBCzjO8XYJvRTC1usblx52lqge56V5ect+frYTQc8WQ=="
+ },
+ "d3-interpolate": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.3.2.tgz",
+ "integrity": "sha512-NlNKGopqaz9qM1PXh9gBF1KSCVh+jSFErrSlD/4hybwoNX/gt1d8CDbDW+3i+5UOHhjC6s6nMvRxcuoMVNgL2w==",
+ "requires": {
+ "d3-color": "1"
+ }
+ },
+ "d3-path": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.7.tgz",
+ "integrity": "sha512-q0cW1RpvA5c5ma2rch62mX8AYaiLX0+bdaSM2wxSU9tXjU4DNvkx9qiUvjkuWCj3p22UO/hlPivujqMiR9PDzA=="
+ },
+ "d3-scale": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-1.0.7.tgz",
+ "integrity": "sha512-KvU92czp2/qse5tUfGms6Kjig0AhHOwkzXG0+PqIJB3ke0WUv088AHMZI0OssO9NCkXt4RP8yju9rpH8aGB7Lw==",
+ "requires": {
+ "d3-array": "^1.2.0",
+ "d3-collection": "1",
+ "d3-color": "1",
+ "d3-format": "1",
+ "d3-interpolate": "1",
+ "d3-time": "1",
+ "d3-time-format": "2"
+ }
+ },
+ "d3-shape": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.2.2.tgz",
+ "integrity": "sha512-hUGEozlKecFZ2bOSNt7ENex+4Tk9uc/m0TtTEHBvitCBxUNjhzm5hS2GrrVRD/ae4IylSmxGeqX5tWC2rASMlQ==",
+ "requires": {
+ "d3-path": "1"
+ }
+ },
+ "d3-time": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.0.10.tgz",
+ "integrity": "sha512-hF+NTLCaJHF/JqHN5hE8HVGAXPStEq6/omumPE/SxyHVrR7/qQxusFDo0t0c/44+sCGHthC7yNGFZIEgju0P8g=="
+ },
+ "d3-time-format": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.1.3.tgz",
+ "integrity": "sha512-6k0a2rZryzGm5Ihx+aFMuO1GgelgIz+7HhB4PH4OEndD5q2zGn1mDfRdNrulspOfR6JXkb2sThhDK41CSK85QA==",
+ "requires": {
+ "d3-time": "1"
+ }
+ },
+ "d3-timer": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.9.tgz",
+ "integrity": "sha512-rT34J5HnQUHhcLvhSB9GjCkN0Ddd5Y8nCwDBG2u6wQEeYxT/Lf51fTFFkldeib/sE/J0clIe0pnCfs6g/lRbyg=="
+ },
+ "d3-voronoi": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.4.tgz",
+ "integrity": "sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg=="
+ },
"damerau-levenshtein": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.4.tgz",
@@ -12076,6 +12159,11 @@
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-4.0.1.tgz",
"integrity": "sha512-xXUbDAZkU08aAkjtUvldqbvI04ogv+a1XdHxvYuHPYKIVk/42BIOD0zSKTHAWV4+gDy3yGm283z2072rA2gdtw=="
},
+ "react-fast-compare": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz",
+ "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw=="
+ },
"react-hint": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/react-hint/-/react-hint-3.1.0.tgz",
@@ -13587,6 +13675,11 @@
"resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz",
"integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww=="
},
+ "shrink-array": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/shrink-array/-/shrink-array-0.0.2.tgz",
+ "integrity": "sha512-M84/lNmWHajLnjSjBc+YceutISmQZsWsRm9vaHSkzK/ueZWzjZ3NwJRsBEt7df+KNE9fSYD2v4JWfwl68LubBg=="
+ },
"signal-exit": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
@@ -15327,6 +15420,83 @@
"extsprintf": "^1.2.0"
}
},
+ "victory": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/victory/-/victory-0.27.0.tgz",
+ "integrity": "sha512-p9NNNCuPOrZNGYriwQ39k4E4kPswvEBXcIkgjNSN3kldqHj6TZhBBOOysQ3EHvPddzak3zb3ZV7Ykv1o9m4OjQ==",
+ "requires": {
+ "victory-chart": "^27.0.0",
+ "victory-core": "^23.0.2",
+ "victory-pie": "^16.0.0"
+ }
+ },
+ "victory-chart": {
+ "version": "27.1.1",
+ "resolved": "https://registry.npmjs.org/victory-chart/-/victory-chart-27.1.1.tgz",
+ "integrity": "sha512-6QLtfDLQQdtzNfLkFIR5QR9SmWwkre0yLz0auanth9XnBdwKoMf+g+l06tsKvNfHxNw8ZaG0C/J+KwtU/5ChPg==",
+ "requires": {
+ "d3-voronoi": "^1.1.2",
+ "lodash": "^4.17.5",
+ "react-fast-compare": "^2.0.0",
+ "victory-core": "^24.0.0"
+ },
+ "dependencies": {
+ "victory-core": {
+ "version": "24.0.1",
+ "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-24.0.1.tgz",
+ "integrity": "sha512-loC+UafBR8nMnLkQ3suSkjMTVMlLsUaI7BiBmFYYFjy1eqEkPwA+Pa51Tbu0KIunR/1alpEjOxP1MBz1Vc1FFg==",
+ "requires": {
+ "d3-ease": "^1.0.0",
+ "d3-interpolate": "^1.1.1",
+ "d3-scale": "^1.0.0",
+ "d3-shape": "^1.2.0",
+ "d3-timer": "^1.0.0",
+ "lodash": "^4.17.5",
+ "react-fast-compare": "^2.0.0"
+ }
+ }
+ }
+ },
+ "victory-core": {
+ "version": "23.0.7",
+ "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-23.0.7.tgz",
+ "integrity": "sha512-RrMxMtuI4imaphp8si8wwPpOBBbJFICYMg8mQlSwFqne3w8HQ86Y4U4SRIQth5bnK4HDmZkdOJr1K7h3x07XPA==",
+ "requires": {
+ "d3-ease": "^1.0.0",
+ "d3-interpolate": "^1.1.1",
+ "d3-scale": "^1.0.0",
+ "d3-shape": "^1.2.0",
+ "d3-timer": "^1.0.0",
+ "lodash": "^4.17.5",
+ "react-fast-compare": "^2.0.0"
+ }
+ },
+ "victory-pie": {
+ "version": "16.1.0",
+ "resolved": "https://registry.npmjs.org/victory-pie/-/victory-pie-16.1.0.tgz",
+ "integrity": "sha512-OV1C2e7KGkGjrPUf7Hr68PWuDzxsFqQkdC9E/dZ2HGQKqaYZ9bdkLC0lZYEr7mKsUs6D8iPe4RTWXyEfX84nMA==",
+ "requires": {
+ "d3-shape": "^1.0.0",
+ "lodash": "^4.17.5",
+ "victory-core": "^24.0.0"
+ },
+ "dependencies": {
+ "victory-core": {
+ "version": "24.0.1",
+ "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-24.0.1.tgz",
+ "integrity": "sha512-loC+UafBR8nMnLkQ3suSkjMTVMlLsUaI7BiBmFYYFjy1eqEkPwA+Pa51Tbu0KIunR/1alpEjOxP1MBz1Vc1FFg==",
+ "requires": {
+ "d3-ease": "^1.0.0",
+ "d3-interpolate": "^1.1.1",
+ "d3-scale": "^1.0.0",
+ "d3-shape": "^1.2.0",
+ "d3-timer": "^1.0.0",
+ "lodash": "^4.17.5",
+ "react-fast-compare": "^2.0.0"
+ }
+ }
+ }
+ },
"vm-browserify": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz",
diff --git a/package.json b/package.json
index ab41d7d9..110c6b67 100644
--- a/package.json
+++ b/package.json
@@ -84,6 +84,7 @@
"redux-actions": "2.4.0",
"reselect": "3.0.1",
"safe-buffer": "5.1.2",
+ "shrink-array": "0.0.2",
"smart-round": "1.0.0",
"socket.io-client": "2.1.1",
"startinterval2": "1.0.1",
@@ -91,6 +92,7 @@
"supports-color": "5.4.0",
"tough-cookie": "2.4.3",
"universal-analytics": "0.4.17",
+ "victory": "0.27.0",
"web3": "1.0.0-beta.34"
},
"devDependencies": {
diff --git a/public/main/main-window.js b/public/main/main-window.js
index 60be6a48..8a1430cb 100644
--- a/public/main/main-window.js
+++ b/public/main/main-window.js
@@ -61,7 +61,7 @@ function loadWindow () {
mainWindow = new BrowserWindow({
show: false,
width: 1140,
- height: 700,
+ height: 750,
minWidth: 640,
minHeight: 578,
useContentSize: true
diff --git a/src/components/Auction.js b/src/components/Auction.js
index 6f9af3b4..7c388290 100644
--- a/src/components/Auction.js
+++ b/src/components/Auction.js
@@ -1,4 +1,5 @@
import CountDownProvider from './providers/CountDownProvider'
+import AuctionChartCard from './AuctionChartCard'
import * as selectors from '../selectors'
import BuyMETDrawer from './BuyMETDrawer'
import { connect } from 'react-redux'
@@ -65,7 +66,7 @@ const Body = styled.div`
align-items: center;
flex-direction: column;
- @media (min-width: 1200px) {
+ @media (min-width: 1140px) {
align-items: flex-start;
margin-top: 4.8rem;
flex-direction: row;
@@ -78,7 +79,7 @@ const BuyBtn = styled(Btn)`
margin-bottom: 3.2rem;
min-width: 300px;
- @media (min-width: 1200px) {
+ @media (min-width: 1140px) {
margin-bottom: 0;
order: 1;
min-width: auto;
@@ -92,7 +93,7 @@ const StatsContainer = styled.div`
width: 100%;
margin-right: 0;
- @media (min-width: 1200px) {
+ @media (min-width: 1140px) {
margin-right: 1.6rem;
max-width: 660px;
}
@@ -117,7 +118,7 @@ const Badge = styled.div`
padding: 0.4rem 0.8rem;
margin-right: 0.4rem;
- @media (min-width: 1100px) {
+ @media (min-width: 1140px) {
font-size: 2rem;
}
`
@@ -127,7 +128,7 @@ const Price = styled.div`
font-weight: 600;
text-shadow: 0 1px 1px ${p => p.theme.colors.darkShade};
- @media (min-width: 1100px) {
+ @media (min-width: 1140px) {
font-size: 2.4rem;
}
`
@@ -138,7 +139,7 @@ const USDPrice = styled.div`
font-weight: 600;
text-align: right;
- @media (min-width: 1100px) {
+ @media (min-width: 1140px) {
font-size: 1.6rem;
}
`
@@ -148,7 +149,7 @@ const AvailableAmount = styled.div`
font-weight: 600;
text-shadow: 0 1px 1px ${p => p.theme.colors.darkShade};
- @media (min-width: 1100px) {
+ @media (min-width: 1140px) {
font-size: 2.4rem;
}
`
@@ -285,6 +286,7 @@ class Auction extends React.Component {
+
+ moment()
+ .subtract({ days: 7 })
+ .unix()
+
+class AuctionChartCard extends Component {
+ state = {
+ chartStatus: 'pending',
+ chartData: []
+ }
+
+ // eslint-disable-next-line arrow-body-style
+ retrieveData = () => {
+ this.setState({ chartStatus: 'pending', chartData: [] })
+
+ const from = sevenDaysAgo()
+ const now = moment().unix()
+
+ fetch(`${config.MET_API_URL}/history?from=${from}&to=${now}`)
+ .then(response => response.json())
+ .then(chartData => {
+ if (!this._isMounted) return
+ this.setState({
+ chartStatus: 'success',
+ chartData: chartData.map(point => ({
+ x: point.timestamp,
+ y: parseInt(point.currentAuctionPrice, 10)
+ }))
+ })
+ })
+ .catch(() => {
+ if (!this._isMounted) return
+ this.setState({ chartStatus: 'failure', chartData: [] })
+ })
+ }
+
+ componentDidMount() {
+ this._isMounted = true
+ this.retrieveData()
+ }
+
+ componentWillUnmount() {
+ this._isMounted = false
+ }
+
+ static getDerivedStateFromProps(props, state) {
+ const point = {
+ y: parseInt(props.auction.currentPrice, 10),
+ x: moment().unix()
+ }
+ const from = sevenDaysAgo()
+ const newChartData = state.chartData.concat(point).filter(p => p.x >= from)
+
+ return {
+ chartData:
+ newChartData.length > 500
+ ? shrinkArray(newChartData, 500, last)
+ : newChartData
+ }
+ }
+
+ render() {
+ return (
+
+ )
+ }
+}
+
+const mapStateToProps = state => ({
+ auction: state.auction.status
+})
+
+export default connect(mapStateToProps)(AuctionChartCard)
diff --git a/src/components/Converter.js b/src/components/Converter.js
index 80d2d501..946119b8 100644
--- a/src/components/Converter.js
+++ b/src/components/Converter.js
@@ -1,3 +1,4 @@
+import ConverterChartCard from './ConverterChartCard'
import * as selectors from '../selectors'
import ConvertDrawer from './ConvertDrawer'
import { connect } from 'react-redux'
@@ -20,7 +21,7 @@ const Container = styled.div`
align-items: center;
flex-direction: column;
- @media (min-width: 1180px) {
+ @media (min-width: 1140px) {
padding: 3.2rem 4.8rem;
align-items: flex-start;
flex-direction: row;
@@ -31,7 +32,7 @@ const ConvertBtn = styled(Btn)`
margin-top: 3.2rem;
min-width: 260px;
- @media (min-width: 1180px) {
+ @media (min-width: 1140px) {
min-width: 200px;
margin-top: 0;
}
@@ -50,7 +51,7 @@ const StatsContainer = styled.div`
margin-right: 0;
width: 100%;
- @media (min-width: 1180px) {
+ @media (min-width: 1140px) {
margin-right: 1.6rem;
max-width: 660px;
}
@@ -189,6 +190,7 @@ class Converter extends React.Component {
+
+ moment()
+ .subtract({ days: 7 })
+ .unix()
+
+class ConverterChartCard extends Component {
+ state = {
+ chartStatus: 'pending',
+ chartData: []
+ }
+
+ // eslint-disable-next-line arrow-body-style
+ retrieveData = () => {
+ this.setState({ chartStatus: 'pending', chartData: [] })
+
+ const from = sevenDaysAgo()
+ const now = moment().unix()
+
+ fetch(`${config.MET_API_URL}/history?from=${from}&to=${now}`)
+ .then(response => response.json())
+ .then(data => data.filter(p => Boolean(p.currentConverterPrice)))
+ .then(data => {
+ if (!this._isMounted) return
+ this.setState({
+ chartStatus: 'success',
+ chartData: data.map(point => ({
+ x: point.timestamp,
+ y: parseInt(point.currentConverterPrice, 10)
+ }))
+ })
+ })
+ .catch(() => {
+ if (!this._isMounted) return
+ this.setState({ chartStatus: 'failure', chartData: [] })
+ })
+ }
+
+ componentDidMount() {
+ this._isMounted = true
+ this.retrieveData()
+ }
+
+ componentWillUnmount() {
+ this._isMounted = false
+ }
+
+ static getDerivedStateFromProps(props, state) {
+ const point = {
+ y: parseInt(props.converter.currentPrice, 10),
+ x: moment().unix()
+ }
+ const from = sevenDaysAgo()
+ const newChartData = state.chartData.concat(point).filter(p => p.x >= from)
+
+ return {
+ chartData:
+ newChartData.length > 500
+ ? shrinkArray(newChartData, 500, last)
+ : newChartData
+ }
+ }
+
+ render() {
+ return (
+
+ )
+ }
+}
+
+const mapStateToProps = state => ({
+ converter: state.converter.status
+})
+
+export default connect(mapStateToProps)(ConverterChartCard)
diff --git a/src/components/common/ChartCard.js b/src/components/common/ChartCard.js
new file mode 100644
index 00000000..2432d91d
--- /dev/null
+++ b/src/components/common/ChartCard.js
@@ -0,0 +1,78 @@
+import { VictoryGroup, VictoryLine } from 'victory'
+import React, { Component } from 'react'
+import LoadingBar from './LoadingBar'
+import { Btn } from './Btn'
+import styled from 'styled-components'
+import Sp from './Spacing'
+
+const Container = styled.div`
+ padding: 32px 16px 24px;
+ min-height: 232px;
+ height: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+`
+
+const Label = styled.div`
+ margin-top: 1.6rem;
+ text-align: center;
+ font-size: 13px;
+ font-weight: 200;
+`
+
+const LoadingContainer = styled.div`
+ text-align: center;
+ font-size: 13px;
+ font-weight: 200;
+ width: 100%;
+ max-width: 300px;
+`
+
+const ChartContainer = styled.div`
+ width: 100%;
+`
+
+const FailureContainer = styled.div`
+ text-align: center;
+`
+
+export default class ChartCard extends Component {
+ // eslint-disable-next-line complexity
+ render() {
+ return (
+
+ {this.props.chartStatus === 'pending' && (
+
+
+
+ Loading data...
+
+ )}
+ {this.props.chartStatus === 'success' && (
+
+
+
+
+
+
+ )}
+ {this.props.chartStatus === 'failure' && (
+
+ Retry
+
+
+ )}
+
+ )
+ }
+}
diff --git a/src/components/common/index.js b/src/components/common/index.js
index 86290800..64d09f1e 100644
--- a/src/components/common/index.js
+++ b/src/components/common/index.js
@@ -8,6 +8,7 @@ import LoadingBar from './LoadingBar'
import WalletIcon from './WalletIcon'
import CloseIcon from './CloseIcon'
import CheckIcon from './CheckIcon'
+import ChartCard from './ChartCard'
import TextInput from './TextInput'
import Tooltips from './Tooltips'
import CopyIcon from './CopyIcon'
@@ -30,6 +31,7 @@ export {
WalletIcon,
CloseIcon,
CheckIcon,
+ ChartCard,
TextInput,
Tooltips,
CopyIcon,
diff --git a/src/config.js b/src/config.js
index 1590cfbc..c046532d 100644
--- a/src/config.js
+++ b/src/config.js
@@ -8,6 +8,7 @@ export default {
CONVERTER_ADDR: addresses.autonomousConverter.toLowerCase(),
MET_EXPLORER_URL: process.env.REACT_APP_MET_EXPLORER_URL || defaultExplorerUrl,
SENTRY_DSN: process.env.REACT_APP_SENTRY_DSN || 'https://d9905c2eec994071935593d4085d3547@sentry.io/290706',
+ MET_API_URL: process.env.REACT_APP_MET_API_URL || 'https://api.metronome.io',
ETH_DEFAULT_GAS_LIMIT: '21000',
MET_DEFAULT_GAS_LIMIT: '250000',
DEFAULT_GAS_PRICE: '1000000000',