Skip to content

fix: preserve settings state when combine flow re-fires (#688)#712

Merged
torlando-tech merged 2 commits intomainfrom
fix/688-message-size-limit-revert
Mar 26, 2026
Merged

fix: preserve settings state when combine flow re-fires (#688)#712
torlando-tech merged 2 commits intomainfrom
fix/688-message-size-limit-revert

Conversation

@torlando-tech
Copy link
Copy Markdown
Owner

Summary

  • Fixes the message size limit setting reverting to 1MB after being changed (Messsage Size limit #688)
  • Also fixes three other settings with the same state-clobbering bug: blockUnknownSenders, sortMessagesBySentTime, and tryPropagationOnFail

Root Cause

SettingsViewModel.loadSettings() has a combine block watching 15 DataStore flows. Whenever any flow emits, it constructs a new SettingsState object. Four fields managed by separate collectors were not preserved in this constructor, so they silently reverted to their data class defaults every time the combine re-fired.

The "briefly flashes then reverts" behavior reported in #688 is the telltale: the separate collector fires first (correct value), then the combine fires and overwrites with the default.

Fix

Added _state.value.xxx preservation for all four affected fields, matching the pattern already used for ~30 other fields in the same block.

Test plan

  • All existing SettingsViewModel tests pass
  • All MessageDeliveryRetrievalCard tests pass
  • Manual verification: size limit chips now persist selection correctly

🤖 Generated with Claude Code

The loadSettings() combine block constructs a new SettingsState on every
emission, but four fields managed by separate collectors were not being
preserved — causing them to silently revert to defaults whenever any of
the 15 combined flows emitted.

Fixes: incomingMessageSizeLimitKb, blockUnknownSenders,
sortMessagesBySentTime, tryPropagationOnFail.

Closes #688

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@torlando-tech torlando-tech linked an issue Mar 26, 2026 that may be closed by this pull request
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 26, 2026

Greptile Summary

This PR fixes a state-clobbering bug in SettingsViewModel.loadSettings() where a large combine block would reconstruct SettingsState from scratch on every emission, silently resetting four fields — blockUnknownSenders, incomingMessageSizeLimitKb, sortMessagesBySentTime, and tryPropagationOnFail — back to their data-class defaults whenever any upstream flow fired.

The fix follows the well-established pattern already used for ~30 other fields: read _state.value.field inside the combine lambda to carry forward the value managed by its own dedicated collector. It also adds the missing cold-start DataStore collector for tryPropagationOnFail (the other three fields already had collectors; this one did not).

Key changes:

  • combine block (SettingsViewModel.kt ~line 406, 443–447): adds _state.value preservation for all four previously-dropped fields, resolving the "briefly flashes then reverts" regression from Messsage Size limit #688
  • loadLocationSharingSettings() (~line 1576): adds the tryPropagationOnFailFlow collector so the correct persisted value is loaded on cold start — though tryPropagationOnFail is a delivery setting, not a location-sharing one (see inline comment)
  • SettingsViewModelIncomingMessageLimitTest.kt: adds the required mock for tryPropagationOnFailFlow so existing tests continue to pass with the new collector in init {}

Confidence Score: 4/5

Safe to merge — the bug fix is correct and complete; the only remaining issue is a minor organisational concern about where the new collector lives.

The four-field state-preservation fix is correct and matches the established pattern for the rest of the combine block. The previously-flagged cold-start gap for tryPropagationOnFail is now addressed. The sole remaining issue is that the new collector was added to loadLocationSharingSettings() rather than a semantically appropriate home, which is a P2 style concern that does not affect runtime behaviour.

SettingsViewModel.kt lines 1576–1580 and 444/446 — the tryPropagationOnFail collector and its associated combine-block comment both live in location-sharing code.

Important Files Changed

Filename Overview
app/src/main/java/com/lxmf/messenger/viewmodel/SettingsViewModel.kt Adds _state.value preservation for four fields in the combine block and adds the missing cold-start DataStore collector for tryPropagationOnFail; the collector is placed in loadLocationSharingSettings() which is semantically incorrect but functionally safe.
app/src/test/java/com/lxmf/messenger/viewmodel/SettingsViewModelIncomingMessageLimitTest.kt Adds the required tryPropagationOnFailFlow mock so existing tests don't fail after the new collector is registered during init.

Sequence Diagram

sequenceDiagram
    participant DS as DataStore
    participant SC as Separate Collector<br/>(loadLocationSharingSettings)
    participant CB as combine block<br/>(loadSettings)
    participant ST as _state

    Note over DS,ST: Before fix — race condition clobbers separate collector
    DS->>SC: tryPropagationOnFailFlow emits saved=false
    SC->>ST: _state.update { tryPropagationOnFail = false }
    DS->>CB: any of 15 flows emits
    CB->>ST: SettingsState(..., tryPropagationOnFail = default(true)) ❌

    Note over DS,ST: After fix — combine preserves current state value
    DS->>SC: tryPropagationOnFailFlow emits saved=false
    SC->>ST: _state.update { tryPropagationOnFail = false }
    DS->>CB: any of 15 flows emits
    CB->>ST: SettingsState(..., tryPropagationOnFail = _state.value.tryPropagationOnFail = false) ✅
Loading
Prompt To Fix All With AI
This is a comment left during a code review.
Path: app/src/main/java/com/lxmf/messenger/viewmodel/SettingsViewModel.kt
Line: 1576-1580

Comment:
**`tryPropagationOnFail` collector misplaced in `loadLocationSharingSettings()`**

The new collector for `tryPropagationOnFail` was added inside `loadLocationSharingSettings()`, but `tryPropagationOnFail` is a message-delivery setting, not a location-sharing one. A future developer looking for where this setting is loaded would not think to look here.

This is consistent with the pre-existing placement of `incomingMessageSizeLimitKb` and `sortMessagesBySentTime` collectors in the same function, but adding a third unrelated setting compounds the confusion. Ideally these three collectors — and the `combine`-block comments "from `loadLocationSharingSettings()`" on lines 444/446 — would move to a dedicated `loadMessageDeliverySettings()` function mirroring the pattern used for `loadPrivacySettings()` and `loadNotificationsSettings()`.

This doesn't affect runtime correctness because `loadLocationSharingSettings()` is always called unconditionally from `init {}`, but it will make the next contributor's grep for where `tryPropagationOnFail` is initialised misleading.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (2): Last reviewed commit: "fix: load tryPropagationOnFail from Data..." | Re-trigger Greptile

@sentry
Copy link
Copy Markdown
Contributor

sentry bot commented Mar 26, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Adds a flow collector so tryPropagationOnFail is restored from
DataStore on startup, not just preserved within-session. Without
this, a user who disabled retry-via-relay would see it revert to
enabled on every app restart.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@torlando-tech torlando-tech merged commit b6dcefd into main Mar 26, 2026
14 checks passed
@torlando-tech torlando-tech deleted the fix/688-message-size-limit-revert branch March 26, 2026 04:42
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.

Messsage Size limit

1 participant