fix: wizard loading order and zeroConf

This commit is contained in:
Taranto 2020-10-20 11:24:36 +01:00 committed by Josh Harvey
parent 0a491e0522
commit 97ffb7bdf1
17 changed files with 259 additions and 109 deletions

View file

@ -10855,7 +10855,8 @@
}, },
"kind-of": { "kind-of": {
"version": "6.0.2", "version": "6.0.2",
"resolved": "", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
"dev": true "dev": true
} }
} }
@ -15026,7 +15027,8 @@
}, },
"kind-of": { "kind-of": {
"version": "6.0.2", "version": "6.0.2",
"resolved": "", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
"dev": true "dev": true
} }
} }
@ -19209,8 +19211,30 @@
"integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==", "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==",
"requires": { "requires": {
"define-properties": "^1.1.3", "define-properties": "^1.1.3",
"es-abstract": "^1.18.0-next.0",
"has-symbols": "^1.0.1", "has-symbols": "^1.0.1",
"object-keys": "^1.1.1" "object-keys": "^1.1.1"
},
"dependencies": {
"es-abstract": {
"version": "1.18.0-next.1",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
"integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
"requires": {
"es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.1",
"is-callable": "^1.2.2",
"is-negative-zero": "^2.0.0",
"is-regex": "^1.1.1",
"object-inspect": "^1.8.0",
"object-keys": "^1.1.1",
"object.assign": "^4.1.1",
"string.prototype.trimend": "^1.0.1",
"string.prototype.trimstart": "^1.0.1"
}
}
} }
} }
} }
@ -27804,7 +27828,8 @@
}, },
"kind-of": { "kind-of": {
"version": "6.0.2", "version": "6.0.2",
"resolved": "", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
"dev": true "dev": true
} }
} }

View file

@ -1,4 +1,3 @@
import { ApolloProvider, useQuery } from '@apollo/react-hooks'
import CssBaseline from '@material-ui/core/CssBaseline' import CssBaseline from '@material-ui/core/CssBaseline'
import { import {
StylesProvider, StylesProvider,
@ -6,15 +5,12 @@ import {
MuiThemeProvider, MuiThemeProvider,
makeStyles makeStyles
} from '@material-ui/core/styles' } from '@material-ui/core/styles'
import gql from 'graphql-tag'
import { create } from 'jss' import { create } from 'jss'
import extendJss from 'jss-plugin-extend' import extendJss from 'jss-plugin-extend'
import React from 'react' import React, { createContext, useState } from 'react'
import { BrowserRouter as Router } from 'react-router-dom' import { useLocation, BrowserRouter as Router } from 'react-router-dom'
import Wizard from 'src/pages/Wizard' import ApolloProvider from 'src/utils/apollo'
import { getWizardStep } from 'src/pages/Wizard/helper'
import client from 'src/utils/apollo'
import Header from './components/layout/Header' import Header from './components/layout/Header'
import { tree, Routes } from './routing/routes' import { tree, Routes } from './routing/routes'
@ -53,51 +49,42 @@ const useStyles = makeStyles({
} }
}) })
const GET_DATA = gql` const AppContext = createContext()
query getData {
config
accounts
cryptoCurrencies {
code
display
}
}
`
const Main = () => { const Main = () => {
const classes = useStyles() const classes = useStyles()
const { data, loading } = useQuery(GET_DATA) const location = useLocation()
if (loading) { const is404 = location.pathname === '/404'
return <></>
}
const wizardStep = getWizardStep(data?.config, data?.cryptoCurrencies)
return ( return (
<div className={classes.root}> <div className={classes.root}>
<Router> {!is404 && <Header tree={tree} />}
{wizardStep > 0 && <Wizard wizardStep={wizardStep} />} <main className={classes.wrapper}>
<Header tree={tree} /> <Routes />
<main className={classes.wrapper}> </main>
<Routes />
</main>
</Router>
</div> </div>
) )
} }
const App = () => { const App = () => {
const [wizardTested, setWizardTested] = useState(false)
return ( return (
<ApolloProvider client={client}> <AppContext.Provider value={{ wizardTested, setWizardTested }}>
<StylesProvider jss={jss}> <Router>
<MuiThemeProvider theme={theme}> <ApolloProvider>
<CssBaseline /> <StylesProvider jss={jss}>
<Main /> <MuiThemeProvider theme={theme}>
</MuiThemeProvider> <CssBaseline />
</StylesProvider> <Main />
</ApolloProvider> </MuiThemeProvider>
</StylesProvider>
</ApolloProvider>
</Router>
</AppContext.Provider>
) )
} }
export default App export default App
export { AppContext }

