mirror of
https://github.com/Permissionless-Software-Foundation/bch-dex-taker-v2.git
synced 2026-09-22 09:12:00 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a93241f880 | ||
|
|
76dcd45d16 | ||
|
|
acb39ceff8 | ||
|
|
63b7bad1d1 | ||
|
|
36c81e7ce9 |
@@ -25,6 +25,7 @@ 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 GlobalFeed from './nostr/global-feed/index.js'
|
||||
import UserDataReview from './user-data-review'
|
||||
function AppBody (props) {
|
||||
// Dependency injection through props
|
||||
@@ -47,6 +48,7 @@ function AppBody (props) {
|
||||
<Route path='/configuration' element={<ServerSelectView appData={appData} />} />
|
||||
<Route path='/nostr-post' element={<NostrPost appData={appData} />} />
|
||||
<Route path='/nostr-read' element={<NostrRead appData={appData} />} />
|
||||
<Route path='/global-feed' element={<GlobalFeed appData={appData} />} />
|
||||
<Route path='/user-data/:cid' element={<UserDataReview appData={appData} />} />
|
||||
</Routes>
|
||||
{/** Show in all paths except the servers view */}
|
||||
|
||||
@@ -102,8 +102,6 @@ function NftsForSale (props) {
|
||||
if (tokenData) {
|
||||
// Set data to the token object , this can be used to display the token name in the token card component.
|
||||
thisToken.tokenData = tokenData
|
||||
// Update the cache data providing the tokenId to update and the data
|
||||
appData.updateNFTCacheData(thisToken.tokenId, { tokenData })
|
||||
}
|
||||
|
||||
// Mark token to prevent fetch token data again.
|
||||
@@ -163,8 +161,6 @@ function NftsForSale (props) {
|
||||
// Set the icon url to the token , this can be used to display the icon in the token card component.
|
||||
thisToken.icon = iconUrl
|
||||
thisToken.tokenData.userData = userData
|
||||
// Update the cache data providing the tokenId to update and the data
|
||||
appData.updateNFTCacheData(thisToken.tokenId, { tokenIcon: iconUrl })
|
||||
}
|
||||
|
||||
// Mark token to prevent fetch token icon again.
|
||||
@@ -177,14 +173,15 @@ function NftsForSale (props) {
|
||||
}
|
||||
}, [fetchTokenMutableData, appData])
|
||||
|
||||
// Load tokens data from the cache
|
||||
const loadCacheData = useCallback(async (offers) => {
|
||||
const cacheData = appData.nftForSaleData
|
||||
// Check if exiist token data in the cache and add it to the tokens object
|
||||
const reviewNftCachedData = useCallback(async (offers) => {
|
||||
const cacheData = appData.nftForSaleCacheData
|
||||
console.log(`Token data from cache: ${JSON.stringify(cacheData, null, 2)}`)
|
||||
// Map all offers and add cache data to the offers if it exists
|
||||
// Map all offers
|
||||
for (let i = 0; i < offers.length; i++) {
|
||||
const thisToken = offers[i]
|
||||
const cacheToken = cacheData[thisToken.tokenId]
|
||||
// If cache data exists, add it to the token object
|
||||
if (cacheToken) {
|
||||
thisToken.tokenData = cacheToken.tokenData
|
||||
thisToken.icon = cacheToken.tokenIcon
|
||||
@@ -192,6 +189,22 @@ function NftsForSale (props) {
|
||||
}
|
||||
}, [appData])
|
||||
|
||||
// Check if exiist token data in the cache and add it to the tokens object
|
||||
const updateNFTCachedData = useCallback(async (offers) => {
|
||||
// Map all offers
|
||||
for (let i = 0; i < offers.length; i++) {
|
||||
const thisToken = offers[i]
|
||||
// save token icon and token data in cache
|
||||
const newTokenCacheData = {
|
||||
tokenIcon: thisToken.icon,
|
||||
tokenData: thisToken.tokenData
|
||||
}
|
||||
|
||||
console.log('newTokenCacheData: ', newTokenCacheData)
|
||||
appData.updateNFTCachedData(thisToken.tokenId, newTokenCacheData)
|
||||
}
|
||||
}, [appData])
|
||||
|
||||
// Main function to load NFT offers
|
||||
const loadNftOffers = useCallback(async () => {
|
||||
try {
|
||||
@@ -199,18 +212,23 @@ function NftsForSale (props) {
|
||||
// Get tokens in offers
|
||||
const offers = await getNftOffers()
|
||||
console.log('offers: ', offers)
|
||||
await loadCacheData(offers)
|
||||
// Review cached data to populate token data from cached data
|
||||
await reviewNftCachedData(offers)
|
||||
// set state to start displaying tokens cards.
|
||||
setOffers(offers)
|
||||
// Load tokens data
|
||||
// Load tokens data for any updates to the token data
|
||||
await lazyLoadTokenData(offers)
|
||||
// Load tokens icons
|
||||
// Load tokens icons from mutable data
|
||||
await lazyLoadMutableData(offers)
|
||||
|
||||
// Update cached data with the latest retrieved data
|
||||
await updateNFTCachedData(offers)
|
||||
setIsLoading(false)
|
||||
} catch (err) {
|
||||
setIsLoading(false)
|
||||
console.error('Error loading NFT offers:', err)
|
||||
}
|
||||
}, [lazyLoadTokenData, lazyLoadMutableData, getNftOffers, loadCacheData])
|
||||
}, [lazyLoadTokenData, lazyLoadMutableData, getNftOffers, reviewNftCachedData, updateNFTCachedData])
|
||||
|
||||
// Effect to load NFTs on component mount
|
||||
useEffect(() => {
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* Component for reading global nostr posts
|
||||
*/
|
||||
// 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 { RelayPool } from 'nostr'
|
||||
|
||||
function Feed (props) {
|
||||
const { bchWalletState } = props.appData
|
||||
const [posts, setPosts] = useState([])
|
||||
const [loaded, setLoaded] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
const start = () => {
|
||||
const psf = 'wss://nostr-relay.psfoundation.info'
|
||||
|
||||
const pool = RelayPool([psf])
|
||||
pool.on('open', relay => {
|
||||
relay.subscribe('REQ', { limit: 10, kinds: [1], '#t': ['slpdex-socialmedia'] })
|
||||
})
|
||||
|
||||
pool.on('eose', relay => {
|
||||
console.log('Closing Relay')
|
||||
setLoaded(true)
|
||||
relay.close()
|
||||
})
|
||||
|
||||
pool.on('event', (relay, subId, ev) => {
|
||||
console.log('Received event:', ev)
|
||||
// const profile = JSON.parse(ev.content)
|
||||
setPosts(currentPosts => [...currentPosts, ev])
|
||||
})
|
||||
}
|
||||
|
||||
if (!loaded) {
|
||||
start()
|
||||
}
|
||||
}, [bchWalletState, loaded])
|
||||
|
||||
return (
|
||||
<Container className='mt-4 mb-5' style={{ marginBottom: '50px' }}>
|
||||
<div>
|
||||
{posts.map((post, index) => (
|
||||
<Card key={index} className='mb-4 bg-light rounded-4 shadow-sm border-0'>
|
||||
<Card.Body className='p-3'>
|
||||
<div className='d-flex align-items-center gap-3 mb-3'>
|
||||
<div
|
||||
className='rounded-circle bg-gradient shadow d-flex align-items-center justify-content-center'
|
||||
style={{
|
||||
width: '48px',
|
||||
height: '48px',
|
||||
background: 'linear-gradient(45deg, #6c757d, #495057)'
|
||||
}}
|
||||
>
|
||||
<FontAwesomeIcon icon={faUser} size='1x' color='#7c7c7d' />
|
||||
</div>
|
||||
<div className='flex-grow-1'>
|
||||
<div className='fw-bold mb-1'>
|
||||
{post.pubkey.slice(0, 8) + '...'}
|
||||
</div>
|
||||
<small className='text-muted'>
|
||||
{`${post.pubkey.slice(0, 8)}...${post.pubkey.slice(-5)}`}
|
||||
</small>
|
||||
</div>
|
||||
<small className='text-muted'>
|
||||
{new Date(post.created_at * 1000).toLocaleString()}
|
||||
</small>
|
||||
</div>
|
||||
<div className='mb-4'>
|
||||
<div className='fs-5 ms-5 text-dark' style={{ lineHeight: '1.3' }}>
|
||||
{post.content}
|
||||
</div>
|
||||
</div>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
))}
|
||||
{!posts.length && loaded && (
|
||||
<div className='text-center text-muted py-5 bg-light rounded-4 shadow-sm'>
|
||||
<i className='bi bi-inbox-fill fs-1 mb-3 d-block opacity-50' />
|
||||
<div>No posts found</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!loaded && (
|
||||
<div className='text-center text-muted py-5 bg-light rounded-4 shadow-sm'>
|
||||
<Spinner animation='border' />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
export default Feed
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
Component for reading the nostr global feed.
|
||||
*/
|
||||
|
||||
// Global npm libraries
|
||||
import React from 'react'
|
||||
import { Container } from 'react-bootstrap'
|
||||
import Feed from './feed'
|
||||
|
||||
function GlobalFeed (props) {
|
||||
const { appData } = props
|
||||
|
||||
return (
|
||||
<>
|
||||
<Container>
|
||||
<h2 style={{
|
||||
textAlign: 'center',
|
||||
margin: '20px 0 50px 0',
|
||||
padding: '10px',
|
||||
borderBottom: '2px solid #ccc'
|
||||
}}
|
||||
>
|
||||
Global Feed
|
||||
</h2>
|
||||
<Feed appData={appData} />
|
||||
</Container>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default GlobalFeed
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
// Global npm libraries
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { Container, Card } from 'react-bootstrap'
|
||||
import { Container, Card, Spinner } from 'react-bootstrap'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { faUser } from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
@@ -28,6 +28,7 @@ function PublicRead (props) {
|
||||
|
||||
pool.on('eose', relay => {
|
||||
console.log('Closing Relay')
|
||||
setLoaded(true)
|
||||
relay.close()
|
||||
})
|
||||
|
||||
@@ -35,8 +36,6 @@ function PublicRead (props) {
|
||||
console.log('Received event:', ev)
|
||||
setPosts(currentPosts => [...currentPosts, ev])
|
||||
})
|
||||
|
||||
setLoaded(true)
|
||||
}
|
||||
|
||||
if (!loaded) {
|
||||
@@ -85,6 +84,11 @@ function PublicRead (props) {
|
||||
<div>No posts found</div>
|
||||
</div>
|
||||
)}
|
||||
{!loaded && (
|
||||
<div className='text-center text-muted py-5 bg-light rounded-4 shadow-sm'>
|
||||
<Spinner animation='border' />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Container>
|
||||
)
|
||||
|
||||
@@ -114,6 +114,13 @@ function NavMenu (props) {
|
||||
>
|
||||
Nostr Profile
|
||||
</NavLink>
|
||||
<NavLink
|
||||
className={currentPath === '/global-feed' ? 'nav-link-active' : 'nav-link-inactive'}
|
||||
to='/global-feed'
|
||||
onClick={handleClickEvent}
|
||||
>
|
||||
Global Feed
|
||||
</NavLink>
|
||||
</Nav>
|
||||
</Navbar.Collapse>
|
||||
</Navbar>
|
||||
|
||||
+8
-9
@@ -37,8 +37,8 @@ function useAppState () {
|
||||
const [hideSpinner, setHideSpinner] = useState(false)
|
||||
const [denyClose, setDenyClose] = useState(false)
|
||||
|
||||
// NFTs for sale state data
|
||||
const [nftForSaleData, setNftForSaleData] = useState({})
|
||||
// NFTs for sale stored data to improve performance
|
||||
const [nftForSaleCacheData, setNftForSaleCacheData] = useState({})
|
||||
|
||||
// The wallet state makes this a true progressive web app (PWA). As
|
||||
// balances, UTXOs, and tokens are retrieved, this state is updated.
|
||||
@@ -100,14 +100,13 @@ function useAppState () {
|
||||
}
|
||||
|
||||
// Update NFT for sale tokens data
|
||||
function updateNFTCacheData (tokenId, data) {
|
||||
const allCacheData = nftForSaleData // Get all cache data
|
||||
function updateNFTCachedData (tokenId, data) {
|
||||
const allCacheData = nftForSaleCacheData // Get all cache data
|
||||
const cacheData = allCacheData[tokenId] || {} // Get cache data for the tokenId
|
||||
|
||||
const newCacheData = Object.assign({}, cacheData, data) // Merge the new data with the cache data
|
||||
console.log('newCacheData: ', newCacheData)
|
||||
allCacheData[tokenId] = newCacheData // Update the cache data
|
||||
setNftForSaleData(allCacheData) // Update the state
|
||||
setNftForSaleCacheData(allCacheData) // Update the state
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -146,9 +145,9 @@ function useAppState () {
|
||||
setDexLib,
|
||||
nostr,
|
||||
setNostr,
|
||||
nftForSaleData,
|
||||
setNftForSaleData,
|
||||
updateNFTCacheData
|
||||
nftForSaleCacheData,
|
||||
setNftForSaleCacheData,
|
||||
updateNFTCachedData
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user