Skip to content

fix: notification sender bugs + opt-in starred-contact DND bypass - #3061

Closed
blue-archon wants to merge 3 commits into
BlueBubblesApp:developmentfrom
blue-archon:pr-to-development
Closed

blue-archon wants to merge 3 commits into
BlueBubblesApp:developmentfrom
blue-archon:pr-to-development

Conversation

@blue-archon

Copy link
Copy Markdown
Contributor

Summary

This PR contains two bug fixes and one new feature, each in a self-contained commit. Happy for these to be taken individually or together.

Bug fixes

Unknown sender in group chat notifications — A second message arriving rapidly in a group chat showed "Unknown" as the sender name. Root cause: the handle lookup on Message fell through to a DB query that raced with ObjectBox relation loading. Fix: check the in-memory transient handle field (populated by addMessageToChat before the DB put) before issuing the query. Also adds a deep_map_normalize utility to fix LinkedHashMap<Object,Object> type corruption on nested server payload maps after cross-isolate transfer (affects both FCM and UnifiedPush background paths).

Notification Like/reaction button silently failing — The flutter. key prefix was missing from hasServerUrl() and hasAuthKey() in SettingsHelper.kt, so HttpService always saw empty values and never made the HTTP call. Fixed in the feat commit below alongside the SettingsHelper changes for the DND feature.

New feature: opt-in starred-contact DND bypass

Removes setBypassDnd(true) from the global notification channel (which bypassed DND for all contacts) and replaces it with per-notification logic: if the sender is starred in the Android contacts app, the notification Person is built with setUri(lookupUri) + setImportant(true), letting Android's DND system grant the exception contact-by-contact.

The feature is off by default, controlled by a new toggle in Settings → Application Settings → Notification Settings → Override DND for Favorites.

Implementation note on settings storage: The toggle value (dndFavoritesOverride) is passed from Dart through the create-incoming-message-notification MethodChannel call rather than read from SharedPreferences on the Kotlin side. This avoids a storage mismatch introduced by shared_preferences v2.5.x: after the DataStore migration, new keys are written to Android DataStore, which is not accessible via the legacy getSharedPreferences() API used by SettingsHelper. Existing keys (written before migration) are unaffected.

Conflict resolution note: DartWorkManager.kt had a minor conflict with the upstream APPEND_OR_REPLACE enqueue fix. Resolved by combining both: enqueueUniqueWork with APPEND_OR_REPLACE inside the try-catch, so sequencing and error handling are both preserved.

Commits

  • fix: resolve unknown sender in group notifications and infinite loading spinner
  • feat: add starred-contact DND bypass with user-controlled settings toggle
  • docs: add fork README documenting changes from v2.0.0+85

…ng spinner

Bug 1: second rapid message in a group chat showed "Unknown" as sender.
Root cause: handle lookup on the Message object fell through to a DB query
that raced with ObjectBox relation loading; the handle arrived after the
notification was built. Fix: check the transient handle field first
(populated by addMessageToChat before the DB put) before issuing the query.
Also normalize all nested server payload maps with a shared deep_map_normalize
utility so that dynamic fields (attributedBody, messageSummaryInfo, etc.)
never arrive as LinkedHashMap<Object,Object> after cross-isolate transfer.

Bug 2: opening a conversation from a notification left an infinite
"Loading more messages…" spinner. Root cause: getMessagesAsync filtered
all messages when chat style was unknown (single-member participant list
on a group chat). Fix: gate the style-43/participant-length group check
on chatStyle so messages are never silently dropped, and set noMoreMessages
on the first empty page response so the spinner clears.
…ggle

Removes the global bypassDnd(true) on the notification channel (which
overrode DND for all contacts) and replaces it with per-notification
logic: if the sender is a starred/favorite Android contact, the
notification Person is built with setUri(lookupUri) + setImportant(true)
so Android's DND system grants the exception contact-by-contact.

New ContactNotificationHelper resolves the native contact lookup URI and
starred status from nativeContactId (threaded from Dart through the full
incoming-message pipeline and DartWorker/UnifiedPush background paths).
PushShareTargetsHandler reuses the same ContactInfo to avoid a second
contacts DB query for conversation shortcuts.

The feature is gated by a new "Override DND for Favorites" toggle in
Settings → Application Settings → Notification Settings (default off).
The toggle value is a Dart RxBool (dndFavoritesOverride in settings.dart)
passed through the create-incoming-message-notification MethodChannel call
rather than read from SharedPreferences on the Kotlin side. This avoids
a DataStore/legacy-XML storage mismatch introduced by shared_preferences
v2.5.x: new keys written post-migration live in DataStore, which is
invisible to the legacy getSharedPreferences() API used by SettingsHelper.

SettingsHelper also gains the flutter. key prefix that was missing from
hasServerUrl() and hasAuthKey(), fixing the native tapback (LikeMessage)
feature that was silently failing to make HTTP calls.
@jjoelj

jjoelj commented Jun 30, 2026

Copy link
Copy Markdown
Member

Only PR relevant files. Breaking into multiple PRs will help us review.

@blue-archon

Copy link
Copy Markdown
Contributor Author

Thanks for the feedback. Split into two focused PRs and dropped the fork-specific README:

Closing this one.

@blue-archon
blue-archon deleted the pr-to-development branch July 1, 2026 17:50
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.

2 participants