Fix engagement for news posts (guid-based)

News posts have id=0, causing early returns in engagement handlers.
Fixed by checking both postId and post?.guid:
- handleTruthVote
- handleLike
- refreshComments (fetch comments)
- Initial data fetch (recordView, fetchTruth)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Your Name 2026-01-31 20:17:49 +00:00
parent fca617681a
commit 53b6426a34
2 changed files with 20 additions and 11 deletions

View File

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

View File

@ -559,7 +559,7 @@ export default function PostDetailModal({ post, onClose, onPostUpdated, onPostDe
shares: post?.share_count || 0, comments: post?.comment_count || 0, shares: post?.share_count || 0, comments: post?.comment_count || 0,
}); });
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, userVote: null });
const [authorAvatar, setAuthorAvatar] = useState(""); const [authorAvatar, setAuthorAvatar] = useState("");
const [commenterAvatars, setCommenterAvatars] = useState({}); const [commenterAvatars, setCommenterAvatars] = useState({});
const [comments, setComments] = useState([]); const [comments, setComments] = useState([]);
@ -623,12 +623,12 @@ export default function PostDetailModal({ post, onClose, onPostUpdated, onPostDe
// Initial data fetch // Initial data fetch
useEffect(() => { useEffect(() => {
if (!postId) return; if (!postId && !post?.guid) return;
recordPostView(postId, post?.guid, post?.app_id).then(res => { recordPostView(postId, post?.guid, post?.app_id).then(res => {
if (res?.counts) setCounts(c => ({ ...c, views: res.counts.view_count || c.views, likes: res.counts.like_count || c.likes })); if (res?.counts) setCounts(c => ({ ...c, views: res.counts.view_count || c.views, likes: res.counts.like_count || c.likes }));
}); });
fetchPostLikeState(postId).then(res => { if (res?.liked !== undefined) setLiked(res.liked); }); fetchPostLikeState(postId).then(res => { if (res?.liked !== undefined) setLiked(res.liked); });
fetchPostTruth(postId).then(res => { if (res) setTruth({ score: res.truth_score ?? res.user_score ?? 50, count: res.vote_count ?? 0 }); }); fetchPostTruth(postId, post?.guid, post?.app_id).then(res => { if (res) setTruth({ score: res.truth_score ?? res.user_score ?? 50, count: res.vote_count ?? 0, userVote: res.user_vote || null }); });
}, [postId]); }, [postId]);
// Fetch full post data from API to get image_urls (map markers don't have it) // Fetch full post data from API to get image_urls (map markers don't have it)
@ -666,8 +666,8 @@ export default function PostDetailModal({ post, onClose, onPostUpdated, onPostDe
// Fetch comments // Fetch comments
const refreshComments = useCallback(async (autoOpen = false) => { const refreshComments = useCallback(async (autoOpen = false) => {
if (!postId) return; if (!postId && !post?.guid) return;
const items = await fetchPostComments(postId, 50); const items = await fetchPostComments(postId, 50, post?.guid, post?.app_id);
if (Array.isArray(items)) { if (Array.isArray(items)) {
setComments(items); setComments(items);
setCounts(c => ({ ...c, comments: items.length })); setCounts(c => ({ ...c, comments: items.length }));
@ -686,7 +686,7 @@ export default function PostDetailModal({ post, onClose, onPostUpdated, onPostDe
// Handlers // Handlers
const handleLike = useCallback(async () => { const handleLike = useCallback(async () => {
if (!postId) return; if (!postId && !post?.guid) return;
const newLiked = !liked; const newLiked = !liked;
setLiked(newLiked); setLiked(newLiked);
setCounts(c => ({ ...c, likes: c.likes + (newLiked ? 1 : -1) })); setCounts(c => ({ ...c, likes: c.likes + (newLiked ? 1 : -1) }));
@ -716,13 +716,14 @@ export default function PostDetailModal({ post, onClose, onPostUpdated, onPostDe
}, [postId, post?.title, post?.guid, post?.app_id]); }, [postId, post?.title, post?.guid, post?.app_id]);
const handleTruthVote = useCallback(async (vote) => { const handleTruthVote = useCallback(async (vote) => {
if (!postId) return; if (!postId && !post?.guid) return;
if (!token) { if (!token) {
window.dispatchEvent(new CustomEvent("sociowire:auth-required")); window.dispatchEvent(new CustomEvent("sociowire:auth-required"));
return; return;
} }
// User can switch their vote by clicking the opposite button
const res = await votePostTruth(postId, vote, post?.guid, post?.app_id); const res = await votePostTruth(postId, vote, post?.guid, post?.app_id);
if (res) setTruth({ score: res.truth_score ?? res.user_score ?? truth.score, count: res.vote_count ?? truth.count }); if (res) setTruth({ score: res.truth_score ?? res.user_score ?? truth.score, count: res.vote_count ?? truth.count, userVote: res.user_vote || vote });
}, [postId, truth, token, post?.guid, post?.app_id]); }, [postId, truth, token, post?.guid, post?.app_id]);
const handleComment = useCallback(async () => { const handleComment = useCallback(async () => {
@ -1057,7 +1058,11 @@ export default function PostDetailModal({ post, onClose, onPostUpdated, onPostDe
{/* Mini Truth Score */} {/* Mini Truth Score */}
<div className="flex items-center gap-1.5 px-2 py-1 rounded-lg bg-themed-elevated"> <div className="flex items-center gap-1.5 px-2 py-1 rounded-lg bg-themed-elevated">
<motion.button whileHover={{ scale: 1.1 }} whileTap={{ scale: 0.9 }} onClick={() => handleTruthVote("false")} <motion.button whileHover={{ scale: 1.1 }} whileTap={{ scale: 0.9 }} onClick={() => handleTruthVote("false")}
className="w-6 h-6 rounded-full bg-red-500/20 text-red-400 flex items-center justify-center hover:bg-red-500/30 text-xs"> className={`w-6 h-6 rounded-full flex items-center justify-center text-xs transition-all ${
truth.userVote === "false"
? "bg-red-500 text-white ring-2 ring-red-300"
: "bg-red-500/20 text-red-400 hover:bg-red-500/30"
}`}>
<i className="fa-solid fa-thumbs-down" /> <i className="fa-solid fa-thumbs-down" />
</motion.button> </motion.button>
<div className="flex items-center gap-1 min-w-[50px]"> <div className="flex items-center gap-1 min-w-[50px]">
@ -1070,7 +1075,11 @@ export default function PostDetailModal({ post, onClose, onPostUpdated, onPostDe
</span> </span>
</div> </div>
<motion.button whileHover={{ scale: 1.1 }} whileTap={{ scale: 0.9 }} onClick={() => handleTruthVote("true")} <motion.button whileHover={{ scale: 1.1 }} whileTap={{ scale: 0.9 }} onClick={() => handleTruthVote("true")}
className="w-6 h-6 rounded-full bg-emerald-500/20 text-emerald-400 flex items-center justify-center hover:bg-emerald-500/30 text-xs"> className={`w-6 h-6 rounded-full flex items-center justify-center text-xs transition-all ${
truth.userVote === "true"
? "bg-emerald-500 text-white ring-2 ring-emerald-300"
: "bg-emerald-500/20 text-emerald-400 hover:bg-emerald-500/30"
}`}>
<i className="fa-solid fa-thumbs-up" /> <i className="fa-solid fa-thumbs-up" />
</motion.button> </motion.button>
</div> </div>