feat(app): Settings puts the most-opened groups first; plan card A/B (settings-plan-card)

YI
2h ago
+791-66

Summary

Settings puts what people open most at the top and makes upgrading one tap from the sheet.

  • Order: Device, Recording & Transcription, Notifications & Display and Data & Privacy (most Settings visits) sit right under Account and Plan & Usage. Voice, Integrations and Connect an Agent come next; Memories and Goals keep their group; Referral moves next to Help & About and Feedback and drops the NEW tag.
  • Device row shows the battery, or Connected / Disconnected, so you can check it without opening the page.
  • Account is the name (large) and email. No placeholder picture, since there are no profile photos.
  • No handle band at the top of the sheet: the header with the close X is the top, and the sheet still swipes down to close. app/docs/ux-contract.md notes the exception.
  • Plan & Usage for Free and Plus gets the minutes meter and a full-width Upgrade, behind an A/B test (below). Upgrade opens the paywall in place via openUpgradePaywall (source usage_page), so paywall-variant still picks the design. Paid plans keep the plain row.

Nothing moved deeper: every row is still on the sheet or one tap below it (settings_structure_test).

upgrade_paywall.dart and standalone_plans_sheet.dart are byte-identical to the copies in #21711, so whichever lands second merges cleanly.

Screenshots

Before (main)After (card arm)
BeforeAfter

Before: widget-test render of main's SettingsDrawer (Free plan). After: iOS simulator, local test account on Free, no device connected.

A/B test: settings-plan-card

  • PostHog flag settings-plan-card (963589) with draft experiment 486057, 50/50 by distinct id. Not launched: launch once this build is in the stores.
  • control: the Plan & Usage row with the minutes meter and a full-width Upgrade under it. card: a plan card with the plan name large, the meter and a full-width Upgrade; tapping the card outside Upgrade opens Plan & Usage.
  • Exposure: $feature_flag_called (via recordFeatureFlagExposure, which also sets $feature/settings-plan-card) when the section is drawn for a Free or Plus reader with a PostHog-assigned arm. No identity, an unread flag or an unknown variant shows control and is not counted.
  • Primary metric: Upgrade Succeeded within 7 days of exposure. Secondary: Upgrade Plan Selected. Guardrail: Product Error per user.
  • Sample: about 6,800 readers open Settings a month and about 8% upgrade, so roughly 3,000 exposed per arm per month detects about a 25% relative change. Plan: run 4 weeks. Pre-registration in product/experiments/settings-plan-card.md; flag added to config/feature-flags.yaml.
  • Debug builds: --dart-define=OMI_SETTINGS_PLAN_CARD=control|card.
  • The reorder, Device status, Account header and handle band ship to everyone.

Testing

  • flutter test test/widgets/settings test/spine/b1_settings_surface_test.dart test/widgets/semantics_baseline_test.dart: 55 pass, including both arms, the fallback-is-not-counted rule, the exposure key, the new order, the Device battery value and the Account header.
  • scripts/check_feature_flag_registry.py: OK.
  • Checked on the iOS simulator (iPhone 17 Pro, iOS 26.5) on this branch.

Product invariants affected

none

🤖 Generated with Claude Code