Fix dictation lifecycle and add real STT e2e

This commit is contained in:
Mohamed Boudra
2026-01-20 13:09:22 +07:00
parent 687ea706b1
commit facd99ab13
9 changed files with 306 additions and 112 deletions
@@ -16,6 +16,7 @@ import Animated, {
import { useReanimatedKeyboardAnimation } from "react-native-keyboard-controller";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { useRealtime } from "@/contexts/realtime-context";
import { useIsFocused } from "@react-navigation/native";
import { FOOTER_HEIGHT, MAX_CONTENT_WIDTH } from "@/constants/layout";
import { generateMessageId } from "@/types/stream";
import { AgentStatusBar } from "./agent-status-bar";
@@ -77,6 +78,7 @@ export function AgentInputArea({
const { theme } = useUnistyles();
const insets = useSafeAreaInsets();
const { height: keyboardHeight } = useReanimatedKeyboardAnimation();
const isScreenFocused = useIsFocused();
const client = useSessionStore(
(state) => state.sessions[serverId]?.client ?? null
@@ -568,6 +570,7 @@ export function AgentInputArea({
placeholder="Message agent..."
autoFocus={autoFocus}
disabled={isRealtimeMode}
isScreenFocused={isScreenFocused}
leftContent={leftContent}
rightContent={rightContent}
isAgentRunning={isAgentRunning}
+12 -1
View File
@@ -54,6 +54,8 @@ export interface MessageInputProps {
placeholder?: string;
autoFocus?: boolean;
disabled?: boolean;
/** True when the containing screen is focused (React Navigation). Used to disable global hotkeys and cancel dictation when unfocused. */
isScreenFocused?: boolean;
/** Content to render on the left side of the button row (e.g., AgentStatusBar) */
leftContent?: React.ReactNode;
/** Content to render on the right side after voice button (e.g., realtime button, cancel button) */
@@ -105,6 +107,7 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(
placeholder = "Message...",
autoFocus = false,
disabled = false,
isScreenFocused = true,
leftContent,
rightContent,
isAgentRunning = false,
@@ -202,12 +205,20 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(
onError: handleDictationError,
canStart: canStartDictation,
canConfirm: canConfirmDictation,
autoStopWhenHidden: { isVisible: isScreenFocused },
enableDuration: true,
});
useEffect(() => {
if (isDictating || isDictationProcessing || dictationStatus === "retrying") {
return;
}
sendAfterTranscriptRef.current = false;
}, [dictationStatus, isDictating, isDictationProcessing]);
// Cmd+D to start/submit dictation, Escape to cancel
useEffect(() => {
if (!IS_WEB) return;
if (!IS_WEB || !isScreenFocused) return;
function handleKeyDown(event: KeyboardEvent) {
// Cmd+D: start dictation or submit if already dictating
if ((event.metaKey || event.ctrlKey) && event.key === "d") {