1
0
mirror of https://github.com/kremalicious/blowfish.git synced 2024-11-22 17:50:11 +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=

1
.gitignore vendored
View File

@ -6,3 +6,4 @@ 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,10 +24,19 @@ const withSvgr = (nextConfig = {}, nextComposePlugins = {}) => {
}) })
} }
module.exports = withSvgr({ const withElectron = (nextConfig = {}) => {
return Object.assign({}, nextConfig, {
webpack: config => { webpack: config => {
config.target = 'electron-renderer' config.target = 'electron-renderer'
return config return config
}
})
}
module.exports = withSvgr(
withElectron({
env: {
ETHERSCAN_API_KEY: process.env.ETHERSCAN_API_KEY
}, },
exportPathMap() { exportPathMap() {
return { return {
@ -34,4 +44,5 @@ module.exports = withSvgr({
'/preferences': { page: '/preferences' } '/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 (
<PriceProvider>
<AppProvider>
<Layout> <Layout>
<Component {...pageProps} /> <Component {...pageProps} />
</Layout> </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
if (process.env.NODE_ENV !== 'test') {
global.ipcRenderer.on('accent-color', (evt, accentColor) => { global.ipcRenderer.on('accent-color', (evt, accentColor) => {
setAccentColor(accentColor) setAccentColor(accentColor)
}) })
}
}, []) }, [])
useEffect(() => { useEffect(() => {
async function init() { async function init() {
try {
await setBalances() await setBalances()
setIsLoading(false) 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() {
try {
const { newPrices, newPriceChanges } = await fetchAndSetPrices() const { newPrices, newPriceChanges } = await fetchAndSetPrices()
setPrices(newPrices) setPrices(newPrices)
setPriceChanges(newPriceChanges) setPriceChanges(newPriceChanges)
global.ipcRenderer.send('prices-updated', Array.from(newPrices)) // convert Map to array, ipc messages seem to kill it 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