1- import { createFileRoute , Link } from "@tanstack/react-router" ;
21import {
3- lazy ,
4- Suspense ,
5- useCallback ,
6- useEffect ,
7- useMemo ,
8- useRef ,
9- useState ,
10- } from "react" ;
2+ createFileRoute ,
3+ Link ,
4+ redirect ,
5+ useNavigate ,
6+ } from "@tanstack/react-router" ;
7+ import { lazy , Suspense , useMemo , useState } from "react" ;
118
129import type { JSONContent } from "@hypr/tiptap/editor" ;
1310import { EMPTY_TIPTAP_DOC } from "@hypr/tiptap/shared" ;
1411import "@hypr/tiptap/styles.css" ;
1512
16- import {
17- FileInfo ,
18- TranscriptDisplay ,
19- } from "@/components/transcription/transcript-display" ;
13+ import { TranscriptDisplay } from "@/components/transcription/transcript-display" ;
2014import { UploadArea } from "@/components/transcription/upload-area" ;
21- import { getSupabaseBrowserClient } from "@/functions/supabase" ;
22- import {
23- getAudioPipelineStatus ,
24- startAudioPipeline ,
25- } from "@/functions/transcription" ;
26- import { createUploadUrl } from "@/functions/upload" ;
15+ import { fetchUser } from "@/functions/auth" ;
2716
2817const NoteEditor = lazy ( ( ) => import ( "@hypr/tiptap/editor" ) ) ;
2918
@@ -32,165 +21,20 @@ export const Route = createFileRoute("/_view/file-transcription")({
3221 validateSearch : ( search : Record < string , unknown > ) => ( {
3322 id : ( search . id as string ) || undefined ,
3423 } ) ,
24+ beforeLoad : async ( { search } ) => {
25+ const user = await fetchUser ( ) ;
26+ if ( user ) {
27+ throw redirect ( { to : "/app/file-transcription" , search } ) ;
28+ }
29+ } ,
3530} ) ;
3631
37- type ProcessingStatus =
38- | "idle"
39- | "uploading"
40- | "queued"
41- | "transcribing"
42- | "done"
43- | "error" ;
44-
4532function Component ( ) {
46- const [ user , setUser ] = useState < { email ?: string ; id ?: string } | null > (
47- null ,
48- ) ;
49-
50- useEffect ( ( ) => {
51- let isMounted = true ;
52- async function loadUser ( ) {
53- try {
54- const supabase = getSupabaseBrowserClient ( ) ;
55- const { data } = await supabase . auth . getUser ( ) ;
56- if ( ! isMounted ) return ;
57- if ( data . user ?. email ) {
58- setUser ( { email : data . user . email , id : data . user . id } ) ;
59- } else {
60- setUser ( null ) ;
61- }
62- } catch {
63- if ( isMounted ) setUser ( null ) ;
64- }
65- }
66- loadUser ( ) ;
67- return ( ) => {
68- isMounted = false ;
69- } ;
70- } , [ ] ) ;
71-
72- const [ file , setFile ] = useState < File | null > ( null ) ;
73- const [ status , setStatus ] = useState < ProcessingStatus > ( "idle" ) ;
74- const [ error , setError ] = useState < string | null > ( null ) ;
75- const [ transcript , setTranscript ] = useState < string | null > ( null ) ;
33+ const navigate = useNavigate ( { from : Route . fullPath } ) ;
7634 const [ noteContent , setNoteContent ] = useState < JSONContent > ( EMPTY_TIPTAP_DOC ) ;
7735
78- const pollingRef = useRef < ReturnType < typeof setInterval > | null > ( null ) ;
79-
80- const stopPolling = useCallback ( ( ) => {
81- if ( pollingRef . current ) {
82- clearInterval ( pollingRef . current ) ;
83- pollingRef . current = null ;
84- }
85- } , [ ] ) ;
86-
87- useEffect ( ( ) => {
88- return ( ) => stopPolling ( ) ;
89- } , [ stopPolling ] ) ;
90-
91- const handleFileSelect = async ( selectedFile : File ) => {
92- if ( ! user ) {
93- setError ( "Please sign in to transcribe audio files" ) ;
94- return ;
95- }
96-
97- setFile ( selectedFile ) ;
98- setTranscript ( null ) ;
99- setError ( null ) ;
100- setStatus ( "uploading" ) ;
101-
102- try {
103- const uploadResult = await createUploadUrl ( {
104- data : {
105- fileName : selectedFile . name ,
106- fileType : selectedFile . type ,
107- } ,
108- } ) ;
109-
110- if ( "error" in uploadResult && uploadResult . error ) {
111- throw new Error ( uploadResult . message ) ;
112- }
113-
114- if ( ! ( "signedUrl" in uploadResult ) ) {
115- throw new Error ( "Failed to get upload URL" ) ;
116- }
117-
118- const uploadResponse = await fetch ( uploadResult . signedUrl , {
119- method : "PUT" ,
120- headers : {
121- "Content-Type" : selectedFile . type ,
122- } ,
123- body : selectedFile ,
124- } ) ;
125-
126- if ( ! uploadResponse . ok ) {
127- throw new Error ( "Failed to upload file" ) ;
128- }
129-
130- setStatus ( "queued" ) ;
131-
132- const pipelineResult = await startAudioPipeline ( {
133- data : { fileId : uploadResult . fileId } ,
134- } ) ;
135-
136- if ( "error" in pipelineResult && pipelineResult . error ) {
137- throw new Error ( pipelineResult . message ) ;
138- }
139-
140- if ( ! ( "pipelineId" in pipelineResult ) ) {
141- throw new Error ( "Failed to start transcription" ) ;
142- }
143-
144- const { pipelineId } = pipelineResult ;
145-
146- pollingRef . current = setInterval ( async ( ) => {
147- try {
148- const statusResult = await getAudioPipelineStatus ( {
149- data : { pipelineId } ,
150- } ) ;
151-
152- if ( "error" in statusResult && statusResult . error ) {
153- throw new Error ( statusResult . message ) ;
154- }
155-
156- if ( ! ( "status" in statusResult ) ) {
157- return ;
158- }
159-
160- const { status : pipelineStatus } = statusResult ;
161-
162- if ( pipelineStatus . status === "TRANSCRIBING" ) {
163- setStatus ( "transcribing" ) ;
164- } else if ( pipelineStatus . status === "DONE" ) {
165- setStatus ( "done" ) ;
166- setTranscript ( pipelineStatus . transcript ?? null ) ;
167- stopPolling ( ) ;
168- } else if ( pipelineStatus . status === "ERROR" ) {
169- setStatus ( "error" ) ;
170- setError ( pipelineStatus . error ?? "Transcription failed" ) ;
171- stopPolling ( ) ;
172- }
173- } catch ( err ) {
174- setStatus ( "error" ) ;
175- setError (
176- err instanceof Error ? err . message : "Failed to check status" ,
177- ) ;
178- stopPolling ( ) ;
179- }
180- } , 2000 ) ;
181- } catch ( err ) {
182- setStatus ( "error" ) ;
183- setError ( err instanceof Error ? err . message : "An error occurred" ) ;
184- }
185- } ;
186-
187- const handleRemoveFile = ( ) => {
188- stopPolling ( ) ;
189- setFile ( null ) ;
190- setTranscript ( null ) ;
191- setStatus ( "idle" ) ;
192- setError ( null ) ;
193- setNoteContent ( EMPTY_TIPTAP_DOC ) ;
36+ const handleFileSelect = ( ) => {
37+ navigate ( { to : "/auth" } ) ;
19438 } ;
19539
19640 const mentionConfig = useMemo (
@@ -203,9 +47,6 @@ function Component() {
20347 [ ] ,
20448 ) ;
20549
206- const isProcessing =
207- status === "uploading" || status === "queued" || status === "transcribing" ;
208-
20950 return (
21051 < div className = "min-h-[calc(100vh-200px)]" >
21152 < div className = "max-w-7xl mx-auto border-x border-neutral-100" >
@@ -244,18 +85,10 @@ function Component() {
24485 </ div >
24586
24687 < div className = "p-6 space-y-6" >
247- { ! file ? (
248- < UploadArea
249- onFileSelect = { handleFileSelect }
250- disabled = { isProcessing || ! user }
251- />
252- ) : (
253- < FileInfo
254- fileName = { file . name }
255- fileSize = { file . size }
256- onRemove = { handleRemoveFile }
257- />
258- ) }
88+ < UploadArea
89+ onFileSelect = { handleFileSelect }
90+ disabled = { false }
91+ />
25992
26093 < div >
26194 < h3 className = "text-sm font-medium text-neutral-700 mb-3" >
@@ -285,14 +118,12 @@ function Component() {
285118 Combined notes with transcript
286119 </ p >
287120 </ div >
288- { transcript && ! user && (
289- < Link
290- to = "/auth"
291- className = "px-4 h-8 flex items-center text-sm bg-linear-to-t from-stone-600 to-stone-500 text-white rounded-full shadow-md hover:shadow-lg hover:scale-[102%] active:scale-[98%] transition-all"
292- >
293- Sign in
294- </ Link >
295- ) }
121+ < Link
122+ to = "/auth"
123+ className = "px-4 h-8 flex items-center text-sm bg-linear-to-t from-stone-600 to-stone-500 text-white rounded-full shadow-md hover:shadow-lg hover:scale-[102%] active:scale-[98%] transition-all"
124+ >
125+ Sign in
126+ </ Link >
296127 </ div >
297128
298129 < div className = "border border-neutral-200 rounded-lg shadow-sm bg-white overflow-hidden" >
@@ -305,20 +136,18 @@ function Component() {
305136
306137 < div className = "p-6" >
307138 < TranscriptDisplay
308- transcript = { user ? transcript : null }
309- status = { user ? status : "idle" }
310- error = { error }
139+ transcript = { null }
140+ status = "idle"
141+ error = { null }
311142 />
312143 </ div >
313144 </ div >
314145
315- { transcript && ! user && (
316- < div className = "p-4 bg-stone-50 border border-neutral-200 rounded-sm" >
317- < p className = "text-sm text-neutral-600" >
318- Sign in to view and save your transcription results
319- </ p >
320- </ div >
321- ) }
146+ < div className = "p-4 bg-stone-50 border border-neutral-200 rounded-sm" >
147+ < p className = "text-sm text-neutral-600" >
148+ Sign in to view and save your transcription results
149+ </ p >
150+ </ div >
322151 </ div >
323152 </ div >
324153 </ div >
0 commit comments