damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

commit e7fe4ab9b4d5ecb35306179a1db97a3cfd78b3d1
parent c146bab08aff0ce50dac0eecc12d607434f0b60c
Author: Terry Yiu <git@tyiu.xyz>
Date:   Sun,  6 Apr 2025 11:48:39 -0400

Inverse hellthread_notifications_enabled to be hellthread_notifications_disabled and add hellthread_notifications_max_pubkeys setting

Signed-off-by: Terry Yiu <git@tyiu.xyz>

Diffstat:
Mdamus/Models/NotificationsManager.swift | 2+-
Mdamus/Models/PushNotificationClient.swift | 28++++++++++++++++++++++++++--
Mdamus/Models/UserSettingsStore.swift | 7+++++--
Mdamus/Views/Notifications/NotificationsView.swift | 29+++++++++++++++++++++--------
Mdamus/Views/Settings/NotificationSettingsView.swift | 40++++++++++++++++++++++++++++++++++------
Mdamus/en-US.lproj/Localizable.stringsdict | 30+++++++++++++++---------------
MdamusTests/LargeEventTests.swift | 2+-
MdamusTests/LocalizationUtilTests.swift | 2+-
Mnostrdb/NdbNote.swift | 8+++++---
9 files changed, 109 insertions(+), 39 deletions(-)

