import React, { PureComponent } from 'react' import PropTypes from 'prop-types' import ms from 'ms' // import { ipcRenderer } from 'electron' import Store from 'electron-store' import { AppContext } from './createContext' import { fetchData } from '../../utils' import { refreshInterval, conversions, oceanTokenContract } from '../../config' // construct initial prices Map to get consistent // order for Ticker and Touchbar let pricesMap = new Map() pricesMap.set('ocean', 1) conversions.map(key => pricesMap.set(key, 0)) export default class AppProvider extends PureComponent { static propTypes = { children: PropTypes.any.isRequired } store = process.env.NODE_ENV === 'test' ? new Store() : global.store state = { isLoading: true, accounts: [], currency: 'ocean', needsConfig: false, prices: pricesMap, priceChanges: Object.assign( ...conversions.map(key => ({ [key]: 0 })) ), toggleCurrencies: currency => this.toggleCurrencies(currency), setBalances: () => this.setBalances(), accentColor: '#f6388a' } async componentDidMount() { // listener for accent color global.ipcRenderer.on('accent-color', (event, accentColor) => { this.setState({ accentColor }) }) // listener for touchbar global.ipcRenderer.on('setCurrency', (evt, currency) => this.state.toggleCurrencies(currency) ) const newPrizes = await this.fetchAndSetPrices() this.setState({ prices: newPrizes }) await this.setBalances() setInterval(this.fetchAndSetPrices, ms(refreshInterval)) setInterval(this.setBalances, ms(refreshInterval)) this.setState({ isLoading: false }) } getAccounts() { let accountsPref if (this.store.has('accounts')) { accountsPref = this.store.get('accounts') !accountsPref.length ? this.setState({ needsConfig: true }) : this.setState({ needsConfig: false }) } else { accountsPref = [] this.setState({ needsConfig: true }) } return accountsPref } async getBalance(account) { const json = await fetchData( `https://api.etherscan.io/api?module=account&action=tokenbalance&contractaddress=${oceanTokenContract}&address=${account}&tag=latest` ) const balance = json.result / 1e18 // Convert from vodka 10^18 return balance } fetchAndSetPrices = async () => { const currencies = conversions.join(',') const json = await fetchData( `https://api.coingecko.com/api/v3/simple/price?ids=ocean-protocol&vs_currencies=${currencies}&include_24hr_change=true` ) let newPrices = new Map(this.state.prices) // make a shallow copy of the Map conversions.map(key => newPrices.set(key, json['ocean-protocol'][key])) // modify the copy const newPriceChanges = await Object.assign( ...conversions.map(key => ({ [key]: json['ocean-protocol'][key + '_24h_change'] })) ) global.ipcRenderer.send('prices-updated', Array.from(newPrices)) // convert Map to array, ipc messages seem to kill it this.setState({ prices: newPrices, priceChanges: newPriceChanges }) return newPrices } setBalances = async () => { const accountsPref = await this.getAccounts() let newAccounts = [] for (const account of accountsPref) { const oceanBalance = await this.getBalance(account) const conversionsBalance = Object.assign( ...conversions.map(key => ({ [key]: oceanBalance * this.state.prices.get(key) || 0 })) ) const newAccount = { address: account, balance: { ocean: oceanBalance, ...conversionsBalance } } newAccounts.push(newAccount) } if (newAccounts !== this.state.accounts) { this.setState({ accounts: newAccounts }) } } toggleCurrencies(currency) { const pricesNew = Array.from(this.state.prices) global.ipcRenderer.send('currency-updated', pricesNew, currency) this.setState({ currency }) } render() { return ( {this.props.children} ) } }