messages implemented
This commit is contained in:
127
resources/js/Pages/Community/LatestCommentsPage.jsx
Normal file
127
resources/js/Pages/Community/LatestCommentsPage.jsx
Normal file
@@ -0,0 +1,127 @@
|
||||
import React, { useState, useEffect, useCallback, useRef } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import CommentsFeed from '../../components/comments/CommentsFeed'
|
||||
|
||||
const FILTER_TABS = [
|
||||
{ key: 'all', label: 'All' },
|
||||
{ key: 'following', label: 'Following', authRequired: true },
|
||||
{ key: 'mine', label: 'My Comments', authRequired: true },
|
||||
]
|
||||
|
||||
function LatestCommentsPage({ initialComments = [], initialMeta = {}, isAuthenticated = false }) {
|
||||
const [activeFilter, setActiveFilter] = useState('all')
|
||||
const [comments, setComments] = useState(initialComments)
|
||||
const [meta, setMeta] = useState(initialMeta)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [error, setError] = useState(null)
|
||||
|
||||
// Track if we've moved off the initial server-rendered data
|
||||
const initialized = useRef(false)
|
||||
|
||||
const fetchComments = useCallback(async (filter, page = 1) => {
|
||||
setLoading(true)
|
||||
setError(null)
|
||||
try {
|
||||
const url = `/api/comments/latest?type=${encodeURIComponent(filter)}&page=${page}`
|
||||
const res = await fetch(url, {
|
||||
headers: { 'Accept': 'application/json', 'X-Requested-With': 'XMLHttpRequest' },
|
||||
credentials: 'same-origin',
|
||||
})
|
||||
|
||||
if (res.status === 401) {
|
||||
setError('Please log in to view this feed.')
|
||||
setComments([])
|
||||
setMeta({})
|
||||
return
|
||||
}
|
||||
|
||||
if (! res.ok) {
|
||||
setError('Failed to load comments. Please try again.')
|
||||
return
|
||||
}
|
||||
|
||||
const json = await res.json()
|
||||
setComments(json.data ?? [])
|
||||
setMeta(json.meta ?? {})
|
||||
} catch {
|
||||
setError('Network error. Please try again.')
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}, [])
|
||||
|
||||
const handleFilterChange = (key) => {
|
||||
if (key === activeFilter) return
|
||||
setActiveFilter(key)
|
||||
initialized.current = true
|
||||
fetchComments(key, 1)
|
||||
}
|
||||
|
||||
const handlePageChange = (page) => {
|
||||
initialized.current = true
|
||||
fetchComments(activeFilter, page)
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' })
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="px-4 pt-10 pb-20 sm:px-6 lg:px-8 max-w-5xl mx-auto">
|
||||
{/* Page header */}
|
||||
<div className="mb-7">
|
||||
<p className="text-xs font-semibold uppercase tracking-widest text-white/30 mb-1">Community</p>
|
||||
<h1 className="text-3xl font-bold text-white leading-tight">Latest Comments</h1>
|
||||
<p className="mt-1 text-sm text-white/50">Most recent artwork comments from the community.</p>
|
||||
</div>
|
||||
|
||||
{/* Filter tabs — pill style */}
|
||||
<div className="flex items-center gap-2 mb-6">
|
||||
{FILTER_TABS.map((tab) => {
|
||||
const disabled = tab.authRequired && !isAuthenticated
|
||||
const active = activeFilter === tab.key
|
||||
|
||||
return (
|
||||
<button
|
||||
key={tab.key}
|
||||
onClick={() => !disabled && handleFilterChange(tab.key)}
|
||||
disabled={disabled}
|
||||
aria-current={active ? 'page' : undefined}
|
||||
title={disabled ? 'Log in to use this filter' : undefined}
|
||||
className={[
|
||||
'px-4 py-1.5 rounded-full text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-500',
|
||||
active
|
||||
? 'bg-sky-600/25 text-sky-300 ring-1 ring-sky-500/40'
|
||||
: 'text-white/50 hover:text-white/80 hover:bg-white/[0.06]',
|
||||
disabled && 'opacity-30 cursor-not-allowed',
|
||||
].filter(Boolean).join(' ')}
|
||||
>
|
||||
{tab.label}
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* Feed content */}
|
||||
<CommentsFeed
|
||||
comments={comments}
|
||||
meta={meta}
|
||||
loading={loading}
|
||||
error={error}
|
||||
onPageChange={handlePageChange}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Auto-mount when the Blade view provides #latest-comments-root
|
||||
const mountEl = document.getElementById('latest-comments-root')
|
||||
if (mountEl) {
|
||||
let props = {}
|
||||
try {
|
||||
const propsEl = document.getElementById('latest-comments-props')
|
||||
props = propsEl ? JSON.parse(propsEl.textContent || '{}') : {}
|
||||
} catch {
|
||||
props = {}
|
||||
}
|
||||
createRoot(mountEl).render(<LatestCommentsPage {...props} />)
|
||||
}
|
||||
|
||||
export default LatestCommentsPage
|
||||
Reference in New Issue
Block a user