Skip to content

refactor(note): add DataState, optimize Flow, add DTOs, entities, domain models - #2666

Merged
therajanmaurya merged 25 commits into
openMF:developmentfrom
itsPronay:datastate-extension
May 5, 2026
Merged

refactor(note): add DataState, optimize Flow, add DTOs, entities, domain models#2666
therajanmaurya merged 25 commits into
openMF:developmentfrom
itsPronay:datastate-extension

Conversation

@itsPronay

@itsPronay itsPronay commented Apr 12, 2026

Copy link
Copy Markdown
Member

JIRA - https://mifosforge.jira.com/browse/MIFOSAC-769

Screen Recording uploaded to Slack by @Arinyadav1:

datastate-flow-dto-entity-domain-models.mp4

Summary by CodeRabbit

  • New Features
    • Added explicit offline detection with a dedicated "network unavailable" error and consistent success/error state wrapping for async operations.
  • Bug Fixes
    • Improved sync flow error handling to show a distinct offline message when there’s no connectivity and to reliably surface success or failure states to the UI.

@itsPronay
itsPronay requested a review from a team April 12, 2026 10:25
@coderabbitai

coderabbitai Bot commented Apr 12, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Added a new NetworkUnavailableException, two suspend helpers runAsDataState (network-aware and basic) to produce DataState, updated SyncGroupPayloadsRepository and its implementation to return DataState<SaveResponse> and accept dispatcher/networkMonitor, and adjusted the ViewModel to branch on DataState including network-specific error handling.

Changes

Cohort / File(s) Summary
Network exception
core/data/src/commonMain/kotlin/com/mifos/core/data/util/NetworkUnavailableException.kt
Added NetworkUnavailableException(message: String = "Network is unavailable. Please check your connection and try again.") : Throwable.
DataState utilities
core/data/src/commonMain/kotlin/com/mifos/core/data/util/DataStateUtils.kt
Added two suspend helpers runAsDataState: a basic overload that runs a suspend block (optionally on a provided CoroutineDispatcher) and returns DataState.Success or DataState.Error, and a network-aware overload that checks NetworkMonitor.isOnline.first() and returns DataState.Error(NetworkUnavailableException()) when offline before delegating to the basic overload.
Repository API
core/data/src/commonMain/kotlin/com/mifos/core/data/repository/SyncGroupPayloadsRepository.kt
Changed createGroup(groupPayload: GroupPayloadEntity) return type from SaveResponse to DataState<SaveResponse>.
Repository implementation
core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/SyncGroupPayloadsRepositoryImp.kt
Constructor now accepts ioDispatcher: CoroutineDispatcher and networkMonitor: NetworkMonitor; createGroup now returns DataState<SaveResponse> and is implemented by delegating to runAsDataState(networkMonitor, ioDispatcher). Other methods unchanged.
ViewModel
feature/offline/src/commonMain/kotlin/com/mifos/feature/offline/syncGroupPayloads/SyncGroupPayloadsViewModel.kt
Replaced try/catch with when (val result = repository.createGroup(...)) handling DataState.Success/Loading/Error; DataState.Error now maps network-unavailable errors to a specific UI string resource.

Sequence Diagram

