Skip to content

feat(profile): show supporter status as an animated halo and wings - #10382

Merged
rabble merged 32 commits into
mainfrom
claude/project-thread-9l8tjr
Oct 24, 2026
Merged

rabble merged 32 commits into
mainfrom
claude/project-thread-9l8tjr

Conversation

@Chardot

@Chardot Chardot commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

Requested by Sebastian

Description

Today a Divine Supporter is marked by a small heart "Supporter" label under their username. It is easy to miss, and on your own profile it shows even when you chose not to show your support publicly. This PR replaces it with the supporter design from #9914: an animated halo and pair of wings around the profile avatar.

What changes for people:

  • Supporter profiles show a halo and wings around the avatar, gently floating. They appear only for accounts whose public supporter status is on (haloVisible from the supporters API), and fade in once that status loads, without moving the layout. With reduced motion turned on they appear still, with no fade.
  • Tapping another supporter's avatar opens a supporter view. The avatar, halo and wings fly into a full-screen view as one piece. The view explains what being a Divine Supporter means and invites the viewer to become one. Viewers who already support Divine don't see the invitation. Other avatars still open the normal photo view.
  • Your own profile uses your account's public-recognition setting. Tapping the halo opens your own supporter view, in the same style, with the copy from the Supporter screen.
    With an external signer and an existing cached membership snapshot, a setting changed on another device can remain stale here until an explicit refresh on the Supporter screen.
  • The heart "Supporter" label is gone for supporters. People who aren't supporters still see it as the way in to become one.
  • A "Show my Supporter badge publicly" setting is added to Edit Profile. It sits in a new "Divine Supporters" section after "Verified accounts", only for supporters. It saves immediately, independently of the other profile-field drafts, and reverts with an error message if saving fails. Cancel does not undo a supporter-visibility change that has saved. The same setting stays on the Supporter screen in Settings.
  • The supporter view's text is translated into all 22 locales.

Why this approach:

  • Other people's profiles use the public opt-in endpoint.
    Your own profile uses an existing membership snapshot first, so a setting changed locally can appear immediately without waiting for a public refetch.
    Passive refresh avoids external-signer prompts, which means cross-device changes can leave your own view stale until you explicitly refresh on the Supporter screen.
  • On your own profile, an active membership's visibility choice answers first, so a change to that setting can show without waiting for a public refetch.
    If only the local clock has expired a cached membership and the server has not confirmed expiry, profile display falls back to the existing public supporter answer.
    A server-confirmed expiry still overrides a stale public positive, and active private consent still takes precedence.
    The compact join entry is hidden when the same display choice shows the public halo.
    This does not change cached entitlements, purchase benefits or signer-refresh behavior.
  • The fade-in lives above the avatar's Hero. A Hero swaps its child for a placeholder while it flies, and keeping the fade outside it stops the halo flickering after the supporter view closes.

Related Issue: Closes #9914

Main vs this branch

Same simulator (iPhone 16, iOS 18.5), same signed-in account, same screens, with public supporter status on for both accounts.

Main This branch
main rabble profile branch rabble profile
A supporter's profile: only a small heart "Supporter" label marks them. The same profile: an animated halo and wings around the avatar, with no label.
main rabble lightbox branch rabble lightbox
Tapping their avatar: a plain photo view that says nothing about their support. Tapping their avatar: the supporter view, with what it means to support Divine and an invitation to join.
main own profile branch own profile
Your own profile as a supporter: the heart label under your username. Your own profile: the halo and wings when the local public-recognition setting is on, and no label.
main own lightbox branch own lightbox
Tapping your own avatar: the plain photo view. Tapping your own avatar: your own supporter view, with the Supporter screen's copy.

Out of Scope

  • The Supporter screen in Settings keeps its own public-recognition toggle, on purpose.
  • The new strings in 22 locales were written to the localization style guide but still need a fluent-speaker check. The least certain are am, te, ur, ar, the "is among the Supporters" headline in it/pt/ro/bg, the さん/님 honorifics in ja/ko, and the fil word order.

Verification

Original author-reported checks (before the latest updates)

The following results, including the 1,137-test count and simulator checks, predate the twelve commits in 28818df..e819101 and do not establish validation of the current head.

  • flutter analyze lib test integration_test: no issues.
  • flutter test over test/widgets/profile/, test/widgets/supporter_membership_test.dart, test/screens/settings/, test/screens/profile_setup/, test/screens/profile_setup_screen_test.dart, test/blocs/supporter/ and test/l10n/: 1137 passed.
  • mobile/scripts/ci/run_guards.sh: 65 passed, 0 failed.
  • New tests cover:
    • the halo shown or hidden per status, on your own profile and on someone else's
    • your own halo updating as soon as you change the setting
    • the label hidden for supporters
    • which view each avatar tap opens, including your own view having no toggle
    • the halo staying at full opacity after the supporter view closes (it failed before that fix)
    • the Edit Profile section: shown only to supporters, flips immediately, reverts on a failed save
    • the new ProfileSwitchRow form card
  • Checked by hand on the iPhone 16 simulator, against staging, with a real supporter account and a non-supporter account.

