mirror of
https://github.com/Permissionless-Software-Foundation/psf-memo-client.git
synced 2026-09-21 16:52:02 -07:00
Added profiles view
This commit is contained in:
@@ -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
|
||||
@@ -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
@@ -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'
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user