refactor, add global error state

This commit is contained in:
Matthias Kretschmann 2020-02-25 15:16:44 +01:00
parent 2efee0ec73
commit 33532c33d3
Signed by: m
GPG Key ID: 606EEEF3C479A91F
12 changed files with 101 additions and 53 deletions

1
.env.example Normal file
View File

@ -0,0 +1 @@
ETHERSCAN_API_KEY=

3
.gitignore vendored
View File

@ -5,4 +5,5 @@ build
dist
coverage
.next
out
out
.env

View File

@ -53,6 +53,7 @@
"babel-jest": "^25.1.0",
"copy": "^0.3.2",
"cross-env": "^7.0.0",
"dotenv": "^8.2.0",
"electron": "^8.0.1",
"electron-builder": "^22.3.2",
"electron-devtools-installer": "^2.2.4",

View File

@ -2,8 +2,6 @@ import React from 'react'
import PropTypes from 'prop-types'
import posed, { PoseGroup } from 'react-pose'
import shortid from 'shortid'
import AppProvider from './store/AppProvider'
import PriceProvider from './store/PriceProvider'
import { defaultAnimation } from './components/Animations'
import Titlebar from './components/Titlebar'
import styles from './Layout.module.css'
@ -12,16 +10,14 @@ const Animation = posed.div(defaultAnimation)
export default function Layout({ children }) {
return (
<PriceProvider>
<AppProvider>
{process.platform === 'darwin' && <Titlebar />}
<div className={styles.app}>
<PoseGroup animateOnMount>
<Animation key={shortid.generate()}>{children}</Animation>
</PoseGroup>
</div>
</AppProvider>
</PriceProvider>
<>
{process.platform === 'darwin' && <Titlebar />}
<div className={styles.app}>
<PoseGroup animateOnMount>
<Animation key={shortid.generate()}>{children}</Animation>
</PoseGroup>
</div>
</>
)
}

View File