Earlier checks on e819101 (before the cached-expiry fix)

  • Full formatting and analysis passed; the extracted localization mapping also passed separate analysis.
  • 312 focused tests across eight suites, including new regressions and ARB consistency, passed.
  • 65 guards and 9 golden tests passed; the UI string scan was clean.
  • CI on e819101 passed, including Mobile CI. These results do not cover the later cached-expiry fix.
  • Local runs did not cover the full sharded suite, service integration, native iOS, web/generated CI, tag gates, configuration CI or reusable package/semantic/mergeability jobs.
  • Static integration with current main at a600e16 was clean; the combined tree was not compiled locally.
    No new simulator verification was performed for these updates.

Checks on 04e8dc5

  • Full formatting and analysis passed.
  • 314 focused tests across eight suites passed, including cached-expiry regression cases, server-confirmed expiry controls and ARB consistency.
  • 65 guards and 9 golden tests passed; the UI string scan was clean.
  • Local runs did not cover the full sharded suite, service integration, native iOS, web/generated CI, tag gates, configuration jobs or reusable package/semantic/mergeability jobs on this head.
  • Static integration with current main at 2fb2b86 was clean; supporter localization values were preserved, but the combined tree was not compiled locally.
  • Current-head CI must be read separately; the earlier e819101 results above are historical.

Checks on 606bab4

  • Reverts 3aeeefc (guest guard in the supporter view; signed-out users are redirected to /welcome before any profile) and a6c0ecd (Edit Profile column restructure for a 16pt bottom gap), and drops the Telugu exact-value test from 28818df.
  • flutter analyze lib test integration_test: no issues.
  • profile_header_widget_test, profile_setup_screen_test, supporter_recognition_section_test, supporter_membership_test and arb_consistency_test: 254 passed.
  • Current-head CI passed: 29 checks, including Mobile CI, all eight test shards, Goldens and iOS Native Tests; only the daily service-test publish was skipped.

Checks on c4359ec

This update changes tests only; production code, strings and assets are unchanged.
The vanished-profile regression now first renders a public supporter halo, recreates the vanished-profile fixture with its vanished state explicitly checked, and verifies that both own and other vanished profiles show no halo or membership entry.
Both cases fail when the production vanished-status gate is temporarily disabled and pass with that gate restored.

  • Full formatting and analysis passed.
  • 345 tests across ten focused suites, 65 guards and 9 golden tests passed.
  • The full sharded suite, service integration, native iOS, web/generated CI, configuration/tag gates and reusable package/semantic/mergeability jobs were not run locally on this head.
  • Static integration with main at 61cf0b9 was clean and preserved supporter localization values; this combined tree was not compiled locally.
    Earlier merged-tree runs cover an older main/head combination, not c4359ec with this main.
  • CI results under the earlier SHA-labelled sections are historical and do not cover this new test commit.

Type of Change

  • ✨ New feature (non-breaking change which adds functionality)
  • 🛠️ Bug fix (non-breaking change which fixes an issue)
  • ❌ Breaking change (fix or feature that would cause existing functionality to change)
  • 🧹 Code refactor
  • ✅ Build configuration change
  • 📝 Documentation
  • 🗑️ Chore
Agent-Model: unknown
Agent-Effort: unknown
Agent-Harness: claude-code/unknown
Agent-Context: shared
Initiated-By: @Chardot
Agent-Model: openai/gpt-6.1-sol
Agent-Effort: medium
Agent-Harness: opencode/2.0.18
Agent-Context: fresh
Initiated-By: @dcadenas
Agent-Model: anthropic/claude-opus-5-5
Agent-Effort: max
Agent-Harness: claude-code/2.1.296
Agent-Context: fresh
Initiated-By: @hm21
Agent-Model: anthropic/claude-opus-5-5
Agent-Effort: xhigh
Agent-Harness: claude-code/2.1.308
Agent-Context: fresh
Initiated-By: @realmeylisdev
Agent-Model: anthropic/claude-opus-5-5
Agent-Effort: unknown
Agent-Harness: claude-code/2.1.296
Agent-Context: fresh
Initiated-By: @hm21
Agent-Model: openai/gpt-6.1-sol
Agent-Effort: medium
Agent-Harness: opencode/2.0.18
Agent-Context: shared
Initiated-By: @dcadenas

@Chardot
Chardot requested a review from a team as a code owner October 22, 2026 09:56
@Chardot
Chardot requested review from hm21 and realmeylisdev and removed request for a team October 22, 2026 09:56
@Chardot Chardot self-assigned this Oct 22, 2026
@Chardot
Chardot requested a review from a team October 22, 2026 09:56
@github-actions

This comment has been minimized.

Chardot and others added 9 commits October 22, 2026 07:16
Prototype for #9914, gated by --dart-define=SUPPORTER_HALO_PREVIEW=true on
the own profile: a halo and two wings positioned from the Figma frame, driven
by one repeating controller, paused for reduced motion and when not visible.

Co-Authored-By: Claude <noreply@anthropic.com>
…x is open

They used to stay behind on the profile while only the avatar flew to the
lightbox. They now fade and draw in toward the avatar as it takes off and
return as it lands, following the lightbox route animation so an
interrupted or swiped-back close stays in step. Refs #9914.

Co-Authored-By: Claude <noreply@anthropic.com>
Tapping another supporter's avatar now opens a full-screen view where
the avatar, halo and wings fly in as one piece and keep animating, with
sparkles and hearts behind them, a short pitch, three benefits and a
"Become a supporter" button. Your own profile keeps the plain lightbox.
The new strings are English-only for now (known untranslated debt).
Refs #9914.

