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:
parent
a296148f5b
commit
8f42243539
10
README.md
10
README.md
@ -1,24 +1,26 @@
|
||||
# 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
|
||||
|
||||
Clone and run:
|
||||
Clone, add adresses, and run:
|
||||
|
||||
```bash
|
||||
# Clone this repository
|
||||
git clone git@github.com:kremalicious/ocean-balance.git
|
||||
cd ocean-balance
|
||||
|
||||
# Add one or more Ethereum addresses to config file
|
||||
vi config.js
|
||||
|
||||
# Install dependencies
|
||||
npm install
|
||||
# Run the app in dev mode
|
||||
npm start
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
## Build package
|
||||
|
||||
```bash
|
||||
|
28
src/App.css
28
src/App.css
@ -55,11 +55,25 @@ html.fullscreen {
|
||||
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 {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-around;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.number-unit {
|
||||
@ -92,6 +106,20 @@ html.fullscreen {
|
||||
font-size: .85rem;
|
||||
display: block;
|
||||
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 {
|
||||
|
26
src/App.jsx
26
src/App.jsx
@ -1,9 +1,12 @@
|
||||
import './App.css'
|
||||
import React, { PureComponent } from 'react'
|
||||
import { webFrame } from 'electron'
|
||||
import AppProvider from './store/AppProvider'
|
||||
import { Consumer } from './store/createContext'
|
||||
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
|
||||
@ -11,17 +14,28 @@ import Accounts from './components/Accounts'
|
||||
webFrame.setVisualZoomLevelLimits(1, 1)
|
||||
webFrame.setLayoutZoomLevelLimits(0, 0)
|
||||
|
||||
class App extends PureComponent {
|
||||
export default class App extends PureComponent {
|
||||
render() {
|
||||
return (
|
||||
<AppProvider>
|
||||
<Titlebar />
|
||||
<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>
|
||||
</AppProvider>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default App
|
||||
|
@ -1,33 +1,27 @@
|
||||
import React, { PureComponent } from 'react'
|
||||
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 = {
|
||||
isNativeShown: PropTypes.bool.isRequired,
|
||||
account: PropTypes.shape({
|
||||
address: PropTypes.string.isRequired,
|
||||
balance: PropTypes.shape({
|
||||
ocean: PropTypes.number.isRequired,
|
||||
eur: PropTypes.number.isRequired
|
||||
eur: PropTypes.number.isRequired,
|
||||
usd: PropTypes.number.isRequired
|
||||
}).isRequired
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
const { balance, address } = this.props.account
|
||||
const { ocean, eur } = balance
|
||||
const { account } = this.props
|
||||
const { balance, address } = account
|
||||
|
||||
return (
|
||||
<div className="number-unit">
|
||||
<h1 className="number">
|
||||
{this.props.isNativeShown ? (
|
||||
<span className="balance-native">{fiatFormatter('EUR', eur)}</span>
|
||||
) : (
|
||||
<span className="balance" title={numberFormatter(ocean)}>
|
||||
{numberFormatter(ocean) || 0} Ọ
|
||||
</span>
|
||||
)}
|
||||
<Balance balance={balance} />
|
||||
</h1>
|
||||
<span className="label" title={address}>
|
||||
{address.substring(0, 12)}...
|
||||
@ -36,5 +30,3 @@ class Account extends PureComponent {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Account
|
||||
|
@ -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;
|
||||
}
|
@ -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>
|
||||
)
|
||||
}
|
||||
}
|
17
src/components/Actions.css
Normal file
17
src/components/Actions.css
Normal 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;
|
||||
}
|
21
src/components/Actions.jsx
Normal file
21
src/components/Actions.jsx
Normal 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>
|
||||
)
|
||||
}
|
||||
}
|
34
src/components/Balance.jsx
Normal file
34
src/components/Balance.jsx
Normal 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
|
@ -1,12 +1,12 @@
|
||||
import React from 'react'
|
||||
import { Consumer } from '../store/createContext'
|
||||
import { numberFormatter } from '../util/moneyFormatter'
|
||||
import Balance from './Balance'
|
||||
|
||||
const calculateTotalBalance = accounts => {
|
||||
const calculateTotalBalance = (accounts, currency) => {
|
||||
const balanceTotalArray = []
|
||||
|
||||
for (const account of accounts) {
|
||||
balanceTotalArray.push(account.balance.ocean)
|
||||
balanceTotalArray.push(account.balance[currency])
|
||||
}
|
||||
|
||||
// Convert array to numbers and add numbers together
|
||||
@ -15,15 +15,28 @@ const calculateTotalBalance = accounts => {
|
||||
0
|
||||
)
|
||||
|
||||
return numberFormatter(balanceTotal)
|
||||
return balanceTotal
|
||||
}
|
||||
|
||||
const Total = () => (
|
||||
<div className="number-unit number-unit--main">
|
||||
<Consumer>
|
||||
{({ accounts }) => {
|
||||
const total = calculateTotalBalance(accounts)
|
||||
return <h1 className="number">{total || 0} Ọ</h1>
|
||||
const totalOcean = calculateTotalBalance(accounts, 'ocean')
|
||||
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>
|
||||
<span className="label">Total balance</span>
|
||||
|
@ -2,7 +2,7 @@ import React, { PureComponent } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import ms from 'ms'
|
||||
import { Provider } from './createContext'
|
||||
import { accounts, refreshInterval, oceanTokenContract } from '../../constants'
|
||||
import { accounts, refreshInterval, oceanTokenContract } from '../../config'
|
||||
|
||||
export default class AppProvider extends PureComponent {
|
||||
static propTypes = {
|
||||
@ -10,7 +10,9 @@ export default class AppProvider extends PureComponent {
|
||||
}
|
||||
|
||||
state = {
|
||||
accounts: []
|
||||
accounts: [],
|
||||
currency: 'ocean',
|
||||
toggleCurrencies: currency => this.setState({ currency })
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
@ -26,11 +28,9 @@ export default class AppProvider extends PureComponent {
|
||||
this.setState({ accounts: [] })
|
||||
}
|
||||
|
||||
fetchBalance = async account => {
|
||||
async fetch(url) {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`https://api.etherscan.io/api?module=account&action=tokenbalance&contractaddress=${oceanTokenContract}&address=${account}`
|
||||
)
|
||||
const response = await fetch(url)
|
||||
|
||||
if (response.status !== 200) {
|
||||
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()
|
||||
if (!json) return
|
||||
|
||||
const balance = (json.result /= 1000000000000000000) // Convert from wei 10^18
|
||||
return balance
|
||||
return json
|
||||
} 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()
|
||||
|
||||
const { usd, eur } = await this.fetchPrice()
|
||||
|
||||
accounts.map(async account => {
|
||||
const oceanBalance = await this.fetchBalance(account)
|
||||
|
||||
@ -56,7 +77,8 @@ export default class AppProvider extends PureComponent {
|
||||
address: account,
|
||||
balance: {
|
||||
ocean: oceanBalance || 0,
|
||||
eur: 0
|
||||
eur: oceanBalance / eur || 0,
|
||||
usd: oceanBalance / usd || 0
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,14 +4,14 @@ const locale = navigator.language
|
||||
const numberFormatter = number =>
|
||||
new Intl.NumberFormat(locale, {
|
||||
minimumFractionDigits: 0,
|
||||
maximumFractionDigits: 2
|
||||
maximumFractionDigits: 4
|
||||
}).format(number)
|
||||
|
||||
const fiatFormatter = (currency, number) =>
|
||||
new Intl.NumberFormat(locale, {
|
||||
style: 'currency',
|
||||
currency,
|
||||
minimumFractionDigits: 0,
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
}).format(number)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user