damus

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

commit 51b1b81c0e28a5ea9ae225afd7fc347ee38a14f7
parent 7b9d0edef4200d403c3e9bd0fc8835ba355c2d1c
Author: Daniel D’Aquino <daniel@daquino.me>
Date:   Wed,  4 Sep 2024 10:46:09 -0700

Merge branch 'release_1.10' into master

Diffstat:
MDamusNotificationService/NotificationFormatter.swift | 3+++
Mdamus.xcodeproj/project.pbxproj | 22++++++++--------------
Mdamus/ContentView.swift | 2+-
Mdamus/Models/NotificationsManager.swift | 9+++++++++
Mdamus/Models/PushNotificationClient.swift | 16+++++++++++++---
Mdamus/Models/UserSettingsStore.swift | 4++--
Mdamus/Util/Constants.swift | 1+
Mdamus/Util/LocalNotification.swift | 1+
Mdamus/Views/Settings/DeveloperSettingsView.swift | 39++++++++++++++++++++++++++++++++++++---
Mhighlighter action extension/Info.plist | 7++++++-
10 files changed, 80 insertions(+), 24 deletions(-)

diff --git a/DamusNotificationService/NotificationFormatter.swift b/DamusNotificationService/NotificationFormatter.swift @@ -70,6 +70,9 @@ struct NotificationFormatter { case .zap, .profile_zap: // not handled here. Try `format_message(displayName: String, notify: LocalNotification, state: HeadlessDamusState) async -> (content: UNMutableNotificationContent, identifier: String)?` return nil + case .reply: + title = String(format: NSLocalizedString("%@ replied to your note", comment: "Heading for local notification indicating a new reply"), displayName) + identifier = "myReplyNotification" } content.title = title content.body = notify.content diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj @@ -1013,6 +1013,8 @@ D7ADD3DE2B53854300F104C4 /* DamusPurpleURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7ADD3DD2B53854300F104C4 /* DamusPurpleURL.swift */; }; D7ADD3E02B538D4200F104C4 /* DamusPurpleURLSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7ADD3DF2B538D4200F104C4 /* DamusPurpleURLSheetView.swift */; }; D7ADD3E22B538E3500F104C4 /* DamusPurpleVerifyNpubView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7ADD3E12B538E3500F104C4 /* DamusPurpleVerifyNpubView.swift */; }; + D7B76C902C825042003A16CB /* PushNotificationClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D2A3802BF815D000E4B42B /* PushNotificationClient.swift */; }; + D7B76C912C82507F003A16CB /* NIP98AuthenticatedRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7C6787D2B2D34CC00BCEAFB /* NIP98AuthenticatedRequest.swift */; }; D7C6787E2B2D34CC00BCEAFB /* NIP98AuthenticatedRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7C6787D2B2D34CC00BCEAFB /* NIP98AuthenticatedRequest.swift */; }; D7CB5D3B2B112FBB00AD4105 /* NotificationFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D70A3B162B02DCE5008BD568 /* NotificationFormatter.swift */; }; D7CB5D3C2B1130C600AD4105 /* LocalNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CDA128B29EB19C40006FA5A /* LocalNotification.swift */; }; @@ -4732,6 +4734,7 @@ D7CE1B402B0BE719002EDAD4 /* FlatBufferObject.swift in Sources */, D7CE1B442B0BE719002EDAD4 /* Mutable.swift in Sources */, D798D2212B08594800234419 /* NdbTagElem.swift in Sources */, + D7B76C902C825042003A16CB /* PushNotificationClient.swift in Sources */, D7CE1B432B0BE719002EDAD4 /* String+extension.swift in Sources */, D7CB5D3F2B116DAD00AD4105 /* NotificationsManager.swift in Sources */, D7CB5D602B11770C00AD4105 /* FollowState.swift in Sources */, @@ -4808,6 +4811,7 @@ D7CCFC152B05891000323D86 /* Referenced.swift in Sources */, D7CE1B2B2B0BE243002EDAD4 /* hex.c in Sources */, D798D2222B08598A00234419 /* ReferencedId.swift in Sources */, + D7B76C912C82507F003A16CB /* NIP98AuthenticatedRequest.swift in Sources */, D7CE1B492B0BE729002EDAD4 /* DisplayName.swift in Sources */, D7CE1B192B0BE132002EDAD4 /* builder.c in Sources */, D7EDED1F2B11797D0018B19C /* LongformEvent.swift in Sources */, @@ -5029,7 +5033,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 4; + CURRENT_PROJECT_VERSION = 5; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -5052,7 +5056,7 @@ GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 16.0; MACOSX_DEPLOYMENT_TARGET = 12.3; - MARKETING_VERSION = 1.9; + MARKETING_VERSION = 1.10; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -5098,7 +5102,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 4; + CURRENT_PROJECT_VERSION = 5; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -5117,7 +5121,7 @@ GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 16.0; MACOSX_DEPLOYMENT_TARGET = 12.3; - MARKETING_VERSION = 1.9; + MARKETING_VERSION = 1.10; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; @@ -5137,7 +5141,6 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\""; DEVELOPMENT_TEAM = XK7H4JAB3D; ENABLE_PREVIEWS = YES; @@ -5188,7 +5191,6 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\""; DEVELOPMENT_TEAM = XK7H4JAB3D; ENABLE_PREVIEWS = YES; @@ -5234,11 +5236,9 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = XK7H4JAB3D; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 16.0; - MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.jb55.damusTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; @@ -5254,11 +5254,9 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = XK7H4JAB3D; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 16.0; - MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.jb55.damusTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; @@ -5273,10 +5271,8 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = XK7H4JAB3D; GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.jb55.damusUITests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; @@ -5291,10 +5287,8 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = XK7H4JAB3D; GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.jb55.damusUITests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; diff --git a/damus/ContentView.swift b/damus/ContentView.swift @@ -722,7 +722,7 @@ struct ContentView: View { selected_timeline = .dms damus_state.dms.set_active_dm(target.pubkey) navigationCoordinator.push(route: Route.DMChat(dms: damus_state.dms.active_model)) - case .like, .zap, .mention, .repost: + case .like, .zap, .mention, .repost, .reply: open_event(ev: target) case .profile_zap: break diff --git a/damus/Models/NotificationsManager.swift b/damus/Models/NotificationsManager.swift @@ -68,6 +68,15 @@ func generate_local_notification_object(from ev: NostrEvent, state: HeadlessDamu let content_preview = render_notification_content_preview(ev: ev, profiles: state.profiles, keypair: state.keypair) return LocalNotification(type: .mention, event: ev, target: ev, content: content_preview) } + if ev.referenced_ids.contains(where: { note_id in + guard let note_author: Pubkey = state.ndb.lookup_note(note_id)?.unsafeUnownedValue?.pubkey else { return false } + guard note_author == state.keypair.pubkey else { return false } + return true + }) { + // This is a reply to one of our posts + let content_preview = render_notification_content_preview(ev: ev, profiles: state.profiles, keypair: state.keypair) + return LocalNotification(type: .reply, event: ev, target: ev, content: content_preview) + } } else if type == .boost, state.settings.repost_notification, let inner_ev = ev.get_inner_event() diff --git a/damus/Models/PushNotificationClient.swift b/damus/Models/PushNotificationClient.swift @@ -160,7 +160,7 @@ struct PushNotificationClient { } func current_push_notification_environment() -> Environment { - return self.settings.send_device_token_to_localhost ? .local_test(host: nil) : .production + return self.settings.push_notification_environment } } @@ -201,9 +201,10 @@ extension PushNotificationClient { } enum Environment: CaseIterable, Codable, Identifiable, StringCodable, Equatable, Hashable { - static var allCases: [Environment] = [.local_test(host: nil), .production] + static var allCases: [Environment] = [.local_test(host: nil), .staging, .production] case local_test(host: String?) + case staging case production func text_description() -> String { @@ -212,6 +213,8 @@ extension PushNotificationClient { return NSLocalizedString("Test (local)", comment: "Label indicating a local test environment for Push notification functionality (Developer feature)") case .production: return NSLocalizedString("Production", comment: "Label indicating the production environment for Push notification functionality") + case .staging: + return NSLocalizedString("Staging (for dev builds)", comment: "Label indicating the staging environment for Push notification functionality") } } @@ -221,7 +224,8 @@ extension PushNotificationClient { URL(string: "http://\(host ?? "localhost:8000")") ?? Constants.PUSH_NOTIFICATION_SERVER_TEST_BASE_URL case .production: Constants.PUSH_NOTIFICATION_SERVER_PRODUCTION_BASE_URL - + case .staging: + Constants.PUSH_NOTIFICATION_SERVER_STAGING_BASE_URL } } @@ -240,6 +244,8 @@ extension PushNotificationClient { self = .local_test(host: nil) case "production": self = .production + case "staging": + self = .staging default: let components = string.split(separator: ":", maxSplits: 1, omittingEmptySubsequences: false) if components.count == 2 && components[0] == "local_test" { @@ -257,6 +263,8 @@ extension PushNotificationClient { return "local_test:\(host)" } return "local_test" + case .staging: + return "staging" case .production: return "production" } @@ -273,6 +281,8 @@ extension PushNotificationClient { } case .production: return "production" + case .staging: + return "staging" } } } diff --git a/damus/Models/UserSettingsStore.swift b/damus/Models/UserSettingsStore.swift @@ -210,8 +210,8 @@ class UserSettingsStore: ObservableObject { @Setting(key: "enable_experimental_push_notifications", default_value: false) var enable_experimental_push_notifications: Bool - @Setting(key: "send_device_token_to_localhost", default_value: false) - var send_device_token_to_localhost: Bool + @StringSetting(key: "push_notification_environment", default_value: .production) + var push_notification_environment: PushNotificationClient.Environment @Setting(key: "enable_experimental_purple_api", default_value: false) var enable_experimental_purple_api: Bool diff --git a/damus/Util/Constants.swift b/damus/Util/Constants.swift @@ -15,6 +15,7 @@ class Constants { // MARK: Push notification server static let PUSH_NOTIFICATION_SERVER_PRODUCTION_BASE_URL: URL = URL(string: "https://notify.damus.io")! + static let PUSH_NOTIFICATION_SERVER_STAGING_BASE_URL: URL = URL(string: "https://notify-staging.damus.io")! static let PUSH_NOTIFICATION_SERVER_TEST_BASE_URL: URL = URL(string: "http://localhost:8000")! // MARK: Purple diff --git a/damus/Util/LocalNotification.swift b/damus/Util/LocalNotification.swift @@ -63,6 +63,7 @@ enum LocalNotificationType: String { case dm case like case mention + case reply case repost case zap case profile_zap diff --git a/damus/Views/Settings/DeveloperSettingsView.swift b/damus/Views/Settings/DeveloperSettingsView.swift @@ -21,9 +21,42 @@ struct DeveloperSettingsView: View { Toggle(NSLocalizedString("Enable experimental push notifications", comment: "Developer mode setting to enable experimental push notifications."), isOn: $settings.enable_experimental_push_notifications) .toggleStyle(.switch) - - Toggle(NSLocalizedString("Send device token to localhost", comment: "Developer mode setting to send device token metadata to a local server instead of the damus.io server."), isOn: $settings.send_device_token_to_localhost) - .toggleStyle(.switch) + + Picker(NSLocalizedString("Push notification environment", comment: "Prompt selection of the Push notification environment (Developer feature to switch between real/production mode to test modes)."), + selection: Binding( + get: { () -> PushNotificationClient.Environment in + switch settings.push_notification_environment { + case .local_test(_): + return .local_test(host: nil) // Avoid errors related to a value which is not a valid picker option + default: + return settings.push_notification_environment + } + }, + set: { new_value in + settings.push_notification_environment = new_value + } + ) + ) { + ForEach(PushNotificationClient.Environment.allCases, id: \.self) { push_notification_environment in + Text(push_notification_environment.text_description()) + .tag(push_notification_environment.to_string()) + } + } + + if case .local_test(_) = settings.push_notification_environment { + TextField( + NSLocalizedString("URL", comment: "Custom URL host for Damus push notification testing"), + text: Binding.init( + get: { + return settings.push_notification_environment.custom_host() ?? "" + }, set: { new_host_value in + settings.push_notification_environment = .local_test(host: new_host_value) + } + ) + ) + .disableAutocorrection(true) + .autocapitalization(UITextAutocapitalizationType.none) + } Toggle(NSLocalizedString("Enable experimental Purple API support", comment: "Developer mode setting to enable experimental Purple API support."), isOn: $settings.enable_experimental_purple_api) .toggleStyle(.switch) diff --git a/highlighter action extension/Info.plist b/highlighter action extension/Info.plist @@ -6,8 +6,13 @@ <dict> <key>NSExtensionAttributes</key> <dict> + <key>NSExtensionServiceRoleType</key> + <string>NSExtensionServiceRoleTypeViewer</string> <key>NSExtensionActivationRule</key> - <string>TRUEPREDICATE</string> + <dict> + <key>NSExtensionActivationRuleSupportsText</key> + <true/> + </dict> <key>NSExtensionJavaScriptPreprocessingFile</key> <string>getSelection</string> <key>NSExtensionServiceAllowsFinderPreviewItem</key>