Skip to content

Add filtering options for announce notifications#426

Merged
torlando-tech merged 6 commits intomainfrom
claude/distinguish-tcp-announcements-0JUsT
Feb 15, 2026
Merged

Add filtering options for announce notifications#426
torlando-tech merged 6 commits intomainfrom
claude/distinguish-tcp-announcements-0JUsT

Conversation

@torlando-tech
Copy link
Copy Markdown
Owner

Summary

Add two new filtering options for announce notifications to reduce notification noise: direct-only mode (only notify for 1-hop neighbors) and TCP exclusion (skip announces from TCP interfaces).

Key Changes

  • NotificationHelper: Updated notifyAnnounceHeard() to accept hops and interfaceType parameters, with logic to filter notifications based on new settings
  • SettingsRepository: Added two new preference flows:
    • notificationAnnounceDirectOnlyFlow: Only notify for direct (1-hop) announces (defaults to true)
    • notificationAnnounceExcludeTcpFlow: Exclude TCP interface announces from notifications (defaults to true)
  • MessageCollector: Pass hops and interfaceType when calling notifyAnnounceHeard()
  • NotificationSettingsScreen: Added UI controls for the two new filters as indented sub-options under "Heard Announce" (only visible when announce notifications are enabled)
  • NotificationSettingsViewModel: Added state properties and toggle methods for the new settings
  • Tests: Updated unit tests to cover the new settings and their behavior

Implementation Details

  • The announce sub-options are conditionally displayed only when the parent "Heard Announce" notification is enabled, keeping the UI clean
  • Both new settings default to true (enabled), providing sensible defaults that reduce notification spam from distant peers and TCP connections
  • The filtering is applied at the notification level before permission checks, ensuring efficient early exit
  • Interface type detection uses InterfaceType.fromInterfaceName() to classify the receiving interface

https://claude.ai/code/session_01JjXTJtNGs16FH8NwmbkFBi

…ations

Add two new notification sub-options under "Heard Announce" that allow
users to filter announce notifications by interface type and hop count:

- "Direct Only" (default: on) - Only notify for 1-hop announces from
  direct neighbors, filtering out multi-hop announces that were relayed
  through transport nodes
- "Exclude TCP" (default: on) - Skip announces received via TCP
  interfaces, which arrive frequently and are less noteworthy than
  BLE or RNode announces

Both options default to enabled, so existing users who have announce
notifications turned on will immediately benefit from reduced noise
without needing to change settings.

Closes #339

https://claude.ai/code/session_01JjXTJtNGs16FH8NwmbkFBi
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Feb 9, 2026

Greptile Summary

This PR adds two notification filters for announce events to reduce noise: direct-only mode (only notify for 1-hop neighbors) and TCP exclusion (skip announces received via TCP interfaces). Both default to enabled, which provides a sensible out-of-the-box experience for reducing notification spam from distant peers and TCP connections.

  • NotificationHelper gains a new shouldNotifyAnnounce() helper that consolidates master toggle, per-type preference, hop-based filtering (hops != 1 when direct-only), TCP exclusion, and permission checks into a single boolean gate
  • SettingsRepository adds two new DataStore-backed preference flows with standard save methods, following existing conventions
  • MessageCollector passes announce.hops and InterfaceType.fromInterfaceName(announce.receivingInterface) at the callsite
  • The UI shows the new filters as indented sub-options under "Heard Announce", conditionally displayed only when announce notifications are enabled
  • The ViewModel refactors its combine() from fragile index-based array access to nested typed combines, improving type safety
  • Tests are extended to cover the new settings and toggle methods

Confidence Score: 5/5

  • This PR is safe to merge — it adds well-tested notification filters with sensible defaults and no behavioral regressions.
  • All changes follow existing codebase patterns consistently. The filtering logic correctly handles the hops semantics (0=unknown, 1=direct, 2+=multi-hop) after an earlier fix. The ViewModel refactor to typed combines is a strict improvement over the previous index-based approach. Test coverage is thorough, and the UI changes are properly gated behind the parent toggle. No security, correctness, or performance concerns.
  • No files require special attention

Important Files Changed

Filename Overview
app/src/main/java/com/lxmf/messenger/notifications/NotificationHelper.kt Extracted shouldNotifyAnnounce() helper with correct direct-only (hops != 1) and TCP exclusion filters; refactored notifyAnnounceHeard() to accept hops and interfaceType parameters with sensible defaults.
app/src/main/java/com/lxmf/messenger/repository/SettingsRepository.kt Added two new DataStore preferences (notificationAnnounceDirectOnlyFlow, notificationAnnounceExcludeTcpFlow) with standard flow/save pattern and true defaults; follows existing repository conventions.
app/src/main/java/com/lxmf/messenger/service/MessageCollector.kt Passes announce.hops and InterfaceType.fromInterfaceName(announce.receivingInterface) to notifyAnnounceHeard(); minimal and correct callsite update.
app/src/main/java/com/lxmf/messenger/ui/screens/NotificationSettingsScreen.kt Added two indented sub-options under "Heard Announce" toggle, conditionally shown when announce notifications are enabled; added indented parameter to NotificationTypeItem for visual hierarchy.
app/src/main/java/com/lxmf/messenger/viewmodel/NotificationSettingsViewModel.kt Added state fields and toggle methods for new settings; refactored from index-based combine() to nested typed combines for type safety.
app/src/test/java/com/lxmf/messenger/viewmodel/NotificationSettingsViewModelTest.kt Extended tests to cover new toggle methods, state collection, default values, and equality checks for announceDirectOnly and announceExcludeTcp settings.

Flowchart