View file

@ -87,6 +87,9 @@ const validationSchema = Yup.object().shape({
const MachineNameComponent = ({ nextStep, classes, setQrCode, setName }) => { const MachineNameComponent = ({ nextStep, classes, setQrCode, setName }) => {
const [register] = useMutation(SAVE_CONFIG, { const [register] = useMutation(SAVE_CONFIG, {
onCompleted: ({ createPairingTotem }) => { onCompleted: ({ createPairingTotem }) => {
if (process.env.NODE_ENV === 'development') {
console.log(`totem: "${createPairingTotem}" `)
}
setQrCode(createPairingTotem) setQrCode(createPairingTotem)
nextStep() nextStep()
}, },

View file

@ -20,7 +20,7 @@ const AuthRegister = () => {
customHandler: (err, res) => { customHandler: (err, res) => {
if (err) return if (err) return
if (res) { if (res) {
history.push('/') history.push('/wizard', { fromAuthRegister: true })
} }
} }
}) })

View file

@ -52,7 +52,6 @@ const getOverridesFields = (getData, currency, auxElements) => {
) )
const suggestionFilter = (it, cryptoData) => { const suggestionFilter = (it, cryptoData) => {
console.log(it)
if (!it?.machine) return cryptoData if (!it?.machine) return cryptoData
return R.differenceWith( return R.differenceWith(

View file

@ -33,7 +33,6 @@ const getCashOutStatus = it => {
} }
const getCashInStatus = it => { const getCashInStatus = it => {
console.log(it)
if (it.operatorCompleted) return 'Cancelled' if (it.operatorCompleted) return 'Cancelled'
if (it.hasError) return 'Error' if (it.hasError) return 'Error'
if (it.sendConfirmed) return 'Sent' if (it.sendConfirmed) return 'Sent'

View file

@ -66,7 +66,7 @@ const WizardSplash = ({ code, name, onContinue }) => {
<P className={classes.text}> <P className={classes.text}>
You are about to enable {name} on your system. This will allow you to You are about to enable {name} on your system. This will allow you to
use this cryptocurrency on your machines. To be able to do that, youll use this cryptocurrency on your machines. To be able to do that, youll
have to setup all the necessary 3rd party services. have to set up all the necessary 3rd party services.
</P> </P>
<Button className={classes.button} onClick={onContinue}> <Button className={classes.button} onClick={onContinue}>
Start configuration Start configuration

View file

@ -1,11 +1,15 @@
import { useQuery } from '@apollo/react-hooks'
import { makeStyles, Dialog, DialogContent } from '@material-ui/core' import { makeStyles, Dialog, DialogContent } from '@material-ui/core'
import classnames from 'classnames' import classnames from 'classnames'
import React, { useState } from 'react' import gql from 'graphql-tag'
import React, { useState, useContext } from 'react'
import { useHistory } from 'react-router-dom'
import { AppContext } from 'src/App'
import { getWizardStep, STEPS } from 'src/pages/Wizard/helper'
import { backgroundColor } from 'src/styling/variables' import { backgroundColor } from 'src/styling/variables'
import Footer from './components/Footer' import Footer from './components/Footer'
import { STEPS } from './helper'
const useStyles = makeStyles({ const useStyles = makeStyles({
wrapper: { wrapper: {
@ -26,12 +30,42 @@ const useStyles = makeStyles({
} }
}) })
const Wizard = ({ wizardStep }) => { const GET_DATA = gql`
const [step, setStep] = useState(0) query getData {
config
accounts
cryptoCurrencies {
code
display
}
}
`
const Wizard = ({ fromAuthRegister }) => {
const classes = useStyles() const classes = useStyles()
const { data, loading } = useQuery(GET_DATA)
const history = useHistory()
const { setWizardTested } = useContext(AppContext)
const [step, setStep] = useState(0)
const [open, setOpen] = useState(true) const [open, setOpen] = useState(true)
const [footerExp, setFooterExp] = useState(false) const [footerExp, setFooterExp] = useState(false)
if (loading) {
return <></>
}
const wizardStep = getWizardStep(data?.config, data?.cryptoCurrencies)
const shouldGoBack =
history.length && !history.location.state?.fromAuthRegister
if (wizardStep === 0) {
setWizardTested(true)
shouldGoBack ? history.goBack() : history.push('/')
}
const isWelcome = step === 0 const isWelcome = step === 0
const classNames = { const classNames = {
[classes.blurred]: footerExp, [classes.blurred]: footerExp,
@ -44,13 +78,17 @@ const Wizard = ({ wizardStep }) => {
} }
const doContinue = () => { const doContinue = () => {
if (step >= STEPS.length - 1) return setOpen(false) if (step >= STEPS.length - 1) {
setOpen(false)
history.push('/')
}
const nextStep = step === 0 && wizardStep ? wizardStep : step + 1 const nextStep = step === 0 && wizardStep ? wizardStep : step + 1
setFooterExp(true) setFooterExp(true)
setStep(nextStep) setStep(nextStep)
} }
const current = STEPS[step] const current = STEPS[step]
return ( return (

View file

@ -1,6 +1,7 @@
import { useQuery, useMutation } from '@apollo/react-hooks' import { useQuery, useMutation } from '@apollo/react-hooks'
import { makeStyles } from '@material-ui/core' import { makeStyles } from '@material-ui/core'
import gql from 'graphql-tag' import gql from 'graphql-tag'
import * as R from 'ramda'
import React from 'react' import React from 'react'
import { Table as EditableTable } from 'src/components/editableTable' import { Table as EditableTable } from 'src/components/editableTable'
@ -14,6 +15,8 @@ import {
} from 'src/pages/Locales/helper' } from 'src/pages/Locales/helper'
import { toNamespace } from 'src/utils/config' import { toNamespace } from 'src/utils/config'
import { getConfiguredCoins } from '../helper'
const useStyles = makeStyles(styles) const useStyles = makeStyles(styles)
const GET_DATA = gql` const GET_DATA = gql`
@ -62,6 +65,11 @@ function Locales({ isActive, doContinue }) {
return saveConfig({ variables: { config } }) return saveConfig({ variables: { config } })
} }
const cryptoCurrencies = getConfiguredCoins(
data?.config || {},
data?.cryptoCurrencies || []
)
return ( return (
<div className={classes.wrapper}> <div className={classes.wrapper}>
<TitleSection title="Locales" /> <TitleSection title="Locales" />
@ -77,7 +85,7 @@ function Locales({ isActive, doContinue }) {
save={save} save={save}
validationSchema={schema} validationSchema={schema}
data={[]} data={[]}
elements={mainFields(data)} elements={mainFields(R.merge(data, { cryptoCurrencies }))}
/> />
</Section> </Section>
</div> </div>

View file

@ -101,7 +101,7 @@ const Mailgun = () => {
<div className={classes.infoMessage}> <div className={classes.infoMessage}>
<WarningIcon /> <WarningIcon />
<Info3> <Info3>
To get email notifications, youll need to setup Mailgun. Check out To get email notifications, youll need to set up Mailgun. Check out
our article on how to set it up. our article on how to set it up.
</Info3> </Info3>
</div> </div>

View file

@ -39,7 +39,6 @@ const SAVE_CONFIG = gql`
const AllSet = ({ data: currentData, doContinue }) => { const AllSet = ({ data: currentData, doContinue }) => {
const classes = useStyles() const classes = useStyles()
console.log(currentData)
const { data } = useQuery(GET_INFO) const { data } = useQuery(GET_INFO)
const [saveConfig] = useMutation(SAVE_CONFIG, { const [saveConfig] = useMutation(SAVE_CONFIG, {

View file

@ -36,6 +36,11 @@ const ChooseCoin = ({ addData }) => {
const onSubmit = it => { const onSubmit = it => {
if (!schema.isValidSync(it)) return setError(true) if (!schema.isValidSync(it)) return setError(true)
if (it.coin !== 'BTC') {
return addData({ coin: it.coin, zeroConf: 'all-zero-conf' })
}
addData(it) addData(it)
} }

View file

@ -95,13 +95,28 @@ const ChooseWallet = ({ data: currentData, addData }) => {
onChange={onSelect} onChange={onSelect}
/> />
{isLocalHosted(selected) && ( {isLocalHosted(selected) && (
<div className={classes.infoMessage}> <>
<WarningIcon /> <div className={classes.infoMessage}>
<Info3> <WarningIcon />
To setup {selected} please read our instructions from our support <Info3>
article. To set up {selected} please read the node wallet instructions from
</Info3> our support portal.
</div> </Info3>
</div>
<a
className={classes.actionButtonLink}
target="_blank"
rel="noopener noreferrer"
href="https://support.lamassu.is/hc/en-us/articles/115001209552-Setting-up-your-node-wallets">
<ActionButton
className={classes.actionButton}
color="primary"
Icon={LinkIcon}
InverseIcon={InverseLinkIcon}>
Support article
</ActionButton>
</a>
</>
)} )}
{!isConfigurable(selected) && ( {!isConfigurable(selected) && (
<Button size="lg" onClick={submit} className={classes.button}> <Button size="lg" onClick={submit} className={classes.button}>

View file

@ -14,6 +14,13 @@ import Twilio from './components/Twilio'
import Wallet from './components/Wallet/Wallet' import Wallet from './components/Wallet/Wallet'
import Welcome from './components/Welcome' import Welcome from './components/Welcome'
const getConfiguredCoins = (config, crypto) => {
const wallet = fromNamespace(namespaces.WALLETS, config)
return R.filter(it =>
WalletSchema.isValidSync(fromNamespace(it.code, wallet))
)(crypto)
}
const hasValidWallet = (config, crypto) => { const hasValidWallet = (config, crypto) => {
const wallet = fromNamespace(namespaces.WALLETS, config) const wallet = fromNamespace(namespaces.WALLETS, config)
const coins = R.map(it => fromNamespace(it.code, wallet))(crypto) const coins = R.map(it => fromNamespace(it.code, wallet))(crypto)
@ -61,9 +68,9 @@ const STEPS = [
Component: Wallet, Component: Wallet,
exImage: '/assets/wizard/fullexample.wallet.png', exImage: '/assets/wizard/fullexample.wallet.png',
subtitle: 'Wallet settings', subtitle: 'Wallet settings',
text: `Your wallet settings are the first step for this wizard. We'll start text: `Your wallet settings are the first step for this wizard.
by setting one of cryptocurrency to get you up and running, but you We'll start by setting up one of cryptocurrencies to get you up and running,
can later setup as many cryptocurrencies as you want.` but you can later set up as many as you want.`
}, },
{ {
id: 'locale', id: 'locale',
@ -125,4 +132,4 @@ const STEPS = [
// } // }
] ]
export { getWizardStep, STEPS } export { getWizardStep, STEPS, getConfiguredCoins }

View file

@ -0,0 +1,27 @@
import React from 'react'
import { Route, Redirect } from 'react-router-dom'
const isAuthenticated = () => {
return localStorage.getItem('loggedIn')
}
const PrivateRoute = ({ children, ...rest }) => {
return (
<Route
{...rest}
render={({ location }) =>
isAuthenticated() ? (
children
) : (
<Redirect
to={{
pathname: '/login'
}}
/>
)
}
/>
)
}
export default PrivateRoute

View file

@ -1,7 +1,14 @@
import * as R from 'ramda' import * as R from 'ramda'
import React from 'react' import React, { useContext } from 'react'
import { Route, Redirect, Switch } from 'react-router-dom' import {
Route,
Redirect,
Switch,
useHistory,
useLocation
} from 'react-router-dom'
import { AppContext } from 'src/App'
import AuthRegister from 'src/pages/AuthRegister' import AuthRegister from 'src/pages/AuthRegister'
import Cashout from 'src/pages/Cashout' import Cashout from 'src/pages/Cashout'
import Commissions from 'src/pages/Commissions' import Commissions from 'src/pages/Commissions'
@ -154,17 +161,34 @@ const leafRoutes = R.compose(R.flatten, map)(tree)
const parentRoutes = R.filter(R.has('children'))(tree) const parentRoutes = R.filter(R.has('children'))(tree)
const flattened = R.concat(leafRoutes, parentRoutes) const flattened = R.concat(leafRoutes, parentRoutes)
const Routes = () => ( const Routes = () => {
<Switch> const history = useHistory()
<Route exact path="/" /> const location = useLocation()
<Route path="/register" component={AuthRegister} /> const { wizardTested } = useContext(AppContext)
<Route path="/wizard" component={Wizard}></Route>
{flattened.map(({ route, component: Page, key }) => (
<Route path={route} key={key}>
<Page name={key} />
</Route>
))}
</Switch>
)
const dontTriggerPages = ['/404', '/register', '/wizard']
if (!wizardTested && !R.contains(location.pathname)(dontTriggerPages)) {
history.push('/wizard')
}
return (
<Switch>
<Route exact path="/">
<Redirect to={{ pathname: '/transactions' }} />
</Route>
<Route path="/wizard" component={Wizard} />
<Route path="/register" component={AuthRegister} />
{flattened.map(({ route, component: Page, key }) => (
<Route path={route} key={key}>
<Page name={key} />
</Route>
))}
<Route path="/404" />
<Route path="*">
<Redirect to={{ pathname: '/404' }} />
</Route>
</Switch>
)
}
export { tree, Routes } export { tree, Routes }

View file

@ -1,43 +1,57 @@
import { ApolloProvider } from '@apollo/react-hooks'
import { InMemoryCache } from 'apollo-cache-inmemory' import { InMemoryCache } from 'apollo-cache-inmemory'
import { ApolloClient } from 'apollo-client' import { ApolloClient } from 'apollo-client'
import { ApolloLink } from 'apollo-link' import { ApolloLink } from 'apollo-link'
import { onError } from 'apollo-link-error' import { onError } from 'apollo-link-error'
import { HttpLink } from 'apollo-link-http' import { HttpLink } from 'apollo-link-http'
import React from 'react'
import { useHistory, useLocation } from 'react-router-dom'
const URI = const URI =
process.env.NODE_ENV === 'development' ? 'https://localhost:8070' : '' process.env.NODE_ENV === 'development' ? 'https://localhost:8070' : ''
const client = new ApolloClient({ const getClient = (history, location) =>
link: ApolloLink.from([ new ApolloClient({
onError(({ graphQLErrors, networkError }) => { link: ApolloLink.from([
if (graphQLErrors) onError(({ graphQLErrors, networkError }) => {
graphQLErrors.forEach(({ message, locations, path }) => if (graphQLErrors)
console.log( graphQLErrors.forEach(({ message, locations, path, extensions }) => {
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}` if (extensions?.code === 'UNAUTHENTICATED') {
) if (location.pathname !== '/404') history.push('/404')
) }
if (networkError) console.log(`[Network error]: ${networkError}`) console.log(
}), `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
new HttpLink({ )
credentials: 'include', })
uri: `${URI}/graphql` if (networkError) console.log(`[Network error]: ${networkError}`)
}) }),
]), new HttpLink({
cache: new InMemoryCache(), credentials: 'include',
defaultOptions: { uri: `${URI}/graphql`
watchQuery: { })
fetchPolicy: 'no-cache', ]),
errorPolicy: 'ignore' cache: new InMemoryCache(),
}, defaultOptions: {
query: { watchQuery: {
fetchPolicy: 'no-cache', fetchPolicy: 'no-cache',
errorPolicy: 'all' errorPolicy: 'ignore'
}, },
mutate: { query: {
errorPolicy: 'all' fetchPolicy: 'no-cache',
errorPolicy: 'all'
},
mutate: {
errorPolicy: 'all'
}
} }
} })
})
export default client const Provider = ({ children }) => {
const history = useHistory()
const location = useLocation()
const client = getClient(history, location)
return <ApolloProvider client={client}>{children}</ApolloProvider>
}
export default Provider
export { URI } export { URI }