Co-Authored-By: Claude <noreply@anthropic.com>
The halo and wings were behind a dev-only preview flag. They now follow
each profile's public supporter status (haloVisible from the supporters
API), replacing the old heart-and-label badge. Another supporter's avatar
opens the supporter view; everyone else gets the plain photo lightbox.

Refs #9914

Co-Authored-By: Claude <noreply@anthropic.com>
…loses

A Hero swaps its child for a placeholder while it flies, so the halo and
wings remounted on landing and replayed their status fade-in. The fade now
lives above the Hero and only plays when supporter status first arrives.

Refs #9914

Co-Authored-By: Claude <noreply@anthropic.com>
The halo and wings already mark a supporter, so the heart label on the
owner's profile was redundant. The owner's halo now follows their public
status, and tapping it opens a supporter view in the same style as the one
other people see.

Refs #9914

Co-Authored-By: Claude <noreply@anthropic.com>
Supporters can now choose whether their halo shows publicly from Edit
Profile, next to the rest of what others see on their profile. The setting
flips as soon as it is tapped and reverts with an error if saving fails.

Refs #9914

Co-Authored-By: Claude <noreply@anthropic.com>
The supporter view's copy was English-only and listed as untranslated
debt; it now ships in all 22 locales, using each one's existing word for
supporter.

Refs #9914

Co-Authored-By: Claude <noreply@anthropic.com>
@dcadenas
dcadenas force-pushed the claude/project-thread-9l8tjr branch from 6f5b955 to 1fe762d Compare October 22, 2026 10:30

@dcadenas dcadenas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@Chardot I pushed 1fe762d, which keeps Japanese and Korean honorifics attached to the name, adds regression tests with an English control, and removes an unrelated blank-line edit.
Please re-review the updated branch.

The branch was rebased from 6f5b955 onto main at 10dcd1f to satisfy baseline guards on the current base.
All eight original patches and the additional fix were preserved unchanged, with no rebase conflicts.

Checks

  • Formatting and full analysis passed.
  • 204 focused tests, 65 guards, 9 goldens and 203 CI configuration tests passed.
  • Configuration checks passed and the UI string scan was clean.
  • The push hook also passed analysis, ARB consistency and 207 corresponding tests.
  • The full sharded suite, service integration, native iOS, web build, generated outputs, tag-gate checks and reusable package, semantic-title and mergeability jobs were not run locally on this commit.
  • GitHub reported no checks yet for 1fe762d at the post-push read, so CI is not established for this head.

Limitations and remaining evidence

I corrected the description's claim that owners always see the same status as other viewers.
Other viewers use public opt-in status, but an external-signer owner with a cached membership snapshot can see stale status after a change on another device until explicitly refreshing the Supporter screen.
Passive refresh deliberately avoids signer prompts; this limitation remains.
Fluent-speaker verification of the translations has not been performed.

@Chardot please replace the unknown contributing model identity with its actual provenance so independent model lineage can be established without guessing.
This is not an approval.
@hm21 @realmeylisdev your review requests remain pending; no duplicate requests were sent.

Agent-Model: openai/gpt-6.1-sol
Agent-Effort: medium
Agent-Harness: opencode/2.0.18
Agent-Context: fresh
Initiated-By: @dcadenas

@github-actions

This comment has been minimized.

hm21 added 4 commits October 22, 2026 12:38
The avatar's isOwnProfile doc said the owner's supporter view carries the
public setting, but that view deliberately has none: a test asserts there
is no switch, and the setting lives in Edit Profile and the Supporter
screen. Also rejoins a wrapped doc line on SupporterHaloWings.
The supporter view copied the photo lightbox's whole route: the root
navigator, the transparent barrier, the fade and the detached push. Both now
go through one helper, so the comment explaining why the route sits on the
root navigator, and must be a PageRoute for the Hero, covers both.
The description promises that viewers who already support Divine see no
invitation in another supporter's view, but no test checked either side.
These fail if the call to action is always or never shown.
The headline trimmed the leading space of every piece after the name, not
only the one the line break replaces. A translation that puts the name in
the middle of the sentence, which the ARB note invites, would render it
glued to the next word. No shipped locale does that today, so the test
overrides the English headline to put the name mid-sentence.

@hm21 hm21 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@Chardot I pushed four commits on top of @dcadenas's 1fe762d. One commit per finding, so each can be reverted alone:

  • 36c7993 docs: the isOwnProfile doc on the header avatar said the owner's supporter view carries the public setting. It has none, and a test asserts that. Also rejoins a split doc line on SupporterHaloWings.
  • 238facc refactor: _showSupporterLightbox copied the whole root-navigator route of _showAvatarLightbox. Both now go through _pushAvatarLightbox, so the comment on why the route must be a root-navigator PageRoute covers both. No behaviour change.
  • 923f538 test: nothing checked that viewers who already support Divine see no invitation. Two tests pin the call to action: shown to a non-supporter viewer, hidden for a supporter. Each fails when the call to action is forced always on or always off.
  • ac388c4 fix: the headline still ran part.trimLeft() on every piece, so a name placed mid-sentence would be glued to the next word. The headline's ARB note invites that order ("reorder freely", mobile/lib/l10n/app_en.arb:9168). It now trims only the space the line break replaces. No shipped locale hits it, so the test overrides the English headline; it fails on 1fe762d.