flowchart TD
    A[MessageCollector receives announce] --> B[notifyAnnounceHeard\nhops, interfaceType]
    B --> C{shouldNotifyAnnounce}
    C --> D{Master notifications\nenabled?}
    D -- No --> Z[Return: no notification]
    D -- Yes --> E{Announce notifications\nenabled?}
    E -- No --> Z
    E -- Yes --> F{Direct-only filter\nenabled?}
    F -- Yes --> G{hops == 1?}
    G -- No --> Z
    G -- Yes --> H{Exclude TCP\nenabled?}
    F -- No --> H
    H -- Yes --> I{interfaceType ==\nTCP_CLIENT?}
    I -- Yes --> Z
    I -- No --> J{Has notification\npermission?}
    H -- No --> J
    J -- No --> Z
    J -- Yes --> K[Post notification]
Loading

Last reviewed commit: 3204916

Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

6 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

@torlando-tech torlando-tech linked an issue Feb 9, 2026 that may be closed by this pull request
@torlando-tech torlando-tech linked an issue Feb 9, 2026 that may be closed by this pull request
@torlando-tech torlando-tech added this to the v0.9.0 milestone Feb 9, 2026
hops==0 means unknown (Python fallback when hops_to() returns None),
not direct. Change condition from `hops > 1` to `hops != 1` so that
only confirmed direct-neighbor announces (exactly 1 hop) pass the
direct-only filter.

https://claude.ai/code/session_01JjXTJtNGs16FH8NwmbkFBi
@torlando-tech
Copy link
Copy Markdown
Owner Author

@greptileai review

Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

6 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Feb 15, 2026

Additional Comments (1)

app/src/main/java/com/lxmf/messenger/viewmodel/NotificationSettingsViewModel.kt
Fragile index-based flow combine
The combine call now merges 9 flows, accessed via values[0] through values[8]. Every time a new preference is added, all subsequent indices must be manually renumbered — which is error-prone and has already required a full re-index in this PR. Consider using a pattern that's resilient to insertion, for example combining sub-groups of flows or using named intermediate variables:

combine(
    combine(
        settingsRepository.notificationsEnabledFlow,
        settingsRepository.notificationReceivedMessageFlow,
        settingsRepository.notificationReceivedMessageFavoriteFlow,
        settingsRepository.notificationHeardAnnounceFlow,
    ) { enabled, msg, fav, announce -> Quadruple(enabled, msg, fav, announce) },
    combine(
        settingsRepository.notificationAnnounceDirectOnlyFlow,
        settingsRepository.notificationAnnounceExcludeTcpFlow,
    ) { directOnly, excludeTcp -> Pair(directOnly, excludeTcp) },
    // ...etc
) { ... }

This makes it impossible to accidentally shift indices when adding new settings.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: app/src/main/java/com/lxmf/messenger/viewmodel/NotificationSettingsViewModel.kt
Line: 51:73

Comment:
**Fragile index-based flow combine**
The `combine` call now merges 9 flows, accessed via `values[0]` through `values[8]`. Every time a new preference is added, all subsequent indices must be manually renumbered — which is error-prone and has already required a full re-index in this PR. Consider using a pattern that's resilient to insertion, for example combining sub-groups of flows or using named intermediate variables:

```kotlin
combine(
    combine(
        settingsRepository.notificationsEnabledFlow,
        settingsRepository.notificationReceivedMessageFlow,
        settingsRepository.notificationReceivedMessageFavoriteFlow,
        settingsRepository.notificationHeardAnnounceFlow,
    ) { enabled, msg, fav, announce -> Quadruple(enabled, msg, fav, announce) },
    combine(
        settingsRepository.notificationAnnounceDirectOnlyFlow,
        settingsRepository.notificationAnnounceExcludeTcpFlow,
    ) { directOnly, excludeTcp -> Pair(directOnly, excludeTcp) },
    // ...etc
) { ... }
```

This makes it impossible to accidentally shift indices when adding new settings.

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

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

torlando-tech and others added 2 commits February 15, 2026 12:22
… NotificationSettingsViewModel

The 9-flow combine() used fragile values[0]..values[8] array indexing,
requiring manual renumbering whenever a preference was added or reordered.
Refactored to use nested combine() sub-groups with named lambda parameters
and destructuring, making the mapping self-documenting and resilient to
insertion.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The notifyAnnounceHeard function had 5 early returns for guard checks
(master toggle, per-type toggle, direct-only filter, TCP exclusion,
permission), exceeding detekt's ReturnCount limit of 3. Extracted a
shouldNotifyAnnounce helper that consolidates these checks into a
single boolean expression, leaving notifyAnnounceHeard with just one
guard return.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@torlando-tech
Copy link
Copy Markdown
Owner Author

@greptileai review

@sentry
Copy link
Copy Markdown
Contributor

sentry bot commented Feb 15, 2026

@torlando-tech
Copy link
Copy Markdown
Owner Author

@greptileai review

… icon

Announce push notifications now display the receiving interface name
(e.g. "via Backbone · 1 hop") instead of generic "Announce received".
All notifications use the Columba launcher icon instead of Android
system icons. Also adds root-level installDebug task defaulting to
noSentry variant.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…values

Use concrete string sentinels for Link.ACTIVE/CLOSED instead of
auto-created MagicMock attributes (Mock == Mock comparison is
nondeterministic across Python versions). Also provide extra
time.time() side_effect values and set Transport.active_links = []
to prevent StopIteration on unexpected calls.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@torlando-tech torlando-tech merged commit 12aa765 into main Feb 15, 2026
9 of 10 checks passed
@torlando-tech torlando-tech deleted the claude/distinguish-tcp-announcements-0JUsT branch February 15, 2026 20:24
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.

Make difference between TCP and other announces in notifications

2 participants