refactor(app): replace voice hooks with voice runtime and audio engine architecture
This commit is contained in:
@@ -101,7 +101,6 @@ export interface MessageInputRef {
|
||||
const MIN_INPUT_HEIGHT = 30
|
||||
const MAX_INPUT_HEIGHT = 160
|
||||
const IS_WEB = Platform.OS === 'web'
|
||||
const IS_DEV = Boolean((globalThis as { __DEV__?: boolean }).__DEV__)
|
||||
|
||||
type WebTextInputKeyPressEvent = NativeSyntheticEvent<
|
||||
TextInputKeyPressEventData & {
|
||||
@@ -125,13 +124,10 @@ type TextAreaHandle = {
|
||||
}
|
||||
|
||||
function logWebStickyBottom(
|
||||
event: string,
|
||||
details: Record<string, unknown>
|
||||
_event: string,
|
||||
_details: Record<string, unknown>
|
||||
): void {
|
||||
if (!IS_DEV || !IS_WEB) {
|
||||
return
|
||||
}
|
||||
console.log('[WebStickyBottom]', event, details)
|
||||
// Intentionally disabled: this path is too noisy during voice debugging.
|
||||
}
|
||||
|
||||
function getDebugNow(): number | null {
|
||||
@@ -220,6 +216,18 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(funct
|
||||
markScrollInvestigationRender(investigationComponentId)
|
||||
const toast = useToast()
|
||||
const voice = useVoiceOptional()
|
||||
console.log("[MessageInput] render", {
|
||||
voiceServerId: voiceServerId ?? null,
|
||||
voiceAgentId: voiceAgentId ?? null,
|
||||
hasVoice: Boolean(voice),
|
||||
isVoiceMode: voice?.isVoiceMode ?? false,
|
||||
isVoiceSwitching: voice?.isVoiceSwitching ?? false,
|
||||
isMuted: voice?.isMuted ?? false,
|
||||
disabled,
|
||||
isSubmitLoading,
|
||||
valueLength: value.length,
|
||||
imageCount: images.length,
|
||||
})
|
||||
const [inputHeight, setInputHeight] = useState(MIN_INPUT_HEIGHT)
|
||||
const rootRef = useRef<View | null>(null)
|
||||
const inputWrapperRef = useRef<View | null>(null)
|
||||
@@ -293,6 +301,19 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(funct
|
||||
)
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
console.log("[MessageInput] mount", {
|
||||
voiceServerId: voiceServerId ?? null,
|
||||
voiceAgentId: voiceAgentId ?? null,
|
||||
})
|
||||
return () => {
|
||||
console.log("[MessageInput] unmount", {
|
||||
voiceServerId: voiceServerId ?? null,
|
||||
voiceAgentId: voiceAgentId ?? null,
|
||||
})
|
||||
}
|
||||
}, [voiceAgentId, voiceServerId])
|
||||
|
||||
useEffect(() => {
|
||||
valueRef.current = value
|
||||
}, [value])
|
||||
@@ -407,6 +428,26 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(funct
|
||||
const showRealtimeOverlay = isRealtimeVoiceForCurrentAgent
|
||||
const showOverlay = showDictationOverlay || showRealtimeOverlay
|
||||
|
||||
useEffect(() => {
|
||||
console.log("[MessageInput] overlay_state", {
|
||||
showDictationOverlay,
|
||||
showRealtimeOverlay,
|
||||
showOverlay,
|
||||
isDictating,
|
||||
isDictationProcessing,
|
||||
dictationStatus,
|
||||
isRealtimeVoiceForCurrentAgent,
|
||||
})
|
||||
}, [
|
||||
dictationStatus,
|
||||
isDictating,
|
||||
isDictationProcessing,
|
||||
isRealtimeVoiceForCurrentAgent,
|
||||
showDictationOverlay,
|
||||
showOverlay,
|
||||
showRealtimeOverlay,
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
if (isDictating || isDictationProcessing) {
|
||||
return
|
||||
@@ -443,6 +484,11 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(funct
|
||||
}))
|
||||
|
||||
const handleVoicePress = useCallback(async () => {
|
||||
console.log("[MessageInput] handleVoicePress", {
|
||||
isRealtimeVoiceForCurrentAgent,
|
||||
isDictating,
|
||||
hasVoice: Boolean(voice),
|
||||
})
|
||||
if (isRealtimeVoiceForCurrentAgent && voice) {
|
||||
voice.toggleMute()
|
||||
return
|
||||
@@ -462,28 +508,39 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(funct
|
||||
])
|
||||
|
||||
const handleCancelRecording = useCallback(async () => {
|
||||
console.log("[MessageInput] handleCancelRecording")
|
||||
await cancelDictation()
|
||||
}, [cancelDictation])
|
||||
|
||||
const handleAcceptRecording = useCallback(async () => {
|
||||
console.log("[MessageInput] handleAcceptRecording")
|
||||
sendAfterTranscriptRef.current = false
|
||||
await confirmDictation()
|
||||
}, [confirmDictation])
|
||||
|
||||
const handleAcceptAndSendRecording = useCallback(async () => {
|
||||
console.log("[MessageInput] handleAcceptAndSendRecording")
|
||||
sendAfterTranscriptRef.current = true
|
||||
await confirmDictation()
|
||||
}, [confirmDictation])
|
||||
|
||||
const handleRetryFailedRecording = useCallback(() => {
|
||||
console.log("[MessageInput] handleRetryFailedRecording")
|
||||
void retryFailedDictation()
|
||||
}, [retryFailedDictation])
|
||||
|
||||
const handleDiscardFailedRecording = useCallback(() => {
|
||||
console.log("[MessageInput] handleDiscardFailedRecording")
|
||||
discardFailedDictation()
|
||||
}, [discardFailedDictation])
|
||||
|
||||
const handleStopRealtimeVoice = useCallback(async () => {
|
||||
console.log("[MessageInput] handleStopRealtimeVoice", {
|
||||
hasVoice: Boolean(voice),
|
||||
isRealtimeVoiceForCurrentAgent,
|
||||
isAgentRunning,
|
||||
voiceAgentId: voiceAgentId ?? null,
|
||||
})
|
||||
if (!voice || !isRealtimeVoiceForCurrentAgent) {
|
||||
return
|
||||
}
|
||||
@@ -503,6 +560,18 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(funct
|
||||
}, [client, isAgentRunning, isRealtimeVoiceForCurrentAgent, voice, voiceAgentId])
|
||||
|
||||
const handleToggleRealtimeVoiceShortcut = useCallback(() => {
|
||||
console.log("[MessageInput] handleToggleRealtimeVoiceShortcut", {
|
||||
hasVoice: Boolean(voice),
|
||||
voiceServerId: voiceServerId ?? null,
|
||||
voiceAgentId: voiceAgentId ?? null,
|
||||
isConnected,
|
||||
disabled,
|
||||
isVoiceSwitching: voice?.isVoiceSwitching ?? false,
|
||||
isVoiceModeForAgent:
|
||||
voice && voiceServerId && voiceAgentId
|
||||
? voice.isVoiceModeForAgent(voiceServerId, voiceAgentId)
|
||||
: false,
|
||||
})
|
||||
if (!voice || !voiceServerId || !voiceAgentId || !isConnected || disabled) {
|
||||
return
|
||||
}
|
||||
@@ -524,6 +593,11 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(funct
|
||||
}, [disabled, handleStopRealtimeVoice, isConnected, toast, voice, voiceAgentId, voiceServerId])
|
||||
|
||||
const handleSendMessage = useCallback(() => {
|
||||
console.log("[MessageInput] handleSendMessage", {
|
||||
valueLength: value.length,
|
||||
imageCount: images.length,
|
||||
isAgentRunning,
|
||||
})
|
||||
const trimmed = value.trim()
|
||||
if (!trimmed && images.length === 0) return
|
||||
const payload = {
|
||||
@@ -1070,10 +1144,7 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(funct
|
||||
/>
|
||||
) : showRealtimeOverlay && voice ? (
|
||||
<RealtimeVoiceOverlay
|
||||
volume={voice.volume}
|
||||
isMuted={voice.isMuted}
|
||||
isDetecting={voice.isDetecting}
|
||||
isSpeaking={voice.isSpeaking}
|
||||
isSwitching={voice.isVoiceSwitching}
|
||||
onToggleMute={voice.toggleMute}
|
||||
onStop={() => {
|
||||
|
||||
Reference in New Issue
Block a user