Files
SkinbaseNova/resources/js/components/profile/tabs/TabActivity.jsx
2026-03-20 21:17:26 +01:00

156 lines
5.9 KiB
JavaScript

import React, { useRef, useState } from 'react'
import LevelBadge from '../../xp/LevelBadge'
const DEFAULT_AVATAR = 'https://files.skinbase.org/default/avatar_default.webp'
function CommentItem({ comment }) {
return (
<div className="flex gap-3 py-4 border-b border-white/5 last:border-0">
<a href={comment.author_profile_url} className="shrink-0 mt-0.5">
<img
src={comment.author_avatar || DEFAULT_AVATAR}
alt={comment.author_name}
className="w-9 h-9 rounded-xl object-cover ring-1 ring-white/10"
onError={(e) => { e.target.src = DEFAULT_AVATAR }}
loading="lazy"
/>
</a>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2 mb-1">
<a
href={comment.author_profile_url}
className="text-sm font-semibold text-slate-200 hover:text-white transition-colors"
>
{comment.author_name}
</a>
<LevelBadge level={comment.author_level} rank={comment.author_rank} compact />
<span className="text-slate-600 text-xs ml-auto whitespace-nowrap">
{(() => {
try {
const d = new Date(comment.created_at)
const diff = Date.now() - d.getTime()
const mins = Math.floor(diff / 60000)
if (mins < 1) return 'just now'
if (mins < 60) return `${mins}m ago`
const hrs = Math.floor(mins / 60)
if (hrs < 24) return `${hrs}h ago`
const days = Math.floor(hrs / 24)
if (days < 30) return `${days}d ago`
return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })
} catch { return '' }
})()}
</span>
</div>
<p className="text-sm text-slate-400 leading-relaxed break-words whitespace-pre-line">
{comment.body}
</p>
{comment.author_signature && (
<p className="text-xs text-slate-600 mt-2 italic border-t border-white/5 pt-1 truncate">
{comment.author_signature}
</p>
)}
</div>
</div>
)
}
/**
* TabActivity
* Profile comments list + comment form for authenticated visitors.
* Also acts as "Activity" tab.
*/
export default function TabActivity({ profileComments, user, isOwner, isLoggedIn }) {
const uname = user.username || user.name
const formRef = useRef(null)
const [submitted, setSubmitted] = useState(false)
return (
<div
id="tabpanel-activity"
role="tabpanel"
aria-labelledby="tab-activity"
className="pt-6 max-w-2xl"
>
<h2 className="text-xs font-semibold uppercase tracking-widest text-slate-500 mb-4 flex items-center gap-2">
<i className="fa-solid fa-comments text-orange-400 fa-fw" />
Comments
{profileComments?.length > 0 && (
<span className="ml-1 px-1.5 py-0.5 rounded bg-white/5 text-slate-400 font-normal text-[11px]">
{profileComments.length}
</span>
)}
</h2>
{/* Comments list */}
<div className="bg-white/4 ring-1 ring-white/10 rounded-2xl p-5 shadow-xl shadow-black/20 mb-5">
{!profileComments?.length ? (
<p className="text-slate-500 text-sm text-center py-8">
No comments yet. Be the first to leave one!
</p>
) : (
<div>
{profileComments.map((comment) => (
<CommentItem key={comment.id} comment={comment} />
))}
</div>
)}
</div>
{/* Comment form */}
{!isOwner && (
<div className="bg-white/4 ring-1 ring-white/10 rounded-2xl p-5 shadow-xl shadow-black/20">
<h3 className="text-xs font-semibold uppercase tracking-widest text-slate-500 mb-4 flex items-center gap-2">
<i className="fa-solid fa-pen text-sky-400 fa-fw" />
Write a Comment
</h3>
{isLoggedIn ? (
submitted ? (
<div className="flex items-center gap-2 text-green-400 text-sm p-3 rounded-xl bg-green-500/10 ring-1 ring-green-500/20">
<i className="fa-solid fa-check fa-fw" />
Your comment has been posted!
</div>
) : (
<form
ref={formRef}
method="POST"
action={`/@${uname.toLowerCase()}/comment`}
onSubmit={() => setSubmitted(false)}
>
<input type="hidden" name="_token" value={
(() => document.querySelector('meta[name="csrf-token"]')?.content ?? '')()
} />
<textarea
name="body"
rows={4}
required
minLength={2}
maxLength={2000}
placeholder={`Write a comment for ${uname}`}
className="w-full bg-white/5 border border-white/10 rounded-xl px-3 py-2.5 text-sm text-slate-200 placeholder:text-slate-600 resize-none focus:outline-none focus:ring-2 focus:ring-sky-400/40 focus:border-sky-400/30 transition-all"
/>
<div className="mt-3 flex justify-end">
<button
type="submit"
className="inline-flex items-center gap-2 px-5 py-2.5 rounded-xl bg-sky-600 hover:bg-sky-500 text-white text-sm font-semibold transition-all shadow-lg shadow-sky-900/30"
>
<i className="fa-solid fa-paper-plane fa-fw" />
Post Comment
</button>
</div>
</form>
)
) : (
<p className="text-sm text-slate-400 text-center py-4">
<a href="/login" className="text-sky-400 hover:text-sky-300 hover:underline transition-colors">
Log in
</a>
{' '}to leave a comment.
</p>
)}
</div>
)}
</div>
)
}