Skip to content

fix(mobile): bound useAfterInteractions so the profile account section always renders - #6411

Open
iscekic wants to merge 4 commits into
mainfrom
kwf/req-20260920-025104-f692
Open

iscekic wants to merge 4 commits into
mainfrom
kwf/req-20260920-025104-f692

Conversation

@iscekic

@iscekic iscekic commented Sep 20, 2026

Copy link
Copy Markdown
Collaborator

Changelog for users

  • The profile screen always shows its Linked accounts section, with the signed-in email address, even when the UI never reports idle.
  • The account section appears within half a second of opening the screen instead of possibly never.

Changelog for maintainers

  • useAfterInteractions reports resolved after at most 500 ms (AFTER_INTERACTIONS_FALLBACK_MS), so work gated on it can no longer wait forever.
  • Chose the bounded fallback over ungating the account queries: the queries still wait for the tab transition to settle, and the bound fixes the hook for every caller.
  • Ungating was worse: both queries would start during the transition animation, discarding the deferral the screen deliberately relies on and adding fetch load while the screen is still animating.
  • The account-query gate in the profile screen is unchanged; only its explanatory comment is new.
  • A settled guard plus cleanup of the interaction handle and fallback timer prevent state updates after unmount or a late interaction callback.
  • The screen spec mocks and helpers moved to a shared test helper so both specs can withhold the interaction callback.
  • The screen spec gains the never-idle repro; the new hook spec covers mount, settled-without-fallback, fallback-only, late-callback, and cleanup.
  • Review hint: the 500 ms bound is the only new contract; check it first if transition-settled timing matters.

E2E proof

