messages implemented

This commit is contained in:
2026-02-26 21:12:32 +01:00
parent d0aefc5ddc
commit 15b7b77d20
168 changed files with 14728 additions and 6786 deletions

View 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