From b148fb735e597397b7bb1dbb5a2480d7ba929a2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20D=E2=80=99Aquino?= Date: Fri, 17 May 2024 16:20:41 -0700 Subject: [PATCH] Move device token sending logic to its own component MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit moves the device token logic to a new PushNotificationClient, to move complexity from this specific feature away from damusApp.swift This commit also slightly improves the handling of device tokens, by caching it on the client struct even if the user is using local notifications, so that the device token can be sent to the server immediately after switching to push notifications mode. Signed-off-by: Daniel D’Aquino Reviewed-by: William Casarin --- damus.xcodeproj/project.pbxproj | 4 ++ damus/ContentView.swift | 2 +- damus/Models/DamusState.swift | 2 + damus/Models/PushNotificationClient.swift | 62 +++++++++++++++++++++++ damus/damusApp.swift | 48 ++---------------- 5 files changed, 73 insertions(+), 45 deletions(-) create mode 100644 damus/Models/PushNotificationClient.swift 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