Skip to content

Commit b23d908

Browse files
feat(transcript-view): Add start recording button and empty state (#835)
1 parent 98c61b5 commit b23d908

File tree

1 file changed

+81
-37
lines changed

1 file changed

+81
-37
lines changed

apps/desktop/src/components/right-panel/views/transcript-view.tsx

Lines changed: 81 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,21 @@
11
import { useQuery, useQueryClient } from "@tanstack/react-query";
22
import { writeText } from "@tauri-apps/plugin-clipboard-manager";
3-
import { CheckIcon, ClipboardCopyIcon, EarIcon, FileAudioIcon, PencilIcon } from "lucide-react";
3+
import {
4+
CheckIcon,
5+
ClipboardCopyIcon,
6+
ClipboardIcon,
7+
EarIcon,
8+
FileAudioIcon,
9+
PencilIcon,
10+
UploadIcon,
11+
} from "lucide-react";
412
import { useEffect, useRef } from "react";
513

614
import { commands as dbCommands } from "@hypr/plugin-db";
715
import { commands as miscCommands } from "@hypr/plugin-misc";
816
import { commands as windowsCommands, events as windowsEvents } from "@hypr/plugin-windows";
917
import { Button } from "@hypr/ui/components/ui/button";
18+
import { Spinner } from "@hypr/ui/components/ui/spinner";
1019
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@hypr/ui/components/ui/tooltip";
1120
import { useOngoingSession, useSessions } from "@hypr/utils/contexts";
1221
import { useTranscript } from "../hooks/useTranscript";
@@ -17,8 +26,13 @@ export function TranscriptView() {
1726
const transcriptContainerRef = useRef<HTMLDivElement>(null);
1827

1928
const sessionId = useSessions((s) => s.currentSessionId);
20-
const isInactive = useOngoingSession((s) => s.status === "inactive");
21-
const { showEmptyMessage, isEnhanced, hasTranscript } = useTranscriptWidget(sessionId);
29+
const ongoingSession = useOngoingSession((s) => ({
30+
start: s.start,
31+
status: s.status,
32+
loading: s.loading,
33+
isInactive: s.status === "inactive",
34+
}));
35+
const { showEmptyMessage, hasTranscript } = useTranscriptWidget(sessionId);
2236
const { isLive, words } = useTranscript(sessionId);
2337

2438
const handleCopyAll = () => {
@@ -66,6 +80,12 @@ export function TranscriptView() {
6680
}
6781
};
6882

83+
const handleStartRecording = () => {
84+
if (sessionId && ongoingSession.status === "inactive") {
85+
ongoingSession.start(sessionId);
86+
}
87+
};
88+
6989
useEffect(() => {
7090
const scrollToBottom = () => {
7191
requestAnimationFrame(() => {
@@ -95,7 +115,7 @@ export function TranscriptView() {
95115
}, []);
96116

97117
return (
98-
<div className="relative w-full h-full flex flex-col">
118+
<div className="w-full h-full flex flex-col">
99119
<div className="p-4 pb-0">
100120
<header className="flex items-center gap-2 w-full">
101121
<div className="flex-1 text-md font-medium">
@@ -111,7 +131,7 @@ export function TranscriptView() {
111131
</div>
112132
</div>
113133
<div className="not-draggable flex items-center gap-2">
114-
{(audioExist.data && isInactive && hasTranscript && sessionId) && (
134+
{(audioExist.data && ongoingSession.isInactive && hasTranscript && sessionId) && (
115135
<TooltipProvider key="listen-recording-tooltip">
116136
<Tooltip>
117137
<TooltipTrigger asChild>
@@ -150,40 +170,64 @@ export function TranscriptView() {
150170
</header>
151171
</div>
152172

153-
{sessionId && (
154-
<div
155-
ref={transcriptContainerRef}
156-
className="flex-1 scrollbar-none px-4 flex flex-col gap-2 overflow-y-auto text-sm py-4"
157-
>
158-
<p className="whitespace-pre-wrap">
159-
{words.map((word, i) => (
160-
<span key={`${word.text}-${i}`}>
161-
{i > 0 ? " " : ""}
162-
{word.text}
163-
</span>
164-
))}
165-
</p>
166-
{isLive && (
167-
<div className="flex items-center gap-2 justify-center py-2 text-neutral-400">
168-
<EarIcon size={14} /> Listening... (there might be a delay)
169-
</div>
170-
)}
171-
</div>
172-
)}
173+
{!sessionId ? null : (
174+
<div className="flex-1">
175+
{showEmptyMessage
176+
? (
177+
<div className="h-full flex items-center justify-center">
178+
<div className="text-neutral-500 font-medium text-center">
179+
<div className="mb-6 text-neutral-600 flex items-center gap-1.5">
180+
<Button size="sm" onClick={handleStartRecording} disabled={ongoingSession.loading}>
181+
{ongoingSession.loading ? <Spinner color="black" /> : (
182+
<div className="relative h-2 w-2 mr-2">
183+
<div className="absolute inset-0 rounded-full bg-red-500"></div>
184+
<div className="absolute inset-0 rounded-full bg-red-400 animate-ping"></div>
185+
</div>
186+
)}
187+
{ongoingSession.loading ? "Starting..." : "Start recording"}
188+
</Button>
189+
<span className="text-sm">to see live transcript</span>
190+
</div>
173191

174-
{!sessionId && (
175-
<div className="absolute inset-0 backdrop-blur-sm bg-white/50 flex items-center justify-center">
176-
<div className="text-neutral-500 font-medium">Session not found</div>
177-
</div>
178-
)}
192+
<div className="flex items-center justify-center w-full max-w-[240px] mb-4">
193+
<div className="h-px bg-neutral-200 flex-grow"></div>
194+
<span className="px-3 text-xs text-neutral-400 font-medium">or</span>
195+
<div className="h-px bg-neutral-200 flex-grow"></div>
196+
</div>
179197

180-
{sessionId && showEmptyMessage && (
181-
<div className="absolute inset-0 backdrop-blur-sm bg-white/50 flex items-center justify-center rounded-2xl">
182-
<div className="text-neutral-500 font-medium">
183-
{isEnhanced
184-
? "No transcript available"
185-
: "Meeting is not active"}
186-
</div>
198+
<div className="flex flex-col gap-2">
199+
<Button variant="outline" size="sm" className="hover:bg-neutral-100" disabled>
200+
<UploadIcon size={14} />Upload recording{" "}
201+
<span className="text-xs text-neutral-400 italic">coming soon</span>
202+
</Button>
203+
<Button variant="outline" size="sm" className="hover:bg-neutral-100" disabled>
204+
<ClipboardIcon size={14} />Paste transcript{" "}
205+
<span className="text-xs text-neutral-400 italic">coming soon</span>
206+
</Button>
207+
</div>
208+
</div>
209+
</div>
210+
)
211+
: (
212+
<div
213+
ref={transcriptContainerRef}
214+
className="h-full scrollbar-none px-4 flex flex-col gap-2 overflow-y-auto text-sm py-4"
215+
>
216+
<p className="whitespace-pre-wrap">
217+
{words.map((word, i) => (
218+
<span key={`${word.text}-${i}`}>
219+
{i > 0 ? " " : ""}
220+
{word.text}
221+
</span>
222+
))}
223+
</p>
224+
{isLive && (
225+
<div className="flex items-center gap-2 justify-center py-2 text-neutral-400">
226+
<EarIcon size={14} /> Listening... (there might be a delay)
227+
</div>
228+
)}
229+
</div>
230+
)}
187231
</div>
188232
)}
189233
</div>

0 commit comments

Comments
 (0)