mirror of
https://github.com/Permissionless-Software-Foundation/bch-dex-taker-v2.git
synced 2026-09-21 16:52:01 -07:00
Fixed merge conflicts
This commit is contained in:
Generated
+1177
File diff suppressed because it is too large
Load Diff
@@ -23,6 +23,7 @@
|
|||||||
"react": "19.0.0",
|
"react": "19.0.0",
|
||||||
"react-bootstrap": "2.10.7",
|
"react-bootstrap": "2.10.7",
|
||||||
"react-dom": "19.0.0",
|
"react-dom": "19.0.0",
|
||||||
|
"react-markdown": "10.1.0",
|
||||||
"react-router-dom": "7.1.3",
|
"react-router-dom": "7.1.3",
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
"stream-browserify": "3.0.0",
|
"stream-browserify": "3.0.0",
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import SignMessage from './sign/index.js'
|
|||||||
import ServerSelectView from './configuration/select-server-view'
|
import ServerSelectView from './configuration/select-server-view'
|
||||||
import NostrPost from './nostr/nostr-post/index.js'
|
import NostrPost from './nostr/nostr-post/index.js'
|
||||||
import NostrRead from './nostr/nostr-read/index.js'
|
import NostrRead from './nostr/nostr-read/index.js'
|
||||||
|
import UserDataReview from './user-data-review'
|
||||||
function AppBody (props) {
|
function AppBody (props) {
|
||||||
// Dependency injection through props
|
// Dependency injection through props
|
||||||
const appData = props.appData
|
const appData = props.appData
|
||||||
@@ -47,7 +47,7 @@ function AppBody (props) {
|
|||||||
<Route path='/configuration' element={<ServerSelectView appData={appData} />} />
|
<Route path='/configuration' element={<ServerSelectView appData={appData} />} />
|
||||||
<Route path='/nostr-post' element={<NostrPost appData={appData} />} />
|
<Route path='/nostr-post' element={<NostrPost appData={appData} />} />
|
||||||
<Route path='/nostr-read' element={<NostrRead appData={appData} />} />
|
<Route path='/nostr-read' element={<NostrRead appData={appData} />} />
|
||||||
|
<Route path='/user-data/:cid' element={<UserDataReview appData={appData} />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
{/** Show in all paths except the servers view */}
|
{/** Show in all paths except the servers view */}
|
||||||
{/* {appData.currentPath !== '/servers' && <SelectServerButton linkTo='/servers' appData={appData} />} */}
|
{/* {appData.currentPath !== '/servers' && <SelectServerButton linkTo='/servers' appData={appData} />} */}
|
||||||
|
|||||||
@@ -102,8 +102,6 @@ function NftsForSale (props) {
|
|||||||
if (tokenData) {
|
if (tokenData) {
|
||||||
// Set data to the token object , this can be used to display the token name in the token card component.
|
// Set data to the token object , this can be used to display the token name in the token card component.
|
||||||
thisToken.tokenData = tokenData
|
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.
|
// Mark token to prevent fetch token data again.
|
||||||
@@ -117,7 +115,7 @@ function NftsForSale (props) {
|
|||||||
}, [appData])
|
}, [appData])
|
||||||
|
|
||||||
// Fetch mutable data if it exist and get the token icon url
|
// Fetch mutable data if it exist and get the token icon url
|
||||||
const fetchTokenInfo = useCallback(async (token) => {
|
const fetchTokenMutableData = useCallback(async (token) => {
|
||||||
try {
|
try {
|
||||||
// Get the token data
|
// Get the token data
|
||||||
const tokenData = token.tokenData
|
const tokenData = token.tokenData
|
||||||
@@ -137,15 +135,16 @@ function NftsForSale (props) {
|
|||||||
if (json.fullSizedUrl && json.fullSizedUrl.includes('http')) {
|
if (json.fullSizedUrl && json.fullSizedUrl.includes('http')) {
|
||||||
iconUrl = json.fullSizedUrl
|
iconUrl = json.fullSizedUrl
|
||||||
}
|
}
|
||||||
|
const userData = json.userData
|
||||||
// Return icon url
|
// Return icon url
|
||||||
return iconUrl
|
return { iconUrl, userData }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}, [appData])
|
}, [appData])
|
||||||
|
|
||||||
// This function loads the token icons from the ipfs gateways.
|
// This function loads the token icons from the ipfs gateways.
|
||||||
const lazyLoadTokenIcons = useCallback(async (tokens) => {
|
const lazyLoadMutableData = useCallback(async (tokens) => {
|
||||||
try {
|
try {
|
||||||
setIconsAreLoaded(false)
|
setIconsAreLoaded(false)
|
||||||
// map each token and fetch the icon url
|
// map each token and fetch the icon url
|
||||||
@@ -156,13 +155,12 @@ function NftsForSale (props) {
|
|||||||
if (thisToken.iconAlreadyDownloaded) continue
|
if (thisToken.iconAlreadyDownloaded) continue
|
||||||
|
|
||||||
// Try to get token icon url from mutable data.
|
// Try to get token icon url from mutable data.
|
||||||
const iconUrl = await fetchTokenInfo(thisToken)
|
const { iconUrl, userData } = await fetchTokenMutableData(thisToken)
|
||||||
console.log('iconUrl', iconUrl)
|
console.log('iconUrl', iconUrl)
|
||||||
if (iconUrl) {
|
if (iconUrl) {
|
||||||
// Set the icon url to the token , this can be used to display the icon in the token card component.
|
// Set the icon url to the token , this can be used to display the icon in the token card component.
|
||||||
thisToken.icon = iconUrl
|
thisToken.icon = iconUrl
|
||||||
// Update the cache data providing the tokenId to update and the data
|
thisToken.tokenData.userData = userData
|
||||||
appData.updateNFTCacheData(thisToken.tokenId, { tokenIcon: iconUrl })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark token to prevent fetch token icon again.
|
// Mark token to prevent fetch token icon again.
|
||||||
@@ -173,16 +171,17 @@ function NftsForSale (props) {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
setIconsAreLoaded(true)
|
setIconsAreLoaded(true)
|
||||||
}
|
}
|
||||||
}, [fetchTokenInfo, appData])
|
}, [fetchTokenMutableData, appData])
|
||||||
|
|
||||||
// Load tokens data from the cache
|
// Check if exiist token data in the cache and add it to the tokens object
|
||||||
const loadCacheData = useCallback(async (offers) => {
|
const reviewNftCachedData = useCallback(async (offers) => {
|
||||||
const cacheData = appData.nftForSaleData
|
const cacheData = appData.nftForSaleCacheData
|
||||||
console.log(`Token data from cache: ${JSON.stringify(cacheData, null, 2)}`)
|
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++) {
|
for (let i = 0; i < offers.length; i++) {
|
||||||
const thisToken = offers[i]
|
const thisToken = offers[i]
|
||||||
const cacheToken = cacheData[thisToken.tokenId]
|
const cacheToken = cacheData[thisToken.tokenId]
|
||||||
|
// If cache data exists, add it to the token object
|
||||||
if (cacheToken) {
|
if (cacheToken) {
|
||||||
thisToken.tokenData = cacheToken.tokenData
|
thisToken.tokenData = cacheToken.tokenData
|
||||||
thisToken.icon = cacheToken.tokenIcon
|
thisToken.icon = cacheToken.tokenIcon
|
||||||
@@ -190,24 +189,46 @@ function NftsForSale (props) {
|
|||||||
}
|
}
|
||||||
}, [appData])
|
}, [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
|
// Main function to load NFT offers
|
||||||
const loadNftOffers = useCallback(async () => {
|
const loadNftOffers = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
// Get tokens in offers
|
// Get tokens in offers
|
||||||
const offers = await getNftOffers()
|
const offers = await getNftOffers()
|
||||||
await loadCacheData(offers)
|
console.log('offers: ', offers)
|
||||||
|
// Review cached data to populate token data from cached data
|
||||||
|
await reviewNftCachedData(offers)
|
||||||
|
// set state to start displaying tokens cards.
|
||||||
setOffers(offers)
|
setOffers(offers)
|
||||||
// Load tokens data
|
// Load tokens data for any updates to the token data
|
||||||
await lazyLoadTokenData(offers)
|
await lazyLoadTokenData(offers)
|
||||||
// Load tokens icons
|
// Load tokens icons from mutable data
|
||||||
await lazyLoadTokenIcons(offers)
|
await lazyLoadMutableData(offers)
|
||||||
|
|
||||||
|
// Update cached data with the latest retrieved data
|
||||||
|
await updateNFTCachedData(offers)
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
console.error('Error loading NFT offers:', err)
|
console.error('Error loading NFT offers:', err)
|
||||||
}
|
}
|
||||||
}, [lazyLoadTokenData, lazyLoadTokenIcons, getNftOffers, loadCacheData])
|
}, [lazyLoadTokenData, lazyLoadMutableData, getNftOffers, reviewNftCachedData, updateNFTCachedData])
|
||||||
|
|
||||||
// Effect to load NFTs on component mount
|
// Effect to load NFTs on component mount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -6,11 +6,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// Global npm libraries
|
// Global npm libraries
|
||||||
import React, { useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { Button, Modal, Container, Row, Col } from 'react-bootstrap'
|
import { Button, Modal, Container, Row, Col } from 'react-bootstrap'
|
||||||
|
|
||||||
function InfoButton (props) {
|
function InfoButton (props) {
|
||||||
const [show, setShow] = useState(false)
|
const [show, setShow] = useState(false)
|
||||||
|
const [mutableDataCid, setMutableDataCid] = useState(null)
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
console.log('handleClose()')
|
console.log('handleClose()')
|
||||||
@@ -22,6 +23,32 @@ function InfoButton (props) {
|
|||||||
console.log('handleOpen()')
|
console.log('handleOpen()')
|
||||||
setShow(true)
|
setShow(true)
|
||||||
}
|
}
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get token user data if it exists and verify if it contains media or markdown
|
||||||
|
useEffect(() => {
|
||||||
|
try {
|
||||||
|
const userDataStr = props.token.tokenData.userData
|
||||||
|
if (userDataStr) {
|
||||||
|
const userData = JSON.parse(userDataStr)
|
||||||
|
|
||||||
|
// If user data contains media or markdown, set the mutable data cid
|
||||||
|
if (userData?.media || userData?.markdown) {
|
||||||
|
setMutableDataCid(parseCid(props.token.tokenData.mutableData))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
}, [props.token, show])
|
||||||
|
|
||||||
// Replace with dummy button until token data is loaded.
|
// Replace with dummy button until token data is loaded.
|
||||||
if (!props.token.tokenData) {
|
if (!props.token.tokenData) {
|
||||||
@@ -70,6 +97,22 @@ function InfoButton (props) {
|
|||||||
<Col xs={8}>{props.token.tokenType}</Col>
|
<Col xs={8}>{props.token.tokenType}</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
|
{mutableDataCid && (
|
||||||
|
<Row>
|
||||||
|
<Col xs={4}><b>User Data</b>:</Col>
|
||||||
|
<Col xs={8}>
|
||||||
|
<a
|
||||||
|
href={`/user-data/${mutableDataCid}`}
|
||||||
|
target='_blank'
|
||||||
|
rel='noopener noreferrer'
|
||||||
|
className='btn btn-link p-0'
|
||||||
|
>
|
||||||
|
View User Data
|
||||||
|
</a>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
)}
|
||||||
|
|
||||||
</Container>
|
</Container>
|
||||||
</Modal.Body>
|
</Modal.Body>
|
||||||
<Modal.Footer />
|
<Modal.Footer />
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
Search in the provided cid of a mutable data, get the data from the user data and show it
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Global npm libraries
|
||||||
|
import React, { useState, useEffect } from 'react'
|
||||||
|
import { useParams } from 'react-router-dom'
|
||||||
|
import { Container, Row, Col, Tabs, Tab, Spinner } from 'react-bootstrap'
|
||||||
|
import ReactMarkdown from 'react-markdown'
|
||||||
|
|
||||||
|
function UserDataReview (props) {
|
||||||
|
const appData = props.appData
|
||||||
|
const [media, setMedia] = useState([])
|
||||||
|
const [markdown, setMarkdown] = useState('')
|
||||||
|
const [activeTab, setActiveTab] = useState('media')
|
||||||
|
const [loading, setLoading] = useState(true)
|
||||||
|
const [error, setError] = useState(null)
|
||||||
|
// Get the id parameter from the URL
|
||||||
|
const { cid } = useParams()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const loadMedia = async () => {
|
||||||
|
setLoading(true)
|
||||||
|
try {
|
||||||
|
const { json } = await appData.wallet.cid2json({ cid })
|
||||||
|
const userDataString = json.userData
|
||||||
|
if (userDataString) {
|
||||||
|
const userData = JSON.parse(userDataString)
|
||||||
|
setMedia(userData.media)
|
||||||
|
setMarkdown(userData.markdown)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
setError(error.message)
|
||||||
|
}
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
|
loadMedia()
|
||||||
|
}, [cid, appData.wallet])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<Row>
|
||||||
|
<Col>
|
||||||
|
{error && <p className='text-danger'>{error}</p>}
|
||||||
|
{loading
|
||||||
|
? (
|
||||||
|
<div className='text-center my-5'>
|
||||||
|
<Spinner animation='border' role='status' variant='primary'>
|
||||||
|
<span className='visually-hidden'>Loading...</span>
|
||||||
|
</Spinner>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
: (
|
||||||
|
<Tabs
|
||||||
|
activeKey={activeTab}
|
||||||
|
onSelect={(k) => setActiveTab(k)}
|
||||||
|
className='mb-4'
|
||||||
|
>
|
||||||
|
<Tab eventKey='media' title='Media'>
|
||||||
|
{media && media.length > 0
|
||||||
|
? (
|
||||||
|
<Row className='mt-3'>
|
||||||
|
{media.map((item, index) => (
|
||||||
|
<Col key={index} xs={12} sm={6} md={4} lg={4} className='mb-3'>
|
||||||
|
<img
|
||||||
|
src={item.url}
|
||||||
|
alt={`Review media ${index + 1}`}
|
||||||
|
style={{ width: '100%', height: '250px', objectFit: 'cover', borderRadius: '8px' }}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
))}
|
||||||
|
</Row>
|
||||||
|
)
|
||||||
|
: (
|
||||||
|
<p className='mt-3'>No media content available</p>
|
||||||
|
)}
|
||||||
|
</Tab>
|
||||||
|
<Tab eventKey='markdown' title='Content'>
|
||||||
|
{markdown
|
||||||
|
? (
|
||||||
|
<div className='markdown-content mt-3'>
|
||||||
|
<ReactMarkdown>{markdown}</ReactMarkdown>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
: (
|
||||||
|
<p className='mt-3'>No content available</p>
|
||||||
|
)}
|
||||||
|
</Tab>
|
||||||
|
</Tabs>
|
||||||
|
)}
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Container>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UserDataReview
|
||||||
+8
-9
@@ -37,8 +37,8 @@ function useAppState () {
|
|||||||
const [hideSpinner, setHideSpinner] = useState(false)
|
const [hideSpinner, setHideSpinner] = useState(false)
|
||||||
const [denyClose, setDenyClose] = useState(false)
|
const [denyClose, setDenyClose] = useState(false)
|
||||||
|
|
||||||
// NFTs for sale state data
|
// NFTs for sale stored data to improve performance
|
||||||
const [nftForSaleData, setNftForSaleData] = useState({})
|
const [nftForSaleCacheData, setNftForSaleCacheData] = useState({})
|
||||||
|
|
||||||
// The wallet state makes this a true progressive web app (PWA). As
|
// The wallet state makes this a true progressive web app (PWA). As
|
||||||
// balances, UTXOs, and tokens are retrieved, this state is updated.
|
// balances, UTXOs, and tokens are retrieved, this state is updated.
|
||||||
@@ -100,14 +100,13 @@ function useAppState () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update NFT for sale tokens data
|
// Update NFT for sale tokens data
|
||||||
function updateNFTCacheData (tokenId, data) {
|
function updateNFTCachedData (tokenId, data) {
|
||||||
const allCacheData = nftForSaleData // Get all cache data
|
const allCacheData = nftForSaleCacheData // Get all cache data
|
||||||
const cacheData = allCacheData[tokenId] || {} // Get cache data for the tokenId
|
const cacheData = allCacheData[tokenId] || {} // Get cache data for the tokenId
|
||||||
|
|
||||||
const newCacheData = Object.assign({}, cacheData, data) // Merge the new data with the cache data
|
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
|
allCacheData[tokenId] = newCacheData // Update the cache data
|
||||||
setNftForSaleData(allCacheData) // Update the state
|
setNftForSaleCacheData(allCacheData) // Update the state
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -146,9 +145,9 @@ function useAppState () {
|
|||||||
setDexLib,
|
setDexLib,
|
||||||
nostr,
|
nostr,
|
||||||
setNostr,
|
setNostr,
|
||||||
nftForSaleData,
|
nftForSaleCacheData,
|
||||||
setNftForSaleData,
|
setNftForSaleCacheData,
|
||||||
updateNFTCacheData
|
updateNFTCachedData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user