Conversation
LoginPresenter now owns the login error state and closes the screen once GodToolsAccountManager reports an authenticated account, which covers both a successful login and opening the screen while already logged in. LoginLayout becomes a stateless @CircuitInject UI that sends UiEvents for close, login and error dismissal. The login launcher registers Activity result callbacks from a composable, so it is wrapped in a LoginLauncherProducer interface with a DefaultLoginLauncherProducer backed by rememberLoginLauncher and a FakeLoginLauncherProducer for presenter tests. The existing stateful LoginLayout overload is kept as a thin wrapper so LoginActivity keeps compiling until callers are switched over.
The Login and Create Account drawer actions now open LoginScreen with startCircuitActivity, which already provides the theme, edge-to-edge and back handling. LoginActivity, its startLoginActivity() helper and its manifest entry are no longer reachable, so they are removed.
The stateful LoginLayout overload and LoginLayoutEvent only existed for LoginActivity, which was replaced by the Circuit-hosted LoginScreen.
ktlint wants a blank line between every when-condition once one of them spans several lines, and the Success branch has a comment above it.
Keying the effect on the collected isAuthenticated value let it pop the navigator more than once when the account was already signed in, which could also close the screen below LoginScreen. Wait for the first authenticated value in a single effect instead, like LoginActivity's finishWhenAuthenticated() did.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## develop #4654 +/- ##
===========================================
+ Coverage 53.56% 54.59% +1.02%
===========================================
Files 440 442 +2
Lines 11572 11590 +18
Branches 1944 1946 +2
===========================================
+ Hits 6198 6327 +129
+ Misses 4790 4675 -115
- Partials 584 588 +4 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
smgoss
marked this pull request as ready for review
September 29, 2026 14:11
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Resolves #4610 (GT-2783)
Login was still hosted by its own Activity (
LoginActivityhosting a statefulLoginLayout). This converts it to a Circuit screen launched withstartCircuitActivity, following the same structure as the Delete Account conversion (DeleteAccountScreen/DeleteAccountPresenter/DeleteAccountLayout).Changes
LoginScreen:@Parcelize data class LoginScreen(val createAccount: Boolean = false) : ParcelableScreen.LoginPresenter: nestedUiState(createAccount,loginError) andUiEvent(Login(AccountType),ClearError,Close), plain@AssistedFactory @CircuitInjectfactory. It owns the login error state (stillrememberSaveable) and a smallCloseWhenAuthenticated()helper that pops the navigator onceGodToolsAccountManager.isAuthenticatedFlowreports an authenticated account. That covers both a successful login and opening the screen while already logged in.Closepops the navigator.LoginLauncherProducer: the login launcher registers Activity result callbacks from a composable, so it can't be mocked with plain mockk. Following theToolFiltersStateProducerpattern, it is an interface withDefaultLoginLauncherProducer(a thin wrapper around the existingrememberLoginLauncher, so Google and Facebook still go through the account-manager abstraction), bound in a newLoginModule, plus a hand-writtenFakeLoginLauncherProducerfor tests.LoginLayout: now a stateless@CircuitInjectUI taking(state: UiState, modifier: Modifier = Modifier)that sends UiEvents.TEST_TAG_*constants were added. The visuals are unchanged.DrawerMenuLayout: the Login and Create Account actions callcontext.startCircuitActivity(LoginScreen(...)).LoginActivity,startLoginActivity()and the.ui.login.LoginActivitymanifest entry in the switch commit. RemovedLoginLayoutEventand the temporary statefulLoginLayoutoverload (kept only so commit 1 compiles) in a separate orphan commit.Commits: 1) add the Circuit screen with tests, 2) switch callers and delete
LoginActivity, 3) remove orphaned code.Behavior change to be aware of:
LoginResponse.Successno longer closes the screen directly. The screen closes when the account manager reports the authenticated account. Both providers store the user id on success, soisAuthenticatedFlowflips right away. This avoids popping twice (the old Activity calledfinish()twice, which did nothing extra, but a doublepop()would also pop the previous screen ifLoginScreenis ever pushed onto an existing back stack).Intentionally not done:
GT_BLUEbackground,FACEBOOK_BLUEcolor and null content descriptions in the layout were kept as they were, so this PR has no visual changes.onClicklines inDrawerMenuLayout.Tests
LoginPresenterTest(src/test):createAccountis passed through to state and to the launcher; a login error shows inloginError; a successful login shows no error; the screen pops once authenticated and when already authenticated;Loginlaunches Google and Facebook;ClearErrordismisses the error;Closepops.LoginLayoutTest(testDebug,v2.runComposeUiTest): heading for login vs create account, close icon, Google and Facebook buttons, and the error dialog (hidden, shown, confirm firesClearError).LoginLayoutPaparazziTest: Login, Create Account and Error scenarios. Goldens are not recorded yet; run the Record Snapshots workflow on this branch and fold the result into the first commit../gradlew :build-logic:ktlintCheck ktlintCheck,./gradlew :app:testProductionDebugUnitTestandverifyPaparazzi(after recording) in CI.Review
Checked against the project conventions for Circuit, migration and testing, a correctness review, a security review and a simplification pass. Nothing needed changing.
LoginScreen/LoginPresenter/LoginLayoutfile set is present. The screen is@Parcelize data class ... : ParcelableScreen. The presenter has a plain nested@AssistedFactory @CircuitInjectFactory, andpresent()stays short.LoginLauncherProducerfollows the interface +Default*+@Binds+Fake*pattern, andLoginModulesits in the feature package. The drawer usesstartCircuitActivity. The switch commit and the orphan-removal commit are separate.LoginLayoutPaparazziTestis present.LoginActivity,startLoginActivityorLoginLayoutEventin code or manifests.rememberLoginLauncherfalls back to the Hilt entry point, so it works insideCircuitActivity.CircuitActivitywas already exported, andLoginScreengives no new privilege.rememberUpdatedState(state.eventSink)matches the owners' pattern.Generated by Claude Code