mirror of
https://github.com/Permissionless-Software-Foundation/bch-dex-taker-v2.git
synced 2026-09-21 16:52:01 -07:00
Merge pull request #2 from Permissionless-Software-Foundation/ct-unstable
Nav Menu used to bring up different Views
This commit is contained in:
@@ -10,8 +10,10 @@ This app can be compiled and uploaded to Filecoin via [web3.storage](https://web
|
||||
- [react-bootstrap](https://react-bootstrap.github.io/) is used for general style and layout control.
|
||||
- An easily customized *waiting modal* component can be invoked while waiting for network calls to complete.
|
||||
- [minimal-slp-wallet](https://www.npmjs.com/package/minimal-slp-wallet) is used to access tokens and BCH on the Bitcoin Cash blockchain.
|
||||
- A 'server selection' dropdown allows the user to select from an array of redundant back end servers.
|
||||
- A 'server selection' dropdown allows the user to select from an array of redundant [web3 back end servers](https://cashstack.info).
|
||||
- This site is statically compiled, uploaded to Filecoin, and served over IPFS for censorship resistance and version control.
|
||||
- A collapsible navigation menu is used to load different views.
|
||||
- This app can be compiled into an native Android app using [react-bootstrap-web3-android](https://github.com/Permissionless-Software-Foundation/react-bootstrap-web3-android).
|
||||
|
||||
## Installation
|
||||
```bash
|
||||
|
||||
Generated
+2
-2
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "nft-browser-v2",
|
||||
"name": "react-bootstrap-web3-spa",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "nft-browser-v2",
|
||||
"name": "react-bootstrap-web3-spa",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"axios": "0.27.2",
|
||||
|
||||
+22
-32
@@ -10,13 +10,12 @@ import { useQueryParam, StringParam } from 'use-query-params'
|
||||
// Local libraries
|
||||
import './App.css'
|
||||
import LoadScripts from './components/load-scripts'
|
||||
// import NFTs from './components/nfts'
|
||||
import WaitingModal from './components/waiting-modal'
|
||||
import AsyncLoad from './services/async-load'
|
||||
import ServerSelect from './components/servers'
|
||||
import Footer from './components/footer'
|
||||
import GetBalance from './components/balance'
|
||||
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'
|
||||
@@ -32,13 +31,13 @@ class App extends React.Component {
|
||||
|
||||
// Working array for storing modal output.
|
||||
this.modalBody = []
|
||||
this.tokenData = {}
|
||||
|
||||
this.state = {
|
||||
walletInitialized: false,
|
||||
wallet: false,
|
||||
modalBody: this.modalBody,
|
||||
hideSpinner: false
|
||||
hideSpinner: false,
|
||||
menuState: 0
|
||||
}
|
||||
|
||||
this.cnt = 0
|
||||
@@ -56,27 +55,6 @@ class App extends React.Component {
|
||||
|
||||
const wallet = await this.asyncLoad.initWallet(serverURL)
|
||||
|
||||
// this.addToModal('Getting Group Token Information')
|
||||
|
||||
// Get Group Token info
|
||||
// const groupData = await this.asyncLoad.getGroupData(groupTokenId)
|
||||
// console.log(`groupData: ${JSON.stringify(groupData, null, 2)}`)
|
||||
|
||||
// this.addToModal('Getting NFT Information')
|
||||
|
||||
/// Get NFT child info
|
||||
// const nftData = []
|
||||
// for (let i = 0; i < groupData.nfts.length; i++) {
|
||||
// const tokenData = await this.asyncLoad.getTokenData(groupData.nfts[i])
|
||||
// nftData.push(tokenData)
|
||||
// }
|
||||
// console.log(`nft data: ${JSON.stringify(nftData, null, 2)}`)
|
||||
|
||||
// this.tokenData = {
|
||||
// groupData,
|
||||
// nftData
|
||||
// }
|
||||
|
||||
this.setState({
|
||||
wallet,
|
||||
walletInitialized: true
|
||||
@@ -96,13 +74,14 @@ class App extends React.Component {
|
||||
|
||||
render () {
|
||||
// console.log('App component rendered. this.state.wallet: ', this.state.wallet)
|
||||
// console.log(`App component menuState: ${this.state.menuState}`)
|
||||
|
||||
return (
|
||||
<>
|
||||
<GetRestUrl />
|
||||
<LoadScripts />
|
||||
<NavMenu />
|
||||
{this.state.walletInitialized ? <InitializedView wallet={this.state.wallet} tokens={this.tokenData} /> : <UninitializedView modalBody={this.state.modalBody} hideSpinner={this.state.hideSpinner} />}
|
||||
<NavMenu menuHandler={this.onMenuClick} />
|
||||
{this.state.walletInitialized ? <InitializedView wallet={this.state.wallet} menuState={this.state.menuState} /> : <UninitializedView modalBody={this.state.modalBody} hideSpinner={this.state.hideSpinner} />}
|
||||
<ServerSelect />
|
||||
<Footer />
|
||||
</>
|
||||
@@ -117,6 +96,17 @@ class App extends React.Component {
|
||||
modalBody: this.modalBody
|
||||
})
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
_this.setState({
|
||||
menuState
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// This is rendered *before* the BCH wallet is initialized.
|
||||
@@ -132,14 +122,18 @@ 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 (
|
||||
<>
|
||||
<br />
|
||||
<GetBalance wallet={_this.state.wallet} />
|
||||
<AppBody menuState={_this.state.menuState} wallet={props.wallet} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
// Get the restURL query parameter.
|
||||
function GetRestUrl (props) {
|
||||
const [restURL] = useQueryParam('restURL', StringParam)
|
||||
// console.log('restURL: ', restURL)
|
||||
@@ -149,8 +143,4 @@ function GetRestUrl (props) {
|
||||
return (<></>)
|
||||
}
|
||||
|
||||
// function sleep (ms) {
|
||||
// return new Promise(resolve => setTimeout(resolve, ms))
|
||||
// }
|
||||
|
||||
export default App
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
This Body component is a container for all the different Views of the app.
|
||||
Views are equivalent to 'pages' in a multi-page app. Views are hidden or
|
||||
displayed to simulate the use of pages in an SPA.
|
||||
The Body app contains all the Views and chooses which to show, based on
|
||||
the state of the Menu component.
|
||||
*/
|
||||
|
||||
// Global npm libraries
|
||||
import React from 'react'
|
||||
|
||||
// Local libraries
|
||||
import GetBalance from '../balance'
|
||||
import TakePicture from '../take-picture'
|
||||
|
||||
let _this
|
||||
|
||||
class AppBody extends React.Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
activeView: 0,
|
||||
menuState: props.menuState,
|
||||
wallet: props.wallet
|
||||
}
|
||||
|
||||
_this = this
|
||||
}
|
||||
|
||||
render () {
|
||||
// console.log(`AppBody menu state: ${this.props.menuState}`)
|
||||
|
||||
return (
|
||||
<>
|
||||
{this.chooseView(this.props.menuState)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
chooseView (menuState) {
|
||||
// console.log(`chooseView() menuState: ${menuState}`)
|
||||
|
||||
switch (menuState) {
|
||||
case 0:
|
||||
return (<GetBalance wallet={_this.state.wallet} />)
|
||||
case 1:
|
||||
return (<TakePicture />)
|
||||
default:
|
||||
return (<GetBalance wallet={_this.state.wallet} />)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default AppBody
|
||||
@@ -9,7 +9,7 @@ import { Container, Row, Col, Form, Button, Spinner } from 'react-bootstrap'
|
||||
let _this
|
||||
|
||||
class GetBalance extends React.Component {
|
||||
constructor(props) {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
@@ -31,10 +31,10 @@ class GetBalance extends React.Component {
|
||||
<Form>
|
||||
<Form.Group className='mb-3' controlId='formBasicEmail'>
|
||||
<Form.Label>Enter a BCH address to check the balance.</Form.Label>
|
||||
<Form.Control type='text' placeholder='bitcoincash:qqlrzp23w08434twmvr4fxw672whkjy0py26r63g3d' onChange={e => this.setState({textInput: e.target.value})} />
|
||||
<Form.Control type='text' placeholder='bitcoincash:qqlrzp23w08434twmvr4fxw672whkjy0py26r63g3d' onChange={e => this.setState({ textInput: e.target.value })} />
|
||||
</Form.Group>
|
||||
|
||||
<Button variant='primary' onClick={this.getBalance}>
|
||||
<Button variant='primary' onClick={this.handleGetBalance}>
|
||||
Check Balance
|
||||
</Button>
|
||||
</Form>
|
||||
@@ -51,13 +51,13 @@ class GetBalance extends React.Component {
|
||||
)
|
||||
}
|
||||
|
||||
async getBalance(event) {
|
||||
async handleGetBalance (event) {
|
||||
try {
|
||||
const textInput = _this.state.textInput
|
||||
|
||||
// Exit on invalid input
|
||||
if(!textInput) return
|
||||
if(!textInput.includes('bitcoincash:')) return
|
||||
if (!textInput) return
|
||||
if (!textInput.includes('bitcoincash:')) return
|
||||
|
||||
_this.setState({
|
||||
balance: (<span>Retrieving balance... <Spinner animation='border' /></span>)
|
||||
@@ -71,7 +71,7 @@ class GetBalance extends React.Component {
|
||||
_this.setState({
|
||||
balance: `Balance: ${balance} sats, ${bchBalance} BCH`
|
||||
})
|
||||
} catch(err) {
|
||||
} catch (err) {
|
||||
_this.setState({
|
||||
balance: `Error: ${err.message}`
|
||||
})
|
||||
|
||||
@@ -7,52 +7,53 @@
|
||||
|
||||
// Global npm libraries
|
||||
import React from 'react'
|
||||
import { Nav, Navbar, NavDropdown, Image } from "react-bootstrap"
|
||||
import { Nav, Navbar, Image } from 'react-bootstrap'
|
||||
import Logo from './psf-logo.png'
|
||||
// import { ReactComponent as Logo } from "./psf-logo.png"
|
||||
|
||||
class NavMenu extends React.Component {
|
||||
constructor(props) {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
console.log('NavMenu')
|
||||
|
||||
// This is the event handler passed in from the Parent component.
|
||||
this.parentMenuHandler = props.menuHandler
|
||||
|
||||
// this.state = {
|
||||
// selectedMenuItem: 0
|
||||
// }
|
||||
}
|
||||
|
||||
render() {
|
||||
return(
|
||||
render () {
|
||||
// console.log(`selectedMenuItem: ${this.state.selectedMenuItem}`)
|
||||
// this.handleMenuClick(this.state.selectedMenuItem)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Navbar collapseOnSelect expand="xxxl" bg="dark" variant="dark" style={{paddingRight: '20px'}} >
|
||||
<Navbar.Brand href="#home" style={{paddingLeft: '20px'}}>
|
||||
<Image src={Logo} thumbnail width="50" />{' '}
|
||||
<Navbar collapseOnSelect expand='xxxl' bg='dark' variant='dark' style={{ paddingRight: '20px' }}>
|
||||
<Navbar.Brand href='#home' style={{ paddingLeft: '20px' }}>
|
||||
<Image src={Logo} thumbnail width='50' />{' '}
|
||||
PSF Web3 Demo
|
||||
</Navbar.Brand>
|
||||
|
||||
<Navbar.Toggle aria-controls="responsive-navbar-nav" />
|
||||
<Navbar.Collapse id="responsive-navbar-nav">
|
||||
<Nav className="mr-auto">
|
||||
<Nav.Link href="#features">Placeholder 1</Nav.Link>
|
||||
<NavDropdown title="Dropdown" id="collasible-nav-dropdown">
|
||||
<NavDropdown.Item href="#action/3.1">Action</NavDropdown.Item>
|
||||
<NavDropdown.Item href="#action/3.2">
|
||||
Another action
|
||||
</NavDropdown.Item>
|
||||
<NavDropdown.Item href="#action/3.3">Something</NavDropdown.Item>
|
||||
<NavDropdown.Divider />
|
||||
<NavDropdown.Item href="#action/3.4">
|
||||
Separated link
|
||||
</NavDropdown.Item>
|
||||
</NavDropdown>
|
||||
</Nav>
|
||||
<Nav>
|
||||
<Nav.Link href="#deets">Placeholder 2</Nav.Link>
|
||||
<Nav.Link eventKey={2} href="#memes">
|
||||
Placeholder 3
|
||||
</Nav.Link>
|
||||
<Navbar.Toggle aria-controls='responsive-navbar-nav' />
|
||||
<Navbar.Collapse id='responsive-navbar-nav'>
|
||||
<Nav className='mr-auto'>
|
||||
<Nav.Link href='#' onClick={() => this.handleClickEvent(0)}>Check Balance</Nav.Link>
|
||||
<Nav.Link href='#' onClick={() => this.handleClickEvent(1)}>Take Picture</Nav.Link>
|
||||
<Nav.Link href='#'>Placeholder 3</Nav.Link>
|
||||
</Nav>
|
||||
</Navbar.Collapse>
|
||||
</Navbar>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
handleClickEvent (menuItem) {
|
||||
// Update the
|
||||
// this.setState({selectedMenuItem: menuItem})
|
||||
|
||||
// Pass the selected menu item up to the parent component.
|
||||
this.parentMenuHandler(menuItem)
|
||||
}
|
||||
}
|
||||
|
||||
export default NavMenu
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
This is a View component that allows the user to take a picture with their
|
||||
webcam or phone.
|
||||
*/
|
||||
|
||||
// Global npm libraries
|
||||
import React from 'react'
|
||||
|
||||
class TakePicture extends React.Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
console.log('Take Picture component loaded')
|
||||
|
||||
// this.state = {
|
||||
// activeView: 0,
|
||||
// menuState: props.menuState
|
||||
// }
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<>
|
||||
<p>Take Picture Component Placeholder</p>
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default TakePicture
|
||||
Reference in New Issue
Block a user