feat: add Notification setting to show or hide Messages on the Notification tab

Adds a "Show Messages" toggle to the Display section of Notification Settings.
When disabled, direct/group messages (NIP-17 chats, NIP-04 DMs, and Marmot
group messages) are filtered out of the Notification tab, keeping them only in
the Messages tab. Defaults to on, preserving existing behavior.

The setting is persisted per-account in AccountSettings/LocalPreferences and is
included in the notification feed key so the feed refreshes immediately when
toggled.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018FwwcaBRyvnJFWaDLoQ16d
This commit is contained in:
Claude
2026-06-20 20:27:32 +00:00
parent cdfe007fb4
commit bd8beb43e1
6 changed files with 44 additions and 2 deletions
@@ -152,6 +152,7 @@ private object PrefKeys {
const val HIDE_NIP_17_WARNING_DIALOG = "hide_nip24_warning_dialog" // delete later
const val ALWAYS_ON_NOTIFICATION_SERVICE = "always_on_notification_service"
const val SPLIT_NOTIFICATIONS_ENABLED = "split_notifications_enabled"
const val SHOW_MESSAGES_IN_NOTIFICATIONS = "show_messages_in_notifications"
// One-shot stamp: set once an account has gone through the notifications
// Global -> Selected (Curated) migration (or was created after it shipped).
@@ -467,6 +468,7 @@ object LocalPreferences {
putBoolean(PrefKeys.CALLS_ENABLED, settings.callsEnabled.value)
putBoolean(PrefKeys.ALWAYS_ON_NOTIFICATION_SERVICE, settings.alwaysOnNotificationService.value)
putBoolean(PrefKeys.SPLIT_NOTIFICATIONS_ENABLED, settings.splitNotificationsEnabled.value)
putBoolean(PrefKeys.SHOW_MESSAGES_IN_NOTIFICATIONS, settings.showMessagesInNotifications.value)
// Any account that reaches a save has its notification filter in its
// post-split meaning, so stamp it as migrated. This keeps the one-shot
// Global -> Selected rewrite from ever touching it again and preserves a
@@ -582,6 +584,7 @@ object LocalPreferences {
val callsEnabled = getBoolean(PrefKeys.CALLS_ENABLED, true)
val alwaysOnNotificationService = getBoolean(PrefKeys.ALWAYS_ON_NOTIFICATION_SERVICE, false)
val splitNotificationsEnabled = getBoolean(PrefKeys.SPLIT_NOTIFICATIONS_ENABLED, false)
val showMessagesInNotifications = getBoolean(PrefKeys.SHOW_MESSAGES_IN_NOTIFICATIONS, true)
val hasDonatedInVersion = getStringSet(PrefKeys.HAS_DONATED_IN_VERSION, null) ?: setOf()
val dismissedPollNoteIds = getStringSet(PrefKeys.DISMISSED_POLL_NOTE_IDS, null) ?: setOf()
val viewedPollResultNoteIdsStr = getString(PrefKeys.VIEWED_POLL_RESULT_NOTE_IDS, null)
@@ -742,6 +745,7 @@ object LocalPreferences {
hideNIP17WarningDialog = hideNIP17WarningDialog,
alwaysOnNotificationService = MutableStateFlow(alwaysOnNotificationService),
splitNotificationsEnabled = MutableStateFlow(splitNotificationsEnabled),
showMessagesInNotifications = MutableStateFlow(showMessagesInNotifications),
backupUserMetadata = latestUserMetadata.await(),
backupContactList = latestContactList.await(),
backupNIP65RelayList = latestNip65RelayList.await(),
@@ -217,6 +217,7 @@ class AccountSettings(
var hideNIP17WarningDialog: Boolean = false,
val alwaysOnNotificationService: MutableStateFlow<Boolean> = MutableStateFlow(false),
val splitNotificationsEnabled: MutableStateFlow<Boolean> = MutableStateFlow(false),
val showMessagesInNotifications: MutableStateFlow<Boolean> = MutableStateFlow(true),
var backupUserMetadata: MetadataEvent? = null,
var backupContactList: ContactListEvent? = null,
var backupDMRelayList: ChatMessageRelayListEvent? = null,
@@ -295,6 +296,13 @@ class AccountSettings(
return newValue
}
fun toggleShowMessagesInNotifications(): Boolean {
val newValue = !showMessagesInNotifications.value
showMessagesInNotifications.tryEmit(newValue)
saveAccountSettings()
return newValue
}
// ---
// Zaps and Reactions
// ---
@@ -313,8 +313,11 @@ fun WatchAccountForNotifications(
) {
val listState by
accountViewModel.account.liveNotificationFollowLists.collectAsStateWithLifecycle()
val showMessages by
accountViewModel.account.settings.showMessagesInNotifications
.collectAsStateWithLifecycle()
LaunchedEffect(accountViewModel, listState) {
LaunchedEffect(accountViewModel, listState, showMessages) {
notifFeedContentState.checkKeysInvalidateDataAndSendToTop()
}
}
@@ -246,7 +246,7 @@ class NotificationFeedFilter(
}
}
override fun feedKey(): String = account.userProfile().pubkeyHex + "-" + followList().code
override fun feedKey(): String = account.userProfile().pubkeyHex + "-" + followList().code + "-" + account.settings.showMessagesInNotifications.value
fun followList(): TopFilter = modeOverride ?: account.settings.defaultNotificationFollowList.value
@@ -299,6 +299,10 @@ class NotificationFeedFilter(
): Boolean {
val loggedInUserHex = account.userProfile().pubkeyHex
// When the user opts out of seeing Messages on the Notification tab, drop
// direct/group message events (DMs and Marmot group chats) entirely.
val showMessages = account.settings.showMessagesInNotifications.value
// Marmot group messages are only acceptable if the gathering chatroom is
// actually in the current account's group list. Notes are stored in the
// global LocalCache and accumulate a gatherer reference from every
@@ -308,6 +312,7 @@ class NotificationFeedFilter(
// instance held by this account's list.
val marmotGatherers = it.inGatherers?.filterIsInstance<MarmotGroupChatroom>()
if (!marmotGatherers.isNullOrEmpty()) {
if (!showMessages) return false
val inCurrentAccount =
marmotGatherers.any { room ->
account.marmotGroupList.rooms.get(room.nostrGroupId) === room
@@ -317,6 +322,16 @@ class NotificationFeedFilter(
}
val noteEvent = it.event
if (!showMessages &&
(
noteEvent is ChatMessageEvent ||
noteEvent is ChatMessageEncryptedFileHeaderEvent ||
noteEvent is PrivateDmEvent
)
) {
return false
}
val notifAuthor =
if (noteEvent is LnZapEvent) {
val zapRequest = noteEvent.zapRequest
@@ -115,6 +115,8 @@ private fun DeliverySection(accountViewModel: AccountViewModel) {
private fun DisplaySection(accountViewModel: AccountViewModel) {
val splitByFollows by accountViewModel.account.settings.splitNotificationsEnabled
.collectAsStateWithLifecycle()
val showMessages by accountViewModel.account.settings.showMessagesInNotifications
.collectAsStateWithLifecycle()
SettingsSection(R.string.notification_settings_section_display) {
SettingsSwitchTile(
@@ -124,6 +126,14 @@ private fun DisplaySection(accountViewModel: AccountViewModel) {
checked = splitByFollows,
onCheckedChange = { accountViewModel.account.settings.toggleSplitNotificationsEnabled() },
)
SettingsDivider()
SettingsSwitchTile(
icon = MaterialSymbols.Mail,
title = R.string.show_messages_in_notifications_setting_title,
description = R.string.show_messages_in_notifications_setting_description,
checked = showMessages,
onCheckedChange = { accountViewModel.account.settings.toggleShowMessagesInNotifications() },
)
}
}
+2
View File
@@ -1281,6 +1281,8 @@
<string name="split_notifications_setting_title">Split notifications by Follows</string>
<string name="split_notifications_setting_description">Show two notification tabs — Following (people you follow) and Everyone. The unread indicator glows only for activity from people you follow.</string>
<string name="show_messages_in_notifications_setting_title">Show Messages</string>
<string name="show_messages_in_notifications_setting_description">Include direct and group messages on the Notification tab. Turn off to keep messages only in the Messages tab.</string>
<string name="notification_tab_following">Following</string>
<string name="notification_tab_everyone">Everyone</string>