diff --git a/src/components/app-body/index.js b/src/components/app-body/index.js
index 6aa02a9..09a98cd 100644
--- a/src/components/app-body/index.js
+++ b/src/components/app-body/index.js
@@ -24,7 +24,7 @@ import SweepWif from './sweep/index.js'
import SignMessage from './sign/index.js'
import ServerSelectView from './configuration/select-server-view'
import NostrPost from './nostr/nostr-post/index.js'
-import NostrRead from './nostr/nostr-read/index.js'
+import Profile from './nostr/profile/index.js'
import GlobalFeed from './nostr/global-feed/index.js'
import ContentCreators from './nostr/content-creators/index.js'
import UserDataReview from './user-data-review'
@@ -49,7 +49,7 @@ function AppBody (props) {
} />
} />
} />
- } />
+ } />
} />
} />
} />
diff --git a/src/components/app-body/nostr/content-creators/content-card.js b/src/components/app-body/nostr/content-creators/content-card.js
index d0156e1..d52005d 100644
--- a/src/components/app-body/nostr/content-creators/content-card.js
+++ b/src/components/app-body/nostr/content-creators/content-card.js
@@ -46,7 +46,7 @@ function ContentCard (props) {
}, [loaded, creator])
const goToProfile = () => {
- const profileUrl = `${window.location.origin}/nostr-read/${creator.npub}`
+ const profileUrl = `${window.location.origin}/profile/${creator.npub}`
window.open(profileUrl, '_blank')
}
diff --git a/src/components/app-body/nostr/nostr-read/public-read.js b/src/components/app-body/nostr/nostr-read/public-read.js
deleted file mode 100644
index c3c3d1c..0000000
--- a/src/components/app-body/nostr/nostr-read/public-read.js
+++ /dev/null
@@ -1,101 +0,0 @@
-/**
- * Component for read nostr information kind 1
- */
-// Global npm libraries
-import React, { useEffect, useState } from 'react'
-import { Container, Card, Spinner } from 'react-bootstrap'
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
-import { faUser } from '@fortawesome/free-solid-svg-icons'
-import NostrFormat from '../nostr-format'
-import { RelayPool } from 'nostr'
-import * as nip19 from 'nostr-tools/nip19'
-
-function PublicRead (props) {
- const { npub } = props
- const { bchWalletState } = props.appData
- const [posts, setPosts] = useState([])
- const [loaded, setLoaded] = useState(false)
-
- useEffect(() => {
- // Get Last post from a author
- const start = () => {
- const pubHexData = nip19.decode(npub)
- const pubHex = pubHexData.data
- console.log('pubhex', pubHex)
- const psf = 'wss://nostr-relay.psfoundation.info'
-
- const pool = RelayPool([psf])
- pool.on('open', relay => {
- relay.subscribe('subid', { limit: 5, kinds: [1], authors: [pubHex] })
- setLoaded(true)
- })
-
- pool.on('eose', relay => {
- console.log('Closing Relay')
- setLoaded(true)
- relay.close()
- })
-
- pool.on('event', (relay, subId, ev) => {
- console.log('Received event:', ev)
- setPosts(currentPosts => [...currentPosts, ev])
- })
- }
-
- if (!loaded && npub) {
- start()
- }
- }, [bchWalletState, loaded, npub])
-
- return (
-
-
- {posts.map((post, index) => (
-
-
-
-
-
-
-
-
{props.profile?.name}
-
- {`${bchWalletState.nostrKeyPair.npub.slice(0, 8)}...${bchWalletState.nostrKeyPair.npub.slice(-5)}`}
-
-
-
- {new Date(post.created_at * 1000).toLocaleString()}
-
-
-
-
-
- ))}
- {!posts.length && loaded && (
-
- )}
- {!loaded && (
-
-
-
- )}
-
-
- )
-}
-
-export default PublicRead
diff --git a/src/components/app-body/nostr/nostr-read/index.js b/src/components/app-body/nostr/profile/index.js
similarity index 79%
rename from src/components/app-body/nostr/nostr-read/index.js
rename to src/components/app-body/nostr/profile/index.js
index 1cc910b..936d853 100644
--- a/src/components/app-body/nostr/nostr-read/index.js
+++ b/src/components/app-body/nostr/profile/index.js
@@ -8,8 +8,9 @@ import { Container } from 'react-bootstrap'
import ProfileRead from './profile-read.js'
import PublicRead from './public-read.js'
import { useParams } from 'react-router-dom'
+import SlpTokensDisplay from './slp-tokens-display'
-function NostrRead (props) {
+function Profile (props) {
const [profile, setProfile] = useState(false)
const { npub } = useParams()
console.log('npub', npub)
@@ -18,9 +19,10 @@ function NostrRead (props) {
+
>
)
}
-export default NostrRead
+export default Profile
diff --git a/src/components/app-body/nostr/nostr-read/profile-read.js b/src/components/app-body/nostr/profile/profile-read.js
similarity index 100%
rename from src/components/app-body/nostr/nostr-read/profile-read.js
rename to src/components/app-body/nostr/profile/profile-read.js
diff --git a/src/components/app-body/nostr/profile/public-read.js b/src/components/app-body/nostr/profile/public-read.js
new file mode 100644
index 0000000..ea702b1
--- /dev/null
+++ b/src/components/app-body/nostr/profile/public-read.js
@@ -0,0 +1,114 @@
+/**
+ * Component for read nostr information kind 1
+ */
+// Global npm libraries
+import React, { useEffect, useState } from 'react'
+import { Container, Card, Spinner } from 'react-bootstrap'
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
+import { faUser } from '@fortawesome/free-solid-svg-icons'
+import NostrFormat from '../nostr-format'
+import { RelayPool } from 'nostr'
+import * as nip19 from 'nostr-tools/nip19'
+
+function PublicRead (props) {
+ const { npub } = props
+ const { bchWalletState } = props.appData
+ const [posts, setPosts] = useState([])
+ const [loaded, setLoaded] = useState(false)
+
+ useEffect(() => {
+ // Get Last post from a author
+ const start = () => {
+ const pubHexData = nip19.decode(npub)
+ const pubHex = pubHexData.data
+ console.log('pubhex', pubHex)
+ const psf = 'wss://nostr-relay.psfoundation.info'
+
+ const pool = RelayPool([psf])
+ pool.on('open', relay => {
+ relay.subscribe('subid', { limit: 5, kinds: [1], authors: [pubHex] })
+ setLoaded(true)
+ })
+
+ pool.on('eose', relay => {
+ console.log('Closing Relay')
+ setLoaded(true)
+ relay.close()
+ })
+
+ pool.on('event', (relay, subId, ev) => {
+ console.log('Received event:', ev)
+ setPosts(currentPosts => [...currentPosts, ev])
+ })
+ }
+
+ if (!loaded && npub) {
+ start()
+ }
+ }, [bchWalletState, loaded, npub])
+
+ return (
+
+ {/* Nostr Feed Section */}
+
+
+
Nostr Feed
+
+
+ {posts.map((post, index) => (
+
+
+
+
+
+
+
+
{props.profile?.name}
+
+ {`${bchWalletState.nostrKeyPair.npub.slice(0, 8)}...${bchWalletState.nostrKeyPair.npub.slice(-5)}`}
+
+
+
+ {new Date(post.created_at * 1000).toLocaleString()}
+
+
+
+
+
+ ))}
+ {!posts.length && loaded && (
+
+ )}
+ {!loaded && (
+
+
+
+ )}
+
+
+
+ )
+}
+
+export default PublicRead
diff --git a/src/components/app-body/nostr/profile/slp-tokens-display.js b/src/components/app-body/nostr/profile/slp-tokens-display.js
new file mode 100644
index 0000000..a503711
--- /dev/null
+++ b/src/components/app-body/nostr/profile/slp-tokens-display.js
@@ -0,0 +1,169 @@
+/**
+ * Component for displaying SLP tokens in a grid layout
+ */
+import React, { useState, useEffect, useCallback } from 'react'
+import { Container, Row, Col, Spinner } from 'react-bootstrap'
+import config from '../../../../config'
+import axios from 'axios'
+import TokenCard from '../../slp-tokens/token-card'
+
+function SlpTokensDisplay (props) {
+ const { appData } = props
+ const { bchWalletState, wallet } = props.appData
+ const { npub } = props
+ const [loaded, setLoaded] = useState(false)
+ const [tokens, setTokens] = useState([])
+ const [iconsAreLoaded, setIconsAreLoaded] = useState(false)
+
+ // Get Cid from url
+ const parseCid = (url) => {
+ // get the cid from the url format 'ipfs://bafybeicem27xbzs65uvbcgykcmscsgln3lmhbfrcoec3gdttkdgtxv5acq
+ if (url && url.includes('ipfs://')) {
+ const cid = url.split('ipfs://')[1]
+ return cid
+ }
+ return url
+ }
+
+ // Fetch mutable data if it exist and get the token icon url
+ const fetchTokenIcon = useCallback(async (token) => {
+ try {
+ // Get the token data
+ const tokenData = await appData.wallet.getTokenData(token.tokenId)
+ if (!tokenData.mutableData) return false // Return false if no mutable data
+ // Get the token icon from the mutable data
+ const cid = parseCid(tokenData.mutableData)
+ console.log('mutable data cid', cid)
+
+ const { json } = await appData.wallet.cid2json({ cid })
+
+ if (!json) return false
+
+ const iconUrl = json.tokenIcon
+ // Return icon url
+ return iconUrl
+ } catch (error) {
+ return false
+ }
+ }, [appData])
+
+ // This function loads the token icons from the ipfs gateways.
+ const lazyLoadTokenIcons = useCallback(async (tokenList) => {
+ try {
+ setIconsAreLoaded(false)
+
+ // map each token and fetch the icon url
+ for (let i = 0; i < tokenList.length; i++) {
+ try {
+ const thisToken = tokenList[i]
+
+ // Incon does not need to be downloaded, so continue with the next one
+ if (thisToken.iconAlreadyDownloaded) continue
+
+ // Try to get token icon url from mutable data.
+ const iconUrl = await fetchTokenIcon(thisToken)
+ console.log('iconUrl', iconUrl)
+ if (iconUrl) {
+ // Set the icon url to the token , this can be used to display the icon in the token card component.
+ thisToken.icon = iconUrl
+ }
+
+ // Mark token to prevent fetch token icon again.
+ thisToken.iconAlreadyDownloaded = true
+ // replace the token in the tokens array
+ const updatedTokens = [...tokenList]
+ updatedTokens[i] = thisToken
+ setTokens(updatedTokens)
+ } catch (error) {
+ continue
+ }
+ }
+
+ setIconsAreLoaded(true)
+ } catch (error) {
+ console.warn(error)
+ setIconsAreLoaded(true)
+ }
+ }, [fetchTokenIcon, setTokens])
+
+ useEffect(() => {
+ // setTokens(bchWalletState.slpTokens || [])
+ const getTokens = async () => {
+ try {
+ const { nostrKeyPair } = bchWalletState
+ if (nostrKeyPair.npub === npub) {
+ setTokens(bchWalletState.slpTokens)
+ setLoaded(true)
+ return
+ }
+ setLoaded(false)
+ const url = `${config.dexServer}/sm/npub/${npub}`
+ const response = await axios.get(url)
+ console.log('response', response.data)
+ const addr = response.data.bchAddr
+
+ const tokenList = await wallet.listTokens(addr)
+ setTokens(tokenList)
+ // setTokens(response.data)
+ setLoaded(true)
+
+ lazyLoadTokenIcons(tokenList)
+ } catch (error) {
+ console.error('Error fetching tokens:', error)
+ setLoaded(true)
+ }
+ }
+ if (!loaded) {
+ getTokens()
+ }
+ }, [bchWalletState, npub, loaded, lazyLoadTokenIcons, wallet])
+
+ return (
+
+ {!loaded && (
+
+
+
+ )}
+ {loaded && tokens.length !== 0 &&
+
+
+
SLP Tokens
+
+
+
+ {!iconsAreLoaded &&
+
+
+ Loading Token Icons
+
+
+
+ }
+
+ {tokens.map(thisToken => (
+ { }}
+ hideSendBtn
+ />
+ ))}
+
+
+
+
+
}
+
+ )
+}
+
+export default SlpTokensDisplay
diff --git a/src/components/app-body/slp-tokens/token-card.js b/src/components/app-body/slp-tokens/token-card.js
index 2ceaa3a..4180184 100644
--- a/src/components/app-body/slp-tokens/token-card.js
+++ b/src/components/app-body/slp-tokens/token-card.js
@@ -12,7 +12,7 @@ import InfoButton from './info-button'
import SendTokenButton from './send-token-button'
function TokenCard (props) {
- const { token } = props
+ const { token, hideSendBtn } = props
const [icon, setIcon] = useState(token.icon)
// Update icon state every token.icon changes
@@ -64,13 +64,15 @@ function TokenCard (props) {
-
-
-
+ {!hideSendBtn && (
+
+
+
+ )}
diff --git a/src/components/nav-menu/index.js b/src/components/nav-menu/index.js
index 0737add..eda91fb 100644
--- a/src/components/nav-menu/index.js
+++ b/src/components/nav-menu/index.js
@@ -71,8 +71,8 @@ function NavMenu (props) {
Nostr Post
Nostr Profile