diff --git a/src/App.js b/src/App.js
index 7291b22..ea66391 100644
--- a/src/App.js
+++ b/src/App.js
@@ -3,7 +3,7 @@
*/
// Global npm libraries
-import React from 'react'
+import React, { useState, useEffect } from 'react'
import { useQueryParam, StringParam } from 'use-query-params'
// Local libraries
@@ -16,9 +16,6 @@ import Footer from './components/footer'
import NavMenu from './components/nav-menu'
import AppBody from './components/app-body'
-// Default restURL for a back-end server.
-let serverUrl = 'https://free-bch.fullstack.cash'
-
// Default alternative servers.
const defaultServerOptions = [
{ value: 'https://free-bch.fullstack.cash', label: 'https://free-bch.fullstack.cash' },
@@ -27,157 +24,176 @@ const defaultServerOptions = [
{ value: 'https://wa-usa-bch-consumer.fullstackcash.nl', label: 'https://wa-usa-bch-consumer.fullstackcash.nl' }
]
-let _this
+// let _this
-class App extends React.Component {
- constructor (props) {
- super(props)
+function App (props) {
+ // BEGIN STATE
- // Encasulate dependencies
- this.asyncLoad = new AsyncLoad()
+ // Get the CashStack URL from a query parameter, if it exists.
+ let [restURL] = useQueryParam('restURL', StringParam)
+ // Otherwise default to free-bch.fullstack.cash
+ if (!restURL) restURL = 'https://free-bch.fullstack.cash'
+ // console.log('restURL: ', restURL)
+ const [serverUrl, setServerUrl] = useState(restURL)
- this.state = {
- wallet: false, // BCH wallet instance
- menuState: 0, // The current View being displayed in the app
- serverUrl, // Stores the URL for the currently selected server.
- servers: defaultServerOptions, // A list of back end servers.
+ const [menuState, setMenuState] = useState(0)
+ const [wallet, setWallet] = useState(false)
+ const [servers, setServers] = useState(defaultServerOptions)
- // Startup Modal
- showStartModal: true, // Should the startup modal be visible?
- asyncInitFinished: false, // Did startup finish?
- asyncInitSucceeded: null, // Did startup finish successfully?
- modalBody: [], // Strings displayed in the modal
- hideSpinner: false, // Spinner gif in modal
- denyClose: false
- }
+ // Startup state
+ // When the page is loaded, it goes through a series of async network calls
+ // to initialize the app. These variables track the state of this startup
+ // process.
+ const [asyncInitStarted, setAsyncInitStarted] = useState(false)
+ const [asyncInitFinished, setAsyncInitFinished] = useState(false)
+ const [asyncInitSucceeded, setAsyncInitSucceeded] = useState(null)
- this.cnt = 0
+ // Startup Modal
+ const [showStartModal, setShowStartModal] = useState(true)
+ const [modalBody, setModalBody] = useState([])
+ const [hideSpinner, setHideSpinner] = useState(false)
+ const [denyClose, setDenyClose] = useState(false)
- _this = this
+ // Load all the app state into a single object that can be passed to child
+ // components.
+ const appData = {
+ wallet,
+ setWallet,
+ serverUrl,
+ setServerUrl,
+ servers,
+ setServers,
+ showStartModal,
+ setShowStartModal,
+ menuState,
+ setMenuState,
+ asyncInitFinished,
+ setAsyncInitFinished,
+ asyncInitSucceeded,
+ setAsyncInitSucceeded,
+ modalBody,
+ setModalBody,
+ hideSpinner,
+ setHideSpinner,
+ denyClose,
+ setDenyClose
}
- async componentDidMount () {
- try {
- this.addToModal('Loading minimal-slp-wallet')
+ // END STATE
- this.setState({
- denyClose: true
- })
+ // Encasulate dependencies
+ const asyncLoad = new AsyncLoad()
- await this.asyncLoad.loadWalletLib()
+ useEffect(() => {
+ async function asyncEffect () {
+ // console.log('asyncInitStarted: ', asyncInitStarted)
+ if (!asyncInitStarted) {
+ try {
+ setAsyncInitStarted(true)
- this.addToModal('Getting alternative servers')
- const servers = await this.asyncLoad.getServers()
- // console.log('servers: ', servers)
+ addToModal('Loading minimal-slp-wallet', appData)
- this.addToModal('Initializing wallet')
- // console.log(`Initializing wallet with back end server ${serverUrl}`)
+ setDenyClose(true)
- const wallet = await this.asyncLoad.initWallet(serverUrl)
+ await asyncLoad.loadWalletLib()
+ // console.log('Wallet: ', Wallet)
- this.setState({
- wallet,
- serverUrl,
- // queryParamExists,
- servers,
- showStartModal: false,
- asyncInitFinished: true,
- asyncInitSucceeded: true,
- denyClose: false
- })
- } catch (err) {
- this.modalBody = [
- `Error: ${err.message}`,
- 'Try selecting a different back end server using the drop-down menu at the bottom of the app.'
- ]
+ addToModal('Getting alternative servers', appData)
+ const gistServers = await asyncLoad.getServers()
+ setServers(gistServers)
+ // console.log('servers: ', servers)
- this.setState({
- modalBody: this.modalBody,
- hideSpinner: true,
- showStartModal: true,
- asyncInitFinished: true,
- asyncInitSucceeded: false,
- denyClose: false
- })
- }
- }
+ // throw new Error('test error')
- render () {
- // console.log('App component rendered. this.state.wallet: ', this.state.wallet)
- // console.log(`App component menuState: ${this.state.menuState}`)
- // console.log(`render() this.state.serverUrl: ${this.state.serverUrl}`)
+ addToModal('Initializing wallet', appData)
+ console.log(`Initializing wallet with back end server ${serverUrl}`)
- // This is a macro object that is passed to all child components. It gathers
- // all the data and handlers used throughout the app.
- const appData = {
- servers: this.state.servers, // Alternative back end servers
- wallet: this.state.wallet
- }
+ const walletTemp = await asyncLoad.initWallet(serverUrl)
+ setWallet(walletTemp)
- return (
- <>
-
-
-
+ // Update state
+ setShowStartModal(false)
+ setDenyClose(false)
- {
- this.state.showStartModal
- ?
- :
+ // Update the startup state.
+ setAsyncInitFinished(true)
+ setAsyncInitSucceeded(true)
+ console.log('App.js useEffect() startup finished successfully')
+ } catch (err) {
+ const errModalBody = [
+ `Error: ${err.message}`,
+ 'Try selecting a different back end server using the drop-down menu at the bottom of the app.'
+ ]
+ setModalBody(errModalBody)
+
+ // Update Modal State
+ setHideSpinner(true)
+ setShowStartModal(true)
+ setDenyClose(false)
+
+ // Update the startup state.
+ setAsyncInitFinished(true)
+ setAsyncInitSucceeded(false)
}
+ }
+ }
+ asyncEffect()
+ })
-
-
- >
- )
- }
+ return (
+ <>
+
+
- // Add a new line to the waiting modal.
- addToModal (inStr) {
- const modalBody = this.state.modalBody
+ {
+ showStartModal
+ ? ()
+ : ()
+ }
- modalBody.push(inStr)
+
+
+ >
+ )
+}
- this.setState({
- modalBody
- })
- }
+// Add a new line to the waiting modal.
+function addToModal (inStr, appData) {
+ // console.log('addToModal() inStr: ', inStr)
- // This handler is passed into the child menu component. When an item in the
- // nav menu is clicked, this handler will update the state. The state is
- // used by the AppBody component to determine which View component to display.
- onMenuClick (menuState) {
- // console.log('menuState: ', menuState)
+ appData.setModalBody(prevBody => {
+ // console.log('prevBody: ', prevBody)
+ prevBody.push(inStr)
+ return prevBody
+ })
+}
- _this.setState({
- menuState
- })
- }
+// This handler is passed into the child menu component. When an item in the
+// nav menu is clicked, this handler will update the state. The state is
+// used by the AppBody component to determine which View component to display.
+function onMenuClick (menuState, appData) {
+ // console.log('onMenuClick() menuState: ', menuState)
+
+ appData.setMenuState(menuState)
}
// This is rendered *before* the BCH wallet is initialized.
-function UninitializedView (props) {
+function UninitializedView (props = {}) {
// console.log('UninitializedView props: ', props)
- const heading = 'Loading Blockchain Data...'
+ const heading = 'Connecting to BCH blockchain...'
return (
<>
-
{
- _this.state.asyncInitFinished
- ?
+ props.asyncInitFinished
+ ?
: null
}
>
@@ -186,28 +202,12 @@ function UninitializedView (props) {
// This is rendered *after* the BCH wallet is initialized.
function InitializedView (props) {
- // console.log(`InitializedView props.menuState: ${props.menuState}`)
- // console.log(`InitializedView _this.state.menuState: ${_this.state.menuState}`)
-
return (
<>
-
+
>
)
}
-// Get the restURL query parameter.
-function GetRestUrl (props) {
- const [restURL] = useQueryParam('restURL', StringParam)
- // console.log('restURL: ', restURL)
-
- if (restURL) {
- serverUrl = restURL
- // queryParamExists = true
- }
-
- return (<>>)
-}
-
export default App
diff --git a/src/components/app-body/index.js b/src/components/app-body/index.js
index f04691b..ed4a35d 100644
--- a/src/components/app-body/index.js
+++ b/src/components/app-body/index.js
@@ -15,50 +15,36 @@ import Placeholder2 from '../placeholder2'
import Placeholder3 from '../placeholder3'
import ServerSelectView from '../servers/select-server-view'
-// let _this
+function AppBody (props) {
+ // Dependency injection through props
+ const appData = props.appData
+ const menuState = props.menuState
+ // console.log('AppBody() menuState: ', menuState)
-class AppBody extends React.Component {
- constructor (props) {
- super(props)
-
- this.state = {
- activeView: 0,
- menuState: props.menuState,
- wallet: props.wallet,
- appData: props.appData
- }
-
- // _this = this
- }
-
- render () {
- // console.log(`AppBody menu state: ${this.props.menuState}`)
-
- return (
- <>
- {this.chooseView(this.props.menuState)}
- >
- )
- }
-
- chooseView (menuState) {
+ function chooseView (menuState) {
// console.log(`chooseView() menuState: ${menuState}`)
switch (menuState) {
case 0:
- return ()
+ return ()
case 1:
return ()
case 2:
return ()
- // Special Views
+ // Special Views
case 100:
- return ()
+ return ()
default:
- return ()
+ return ()
}
}
+
+ return (
+ <>
+ {chooseView(menuState)}
+ >
+ )
}
export default AppBody
diff --git a/src/components/nav-menu/index.js b/src/components/nav-menu/index.js
index 84647e8..4844975 100644
--- a/src/components/nav-menu/index.js
+++ b/src/components/nav-menu/index.js
@@ -10,43 +10,31 @@ import React from 'react'
import { Nav, Navbar, Image } from 'react-bootstrap'
import Logo from './psf-logo.png'
-class NavMenu extends React.Component {
- constructor (props) {
- super(props)
-
- // This is the event handler passed in from the Parent component.
- this.parentMenuHandler = props.menuHandler
- }
-
- render () {
- // console.log(`selectedMenuItem: ${this.state.selectedMenuItem}`)
- // this.handleMenuClick(this.state.selectedMenuItem)
-
- return (
- <>
-
-
- {' '}
- PSF Web3 Demo
-
-
-
-
-
-
-
- >
- )
- }
-
- handleClickEvent (menuItem) {
+function NavMenu (props) {
+ const handleClickEvent = (menuItem) => {
// Pass the selected menu item up to the parent component.
- this.parentMenuHandler(menuItem)
+ props.menuHandler(menuItem, props.appData)
}
+
+ return (
+ <>
+
+
+ {' '}
+ PSF Web3 Demo
+
+
+
+
+
+
+
+ >
+ )
}
export default NavMenu
diff --git a/src/components/nav-menu/old-index.js b/src/components/nav-menu/old-index.js
new file mode 100644
index 0000000..84647e8
--- /dev/null
+++ b/src/components/nav-menu/old-index.js
@@ -0,0 +1,52 @@
+/*
+ This component controlls the navigation menu.
+
+ Inspired from this example:
+ https://codesandbox.io/s/react-bootstrap-hamburger-menu-example-rnud4?from-embed
+*/
+
+// Global npm libraries
+import React from 'react'
+import { Nav, Navbar, Image } from 'react-bootstrap'
+import Logo from './psf-logo.png'
+
+class NavMenu extends React.Component {
+ constructor (props) {
+ super(props)
+
+ // This is the event handler passed in from the Parent component.
+ this.parentMenuHandler = props.menuHandler
+ }
+
+ render () {
+ // console.log(`selectedMenuItem: ${this.state.selectedMenuItem}`)
+ // this.handleMenuClick(this.state.selectedMenuItem)
+
+ return (
+ <>
+
+
+ {' '}
+ PSF Web3 Demo
+
+
+
+
+
+
+
+ >
+ )
+ }
+
+ handleClickEvent (menuItem) {
+ // Pass the selected menu item up to the parent component.
+ this.parentMenuHandler(menuItem)
+ }
+}
+
+export default NavMenu
diff --git a/src/components/servers/select-server-button.js b/src/components/servers/select-server-button.js
index cc860a0..b9601e3 100644
--- a/src/components/servers/select-server-button.js
+++ b/src/components/servers/select-server-button.js
@@ -10,17 +10,18 @@ import { Container, Row, Col, Button } from 'react-bootstrap'
// Local libraries
// import GistServers from '../../services/gist-servers'
-let _this
+// let _this
class ServerSelect extends React.Component {
constructor (props) {
super(props)
this.state = {
- menuHandler: props.menuHandler
+ menuHandler: props.menuHandler,
+ appData: props.appData
}
- _this = this
+ this.handleServerSelect = this.handleServerSelect.bind(this)
}
render () {
@@ -46,7 +47,7 @@ class ServerSelect extends React.Component {
handleServerSelect () {
console.log('This function should navigate to the server selection view.')
- _this.state.menuHandler(100)
+ this.state.menuHandler(100, this.state.appData)
}
}
diff --git a/src/components/waiting-modal/index.js b/src/components/waiting-modal/index.js
index 5c0a79b..6c172d7 100644
--- a/src/components/waiting-modal/index.js
+++ b/src/components/waiting-modal/index.js
@@ -45,6 +45,7 @@ function ModalTemplate (props) {
function BodyList (props) {
const items = props.body
+ // console.log('BodyList items: ', items)
const listItems = []
diff --git a/src/services/async-load.js b/src/services/async-load.js
index f42130a..dc0f6c0 100644
--- a/src/services/async-load.js
+++ b/src/services/async-load.js
@@ -17,6 +17,7 @@ class AsyncLoad {
// attached to the global 'window' object.
async loadWalletLib () {
do {
+ // console.log('window.SlpWallet: ', window.SlpWallet)
if (typeof window !== 'undefined' && window.SlpWallet) {
this.BchWallet = window.SlpWallet