🎤 Micdrop

Hooks and Call State

The hooks live in @micdrop/react and are the same ones a web app uses, since the call state comes from @micdrop/client on both platforms.

Terminal window
npm install @micdrop/react

Only the components around them change, Text and View instead of div:

import { useMicdropState } from '@micdrop/react'
import { Text } from 'react-native'
function CallStatus() {
const state = useMicdropState()
if (state.isReconnecting) return <Text>Reconnecting</Text>
if (state.isStarting) return <Text>Starting</Text>
if (!state.isStarted) return <Text>Ready</Text>
if (state.isUserSpeaking) return <Text>Listening</Text>
if (state.isAssistantSpeaking) return <Text>Speaking</Text>
if (state.isProcessing) return <Text>Thinking</Text>
return <Text>Your turn</Text>
}

Every hook and every field of the state is documented once, for both platforms:

  • React hooks for useMicdropState, useMicdropError, useMicdropEndCall, useMicdropToolCall, useMicVolume and useSpeakerVolume
  • Call state for what each field means and how the states follow one another
  • Error handling for the error codes, which are the same when the microphone is refused or the server is unreachable

What a phone reads differently

state.speakerDeviceId is a route rather than a device, 'speaker' or 'earpiece', see Audio output and devices. state.micDevices fills up once the call has started, since the audio session has to be active for the system to answer.

Leaving the screen when the assistant hangs up

useMicdropEndCall fires when the agent ends the call on its own, see Auto end call. On a phone that usually means stopping the call and going back:

import { useMicdropEndCall } from '@micdrop/react'
import { Micdrop } from '@micdrop/react-native'
import { useCallback } from 'react'
function CallScreen() {
useMicdropEndCall(
useCallback(() => {
Micdrop.stop()
navigation.goBack()
}, [navigation])
)
}

Wrap the callback in useCallback, the hook resubscribes whenever it changes.

Level meters

useMicVolume and useSpeakerVolume give levels in decibels, updated about ten times a second, for a meter or a pulsing avatar. Silence reads as -Infinity, so map the range you care about:

import { useMicVolume } from '@micdrop/react'
import { View } from 'react-native'
function MicMeter() {
const { micVolume } = useMicVolume()
const ratio = Math.max(0, Math.min(1, (micVolume + 60) / 60))
return <View style={{ width: `${ratio * 100}%`, height: 8 }} />
}