1
0
mirror of https://github.com/kremalicious/blowfish.git synced 2024-11-22 09:47:00 +01:00

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 dist
coverage coverage
.next .next
out out
.env

View File

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

View File

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

View File

@ -1,5 +1,6 @@
// eslint-disable-next-line no-unused-vars require('dotenv').config()
const withSvgr = (nextConfig = {}, nextComposePlugins = {}) => {
const withSvgr = (nextConfig = {}) => {
return Object.assign({}, nextConfig, { return Object.assign({}, nextConfig, {
webpack(config, options) { webpack(config, options) {
config.module.rules.push({ config.module.rules.push({
@ -23,15 +24,25 @@ const withSvgr = (nextConfig = {}, nextComposePlugins = {}) => {
}) })
} }
module.exports = withSvgr({ const withElectron = (nextConfig = {}) => {
webpack: config => { return Object.assign({}, nextConfig, {
config.target = 'electron-renderer' webpack: config => {
return config config.target = 'electron-renderer'
}, return config
exportPathMap() {
return {
'/': { page: '/' },
'/preferences': { page: '/preferences' }
} }
} })
}) }
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 PropTypes from 'prop-types'
import Router from 'next/router' import Router from 'next/router'
// import { ipcRenderer } from 'electron' // import { ipcRenderer } from 'electron'
import AppProvider from '../store/AppProvider'
import PriceProvider from '../store/PriceProvider'
import Layout from '../Layout' import Layout from '../Layout'
import '../global.css' import '../global.css'
@ -14,9 +16,13 @@ export default function App({ Component, pageProps }) {
}, []) }, [])
return ( return (
<Layout> <PriceProvider>
<Component {...pageProps} /> <AppProvider>
</Layout> <Layout>
<Component {...pageProps} />
</Layout>
</AppProvider>
</PriceProvider>
) )
} }

View File

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

View File

@ -32,3 +32,8 @@
justify-items: start; justify-items: start;
width: 100%; 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) { async function getBalance(account) {
const json = await fetchData( 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') const balance = unit.fromWei(`${json.result}`, 'ether')
@ -24,18 +24,26 @@ export default function AppProvider({ children }) {
const [needsConfig, setNeedsConfig] = useState(false) const [needsConfig, setNeedsConfig] = useState(false)
const [currency, setCurrency] = useState('ocean') const [currency, setCurrency] = useState('ocean')
const [accentColor, setAccentColor] = useState('#f6388a') const [accentColor, setAccentColor] = useState('#f6388a')
const [error, setError] = useState()
useEffect(() => { useEffect(() => {
// listener for accent color // listener for accent color
global.ipcRenderer.on('accent-color', (evt, accentColor) => { if (process.env.NODE_ENV !== 'test') {
setAccentColor(accentColor) global.ipcRenderer.on('accent-color', (evt, accentColor) => {
}) setAccentColor(accentColor)
})
}
}, []) }, [])
useEffect(() => { useEffect(() => {
async function init() { async function init() {
await setBalances() try {
setIsLoading(false) await setBalances()
setIsLoading(false)
} catch (error) {
console.error(error.message)
setError(error.message)
}
// listener for touchbar // listener for touchbar
global.ipcRenderer.on('setCurrency', (evt, currency) => global.ipcRenderer.on('setCurrency', (evt, currency) =>
@ -96,9 +104,9 @@ export default function AppProvider({ children }) {
} }
function toggleCurrencies(currency) { function toggleCurrencies(currency) {
setCurrency(currency)
const pricesNew = Array.from(prices) const pricesNew = Array.from(prices)
global.ipcRenderer.send('currency-updated', pricesNew, currency) global.ipcRenderer.send('currency-updated', pricesNew, currency)
setCurrency(currency)
} }
const context = { const context = {
@ -107,8 +115,9 @@ export default function AppProvider({ children }) {
currency, currency,
needsConfig, needsConfig,
accentColor, accentColor,
toggleCurrencies: currency => toggleCurrencies(currency), error,
setBalances: () => setBalances() toggleCurrencies,
setBalances
} }
return <AppContext.Provider value={context}>{children}</AppContext.Provider> return <AppContext.Provider value={context}>{children}</AppContext.Provider>

View File

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