1
0
mirror of https://github.com/kremalicious/blowfish.git synced 2024-11-15 01:25:22 +01:00

Merge pull request #17 from kremalicious/feature/price-changes

24hr price changes
This commit is contained in:
Matthias Kretschmann 2019-06-04 22:03:58 +02:00 committed by GitHub
commit 639909d986
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 116 additions and 50 deletions

View File

@ -21,7 +21,7 @@
}, },
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@coingecko/cryptoformat": "^0.3.1", "@coingecko/cryptoformat": "^0.3.2",
"ethereum-address": "0.0.4", "ethereum-address": "0.0.4",
"ms": "^2.1.1" "ms": "^2.1.1"
}, },
@ -33,7 +33,7 @@
"@babel/preset-react": "^7.0.0", "@babel/preset-react": "^7.0.0",
"@babel/runtime": "^7.4.5", "@babel/runtime": "^7.4.5",
"@reach/router": "^1.2.1", "@reach/router": "^1.2.1",
"@svgr/webpack": "^4.2.0", "@svgr/webpack": "^4.3.0",
"babel-eslint": "^10.0.1", "babel-eslint": "^10.0.1",
"babel-loader": "^8.0.6", "babel-loader": "^8.0.6",
"copy-webpack-plugin": "^5.0.3", "copy-webpack-plugin": "^5.0.3",
@ -53,13 +53,13 @@
"react-blockies": "^1.4.1", "react-blockies": "^1.4.1",
"react-dom": "^16.8.6", "react-dom": "^16.8.6",
"react-pose": "^4.0.8", "react-pose": "^4.0.8",
"release-it": "^12.2.1", "release-it": "^12.2.2",
"style-loader": "^0.23.1", "style-loader": "^0.23.1",
"stylelint": "^10.0.1", "stylelint": "^10.0.1",
"stylelint-config-standard": "^18.3.0", "stylelint-config-standard": "^18.3.0",
"webpack": "^4.32.2", "webpack": "^4.32.2",
"webpack-cli": "^3.3.2", "webpack-cli": "^3.3.2",
"webpack-dev-server": "^3.3.1" "webpack-dev-server": "^3.5.1"
}, },
"browserslist": "electron >= 5.0", "browserslist": "electron >= 5.0",
"build": { "build": {

View File

@ -17,8 +17,8 @@ if (
isDev = true isDev = true
} }
const width = 620 const width = 640
const height = 440 const height = 450
const createWindow = async () => { const createWindow = async () => {
const isDarkMode = systemPreferences.isDarkMode() const isDarkMode = systemPreferences.isDarkMode()

View File

@ -1,24 +1,25 @@
.ticker { .ticker {
justify-content: center; margin-top: 3rem;
margin-top: 2rem; margin-bottom: 4rem;
margin-bottom: 2rem; width: 100%;
} }
.ticker .number-unit { .ticker .number-unit {
flex: initial; margin-top: 0;
margin-top: 1rem;
} }
.ticker button { .ticker button {
background: none; background: none;
border: 1px solid #e2e2e2; border: 1px solid #e2e2e2;
box-shadow: none; box-shadow: none;
margin: 0; margin: 0 auto;
outline: 0; outline: 0;
font-size: .8rem; font-size: .75rem;
border-radius: .3rem; border-radius: .3rem;
padding: .2rem .4rem; padding: .3rem .4rem;
display: block; display: block;
width: 100%;
max-width: 12rem;
transition: border .2s ease-out; transition: border .2s ease-out;
color: #41474e; color: #41474e;
} }
@ -33,6 +34,19 @@
} }
.label--price { .label--price {
display: inline-block;
font-size: .95rem; font-size: .95rem;
} }
.change {
font-size: .65rem;
display: inline-block;
margin-left: .25rem;
}
.change--positive {
color: forestgreen;
}
.change--negative {
color: crimson;
}

View File

@ -1,4 +1,5 @@
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import posed, { PoseGroup } from 'react-pose' import posed, { PoseGroup } from 'react-pose'
import { AppContext } from '../store/createContext' import { AppContext } from '../store/createContext'
import { cryptoFormatter } from '../../utils' import { cryptoFormatter } from '../../utils'
@ -7,31 +8,37 @@ import { fadeIn } from './Animations'
const Item = posed.div(fadeIn) const Item = posed.div(fadeIn)
const Change = ({ currency }) => (
<AppContext.Consumer>
{({ priceChanges }) => {
const isNegative = JSON.stringify(priceChanges[currency]).startsWith('-')
let classes = isNegative ? 'change--negative' : 'change--positive'
return (
<span className={`change ${classes}`} title="24hr change">
{!isNegative && '+'}
{Number(priceChanges[currency]).toFixed(1)}%
</span>
)
}}
</AppContext.Consumer>
)
Change.propTypes = {
currency: PropTypes.string.isRequired
}
export default class Ticker extends PureComponent { export default class Ticker extends PureComponent {
static contextType = AppContext static contextType = AppContext
items = activeStyle => items = () => {
// convert Map to array first, cause for...of or forEach returns undefined, const {
// so it cannot be mapped to a collection of elements prices,
[...this.context.prices.entries()].map(([key, value]) => ( needsConfig,
<Item key={key} className="number-unit"> currency,
<button toggleCurrencies,
className="label label--price" accentColor
onClick={() => this.context.toggleCurrencies(key)} } = this.context
disabled={this.context.needsConfig}
style={
key === this.context.currency && !this.context.needsConfig
? activeStyle
: {}
}
>
{cryptoFormatter(value, key)}
</button>
</Item>
))
render() {
const { accentColor } = this.context
const activeStyle = { const activeStyle = {
backgroundColor: accentColor, backgroundColor: accentColor,
@ -39,9 +46,29 @@ export default class Ticker extends PureComponent {
borderColor: accentColor borderColor: accentColor
} }
// convert Map to array first, cause for...of or forEach returns undefined,
// so it cannot be mapped to a collection of elements
return [...prices.entries()].map(([key, value]) => (
<Item key={key} className="number-unit">
<button
className="label label--price"
onClick={() => toggleCurrencies(key)}
disabled={needsConfig}
style={key === currency && !needsConfig ? activeStyle : {}}
>
{cryptoFormatter(value, key)}
{key !== 'ocean' && <Change currency={key} />}
</button>
</Item>
))
}
render() {
return ( return (
<footer className="number-unit-wrap ticker" {...this.props}> <footer className="ticker" {...this.props}>
<PoseGroup animateOnMount>{this.items(activeStyle)}</PoseGroup> <div className="number-unit-wrap">
<PoseGroup animateOnMount>{this.items()}</PoseGroup>
</div>
</footer> </footer>
) )
} }

View File

@ -26,19 +26,16 @@
} }
.number-unit-wrap { .number-unit-wrap {
display: flex; display: grid;
grid-gap: .5rem;
grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
justify-items: start;
width: 100%; width: 100%;
flex-wrap: wrap;
justify-content: space-around;
position: relative;
} }
.number-unit { .number-unit {
text-align: center; text-align: center;
flex: 1 1 20%; width: 100%;
margin-top: 5%;
padding-left: 2%;
padding-right: 2%;
} }
.label { .label {
@ -52,6 +49,7 @@
.number-unit-wrap--accounts { .number-unit-wrap--accounts {
min-height: 55px; min-height: 55px;
padding-top: 2rem;
} }
.number-unit--main { .number-unit--main {

View File

@ -3,6 +3,7 @@
width: 100%; width: 100%;
position: relative; position: relative;
margin-top: 7vh; margin-top: 7vh;
padding-bottom: 4rem;
} }
.preferences__title { .preferences__title {

View File

@ -26,6 +26,11 @@ export default class AppProvider extends PureComponent {
currency: 'ocean', currency: 'ocean',
needsConfig: false, needsConfig: false,
prices: pricesMap, prices: pricesMap,
priceChanges: Object.assign(
...conversions.map(key => ({
[key]: 0
}))
),
toggleCurrencies: currency => this.toggleCurrencies(currency), toggleCurrencies: currency => this.toggleCurrencies(currency),
setBalances: () => this.setBalances(), setBalances: () => this.setBalances(),
accentColor: '#f6388a' accentColor: '#f6388a'
@ -75,21 +80,27 @@ export default class AppProvider extends PureComponent {
`https://api.etherscan.io/api?module=account&action=tokenbalance&contractaddress=${oceanTokenContract}&address=${account}&tag=latest` `https://api.etherscan.io/api?module=account&action=tokenbalance&contractaddress=${oceanTokenContract}&address=${account}&tag=latest`
) )
const balance = (json.result /= 1000000000000000000) // Convert from vodka 10^18 const balance = json.result / 1e18 // Convert from vodka 10^18
return balance return balance
} }
fetchAndSetPrices = async () => { fetchAndSetPrices = async () => {
const currencies = conversions.join(',') const currencies = conversions.join(',')
const json = await fetchData( const json = await fetchData(
`https://api.coingecko.com/api/v3/simple/price?ids=ocean-protocol&vs_currencies=${currencies}` `https://api.coingecko.com/api/v3/simple/price?ids=ocean-protocol&vs_currencies=${currencies}&include_24hr_change=true`
) )
let newPrices = new Map(this.state.prices) // make a shallow copy of the Map let newPrices = new Map(this.state.prices) // make a shallow copy of the Map
conversions.map(key => newPrices.set(key, json['ocean-protocol'][key])) // modify the copy conversions.map(key => newPrices.set(key, json['ocean-protocol'][key])) // modify the copy
const newPriceChanges = await Object.assign(
...conversions.map(key => ({
[key]: json['ocean-protocol'][key + '_24h_change']
}))
)
ipcRenderer.send('prices-updated', Array.from(newPrices)) // convert Map to array, ipc messages seem to kill it ipcRenderer.send('prices-updated', Array.from(newPrices)) // convert Map to array, ipc messages seem to kill it
this.setState({ prices: newPrices }) this.setState({ prices: newPrices, priceChanges: newPriceChanges })
return newPrices return newPrices
} }

View File

@ -1,6 +1,8 @@
const { app, shell } = require('electron') const { app, shell } = require('electron')
const { formatCurrency } = require('@coingecko/cryptoformat') const { formatCurrency } = require('@coingecko/cryptoformat')
const isFiat = currency => currency === 'eur' || currency === 'usd'
const openUrl = url => { const openUrl = url => {
shell.openExternal(url) shell.openExternal(url)
} }
@ -35,9 +37,22 @@ const formatOcean = value => {
return numberformatted.replace(/EUR/, 'Ọ').replace(/€/, 'Ọ') return numberformatted.replace(/EUR/, 'Ọ').replace(/€/, 'Ọ')
} }
const formatFiat = (value, currency) => {
const numberformatted = new Intl.NumberFormat(locale, {
minimumFractionDigits: 0,
maximumFractionDigits: 4,
style: 'currency',
currency: currency.toUpperCase()
}).format(value)
return numberformatted
}
const cryptoFormatter = (value, currency) => { const cryptoFormatter = (value, currency) => {
if (currency === 'ocean') { if (currency === 'ocean') {
return formatOcean(value) return formatOcean(value)
} else if (isFiat(currency)) {
return formatFiat(value, currency)
} else { } else {
return formatCurrency(value, currency.toUpperCase(), locale) return formatCurrency(value, currency.toUpperCase(), locale)
.replace(/BTC/, 'Ƀ') .replace(/BTC/, 'Ƀ')