sequenceDiagram
    participant Caller
    participant RunAsDataState_Network as runAsDataState\n(network-aware)
    participant NetworkMonitor
    participant RunAsDataState_Basic as runAsDataState\n(basic)
    participant Dispatcher as CoroutineDispatcher
    participant DataState

    Caller->>RunAsDataState_Network: invoke(networkMonitor, context?, block)
    RunAsDataState_Network->>NetworkMonitor: isOnline.first()
    alt Offline
        NetworkMonitor-->>RunAsDataState_Network: false
        RunAsDataState_Network->>DataState: Error(NetworkUnavailableException())
        DataState-->>Caller: DataState.Error
    else Online
        NetworkMonitor-->>RunAsDataState_Network: true
        RunAsDataState_Network->>RunAsDataState_Basic: invoke(context?, block)
        rect rgba(100, 150, 200, 0.5)
            RunAsDataState_Basic->>Dispatcher: withContext(context) [optional]
            RunAsDataState_Basic->>RunAsDataState_Basic: execute block()
        end
        alt Success
            RunAsDataState_Basic->>DataState: Success(result)
        else Exception caught
            RunAsDataState_Basic->>DataState: Error(exception)
        end
        DataState-->>Caller: DataState<T>
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped through lines to check the net,
Wrapped outcomes neat, no panic yet.
If wires sleep, I raise my flag—
Success or error, not a snag.
Tiny paws, dependable bet.

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.18% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ⚠️ Warning The title describes general refactoring work but does not clearly summarize the main change; the actual PR introduces DataState helper utilities (runAsDataState, NetworkUnavailableException) and updates group sync to use DataState, not DTOs/entities/domain models. Align the title with the actual changes—something like 'feat(core-data): add runAsDataState helper for DataState mapping' or 'refactor: wrap group sync with DataState and network monitoring utilities' would better reflect the changeset.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

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

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
core/data/src/commonMain/kotlin/com/mifos/core/data/util/NetworkUnavailableException.kt (1)

15-17: Change to Exception as the base type for this domain error.

Inheriting from Throwable is overly broad and bypasses the exception handlers that catch Exception throughout the codebase (over 80 instances across feature, core, and library modules). For app-level failures like offline state, Exception is the appropriate choice.

♻️ Proposed change
 class NetworkUnavailableException(
     message: String = "Network is unavailable. Please check your connection and try again.",
-) : Throwable(message)
+) : Exception(message)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@core/data/src/commonMain/kotlin/com/mifos/core/data/util/NetworkUnavailableException.kt`
around lines 15 - 17, Update the domain error class NetworkUnavailableException
to extend Exception instead of Throwable so it is caught by existing exception
handlers; locate the class declaration for NetworkUnavailableException and
change its base type from Throwable to Exception, keeping the message parameter
and behavior unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@core/data/src/commonMain/kotlin/com/mifos/core/data/util/AsDataStateExtension.kt`:
- Around line 47-48: The catch block in the asDataState extension currently
swallows all Throwables and returns DataState.Error(e), which breaks structured
concurrency; update the catch (e: Throwable) in AsDataStateExtension (the
asDataState extension function) to rethrow
kotlinx.coroutines.CancellationException (i.e., if e is CancellationException
then throw e) and only convert other exceptions into DataState.Error(e).

---

Nitpick comments:
In
`@core/data/src/commonMain/kotlin/com/mifos/core/data/util/NetworkUnavailableException.kt`:
- Around line 15-17: Update the domain error class NetworkUnavailableException
to extend Exception instead of Throwable so it is caught by existing exception
handlers; locate the class declaration for NetworkUnavailableException and
change its base type from Throwable to Exception, keeping the message parameter
and behavior unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a3f81870-2fc8-4b40-9df4-f54e6f3b6ef6

📥 Commits

Reviewing files that changed from the base of the PR and between 6b66e8a and 1fd528d.

📒 Files selected for processing (2)
  • core/data/src/commonMain/kotlin/com/mifos/core/data/util/AsDataStateExtension.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/util/NetworkUnavailableException.kt

@biplab1

biplab1 commented Apr 13, 2026

Copy link
Copy Markdown
Contributor

@coderabbitai Improve PR title based on github.com/openMF/mobile-wallet/wiki/Commit-style-guide. Give us just the title.

@coderabbitai

coderabbitai Bot commented Apr 13, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@biplab1: Based on the Commit Style Guide (<type>(<scope>): <summary in present tense, lower case>), here is the improved PR title:

feat(core-data): add asDataState extension for cleaner DataState mapping

@biplab1 biplab1 changed the title Introduce asDataState for cleaner DataState mapping feat(core-data): add asDataState extension for cleaner DataState mapping Apr 13, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
core/data/src/commonMain/kotlin/com/mifos/core/data/util/DataStateUtils.kt (1)

47-48: ⚠️ Potential issue | 🟠 Major

Re-throw coroutine cancellation instead of wrapping it into DataState.Error.

Line 47–48 currently catches Throwable; this swallows CancellationException and can break cancellation/timeout propagation.

