BCH Balance
@@ -63,10 +67,16 @@ class AdminLTEPage extends React.Component {
- {_this.state.section === "Settings" &&
}
- {_this.state.section === "Icons" &&
}
- {_this.state.section === "Configure" &&
}
- {_this.state.section === "Audit" &&
}
+ {_this.state.section === "Wallet" && (
+
+ )}
+ {_this.state.section === "Icons" &&
}
+ {_this.state.section === "Configure" &&
}
+ {_this.state.section === "Audit" &&
}
@@ -80,12 +90,49 @@ class AdminLTEPage extends React.Component {
>
)
}
- componentDidMount() {
+ // Get wallet balance
+ async getBalance() {
+ try {
+ const { mnemonic } = _this.props.walletInfo
+ if (mnemonic) {
+ const bchWalletLib = new _this.BchWallet(mnemonic)
+ await bchWalletLib.walletInfoPromise
+ const myBalance = await bchWalletLib.getBalance()
+ _this.props.updateBalance(myBalance)
+ }
+ } catch (error) {
+ console.error(error)
+ }
+ }
+
+ async componentDidMount() {
_this.customMenuItems()
_this.dropDownBalance()
_this.addOnClickEventToScanner()
- _this.activeItemById("Settings")
+ _this.activeItemById("Wallet")
+
+ await _this.updateState()
+ setTimeout(() => {
+ _this.getBalance()
+ }, 250)
+ }
+
+ componentDidUpdate() {
+ _this.updateState()
+ }
+ // Update component state when props change
+ updateState() {
+ if (_this.props.walletInfo.mnemonic !== _this.state.walletInfo.mnemonic) {
+ _this.setState({
+ walletInfo: _this.props.walletInfo,
+ })
+ }
+ if (_this.props.bchBalance !== _this.state.bchBalance) {
+ _this.setState({
+ bchBalance: _this.props.bchBalance,
+ })
+ }
}
// Due to that it is not possible to add the "onClick" method
@@ -153,13 +200,12 @@ class AdminLTEPage extends React.Component {
try {
const windowWidth = window.innerWidth
//console.log("Window Width : ",windowWidth)
- if(windowWidth > MENU_HIDE_WIDTH) return
+ if (windowWidth > MENU_HIDE_WIDTH) return
const toggleEle = document.getElementsByClassName("sidebar-toggle")
toggleEle[0].click()
} catch (error) {
console.error(error)
}
-
}
// Adds the "onClick" event to the QR scanner item
@@ -170,7 +216,6 @@ class AdminLTEPage extends React.Component {
} catch (error) {
console.error(error)
}
-
}
// Controller to show the QR scanner
@@ -182,6 +227,14 @@ class AdminLTEPage extends React.Component {
showScannerModal: !_this.state.showScannerModal,
})
}
+
+}
+// Props prvided by redux
+AdminLTEPage.propTypes = {
+ walletInfo: PropTypes.object.isRequired,
+ bchBalance: PropTypes.number.isRequired,
+ setWallet: PropTypes.func.isRequired,
+ updateBalance: PropTypes.func.isRequired,
}
export default AdminLTEPage
diff --git a/src/components/admin-lte/settings/create.js b/src/components/admin-lte/settings/create.js
deleted file mode 100644
index 5fe5c23..0000000
--- a/src/components/admin-lte/settings/create.js
+++ /dev/null
@@ -1,41 +0,0 @@
-import React from "react"
-
-import { Row, Col, Box, Button } from "adminlte-2-react"
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
-class NewWallet extends React.Component {
- state = {}
-
- render() {
- return (
-
-
-
-
-
-
-
-
- New Wallet
-
-
-
-
-
-
-
-
-
-
- )
- }
-}
-
-export default NewWallet
diff --git a/src/components/admin-lte/settings/index.js b/src/components/admin-lte/settings/index.js
deleted file mode 100644
index be12343..0000000
--- a/src/components/admin-lte/settings/index.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import React from "react"
-
-import { Content } from "adminlte-2-react"
-import ImportWallet from "./import"
-import NewWallet from "./create"
-import InfoWallets from "./info"
-
-class Settings extends React.Component {
- state = {}
-
- render() {
- return (
- <>
-
-
-
-
-
- >
- )
- }
-}
-
-export default Settings
diff --git a/src/components/admin-lte/wallet/create.js b/src/components/admin-lte/wallet/create.js
new file mode 100644
index 0000000..0ce2d50
--- /dev/null
+++ b/src/components/admin-lte/wallet/create.js
@@ -0,0 +1,74 @@
+import React from "react"
+import PropTypes from "prop-types"
+import { Row, Col, Box, Button } from "adminlte-2-react"
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
+import BchWallet from "minimal-slp-wallet"
+
+let _this
+class NewWallet extends React.Component {
+ constructor(props) {
+ super(props)
+ _this = this
+ this.state = {}
+
+ _this.BchWallet = BchWallet
+ }
+
+ render() {
+ return (
+ <>
+
+
+
+
+
+
+
+
+ New Wallet
+
+
+
+
+
+
+
+
+
+
+ >
+ )
+ }
+ async createWallet() {
+ try {
+
+ const bchWalletLib = new _this.BchWallet()
+ await bchWalletLib.walletInfoPromise // Wait for wallet to be created.
+ const walletInfo = bchWalletLib.walletInfo
+ walletInfo.from = "created"
+
+ const myBalance = await bchWalletLib.getBalance()
+
+ // Update redux state
+ _this.props.setWallet(walletInfo)
+ _this.props.updateBalance(myBalance)
+
+ } catch (error) {
+ console.error(error)
+ }
+ }
+}
+NewWallet.propTypes = {
+ setWallet: PropTypes.func.isRequired,
+ updateBalance: PropTypes.func.isRequired,
+}
+export default NewWallet
diff --git a/src/components/admin-lte/settings/import.js b/src/components/admin-lte/wallet/import.js
similarity index 51%
rename from src/components/admin-lte/settings/import.js
rename to src/components/admin-lte/wallet/import.js
index 754718d..24109d9 100644
--- a/src/components/admin-lte/settings/import.js
+++ b/src/components/admin-lte/wallet/import.js
@@ -1,7 +1,9 @@
import React from "react"
-
+import PropTypes from "prop-types"
import { Row, Col, Box, Button, Inputs } from "adminlte-2-react"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
+import BchWallet from "minimal-slp-wallet"
+
const { Text } = Inputs
let _this
@@ -14,7 +16,9 @@ class ImportWallet extends React.Component {
this.state = {
mnemonic: "",
privateKey: "",
+ errMsg: "",
}
+ _this.BchWallet = BchWallet
}
render() {
@@ -44,7 +48,6 @@ class ImportWallet extends React.Component {
placeholder="12 word mnemonic"
label="12 word mnemonic"
labelPosition="above"
- maxlength={12}
onChange={_this.handleUpdate}
/>
+
+ {_this.state.errMsg && (
+ {_this.state.errMsg}
+ )}
+
-
+
@@ -70,10 +83,9 @@ class ImportWallet extends React.Component {
)
}
- componentDidMount(){
+ componentDidMount() {
// add max length property to mnemonic input
- document.getElementById("import-mnemonic").maxLength = "12";
-
+ //document.getElementById("import-mnemonic").maxLength = "12"
}
handleUpdate(event) {
let value = event.target.value
@@ -83,8 +95,66 @@ class ImportWallet extends React.Component {
_this.setState({
[event.target.name]: value,
})
- //console.log(_this.state)
+ }
+ async importWallet() {
+ try {
+ _this.validateInputs()
+
+ const bchWalletLib = new _this.BchWallet(_this.state.mnemonic)
+ await bchWalletLib.walletInfoPromise // Wait for wallet to be created.
+ const walletInfo = bchWalletLib.walletInfo
+ walletInfo.from = "imported"
+
+ const myBalance = await bchWalletLib.getBalance()
+
+ // Update redux state
+ _this.props.setWallet(walletInfo)
+ _this.props.updateBalance(myBalance)
+
+ // Reset form and component state
+ _this.resetValues()
+ } catch (error) {
+ console.warn(error)
+ _this.setState({
+ errMsg: error.message,
+ })
+ }
+ }
+
+ // Reset form and component state
+ resetValues() {
+ _this.setState({
+ mnemonic: "",
+ privateKey: "",
+ errMsg: "",
+ })
+ const mnemonicEle = document.getElementById("import-mnemonic")
+ mnemonicEle.value = ""
+
+ const privateKeyEle = document.getElementById("privateKey")
+ privateKeyEle.value = ""
+ }
+
+ validateInputs() {
+ try {
+ const { mnemonic } = _this.state
+ if (mnemonic) {
+ const spaceCount = mnemonic.split(" ").length //mnemonic.match(/ /g).length
+
+ if (spaceCount !== 12) {
+ console.log("reject")
+ throw new Error("mnemonic must contain 12 words")
+ }
+ } else {
+ throw new Error("12 word mnemonic is required")
+ }
+ } catch (error) {
+ throw error
+ }
}
}
-
+ImportWallet.propTypes = {
+ setWallet: PropTypes.func.isRequired,
+ updateBalance: PropTypes.func.isRequired,
+}
export default ImportWallet
diff --git a/src/components/admin-lte/wallet/index.js b/src/components/admin-lte/wallet/index.js
new file mode 100644
index 0000000..13573dc
--- /dev/null
+++ b/src/components/admin-lte/wallet/index.js
@@ -0,0 +1,54 @@
+import React from "react"
+import PropTypes from "prop-types"
+import { Content } from "adminlte-2-react"
+import ImportWallet from "./import"
+import NewWallet from "./create"
+import InfoWallets from "./info"
+import WalletInfo from "./wallet-info"
+
+let _this
+class Wallet extends React.Component {
+ constructor(props) {
+ super(props)
+ _this = this
+ this.state = {}
+ }
+
+ render() {
+ return (
+ <>
+
+
+
+ {_this.props.walletInfo.mnemonic &&
+ _this.props.walletInfo.from === "created" && (
+
+ )}
+
+
+
+ {_this.props.walletInfo.mnemonic &&
+ _this.props.walletInfo.from === "imported" && (
+
+ )}
+
+
+
+ >
+ )
+ }
+
+}
+Wallet.propTypes = {
+ setWallet: PropTypes.func.isRequired,
+ walletInfo: PropTypes.object.isRequired,
+ updateBalance: PropTypes.func.isRequired,
+}
+
+export default Wallet
diff --git a/src/components/admin-lte/settings/info.js b/src/components/admin-lte/wallet/info.js
similarity index 100%
rename from src/components/admin-lte/settings/info.js
rename to src/components/admin-lte/wallet/info.js
diff --git a/src/components/admin-lte/wallet/wallet-info.js b/src/components/admin-lte/wallet/wallet-info.js
new file mode 100644
index 0000000..025aa5c
--- /dev/null
+++ b/src/components/admin-lte/wallet/wallet-info.js
@@ -0,0 +1,193 @@
+import React from "react"
+import PropTypes from "prop-types"
+
+import { Row, Col, Box } from "adminlte-2-react"
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
+import "./wallet.css"
+let _this
+class WalletInfo extends React.Component {
+ constructor(props) {
+ super(props)
+ _this = this
+ this.state = {
+ walletInfo: _this.props.walletInfo,
+ mnemonic: "",
+ privateKey: "",
+ cashAddress: "",
+ address: "",
+ slpAddress: "",
+ legacyAddress: "",
+ hdPath: "",
+ }
+ }
+
+ render() {
+ return (
+
+
+
+
+
+
+
+
+ My Wallet
+
+
+
+
+
+
+
+ Mnemonic: {_this.state.mnemonic}
+
+
+
+ _this.copyToClipBoard("mnemonic")}
+ icon={"copy"}
+ />
+
+
+
+
+
+ Private Key: {_this.state.privateKey}
+
+
+
+ _this.copyToClipBoard("privateKey")}
+ icon={"copy"}
+ />
+
+
+
+
+
+ Cash Address: {_this.state.cashAddress}
+
+
+
+ _this.copyToClipBoard("cashAddress")}
+ icon={"copy"}
+ />
+
+
+
+
+
+ Address: {_this.state.address}
+
+
+
+ _this.copyToClipBoard("address")}
+ icon={"copy"}
+ />
+
+
+
+
+
+ Slp Address: {_this.state.slpAddress}
+
+
+
+ _this.copyToClipBoard("slpAddress")}
+ icon={"copy"}
+ />
+
+
+
+
+
+ Legacy Address: {_this.state.legacyAddress}
+
+
+
+ _this.copyToClipBoard("legacyAddress")}
+ icon={"copy"}
+ />
+
+
+
+
+
+ HD Path:
+ {_this.state.hdPath}
+
+
+
+ _this.copyToClipBoard("hdPath")}
+ icon={"copy"}
+ />
+
+
+
+
+
+
+
+
+ )
+ }
+ // copy info to clipboard
+ copyToClipBoard(key) {
+ const val = _this.state[key]
+ var textArea = document.createElement("textarea")
+ textArea.value = val //copyText.textContent;
+ document.body.appendChild(textArea)
+ textArea.select()
+ document.execCommand("Copy")
+ textArea.remove()
+ }
+
+ componentDidMount() {
+ // set component state
+ const wallet = _this.props.walletInfo
+ Object.entries(wallet).forEach(([key, value]) => {
+ _this.setState({
+ [key]: value,
+ })
+ })
+ }
+ componentDidUpdate() {
+ // set component state
+ if (_this.props.walletInfo.mnemonic !== _this.state.mnemonic) {
+ const wallet = _this.props.walletInfo
+ Object.entries(wallet).forEach(([key, value]) => {
+ _this.setState({
+ [key]: value,
+ })
+ })
+ }
+ }
+}
+WalletInfo.propTypes = {
+ walletInfo: PropTypes.object.isRequired,
+}
+
+export default WalletInfo
diff --git a/src/components/admin-lte/wallet/wallet.css b/src/components/admin-lte/wallet/wallet.css
new file mode 100644
index 0000000..fb08619
--- /dev/null
+++ b/src/components/admin-lte/wallet/wallet.css
@@ -0,0 +1,17 @@
+.wallet-info-content{
+ /* border:0.7px solid var(--main-color); */
+ border-radius: 5px;
+ max-width: 100%;
+ padding-top: 4px;
+ padding-bottom: 4px;
+ margin-right: 0px;
+ margin-left: 0px;
+ border-top: none;
+ border-right: none;
+ border-left: none;
+}
+.wallet-info-content div{
+ overflow:hidden;
+ white-space:nowrap;
+ text-overflow: ellipsis;
+}
\ No newline at end of file
diff --git a/src/components/layout.css b/src/components/layout.css
index 89a120d..a66b911 100644
--- a/src/components/layout.css
+++ b/src/components/layout.css
@@ -621,6 +621,12 @@ pre tt:after {
}
}
+.text-right{
+ text-align: right;
+}
+.text-left{
+ text-align: left;
+}
.text-center{
text-align: center;
}
@@ -661,7 +667,34 @@ pre tt:after {
.mb-3{
margin-bottom: 3em;
}
+.mr-1{
+ margin-right: 1em;
+}
+.mr-2{
+ margin-right: 2em;
+}
+.ml-3{
+ margin-right: 3em;
+}
+.ml-1{
+ margin-left: 1em;
+}
+.ml-2{
+ margin-left: 2em;
+}
+.ml-3{
+ margin-left: 3em;
+}
.title-icon{
margin-right: 7px;
color: var(--main-color);
-}
\ No newline at end of file
+}
+
+.icon{
+ cursor: pointer;
+ color: var(--main-color);
+}
+.btn-animation:active {
+ box-shadow: 0 5px #666;
+ transform: translateY(4px);
+}
diff --git a/src/components/localWallet.js b/src/components/localWallet.js
new file mode 100644
index 0000000..6cc221e
--- /dev/null
+++ b/src/components/localWallet.js
@@ -0,0 +1,15 @@
+// Local storage handler
+
+
+const localStorageWalletKey = "fullstack-wallet-info"
+
+// Detect if the app is running in a browser.
+export const isBrowser = () => typeof window !== "undefined"
+
+export const getWalletInfo = () =>
+ isBrowser() && window.localStorage.getItem(localStorageWalletKey)
+ ? JSON.parse(window.localStorage.getItem(localStorageWalletKey))
+ : {}
+
+export const setWalletInfo = wallet =>
+ window.localStorage.setItem(localStorageWalletKey, JSON.stringify(wallet))
diff --git a/src/pages/index.js b/src/pages/index.js
index 83702c2..eed0fbc 100644
--- a/src/pages/index.js
+++ b/src/pages/index.js
@@ -1,16 +1,30 @@
-import React from 'react'
+import React from "react"
+import { connect } from "react-redux"
let AdminLTE =
typeof window !== `undefined`
- ? require('../components/admin-lte').default
+ ? require("../components/admin-lte").default
: null
+const mapStateToProps = ({ walletInfo, bchBalance }) => {
+ return { walletInfo,bchBalance }
+}
+
+const mapDispatchToProps = dispatch => {
+ return {
+ setWallet: value => dispatch({ type: `SET_WALLET`, value }),
+ updateBalance: value =>dispatch({ type: `UPDATE_BALANCE`, value })
+ }
+}
+
+let ConnectedDashboard = AdminLTE
+ ? connect(mapStateToProps, mapDispatchToProps)(AdminLTE)
+ : null
class AdminLTEPage extends React.Component {
state = {}
-
render() {
- return <>{AdminLTE && }>
+ return <>{ConnectedDashboard && }>
}
}
diff --git a/src/redux/createStore.js b/src/redux/createStore.js
new file mode 100644
index 0000000..b30723a
--- /dev/null
+++ b/src/redux/createStore.js
@@ -0,0 +1,36 @@
+import { createStore as reduxCreateStore } from "redux"
+// Wallet from localStorage
+import { getWalletInfo, setWalletInfo } from "../components/localWallet"
+
+const reducer = (state, action) => {
+ // Update walletInfo state property
+ if (action.type === `SET_WALLET`) {
+ setWalletInfo(action.value) // Add wallet to local storage
+ return Object.assign({}, state, {
+ walletInfo: action.value,
+ })
+ }
+
+ // Update bchBalance state property
+ if (action.type === `UPDATE_BALANCE`) {
+ // Convert satoshis to bch
+ const satoshis = action.value
+ const bch = satoshis / 100000000
+ return Object.assign({}, state, {
+ bchBalance: Number(bch.toFixed(8)),
+ })
+ }
+
+ return state
+}
+
+// Wallet info from local storage
+const localWallet = getWalletInfo()
+
+const initialState = {
+ walletInfo: localWallet.mnemonic ? localWallet : {},
+ bchBalance: 0,
+}
+
+const createStore = () => reduxCreateStore(reducer, initialState)
+export default createStore
diff --git a/wrap-with-provider.js b/wrap-with-provider.js
new file mode 100644
index 0000000..b893d04
--- /dev/null
+++ b/wrap-with-provider.js
@@ -0,0 +1,13 @@
+import React from "react"
+import { Provider } from "react-redux"
+
+import createStore from "./src/redux/createStore"
+
+// eslint-disable-next-line react/display-name,react/prop-types
+export default ({ element }) => {
+ // Instantiating store in `wrapRootElement` handler ensures:
+ // - there is fresh store for each SSR page
+ // - it will be called only once in browser, when React mounts
+ const store = createStore()
+ return {element}
+}
\ No newline at end of file