@ -1,5 +1,6 @@
// eslint-disable-next-line no-unused-vars
const withSvgr = (nextConfig = {}, nextComposePlugins = {}) => {
require('dotenv').config()
const withSvgr = (nextConfig = {}) => {
return Object.assign({}, nextConfig, {
webpack(config, options) {
config.module.rules.push({
@ -23,15 +24,25 @@ const withSvgr = (nextConfig = {}, nextComposePlugins = {}) => {
})
}
module.exports = withSvgr({
webpack: config => {
config.target = 'electron-renderer'
return config
},
exportPathMap() {
return {
'/': { page: '/' },
'/preferences': { page: '/preferences' }
const withElectron = (nextConfig = {}) => {
return Object.assign({}, nextConfig, {
webpack: config => {
config.target = 'electron-renderer'
return config
}
}
})
})
}
module.exports = withSvgr(
withElectron({
env: {
ETHERSCAN_API_KEY: process.env.ETHERSCAN_API_KEY
},
exportPathMap() {
return {
'/': { page: '/' },
'/preferences': { page: '/preferences' }
}
}
})
)

View File

@ -2,6 +2,8 @@ import React, { useEffect } from 'react'
import PropTypes from 'prop-types'
import Router from 'next/router'
// import { ipcRenderer } from 'electron'
import AppProvider from '../store/AppProvider'
import PriceProvider from '../store/PriceProvider'
import Layout from '../Layout'
import '../global.css'
@ -14,9 +16,13 @@ export default function App({ Component, pageProps }) {
}, [])
return (
<Layout>
<Component {...pageProps} />
</Layout>
<PriceProvider>
<AppProvider>
<Layout>
<Component {...pageProps} />
</Layout>
</AppProvider>
</PriceProvider>
)
}

View File

@ -11,7 +11,7 @@ import IconCog from '../images/cog.svg'
import styles from './index.module.css'
export default function Home() {
const { isLoading, needsConfig } = useContext(AppContext)
const { isLoading, error, needsConfig } = useContext(AppContext)
return (
<>
@ -24,6 +24,8 @@ export default function Home() {
{needsConfig ? (
<Welcome />
) : error ? (
<div className={styles.error}>{error}</div>
) : isLoading ? (
<Spinner />
) : (

View File

@ -32,3 +32,8 @@
justify-items: start;
width: 100%;
}
.error {
font-size: 0.9rem;
color: lightcoral;
}

View File

@ -10,7 +10,7 @@ import { refreshInterval, conversions, oceanTokenContract } from '../../config'
async function getBalance(account) {
const json = await fetchData(
`https://api.etherscan.io/api?module=account&action=tokenbalance&contractaddress=${oceanTokenContract}&address=${account}&tag=latest`
`https://api.etherscan.io/api?module=account&action=tokenbalance&contractaddress=${oceanTokenContract}&address=${account}&tag=latest&apikey=${process.env.ETHERSCAN_API_KEY}`
)
const balance = unit.fromWei(`${json.result}`, 'ether')
@ -24,18 +24,26 @@ export default function AppProvider({ children }) {
const [needsConfig, setNeedsConfig] = useState(false)
const [currency, setCurrency] = useState('ocean')
const [accentColor, setAccentColor] = useState('#f6388a')
const [error, setError] = useState()
useEffect(() => {
// listener for accent color
global.ipcRenderer.on('accent-color', (evt, accentColor) => {
setAccentColor(accentColor)
})
if (process.env.NODE_ENV !== 'test') {
global.ipcRenderer.on('accent-color', (evt, accentColor) => {
setAccentColor(accentColor)
})
}
}, [])
useEffect(() => {
async function init() {
await setBalances()
setIsLoading(false)
try {
await setBalances()
setIsLoading(false)
} catch (error) {
console.error(error.message)
setError(error.message)
}
// listener for touchbar
global.ipcRenderer.on('setCurrency', (evt, currency) =>
@ -96,9 +104,9 @@ export default function AppProvider({ children }) {
}
function toggleCurrencies(currency) {
setCurrency(currency)
const pricesNew = Array.from(prices)
global.ipcRenderer.send('currency-updated', pricesNew, currency)
setCurrency(currency)
}
const context = {
@ -107,8 +115,9 @@ export default function AppProvider({ children }) {
currency,
needsConfig,
accentColor,
toggleCurrencies: currency => toggleCurrencies(currency),
setBalances: () => setBalances()
error,
toggleCurrencies,
setBalances
}
return <AppContext.Provider value={context}>{children}</AppContext.Provider>

View File

@ -41,10 +41,14 @@ export default function PriceProvider({ children }) {
useEffect(() => {
async function init() {
const { newPrices, newPriceChanges } = await fetchAndSetPrices()
setPrices(newPrices)
setPriceChanges(newPriceChanges)
global.ipcRenderer.send('prices-updated', Array.from(newPrices)) // convert Map to array, ipc messages seem to kill it
try {
const { newPrices, newPriceChanges } = await fetchAndSetPrices()
setPrices(newPrices)
setPriceChanges(newPriceChanges)
global.ipcRenderer.send('prices-updated', Array.from(newPrices)) // convert Map to array, ipc messages seem to kill it
} catch (error) {
console.error(error.message)
}
}
init()

22
tests/Providers.test.jsx Normal file
View File

@ -0,0 +1,22 @@
import React from 'react'
import { render, waitForElement } from '@testing-library/react'
import AppProvider from '../src/renderer/store/AppProvider'
import PriceProvider from '../src/renderer/store/PriceProvider'
import { PriceContext } from '../src/renderer/store/createContext'
import { priceContext } from './__fixtures__/context'
describe('Providers', () => {
it('PriceProvider', async () => {
const { getByText } = render(<PriceProvider>Hello</PriceProvider>)
await waitForElement(() => getByText('Hello'))
})
it('AppProvider', async () => {
const { getByText } = render(
<PriceContext.Provider value={priceContext}>
<AppProvider>Hello</AppProvider>
</PriceContext.Provider>
)
await waitForElement(() => getByText('Hello'))
})
})

View File

@ -1,10 +0,0 @@
const global = {
ipcRenderer: {
on: () => jest.fn()
},
store: {
has: () => jest.fn()
}
}
module.exports = global