Fix live post removal + add commenter avatars (v0.0.93)

- Always dispatch live-post-deleted when stream ends (removes marker)
- If saved, also dispatch live-post-created with video post
- Add commenter avatar fetching from profile API
- Display profile pictures in live chat comments

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Your Name 2026-01-20 07:33:04 +00:00
parent bacf7d9df3
commit 01272f492b
2 changed files with 31 additions and 8 deletions

View File

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

View File

@ -2,7 +2,7 @@ import { useState, useRef, useEffect, useCallback } from "react";
import { useLiveKit } from "./useLiveKit"; import { useLiveKit } from "./useLiveKit";
import { useDualCamera } from "./useDualCamera"; import { useDualCamera } from "./useDualCamera";
import { useAuth } from "../../auth/AuthContext"; import { useAuth } from "../../auth/AuthContext";
import { fetchPostComments, createPostComment } from "../../api/client"; import { fetchPostComments, createPostComment, fetchProfile } from "../../api/client";
import { CREATE_CATEGORY_MAP, FILTER_MAIN_CATEGORIES } from "../Map/mapConfig"; import { CREATE_CATEGORY_MAP, FILTER_MAIN_CATEGORIES } from "../Map/mapConfig";
const API_BASE = (import.meta?.env?.VITE_API_BASE_URL || "").replace(/\/+$/, ""); const API_BASE = (import.meta?.env?.VITE_API_BASE_URL || "").replace(/\/+$/, "");
@ -32,6 +32,7 @@ export default function LiveKitBroadcast({ coords, onClose, onStreamCreated, ini
const [comments, setComments] = useState([]); const [comments, setComments] = useState([]);
const [commentInput, setCommentInput] = useState(""); const [commentInput, setCommentInput] = useState("");
const [showComments, setShowComments] = useState(true); const [showComments, setShowComments] = useState(true);
const [commenterAvatars, setCommenterAvatars] = useState({});
const commentsEndRef = useRef(null); const commentsEndRef = useRef(null);
const [liveCoords] = useState(() => { const [liveCoords] = useState(() => {
@ -144,6 +145,21 @@ export default function LiveKitBroadcast({ coords, onClose, onStreamCreated, ini
commentsEndRef.current?.scrollIntoView({ behavior: "smooth" }); commentsEndRef.current?.scrollIntoView({ behavior: "smooth" });
}, [comments]); }, [comments]);
// Fetch commenter avatars
useEffect(() => {
if (!comments.length) return;
const usernames = [...new Set(comments.map(c => c.username).filter(Boolean))];
const toFetch = usernames.filter(u => !commenterAvatars[u]);
if (!toFetch.length) return;
toFetch.forEach(uname => {
fetchProfile(uname).then(res => {
if (res?.avatar_url) {
setCommenterAvatars(prev => ({ ...prev, [uname]: res.avatar_url }));
}
});
});
}, [comments]);
const formatTime = (seconds) => { const formatTime = (seconds) => {
const mins = Math.floor(seconds / 60); const mins = Math.floor(seconds / 60);
const secs = seconds % 60; const secs = seconds % 60;
@ -306,12 +322,15 @@ export default function LiveKitBroadcast({ coords, onClose, onStreamCreated, ini
comments: [], comments: [],
}), }),
}); });
if (res.ok && save) { // Always delete the live post marker first
const post = await res.json();
window.dispatchEvent(new CustomEvent("live-post-updated", { detail: post }));
}
if (res.ok && !save) {
window.dispatchEvent(new CustomEvent("live-post-deleted", { detail: { post_id: livePostId } })); window.dispatchEvent(new CustomEvent("live-post-deleted", { detail: { post_id: livePostId } }));
if (res.ok && save) {
// If saved, add the video post back
const post = await res.json();
if (post?.id) {
window.dispatchEvent(new CustomEvent("live-post-created", { detail: post }));
}
} }
} catch (err) { } catch (err) {
console.error("[LiveKit] Failed to finalize live post:", err); console.error("[LiveKit] Failed to finalize live post:", err);
@ -484,8 +503,12 @@ export default function LiveKitBroadcast({ coords, onClose, onStreamCreated, ini
key={c.id || idx} key={c.id || idx}
className={`flex items-start gap-2 px-3 py-2 rounded-xl bg-black/50 backdrop-blur-sm ${c._pending ? 'opacity-60' : ''}`} className={`flex items-start gap-2 px-3 py-2 rounded-xl bg-black/50 backdrop-blur-sm ${c._pending ? 'opacity-60' : ''}`}
> >
<div className="w-8 h-8 rounded-full bg-gradient-to-br from-cyan-500 to-blue-500 flex items-center justify-center text-white text-xs font-bold flex-shrink-0"> <div className="w-8 h-8 rounded-full bg-gradient-to-br from-cyan-500 to-blue-500 flex items-center justify-center text-white text-xs font-bold flex-shrink-0 overflow-hidden">
{(c.username || "?")[0]?.toUpperCase()} {commenterAvatars[c.username] ? (
<img src={commenterAvatars[c.username]} alt="" className="w-full h-full object-cover" />
) : (
(c.username || "?")[0]?.toUpperCase()
)}
</div> </div>
<div className="min-w-0"> <div className="min-w-0">
<span className="text-cyan-400 text-sm font-semibold">{c.username || "User"}</span> <span className="text-cyan-400 text-sm font-semibold">{c.username || "User"}</span>