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:
parent
68c1fce064
commit
0119174969
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "sociowire-frontend",
|
||||
"private": true,
|
||||
"version": "0.0.87",
|
||||
"version": "0.0.88",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --host 0.0.0.0",
|
||||
|
|
|
|||
|
|
@ -512,7 +512,7 @@ export default function TopBarNew({ theme = "dark", onChangeTheme, onIslandsClic
|
|||
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">
|
||||
<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 className="flex flex-col min-w-0">
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import {
|
|||
editPost,
|
||||
deletePost,
|
||||
updatePostVisibility,
|
||||
fetchProfile,
|
||||
} from "../../api/client";
|
||||
|
||||
// ============================================================================
|
||||
|
|
@ -44,6 +45,25 @@ function formatCount(n) {
|
|||
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) {
|
||||
if (!dateStr) return "";
|
||||
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 (
|
||||
<div className={`absolute inset-0 bg-gradient-to-br ${config.gradient}`}>
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<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">
|
||||
{author.charAt(0).toUpperCase()}
|
||||
<div className="absolute inset-0 flex flex-col items-center justify-center gap-3">
|
||||
<div className="w-20 h-20 rounded-2xl bg-white/20 backdrop-blur-sm flex items-center justify-center text-white shadow-lg">
|
||||
<i className={`${categoryIconClass(post)} text-4xl`} />
|
||||
</div>
|
||||
<span className="text-white/80 text-sm font-medium uppercase tracking-wider">{post?.category || "News"}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -464,6 +485,7 @@ export default function PostDetailModal({ post, onClose, onPostUpdated, onPostDe
|
|||
});
|
||||
const [liked, setLiked] = useState(false);
|
||||
const [truth, setTruth] = useState({ score: 50, count: 0 });
|
||||
const [authorAvatar, setAuthorAvatar] = useState("");
|
||||
const [comments, setComments] = useState([]);
|
||||
const [commentInput, setCommentInput] = useState("");
|
||||
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 }); });
|
||||
}, [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
|
||||
const refreshComments = useCallback(async (autoOpen = false) => {
|
||||
if (!postId) return;
|
||||
|
|
@ -659,8 +690,12 @@ export default function PostDetailModal({ post, onClose, onPostUpdated, onPostDe
|
|||
{/* Author Row */}
|
||||
<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
|
||||
text-white font-bold text-base sm:text-lg shadow-lg`}>
|
||||
{author.charAt(0).toUpperCase()}
|
||||
text-white font-bold text-base sm:text-lg shadow-lg overflow-hidden`}>
|
||||
{authorAvatar ? (
|
||||
<img src={authorAvatar} alt={author} className="w-full h-full object-cover" />
|
||||
) : (
|
||||
author.charAt(0).toUpperCase()
|
||||
)}
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="font-semibold text-themed-primary truncate">@{author}</p>
|
||||
|
|
|
|||
Loading…
Reference in New Issue