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.