1
0
mirror of https://github.com/kremalicious/blowfish.git synced 2024-12-27 23:27:52 +01:00

switch currencies, balance component

This commit is contained in:
Matthias Kretschmann 2019-05-06 00:10:28 +02:00
parent a296148f5b
commit 8f42243539
Signed by: m
GPG Key ID: 606EEEF3C479A91F
13 changed files with 187 additions and 113 deletions

View File

@ -1,24 +1,26 @@
# ocean-balance # ocean-balance
> Simple Electron-based desktop app to retrieve and display your total Ocean balances. > Simple Electron-based desktop app to retrieve and display your total Ocean Token balances.
> https://oceanprotocol.com
## Usage ## Usage
Clone and run: Clone, add adresses, and run:
```bash ```bash
# Clone this repository # Clone this repository
git clone git@github.com:kremalicious/ocean-balance.git git clone git@github.com:kremalicious/ocean-balance.git
cd ocean-balance cd ocean-balance
# Add one or more Ethereum addresses to config file
vi config.js
# Install dependencies # Install dependencies
npm install npm install
# Run the app in dev mode # Run the app in dev mode
npm start npm start
``` ```
## Configuration
## Build package ## Build package
```bash ```bash

View File

@ -55,11 +55,25 @@ html.fullscreen {
transform: translate3d(0, -36px, 0); transform: translate3d(0, -36px, 0);
} }
.main {
width: 100%;
padding: 5%;
background: #303030;
border-radius: 5px;
border: .1rem solid #41474e;
min-height: 186px;
display: flex;
align-items: center;
flex-wrap: wrap;
position: relative;
}
.number-unit-wrap { .number-unit-wrap {
display: flex; display: flex;
width: 100%; width: 100%;
flex-wrap: wrap; flex-wrap: wrap;
justify-content: space-around; justify-content: space-around;
position: relative;
} }
.number-unit { .number-unit {
@ -92,6 +106,20 @@ html.fullscreen {
font-size: .85rem; font-size: .85rem;
display: block; display: block;
white-space: nowrap; white-space: nowrap;
margin-top: .3rem;
}
.number-unit-wrap--accounts {
min-height: 55px;
}
.number-unit--main {
padding-bottom: 5%;
border-bottom: 1px solid #41474e;
}
.number-unit--main .number {
font-size: 2.5rem;
} }
@keyframes updated { @keyframes updated {

View File

@ -1,9 +1,12 @@
import './App.css'
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import { webFrame } from 'electron' import { webFrame } from 'electron'
import AppProvider from './store/AppProvider' import AppProvider from './store/AppProvider'
import { Consumer } from './store/createContext'
import Titlebar from './components/Titlebar' import Titlebar from './components/Titlebar'
import Accounts from './components/Accounts' import Total from './components/Total'
import Account from './components/Account'
import Actions from './components/Actions'
import './App.css'
// //
// Disable zooming // Disable zooming
@ -11,17 +14,28 @@ import Accounts from './components/Accounts'
webFrame.setVisualZoomLevelLimits(1, 1) webFrame.setVisualZoomLevelLimits(1, 1)
webFrame.setLayoutZoomLevelLimits(0, 0) webFrame.setLayoutZoomLevelLimits(0, 0)
class App extends PureComponent { export default class App extends PureComponent {
render() { render() {
return ( return (
<AppProvider> <AppProvider>
<Titlebar /> <Titlebar />
<div className="app__content"> <div className="app__content">
<Accounts /> <main className="main">
<Actions />
<Total />
<div className="number-unit-wrap number-unit-wrap--accounts">
<Consumer>
{({ accounts }) =>
accounts.map((account, i) => (
<Account key={i} account={account} />
))
}
</Consumer>
</div>
</main>
</div> </div>
</AppProvider> </AppProvider>
) )
} }
} }
export default App

View File

@ -1,33 +1,27 @@
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { fiatFormatter, numberFormatter } from '../util/moneyFormatter' import Balance from './Balance'
class Account extends PureComponent { export default class Account extends PureComponent {
static propTypes = { static propTypes = {
isNativeShown: PropTypes.bool.isRequired,
account: PropTypes.shape({ account: PropTypes.shape({
address: PropTypes.string.isRequired, address: PropTypes.string.isRequired,
balance: PropTypes.shape({ balance: PropTypes.shape({
ocean: PropTypes.number.isRequired, ocean: PropTypes.number.isRequired,
eur: PropTypes.number.isRequired eur: PropTypes.number.isRequired,
usd: PropTypes.number.isRequired
}).isRequired }).isRequired
}) })
} }
render() { render() {
const { balance, address } = this.props.account const { account } = this.props
const { ocean, eur } = balance const { balance, address } = account
return ( return (
<div className="number-unit"> <div className="number-unit">
<h1 className="number"> <h1 className="number">
{this.props.isNativeShown ? ( <Balance balance={balance} />
<span className="balance-native">{fiatFormatter('EUR', eur)}</span>
) : (
<span className="balance" title={numberFormatter(ocean)}>
{numberFormatter(ocean) || 0} Ọ
</span>
)}
</h1> </h1>
<span className="label" title={address}> <span className="label" title={address}>
{address.substring(0, 12)}... {address.substring(0, 12)}...
@ -36,5 +30,3 @@ class Account extends PureComponent {
) )
} }
} }
export default Account

View File

@ -1,29 +0,0 @@
.main {
width: 100%;
padding: 5%;
background: #303030;
border-radius: 5px;
border: .1rem solid #41474e;
min-height: 186px;
display: flex;
align-items: center;
flex-wrap: wrap;
position: relative;
}
.number-unit-wrap--accounts {
min-height: 55px;
}
.number-unit--main {
padding-bottom: 5%;
border-bottom: 1px solid #41474e;
}
.number-unit--main .number {
font-size: 2.5rem;
}
.number-unit--main .label {
font-size: .95rem;
}

View File

@ -1,40 +0,0 @@
import React, { PureComponent } from 'react'
import { Consumer } from '../store/createContext'
import Total from './Total'
import Account from './Account'
import './Accounts.css'
export default class Accounts extends PureComponent {
state = {
isNativeShown: false
}
toggleBalances = () => {
this.setState({ isNativeShown: !this.state.isNativeShown })
}
render() {
return (
<main className="main">
<Total />
<div
className="number-unit-wrap number-unit-wrap--accounts"
onClick={this.toggleBalances}
>
<Consumer>
{({ accounts }) =>
accounts.map((account, i) => (
<Account
key={i}
account={account}
isNativeShown={this.state.isNativeShown}
/>
))
}
</Consumer>
</div>
</main>
)
}
}

View File

@ -0,0 +1,17 @@
.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

@ -0,0 +1,21 @@
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 }) => (
<>
<button onClick={() => toggleCurrencies('ocean')}>OCEAN</button>
<button onClick={() => toggleCurrencies('eur')}>EUR</button>
<button onClick={() => toggleCurrencies('usd')}>USD</button>
</>
)}
</Consumer>
</div>
)
}
}

