diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj index 8386ebe7..09103f60 100644 --- a/damus.xcodeproj/project.pbxproj +++ b/damus.xcodeproj/project.pbxproj @@ -615,6 +615,7 @@ D7CE1B472B0BE719002EDAD4 /* NativeObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C32B9462A9AD44700DC3548 /* NativeObject.swift */; }; D7CE1B482B0BE719002EDAD4 /* Message.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C32B93D2A9AD44700DC3548 /* Message.swift */; }; D7CE1B492B0BE729002EDAD4 /* DisplayName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9BB83029C0ED4F00FC4E37 /* DisplayName.swift */; }; + D7D2A3812BF815D000E4B42B /* PushNotificationClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D2A3802BF815D000E4B42B /* PushNotificationClient.swift */; }; D7DBD41F2B02F15E002A6197 /* NostrKind.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3BEFD32819DE8F00B3DE84 /* NostrKind.swift */; }; D7DEEF2F2A8C021E00E0C99F /* NostrEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7DEEF2E2A8C021E00E0C99F /* NostrEventTests.swift */; }; D7EDED152B11776B0018B19C /* LibreTranslateServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE45AF5297BB2E700C1D842 /* LibreTranslateServer.swift */; }; @@ -1437,6 +1438,7 @@ D7CB5D5E2B11770C00AD4105 /* FollowState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowState.swift; sourceTree = ""; }; D7CBD1D32B8D21DC00BFD889 /* DamusPurpleNotificationManagement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusPurpleNotificationManagement.swift; sourceTree = ""; }; D7CBD1D52B8D509800BFD889 /* DamusPurpleImpendingExpirationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusPurpleImpendingExpirationTests.swift; sourceTree = ""; }; + D7D2A3802BF815D000E4B42B /* PushNotificationClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushNotificationClient.swift; sourceTree = ""; }; D7DEEF2E2A8C021E00E0C99F /* NostrEventTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NostrEventTests.swift; sourceTree = ""; }; D7EDED1B2B1178FE0018B19C /* NoteContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoteContent.swift; sourceTree = ""; }; D7EDED1D2B11797D0018B19C /* LongformEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LongformEvent.swift; sourceTree = ""; }; @@ -1663,6 +1665,7 @@ D74AAFC12B153395006CF0F4 /* HeadlessDamusState.swift */, B5C60C1F2B530D5100C5ECA7 /* MuteItem.swift */, B533694D2B66D791008A805E /* MutelistManager.swift */, + D7D2A3802BF815D000E4B42B /* PushNotificationClient.swift */, ); path = Models; sourceTree = ""; @@ -3291,6 +3294,7 @@ 5CF2DCCC2AA3AF0B00984B8D /* RelayPicView.swift in Sources */, 4C687C242A5FA86D0092C550 /* SearchHeaderView.swift in Sources */, 64FBD06F296255C400D9D3B2 /* Theme.swift in Sources */, + D7D2A3812BF815D000E4B42B /* PushNotificationClient.swift in Sources */, 4C1A9A2329DDDB8100516EAC /* IconLabel.swift in Sources */, 4CA352AC2A76C07F003BB08B /* NewUnmutesNotify.swift in Sources */, 4C3EA64928FF597700C48A62 /* bech32.c in Sources */, diff --git a/damus/ContentView.swift b/damus/ContentView.swift index b3846a57..a3bc5bd1 100644 --- a/damus/ContentView.swift +++ b/damus/ContentView.swift @@ -308,7 +308,7 @@ struct ContentView: View { active_sheet = .onboardingSuggestions hasSeenOnboardingSuggestions = true } - self.appDelegate?.settings = damus_state?.settings + self.appDelegate?.state = damus_state } .sheet(item: $active_sheet) { item in switch item { diff --git a/damus/Models/DamusState.swift b/damus/Models/DamusState.swift index 13679e45..2d42463a 100644 --- a/damus/Models/DamusState.swift +++ b/damus/Models/DamusState.swift @@ -36,6 +36,7 @@ class DamusState: HeadlessDamusState { let video: VideoController let ndb: Ndb var purple: DamusPurple + var push_notification_client: PushNotificationClient init(pool: RelayPool, keypair: Keypair, likes: EventCounter, boosts: EventCounter, contacts: Contacts, mutelist_manager: MutelistManager, profiles: Profiles, dms: DirectMessagesModel, previews: PreviewCache, zaps: Zaps, lnurls: LNUrls, settings: UserSettingsStore, relay_filters: RelayFilters, relay_model_cache: RelayModelCache, drafts: Drafts, events: EventCache, bookmarks: BookmarksManager, postbox: PostBox, bootstrap_relays: [RelayURL], replies: ReplyCounter, wallet: WalletModel, nav: NavigationCoordinator, music: MusicController?, video: VideoController, ndb: Ndb, purple: DamusPurple? = nil, quote_reposts: EventCounter) { self.pool = pool @@ -68,6 +69,7 @@ class DamusState: HeadlessDamusState { keypair: keypair ) self.quote_reposts = quote_reposts + self.push_notification_client = PushNotificationClient(keypair: keypair, settings: settings) } @discardableResult diff --git a/damus/Models/PushNotificationClient.swift b/damus/Models/PushNotificationClient.swift new file mode 100644 index 00000000..9f862896 --- /dev/null +++ b/damus/Models/PushNotificationClient.swift @@ -0,0 +1,62 @@ +// +// PushNotificationClient.swift +// damus +// +// Created by Daniel D’Aquino on 2024-05-17. +// + +import Foundation + +struct PushNotificationClient { + let keypair: Keypair + let settings: UserSettingsStore + private(set) var device_token: Data? = nil + + mutating func set_device_token(new_device_token: Data) async throws { + self.device_token = new_device_token + if settings.enable_experimental_push_notifications && settings.notifications_mode == .push { + try await self.send_token() + } + } + + func send_token() async throws { + guard let device_token else { return } + // Send the device token and pubkey to the server + let token = device_token.map { String(format: "%02.2hhx", $0) }.joined() + + Log.info("Sending device token to server: %s", for: .push_notifications, token) + + let pubkey = self.keypair.pubkey + + // Send those as JSON to the server + let json: [String: Any] = ["deviceToken": token, "pubkey": pubkey.hex()] + + // create post request + let url = self.settings.send_device_token_to_localhost ? Constants.DEVICE_TOKEN_RECEIVER_TEST_URL : Constants.DEVICE_TOKEN_RECEIVER_PRODUCTION_URL + var request = URLRequest(url: url) + request.httpMethod = "POST" + + // insert json data to the request + request.httpBody = try? JSONSerialization.data(withJSONObject: json, options: []) + request.setValue("application/json", forHTTPHeaderField: "Content-Type") + + let task = URLSession.shared.dataTask(with: request) { data, response, error in + guard let data = data, error == nil else { + print(error?.localizedDescription ?? "No data") + return + } + + if let response = response as? HTTPURLResponse, !(200...299).contains(response.statusCode) { + print("Unexpected status code: \(response.statusCode)") + return + } + + let responseJSON = try? JSONSerialization.jsonObject(with: data, options: []) + if let responseJSON = responseJSON as? [String: Any] { + print(responseJSON) + } + } + + task.resume() + } +} diff --git a/damus/damusApp.swift b/damus/damusApp.swift index ebd4c72c..eedd7b52 100644 --- a/damus/damusApp.swift +++ b/damus/damusApp.swift @@ -54,14 +54,12 @@ struct MainView: View { .onAppear { orientationTracker.setDeviceMajorAxis() keypair = get_saved_keypair() - appDelegate.keypair = keypair } } } class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate { - var keypair: Keypair? = nil - var settings: UserSettingsStore? = nil + var state: DamusState? = nil func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { UNUserNotificationCenter.current().delegate = self @@ -71,51 +69,13 @@ class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDele } func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { - // Return if this feature is disabled - guard let settings = self.settings else { return } - if !settings.enable_experimental_push_notifications || settings.notifications_mode == .local { + guard let state else { return } - // Send the device token and pubkey to the server - let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined() - - print("Received device token: \(token)") - - guard let pubkey = keypair?.pubkey else { - return + Task { + try await state.push_notification_client.set_device_token(new_device_token: deviceToken) } - - // Send those as JSON to the server - let json: [String: Any] = ["deviceToken": token, "pubkey": pubkey.hex()] - - // create post request - let url = settings.send_device_token_to_localhost ? Constants.DEVICE_TOKEN_RECEIVER_TEST_URL : Constants.DEVICE_TOKEN_RECEIVER_PRODUCTION_URL - var request = URLRequest(url: url) - request.httpMethod = "POST" - - // insert json data to the request - request.httpBody = try? JSONSerialization.data(withJSONObject: json, options: []) - request.setValue("application/json", forHTTPHeaderField: "Content-Type") - - let task = URLSession.shared.dataTask(with: request) { data, response, error in - guard let data = data, error == nil else { - print(error?.localizedDescription ?? "No data") - return - } - - if let response = response as? HTTPURLResponse, !(200...299).contains(response.statusCode) { - print("Unexpected status code: \(response.statusCode)") - return - } - - let responseJSON = try? JSONSerialization.jsonObject(with: data, options: []) - if let responseJSON = responseJSON as? [String: Any] { - print(responseJSON) - } - } - - task.resume() } // Handle the notification in the foreground state