diff --git a/damus/Models/NotificationsManager.swift b/damus/Models/NotificationsManager.swift @@ -41,7 +41,7 @@ func should_display_notification(state: HeadlessDamusState, event ev: NostrEvent return false } - if !state.settings.hellthread_notification && ev.is_hellthread { + if state.settings.hellthread_notifications_disabled && ev.is_hellthread(max_pubkeys: state.settings.hellthread_notification_max_pubkeys) { return false } diff --git a/damus/Models/PushNotificationClient.swift b/damus/Models/PushNotificationClient.swift @@ -7,6 +7,12 @@ import Foundation +// Minimum threshold the hellthread pubkey tag count setting can go down to. +let HELLTHREAD_MIN_PUBKEYS: Int = 6 + +// Maximum threshold the hellthread pubkey tag count setting can go up to. +let HELLTHREAD_MAX_PUBKEYS: Int = 24 + struct PushNotificationClient { let keypair: Keypair let settings: UserSettingsStore @@ -181,10 +187,27 @@ extension PushNotificationClient { let reaction_notifications_enabled: Bool? let dm_notifications_enabled: Bool? let only_notifications_from_following_enabled: Bool? - let hellthread_notifications_enabled: Bool? + let hellthread_notifications_disabled: Bool? + let hellthread_notifications_max_pubkeys: Int? static func from(json_data: Data) -> Self? { guard let decoded = try? JSONDecoder().decode(Self.self, from: json_data) else { return nil } + + // Normalize hellthread_notifications_max_pubkeys in case + // it goes beyond the expected range supported on the client. + if let max_pubkeys = decoded.hellthread_notifications_max_pubkeys, max_pubkeys < HELLTHREAD_MIN_PUBKEYS || max_pubkeys > HELLTHREAD_MAX_PUBKEYS { + return NotificationSettings( + zap_notifications_enabled: decoded.zap_notifications_enabled, + mention_notifications_enabled: decoded.mention_notifications_enabled, + repost_notifications_enabled: decoded.repost_notifications_enabled, + reaction_notifications_enabled: decoded.reaction_notifications_enabled, + dm_notifications_enabled: decoded.dm_notifications_enabled, + only_notifications_from_following_enabled: decoded.only_notifications_from_following_enabled, + hellthread_notifications_disabled: decoded.hellthread_notifications_disabled, + hellthread_notifications_max_pubkeys: max(min(HELLTHREAD_MAX_PUBKEYS, max_pubkeys), HELLTHREAD_MIN_PUBKEYS) + ) + } + return decoded } @@ -196,7 +219,8 @@ extension PushNotificationClient { reaction_notifications_enabled: settings.like_notification, dm_notifications_enabled: settings.dm_notification, only_notifications_from_following_enabled: settings.notification_only_from_following, - hellthread_notifications_enabled: settings.hellthread_notification + hellthread_notifications_disabled: settings.hellthread_notifications_disabled, + hellthread_notifications_max_pubkeys: settings.hellthread_notification_max_pubkeys ) } diff --git a/damus/Models/UserSettingsStore.swift b/damus/Models/UserSettingsStore.swift @@ -161,8 +161,11 @@ class UserSettingsStore: ObservableObject { @Setting(key: "notification_only_from_following", default_value: false) var notification_only_from_following: Bool - @Setting(key: "hellthread_notification", default_value: false) - var hellthread_notification: Bool + @Setting(key: "hellthread_notifications_disabled", default_value: false) + var hellthread_notifications_disabled: Bool + + @Setting(key: "hellthread_notification_max_pubkeys", default_value: DEFAULT_HELLTHREAD_MAX_PUBKEYS) + var hellthread_notification_max_pubkeys: Int @Setting(key: "translate_dms", default_value: false) var translate_dms: Bool diff --git a/damus/Views/Notifications/NotificationsView.swift b/damus/Views/Notifications/NotificationsView.swift @@ -10,16 +10,26 @@ import SwiftUI class NotificationFilter: ObservableObject, Equatable { @Published var state: NotificationFilterState @Published var friend_filter: FriendFilter - @Published var show_hellthreads: Bool = false + @Published var hellthread_notifications_disabled: Bool + @Published var hellthread_notification_max_pubkeys: Int static func == (lhs: NotificationFilter, rhs: NotificationFilter) -> Bool { - return lhs.state == rhs.state && lhs.friend_filter == rhs.friend_filter && lhs.show_hellthreads == rhs.show_hellthreads + return lhs.state == rhs.state + && lhs.friend_filter == rhs.friend_filter + && lhs.hellthread_notifications_disabled == rhs.hellthread_notifications_disabled + && lhs.hellthread_notification_max_pubkeys == rhs.hellthread_notification_max_pubkeys } - init(state: NotificationFilterState = .all, friend_filter: FriendFilter = .all, show_hellthreads: Bool = false) { + init( + state: NotificationFilterState = .all, + friend_filter: FriendFilter = .all, + hellthread_notifications_disabled: Bool = false, + hellthread_notification_max_pubkeys: Int = DEFAULT_HELLTHREAD_MAX_PUBKEYS + ) { self.state = state self.friend_filter = friend_filter - self.show_hellthreads = show_hellthreads + self.hellthread_notifications_disabled = hellthread_notifications_disabled + self.hellthread_notification_max_pubkeys = hellthread_notification_max_pubkeys } func filter(contacts: Contacts, items: [NotificationItem]) -> [NotificationItem] { @@ -31,7 +41,7 @@ class NotificationFilter: ObservableObject, Equatable { if let item = item.filter({ ev in self.friend_filter.filter(contacts: contacts, pubkey: ev.pubkey) && - (show_hellthreads || !ev.is_hellthread) + (!hellthread_notifications_disabled || !ev.is_hellthread(max_pubkeys: hellthread_notification_max_pubkeys)) }) { acc.append(item) } @@ -71,7 +81,8 @@ struct NotificationsView: View { NotificationFilter( state: .all, friend_filter: filter.friend_filter, - show_hellthreads: state.settings.hellthread_notification + hellthread_notifications_disabled: state.settings.hellthread_notifications_disabled, + hellthread_notification_max_pubkeys: state.settings.hellthread_notification_max_pubkeys ) ) .tag(NotificationFilterState.all) @@ -80,7 +91,8 @@ struct NotificationsView: View { NotificationFilter( state: .zaps, friend_filter: filter.friend_filter, - show_hellthreads: state.settings.hellthread_notification + hellthread_notifications_disabled: state.settings.hellthread_notifications_disabled, + hellthread_notification_max_pubkeys: state.settings.hellthread_notification_max_pubkeys ) ) .tag(NotificationFilterState.zaps) @@ -89,7 +101,8 @@ struct NotificationsView: View { NotificationFilter( state: .replies, friend_filter: filter.friend_filter, - show_hellthreads: state.settings.hellthread_notification + hellthread_notifications_disabled: state.settings.hellthread_notifications_disabled, + hellthread_notification_max_pubkeys: state.settings.hellthread_notification_max_pubkeys ) ) .tag(NotificationFilterState.replies) diff --git a/damus/Views/Settings/NotificationSettingsView.swift b/damus/Views/Settings/NotificationSettingsView.swift @@ -17,8 +17,6 @@ struct NotificationSettingsView: View { @Environment(\.dismiss) var dismiss - let hellthread_notification_settings_text = pluralizedString(key: "hellthread_notification_settings", count: MIN_HELLTHREAD_PUBKEYS - 1) - func indicator_binding(_ val: NewEventsBits) -> Binding<Bool> { return Binding.init(get: { (settings.notification_indicators & val.rawValue) > 0 @@ -30,7 +28,15 @@ struct NotificationSettingsView: View { } }) } - + + var hellthread_notification_max_pubkeys_binding: Binding<Double> { + Binding<Double>(get: { + return Double(settings.hellthread_notification_max_pubkeys) + }, set: { + settings.hellthread_notification_max_pubkeys = Int($0) + }) + } + func try_to_set_notifications_mode(new_value: UserSettingsStore.NotificationsMode) { notification_mode_setting_error = nil if new_value == .push { @@ -113,7 +119,24 @@ struct NotificationSettingsView: View { } // MARK: - View layout - + + func hellthread_notification_settings_text() -> String { + if !settings.hellthread_notifications_disabled { + return NSLocalizedString("Hide notifications that tag many profiles", comment: "Label for notification settings toggle that hides notifications that tag many people.") + } + return pluralizedString(key: "hellthread_notifications_disabled", count: $settings.hellthread_notification_max_pubkeys.wrappedValue) + } + + var hellthread_notifications_max_pubkeys_view: some View { + VStack(alignment: .leading) { + Slider( + value: self.notification_preference_binding(hellthread_notification_max_pubkeys_binding), + in: Double(HELLTHREAD_MIN_PUBKEYS)...Double(HELLTHREAD_MAX_PUBKEYS), + step: 1 + ) + } + } + var body: some View { Form { if settings.enable_push_notifications { @@ -177,8 +200,13 @@ struct NotificationSettingsView: View { .toggleStyle(.switch) Toggle(NSLocalizedString("Show only from users you follow", comment: "Setting to Show notifications only associated to users your follow"), isOn: self.notification_preference_binding($settings.notification_only_from_following)) .toggleStyle(.switch) - Toggle(hellthread_notification_settings_text, isOn: $settings.hellthread_notification) - .toggleStyle(.switch) + VStack { + Toggle(hellthread_notification_settings_text(), isOn: self.notification_preference_binding($settings.hellthread_notifications_disabled)) + .toggleStyle(.switch) + if settings.hellthread_notifications_disabled { + hellthread_notifications_max_pubkeys_view + } + } } Section( diff --git a/damus/en-US.lproj/Localizable.stringsdict b/damus/en-US.lproj/Localizable.stringsdict @@ -50,52 +50,52 @@ <string>Following</string> </dict> </dict> - <key>imports_count</key> + <key>hellthread_notifications_disabled</key> <dict> <key>NSStringLocalizedFormatKey</key> - <string>%#@IMPORTS@</string> - <key>IMPORTS</key> + <string>%#@HELLTHREAD_PROFILES@</string> + <key>HELLTHREAD_PROFILES</key> <dict> <key>NSStringFormatSpecTypeKey</key> <string>NSStringPluralRuleType</string> <key>NSStringFormatValueTypeKey</key> <string>d</string> <key>one</key> - <string>Import</string> + <string>Hide notifications that tag more than %d profile</string> <key>other</key> - <string>Imports</string> + <string>Hide notifications that tag more than %d profiles</string> </dict> </dict> - <key>people_reposted_count</key> + <key>imports_count</key> <dict> <key>NSStringLocalizedFormatKey</key> - <string>%#@REPOSTED@</string> - <key>REPOSTED</key> + <string>%#@IMPORTS@</string> + <key>IMPORTS</key> <dict> <key>NSStringFormatSpecTypeKey</key> <string>NSStringPluralRuleType</string> <key>NSStringFormatValueTypeKey</key> <string>d</string> <key>one</key> - <string>%2$@ and %1$d other reposted</string> + <string>Import</string> <key>other</key> - <string>%2$@ and %1$d others reposted</string> + <string>Imports</string> </dict> </dict> - <key>hellthread_notification_settings</key> + <key>people_reposted_count</key> <dict> <key>NSStringLocalizedFormatKey</key> - <string>%#@HELLTHREAD_PROFILES@</string> - <key>HELLTHREAD_PROFILES</key> + <string>%#@REPOSTED@</string> + <key>REPOSTED</key> <dict> <key>NSStringFormatSpecTypeKey</key> <string>NSStringPluralRuleType</string> <key>NSStringFormatValueTypeKey</key> <string>d</string> <key>one</key> - <string>Show notifications that mention more than %d profile</string> + <string>%2$@ and %1$d other reposted</string> <key>other</key> - <string>Show notifications that mention more than %d profiles</string> + <string>%2$@ and %1$d others reposted</string> </dict> </dict> <key>quoted_reposts_count</key> diff --git a/damusTests/LargeEventTests.swift b/damusTests/LargeEventTests.swift @@ -43,7 +43,7 @@ final class LargeEventTests: XCTestCase { XCTAssertEqual(subid, "subid") XCTAssertTrue(ev.should_show_event) - XCTAssertTrue(ev.is_hellthread) + XCTAssertTrue(ev.is_hellthread(max_pubkeys: 10)) XCTAssertTrue(validate_event(ev: ev) == .ok) } diff --git a/damusTests/LocalizationUtilTests.swift b/damusTests/LocalizationUtilTests.swift @@ -17,8 +17,8 @@ final class LocalizationUtilTests: XCTestCase { let keys = [ ["followers_count", "Followers", "Follower", "Followers"], ["following_count", "Following", "Following", "Following"], + ["hellthread_notifications_disabled", "Hide notifications that tag more than 0 profiles", "Hide notifications that tag more than 1 profile", "Hide notifications that tag more than 2 profiles"], ["imports_count", "Imports", "Import", "Imports"], - ["hellthread_notification_settings", "Show notifications that mention more than 0 profiles", "Show notifications that mention more than 1 profile", "Show notifications that mention more than 2 profiles"], ["quoted_reposts_count", "Quotes", "Quote", "Quotes"], ["reactions_count", "Reactions", "Reaction", "Reactions"], ["relays_count", "Relays", "Relay", "Relays"], diff --git a/nostrdb/NdbNote.swift b/nostrdb/NdbNote.swift @@ -13,7 +13,9 @@ import secp256k1_implementation import CryptoKit let MAX_NOTE_SIZE: Int = 2 << 18 -let MIN_HELLTHREAD_PUBKEYS = 11 + +// Default threshold of the hellthread pubkey tag count setting if it is not set. +let DEFAULT_HELLTHREAD_MAX_PUBKEYS: Int = 10 struct NdbStr { let note: NdbNote @@ -300,10 +302,10 @@ extension NdbNote { return !too_big } - var is_hellthread: Bool { + func is_hellthread(max_pubkeys: Int) -> Bool { switch known_kind { case .text, .boost, .like, .zap: - Set(referenced_pubkeys).count >= MIN_HELLTHREAD_PUBKEYS + Set(referenced_pubkeys).count > max_pubkeys default: false }