fix(recordings): stop faking duration from file size + render edited content in search

The voice_recordings table has no duration column, but the backend aliased
duration_bytes = size_bytes (file size in bytes) and RecordingCard divided it
by 60 as if it were seconds — a 3MB MP3 rendered as a nonsensical '55924:3'
fake timestamp. Drop the fabricated field and show real file size instead.

Also render edited_content fallback in the analysis search results for
consistency with message cards/detail.
This commit is contained in:
asepharyana
2026-08-02 10:32:58 +07:00
parent 3f199aa70d
commit 25f6609a9f
4 changed files with 9 additions and 9 deletions
@@ -20,7 +20,6 @@ export interface RecordingRow {
upload_error: string | null; upload_error: string | null;
created_at: number; created_at: number;
uploaded_at: number | null; uploaded_at: number | null;
duration_bytes: number;
} }
export interface PaginatedRecordings { export interface PaginatedRecordings {
@@ -69,7 +68,6 @@ export class RecordingsService {
upload_error: pgVoiceRecordingsTable.upload_error, upload_error: pgVoiceRecordingsTable.upload_error,
created_at: pgVoiceRecordingsTable.created_at, created_at: pgVoiceRecordingsTable.created_at,
uploaded_at: pgVoiceRecordingsTable.uploaded_at, uploaded_at: pgVoiceRecordingsTable.uploaded_at,
duration_bytes: pgVoiceRecordingsTable.size_bytes,
}) })
.from(pgVoiceRecordingsTable) .from(pgVoiceRecordingsTable)
.where(where) .where(where)
@@ -96,7 +96,10 @@ export function SearchPanel() {
)} )}
</div> </div>
<p className="text-sm leading-relaxed"> <p className="text-sm leading-relaxed">
{renderMessageContent(msg.content, msg.metadata)} {renderMessageContent(
msg.edited_content ?? msg.content,
msg.metadata,
)}
</p> </p>
{msg.ai_moderation_flags && {msg.ai_moderation_flags &&
msg.ai_moderation_flags !== "[]" && ( msg.ai_moderation_flags !== "[]" && (
@@ -3,6 +3,7 @@
import { Download, Loader2, Pause, Play } from "lucide-react"; import { Download, Loader2, Pause, Play } from "lucide-react";
import { useState } from "react"; import { useState } from "react";
import { GlassCard } from "@/components/glass/card"; import { GlassCard } from "@/components/glass/card";
import { formatBytes } from "@/lib/format";
import type { VoiceRecording } from "@/lib/types"; import type { VoiceRecording } from "@/lib/types";
interface RecordingCardProps { interface RecordingCardProps {
@@ -24,9 +25,9 @@ export function RecordingCard({
onTogglePlay, onTogglePlay,
}: RecordingCardProps) { }: RecordingCardProps) {
const [downloading, setDownloading] = useState(false); const [downloading, setDownloading] = useState(false);
const durationStr = recording.duration_bytes const sizeStr = recording.size_bytes
? `${Math.floor(recording.duration_bytes / 60)}:${String(recording.duration_bytes % 60).padStart(2, "0")}` ? formatBytes(recording.size_bytes)
: "--:--"; : "--";
// Fetch the file (CORS is open on the uploader) → blob → force download with // Fetch the file (CORS is open on the uploader) → blob → force download with
// the real filename. Falls back to opening the URL in a new tab. // the real filename. Falls back to opening the URL in a new tab.
@@ -119,7 +120,7 @@ export function RecordingCard({
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<span className="text-[10px] font-mono text-text-secondary/60"> <span className="text-[10px] font-mono text-text-secondary/60">
{durationStr} {sizeStr}
</span> </span>
<span className="text-[10px] text-text-secondary/40"> <span className="text-[10px] text-text-secondary/40">
{new Date(recording.created_at).toLocaleString()} {new Date(recording.created_at).toLocaleString()}
@@ -8,8 +8,6 @@ export interface VoiceRecording {
channel_name?: string | null; channel_name?: string | null;
filename: string; filename: string;
size_bytes: number; size_bytes: number;
/** Present on REST rows; absent on WS voice_recording_uploaded events */
duration_bytes?: number | null;
download_url?: string | null; download_url?: string | null;
upload_status: string; upload_status: string;
upload_error?: string | null; upload_error?: string | null;