Added profiles view

This commit is contained in:
Chris Troutner
2026-06-03 11:35:29 -07:00
parent 9726f9b33c
commit a32ae73ba4
5 changed files with 146 additions and 1 deletions
+2
View File
@@ -23,6 +23,7 @@ import SweepWif from './sweep/index.js'
import SignMessage from './sign/index.js'
import ServerSelectView from './configuration/select-server-view'
import UserDataReview from './user-data-review'
import RecentProfiles from './recent-profiles'
function AppBody (props) {
// Dependency injection through props
@@ -36,6 +37,7 @@ function AppBody (props) {
<Route path='/bch' element={<BchSend appData={appData} />} />
<Route path='/wallet' element={<Wallet appData={appData} />} />
<Route path='/slp-tokens' element={<SlpTokens appData={appData} />} />
<Route path='/profile/recent' element={<RecentProfiles />} />
<Route path='/placeholder2' element={<Placeholder2 />} />
<Route path='/placeholder3' element={<Placeholder3 />} />
<Route path='/servers' element={<ServerSelectView appData={appData} />} />
@@ -0,0 +1,106 @@
/*
Display the most recent Memo profiles from psf-memo-db.
*/
// Global npm libraries
import React, { useState, useEffect } from 'react'
import { Container, Row, Col, Spinner, Table } from 'react-bootstrap'
// Local libraries
import MemoDb from '../../../services/memo-db'
import '../../../App.css'
function truncate (str, maxLen = 16) {
if (!str || str.length <= maxLen) return str
const half = Math.floor((maxLen - 3) / 2)
return `${str.slice(0, half)}...${str.slice(-half)}`
}
function formatSeen (seen) {
if (!seen) return ''
const ms = seen > 1e12 ? seen : seen * 1000
return new Date(ms).toLocaleString()
}
function RecentProfiles () {
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
const [profiles, setProfiles] = useState([])
const [pagination, setPagination] = useState(null)
useEffect(() => {
const loadProfiles = async () => {
try {
const memoDb = new MemoDb()
const data = await memoDb.getRecentProfiles({ limit: 100, offset: 0 })
setProfiles(data.profiles || [])
setPagination(data.pagination || null)
} catch (err) {
setError(err.message || 'Failed to load recent profiles')
}
setLoading(false)
}
loadProfiles()
}, [])
return (
<Container>
<Row>
<Col>
<h1 className='mt-4'>Recent Profiles</h1>
{pagination && (
<p className='text-muted'>
Showing {profiles.length} of {pagination.total} profiles
</p>
)}
{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>
)}
{!loading && !error && (
<Table striped bordered hover responsive className='mt-3'>
<thead>
<tr>
<th>Address</th>
<th>Bio</th>
<th>Block</th>
<th>Seen</th>
<th>TxID</th>
</tr>
</thead>
<tbody>
{profiles.map((profile) => (
<tr key={`${profile.addr}-${profile.txid}`}>
<td>
<span style={{ fontFamily: 'monospace' }} title={profile.addr}>
{truncate(profile.addr, 24)}
</span>
</td>
<td>{profile.text}</td>
<td>{profile.blockHeight}</td>
<td>{formatSeen(profile.seen)}</td>
<td>
<span style={{ fontFamily: 'monospace' }} title={profile.txid}>
{truncate(profile.txid, 20)}
</span>
</td>
</tr>
))}
</tbody>
</Table>
)}
</Col>
</Row>
</Container>
)
}
export default RecentProfiles
+8
View File
@@ -54,6 +54,14 @@ function NavMenu (props) {
Tokens
</NavLink>
<NavLink
className={currentPath === '/profile/recent' ? 'nav-link-active' : 'nav-link-inactive'}
to='/profile/recent'
onClick={handleClickEvent}
>
Profiles
</NavLink>
<NavLink
className={currentPath === '/wallet' ? 'nav-link-active' : 'nav-link-inactive'}
to='/wallet'
+3 -1
View File
@@ -12,7 +12,9 @@ const config = {
// Backup Info that goes into the Footer.
ghPagesUrl: 'https://permissionless-software-foundation.github.io/react-bootstrap-web3-spa/',
ghRepo: 'https://github.com/Permissionless-Software-Foundation/bch-wallet-web3-spa',
radicleUrl: 'https://app.radicle.network/seeds/maple.radicle.garden/rad:git:hnrkd5cjwwb5tzx37hq9uqm5ubon7ee468xcy/remotes/hyyycncbn9qzqmobnhjq9rry6t4mbjiadzjoyhaknzxjcz3cxkpfpc'
radicleUrl: 'https://app.radicle.network/seeds/maple.radicle.garden/rad:git:hnrkd5cjwwb5tzx37hq9uqm5ubon7ee468xcy/remotes/hyyycncbn9qzqmobnhjq9rry6t4mbjiadzjoyhaknzxjcz3cxkpfpc',
backend: 'http://localhost:5021'
}
+27
View File
@@ -0,0 +1,27 @@
/*
HTTP client for the psf-memo-db REST API.
*/
import axios from 'axios'
import config from '../config'
class MemoDb {
constructor () {
this.axios = axios
}
async getRecentProfiles ({ limit = 100, offset = 0 } = {}) {
try {
const result = await this.axios.get(`${config.backend}/profile/recent`, {
params: { limit, offset }
})
return result.data
} catch (err) {
console.error('Error in getRecentProfiles()')
throw err
}
}
}
export default MemoDb