@@ -223,6 +223,7 @@ export function useSpeechToText({
223223 if ( startingRef . current ) return false
224224 startingRef . current = true
225225 const generation = ++ generationRef . current
226+ setIsListening ( true )
226227
227228 try {
228229 let tokenData : Awaited < ReturnType < typeof requestJson < typeof speechTokenContract > > >
@@ -236,6 +237,7 @@ export function useSpeechToText({
236237 if ( ! mountedRef . current || generation !== generationRef . current ) return false
237238 if ( isApiClientError ( err ) && err . status === 402 ) {
238239 const isMemberLimit = ( err . body as { scope ?: string } | null ) ?. scope === 'member'
240+ setIsListening ( false )
239241 onUsageLimitExceededRef . current ?.( err . message , isMemberLimit )
240242 return false
241243 }
@@ -262,6 +264,10 @@ export function useSpeechToText({
262264 }
263265
264266 streamRef . current = stream
267+ sessionTimerRef . current = setTimeout ( ( ) => {
268+ logger . info ( 'Voice input session reached max duration, stopping' )
269+ stopStreamingRef . current ( )
270+ } , MAX_SESSION_MS )
265271
266272 const params = new URLSearchParams ( {
267273 token,
@@ -349,19 +355,13 @@ export function useSpeechToText({
349355
350356 sendIntervalRef . current = setInterval ( flushAudioBuffer , CHUNK_SEND_INTERVAL_MS )
351357
352- setIsListening ( true )
353-
354- sessionTimerRef . current = setTimeout ( ( ) => {
355- logger . info ( 'Voice input session reached max duration, stopping' )
356- stopStreamingRef . current ( )
357- } , MAX_SESSION_MS )
358-
359358 return true
360359 } catch ( error ) {
361360 if ( generation !== generationRef . current ) return false
362361 logger . error ( 'Failed to start speech streaming' , error )
363362 cleanup ( )
364363 if ( mountedRef . current ) {
364+ setIsListening ( false )
365365 onErrorRef . current ?.( classifyStartError ( error ) )
366366 }
367367 return false
@@ -371,6 +371,14 @@ export function useSpeechToText({
371371 } , [ cleanup , flushAudioBuffer ] )
372372
373373 const stopStreaming = useCallback ( ( ) => {
374+ if ( startingRef . current ) {
375+ generationRef . current ++
376+ startingRef . current = false
377+ cleanup ( )
378+ setIsListening ( false )
379+ return
380+ }
381+
374382 if ( sessionTimerRef . current ) {
375383 clearTimeout ( sessionTimerRef . current )
376384 sessionTimerRef . current = null
@@ -426,7 +434,7 @@ export function useSpeechToText({
426434 }
427435
428436 setIsListening ( false )
429- } , [ flushAudioBuffer ] )
437+ } , [ cleanup , flushAudioBuffer ] )
430438
431439 stopStreamingRef . current = stopStreaming
432440
@@ -439,7 +447,7 @@ export function useSpeechToText({
439447 } , [ cleanup ] )
440448
441449 const toggleListening = useCallback ( ( ) => {
442- if ( isListening ) {
450+ if ( startingRef . current || isListening ) {
443451 stopStreaming ( )
444452 } else {
445453 startStreaming ( )
0 commit comments