Skip to content

feat(feature/auth): migrate LoginViewModel to SubmitHandler - #2685

Closed
therajanmaurya wants to merge 2 commits into
openMF:store5Migrationfrom
therajanmaurya:feat/store5-c2-auth
Closed

feat(feature/auth): migrate LoginViewModel to SubmitHandler#2685
therajanmaurya wants to merge 2 commits into
openMF:store5Migrationfrom
therajanmaurya:feat/store5-c2-auth

Conversation

@therajanmaurya

Copy link
Copy Markdown
Member

Summary

Phase C Wave 2 — migrates LoginViewModel to SubmitHandler<PostAuthenticationResponse>, consuming Store5 primitives through com.mifos.core.data.store re-exports (the architectural pattern established in Wave 1 #2684).

Chained on Wave 1 #2684 — merge that first. This PR contains only the auth-specific change.

Changes (1 file, +57 / -49)

feature/auth/.../login/LoginViewModel.kt:

  • Single SubmitHandler<PostAuthenticationResponse> (via com.mifos.core.data.store.submitHandler) replaces viewModelScope.launch { ... .collect { ... } }
  • submit.state collected in init { ... } via launchIn(viewModelScope) + onEach — maps SubmitState to LoginUiState:
    • SubmittingShowProgress
    • Submitted<PostAuthenticationResponse> → persists user via prefManager.updateUser, then PassCodeActivityIntent
    • FailedShowError (logs the actual error throwable)
  • Validation logic untouched (it's a pre-submit gate, sets ShowValidationError directly)
  • LoginUseCase Flow<DataState<T>> bridged via .first { it !is DataState.Loading }:
    • DataState.Error → throw terminal.exception
    • DataState.Success with authenticated == true → return response
    • DataState.Success with authenticated != true → throw sentinel LoginRejectedException
  • No feature/auth/build.gradle.kts changes needed — the implementation(projects.core.data) dep already present exposes the com.mifos.core.data.store.* re-exports

What this PR does NOT change

  • LoginUiState.kt — sealed-class definition unchanged (Empty, ShowProgress, ShowError, ShowValidationError, PassCodeActivityIntent)
  • LoginScreen.kt — Screen-side consumer code unchanged
  • LoginUseCase / UsernameValidationUseCase / PasswordValidationUseCase — domain layer untouched
  • Any other feature module — pure feature/auth scoped change

Pattern proven on second feature

Wave 1 established the pattern on activate (3 isolated submit methods with ActivateUiState mapping). Wave 2 validates the same pattern on auth with extra complexity:

  • Single submit method that produces a typed response (PostAuthenticationResponse) consumed in Submitted handler for a follow-up side effect (prefManager.updateUser)
  • Mixed-source UiState — ShowValidationError set outside SubmitHandler.state flow (pre-submit gate), ShowProgress / ShowError / PassCodeActivityIntent driven by SubmitHandler state

This validates that the re-export layer + SubmitHandler are flexible enough for both "fire-and-forget" (activate) and "act-on-success-response" (auth) flows.

Test plan

  • ./gradlew :feature:auth:build compiles cleanly
  • No template.core.base.store.* imports in feature/auth/**
  • Login happy path: validate → progress → success → navigation
  • Login failure path: validate → progress → ShowError
  • Validation-only failure: ShowValidationError without entering SubmitHandler
  • Auth-rejected case (authenticated != true): ShowError (sentinel exception path)

Phase status

  • ✅ Phase A · ✅ Phase B
  • 🟢 Phase C Wave 1 (#2684activate)
  • 🟢 Phase C Wave 2 (this PR — auth)
  • 🔵 Phase C Waves 3–7
  • 🔵 Phase D

Related

  • Wave 1 PR: #2684 (must merge first — this branch is chained on it)

Rajan Maurya added 2 commits May 21, 2026 18:09
Replaces manual DataState collection with template.core.base.store's
SubmitHandler — a one-shot, idempotent submission executor with built-in
Submitting / Submitted / Failed lifecycle and automatic cancel-prior-job
semantics for sequential calls.

VM:
  - Single SubmitHandler<Unit> instead of three viewModelScope.launch blocks
  - submit.state mapped to existing ActivateUiState via .map.stateIn so
    Screen-side state shape (Initial / Loading / ActivatedSuccessfully /
    Error) is unchanged — no Screen changes required
  - Per-action success/failure StringResource tracked in two vars set
    before each submit; safe because they're written synchronously with
    submit.submit { ... } and only read when state transitions to a
    terminal value
  - ActivateClientUseCase / ActivateCenterUseCase (Flow<DataState<T>>)
    bridged via .first { it !is DataState.Loading } + throw on Error
  - ActivateGroupUseCase (suspend, returns DataState<Unit> directly) just
    checks for Error and throws

build.gradle.kts:
  + implementation(projects.coreBase.store)

This is the migration template for all subsequent mutation-feature
migrations (auth, savings, recurringDeposit, note, checker-inbox-task,
collectionSheet, report, document, loan, client). Each follows the same
shape: introduce a SubmitHandler<R> in the VM, map its state to the
feature's existing UiState, bridge use-case return shapes through
SubmitHandler.submit { ... }, leave the Screen untouched.
…data re-exports

Establishes the app-facing dependency chain for Store5 submission primitives:

  core-base/store -> core/store -> core/data -> feature/*

Feature modules consume Store5 abstractions through 'com.mifos.core.data.store'
re-exports — they never import 'template.core.base.store.*' directly.

New file: core/data/src/commonMain/.../store/SubmitTypes.kt
  - typealias SubmitHandler<R> = template.core.base.store.submit.SubmitHandler<R>
  - typealias SubmitState<R>   = template.core.base.store.submit.SubmitState<R>
  - fun CoroutineScope.submitHandler<R>(): SubmitHandler<R>  (delegates)

core/data/build.gradle.kts:
  - api(projects.coreBase.store)  ->  api(projects.core.store)
    Routes the framework primitives through core/store (the consumer seam)
    instead of bypassing it.

feature/auth/build.gradle.kts:
  No new dep required — Store5 types flow in via projects.core.data's api.

LoginViewModel:
  - Replaces .collect { when (DataState) } with SubmitHandler<PostAuthenticationResponse>
  - submit.state collected in init via launchIn + onEach; maps SubmitState
    to existing LoginUiState (Empty / ShowProgress / ShowError / ShowValidationError /
    PassCodeActivityIntent). Screen API unchanged.
  - Validation logic untouched (pre-submit gate, not a Submit state)
  - LoginUseCase Flow<DataState<T>> bridged via .first { !Loading } + throw on Error
  - Auth-rejected case (DataState.Success but authenticated != true) throws a
    sentinel LoginRejectedException — handled by SubmitState.Failed
  - Persists user via prefManager.updateUser, then sets PassCodeActivityIntent
@therajanmaurya
therajanmaurya requested a review from a team May 21, 2026 12:41
@coderabbitai

coderabbitai Bot commented May 21, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (1)
  • development

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e05b6c62-9283-4d3c-8ad6-5d87ad740e05

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sonarqubecloud

Copy link
Copy Markdown

@therajanmaurya

Copy link
Copy Markdown
Member Author

Closing — superseded by the revised end-to-end plan. Wave 2 (auth) will be re-opened after Wave 1 (activate) lands.

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.

1 participant