+ )
+ }
+
+
+
+
+
+
+ {generateCards()}
+
+ {/** Display a message if no tokens are found */}
+ {tokens.length === 0 && (
+
+ No tokens found in wallet
+
+ )}
+
+
+ >
+ )
+}
+
+export default SlpTokens
diff --git a/src/components/app-body/slp-tokens/info-button.js b/src/components/app-body/slp-tokens/info-button.js
new file mode 100644
index 0000000..8fed8ac
--- /dev/null
+++ b/src/components/app-body/slp-tokens/info-button.js
@@ -0,0 +1,98 @@
+/*
+ This component renders as a button. When clicked, it opens a modal that
+ displays information about the token.
+
+ This is a functional component with as little state as possible.
+*/
+
+// Global npm libraries
+import React, { useState } from 'react'
+import { Button, Modal, Container, Row, Col } from 'react-bootstrap'
+
+// Takes a string as input. If it matches a pattern for a link, a JSX object is
+// returned with a link. Otherwise the original string is returned.
+function linkIfUrl (url) {
+ // Convert the URL into a link if it contains 'http'
+ if (url.includes('http')) {
+ url = ({url})
+
+ //
+ } else if (url.includes('ipfs://')) {
+ // Convert to a Filecoin link if its an IPFS reference.
+
+ const cid = url.substring(7)
+ url = ({url})
+ }
+
+ return url
+}
+
+function InfoButton (props) {
+ const [show, setShow] = useState(false)
+
+ const handleClose = () => {
+ setShow(false)
+ // props.instance.setState({ showModal: false })
+ }
+
+ const handleOpen = () => {
+ setShow(true)
+ }
+
+ // Convert the url property of the token to a link, if it matches common patterns.
+ let url = props.token.url
+ url = linkIfUrl(props.token.url)
+
+ // console.log('props.token: ', props.token)
+
+ return (
+ <>
+
+
+
+ Token Information
+
+
+
+
+
+ Warning: Careful! Not all Bitcoin Cash wallets are token-aware.
+ If you send this token to a wallet that is not
+ token-aware, it could be burned. It's best practice to
+ only send tokens to 'simpleledger' addresses and not
+ 'bitcoincash' addresses.
+
+
+
+
+ >
+ )}
+
+
+ {statusMsg}
+
+
+
+ {!hideSpinner && }
+
+
+
+
+
+
+
+ )
+ }
+
+ return (
+ <>
+
+ {showModal && getModal()}
+ >
+ )
+}
+
+export default SendTokenButton
diff --git a/src/components/app-body/slp-tokens/token-card.js b/src/components/app-body/slp-tokens/token-card.js
new file mode 100644
index 0000000..2ceaa3a
--- /dev/null
+++ b/src/components/app-body/slp-tokens/token-card.js
@@ -0,0 +1,83 @@
+/*
+ This Card component summarizes an SLP token.
+ if a token icon does not exist or cant be loaded , then display a default icon from Jdenticon library.
+*/
+
+// Global npm libraries
+import React, { useState, useEffect } from 'react'
+import { Container, Row, Col, Card } from 'react-bootstrap'
+import Jdenticon from '@chris.troutner/react-jdenticon'
+// Local libraries
+import InfoButton from './info-button'
+import SendTokenButton from './send-token-button'
+
+function TokenCard (props) {
+ const { token } = props
+ const [icon, setIcon] = useState(token.icon)
+
+ // Update icon state every token.icon changes
+ useEffect(() => {
+ setIcon(token.icon)
+ }, [token.icon])
+
+ return (
+ <>
+
+
+
+ {/** If the icon is loaded, display it */
+ icon && (
+ {
+ setIcon(null) // Set the icon to null if it fails to load the image url.
+ }}
+ />
+ )
+ }
+
+ {/** If the icon is not loaded, display the Jdenticon */
+ !icon && (
+
+ )
+ }
+
+
{props.token.ticker}
+
+
+
+
+
+ {props.token.name}
+
+
+
+
+
+
Balance:
+
{props.token.qty}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ )
+}
+
+export default TokenCard
diff --git a/src/components/nav-menu/index.js b/src/components/nav-menu/index.js
index 5723fb7..50f9bfd 100644
--- a/src/components/nav-menu/index.js
+++ b/src/components/nav-menu/index.js
@@ -59,10 +59,17 @@ function NavMenu (props) {
>
Wallet
+
+ Tokens
+ handleClickEvent(1)}
+ onClick={handleClickEvent}
>
Placeholder2
diff --git a/src/hooks/state.js b/src/hooks/state.js
index 15c6e72..d3ffb8e 100644
--- a/src/hooks/state.js
+++ b/src/hooks/state.js
@@ -4,6 +4,7 @@ import useLocalStorageState from 'use-local-storage-state'
import AppUtil from '../util'
import { useLocation } from 'react-router-dom'
+
function useAppState () {
const location = useLocation()
diff --git a/src/services/async-load.js b/src/services/async-load.js
index f5a0b45..03be3df 100644
--- a/src/services/async-load.js
+++ b/src/services/async-load.js
@@ -4,7 +4,6 @@
// Global npm libraries
import axios from 'axios'
-
// Local libraries
import GistServers from './gist-servers'
@@ -112,13 +111,6 @@ class AsyncLoad {
const slpTokens = await wallet.listTokens(wallet.walletInfo.cashAddress)
// console.log('slpTokens: ', slpTokens)
- // Add an icon property to each token.
- slpTokens.map(x => {
- x.icon = ''// ()
- x.iconNeedsDownload = true
- return true
- })
-
console.log('slpTokens: ', slpTokens)
// Update the state of the wallet with the balances