🛠️ Proposed fix
 import com.mifos.core.common.utils.DataState
+import kotlinx.coroutines.CancellationException
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.withContext
@@
-    } catch (e: Throwable) {
+    } catch (e: CancellationException) {
+        throw e
+    } catch (e: Exception) {
         DataState.Error(e)
     }
#!/bin/bash
# Verify cancellation handling in DataState utilities.
rg -nP --type=kotlin 'catch\s*\(\s*\w+\s*:\s*Throwable\s*\)' core/data/src/commonMain/kotlin/com/mifos/core/data/util -C3
rg -nP --type=kotlin 'CancellationException' core/data/src/commonMain/kotlin/com/mifos/core/data/util -C3
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core/data/src/commonMain/kotlin/com/mifos/core/data/util/DataStateUtils.kt`
around lines 47 - 48, The catch block currently swallows CancellationException
by catching Throwable and returning DataState.Error(e); change it to preserve
coroutine cancellation by detecting CancellationException and rethrowing it (or
only catch Exception instead of Throwable) before wrapping other failures into
DataState.Error so cancellation/timeouts propagate correctly; update the catch
in DataStateUtils.kt where DataState.Error(e) is returned to rethrow
CancellationException and only wrap non-cancellation errors.
🧹 Nitpick comments (1)
core/data/src/commonMain/kotlin/com/mifos/core/data/util/DataStateUtils.kt (1)

81-81: Consider using StateFlow<Boolean> in NetworkMonitor for explicit initial-value guarantees.

The current implementation emits an initial value before async setup (Android sends synchronously at line 64; other platforms use flowOf(true)), so first() won't suspend. However, using StateFlow would make this guarantee explicit in the type system and prevent future regression if the initial emission is accidentally removed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core/data/src/commonMain/kotlin/com/mifos/core/data/util/DataStateUtils.kt`
at line 81, The check using networkMonitor.isOnline.first() relies on an initial
emission but that contract isn't explicit; change NetworkMonitor.isOnline from a
Flow<Boolean> to a StateFlow<Boolean> (or expose a StateFlow wrapper) so callers
like the code in DataStateUtils.kt can rely on an immediate initial value.
Update the NetworkMonitor interface/type to return StateFlow<Boolean>, ensure
all platform implementations initialize a concrete MutableStateFlow with the
correct initial value (e.g., true/false as appropriate), and adjust usages
(e.g., the call in DataStateUtils.kt) to use the new StateFlow<Boolean> without
changing behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@core/data/src/commonMain/kotlin/com/mifos/core/data/util/DataStateUtils.kt`:
- Around line 47-48: The catch block currently swallows CancellationException by
catching Throwable and returning DataState.Error(e); change it to preserve
coroutine cancellation by detecting CancellationException and rethrowing it (or
only catch Exception instead of Throwable) before wrapping other failures into
DataState.Error so cancellation/timeouts propagate correctly; update the catch
in DataStateUtils.kt where DataState.Error(e) is returned to rethrow
CancellationException and only wrap non-cancellation errors.

---

Nitpick comments:
In `@core/data/src/commonMain/kotlin/com/mifos/core/data/util/DataStateUtils.kt`:
- Line 81: The check using networkMonitor.isOnline.first() relies on an initial
emission but that contract isn't explicit; change NetworkMonitor.isOnline from a
Flow<Boolean> to a StateFlow<Boolean> (or expose a StateFlow wrapper) so callers
like the code in DataStateUtils.kt can rely on an immediate initial value.
Update the NetworkMonitor interface/type to return StateFlow<Boolean>, ensure
all platform implementations initialize a concrete MutableStateFlow with the
correct initial value (e.g., true/false as appropriate), and adjust usages
(e.g., the call in DataStateUtils.kt) to use the new StateFlow<Boolean> without
changing behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 28697f6e-f26c-4db4-911e-ad1146d54b04

📥 Commits

Reviewing files that changed from the base of the PR and between 1f43db8 and 6ea479a.

📒 Files selected for processing (1)
  • core/data/src/commonMain/kotlin/com/mifos/core/data/util/DataStateUtils.kt

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
feature/offline/src/commonMain/kotlin/com/mifos/feature/offline/syncGroupPayloads/SyncGroupPayloadsViewModel.kt (1)

122-127: Remove nullable parameter and non-null assertion here.

groupPayload is nullable but force-unwrapped at Line 127. Make it non-null at the function boundary to prevent accidental NPE regressions.

Proposed refactor
-    private fun syncGroupPayload(groupPayload: GroupPayloadEntity?) {
+    private fun syncGroupPayload(groupPayload: GroupPayloadEntity) {
         viewModelScope.launch {
             _syncGroupPayloadsUiState.value =
                 SyncGroupPayloadsUiState.Loading

-            when (val result = repository.createGroup(groupPayload!!)) {
+            when (val result = repository.createGroup(groupPayload)) {
Based on learnings: when downstream requires non-null, avoid nullable parameters with force-unwrapping and add explicit null-safety.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/offline/src/commonMain/kotlin/com/mifos/feature/offline/syncGroupPayloads/SyncGroupPayloadsViewModel.kt`
around lines 122 - 127, Change the syncGroupPayload function signature to accept
a non-null GroupPayloadEntity (remove the nullable type) and delete the
force-unwrap (!!) inside the function; update any callers to ensure they pass a
non-null GroupPayloadEntity or perform null-checks before calling
syncGroupPayload so repository.createGroup(groupPayload) is always invoked with
a non-null value (refer to syncGroupPayload and repository.createGroup for
locating the code).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@feature/offline/src/commonMain/kotlin/com/mifos/feature/offline/syncGroupPayloads/SyncGroupPayloadsViewModel.kt`:
- Around line 127-137: The error branch currently calls updateGroupPayload()
regardless of exception type, which advances sync even on
NetworkUnavailableException; change the logic in the DataState.Error branch
handling repository.createGroup(groupPayload!!) so that after setting
_syncGroupPayloadsUiState.value = SyncGroupPayloadsUiState.Error(errorMessage)
you only call updateGroupPayload() when result.exception is NOT a
NetworkUnavailableException (i.e., skip the updateGroupPayload() call for
NetworkUnavailableException) to stop progression while offline.

---

Nitpick comments:
In
`@feature/offline/src/commonMain/kotlin/com/mifos/feature/offline/syncGroupPayloads/SyncGroupPayloadsViewModel.kt`:
- Around line 122-127: Change the syncGroupPayload function signature to accept
a non-null GroupPayloadEntity (remove the nullable type) and delete the
force-unwrap (!!) inside the function; update any callers to ensure they pass a
non-null GroupPayloadEntity or perform null-checks before calling
syncGroupPayload so repository.createGroup(groupPayload) is always invoked with
a non-null value (refer to syncGroupPayload and repository.createGroup for
locating the code).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cdd8ab05-ebf7-4fb5-93a1-0e27e17f7e75

📥 Commits

Reviewing files that changed from the base of the PR and between bbeca77 and 35166d7.

📒 Files selected for processing (4)
  • core/data/src/commonMain/kotlin/com/mifos/core/data/repository/SyncGroupPayloadsRepository.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/SyncGroupPayloadsRepositoryImp.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/util/DataStateUtils.kt
  • feature/offline/src/commonMain/kotlin/com/mifos/feature/offline/syncGroupPayloads/SyncGroupPayloadsViewModel.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • core/data/src/commonMain/kotlin/com/mifos/core/data/util/DataStateUtils.kt

@itsPronay
itsPronay force-pushed the datastate-extension branch from 57cd2f8 to 35166d7 Compare April 13, 2026 18:35
@biplab1 biplab1 changed the title feat(core-data): add asDataState extension for cleaner DataState mapping feat(core-data): add runAsDataState functions for cleaner DataState mapping Apr 13, 2026
@biplab1

biplab1 commented Apr 13, 2026

Copy link
Copy Markdown
Contributor

@itsPronay Please upload screen recording of the feature screen - with and without network.

@biplab1 biplab1 changed the title feat(core-data): add runAsDataState functions for cleaner DataState mapping refactor: Add DataState utility and optimize Flow network handling Apr 24, 2026
@biplab1 biplab1 changed the title refactor: Add DataState utility and optimize Flow network handling refactor: add DataState utility and optimize Flow network handling Apr 24, 2026
@biplab1

biplab1 commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

@Arinyadav1 @itsPronay Could you please address the @coderabbitai suggestions relevant to this PR?

@coderabbitai

coderabbitai Bot commented Apr 27, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Fetch all review comments made by coderabbitai on PR `#2666`
gh pr view 2666 --repo openMF/mifos-x-field-officer-app --json reviews,comments