View File

@ -0,0 +1,34 @@
import React from 'react'
import PropTypes from 'prop-types'
import { Consumer } from '../store/createContext'
import { numberFormatter, fiatFormatter } from '../util/moneyFormatter'
const Balance = ({ balance }) => {
const { ocean, eur, usd } = balance
return (
<Consumer>
{({ currency }) =>
currency === 'ocean' ? (
<span className="balance" title={numberFormatter(ocean)}>
Ọ {numberFormatter(ocean) || 0}
</span>
) : currency === 'eur' ? (
<span className="balance">{fiatFormatter('EUR', eur)}</span>
) : (
<span className="balance">{fiatFormatter('USD', usd)}</span>
)
}
</Consumer>
)
}
Balance.propTypes = {
balance: PropTypes.shape({
ocean: PropTypes.number.isRequired,
eur: PropTypes.number.isRequired,
usd: PropTypes.number.isRequired
})
}
export default Balance

View File

@ -1,12 +1,12 @@
import React from 'react' import React from 'react'
import { Consumer } from '../store/createContext' import { Consumer } from '../store/createContext'
import { numberFormatter } from '../util/moneyFormatter' import Balance from './Balance'
const calculateTotalBalance = accounts => { const calculateTotalBalance = (accounts, currency) => {
const balanceTotalArray = [] const balanceTotalArray = []
for (const account of accounts) { for (const account of accounts) {
balanceTotalArray.push(account.balance.ocean) balanceTotalArray.push(account.balance[currency])
} }
// Convert array to numbers and add numbers together // Convert array to numbers and add numbers together
@ -15,15 +15,28 @@ const calculateTotalBalance = accounts => {
0 0
) )
return numberFormatter(balanceTotal) return balanceTotal
} }
const Total = () => ( const Total = () => (
<div className="number-unit number-unit--main"> <div className="number-unit number-unit--main">
<Consumer> <Consumer>
{({ accounts }) => { {({ accounts }) => {
const total = calculateTotalBalance(accounts) const totalOcean = calculateTotalBalance(accounts, 'ocean')
return <h1 className="number">{total || 0} Ọ</h1> const totalEur = calculateTotalBalance(accounts, 'eur')
const totalUsd = calculateTotalBalance(accounts, 'usd')
const balance = {
ocean: totalOcean,
eur: totalEur,
usd: totalUsd
}
return (
<h1 className="number">
<Balance balance={balance} />
</h1>
)
}} }}
</Consumer> </Consumer>
<span className="label">Total balance</span> <span className="label">Total balance</span>

View File

@ -2,7 +2,7 @@ import React, { PureComponent } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import ms from 'ms' import ms from 'ms'
import { Provider } from './createContext' import { Provider } from './createContext'
import { accounts, refreshInterval, oceanTokenContract } from '../../constants' import { accounts, refreshInterval, oceanTokenContract } from '../../config'
export default class AppProvider extends PureComponent { export default class AppProvider extends PureComponent {
static propTypes = { static propTypes = {
@ -10,7 +10,9 @@ export default class AppProvider extends PureComponent {
} }
state = { state = {
accounts: [] accounts: [],
currency: 'ocean',
toggleCurrencies: currency => this.setState({ currency })
} }
componentDidMount() { componentDidMount() {
@ -26,11 +28,9 @@ export default class AppProvider extends PureComponent {
this.setState({ accounts: [] }) this.setState({ accounts: [] })
} }
fetchBalance = async account => { async fetch(url) {
try { try {
const response = await fetch( const response = await fetch(url)
`https://api.etherscan.io/api?module=account&action=tokenbalance&contractaddress=${oceanTokenContract}&address=${account}`
)
if (response.status !== 200) { if (response.status !== 200) {
return console.log('Non-200 response: ' + response.status) // eslint-disable-line return console.log('Non-200 response: ' + response.status) // eslint-disable-line
@ -39,16 +39,37 @@ export default class AppProvider extends PureComponent {
const json = await response.json() const json = await response.json()
if (!json) return if (!json) return
const balance = (json.result /= 1000000000000000000) // Convert from wei 10^18 return json
return balance
} catch (error) { } catch (error) {
console.log('Error parsing etherscan.io json:' + error) // eslint-disable-line console.log('Error parsing json:' + error) // eslint-disable-line
} }
} }
setBalances = () => { fetchBalance = async account => {
const json = await this.fetch(
`https://api.etherscan.io/api?module=account&action=tokenbalance&contractaddress=${oceanTokenContract}&address=${account}&tag=latest`
)
const balance = (json.result /= 1000000000000000000) // Convert from wei 10^18
return balance
}
fetchPrice = async () => {
const json = await this.fetch(
'https://api.coingecko.com/api/v3/simple/price?ids=ocean-protocol&vs_currencies=usd,eur'
)
const { usd, eur } = json['ocean-protocol']
return { usd, eur }
}
setBalances = async () => {
// TODO: make this less lazy and update numbers in place
// when they are changed instead of resetting all to 0 here
this.clearAccounts() this.clearAccounts()
const { usd, eur } = await this.fetchPrice()
accounts.map(async account => { accounts.map(async account => {
const oceanBalance = await this.fetchBalance(account) const oceanBalance = await this.fetchBalance(account)
@ -56,7 +77,8 @@ export default class AppProvider extends PureComponent {
address: account, address: account,
balance: { balance: {
ocean: oceanBalance || 0, ocean: oceanBalance || 0,
eur: 0 eur: oceanBalance / eur || 0,
usd: oceanBalance / usd || 0
} }
} }

View File

@ -4,14 +4,14 @@ const locale = navigator.language
const numberFormatter = number => const numberFormatter = number =>
new Intl.NumberFormat(locale, { new Intl.NumberFormat(locale, {
minimumFractionDigits: 0, minimumFractionDigits: 0,
maximumFractionDigits: 2 maximumFractionDigits: 4
}).format(number) }).format(number)
const fiatFormatter = (currency, number) => const fiatFormatter = (currency, number) =>
new Intl.NumberFormat(locale, { new Intl.NumberFormat(locale, {
style: 'currency', style: 'currency',
currency, currency,
minimumFractionDigits: 0, minimumFractionDigits: 2,
maximumFractionDigits: 2 maximumFractionDigits: 2
}).format(number) }).format(number)