mirror of
https://github.com/kremalicious/blowfish.git
synced 2024-12-29 16:17:52 +01:00
add ticker, add loading state, number formatting
This commit is contained in:
parent
532e8ce7cb
commit
c4d071ddd0
@ -18,8 +18,8 @@
|
|||||||
"author": "Matthias Kretschmann",
|
"author": "Matthias Kretschmann",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@coingecko/cryptoformat": "^0.3.1",
|
||||||
"@oceanprotocol/typographies": "^0.1.0",
|
"@oceanprotocol/typographies": "^0.1.0",
|
||||||
"crypto-symbols": "^1.0.0",
|
|
||||||
"ms": "^2.1.1",
|
"ms": "^2.1.1",
|
||||||
"react": "^16.8.6",
|
"react": "^16.8.6",
|
||||||
"react-dom": "^16.8.6"
|
"react-dom": "^16.8.6"
|
||||||
|
13
src/App.css
13
src/App.css
@ -69,7 +69,6 @@ button {
|
|||||||
cursor: default;
|
cursor: default;
|
||||||
height: calc(100vh - 35px);
|
height: calc(100vh - 35px);
|
||||||
transition: .15s ease-out;
|
transition: .15s ease-out;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@ -92,6 +91,7 @@ button {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
animation: fadein .5s .5s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .main {
|
.dark .main {
|
||||||
@ -137,12 +137,12 @@ button {
|
|||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 0 .3rem;
|
padding: 0 .3rem;
|
||||||
animation: fadein .5s ease-out forwards;
|
animation: fadeIn .5s ease-out;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.updated {
|
.updated {
|
||||||
animation: updated .5s ease-out forwards;
|
animation: updated .5s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.number-unit-wrap--accounts {
|
.number-unit-wrap--accounts {
|
||||||
@ -176,17 +176,12 @@ button {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes fadein {
|
@keyframes fadeIn {
|
||||||
0% {
|
0% {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
50% {
|
|
||||||
background: rgba(255, 255, 255, .2);
|
|
||||||
}
|
|
||||||
|
|
||||||
100% {
|
100% {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
background: rgba(255, 255, 255, 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
25
src/App.jsx
25
src/App.jsx
@ -5,7 +5,8 @@ import { Consumer } from './store/createContext'
|
|||||||
import Titlebar from './components/Titlebar'
|
import Titlebar from './components/Titlebar'
|
||||||
import Total from './components/Total'
|
import Total from './components/Total'
|
||||||
import Account from './components/Account'
|
import Account from './components/Account'
|
||||||
import Actions from './components/Actions'
|
import Ticker from './components/Ticker'
|
||||||
|
import Spinner from './components/Spinner'
|
||||||
import './App.css'
|
import './App.css'
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -20,22 +21,28 @@ export default class App extends PureComponent {
|
|||||||
<AppProvider>
|
<AppProvider>
|
||||||
<Titlebar />
|
<Titlebar />
|
||||||
<div className="app__content">
|
<div className="app__content">
|
||||||
|
<Consumer>
|
||||||
|
{({ isLoading, accounts }) =>
|
||||||
|
isLoading ? (
|
||||||
|
<Spinner />
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
<main className="main">
|
<main className="main">
|
||||||
<Total />
|
<Total />
|
||||||
|
|
||||||
<div className="number-unit-wrap number-unit-wrap--accounts">
|
<div className="number-unit-wrap number-unit-wrap--accounts">
|
||||||
<Consumer>
|
{accounts.map((account, i) => (
|
||||||
{({ accounts }) =>
|
|
||||||
accounts.map((account, i) => (
|
|
||||||
<Account key={i} account={account} />
|
<Account key={i} account={account} />
|
||||||
))
|
))}
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<Ticker />
|
||||||
|
</>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
</Consumer>
|
</Consumer>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Actions />
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
</AppProvider>
|
</AppProvider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -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;
|
|
||||||
}
|
|
@ -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>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,25 +1,18 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { Consumer } from '../store/createContext'
|
import { Consumer } from '../store/createContext'
|
||||||
import { numberFormatter, fiatFormatter } from '../util/moneyFormatter'
|
import { locale } from '../util/moneyFormatter'
|
||||||
import symbols from 'crypto-symbols'
|
import { formatCurrency } from '@coingecko/cryptoformat'
|
||||||
|
|
||||||
const Balance = ({ balance }) => (
|
const Balance = ({ balance }) => (
|
||||||
<h1 className="number">
|
<h1 className="number">
|
||||||
<Consumer>
|
<Consumer>
|
||||||
{({ currency }) => {
|
{({ currency }) =>
|
||||||
const isFiat = currency === 'usd' || currency === 'eur'
|
formatCurrency(balance[currency], currency.toUpperCase(), locale)
|
||||||
const symbol =
|
.replace(/BTC/, 'Ƀ')
|
||||||
currency === 'ocean' ? 'Ọ' : symbols[currency.toUpperCase()]
|
.replace(/ETH/, 'Ξ')
|
||||||
|
.replace(/OCEAN/, 'Ọ')
|
||||||
return isFiat ? (
|
}
|
||||||
fiatFormatter(currency.toUpperCase(), balance[currency])
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
{symbol} {numberFormatter(balance[currency]) || 0}
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</Consumer>
|
</Consumer>
|
||||||
</h1>
|
</h1>
|
||||||
)
|
)
|
||||||
|
26
src/components/Spinner.css
Normal file
26
src/components/Spinner.css
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
5
src/components/Spinner.jsx
Normal file
5
src/components/Spinner.jsx
Normal 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
49
src/components/Ticker.css
Normal 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
34
src/components/Ticker.jsx
Normal 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>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -13,8 +13,8 @@ if (
|
|||||||
isDev = true
|
isDev = true
|
||||||
}
|
}
|
||||||
|
|
||||||
const width = 550
|
const width = 620
|
||||||
const height = 380
|
const height = 440
|
||||||
|
|
||||||
const isDarkMode = systemPreferences.isDarkMode()
|
const isDarkMode = systemPreferences.isDarkMode()
|
||||||
|
|
||||||
@ -30,6 +30,8 @@ const createWindow = async () => {
|
|||||||
backgroundColor: isDarkMode ? '#141414' : '#fff',
|
backgroundColor: isDarkMode ? '#141414' : '#fff',
|
||||||
frame: false,
|
frame: false,
|
||||||
show: false,
|
show: false,
|
||||||
|
title: 'Ocean',
|
||||||
|
scrollBounce: true,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
nodeIntegration: true
|
nodeIntegration: true
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ export default class AppProvider extends PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
|
isLoading: true,
|
||||||
accounts: [],
|
accounts: [],
|
||||||
currency: 'ocean',
|
currency: 'ocean',
|
||||||
prices: Object.assign(...prices.map(key => ({ [key]: 0 }))),
|
prices: Object.assign(...prices.map(key => ({ [key]: 0 }))),
|
||||||
@ -24,6 +25,7 @@ export default class AppProvider extends PureComponent {
|
|||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
await this.fetchAndSetPrices()
|
await this.fetchAndSetPrices()
|
||||||
await this.setBalances()
|
await this.setBalances()
|
||||||
|
this.setState({ isLoading: false })
|
||||||
|
|
||||||
setInterval(this.fetchAndSetPrices, ms(refreshInterval))
|
setInterval(this.fetchAndSetPrices, ms(refreshInterval))
|
||||||
setInterval(this.setBalances, ms(refreshInterval))
|
setInterval(this.setBalances, ms(refreshInterval))
|
||||||
@ -72,6 +74,7 @@ export default class AppProvider extends PureComponent {
|
|||||||
await this.setState({
|
await this.setState({
|
||||||
prices: Object.assign(
|
prices: Object.assign(
|
||||||
...prices.map(key => ({
|
...prices.map(key => ({
|
||||||
|
ocean: 1,
|
||||||
[key]: json['ocean-protocol'][key]
|
[key]: json['ocean-protocol'][key]
|
||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
|
@ -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
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat
|
||||||
const numberFormatter = number =>
|
export const numberFormatter = number =>
|
||||||
new Intl.NumberFormat(locale, {
|
new Intl.NumberFormat(locale, {
|
||||||
minimumFractionDigits: 0,
|
minimumFractionDigits: 0,
|
||||||
maximumFractionDigits: 4
|
maximumFractionDigits: 4
|
||||||
}).format(number)
|
}).format(number)
|
||||||
|
|
||||||
const fiatFormatter = (currency, number) =>
|
|
||||||
new Intl.NumberFormat(locale, {
|
|
||||||
style: 'currency',
|
|
||||||
currency,
|
|
||||||
minimumFractionDigits: 2,
|
|
||||||
maximumFractionDigits: 2
|
|
||||||
}).format(number)
|
|
||||||
|
|
||||||
export { numberFormatter, fiatFormatter }
|
|
||||||
|
Loading…
Reference in New Issue
Block a user