mirror of
https://github.com/kremalicious/blowfish.git
synced 2024-12-28 07:37:51 +01:00
Merge pull request #17 from kremalicious/feature/price-changes
24hr price changes
This commit is contained in:
commit
639909d986
@ -21,7 +21,7 @@
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@coingecko/cryptoformat": "^0.3.1",
|
||||
"@coingecko/cryptoformat": "^0.3.2",
|
||||
"ethereum-address": "0.0.4",
|
||||
"ms": "^2.1.1"
|
||||
},
|
||||
@ -33,7 +33,7 @@
|
||||
"@babel/preset-react": "^7.0.0",
|
||||
"@babel/runtime": "^7.4.5",
|
||||
"@reach/router": "^1.2.1",
|
||||
"@svgr/webpack": "^4.2.0",
|
||||
"@svgr/webpack": "^4.3.0",
|
||||
"babel-eslint": "^10.0.1",
|
||||
"babel-loader": "^8.0.6",
|
||||
"copy-webpack-plugin": "^5.0.3",
|
||||
@ -53,13 +53,13 @@
|
||||
"react-blockies": "^1.4.1",
|
||||
"react-dom": "^16.8.6",
|
||||
"react-pose": "^4.0.8",
|
||||
"release-it": "^12.2.1",
|
||||
"release-it": "^12.2.2",
|
||||
"style-loader": "^0.23.1",
|
||||
"stylelint": "^10.0.1",
|
||||
"stylelint-config-standard": "^18.3.0",
|
||||
"webpack": "^4.32.2",
|
||||
"webpack-cli": "^3.3.2",
|
||||
"webpack-dev-server": "^3.3.1"
|
||||
"webpack-dev-server": "^3.5.1"
|
||||
},
|
||||
"browserslist": "electron >= 5.0",
|
||||
"build": {
|
||||
|
@ -17,8 +17,8 @@ if (
|
||||
isDev = true
|
||||
}
|
||||
|
||||
const width = 620
|
||||
const height = 440
|
||||
const width = 640
|
||||
const height = 450
|
||||
|
||||
const createWindow = async () => {
|
||||
const isDarkMode = systemPreferences.isDarkMode()
|
||||
|
@ -1,24 +1,25 @@
|
||||
.ticker {
|
||||
justify-content: center;
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
margin-top: 3rem;
|
||||
margin-bottom: 4rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ticker .number-unit {
|
||||
flex: initial;
|
||||
margin-top: 1rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.ticker button {
|
||||
background: none;
|
||||
border: 1px solid #e2e2e2;
|
||||
box-shadow: none;
|
||||
margin: 0;
|
||||
margin: 0 auto;
|
||||
outline: 0;
|
||||
font-size: .8rem;
|
||||
font-size: .75rem;
|
||||
border-radius: .3rem;
|
||||
padding: .2rem .4rem;
|
||||
padding: .3rem .4rem;
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-width: 12rem;
|
||||
transition: border .2s ease-out;
|
||||
color: #41474e;
|
||||
}
|
||||
@ -33,6 +34,19 @@
|
||||
}
|
||||
|
||||
.label--price {
|
||||
display: inline-block;
|
||||
font-size: .95rem;
|
||||
}
|
||||
|
||||
.change {
|
||||
font-size: .65rem;
|
||||
display: inline-block;
|
||||
margin-left: .25rem;
|
||||
}
|
||||
|
||||
.change--positive {
|
||||
color: forestgreen;
|
||||
}
|
||||
|
||||
.change--negative {
|
||||
color: crimson;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React, { PureComponent } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import posed, { PoseGroup } from 'react-pose'
|
||||
import { AppContext } from '../store/createContext'
|
||||
import { cryptoFormatter } from '../../utils'
|
||||
@ -7,31 +8,37 @@ import { fadeIn } from './Animations'
|
||||
|
||||
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 {
|
||||
static contextType = AppContext
|
||||
|
||||
items = activeStyle =>
|
||||
// convert Map to array first, cause for...of or forEach returns undefined,
|
||||
// so it cannot be mapped to a collection of elements
|
||||
[...this.context.prices.entries()].map(([key, value]) => (
|
||||
<Item key={key} className="number-unit">
|
||||
<button
|
||||
className="label label--price"
|
||||
onClick={() => this.context.toggleCurrencies(key)}
|
||||
disabled={this.context.needsConfig}
|
||||
style={
|
||||
key === this.context.currency && !this.context.needsConfig
|
||||
? activeStyle
|
||||
: {}
|
||||
}
|
||||
>
|
||||
{cryptoFormatter(value, key)}
|
||||
</button>
|
||||
</Item>
|
||||
))
|
||||
|
||||
render() {
|
||||
const { accentColor } = this.context
|
||||
items = () => {
|
||||
const {
|
||||
prices,
|
||||
needsConfig,
|
||||
currency,
|
||||
toggleCurrencies,
|
||||
accentColor
|
||||
} = this.context
|
||||
|
||||
const activeStyle = {
|
||||
backgroundColor: accentColor,
|
||||
@ -39,9 +46,29 @@ export default class Ticker extends PureComponent {
|
||||
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 (
|
||||
<footer className="number-unit-wrap ticker" {...this.props}>
|
||||
<PoseGroup animateOnMount>{this.items(activeStyle)}</PoseGroup>
|
||||
<footer className="ticker" {...this.props}>
|
||||
<div className="number-unit-wrap">
|
||||
<PoseGroup animateOnMount>{this.items()}</PoseGroup>
|
||||
</div>
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
|
@ -26,19 +26,16 @@
|
||||
}
|
||||
|
||||
.number-unit-wrap {
|
||||
display: flex;
|
||||
display: grid;
|
||||
grid-gap: .5rem;
|
||||
grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
|
||||
justify-items: start;
|
||||
width: 100%;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-around;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.number-unit {
|
||||
text-align: center;
|
||||
flex: 1 1 20%;
|
||||
margin-top: 5%;
|
||||
padding-left: 2%;
|
||||
padding-right: 2%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.label {
|
||||
@ -52,6 +49,7 @@
|
||||
|
||||
.number-unit-wrap--accounts {
|
||||
min-height: 55px;
|
||||
padding-top: 2rem;
|
||||
}
|
||||
|
||||
.number-unit--main {
|
||||
|
@ -3,6 +3,7 @@
|
||||
width: 100%;
|
||||
position: relative;
|
||||
margin-top: 7vh;
|
||||
padding-bottom: 4rem;
|
||||
}
|
||||
|
||||
.preferences__title {
|
||||
|
@ -26,6 +26,11 @@ export default class AppProvider extends PureComponent {
|
||||
currency: 'ocean',
|
||||
needsConfig: false,
|
||||
prices: pricesMap,
|
||||
priceChanges: Object.assign(
|
||||
...conversions.map(key => ({
|
||||
[key]: 0
|
||||
}))
|
||||
),
|
||||
toggleCurrencies: currency => this.toggleCurrencies(currency),
|
||||
setBalances: () => this.setBalances(),
|
||||
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`
|
||||
)
|
||||
|
||||
const balance = (json.result /= 1000000000000000000) // Convert from vodka 10^18
|
||||
const balance = json.result / 1e18 // Convert from vodka 10^18
|
||||
return balance
|
||||
}
|
||||
|
||||
fetchAndSetPrices = async () => {
|
||||
const currencies = conversions.join(',')
|
||||
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
|
||||
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
|
||||
this.setState({ prices: newPrices })
|
||||
this.setState({ prices: newPrices, priceChanges: newPriceChanges })
|
||||
return newPrices
|
||||
}
|
||||
|
||||
|
15
src/utils.js
15
src/utils.js
@ -1,6 +1,8 @@
|
||||
const { app, shell } = require('electron')
|
||||
const { formatCurrency } = require('@coingecko/cryptoformat')
|
||||
|
||||
const isFiat = currency => currency === 'eur' || currency === 'usd'
|
||||
|
||||
const openUrl = url => {
|
||||
shell.openExternal(url)
|
||||
}
|
||||
@ -35,9 +37,22 @@ const formatOcean = value => {
|
||||
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) => {
|
||||
if (currency === 'ocean') {
|
||||
return formatOcean(value)
|
||||
} else if (isFiat(currency)) {
|
||||
return formatFiat(value, currency)
|
||||
} else {
|
||||
return formatCurrency(value, currency.toUpperCase(), locale)
|
||||
.replace(/BTC/, 'Ƀ')
|
||||
|
Loading…
Reference in New Issue
Block a user