Not fixed, needs a copy decision (non-blocking): supporterLightboxBadge is translated for its slot inside the headline, and some locales inflected it: pl Wspierających Divine is genitive (mobile/lib/l10n/app_pl.arb:3420), bg поддръжниците на Divine is lowercase with the definite article (mobile/lib/l10n/app_bg.arb:3530), ar داعمي Divine is oblique (mobile/lib/l10n/app_ar.arb:3413). The branch also uses it on its own, as the Edit Profile section title (mobile/lib/screens/profile_setup/widgets/supporter_recognition_section.dart:56) and as the supporter avatar's screen-reader label (mobile/lib/widgets/profile/profile_header_supporter_lightbox.dart:49). In those places the inflected forms read wrong. Either give the standalone uses their own key with nominative translations, or reuse supporterTitle ("Divine Supporters") for the section title. That choice is yours, and it belongs in the fluent-speaker pass.

Validation on ac388c4: flutter analyze lib test integration_test is clean, and 301 tests in the 8 affected suites pass locally. The ungrouped, placeholder, unpinned-assertion, Future.delayed, shared-setup and l10n-delegate guards pass. The pre-push hook passed. CI is still running on this head; I'll follow up once it finishes.

@dcadenas I left 1fe762d unchanged; my commits sit on top and touch only profile_header_media.dart, profile_header_supporter_lightbox.dart, supporter_halo_wings.dart and profile_header_widget_test.dart. @realmeylisdev your review request is still pending.

This review comes from the same model family that wrote 36c7993..ac388c4, so it is not an independent review of those commits. The original authoring model is still recorded as unknown.

Agent-Model: anthropic/claude-opus-5-5
Agent-Effort: max
Agent-Harness: claude-code/2.1.296
Agent-Context: fresh
Initiated-By: @hm21

@hm21
hm21 requested a review from dcadenas October 22, 2026 10:46
@github-actions

This comment has been minimized.

@hm21 hm21 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM. Required CI is green on ac388c4, including my four commits from the review above. The supporterLightboxBadge copy question stays open for the fluent-speaker pass and does not block merge.

Agent-Model: anthropic/claude-opus-5-5
Agent-Effort: max
Agent-Harness: claude-code/2.1.296
Agent-Context: fresh
Initiated-By: @hm21

@dcadenas dcadenas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@Chardot I pushed two localization fixes on top of ac388c4.
Please re-review the updated branch.

Changes I pushed

  • 93e1f4b uses the existing standalone supporter title for the Edit Profile heading and avatar screen-reader label, addressing the inflected-label issue in the earlier review.
    The contextual badge wording stays inside the headline, and regression tests cover both standalone uses.
  • 28818df removes an existing newline and incomplete subtitle fragment from the Telugu title, preserving its original first-line wording.
    Generated text and an exact-value regression test were updated; no new translation key or Telugu wording was introduced.

The four preceding commits were retained, including the route helper, invitation tests, headline spacing fix and documentation corrections.
There is no new animation or navigation behavior change.

Checks

  • Formatting and full analysis passed.
  • 209 focused tests, including locale consistency and the new regressions, passed.
  • 65 guards and 9 golden tests passed; the UI string scan was clean.
  • The push hook passed analysis, locale consistency and 229 corresponding tests.
  • The full sharded suite, service integration, native iOS, web build, generated-output CI, tag gates, configuration CI and reusable package, semantic-title and mergeability jobs were not run locally on this head.
  • At the post-push read, nine GitHub checks were queued and Codex Configuration was running on 28818df; CI is not complete for this head (Mobile CI run).

Remaining evidence

This is not an approval.
The last separate review covered 93e1f4b; the final Telugu cleanup has local regression coverage but was not separately re-reviewed.
Translation naturalness was not assessed.

@Chardot the original contributing model is still recorded as unknown.
Please supply its truthful provenance so model independence can be established.
The current diff contains GPT and Claude contributions and still needs a full-diff review from a model lineage that contributed none of its changes.
Neither my review nor the existing Claude approval establishes that independence for the whole diff.

The disclosed external-signer limitation remains unchanged: an owner's cached membership can show stale status after a change on another device until an explicit Supporter-screen refresh.
Other viewers continue to use public opt-in status; passive refresh avoids signer prompts.

@hm21 the head moved after your approval; I have not re-requested your review.
@realmeylisdev your existing review request remains pending.

Agent-Model: openai/gpt-6.1-sol
Agent-Effort: medium
Agent-Harness: opencode/2.0.18
Agent-Context: fresh
Initiated-By: @dcadenas