[e2] Force the never-idle state in the worktree (make InteractionManager.runAfterInteractions in apps/mobile/src/lib/hooks/use-after-interactions.ts never invoke its callback, e.g. — android emulator-5554: with the staged never-idle hook live, a cold open of Profile gives 'SCENE e2 OK' with LINKED ACCOUNTS tappable [55,1061][1025,1105] and e2e-mobile-req-20260920-025104-f692-android@example.com tappable [189,1211][998,1248] (the 500 ms AFTER_INTERACTIONS_FALLBACK_MS bound releases them); the same forced state without the fallback leaves those rows absent in e2-control-baseline.txt (header at bounds [55,1233][1025,1277], no Email/address node); the forcing was reverted with git checkout and git status --porcelain plus git diff --stat are empty.

[e2] Force the never-idle state in the worktree (make InteractionManager.runAfterInteractions in apps/mobile/src/lib/hooks/use-after-interactions.ts never invoke its callback, e.g. — e2e-mobile-app/e2-control-baseline.png

E2E proof — log excerpts

[e1] Signed in, cold open the profile screen on the built app: the Linked accoun -> pass :: android emulator-5554, packed tree: the scripted digest shows 'SCENE e1 OK' with android.widget.TextView LINKED ACCOUNTS tappable [55,1061][1025,1105] and the provider row e2e-mobile-req-20260920-025104-f692-android@example.com tappable [189,1211][998,1248], so the account row renders on the cold profile open.
[e2] Force the never-idle state in the worktree (make InteractionManager.runAfte -> pass :: android emulator-5554: with the staged never-idle hook live, a cold open of Profile gives 'SCENE e2 OK' with LINKED ACCOUNTS tappable [55,1061][1025,1105] and e2e-mobile-req-20260920-025104-f692-android@example.com tappable [189,1211][998,1248] (the 500 ms AFTER_INTERACTIONS_FALLBACK_MS bound releases them); the same forced state without the fallback leaves those rows absent in e2-control-baseline.txt (header at bounds [55,1233][1025,1277], no Email/address node); the forcing was reverted with git checkout and git status --porcelain plus git diff --stat are empty.
/home/igor_kilocode_ai/.local/share/kwf/sections/req-20260920-025104-f692/e2e-mobile-app/scripted-e1.log
android.widget.TextView Preferences tappable [189,729][933,775]
android.widget.TextView Appearance, notifications, thinking, and screen behavior tappable [189,779][933,816]
android.widget.Button Tutorial tappable [55,872][1025,1006]
android.widget.TextView Tutorial tappable [189,916][933,962]
android.widget.TextView LINKED ACCOUNTS tappable [55,1061][1025,1105]
android.widget.TextView Email tappable [189,1161][998,1207]
android.widget.TextView e2e-mobile-req-20260920-025104-f692-android@example.com tappable [189,1211][998,1248]
android.widget.TextView Test Account tappable [189,1332][998,1378]
android.widget.TextView e2e-mobile-req-20260920-025104-f692-android@example.com tappable [189,1382][998,1419]
android.widget.Button Feedback tappable [55,1502][1025,1628]
android.widget.TextView Feedback tappable [171,1542][989,1588]
android.widget.Button Privacy choices tappable [55,1656][1025,1782]
android.widget.TextView Privacy choices tappable [171,1696][989,1742]
android.widget.Button Sign out tappable [55,1809][1025,1935]
android.widget.TextView Sign out tappable [171,1849][989,1895]
android.widget.Button Delete Account tappable [55,1963][1025,2089]
android.widget.TextView Delete Account tappable [171,2003][989,2049]
android.widget.TextView v1.0.12 (1) tappable [55,2116][1025,2153]
android.view.View Home, tab, 1 of 3 tappable [0,2195][360,2337]
android.widget.TextView HOME tappable [13,2281][347,2320]
android.view.View Agents, tab, 2 of 3 tappable [360,2195][720,2337]
android.widget.TextView AGENTS tappable [373,2281][707,2320]
android.view.View Profile, tab, 3 of 3 tappable [720,2195][1080,2337]
android.widget.TextView PROFILE tappable [733,2281][1067,2320]
/home/igor_kilocode_ai/.local/share/kwf/sections/req-20260920-025104-f692/e2e-mobile-app/e2-forced-never-idle.log
android.widget.TextView Preferences tappable [189,729][933,775]
android.widget.TextView Appearance, notifications, thinking, and screen behavior tappable [189,779][933,816]
android.widget.Button Tutorial tappable [55,872][1025,1006]
android.widget.TextView Tutorial tappable [189,916][933,962]
android.widget.TextView LINKED ACCOUNTS tappable [55,1061][1025,1105]
android.widget.TextView Email tappable [189,1161][998,1207]
android.widget.TextView e2e-mobile-req-20260920-025104-f692-android@example.com tappable [189,1211][998,1248]
android.widget.TextView Test Account tappable [189,1332][998,1378]
android.widget.TextView e2e-mobile-req-20260920-025104-f692-android@example.com tappable [189,1382][998,1419]
android.widget.Button Feedback tappable [55,1502][1025,1628]
android.widget.TextView Feedback tappable [171,1542][989,1588]
android.widget.Button Privacy choices tappable [55,1656][1025,1782]
android.widget.TextView Privacy choices tappable [171,1696][989,1742]
android.widget.Button Sign out tappable [55,1809][1025,1935]
android.widget.TextView Sign out tappable [171,1849][989,1895]
android.widget.Button Delete Account tappable [55,1963][1025,2089]
android.widget.TextView Delete Account tappable [171,2003][989,2049]
android.widget.TextView v1.0.12 (1) tappable [55,2116][1025,2153]
android.view.View Home, tab, 1 of 3 tappable [0,2195][360,2337]
android.widget.TextView HOME tappable [13,2281][347,2320]
android.view.View Agents, tab, 2 of 3 tappable [360,2195][720,2337]
android.widget.TextView AGENTS tappable [373,2281][707,2320]
android.view.View Profile, tab, 3 of 3 tappable [720,2195][1080,2337]
android.widget.TextView PROFILE tappable [733,2281][1067,2320]
/home/igor_kilocode_ai/.local/share/kwf/sections/req-20260920-025104-f692/e2e-mobile-app/e2-control-baseline.txt
<redacted>
<redacted>
                                        </com.horcrux.svg.GroupView>
                                      </com.horcrux.svg.SvgView>
                                    </android.view.View>
                                  </android.view.ViewGroup>
                                </android.view.ViewGroup>
                              </android.view.ViewGroup>
                            </android.widget.ScrollView>
                          </android.view.ViewGroup>
                        </android.view.ViewGroup>
                      </android.view.ViewGroup>
<redacted>
                    </android.view.ViewGroup>
                  </android.view.ViewGroup>
                </android.view.ViewGroup>
              </android.widget.FrameLayout>
            </android.widget.FrameLayout>
          </android.widget.FrameLayout>
        </android.widget.LinearLayout>
      </android.widget.FrameLayout>
    </android.widget.LinearLayout>
  </android.widget.FrameLayout>
</hierarchy>
Owner request

Surface: the mobile app (apps/mobile).

The profile screen can finish loading with no account section at all. apps/mobile/src/components/profile-screen.tsx gates the trpc.user.getAuthProviders and organizations.list queries on useAfterInteractions(). Under an automated UI session InteractionManager sometimes never reports idle, so those queries never start and the Linked accounts row -- the only place the app renders the signed-in email address -- never appears. The screen looks loaded and is missing its identity.

Evidence: of 27 passing sign-in probes, 27 had the account row on the first screen dump; of 40 failing ones, 0 had it after four dumps. That is 40 occurrences in one day.

Fix it so the account section cannot be lost this way: either give useAfterInteractions a timeout fallback that resolves anyway, or do not gate the account queries on interactions at all. Whichever you choose, say why the other is worse.

Prove it with the account row rendered on a cold open of the profile screen, and with a second capture showing it still renders when interactions never report idle -- force that state in the worktree to capture it, then revert the forcing before you finish.

Follow-ups (not changed here)

  • not proved live: iOS: not run — the diff forks on no platform, so Android proves both

…n always renders

Surface: the mobile app (apps/mobile).

The profile screen can finish loading with no account section at all. apps/mobile/src/components/profile-screen.tsx gates the trpc.user.getAuthProviders and organizations.list queries on useAfterInteractions(). Under an automated UI session InteractionManager sometimes never reports idle, so those queries never start and the Linked accounts row -- the only place the app renders the signed-in email address -- never appears. The screen looks loaded and is missing its identity.

Evidence: of 27 passing sign-in probes, 27 had the account row on the first screen dump; of 40 failing ones, 0 had it after four dumps. That is 40 occurrences in one day.

Fix it so the account section cannot be lost this way: either give useAfterInteractions a timeout fallback that resolves anyway, or do not gate the account queries on interactions at all. Whichever you choose, say why the other is worse.

Prove it with the account row rendered on a cold open of the profile screen, and with a second capture showing it still renders when interactions never report idle -- force that state in the worktree to capture it, then revert the forcing before you finish.
@kilo-code-bot

kilo-code-bot Bot commented Sep 20, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Executive Summary

The incremental changes since the previous review extract the Profile screen's Android destructive-confirm platform check into destructive-confirm-platform.ts, keeping the screen free of a platform branch so screen-insets.test.ts passes, with test-only mock and hitSlop-helper adaptations; the change is leak-free and no defects were found.

Files Reviewed (6 changed files)
  • apps/mobile/src/lib/destructive-confirm-platform.ts
  • apps/mobile/src/components/profile-screen.tsx
  • apps/mobile/src/components/profile-screen.queries.mounted.test.tsx
  • apps/mobile/src/components/profile-screen.signout.mounted.test.tsx
  • apps/mobile/src/components/agents/new-session-configure-form.test.ts
  • apps/mobile/src/components/agents/session-list-header-actions.mounted.test.tsx

Unchanged since the previous review and re-verified: apps/mobile/src/lib/hooks/use-after-interactions.ts, apps/mobile/src/lib/hooks/use-after-interactions.mounted.test.tsx, apps/mobile/src/components/profile-screen.test-helpers.tsx.

Previous Review Summary (commit 9a81b7d)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 9a81b7d)

Status: No Issues Found | Recommendation: Merge

Executive Summary

The bounded-fallback fix in use-after-interactions.ts is correct and leak-free: the settled guard plus cleanup of the interaction handle and fallback timer prevent post-unmount state updates, and the tests cover the never-idle path.

Files Reviewed (5 files)
  • apps/mobile/src/lib/hooks/use-after-interactions.ts
  • apps/mobile/src/components/profile-screen.tsx
  • apps/mobile/src/lib/hooks/use-after-interactions.mounted.test.tsx
  • apps/mobile/src/components/profile-screen.test-helpers.tsx
  • apps/mobile/src/components/profile-screen.queries.mounted.test.tsx

Reviewed by deepseek-v4.1-flash · Input: 0 · Output: 0 · Cached: 0

Review guidance: REVIEW.md from base branch main

@iscekic

iscekic commented Sep 20, 2026

Copy link
Copy Markdown
Collaborator Author

This description names a scenario the proof did not capture:

  • not proved live: iOS: not run — the diff forks on no platform, so Android proves both

A repeated proof run rebuilds the same evidence, so no proof run is dispatched for a named gap. Merging with this gap open is your decision.

@iscekic iscekic added the human-ready The PR is ready for human review. label Sep 20, 2026
@iscekic iscekic self-assigned this Sep 20, 2026

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

Approved after a shallow triage pass. No blocking findings.

@iscekic iscekic added the merge-by-human the merge bot routed this PR to a human label Sep 21, 2026
@iscekic

iscekic commented Sep 21, 2026

Copy link
Copy Markdown
Collaborator Author

The merge bot leaves this one to a human: Jev reads it as a human's merge (confidence 0.83).

@iscekic iscekic removed the merge-by-human the merge bot routed this PR to a human label Sep 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

human-ready The PR is ready for human review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants