Skip to content

Capture Button

Metadata

  • System type: library

System Intent

  • What this is: A React Native component that renders the circular record/stop button in the app footer. It reads live recording state and the user's selected recording device, applies an optimistic UI toggle on press, and delegates to startRecordingFromControl / stopRecordingFromControl. When the selected device is a glasses mode (glasses or glasses-audio) but no glasses are connected, it skips the recording attempt entirely and calls an optional onGlassesUnavailable callback (fail-fast guard).

Mermaid Diagram

flowchart TD
  Press([User presses CaptureButton]) --> HP[handlePress]

  HP --> RecordingCheck{state is\nrecording or stopping?}
  RecordingCheck -->|Yes| Stop[setOptimisticRecording false\nstopRecordingFromControl]
  RecordingCheck -->|No| GlassesCheck{selectedDevice is\nglasses or glasses-audio?}

  GlassesCheck -->|No — phone mode| Start[setOptimisticRecording true\nstartRecordingFromControl]
  GlassesCheck -->|Yes| AvailCheck{isGlassesDeviceAvailable?}

  AvailCheck -->|Yes| Start
  AvailCheck -->|No| Log[log capture_glasses_unavailable]
  Log --> Callback[onGlassesUnavailable?.\(\)]
  Callback --> Return([return — no recording attempt])

  Stop -->|error| StopAlert[Alert + log capture_stop_error]
  Start -->|error| StartAlert[Alert + log capture_start_error]

Flows

Flow: handlePress

  • Test files: main/app/__tests__/capture-button.test.tsx
  • Core files: main/app/components/CaptureButton.tsx

Types

CaptureButtonProps {
  onGlassesUnavailable?: () => void   (optional; called when glasses mode is selected but no glasses are connected)
}

SelectedDevice = "glasses" | "glasses-audio" | "phone" | ...   (from recording-device-preference)

Paths

path input output path-type notes
handlePress.stopRecording state is recording or stopping optimistic UI flips to stopped; stopRecordingFromControl() called happy path optimistic state cleared if stop fails
handlePress.stopRecording.error stopRecordingFromControl throws optimistic state cleared; error Alert shown; capture_stop_error logged error
handlePress.phoneMode state is idle; selectedDevice is not glasses mode optimistic UI flips to recording; startRecordingFromControl() called happy path
handlePress.glassesConnected state is idle; selectedDevice is glasses or glasses-audio; isGlassesDeviceAvailable() returns true optimistic UI flips to recording; startRecordingFromControl() called happy path
handlePress.glassesUnavailable state is idle; selectedDevice is glasses or glasses-audio; isGlassesDeviceAvailable() returns false capture_glasses_unavailable logged; onGlassesUnavailable?.() called; returns immediately happy path (fail-fast) no recording attempt; Footer passes openDeviceSheet here to prompt device selection
handlePress.startRecording.error startRecordingFromControl throws optimistic state cleared; error Alert shown; capture_start_error logged error

Pseudocode

handlePress():
  if recording:                               // state === "recording" || "stopping"
    setOptimisticRecording(false)
    try:
      log capture_stopping
      await stopRecordingFromControl()
      log capture_stopped
    catch err:
      setOptimisticRecording(null)
      log capture_stop_error
      Alert.alert(err.message)
    return

  glassesMode = selectedDevice === "glasses" || selectedDevice === "glasses-audio"
  if glassesMode && !isGlassesDeviceAvailable():
    log capture_glasses_unavailable
    onGlassesUnavailable?.()
    return

  setOptimisticRecording(true)
  try:
    log capture_starting
    await startRecordingFromControl()
    log capture_started
  catch err:
    setOptimisticRecording(null)
    log capture_start_error
    Alert.alert(err.message)

Logs

Source Location
capture_stopping useLogging() frontend logger
capture_stopped useLogging() frontend logger
capture_stop_error useLogging() frontend logger
capture_starting useLogging() frontend logger
capture_started useLogging() frontend logger
capture_start_error useLogging() frontend logger
capture_glasses_unavailable useLogging() frontend logger — emitted when fail-fast guard fires

Deployment

  • Mechanism: local only (shipped as part of the React Native app bundle)
  • Deploy command:
    cd main && npx expo start
    
  • Notes: The component reads selectedDevice from useSelectedRecordingDevice() and calls isGlassesDeviceAvailable() synchronously at press time. The optimistic recording state (optimisticRecording) is reconciled back to null once the true recording state catches up, preventing the button from staying in a visually-wrong state after a start or stop error.