Compare commits

...
4 Commits
Author SHA1 Message Date
Chris Troutner d708d88c3c Merge pull request #41 from Permissionless-Software-Foundation/dh-loading-time
feat(nostr): used '#' to reduce page loading time on profile page
2025-07-30 08:03:39 -07:00
Daniel Gonzalez f80c43f35e feat(nostr): used '#' to reduce page loading time on profile page 2025-07-29 20:00:14 -04:00
Daniel Gonzalez 3d73b7943d Fixed public read npub 2025-07-29 18:22:18 -04:00
Daniel Gonzalez 13ad04cde1 Fixed profile npub 2025-07-29 18:15:50 -04:00
8 changed files with 195 additions and 117 deletions
+36 -3
View File
@@ -14,6 +14,7 @@ import NavMenu from './components/nav-menu'
import useAppState from './hooks/state' import useAppState from './hooks/state'
import { UninitializedView, InitializedView } from './components/starter-views' import { UninitializedView, InitializedView } from './components/starter-views'
const sigleViewPaths = ['/profile']
function App (props) { function App (props) {
// Load all the app state into a single object that can be passed to child // Load all the app state into a single object that can be passed to child
// components. // components.
@@ -30,12 +31,44 @@ function App (props) {
}) })
}, []) }, [])
const isSignleView = useCallback(async () => {
// Get current path
const currentPath = window.location.pathname
// Get hash from url
const hash = window.location.hash
const allowedPath = sigleViewPaths.find((val) => { return currentPath.match(val) })
if (hash === '#single-view' && allowedPath) {
addToModal('Loading minimal-slp-wallet', appData)
const asyncLoad = new AsyncLoad()
if (!appData.wallet) {
await asyncLoad.loadWalletLib()
const walletTemp = await asyncLoad.initStarterWallet(appData.serverUrl, appData.lsState.mnemonic, appData)
appData.setWallet(walletTemp)
}
// Update Modal State
appData.setIsSingleView(true)
appData.setHideSpinner(true)
appData.setShowStartModal(false)
appData.setDenyClose(false)
/* // Update the startup state.
appData.setAsyncInitFinished(true)
appData.setAsyncInitSucceeded(false) */
return true
}
return false
}, [appData, addToModal])
/** Load all required data before component start. */ /** Load all required data before component start. */
useEffect(() => { useEffect(() => {
async function asyncEffect () { async function asyncEffect () {
console.log('asyncInitStarted: ', appData.asyncInitStarted) const singleView = await isSignleView()
if (!appData.asyncInitStarted) { console.log('asyncInitStarted: ', appData.asyncInitStarted)
console.log('is single view : ', singleView)
if (!appData.asyncInitStarted && !singleView) {
try { try {
// Instantiate the async load object. // Instantiate the async load object.
const asyncLoad = new AsyncLoad() const asyncLoad = new AsyncLoad()
@@ -112,7 +145,7 @@ function App (props) {
} }
} }
asyncEffect() asyncEffect()
}, [appData, addToModal]) }, [appData, addToModal, isSignleView])
return ( return (
<> <>
@@ -18,7 +18,7 @@ function ContentCard (props) {
}, [creator.profile]) }, [creator.profile])
const goToProfile = () => { const goToProfile = () => {
const profileUrl = `${window.location.origin}/profile/${creator.npub}` const profileUrl = `${window.location.origin}/profile/${creator.npub}#single-view`
window.open(profileUrl, '_blank') window.open(profileUrl, '_blank')
} }
@@ -13,8 +13,6 @@ import * as nip19 from 'nostr-tools/nip19'
function ProfileRead (props) { function ProfileRead (props) {
const { npub } = props const { npub } = props
const { bchWalletState } = props.appData
console.log('npub prop', npub)
const { setProfile } = props const { setProfile } = props
const [post, setPost] = useState({}) const [post, setPost] = useState({})
const [loaded, setLoaded] = useState(false) const [loaded, setLoaded] = useState(false)
@@ -24,7 +22,6 @@ function ProfileRead (props) {
const start = () => { const start = () => {
const pubHexData = nip19.decode(npub) const pubHexData = nip19.decode(npub)
const pubHex = pubHexData.data const pubHex = pubHexData.data
console.log('pubhex', pubHex)
const psf = 'wss://nostr-relay.psfoundation.info' const psf = 'wss://nostr-relay.psfoundation.info'
const pool = RelayPool([psf]) const pool = RelayPool([psf])
@@ -56,7 +53,7 @@ function ProfileRead (props) {
if (!loaded && npub) { if (!loaded && npub) {
start() start()
} }
}, [bchWalletState, loaded, setProfile, npub]) }, [loaded, setProfile, npub])
const handleImageError = (type) => { const handleImageError = (type) => {
setImageError(prev => ({ ...prev, [type]: true })) setImageError(prev => ({ ...prev, [type]: true }))
@@ -117,13 +114,13 @@ function ProfileRead (props) {
<div className='text-muted small mb-3 d-flex align-items-center flex-column flex-md-row'> <div className='text-muted small mb-3 d-flex align-items-center flex-column flex-md-row'>
<span className='text-truncate me-2 mb-2 mb-md-0'> <span className='text-truncate me-2 mb-2 mb-md-0'>
<span className='d-md-none'> <span className='d-md-none'>
{`${bchWalletState.nostrKeyPair.npub.slice(0, 8)}...${bchWalletState.nostrKeyPair.npub.slice(-5)}`} {`${npub?.slice(0, 8)}...${npub?.slice(-5)}`}
</span> </span>
<span className='d-none d-md-inline'> <span className='d-none d-md-inline'>
{bchWalletState.nostrKeyPair.npub} {npub}
</span> </span>
</span> </span>
<CopyOnClick walletProp='npub' appData={props.appData} value={bchWalletState.nostrKeyPair.npub} /> <CopyOnClick walletProp='npub' appData={props.appData} value={npub} />
</div> </div>
{/* About Section */} {/* About Section */}
@@ -79,7 +79,7 @@ function PublicRead (props) {
<div className='flex-grow-1'> <div className='flex-grow-1'>
<div className='fw-bold mb-1'>{props.profile?.name}</div> <div className='fw-bold mb-1'>{props.profile?.name}</div>
<small className='text-muted'> <small className='text-muted'>
{`${bchWalletState.nostrKeyPair.npub.slice(0, 8)}...${bchWalletState.nostrKeyPair.npub.slice(-5)}`} {`${npub.slice(0, 8)}...${npub.slice(-5)}`}
</small> </small>
</div> </div>
<small className='text-muted'> <small className='text-muted'>
@@ -91,7 +91,7 @@ function SlpTokensDisplay (props) {
const getTokens = async () => { const getTokens = async () => {
try { try {
const { nostrKeyPair } = bchWalletState const { nostrKeyPair } = bchWalletState
if (nostrKeyPair.npub === npub) { if (nostrKeyPair?.npub === npub) {
setTokens(bchWalletState.slpTokens) setTokens(bchWalletState.slpTokens)
setLoaded(true) setLoaded(true)
return return
+107 -103
View File
@@ -15,8 +15,7 @@ import Logo from './psf-logo.png'
function NavMenu (props) { function NavMenu (props) {
// Get the current path // Get the current path
const { currentPath, bchWalletState } = props.appData const { currentPath, bchWalletState, isSingleView } = props.appData
// Navbar state // Navbar state
const [expanded, setExpanded] = useState(false) const [expanded, setExpanded] = useState(false)
@@ -25,125 +24,130 @@ function NavMenu (props) {
// Collapse the navbar // Collapse the navbar
setExpanded(false) setExpanded(false)
} }
const goToHome = () => {
if (isSingleView) {
window.location.href = '/'
}
}
return ( return (
<> <>
<Navbar expanded={expanded} onToggle={setExpanded} expand='xxxl' bg='dark' variant='dark' style={{ paddingRight: '20px' }}> <Navbar expanded={expanded} onToggle={setExpanded} expand='xxxl' bg='dark' variant='dark' style={{ paddingRight: '20px' }}>
<Navbar.Brand href='#home' style={{ paddingLeft: '20px' }}> <Navbar.Brand href='#home' style={{ paddingLeft: '20px' }} onClick={goToHome}>
<Image src={Logo} thumbnail width='50' />{' '} <Image src={Logo} thumbnail width='50' />{' '}
DEX DEX
</Navbar.Brand> </Navbar.Brand>
<Navbar.Toggle aria-controls='responsive-navbar-nav' /> {!isSingleView && <Navbar.Toggle aria-controls='responsive-navbar-nav' />}
<Navbar.Collapse id='responsive-navbar-nav'> {!isSingleView && (
<Nav className='mr-auto'> <Navbar.Collapse id='responsive-navbar-nav'>
<Nav className='mr-auto'>
<NavLink
className={(currentPath === '/nfts-for-sale' || currentPath === '/') ? 'nav-link-active' : 'nav-link-inactive'}
to='/nfts-for-sale'
onClick={handleClickEvent}
>
NFTs
</NavLink>
<NavLink <NavLink
className={(currentPath === '/nfts-for-sale' || currentPath === '/') ? 'nav-link-active' : 'nav-link-inactive'} className={currentPath === '/offers' ? 'nav-link-active' : 'nav-link-inactive'}
to='/nfts-for-sale' to='/offers'
onClick={handleClickEvent} onClick={handleClickEvent}
> >
NFTs Fungible Tokens
</NavLink> </NavLink>
<NavLink <hr />
className={currentPath === '/offers' ? 'nav-link-active' : 'nav-link-inactive'}
to='/offers'
onClick={handleClickEvent}
>
Fungible Tokens
</NavLink>
<hr /> <NavLink
className={currentPath === '/global-feed' ? 'nav-link-active' : 'nav-link-inactive'}
to='/feeds'
onClick={handleClickEvent}
>
Feeds
</NavLink>
<NavLink
className={currentPath === '/nostr-post' ? 'nav-link-active' : 'nav-link-inactive'}
to='/nostr-post'
onClick={handleClickEvent}
>
Nostr Post
</NavLink>
<NavLink
className={currentPath === '/profile' ? 'nav-link-active' : 'nav-link-inactive'}
to={`/profile/${bchWalletState?.nostrKeyPair?.npub}`}
onClick={handleClickEvent}
>
Nostr Profile
</NavLink>
<NavLink
className={currentPath === '/content-creators' ? 'nav-link-active' : 'nav-link-inactive'}
to='/content-creators'
onClick={handleClickEvent}
>
Content Creators
</NavLink>
<NavLink <hr />
className={currentPath === '/global-feed' ? 'nav-link-active' : 'nav-link-inactive'}
to='/feeds'
onClick={handleClickEvent}
>
Feeds
</NavLink>
<NavLink
className={currentPath === '/nostr-post' ? 'nav-link-active' : 'nav-link-inactive'}
to='/nostr-post'
onClick={handleClickEvent}
>
Nostr Post
</NavLink>
<NavLink
className={currentPath === '/profile' ? 'nav-link-active' : 'nav-link-inactive'}
to={`/profile/${bchWalletState?.nostrKeyPair?.npub}`}
onClick={handleClickEvent}
>
Nostr Profile
</NavLink>
<NavLink
className={currentPath === '/content-creators' ? 'nav-link-active' : 'nav-link-inactive'}
to='/content-creators'
onClick={handleClickEvent}
>
Content Creators
</NavLink>
<hr /> <NavLink
className={(currentPath === '/bch') ? 'nav-link-active' : 'nav-link-inactive'}
to='/bch'
onClick={handleClickEvent}
>
BCH
</NavLink>
<NavLink <NavLink
className={(currentPath === '/bch') ? 'nav-link-active' : 'nav-link-inactive'} className={currentPath === '/slp-tokens' ? 'nav-link-active' : 'nav-link-inactive'}
to='/bch' to='/slp-tokens'
onClick={handleClickEvent} onClick={handleClickEvent}
> >
BCH Tokens
</NavLink> </NavLink>
<NavLink <NavLink
className={currentPath === '/slp-tokens' ? 'nav-link-active' : 'nav-link-inactive'} className={currentPath === '/wallet' ? 'nav-link-active' : 'nav-link-inactive'}
to='/slp-tokens' to='/wallet'
onClick={handleClickEvent} onClick={handleClickEvent}
> >
Tokens Wallet
</NavLink> </NavLink>
<NavLink <NavLink
className={currentPath === '/wallet' ? 'nav-link-active' : 'nav-link-inactive'} className={(currentPath === '/balance') ? 'nav-link-active' : 'nav-link-inactive'}
to='/wallet' to='/balance'
onClick={handleClickEvent} onClick={handleClickEvent}
>
Check Balance
</NavLink>
> <NavLink
Wallet className={(currentPath === '/sweep') ? 'nav-link-active' : 'nav-link-inactive'}
</NavLink> to='/sweep'
onClick={handleClickEvent}
>
Sweep
</NavLink>
<NavLink
className={(currentPath === '/sign') ? 'nav-link-active' : 'nav-link-inactive'}
to='/sign'
onClick={handleClickEvent}
>
Sign
</NavLink>
<NavLink
className={currentPath === '/configuration' ? 'nav-link-active' : 'nav-link-inactive'}
to='/configuration'
onClick={handleClickEvent}
>
Configuration
</NavLink>
<NavLink </Nav>
className={(currentPath === '/balance') ? 'nav-link-active' : 'nav-link-inactive'} </Navbar.Collapse>
to='/balance' )}
onClick={handleClickEvent}
>
Check Balance
</NavLink>
<NavLink
className={(currentPath === '/sweep') ? 'nav-link-active' : 'nav-link-inactive'}
to='/sweep'
onClick={handleClickEvent}
>
Sweep
</NavLink>
<NavLink
className={(currentPath === '/sign') ? 'nav-link-active' : 'nav-link-inactive'}
to='/sign'
onClick={handleClickEvent}
>
Sign
</NavLink>
<NavLink
className={currentPath === '/configuration' ? 'nav-link-active' : 'nav-link-inactive'}
to='/configuration'
onClick={handleClickEvent}
>
Configuration
</NavLink>
</Nav>
</Navbar.Collapse>
</Navbar> </Navbar>
</> </>
) )
+4 -1
View File
@@ -39,6 +39,7 @@ function useAppState () {
const [modalBody, setModalBody] = useState([]) const [modalBody, setModalBody] = useState([])
const [hideSpinner, setHideSpinner] = useState(false) const [hideSpinner, setHideSpinner] = useState(false)
const [denyClose, setDenyClose] = useState(false) const [denyClose, setDenyClose] = useState(false)
const [isSingleView, setIsSingleView] = useState(false)
// NFTs for sale stored data to improve performance // NFTs for sale stored data to improve performance
const [nftForSaleCacheData, setNftForSaleCacheData] = useState(lsState.nftData || {}) const [nftForSaleCacheData, setNftForSaleCacheData] = useState(lsState.nftData || {})
@@ -153,7 +154,9 @@ function useAppState () {
setNftForSaleCacheData, setNftForSaleCacheData,
updateNFTCachedData, updateNFTCachedData,
lastFeedTab, lastFeedTab,
setLastFeedTab setLastFeedTab,
isSingleView,
setIsSingleView
} }
} }
+41
View File
@@ -283,6 +283,47 @@ class AsyncLoad {
npub npub
} }
} }
// Initialize the BCH wallet without initialize() promise
async initStarterWallet (restURL, mnemonic, appData) {
try {
const options = {
interface: 'consumer-api',
restURL,
noUpdate: true
}
let wallet
if (mnemonic) {
// Load the wallet from the mnemonic, if it's available from local storage.
wallet = new this.BchWallet(mnemonic, options)
} else {
// Generate a new mnemonic and wallet.
wallet = new this.BchWallet(null, options)
}
await wallet.walletInfoPromise
// Get Nostr key pair from WIF
const nostrKeyPair = this.nostrKeyPairFromWIF(wallet.walletInfo.privateKey)
const walletInfo = wallet.walletInfo
walletInfo.nostrKeyPair = nostrKeyPair
// Update the state of the wallet.
appData.updateBchWalletState({ walletObj: walletInfo, appData })
// Save the mnemonic to local storage.
if (!mnemonic) {
const newMnemonic = wallet.walletInfo.mnemonic
appData.updateLocalStorage({ mnemonic: newMnemonic })
}
this.wallet = wallet
return wallet
} catch (error) {
console.error('Error initStarterWallet: ', error)
throw error
}
}
} }
function sleep (ms) { function sleep (ms) {