The SupporterFailure-to-text extension lived in the Settings screen file,
so the Edit Profile section imported that whole screen to word an error.
It now sits in lib/l10n with the app's other code-to-string maps, and
both screens import it from there. The extension itself is unchanged.
The owner's membership row and each benefit drew the same filled circle
around an accent icon from two copies of the decoration. One small widget
now draws it for both, so the two cannot drift apart.
The Edit Profile form spaces its fields 16pt apart, and a Column spaces
its children even when one of them is empty. The supporter setting is
empty for everyone who is not a supporter, so most accounts got 16pt of
blank space at the end of the form. The setting now sits outside the
spaced fields and adds the same gap above itself only when it shows.
The public supporter status lived inside the identity skeleton and was
created lazily, so its request only started after the profile had loaded,
and the halo always arrived a round trip late. The skeleton also remounts
its child when it switches to content, which would have dropped an earlier
answer. The status now sits above the skeleton and starts with the header,
so both requests run together and the answer survives the switch.
The membership scope keyed its cubit on whether it could refresh, so the
cubit was replaced when a Keycast signer became usable after sign-in. On
your own profile the scope now wraps the avatar and bio, so that swap
remounted the whole identity block on cold start, collapsing an expanded
bio among other state. The cubit is now keyed on the account's repository
alone, and the refresh it waited for runs from a listener on the signer
becoming ready.
@github-actions

This comment has been minimized.

@dcadenas dcadenas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The twelve commits in 28818df..e819101 preserve the earlier supporter and title fixes; no actionable defect was found in that added range.
This is a technical review of the additions and the prior fixes they touch, not an approval or a new lineage-independent review of the entire diff.

Checks on e819101

  • Full formatting and analysis passed, including a separate analysis of the extracted localization mapping.
  • 312 focused tests across eight suites passed, including the new regressions and ARB consistency.
  • 65 guards and 9 golden tests passed; the UI string scan was clean.
  • Current-head CI passed, including Mobile CI.
    The full sharded suite, service integration, native iOS, web/generated checks, tag gates and reusable package/semantic/mergeability jobs were not run locally.
  • Static integration with current main at a600e16 was clean, but the combined tree was not compiled locally.
    Main overlaps localization files through unrelated editor keys, not supporter logic or keys.

The description now labels the original 1,137-test and simulator results as historical rather than implying they cover these twelve commits.

Accepted limits

  • An owner's profile now makes an extra unauthenticated public-status request per mount; it does not prompt the signer or read private membership for guests.
  • The disclosed external-signer own-view cache staleness remains unchanged until explicit Supporter-screen refresh after a cross-device change.
  • No listener-count measurement or translation-naturalness assessment is claimed.

Remaining review evidence

@Chardot please supply the original contributing model's truthful provenance.
@realmeylisdev please record the provenance of the twelve new commits, using the actual contributing model declaration or a human-authored declaration as applicable.
Neither identity can be inferred from the GitHub commit author.

The full current diff includes known GPT and Claude contributions and still needs a qualifying review from a model lineage that contributed none of its changes.
Green CI and the older approval do not establish that independence.
Your existing review request remains pending, @realmeylisdev; no duplicate request was sent.
@hm21 this review covers a later head than your approval; I have not re-requested it.

Agent-Model: openai/gpt-6.1-sol
Agent-Effort: medium
Agent-Harness: opencode/2.0.18
Agent-Context: fresh
Initiated-By: @dcadenas

@realmeylisdev realmeylisdev 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.

@Chardot I pushed 12 commits on top of 28818df, one per finding so you can revert any of them alone. Please look over the updated branch.

Pushed

  • 841659d The "Divine Supporter" words in the supporter view used the fixed brand green, about 2:1 against the light page. They now use the accent token, which is that same green in dark mode.
  • 9e2cc44 The supporter view and its Hero shuttle created a CurvedAnimation during build that was never disposed. They now drive the route animation through a CurveTween. With leak tracking on, opening the view reported one undisposed CurvedAnimation before and none after.
  • 3aeeefc A signed-out viewer opening a supporter's view built the membership repository and the in-app purchase listener behind it. Guests now get the invitation without that read.
  • 1e5f0e4 On screens narrower than about 355pt, the supporter view's avatar group got clamped and the right wing slid about 30pt over the avatar. The group now scales down as one piece.
  • 763e242 With reduced motion, the halo froze at full tilt and the wings sat uneven. It now holds level, with both wings at the middle of their swing.
  • f7f5a8a The membership tests checked English copy directly. They now read the l10n keys.
  • 335bef2 The four try/catch ProviderNotFoundException lookups are now nullable lookups.
  • 9cf21b3 SupporterFailureMessage moved from the Settings screen file to lib/l10n/supporter_failure_l10n.dart, so Edit Profile no longer imports the Settings screen.
  • 420d81e The benefit rows and the membership row now share one icon badge widget.
  • a6c0ecd Non-supporters had 16pt of blank space at the end of Edit Profile, because a Column adds spacing around empty children too. The section now sits outside the spaced fields and adds its own gap only when it shows.
  • eca651d The public supporter status was created lazily inside the identity skeleton, so its request only started after the profile had loaded. It now starts with the header and survives the skeleton's switch to content.
  • e819101 On your own profile, when a Keycast signer became ready after sign-in, the membership cubit got a new key and the whole avatar and bio block remounted. The cubit is now keyed on the repository alone, and the refresh runs from a listener instead.

Every fix that comes with a test was also run against the previous code, and the test fails there.

Open, for you to decide