# Also fetch review comments (inline)
gh api repos/openMF/mifos-x-field-officer-app/pulls/2666/comments --paginate | jq '[.[] | select(.user.login == "coderabbitai[bot]") | {path: .path, line: .line, body: .body}]'

# Fetch issue comments as well
gh api repos/openMF/mifos-x-field-officer-app/issues/2666/comments --paginate | jq '[.[] | select(.user.login == "coderabbitai[bot]") | {body: .body}]'

Length of output: 60164


@biplab1, @Arinyadav1, @itsPronay — here is a summary of all outstanding CodeRabbit suggestions for this PR:


🟠 Major (Actionable) Issues

1. Re-throw CancellationException in DataStateUtils.kt (Line 47–48)

The catch (e: Throwable) block silently swallows CancellationException, breaking structured concurrency and coroutine cancellation/timeout propagation. Fix:

 import com.mifos.core.common.utils.DataState
+import kotlinx.coroutines.CancellationException
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.withContext
-    } catch (e: Throwable) {
+    } catch (e: CancellationException) {
+        throw e
+    } catch (e: Exception) {
         DataState.Error(e)
     }

2. Stop sync progression on offline error in SyncGroupPayloadsViewModel.kt (Line 136–137)

updateGroupPayload() is called even when result.exception is NetworkUnavailableException, which continues sync progression while still offline. Fix:

