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
|
# 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
|
||||||
|
28
src/App.css
28
src/App.css
@ -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 {
|
||||||
|
26
src/App.jsx
26
src/App.jsx
@ -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
|
|
||||||
|
@ -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
|
|
||||||
|
@ -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 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>
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user