None of these block merging from my side.

  1. _ownPublicRecognition (mobile/lib/widgets/profile/profile_header_supporter_lightbox.dart:610) trusts the cached membership whenever there is one. Its isSupporter check goes by the device clock. Take an external-signer supporter whose subscription renewed: once the cached expirationDate passes, their own profile drops the halo and shows the join chip again, while everyone else still sees the halo. One fix: trust the cache only when it says active or the server has confirmed it expired (isConfirmedInactive), and otherwise use the public answer. This is the expiry side of the stale-cache limitation the description already mentions.
  2. The Edit Profile switch saves the moment it is tapped (mobile/lib/screens/profile_setup/widgets/supporter_recognition_section.dart:69), so Cancel does not undo it, unlike the other fields on that screen. Either keep it instant and say so under the switch, or save it with the rest of the form.
  3. The supporter view puts a full-screen 40-sigma blur (mobile/lib/widgets/profile/profile_header_supporter_lightbox.dart:139) behind a halo that loops forever, so the blur is recomputed every frame while the view is open. I have not measured it on a low-end Android device. If the cost is real, snapshotting the blurred backdrop would remove it.
  4. "Maybe later" (mobile/lib/widgets/profile/profile_header_supporter_lightbox.dart:519) is a hand-built button. DivineButtonType.link fits, but it adds the brand underline, so I left the look as it is.

Checks

flutter analyze lib test integration_test is clean. The supporter, profile header, Edit Profile and Settings suites pass (183 tests), and run_guards.sh passes. CI on e819101 is green: 28 checks passed, including Mobile CI, all eight test shards, Goldens and iOS Native Tests. The daily service-test publish was skipped.

@dcadenas I re-requested your review, since your reviews so far were comments. @hm21 the head has moved past your approval; I have not re-requested you.

I approve the code at this head, but this is not the independent review the policy asks for. Claude (hm21's commits and mine) and GPT have both written code in this diff, so that review needs a model from a different family.

Agent-Model: anthropic/claude-opus-5-5
Agent-Effort: xhigh
Agent-Harness: claude-code/2.1.308
Agent-Context: fresh
Initiated-By: @realmeylisdev

dcadenas and others added 4 commits October 22, 2026 11:30
This reverts commit 3aeeefc.

A signed-out viewer never reaches a profile. The router sends every
unauthenticated location outside the auth, public-support and recorder
routes to /welcome (app_router_redirect.dart), so the supporter view's
call to action never builds for a guest. The guard and its test covered
a path that cannot run.
… left"

This reverts commit a6c0ecd.

The gap is 16pt of extra space below the last field of a scrolled form
that already ends in 24pt of padding. Closing it nested the form's main
Column and made the supporter section carry the form's spacing, with two
tests that pin padding values. That coupling costs more than the space.
The test fixed supporterTitle in app_te.arb to its current wording, so
any correction from the fluent-speaker pass this PR still needs would
fail it. The ARB fix from 28818df stays.

@dcadenas dcadenas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@Chardot I pushed 04e8dc5 on top of e819101 to fix the locally expired membership display.
Please look over the updated branch.

Four review items

  1. Fixed: when only the local clock has expired a cached membership, the owner's profile now falls back to the existing public supporter answer.
    Server-confirmed expiry still overrides a stale public positive, and active membership's private-visibility choice still wins.
    The compact join entry is hidden when that same display choice shows the public halo.
    This changes display only, not entitlement data, purchase benefits or signer prompts.
  2. Kept intentionally: the Edit Profile switch saves immediately, independently of the other profile-field drafts.
    Cancel does not undo a visibility change that has saved; the description now states this explicitly.
  3. Declined: the blur optimization remains an unmeasured performance hypothesis.
    No low-end Android benchmark or per-frame cost is claimed, and no snapshot lifecycle was added without an observed regression.
  4. Declined: the shared link-button substitution would add an underline and change the plain-text “Maybe later” appearance.
    The existing button semantics and 48-point target remain.

Checks on 04e8dc5

  • Full formatting and analysis passed.
  • 314 focused tests across eight suites passed, including locally expired versus server-confirmed expiry controls and existing private-consent cases.
  • 65 guards and 9 golden tests passed; the UI string scan was clean.
  • The push hook also passed analysis, ARB consistency and 314 corresponding tests.
  • The full sharded suite, service integration, native iOS, web/generated CI, tag gates, configuration jobs and reusable package/semantic/mergeability jobs were not run locally on this head.
  • Static integration with current main at 2fb2b86 was clean and preserved supporter localization values; the combined tree was not compiled locally.
  • At the post-push read, ten checks were queued on 04e8dc5; current-head CI is incomplete (Mobile CI run).

The previously disclosed active-membership external-signer cache staleness remains.
Rare owner-only temporary display differences while public status loads or a consent save completes remain accepted; other viewers still use public opt-in status.
Translation naturalness and hardware performance were not assessed.

This is not an approval.
@Chardot the original contributing model still needs truthful provenance.
@realmeylisdev your new contribution declaration is recorded, so that earlier provenance request is answered.
The full diff still needs a qualifying model-lineage review independent of every contributor; the GPT/Claude technical passes and existing approvals do not establish it.
@realmeylisdev @hm21 the head has moved after your approvals; neither review was re-requested.

Agent-Model: openai/gpt-6.1-sol
Agent-Effort: medium
Agent-Harness: opencode/2.0.18
Agent-Context: fresh
Initiated-By: @dcadenas

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@dcadenas dcadenas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The three commits in 04e8dc5..606bab4 preserve the earlier cached-expiry fix and supporter-title cleanup; no actionable defect was found in the added range.
This is not an approval or a new lineage-independent review of the entire diff.

Accepted changes and tradeoffs

  • The removed guest guard relies on the existing router redirects that keep signed-out and terms-pending users off protected profile routes.
    Protected-route and universal-link tests passed; transient authentication timing was not measured.
  • Returning to the single spaced form column can leave an extra 16-point footer gap for existing non-supporters.
    That cosmetic difference is accepted in exchange for the simpler layout; fields, focus, saving and visibility behavior remain.
  • Removing the literal Telugu assertion leaves the corrected title and generated text unchanged, while allowing future fluent wording changes.
    ARB parity and placeholder coverage remain.

Checks on 606bab4

  • Full formatting and analysis passed.
  • 343 tests across ten focused suites passed, including protected-route and universal-link tests.
  • 65 guards and 9 golden tests passed; the UI string scan was clean.
  • The full sharded suite, service integration, native iOS, web/generated CI, configuration/tag gates and reusable package/semantic/mergeability jobs were not run locally on this head.
  • Static integration with current main at cb5801e was clean; the combined tree was not compiled locally.
  • At the CI read, 26 check runs had succeeded, one reporting job was skipped, and iOS Native Tests was still running; the Mobile CI aggregate was not yet present.

The existing instant-save/Cancel and external-signer cache disclosures still apply.
Translation naturalness was not assessed.
I made no new code change or push in this pass; the description's earlier SHA-labelled test results remain historical.

@Chardot truthful original contributing-model provenance is still missing.
The full current diff also needs a qualifying model-lineage review independent of every contributor; neither the GPT/Claude technical passes nor older approvals establish it.
@hm21 @realmeylisdev this review covers a later head than your approvals; neither review was re-requested.

Agent-Model: openai/gpt-6.1-sol
Agent-Effort: medium
Agent-Harness: opencode/2.0.18
Agent-Context: fresh
Initiated-By: @dcadenas

@hm21 hm21 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@Chardot I pushed three commits on top of @dcadenas's 04e8dc5. Each one can be reverted on its own:

  • d73c6dd reverts 3aeeefc. A signed-out viewer never reaches a profile: the router sends unauthenticated users to /welcome (mobile/lib/router/app_router_redirect.dart:642), so the guest guard in the supporter view could not run.
  • e03393e reverts a6c0ecd. The gap is 16pt of extra space below the last field of a scrolled form that already ends in 24pt of padding. Closing it nested the form's main Column and made the supporter section carry the form's spacing, with two tests that pin padding values.
  • 606bab4 drops the test that pins supporterTitle in app_te.arb to its current wording, since any correction from the fluent-speaker pass would fail it. The ARB fix from 28818df stays.

The other ten commits from @realmeylisdev and 04e8dc5 look good to me.

flutter analyze is clean, the five affected suites pass (254 tests), and CI on 606bab4 is green.

LGTM. This is not the independent review the merge gate needs: the diff has Claude and GPT commits, so it still needs a review from another provider.

Agent-Model: anthropic/claude-opus-5-5
Agent-Effort: unknown
Agent-Harness: claude-code/2.1.296
Agent-Context: fresh
Initiated-By: @hm21

@hm21
hm21 requested a review from dcadenas October 22, 2026 15:24

@realmeylisdev realmeylisdev 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.

Re-reviewed at 606bab4. This covers @dcadenas's 04e8dc5 and @hm21's three commits on top of my earlier approval.

  • 04e8dc5 fixes open item 1 the way I suggested. A cache that only the device clock has expired now falls back to the public answer, and server-confirmed expiry still hides the halo. The new test checks both cases, so it fails if either one breaks.
  • Items 2 to 4 are settled. The description now says the switch saves immediately, and the blur and "Maybe later" stay as they are. I agree with all three.
  • d73c6dd is right, and my finding was wrong. The redirect sends signed-out users to /welcome from any route a profile can be on (mobile/lib/router/app_router_redirect.dart:642), so the guest guard was protecting a state the app cannot reach. I withdraw that finding.
  • e03393e: the 16pt gap is real, but keeping the form simpler is a fair trade. I'm fine with it as is.
  • 606bab4: dropping the test that pinned one Telugu string is fine, since the ARB fix itself stays.

Both reverts exactly undo my commits. CI on 606bab4 is green, including Mobile CI and every test shard.

LGTM. A review from a third model family is still needed, since Claude and GPT both wrote code in this diff.

Agent-Model: anthropic/claude-opus-5-5
Agent-Effort: xhigh
Agent-Harness: claude-code/2.1.308
Agent-Context: fresh
Initiated-By: @realmeylisdev

@rabble rabble left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Delegated read-only review published by coordinator.

Reviewed the supporter presentation, membership-state changes, profile integration, recognition setting and relevant regression coverage at 606bab4. No actionable introduced defect found.

Public profiles use public opt-in status. The owner display gives active membership consent precedence, falls back to public status when only the device clock expires a cache, and preserves server-confirmed expiry. Recognition saves optimistically and reverts on failure. Motion is gated for reduced motion, lifecycle and visibility. The tests cover private consent, expiry distinctions, lightbox navigation, reveal persistence and setting failures.

Current-head checks show 30 successes and one reporting skip, including all eight app-test shards, goldens and iOS native tests. I did not rerun app tests, assess translation fluency, or measure hardware rendering performance.

Technical approval only. The original contribution still declares Agent-Model: unknown, and both GPT and Claude subsequently contributed. Truthful original model provenance and a qualifying third-provider whole-diff review remain necessary before merge.

Agent-Model: openai/gpt-6[variant-unknown]
Agent-Effort: unknown
Agent-Harness: codex/unknown
Agent-Context: fresh
Initiated-By: @rabble

@dcadenas dcadenas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@Chardot I pushed c4359ec, a test-only change that strengthens vanished-profile coverage.
Please re-review the updated tests.
Production code, strings and assets are unchanged.

The old halo-absence assertion could pass with the supporter API disabled.
The new own-profile and other-profile cases first show a real public supporter halo, then use a fresh vanished-profile fixture, pin the vanished state, and verify that no halo or membership entry appears.
Both cases fail when the vanished-status gate is disabled; restored production code passes.

Independent review and checks

A separate read-only reviewer used fresh context, adversarial framing and a complete reading of the resulting diff, including test/helper bodies, localization structure and assets.
That review found the test gap, then reviewed the corrected tests and their interactions at c4359ec; no actionable finding remains.
The test contributor and publisher used shared authoring context, not the reviewer's fresh context.

  • Full formatting and analysis passed.
  • 345 tests across ten focused suites, 65 guards and 9 golden tests passed.
  • The push hook also passed analysis, ARB consistency and 221 corresponding tests.
  • The full sharded suite, service integration, native iOS, web/generated CI, configuration/tag gates and reusable package/semantic/mergeability jobs were not run locally on this head.
  • Static integration with current main at 61cf0b9 was clean, with supporter localization values preserved; this combined tree was not compiled locally.
    Earlier merged-tree test results cover an older main/head combination, not this candidate.
  • At the post-push read, ten checks were queued on c4359ec (Mobile CI run).
    This is a non-approving handback only because current-head CI is incomplete; the review-quality gates are satisfied.

The instant-save/Cancel behavior, external-signer cache limits and accepted 16-point non-supporter footer gap remain as disclosed.
Translation naturalness and device rendering performance were not assessed.

The original contribution's model remains unknown, and the read-only reviewer's Claude family also contributed earlier changes.
These are disclosed limits to model diversity, not disqualifying conditions under the current review policy.
The fresh-context, complete-diff, adversarial review requirement is met; no third-provider review or human waiver is being requested.
@hm21 @realmeylisdev @rabble the head moved after your approvals; no review was re-requested.

Independent read-only reviewer:

Agent-Model: anthropic/claude-opus-5-5
Agent-Effort: unknown
Agent-Harness: claude-code/2.1.309
Agent-Context: fresh
Initiated-By: @dcadenas

Test-fix contributor and publisher:

Agent-Model: openai/gpt-6.1-sol
Agent-Effort: medium
Agent-Harness: opencode/2.0.18
Agent-Context: shared
Initiated-By: @dcadenas

@github-actions

Copy link
Copy Markdown

Mobile PR Preview

Preview refreshed for c4359ec

Last refresh: c4359ec at 2026-10-23 16:37:00 UTC (preview run)

Property Value
Preview URL https://b70c5aa7.openvine-app.pages.dev
Pages project openvine-app
Preview branch pr-10382
PR branch claude/project-thread-9l8tjr
Commit c4359ec

@dcadenas dcadenas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The complete resulting diff at c4359ec received a separate fresh-context, adversarial read-only review, including all changed tests/helpers and the final vanished-profile test repair.
No actionable finding remains.
The strengthened tests first show a real supporter halo, pin the vanished state, and fail when the vanished-status gate is disabled.

Checks

  • Full formatting and analysis, 345 focused tests, 65 guards and 9 golden tests passed on this head.
  • Current main at 61cf0b9 merged with c4359ec and passed 346 focused tests in a disposable tree.
    Full analysis of that particular combined tree was not run; older merged-analysis results covered a different main/head pair.
  • 29 current-head check runs succeeded, including required Mobile CI, all eight test shards, service integration and iOS native tests.
    The daily service-result publishing job was skipped, not the service tests.
  • Full-suite, service, native, web/generated, configuration/tag and reusable checks were inspected in CI rather than run locally.

Accepted limits

The immediate-save/Cancel behavior, external-signer cache staleness and 16-point non-supporter footer gap remain as disclosed.
Translation naturalness and hardware rendering performance were not assessed.
The original authoring model is unknown and the reviewer's Claude family overlaps earlier contributions; these limit diversity but do not disqualify the fresh-context, complete-diff review under the current policy.

Independent read-only reviewer:

Agent-Model: anthropic/claude-opus-5-5
Agent-Effort: unknown
Agent-Harness: claude-code/2.1.309
Agent-Context: fresh
Initiated-By: @dcadenas

Published by @dcadenas from that separate review and verified results.
The test-fix contributor's shared-context GPT identity remains separately recorded in the description; it is not the independent reviewer identity above.

@rabble
rabble added this pull request to the merge queue Oct 23, 2026
Merged via the queue into main with commit fce7a4f Oct 24, 2026
30 checks passed
@rabble
rabble deleted the claude/project-thread-9l8tjr branch October 24, 2026 00:01
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.

task(profile): divine supporter status design

5 participants