Repository navigation
Refactor: simplify session bootstrap, repositories, app state and swipe rows - #79
Merged
Merged
Conversation
Data.updateRelationships returned a copy with the same relationships (or the same instance), so getDatumWithRelationships() was the decoded datum and Shop/ItemTag.getData() was datum. The callers now read datum directly and the no-op is gone; nothing merges "included", as before. DataRelationshipsTest pinned the no-op itself (equal copy, same instance, getDatumWithRelationships == datum). Those cases tested the removed functions, so they go; the behavior they guarded (included resources are not merged, getData() is the decoded data, getShopName() does not come from included) stays covered by the remaining cases. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K5hZEFqXhBsUQwSWsgR6eH
Shop Detail and the item tag list wrote Data.isOptionsRevealed into a toMutableList() copy built during composition. Nothing observed that copy and the next composition rebuilt it, so the writes were lost and isRevealed was always the decoded default, false (the API never sends the field). Whether a row is open has always lived in the row's own Animatable. SwipeableItemWithActions loses isRevealed/onExpanded/onCollapsed (its LaunchedEffect keeps animating to closed when the actions width changes, as it did with isRevealed = false), the screens read the list from the UI state directly, and Data loses isOptionsRevealed. SwipeableItemWithActionsTest characterizes the row (swipe left opens it and it stays open across a parent recomposition; swipe right closes it). It passed against the old API (with isRevealed = false, as the screens passed) before the change and passes after; each case went red when the matching drag-end branch was broken. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K5hZEFqXhBsUQwSWsgR6eH
- Drop isLoggedIn, shouldUpdateApp, shouldUpdatePrivacy and shouldUpdateTerms: nothing reads them (routing uses authDestination). - shouldShowBottomBar repeated authDestination's combine of the same four flows; it is now authDestination == SHOPS, which is the same condition (signed in, no app/privacy/terms update pending) and the same initial false. - The flag StateFlows share one stateIn helper. NativeAppTemplateAppStateTest (unchanged) covers shouldShowBottomBar, authDestination and the flags. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K5hZEFqXhBsUQwSWsgR6eH
Every pass-through repository method built the same flow by hand: read the
account id, call the API, emitApiResponse (or emitApiResponse<Status,
Boolean> { true }), flowOn(ioDispatcher). apiRequest/apiAction
(network/ApiResponseExtensions.kt) and
NativeAppTemplatePreferencesDataSource.currentAccountId() now hold that
once; each method only names its API call. The requests, the dispatcher and
the error mapping are unchanged (RepositoryRequestsTest covers every
endpoint; no test changed). Same helpers as NativeAppTemplate-Android #112.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5hZEFqXhBsUQwSWsgR6eH
Sign-in stored the session, synced the locale and fetched the permissions in the sign-in screen's viewModelScope. Storing the session routes to the shop list, which clears that view model and cancels its scope while the permissions request (and the locale request) are still in flight, so a fresh session could run without permissions until the next resume. Likewise the sign-out after an email change ran in Edit Profile's scope and stopped if the screen closed before the session was cleared. SessionLifecycle (@singleton, @ApplicationScope) now owns these steps: start(), refreshPermissions() and signOut() run in the application scope and the caller awaits them, so the screens behave as before but cancelling them no longer cancels the work. The "fetch permissions, sign out only on 401" logic that SignInEmailAndPasswordViewModel and MainActivityViewModel each carried now lives there once. Ported from NativeAppTemplate-Android #112 (with its runCatchingCancellable helper). New tests (each seen red with the steps run in the caller's scope, i.e. on the code before this commit): - SignInEmailAndPasswordViewModelTest .login_whenTheScreenClosesMidBootstrap_stillStoresThePermissionsAndSyncsTheLocale - ShopkeeperEditViewModelTest .updateShopkeeper_withANewEmail_whenTheScreenClosesDuringTheSignOutRequest_stillSignsOut TestLoginRepository gains an onLogoutRequest hook for the second one. Existing tests only changed how the view models are constructed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K5hZEFqXhBsUQwSWsgR6eH
- ItemTagDetailViewModel.completeItemTag and idleItemTag were the same function apart from the repository call; both now go through toggle(). - ShopkeeperEditViewModel.deleteShopkeeper wrote out by hand what catchShowingError does (message, loading stopped); use it. No test changed (ItemTagDetailViewModelTest and ShopkeeperEditViewModelTest cover both paths). 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 5 (refactoring) for NativeAppTemplate-Free-Android. No intended behavior change apart from the sign-in bootstrap / email-change sign-out now surviving their screen closing (aligned with NativeAppTemplate-Android #112).
Changes (one commit each)
Data.updateRelationships– it returned a copy with the same relationships (or the same instance), sogetDatumWithRelationships()wasdatumandShop/ItemTag.getData()wasdatum. Callers readdatum; nothing mergesincluded, as before.DataRelationshipsTest: the cases that called the removed functions are gone (they tested the no-op itself); the behavior they guarded (included resources are not merged,getData()is the decoded data,getShopName()does not come fromincluded) stays covered.Data.isOptionsRevealedinto atoMutableList()copy built during composition; nothing observed it and the next composition rebuilt it, soisRevealedwas alwaysfalse.SwipeableItemWithActionslosesisRevealed/onExpanded/onCollapsed;DatalosesisOptionsRevealed; the screens read the list from UI state. NewSwipeableItemWithActionsTest(swipe left opens and stays open across a parent recomposition; swipe right closes) passed against the old API before the change and after it.NativeAppTemplateAppState– unusedisLoggedIn/shouldUpdateApp/shouldUpdatePrivacy/shouldUpdateTermsremoved;shouldShowBottomBar=authDestination == SHOPS(same condition, same initial false); onestateInhelper. ExistingNativeAppTemplateAppStateTestunchanged.apiRequest/apiAction(network/ApiResponseExtensions.kt) andcurrentAccountId(), same helpers as paid #112.RepositoryRequestsTest(every endpoint) unchanged.SessionLifecycle(@singleton on @ApplicationScope:start,refreshPermissions,signOut; callers await). Storing the session routes away from sign-in and cancelled the screen's scope while the permissions/locale requests were in flight; the email-change sign-out could likewise be cut short. The "fetch permissions, sign out only on 401" logic of the sign-in VM andMainActivityViewModellives there once. AddsrunCatchingCancellable(as in paid).completeItemTag/idleItemTaggo throughtoggle();deleteShopkeeperusescatchShowingError(identical state update).New tests and red checks
SignInEmailAndPasswordViewModelTest.login_whenTheScreenClosesMidBootstrap_stillStoresThePermissionsAndSyncsTheLocale– red on the pre-change code (shopLimitCount expected 99, was -1), green after.ShopkeeperEditViewModelTest.updateShopkeeper_withANewEmail_whenTheScreenClosesDuringTheSignOutRequest_stillSignsOut– red on the pre-change code, green after (uses the newTestLoginRepository.onLogoutRequesthook).SwipeableItemWithActionsTest– each case red when its drag-end branch was broken (open → animate to 0; close → animate to open).Existing tests otherwise only changed how view models are constructed (new
sessionLifecycleparameter).Local checks
./scripts/pre-push(Spotless, lint, R8 shrink, all unit tests): passed; 395 app unit tests, 0 failures../gradlew :app:assembleRelease: passed.@Parcelizeannotation changed.Device check (Pixel, Free debug build, local API)
Shop list (counts), Shop1 detail: swipe reveals Complete, completing/idling works and rows close after reload; Manage Item Tags: swipe reveals Delete and swipe back closes it; item tag detail: Mark as completed / Mark as idled (toggle); Edit Item Tag: rename and rename back ("Item tag updated successfully."); Settings and Edit Profile load. Sign-out → sign-in as shopkeeper1 with this build: sign-out routed to onboarding (
DELETE /shopkeeper_auth/sign_out200), sign-in landed on the shop list with the tabs shown, and the server log showsPOST /shopkeeper_auth/sign_in200 followed byGET /{account}/api/v1/shopkeeper/permissions200 andGET .../shops200 (the bootstrap's permissions request now runs in the app scope). The app was left signed in as shopkeeper1.🤖 Generated with Claude Code
https://claude.ai/code/session_01K5hZEFqXhBsUQwSWsgR6eH