Skip to content

Group reasoning picker by availability - #9819

Merged
malmstein merged 3 commits into
feature/david/model_picker_uifrom
feature/david/reasoning_picker_ui
Sep 25, 2026
Merged

malmstein merged 3 commits into
feature/david/model_picker_uifrom
feature/david/reasoning_picker_ui

Conversation

@malmstein

@malmstein malmstein commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

Task/Issue URL: https://app.asana.com/1/137249556945/task/1218299204169927
Tech Design URL (if applicable):
API Proposals URL(s) (if applicable): None

Description

Gives the reasoning picker the same shape as the model picker behind updatedPickers:
accessible modes first, a divider, then gated modes under a header naming what unlocks them.
The header rule moves next to the upsell routing as gatedSectionHeaderRes, so both pickers
share one definition instead of drifting.

Gated reasoning rows deliberately do not take the follow-up ellipsis that gated model rows use,
matching the design, even though tapping one also opens an upsell. ReasoningModePickerState
now exposes sections rather than a flat row list, with a rows accessor for callers that only
care about the modes. With the flag off it builds a single unheaded section, which renders
exactly as today.

Stacked on #9814.

Steps to test this PR

Feature 1

  • With updatedPickers off, open the reasoning picker and confirm it is unchanged from
    develop: flat list, no header, no divider
  • Enable updatedPickers, force stop and relaunch
  • On a model with a gated reasoning effort, confirm Fast and Reasoning appear first, then a
    divider, then the gated mode under a header
  • Confirm the header matches the tier: "Pro Plan Exclusive" when the gated mode needs Pro,
    "Try Free for 7 Days" when a trial is still available, "Subscriber Exclusive" otherwise
  • Tap the gated mode and confirm the upsell still opens, purchase for free users and upgrade
    for Plus
  • Confirm the reasoning rows still carry their subtitles and the tick sits on the active mode

UI changes

Before After
!(Upload before screenshot) (Upload after screenshot)

@malmstein
malmstein added this pull request to stack #9820 September 21, 2026 14:53
@malmstein
malmstein marked this pull request as ready for review September 21, 2026 14:53

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review for a one-time review, or @claude review always to subscribe this PR to a review on every future push.

Tip: disable this comment in your organization's Code Review settings.

@malmstein
malmstein force-pushed the feature/david/reasoning_picker_ui branch 2 times, most recently from 7618215 to 0d91c7d Compare September 23, 2026 09:59
if (index > 0) container.addView(HorizontalDivider(context))
section.headerRes?.let { container.addSectionHeader(context.getString(it)) }
section.rows.forEach { row ->
val item = pickerMenuItem(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@malmstein Following from https://github.com/duckduckgo/Android/pull/9814/changes#r4094482313, should we set opensFollowUp here? "Set [opensFollowUp] for a row that leads somewhere else" a gated reasoning routes to upsell so I assume we want this as true?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, agreed, they route to the same upsell so they should read the same.

@karlenDimla karlenDimla left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good but would like to get clarity on #9819 (comment)

@malmstein
malmstein force-pushed the feature/david/reasoning_picker_ui branch from 0d91c7d to 6178b67 Compare September 24, 2026 14:46
@malmstein
malmstein force-pushed the feature/david/reasoning_picker_ui branch from 6178b67 to 6e919d6 Compare September 24, 2026 14:52
malmstein and others added 3 commits September 24, 2026 22:10
Gives the reasoning picker the same treatment as the model picker behind
updatedPickers: accessible modes first, a divider, then gated ones under
a header naming what unlocks them. The header rule moves next to the
upsell routing so both pickers share one definition rather than drifting
apart.

Gated reasoning rows do not take the follow-up ellipsis the gated model
rows use, matching the design, even though tapping one also opens an
upsell.

State now exposes sections instead of a flat row list, with a rows
accessor for the callers that only care about the modes.

https://app.asana.com/1/137249556945/task/1218299204169927

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The reasoning popup was 220dp against the model picker's 260dp, so
Extended Reasoning wrapped onto two lines once the gated section gave it
a header to sit under. Both now use nativeInputMenuWidth, the width the
other native input menus already share.

https://app.asana.com/1/137249556945/task/1218299204169927

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Review feedback: tapping a gated reasoning mode opens an upsell exactly
as a gated model row does, so the two should read the same. The design
shows the reasoning rows without it, worth confirming with design, but
matching behaviour wins over matching the mock here.

https://app.asana.com/1/137249556945/task/1218299204169927

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@malmstein
malmstein force-pushed the feature/david/reasoning_picker_ui branch from 225ed4f to 97891bf Compare September 24, 2026 20:10
@malmstein
malmstein added this pull request to the merge queue Sep 25, 2026
Merged via the queue into develop with commit 4e42c0f Sep 25, 2026
15 checks passed
@malmstein
malmstein deleted the feature/david/reasoning_picker_ui branch September 25, 2026 09:17
daotuananh1999tgdd-sudo pushed a commit to daotuananh1999tgdd-sudo/Android that referenced this pull request Sep 27, 2026
Task/Issue URL:
https://app.asana.com/1/137249556945/task/1218292003956804
Tech Design URL (if applicable): 
API Proposals URL(s) (if applicable): None

### Description

Two pixels for the updated pickers.

`m_aichat_unified_input_picker_upsell_shown` fires when a picker opens
with a gated section, so
the funnel has a denominator for the existing
`..._subscription_upsell_triggered` click: fewer
gated-row taps only means something against how often the section was
seen. It carries which
header the user saw, since that copy is what tells them why the rows are
locked.

`m_aichat_model_label_unknown_daily` is the debug pixel for a model
`label` this version cannot
render. Models are re-fetched whenever a picker attaches, so each
unrecognised value is reported
once per process rather than on every refresh, and the value is
uppercased and stripped to the
backend id shape rather than passed through verbatim.

The gated header is now a typed `GatedHeader` carrying both the string
resource and the pixel
value, so the copy and the telemetry cannot drift apart. Both pixels are
registered in
`duck_chat.json5`. Local registry validation could not be run here (`npm
ci` needs the
git-hosted `@duckduckgo/pixel-schema`), so CI is the first check of the
JSON5.

Stacked on duckduckgo#9819.

### Steps to test this PR

_Feature 1_
- [x] Enable `updatedPickers`, then open the model picker on an account
that can purchase a
      subscription, so the gated section renders
- [x] Confirm `m_aichat_unified_input_picker_upsell_shown` fires once
with `source=model_picker`,
the `header` matching the section header on screen, your `current_tier`,
and the picker's
      `origin`
- [x] Repeat with the reasoning picker on a model that has a gated
reasoning effort, and confirm
      `source=reasoning_picker`
- [x] Open a picker with nothing gated and confirm no upsell impression
fires
- [x] Confirm `..._model_picker_shown` and `..._model_selected` still
fire as they do on develop

### UI changes
None

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants