WIP: Add timestamp to comments and fix frontend errors

This commit is contained in:
Mistral Vibe
2026-03-29 22:06:36 +02:00
parent f7a07ba05e
commit a8aba72b3a
6 changed files with 139 additions and 6 deletions

View File

@@ -14,6 +14,7 @@ interface SongComment {
author_id: string;
author_name: string;
created_at: string;
timestamp: number; // Timestamp in seconds
}
export function SongPage() {
@@ -37,7 +38,7 @@ export function SongPage() {
enabled: !!activeVersion,
});
const { isPlaying, currentTime, play, pause, seekTo } = useWaveform(waveformRef, {
const { isPlaying, currentTime, play, pause, seekTo, addMarker, clearMarkers } = useWaveform(waveformRef, {
url: activeVersion ? `/api/v1/versions/${activeVersion}/stream` : null,
peaksUrl: activeVersion ? `/api/v1/versions/${activeVersion}/waveform` : null,
});
@@ -59,12 +60,40 @@ export function SongPage() {
return () => window.removeEventListener("keydown", handleKeyDown);
}, [isPlaying, play, pause]);
const { data: comments } = useQuery({
const { data: comments } = useQuery<SongComment[]>({
queryKey: ["comments", songId],
queryFn: () => api.get<SongComment[]>(`/songs/${songId}/comments`),
enabled: !!songId,
});
// Scroll to comment when a marker is clicked
const scrollToComment = (commentId: string) => {
const commentElement = document.getElementById(`comment-${commentId}`);
if (commentElement) {
commentElement.scrollIntoView({ behavior: "smooth", block: "center" });
commentElement.style.backgroundColor = "var(--accent-bg)";
setTimeout(() => {
commentElement.style.backgroundColor = "var(--bg-subtle)";
}, 2000);
}
};
useEffect(() => {
if (comments) {
clearMarkers();
comments.forEach((comment) => {
if (comment.timestamp !== undefined && comment.timestamp !== null) {
addMarker({
id: comment.id,
time: comment.timestamp,
onClick: () => scrollToComment(comment.id),
icon: "https://via.placeholder.com/20", // Replace with actual user icon URL
});
}
});
}
}, [comments, addMarker, clearMarkers]);
const addCommentMutation = useMutation({
mutationFn: (body: string) => api.post(`/songs/${songId}/comments`, { body }),
onSuccess: () => {
@@ -131,6 +160,13 @@ export function SongPage() {
</div>
</div>
{/* Current Play Time Display */}
<div style={{ marginBottom: 16, textAlign: "center" }}>
<span style={{ color: "var(--text-muted)", fontSize: 14, fontFamily: "monospace" }}>
Current Time: {formatTime(currentTime)}
</span>
</div>
{/* Annotations */}
<div style={{ display: "grid", gap: 8, marginBottom: 32 }}>
{annotations?.map((a) => (
@@ -144,7 +180,7 @@ export function SongPage() {
<div style={{ display: "grid", gap: 8, marginBottom: 16 }}>
{comments?.map((c) => (
<div key={c.id} style={{ background: "var(--bg-subtle)", border: "1px solid var(--border)", borderRadius: 8, padding: "12px 16px" }}>
<div id={`comment-${c.id}`} key={c.id} style={{ background: "var(--bg-subtle)", border: "1px solid var(--border)", borderRadius: 8, padding: "12px 16px" }}>
<div style={{ display: "flex", justifyContent: "space-between", marginBottom: 6 }}>
<span style={{ fontWeight: 600, fontSize: 13, color: "var(--text)" }}>{c.author_name}</span>
<div style={{ display: "flex", alignItems: "center", gap: 10 }}>
@@ -157,6 +193,16 @@ export function SongPage() {
</button>
</div>
</div>
{c.timestamp !== undefined && c.timestamp !== null && (
<div style={{ display: "flex", gap: 8, marginBottom: 6 }}>
<button
onClick={() => seekTo(c.timestamp)}
style={{ background: "var(--accent-bg)", border: "1px solid var(--accent)", borderRadius: 4, color: "var(--accent)", cursor: "pointer", fontSize: 10, padding: "2px 8px", fontFamily: "monospace" }}
>
{formatTime(c.timestamp)}
</button>
</div>
)}
<p style={{ margin: 0, fontSize: 13, color: "var(--text)", lineHeight: 1.5 }}>{c.body}</p>
</div>
))}