Add category icons & author avatars to PostDetailModal (v0.0.88)

- Category icon shown when post has no image (with gradient background)
- Author avatar fetched from profile API and displayed
- Logo position adjusted (6px down, 1px left)
- Comments auto-open when existing comments present

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Your Name 2026-01-20 06:45:06 +00:00
parent 68c1fce064
commit 0119174969
3 changed files with 43 additions and 8 deletions

View File

@ -1,7 +1,7 @@
{ {
"name": "sociowire-frontend", "name": "sociowire-frontend",
"private": true, "private": true,
"version": "0.0.87", "version": "0.0.88",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite --host 0.0.0.0", "dev": "vite --host 0.0.0.0",

View File

@ -512,7 +512,7 @@ export default function TopBarNew({ theme = "dark", onChangeTheme, onIslandsClic
transition={{ duration: 2, repeat: Infinity }} transition={{ duration: 2, repeat: Infinity }}
/> />
<div className="relative w-8 h-8 sm:w-10 sm:h-10 md:w-11 md:h-11 lg:w-12 lg:h-12 rounded-lg sm:rounded-xl bg-gradient-to-br from-blue-500 via-cyan-500 to-emerald-500 shadow-lg shadow-cyan-500/25 flex items-center justify-center flex-shrink-0"> <div className="relative w-8 h-8 sm:w-10 sm:h-10 md:w-11 md:h-11 lg:w-12 lg:h-12 rounded-lg sm:rounded-xl bg-gradient-to-br from-blue-500 via-cyan-500 to-emerald-500 shadow-lg shadow-cyan-500/25 flex items-center justify-center flex-shrink-0">
<img src="/logo.png" alt="SocioWire" className="w-[130%] h-[130%] max-w-none object-cover" style={{ marginTop: '5px', marginLeft: '2px' }} /> <img src="/logo.png" alt="SocioWire" className="w-[130%] h-[130%] max-w-none object-cover" style={{ marginTop: '6px', marginLeft: '1px' }} />
</div> </div>
</div> </div>
<div className="flex flex-col min-w-0"> <div className="flex flex-col min-w-0">

View File

@ -16,6 +16,7 @@ import {
editPost, editPost,
deletePost, deletePost,
updatePostVisibility, updatePostVisibility,
fetchProfile,
} from "../../api/client"; } from "../../api/client";
// ============================================================================ // ============================================================================
@ -44,6 +45,25 @@ function formatCount(n) {
return num.toString(); return num.toString();
} }
function categoryIconClass(post) {
const c = (post?.category || "NEWS").toString().toUpperCase();
switch (c) {
case "EVENT":
case "EVENTS":
return "fa-regular fa-calendar";
case "FRIENDS":
return "fa-solid fa-user-group";
case "MARKET":
return "fa-solid fa-store";
case "CAMERA":
return "fa-solid fa-video";
case "LIVE":
return "fa-solid fa-tower-broadcast";
default:
return "fa-regular fa-newspaper";
}
}
function relativeTime(dateStr, t) { function relativeTime(dateStr, t) {
if (!dateStr) return ""; if (!dateStr) return "";
const d = new Date(dateStr); const d = new Date(dateStr);
@ -362,13 +382,14 @@ function HeroStory({ post, gallery, imageIndex, setImageIndex }) {
); );
} }
// No image - show gradient with author // No image - show gradient with category icon
return ( return (
<div className={`absolute inset-0 bg-gradient-to-br ${config.gradient}`}> <div className={`absolute inset-0 bg-gradient-to-br ${config.gradient}`}>
<div className="absolute inset-0 flex items-center justify-center"> <div className="absolute inset-0 flex flex-col items-center justify-center gap-3">
<div className="w-24 h-24 rounded-full bg-white/20 backdrop-blur-sm flex items-center justify-center text-white font-bold text-4xl shadow-lg"> <div className="w-20 h-20 rounded-2xl bg-white/20 backdrop-blur-sm flex items-center justify-center text-white shadow-lg">
{author.charAt(0).toUpperCase()} <i className={`${categoryIconClass(post)} text-4xl`} />
</div> </div>
<span className="text-white/80 text-sm font-medium uppercase tracking-wider">{post?.category || "News"}</span>
</div> </div>
</div> </div>
); );
@ -464,6 +485,7 @@ export default function PostDetailModal({ post, onClose, onPostUpdated, onPostDe
}); });
const [liked, setLiked] = useState(false); const [liked, setLiked] = useState(false);
const [truth, setTruth] = useState({ score: 50, count: 0 }); const [truth, setTruth] = useState({ score: 50, count: 0 });
const [authorAvatar, setAuthorAvatar] = useState("");
const [comments, setComments] = useState([]); const [comments, setComments] = useState([]);
const [commentInput, setCommentInput] = useState(""); const [commentInput, setCommentInput] = useState("");
const [commentSending, setCommentSending] = useState(false); const [commentSending, setCommentSending] = useState(false);
@ -504,6 +526,15 @@ export default function PostDetailModal({ post, onClose, onPostUpdated, onPostDe
fetchPostTruth(postId).then(res => { if (res) setTruth({ score: res.truth_score ?? res.user_score ?? 50, count: res.vote_count ?? 0 }); }); fetchPostTruth(postId).then(res => { if (res) setTruth({ score: res.truth_score ?? res.user_score ?? 50, count: res.vote_count ?? 0 }); });
}, [postId]); }, [postId]);
// Fetch author avatar
useEffect(() => {
const authorName = post?.author || post?.username;
if (!authorName) return;
fetchProfile(authorName).then(res => {
if (res?.avatar_url) setAuthorAvatar(res.avatar_url);
});
}, [post?.author, post?.username]);
// Fetch comments // Fetch comments
const refreshComments = useCallback(async (autoOpen = false) => { const refreshComments = useCallback(async (autoOpen = false) => {
if (!postId) return; if (!postId) return;
@ -659,8 +690,12 @@ export default function PostDetailModal({ post, onClose, onPostUpdated, onPostDe
{/* Author Row */} {/* Author Row */}
<div className="flex items-center gap-3 mb-4"> <div className="flex items-center gap-3 mb-4">
<div className={`w-11 h-11 sm:w-12 sm:h-12 rounded-full bg-gradient-to-br ${config.gradient} flex items-center justify-center <div className={`w-11 h-11 sm:w-12 sm:h-12 rounded-full bg-gradient-to-br ${config.gradient} flex items-center justify-center
text-white font-bold text-base sm:text-lg shadow-lg`}> text-white font-bold text-base sm:text-lg shadow-lg overflow-hidden`}>
{author.charAt(0).toUpperCase()} {authorAvatar ? (
<img src={authorAvatar} alt={author} className="w-full h-full object-cover" />
) : (
author.charAt(0).toUpperCase()
)}
</div> </div>
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<p className="font-semibold text-themed-primary truncate">@{author}</p> <p className="font-semibold text-themed-primary truncate">@{author}</p>