Skip to content

mobile: two Stop controls render during the dictation error phase #244

Description

@rynfar

Found during the adversarial review of #241. Pre-existing on pylon, not introduced by that branch — the gates are byte-identical before and after.

What happens

In apps/mobile/src/features/threads/ThreadComposer.tsx, the collapsed Stop
control renders under {!isExpanded && !voiceInput.isBusy} and the toolbar Stop
renders under {isToolbarVisible}, where
isToolbarVisible = isExpanded || isVoiceInputPresented.

Those are meant to be mutually exclusive, and normally are. But in the dictation
error phase isVoiceInputPresented is true — the status label is the error
message — while voiceInput.isBusy is false, because the error phase is not a
busy phase. Both conditions then pass at once.

Repro

  1. Collapsed composer, agent running (so showStopAction is true).
  2. Start dictation and drive it to an error — the phase that shows a status
    label and keeps Send available.
  3. Two Stop buttons render simultaneously.

Why it matters

Cosmetic rather than functional — both buttons call props.onStopThread, so
either one works. But it is a duplicated control in a state the user is already
recovering from, and VoiceOver announces the same action twice.

Suggested direction

The collapsed gate wants "not showing the toolbar" rather than "not busy".
Deriving it from isToolbarVisible instead of voiceInput.isBusy would make the
two mutually exclusive by construction rather than by coincidence, and would
close the gap without a second predicate to keep in sync.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions