mirror of
https://github.com/Permissionless-Software-Foundation/psf-memo-client.git
synced 2026-09-21 16:52:02 -07:00
151 lines
4.1 KiB
JavaScript
151 lines
4.1 KiB
JavaScript
/*
|
||
Display the most recent Memo posts from psf-memo-db.
|
||
*/
|
||
|
||
// Global npm libraries
|
||
import React, { useState, useEffect } from 'react'
|
||
import { Container, Row, Col, Spinner, Button } from 'react-bootstrap'
|
||
|
||
// Local libraries
|
||
import MemoDb from '../../../services/memo-db'
|
||
import PostFeedItem from '../../post-feed/post-feed-item'
|
||
import PostThreadModal from '../../post-thread-modal'
|
||
import {
|
||
collectPostAddrs,
|
||
loadThreadProfiles
|
||
} from '../../post-thread-modal/thread-profiles'
|
||
import '../../../App.css'
|
||
import '../../post-feed/post-feed.css'
|
||
|
||
const PAGE_SIZE = 100
|
||
|
||
function RecentPosts () {
|
||
const [loading, setLoading] = useState(true)
|
||
const [error, setError] = useState(null)
|
||
const [posts, setPosts] = useState([])
|
||
const [profiles, setProfiles] = useState({})
|
||
const [pagination, setPagination] = useState(null)
|
||
const [offset, setOffset] = useState(0)
|
||
const [threadTxid, setThreadTxid] = useState(null)
|
||
const [showThreadModal, setShowThreadModal] = useState(false)
|
||
|
||
const openThread = (txid) => {
|
||
setThreadTxid(txid)
|
||
setShowThreadModal(true)
|
||
}
|
||
|
||
const closeThread = () => {
|
||
setShowThreadModal(false)
|
||
setThreadTxid(null)
|
||
}
|
||
|
||
useEffect(() => {
|
||
const loadPosts = async () => {
|
||
setLoading(true)
|
||
setError(null)
|
||
setProfiles({})
|
||
|
||
try {
|
||
const memoDb = new MemoDb()
|
||
const data = await memoDb.getRecentPosts({ limit: PAGE_SIZE, offset })
|
||
const loadedPosts = data.posts || []
|
||
const addrs = collectPostAddrs(loadedPosts)
|
||
const profileMap = await loadThreadProfiles(addrs, memoDb)
|
||
|
||
setPosts(loadedPosts)
|
||
setProfiles(profileMap)
|
||
setPagination(data.pagination || null)
|
||
} catch (err) {
|
||
setError(err.message || 'Failed to load recent posts')
|
||
setPosts([])
|
||
setProfiles({})
|
||
setPagination(null)
|
||
}
|
||
|
||
setLoading(false)
|
||
}
|
||
|
||
loadPosts()
|
||
}, [offset])
|
||
|
||
const canGoBack = offset > 0
|
||
const canGoNext = pagination?.hasMore ?? false
|
||
|
||
const handlePrevious = () => {
|
||
setOffset((prev) => Math.max(0, prev - PAGE_SIZE))
|
||
}
|
||
|
||
const handleNext = () => {
|
||
setOffset((prev) => prev + PAGE_SIZE)
|
||
}
|
||
|
||
return (
|
||
<Container>
|
||
<Row>
|
||
<Col>
|
||
<h1 className='mt-4'>Recent Posts</h1>
|
||
{pagination && posts.length > 0 && (
|
||
<p className='text-muted'>
|
||
Showing {pagination.offset + 1}–{pagination.offset + posts.length} of {pagination.total} posts
|
||
</p>
|
||
)}
|
||
{pagination && posts.length === 0 && (
|
||
<p className='text-muted'>No posts on this page.</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 && posts.length > 0 && (
|
||
<div className='posts-feed mt-3'>
|
||
{posts.map((post) => (
|
||
<PostFeedItem
|
||
key={post.txid}
|
||
post={post}
|
||
profiles={profiles}
|
||
onReplyClick={() => openThread(post.txid)}
|
||
showFooterMeta
|
||
/>
|
||
))}
|
||
</div>
|
||
)}
|
||
|
||
{!loading && !error && (pagination || offset > 0) && (
|
||
<div className='d-flex justify-content-between mt-3 mb-4'>
|
||
<Button
|
||
variant='outline-primary'
|
||
onClick={handlePrevious}
|
||
disabled={!canGoBack}
|
||
>
|
||
Previous
|
||
</Button>
|
||
<Button
|
||
variant='outline-primary'
|
||
onClick={handleNext}
|
||
disabled={!canGoNext}
|
||
>
|
||
Next
|
||
</Button>
|
||
</div>
|
||
)}
|
||
</Col>
|
||
</Row>
|
||
|
||
<PostThreadModal
|
||
show={showThreadModal}
|
||
txid={threadTxid}
|
||
onHide={closeThread}
|
||
/>
|
||
</Container>
|
||
)
|
||
}
|
||
|
||
export default RecentPosts
|