-                is DataState.Error -> {
-                    val errorMessage = if (result.exception is NetworkUnavailableException) {
-                        Res.string.feature_offline_error_not_connected_internet
-                    } else {
-                        Res.string.feature_offline_error_group_sync_failed
-                    }
-                    _syncGroupPayloadsUiState.value = SyncGroupPayloadsUiState.Error(errorMessage)
-                    updateGroupPayload()
-                }
+                is DataState.Error -> {
+                    if (result.exception is NetworkUnavailableException) {
+                        _syncGroupPayloadsUiState.value = SyncGroupPayloadsUiState.Error(
+                            Res.string.feature_offline_error_not_connected_internet,
+                        )
+                        return@launch
+                    }
+                    _syncGroupPayloadsUiState.value = SyncGroupPayloadsUiState.Error(
+                        Res.string.feature_offline_error_group_sync_failed,
+                    )
+                    updateGroupPayload()
+                }

🧹 Nitpick Suggestions

3. NetworkUnavailableException.kt (Line 15–17) — Extend Exception instead of Throwable so it's caught by the 80+ existing catch (e: Exception) handlers across the codebase:

 class NetworkUnavailableException(
     message: String = "Network is unavailable. Please check your connection and try again.",
-) : Throwable(message)
+) : Exception(message)

4. SyncGroupPayloadsViewModel.kt (Lines 122–127) — Remove the nullable parameter and force-unwrap (!!) from syncGroupPayload:

