Repository navigation
Store the sign-out notice in the write that clears the session - #78
Merged
Merged
Conversation
ShopkeeperEditViewModel set isEmailUpdated / isMyAccountDeleted from viewModelScope after the session was cleared. Clearing the session routes to onboarding, which pops Edit Profile and cancels that scope, so the flag write could be lost and the "check your email" / "account deleted" snackbar would not show. New SignOutNotice; logout(notice) and clearSession(notice) store it in the same DataStore write that clears the session. Port of NativeAppTemplate-Android #110. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K5hZEFqXhBsUQwSWsgR6eH
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.
Phase 2 follow-up (bug fixes). Port of NativeAppTemplate-Android #110.
Problem
After you change the email or delete the account in Settings → Edit Profile, onboarding should show a snackbar ("check your email" / "account deleted"). Whether it does depends on a race.
Cause
ShopkeeperEditViewModelsetisEmailUpdated/isMyAccountDeletedfromviewModelScopeafter the session was cleared:logout()thensetIsEmailUpdated(true)clearSession()thensetIsMyAccountDeleted(true)Clearing the session routes to onboarding, which pops Edit Profile and cancels its
viewModelScope. If that happens before the next suspension point returns, the flag write never runs. On the Pixel the timing happened to win (both snackbars showed in #77's device check), because Free has no FCM token deletion after the clear, unlike paid. The race is still there, and the unit test below reproduces it deterministically.Fix
SignOutNoticeenum (EMAIL_UPDATED,ACCOUNT_DELETED).LoginRepository.logout(notice)andclearSession(notice)take the notice (defaultnull, so the other callers are unchanged).NativeAppTemplatePreferencesDataSource.clearSession(isEmailUpdated, isMyAccountDeleted)writes the notice in the same DataStore write that clears the session.ShopkeeperEditViewModelpasses the notice and no longer sets the flags itself.logoutalready clears the session (now with the notice) when the request fails, so the email-change message also shows when the sign-out request fails.Tests
ShopkeeperEditViewModelTest):updateShopkeeper_withANewEmail_whenTheScreenClosesOnSignOut_stillReportsTheEmailChangeanddeleteShopkeeper_whenTheScreenClosesOnSignOut_stillReportsTheDeletion.TestLoginRepository.onSessionClearedlets a test cancel the view model's scope and thenyield(), as the onboarding route does. Both failed on the unchanged main code with anAssertionError(flag false). I added only the hook to the double first, with no API change, ran them red, and then made the fix.LoginRepositoryImplTest, backed by a store that records every DataStore write:logout_withANotice_storesItInTheWriteThatSignsOutandclearSession_withANotice_storesItInTheWriteThatSignsOut: the first write withisLoggedIn == falsealready carries the notice.logout_withANotice_whenTheServerRejectsIt_stillStoresTheNoticelogout_withoutANotice_clearsAnEarlierOne...InTheWriteThatSignsOuttests red. An earlier version that observeduserDatamissed this in thelogoutcase because the StateFlow conflated the two writes. That is why the test records writes instead.clearSessionalways writefalseturned three notice tests red.flag || it.flag) turnedlogout_withoutANotice_clearsAnEarlierOnered.Local checks
./scripts/pre-push(spotless, lint, R8 minify, unit tests): passed. 406 tests (app + model), 0 failures../gradlew :app:assembleRelease: passed.Device check (Pixel,
com.nativeapptemplate.nativeapptemplatefreedebug build, local API)With a throwaway shopkeeper (signed up in the app, confirmed via the API, deleted at the end):
The app is left signed in as shopkeeper1.
🤖 Generated with Claude Code
https://claude.ai/code/session_01K5hZEFqXhBsUQwSWsgR6eH