add ticker, add loading state, number formatting

This commit is contained in:
Matthias Kretschmann 2019-05-06 23:39:59 +02:00
parent 532e8ce7cb
commit c4d071ddd0
Signed by: m
GPG Key ID: 606EEEF3C479A91F
13 changed files with 158 additions and 97 deletions

View File

@ -18,8 +18,8 @@
"author": "Matthias Kretschmann",
"license": "MIT",
"dependencies": {
"@coingecko/cryptoformat": "^0.3.1",
"@oceanprotocol/typographies": "^0.1.0",
"crypto-symbols": "^1.0.0",
"ms": "^2.1.1",
"react": "^16.8.6",
"react-dom": "^16.8.6"

View File

@ -69,7 +69,6 @@ button {
cursor: default;
height: calc(100vh - 35px);
transition: .15s ease-out;
display: flex;
align-items: center;
justify-content: center;
@ -92,6 +91,7 @@ button {
align-items: center;
flex-wrap: wrap;
position: relative;
animation: fadein .5s .5s ease-out;
}
.dark .main {
@ -137,12 +137,12 @@ button {
font-size: 1rem;
display: inline-block;
padding: 0 .3rem;
animation: fadein .5s ease-out forwards;
animation: fadeIn .5s ease-out;
border-radius: 4px;
}
.updated {
animation: updated .5s ease-out forwards;
animation: updated .5s ease-out;
}
.number-unit-wrap--accounts {
@ -176,17 +176,12 @@ button {
}
}
@keyframes fadein {
@keyframes fadeIn {
0% {
opacity: 0;
}
50% {
background: rgba(255, 255, 255, .2);
}
100% {
opacity: 1;
background: rgba(255, 255, 255, 0);
}
}

View File

@ -5,7 +5,8 @@ import { Consumer } from './store/createContext'
import Titlebar from './components/Titlebar'
import Total from './components/Total'
import Account from './components/Account'
import Actions from './components/Actions'
import Ticker from './components/Ticker'
import Spinner from './components/Spinner'
import './App.css'
//
@ -20,21 +21,27 @@ export default class App extends PureComponent {
<AppProvider>
<Titlebar />
<div className="app__content">
<main className="main">
<Total />
<Consumer>
{({ isLoading, accounts }) =>
isLoading ? (
<Spinner />
) : (
<>
<main className="main">
<Total />
<div className="number-unit-wrap number-unit-wrap--accounts">
<Consumer>
{({ accounts }) =>
accounts.map((account, i) => (
<Account key={i} account={account} />
))
}
</Consumer>
</div>
<div className="number-unit-wrap number-unit-wrap--accounts">
{accounts.map((account, i) => (
<Account key={i} account={account} />
))}
</div>
</main>
<Actions />
</main>
<Ticker />
</>
)
}
</Consumer>
</div>
</AppProvider>
)

View File

@ -1,17 +0,0 @@
.actions {
width: 100%;
text-align: right;
position: absolute;
top: -2rem;
right: 0;
}
button {
background: none;
border: 0;
box-shadow: none;
margin: 0;
outline: 0;
color: #f6388a;
font-size: .85rem;
}

View File

@ -1,27 +0,0 @@
import React, { PureComponent } from 'react'
import { Consumer } from '../store/createContext'
import './Actions.css'
export default class Actions extends PureComponent {
render() {
return (
<div className="actions">
<Consumer>
{({ toggleCurrencies, accounts }) => (
<>
{accounts.length > 0 &&
Object.keys(accounts[0].balance).map(currency => (
<button
key={currency}
onClick={() => toggleCurrencies(currency)}
>
{currency}
</button>
))}
</>
)}
</Consumer>
</div>
)
}
}

View File

@ -1,25 +1,18 @@
import React from 'react'
import PropTypes from 'prop-types'
import { Consumer } from '../store/createContext'
import { numberFormatter, fiatFormatter } from '../util/moneyFormatter'
import symbols from 'crypto-symbols'
import { locale } from '../util/moneyFormatter'
import { formatCurrency } from '@coingecko/cryptoformat'
const Balance = ({ balance }) => (
<h1 className="number">
<Consumer>
{({ currency }) => {
const isFiat = currency === 'usd' || currency === 'eur'
const symbol =
currency === 'ocean' ? 'Ọ' : symbols[currency.toUpperCase()]
return isFiat ? (
fiatFormatter(currency.toUpperCase(), balance[currency])
) : (
<>
{symbol} {numberFormatter(balance[currency]) || 0}
</>
)
}}
{({ currency }) =>
formatCurrency(balance[currency], currency.toUpperCase(), locale)
.replace(/BTC/, 'Ƀ')
.replace(/ETH/, 'Ξ')
.replace(/OCEAN/, 'Ọ')
}
</Consumer>
</h1>
)

View File

@ -0,0 +1,26 @@
.spinner {
position: relative;
text-align: center;
}
.spinner::before {
content: '';
box-sizing: border-box;
position: absolute;
top: 0;
left: 50%;
width: 20px;
height: 20px;
margin-top: -20px;
margin-left: -10px;
border-radius: 50%;
border: 2px solid #7b1173;
border-top-color: #e000cf;
animation: spinner .6s linear infinite;
}
@keyframes spinner {
to {
transform: rotate(360deg);
}
}

View File

@ -0,0 +1,5 @@
import React from 'react'
import './Spinner.css'
const Spinner = () => <div className="spinner" />
export default Spinner

49
src/components/Ticker.css Normal file
View File

@ -0,0 +1,49 @@
.ticker {
justify-content: center;
margin-top: 2rem;
margin-bottom: 2rem;
}
.ticker .number-unit {
flex: initial;
margin-top: 1rem;
}
.ticker button {
background: none;
border: 1px solid #e2e2e2;
box-shadow: none;
margin: 0;
outline: 0;
font-size: .8rem;
border-radius: .3rem;
padding: .2rem .4rem;
display: block;
transition: border .2s ease-out;
}
.dark .ticker button {
border-color: #303030;
}
.ticker button:hover {
border-color: #f6388a;
color: #f6388a;
}
.ticker button.active,
.ticker button.active:hover {
border-color: #e2e2e2;
background: #f6388a;
color: #fff;
}
.dark .ticker button.active,
.dark .ticker button.active:hover {
border-color: #303030;
}
.label--price {
display: inline-block;
font-size: .95rem;
}

34
src/components/Ticker.jsx Normal file
View File

@ -0,0 +1,34 @@
import React, { PureComponent } from 'react'
import { Consumer } from '../store/createContext'
import { userlocale } from '../util/moneyFormatter'
import { formatCurrency } from '@coingecko/cryptoformat'
import './Ticker.css'
export default class Ticker extends PureComponent {
render() {
return (
<footer className="number-unit-wrap ticker">
<Consumer>
{({ toggleCurrencies, currency, prices }) => (
<>
{Object.keys(prices).map((key, i) => (
<div key={i} className="number-unit">
<button
className={`label label--price ${key === currency &&
'active'}`}
onClick={() => toggleCurrencies(key)}
>
{formatCurrency(prices[key], key.toUpperCase(), userlocale)
.replace(/BTC/, 'Ƀ')
.replace(/ETH/, 'Ξ')
.replace(/OCEAN/, 'Ọ')}
</button>
</div>
))}
</>
)}
</Consumer>
</footer>
)
}
}

View File

@ -13,8 +13,8 @@ if (
isDev = true
}
const width = 550
const height = 380
const width = 620
const height = 440
const isDarkMode = systemPreferences.isDarkMode()
@ -30,6 +30,8 @@ const createWindow = async () => {
backgroundColor: isDarkMode ? '#141414' : '#fff',
frame: false,
show: false,
title: 'Ocean',
scrollBounce: true,
webPreferences: {
nodeIntegration: true
}

View File

@ -15,6 +15,7 @@ export default class AppProvider extends PureComponent {
}
state = {
isLoading: true,
accounts: [],
currency: 'ocean',
prices: Object.assign(...prices.map(key => ({ [key]: 0 }))),
@ -24,6 +25,7 @@ export default class AppProvider extends PureComponent {
async componentDidMount() {
await this.fetchAndSetPrices()
await this.setBalances()
this.setState({ isLoading: false })
setInterval(this.fetchAndSetPrices, ms(refreshInterval))
setInterval(this.setBalances, ms(refreshInterval))
@ -72,6 +74,7 @@ export default class AppProvider extends PureComponent {
await this.setState({
prices: Object.assign(
...prices.map(key => ({
ocean: 1,
[key]: json['ocean-protocol'][key]
}))
)

View File

@ -1,18 +1,9 @@
const locale = navigator.language
export const locale = navigator.language.split('-')[0]
// export const locale = 'de'
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat
const numberFormatter = number =>
export const numberFormatter = number =>
new Intl.NumberFormat(locale, {
minimumFractionDigits: 0,
maximumFractionDigits: 4
}).format(number)
const fiatFormatter = (currency, number) =>
new Intl.NumberFormat(locale, {
style: 'currency',
currency,
minimumFractionDigits: 2,
maximumFractionDigits: 2
}).format(number)
export { numberFormatter, fiatFormatter }