-    private fun syncGroupPayload(groupPayload: GroupPayloadEntity?) {
+    private fun syncGroupPayload(groupPayload: GroupPayloadEntity) {
         viewModelScope.launch {
             _syncGroupPayloadsUiState.value = SyncGroupPayloadsUiState.Loading
-            when (val result = repository.createGroup(groupPayload!!)) {
+            when (val result = repository.createGroup(groupPayload)) {

5. DataStateUtils.kt (Line 81) — Consider changing NetworkMonitor.isOnline from Flow<Boolean> to StateFlow<Boolean> to make the initial-value guarantee explicit in the type system and prevent future regressions.


❌ Pre-merge Check Failure

Docstring Coverage: Currently at 18.18%, below the required 80% threshold. Please add KDoc comments to the new public functions (runAsDataState overloads, NetworkUnavailableException) and any other missing ones.

@biplab1

biplab1 commented Apr 27, 2026

Copy link
Copy Markdown
Contributor
image

Shouldn't the screen stay where it is when clicking the submit button, after typing the note, since there is a "no internet" which is shown below instead of showing the try again page? Showing a try again page indicates the note being typed was lost, however, it is not. When the network is restored it has the note, which was just added, in the background. The UX could be improved a bit.

This could be handled in a separate PR, I think.

@biplab1

biplab1 commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

Could you please take a look at this issue: #2666 (comment)

Comment thread core/database/src/commonMain/kotlin/com/mifos/room/entities/noncore/NoteEntity.kt Outdated
@Arinyadav1

Copy link
Copy Markdown
Member

@biplab1 do the final review

Comment thread core/network/src/commonMain/kotlin/com/mifos/core/network/dto/note/NoteDto.kt Outdated
@sonarqubecloud

sonarqubecloud Bot commented May 5, 2026

Copy link
Copy Markdown

@biplab1

biplab1 commented May 5, 2026

Copy link
Copy Markdown
Contributor

@Arinyadav1 Have you tested the screens if they are working fine after the recent updates? Please upload a new screen recording.

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

Looks good to me. This can be merged.

@biplab1 biplab1 changed the title refactor: add DataState utility and optimize Flow network handling refactor: add DataState utility, optimize Flow network handling, add DTOs, entity, domain models May 5, 2026
@biplab1 biplab1 changed the title refactor: add DataState utility, optimize Flow network handling, add DTOs, entity, domain models refactor: add DataState, optimize Flow, add DTOs, entities, domain models May 5, 2026
@biplab1
biplab1 dismissed niyajali’s stale review May 5, 2026 16:28

Marked as resolved.

@therajanmaurya
therajanmaurya merged commit b811bcd into openMF:development May 5, 2026
10 checks passed
@biplab1 biplab1 changed the title refactor: add DataState, optimize Flow, add DTOs, entities, domain models refactor(note): add DataState, optimize Flow, add DTOs, entities, domain models May 5, 2026
sahilshivekar pushed a commit to sahilshivekar/mifos-x-field-officer-app that referenced this pull request May 7, 2026
…dels (openMF#2666)

Co-authored-by: Arin Yadav <115909232+Arinyadav1@users.noreply.github.com>
Co-authored-by: Arin Yadav <arinyadav98@gmail.com>
sahilshivekar pushed a commit to sahilshivekar/mifos-x-field-officer-app that referenced this pull request May 7, 2026
…dels (openMF#2666)

Co-authored-by: Arin Yadav <115909232+Arinyadav1@users.noreply.github.com>
Co-authored-by: Arin Yadav <arinyadav98@gmail.com>
PRAteek-singHWY added a commit to PRAteek-singHWY/mifos-x-field-officer-app that referenced this pull request May 8, 2026
Apply the DataState + NetworkMonitor + DispatcherManager pattern
established in PR openMF#2666 (note module refactor) to the close-loan flow:

- LoanService.closeLoanAccount: change Flow<GenericResponse> to
  suspend GenericResponse to match PR openMF#2666's service-layer style.
- DataManagerLoan.closeLoanAccount: mirror as suspend GenericResponse.
- CloseLoanRepository: action methods now return DataState<Unit>;
  observation flow unchanged (Flow<DataState<LoanTransactionTemplate?>>).
- CloseLoanRepositoryImp: inject NetworkMonitor and DispatcherManager;
  use runAsDataState() for actions and withNetworkCheck()/flowOn(io)
  for observation, matching NoteRepositoryImp.
- CloseLoanUseCase: simplify by delegating to the DataState-returning
  repository; sync remains best-effort.
gurnoorpannu added a commit to gurnoorpannu/mifos-x-field-officer-app that referenced this pull request May 9, 2026
- Split RejectLoanPayload into RejectLoanInput (domain) +
  RejectLoanRequestDto (network); drop RejectLoanResponse domain class
  in favor of typed RejectLoanResponseDto consumed by Ktorfit.
- LoanService.rejectLoan returns the typed DTO directly; DataManagerLoan
  becomes a thin pass-through (no manual HttpResponse / bodyAsText /
  Json.decodeFromString).
- LoanAccountRejectRepository returns DataState<Unit> via
  runAsDataState(networkMonitor, dispatcher.io); connectivity check moves
  out of the ViewModel into the repository.
- Add RejectLoanUseCase as a suspend pass-through; register in
  UseCaseModule.
- ViewModel injects the use case (not the repo), drops NetworkMonitor /
  networkConnection / isLoading state, surfaces results through
  Action.Internal.ReceiveRejectResult(DataState<Unit>) per the new
  pattern, and renames the discard guard to PreventAccidentalBack.
- Screen drives loading from DialogState.Loading (single source of truth)
  and uses the renamed dialog state.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
gurnoorpannu added a commit to gurnoorpannu/mifos-x-field-officer-app that referenced this pull request May 9, 2026
The post-openMF#2666 typed-DTO pass-through hid backend validation messages.
Without expectSuccess on the Ktor client, 4xx/5xx responses were either
silently decoded into an empty RejectLoanResponseDto (with
ignoreUnknownKeys) — surfacing as a fake success — or thrown as a
SerializationException with a developer-facing message. Either way,
Fineract's defaultUserMessage (e.g. "Loan cannot be rejected as it is
not in submitted state") never reached the UI.

Mirror the convention already used by submitLoanReschedule,
submitAccountTransfer, and createLoansAccount in this same file:
LoanService.rejectLoan returns the raw HttpResponse, and
DataManagerLoan.rejectLoan checks status, runs extractErrorMessage on
failure, and decodes RejectLoanResponseDto on success. The repository
contract (DataState<Unit>) and all upstream layers are unchanged.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
gurnoorpannu added a commit to gurnoorpannu/mifos-x-field-officer-app that referenced this pull request May 9, 2026
- Split RejectLoanPayload into RejectLoanInput (domain) +
  RejectLoanRequestDto (network); drop RejectLoanResponse domain class
  in favor of typed RejectLoanResponseDto consumed by Ktorfit.
- LoanService.rejectLoan returns the typed DTO directly; DataManagerLoan
  becomes a thin pass-through (no manual HttpResponse / bodyAsText /
  Json.decodeFromString).
- LoanAccountRejectRepository returns DataState<Unit> via
  runAsDataState(networkMonitor, dispatcher.io); connectivity check moves
  out of the ViewModel into the repository.
- Add RejectLoanUseCase as a suspend pass-through; register in
  UseCaseModule.
- ViewModel injects the use case (not the repo), drops NetworkMonitor /
  networkConnection / isLoading state, surfaces results through
  Action.Internal.ReceiveRejectResult(DataState<Unit>) per the new
  pattern, and renames the discard guard to PreventAccidentalBack.
- Screen drives loading from DialogState.Loading (single source of truth)
  and uses the renamed dialog state.
gurnoorpannu added a commit to gurnoorpannu/mifos-x-field-officer-app that referenced this pull request May 9, 2026
The post-openMF#2666 typed-DTO pass-through hid backend validation messages.
Without expectSuccess on the Ktor client, 4xx/5xx responses were either
silently decoded into an empty RejectLoanResponseDto (with
ignoreUnknownKeys) — surfacing as a fake success — or thrown as a
SerializationException with a developer-facing message. Either way,
Fineract's defaultUserMessage (e.g. "Loan cannot be rejected as it is
not in submitted state") never reached the UI.

Mirror the convention already used by submitLoanReschedule,
submitAccountTransfer, and createLoansAccount in this same file:
LoanService.rejectLoan returns the raw HttpResponse, and
DataManagerLoan.rejectLoan checks status, runs extractErrorMessage on
failure, and decodes RejectLoanResponseDto on success. The repository
contract (DataState<Unit>) and all upstream layers are unchanged.
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.

5 participants