From fc5a9066c1851e0c705c48777031f3728778fb74 Mon Sep 17 00:00:00 2001 From: Tony Li Date: Fri, 7 Aug 2026 21:32:01 +1200 Subject: [PATCH 1/2] Format notification migration files with swift-format --- .../MigrationHeaderConfiguration.swift | 85 ++++--- .../JetpackNotificationMigrationService.swift | 49 ++-- .../InteractiveNotificationsManager.swift | 213 ++++++++++++------ 3 files changed, 230 insertions(+), 117 deletions(-) diff --git a/WordPress/Classes/Jetpack/JetpackMigration/Common/Views/Configuration/MigrationHeaderConfiguration.swift b/WordPress/Classes/Jetpack/JetpackMigration/Common/Views/Configuration/MigrationHeaderConfiguration.swift index c47d0a614615..4231512a865d 100644 --- a/WordPress/Classes/Jetpack/JetpackMigration/Common/Views/Configuration/MigrationHeaderConfiguration.swift +++ b/WordPress/Classes/Jetpack/JetpackMigration/Common/Views/Configuration/MigrationHeaderConfiguration.swift @@ -65,7 +65,8 @@ private extension MigrationHeaderConfiguration { case .welcome: return welcomeSecondaryDescription(plural: multiSite) case .notifications: - return JetpackNotificationMigrationService.shared.isMigrationSupported ? notificationsSecondaryDescription : nil + return JetpackNotificationMigrationService.shared.isMigrationSupported + ? notificationsSecondaryDescription : nil case .done: return nil case .dismiss: @@ -73,47 +74,67 @@ private extension MigrationHeaderConfiguration { } } - static let welcomeTitle = NSLocalizedString("migration.welcome.title", - value: "Welcome to Jetpack!", - comment: "The title in the migration welcome screen") - - static let notificationsTitle = NSLocalizedString("migration.notifications.title", - value: "Allow notifications to keep up with your site", - comment: "Title of the migration notifications screen.") - - static let doneTitle = NSLocalizedString("migration.done.title", - value: "Thanks for switching to Jetpack!", - comment: "Title of the migration done screen.") - - static let welcomePrimaryDescription = NSLocalizedString("migration.welcome.primaryDescription", - value: "It looks like you’re switching from the WordPress app.", - comment: "The primary description in the migration welcome screen") - - static let notificationsPrimaryDescription = NSLocalizedString("migration.notifications.primaryDescription", - value: "You’ll get all the same notifications but now they’ll come from the Jetpack app.", - comment: "Primary description in the migration notifications screen.") - - static let donePrimaryDescription = NSLocalizedString("migration.done.primaryDescription", - value: "We’ve transferred all your data and settings. Everything is right where you left it.", - comment: "Primary description in the migration done screen.") - - static let doneSecondaryDescription = NSLocalizedString("migration.done.secondaryDescription", value: "It's time to continue your WordPress journey on the Jetpack app!", comment: "Secondary description (second paragraph) in the migration done screen.") - - static let notificationsSecondaryDescription = NSLocalizedString("migration.notifications.secondaryDescription", - value: "We’ll disable notifications for the WordPress app.", - comment: "Secondary description in the migration notifications screen") + static let welcomeTitle = NSLocalizedString( + "migration.welcome.title", + value: "Welcome to Jetpack!", + comment: "The title in the migration welcome screen" + ) + + static let notificationsTitle = NSLocalizedString( + "migration.notifications.title", + value: "Allow notifications to keep up with your site", + comment: "Title of the migration notifications screen." + ) + + static let doneTitle = NSLocalizedString( + "migration.done.title", + value: "Thanks for switching to Jetpack!", + comment: "Title of the migration done screen." + ) + + static let welcomePrimaryDescription = NSLocalizedString( + "migration.welcome.primaryDescription", + value: "It looks like you’re switching from the WordPress app.", + comment: "The primary description in the migration welcome screen" + ) + + static let notificationsPrimaryDescription = NSLocalizedString( + "migration.notifications.primaryDescription", + value: "You’ll get all the same notifications but now they’ll come from the Jetpack app.", + comment: "Primary description in the migration notifications screen." + ) + + static let donePrimaryDescription = NSLocalizedString( + "migration.done.primaryDescription", + value: "We’ve transferred all your data and settings. Everything is right where you left it.", + comment: "Primary description in the migration done screen." + ) + + static let doneSecondaryDescription = NSLocalizedString( + "migration.done.secondaryDescription", + value: "It's time to continue your WordPress journey on the Jetpack app!", + comment: "Secondary description (second paragraph) in the migration done screen." + ) + + static let notificationsSecondaryDescription = NSLocalizedString( + "migration.notifications.secondaryDescription", + value: "We’ll disable notifications for the WordPress app.", + comment: "Secondary description in the migration notifications screen" + ) static func welcomeSecondaryDescription(plural: Bool) -> String { if plural { return NSLocalizedString( "migration.welcome.secondaryDescription.plural", - value: "We found your sites. Continue to transfer all your data and sign in to Jetpack automatically.", + value: + "We found your sites. Continue to transfer all your data and sign in to Jetpack automatically.", comment: "The plural form of the secondary description in the migration welcome screen" ) } else { return NSLocalizedString( "migration.welcome.secondaryDescription.singular", - value: "We found your site. Continue to transfer all your data and sign in to Jetpack automatically.", + value: + "We found your site. Continue to transfer all your data and sign in to Jetpack automatically.", comment: "The singular form of the secondary description in the migration welcome screen" ) } diff --git a/WordPress/Classes/Services/JetpackNotificationMigrationService.swift b/WordPress/Classes/Services/JetpackNotificationMigrationService.swift index dad6d2c6abca..801a8a9791da 100644 --- a/WordPress/Classes/Services/JetpackNotificationMigrationService.swift +++ b/WordPress/Classes/Services/JetpackNotificationMigrationService.swift @@ -23,7 +23,7 @@ final class JetpackNotificationMigrationService: JetpackNotificationMigrationSer private let jetpackNotificationMigrationDefaultsKey = "jetpackNotificationMigrationDefaultsKey" private var jetpackMigrationPreventDuplicateNotifications: Bool { - return RemoteFeatureFlag.jetpackMigrationPreventDuplicateNotifications.enabled(using: featureFlagStore) + RemoteFeatureFlag.jetpackMigrationPreventDuplicateNotifications.enabled(using: featureFlagStore) } private lazy var notificationSettingsService: NotificationSettingsService? = { @@ -79,17 +79,19 @@ final class JetpackNotificationMigrationService: JetpackNotificationMigrationSer /// disableWordPressNotificationsFromJetpack may get triggered multiple times from Jetpack app but it only needs to be executed the first time private var isMigrationDone: Bool { get { - return userDefaults.bool(forKey: jetpackNotificationMigrationDefaultsKey) + userDefaults.bool(forKey: jetpackNotificationMigrationDefaultsKey) } set { userDefaults.setValue(newValue, forKey: jetpackNotificationMigrationDefaultsKey) } } - init(remoteNotificationRegister: RemoteNotificationRegister = UIApplication.shared, - featureFlagStore: RemoteFeatureFlagStore = RemoteFeatureFlagStore(), - userDefaults: UserDefaults = .standard, - isWordPress: Bool = AppConfiguration.isWordPress) { + init( + remoteNotificationRegister: RemoteNotificationRegister = UIApplication.shared, + featureFlagStore: RemoteFeatureFlagStore = RemoteFeatureFlagStore(), + userDefaults: UserDefaults = .standard, + isWordPress: Bool = AppConfiguration.isWordPress + ) { self.remoteNotificationRegister = remoteNotificationRegister self.featureFlagStore = featureFlagStore self.userDefaults = userDefaults @@ -97,12 +99,13 @@ final class JetpackNotificationMigrationService: JetpackNotificationMigrationSer } func shouldShowNotificationControl() -> Bool { - return jetpackMigrationPreventDuplicateNotifications && isWordPress + jetpackMigrationPreventDuplicateNotifications && isWordPress } func shouldPresentNotifications() -> Bool { let notificationsDisabled = !JetpackFeaturesRemovalCoordinator.jetpackFeaturesEnabled() - let appMigrated = jetpackMigrationPreventDuplicateNotifications + let appMigrated = + jetpackMigrationPreventDuplicateNotifications && isWordPress && userDefaults.bool(forKey: wordPressNotificationsToggledDefaultsKey) && !wordPressNotificationsEnabled @@ -160,7 +163,9 @@ final class JetpackNotificationMigrationService: JetpackNotificationMigrationSer // MARK: - Local notifications - private func cancelAllPendingWordPressLocalNotifications(notificationCenter: UNUserNotificationCenter = UNUserNotificationCenter.current()) { + private func cancelAllPendingWordPressLocalNotifications( + notificationCenter: UNUserNotificationCenter = UNUserNotificationCenter.current() + ) { if isWordPress { notificationCenter.removeAllPendingNotificationRequests() } @@ -178,21 +183,25 @@ final class JetpackNotificationMigrationService: JetpackNotificationMigrationSer } private func rescheduleBloggingReminderNotifications() { - notificationSettingsService?.getAllSettings { [weak self] settings in - for setting in settings { - if let blog = setting.blog, - let schedule = self?.bloggingRemindersScheduler?.schedule(for: blog), - let time = self?.bloggingRemindersScheduler?.scheduledTime(for: blog) { - if schedule != .none { - self?.bloggingRemindersScheduler?.schedule(schedule, for: blog, time: time) { result in - if case .success = result { - BloggingRemindersFlow.setHasShownWeeklyRemindersFlow(for: blog) - } + notificationSettingsService? + .getAllSettings { [weak self] settings in + for setting in settings { + if let blog = setting.blog, + let schedule = self?.bloggingRemindersScheduler?.schedule(for: blog), + let time = self?.bloggingRemindersScheduler?.scheduledTime(for: blog) + { + if schedule != .none { + self?.bloggingRemindersScheduler? + .schedule(schedule, for: blog, time: time) { result in + if case .success = result { + BloggingRemindersFlow.setHasShownWeeklyRemindersFlow(for: blog) + } + } } } } + } failure: { _ in } - } failure: { _ in } } } diff --git a/WordPress/Classes/Utility/Notifications/InteractiveNotificationsManager.swift b/WordPress/Classes/Utility/Notifications/InteractiveNotificationsManager.swift index 385c19767d34..cc7c26daf291 100644 --- a/WordPress/Classes/Utility/Notifications/InteractiveNotificationsManager.swift +++ b/WordPress/Classes/Utility/Notifications/InteractiveNotificationsManager.swift @@ -23,19 +23,19 @@ final class InteractiveNotificationsManager: NSObject { /// Returns the Core Data main context. /// @objc var context: NSManagedObjectContext { - return ContextManager.shared.mainContext + ContextManager.shared.mainContext } /// Returns a CommentService instance. /// @objc var commentService: CommentService { - return CommentService(coreDataStack: ContextManager.shared) + CommentService(coreDataStack: ContextManager.shared) } /// Returns a NotificationSyncMediator instance. /// var notificationSyncMediator: NotificationSyncMediator? { - return NotificationSyncMediator() + NotificationSyncMediator() } /// Registers the device for User Notifications. @@ -83,10 +83,23 @@ final class InteractiveNotificationsManager: NSObject { /// - Returns: True on success /// @objc @discardableResult - func handleAction(with identifier: String, category: String, threadId: String?, userInfo: NSDictionary, responseText: String?) -> Bool { + func handleAction( + with identifier: String, + category: String, + threadId: String?, + userInfo: NSDictionary, + responseText: String? + ) -> Bool { if let noteCategory = NoteCategoryDefinition(rawValue: category), - noteCategory.isLocalNotification { - return handleLocalNotificationAction(with: identifier, category: category, threadId: threadId, userInfo: userInfo, responseText: responseText) + noteCategory.isLocalNotification + { + return handleLocalNotificationAction( + with: identifier, + category: category, + threadId: threadId, + userInfo: userInfo, + responseText: responseText + ) } if NoteActionDefinition.approveLogin == NoteActionDefinition(rawValue: identifier) { @@ -96,7 +109,8 @@ final class InteractiveNotificationsManager: NSObject { guard AccountHelper.isDotcomAvailable(), let noteID = userInfo.object(forKey: "note_id") as? NSNumber, let siteID = userInfo.object(forKey: "blog_id") as? NSNumber, - let commentID = userInfo.object(forKey: "comment_id") as? NSNumber else { + let commentID = userInfo.object(forKey: "comment_id") as? NSNumber + else { return false } @@ -137,14 +151,20 @@ final class InteractiveNotificationsManager: NSObject { ] WPAppAnalytics.track(.pushNotificationQuickActionCompleted, withProperties: modernEventProperties) - let legacyEventProperties = [ WPAppAnalyticsKeyLegacyQuickAction: true ] + let legacyEventProperties = [WPAppAnalyticsKeyLegacyQuickAction: true] WPAppAnalytics.track(actionEvent, withProperties: legacyEventProperties) } return true } - func handleLocalNotificationAction(with identifier: String, category: String, threadId: String?, userInfo: NSDictionary, responseText: String?) -> Bool { + func handleLocalNotificationAction( + with identifier: String, + category: String, + threadId: String?, + userInfo: NSDictionary, + responseText: String? + ) -> Bool { if let noteCategory = NoteCategoryDefinition(rawValue: category) { switch noteCategory { case .mediaUploadSuccess, .mediaUploadFailure: @@ -250,7 +270,7 @@ final class InteractiveNotificationsManager: NSObject { case .dismissPrompt: // user taps on the "Dismiss" button. WPAnalytics.track(.promptsNotificationDismissActionTapped) - // no-op, let the notification be dismissed. + // no-op, let the notification be dismissed. default: break @@ -291,7 +311,8 @@ extension InteractiveNotificationsManager { private func blog(from threadId: String?) -> Blog? { if let threadId, - let blogId = Int(threadId) { + let blogId = Int(threadId) + { return try? Blog.lookup(withID: blogId, in: ContextManager.shared.mainContext) } @@ -316,12 +337,17 @@ private extension InteractiveNotificationsManager { /// - siteID: The site identifier /// func likeCommentWithCommentID(_ commentID: NSNumber, noteID: NSNumber, siteID: NSNumber) { - commentService.likeComment(withID: commentID, siteID: siteID, success: { - self.notificationSyncMediator?.markAsReadAndSync(noteID.stringValue) - DDLogInfo("Liked comment from push notification") - }, failure: { _ in - DDLogInfo("Couldn't like comment from push notification") - }) + commentService.likeComment( + withID: commentID, + siteID: siteID, + success: { + self.notificationSyncMediator?.markAsReadAndSync(noteID.stringValue) + DDLogInfo("Liked comment from push notification") + }, + failure: { _ in + DDLogInfo("Couldn't like comment from push notification") + } + ) } /// Approves a comment and marks the associated notification as read @@ -331,12 +357,17 @@ private extension InteractiveNotificationsManager { /// - siteID: The site identifier /// func approveCommentWithCommentID(_ commentID: NSNumber, noteID: NSNumber, siteID: NSNumber) { - commentService.approveComment(withID: commentID, siteID: siteID, success: { - self.notificationSyncMediator?.markAsReadAndSync(noteID.stringValue) - DDLogInfo("Successfully moderated comment from push notification") - }, failure: { _ in - DDLogInfo("Couldn't moderate comment from push notification") - }) + commentService.approveComment( + withID: commentID, + siteID: siteID, + success: { + self.notificationSyncMediator?.markAsReadAndSync(noteID.stringValue) + DDLogInfo("Successfully moderated comment from push notification") + }, + failure: { _ in + DDLogInfo("Couldn't moderate comment from push notification") + } + ) } /// Opens the details for a given notificationId @@ -355,12 +386,18 @@ private extension InteractiveNotificationsManager { /// - content: The text for the comment reply /// func replyToCommentWithCommentID(_ commentID: NSNumber, noteID: NSNumber, siteID: NSNumber, content: String) { - commentService.replyToComment(withID: commentID, siteID: siteID, content: content, success: { - self.notificationSyncMediator?.markAsReadAndSync(noteID.stringValue) - DDLogInfo("Successfully replied comment from push notification") - }, failure: { _ in - DDLogInfo("Couldn't reply to comment from push notification") - }) + commentService.replyToComment( + withID: commentID, + siteID: siteID, + content: content, + success: { + self.notificationSyncMediator?.markAsReadAndSync(noteID.stringValue) + DDLogInfo("Successfully replied comment from push notification") + }, + failure: { _ in + DDLogInfo("Couldn't reply to comment from push notification") + } + ) } /// Returns a collection of *UNNotificationCategory* instances, for each one of the @@ -369,7 +406,9 @@ private extension InteractiveNotificationsManager { /// - Returns: A set of *UNNotificationCategory* instances. /// func supportedNotificationCategories() -> Set { - let categories: [UNNotificationCategory] = NoteCategoryDefinition.allDefinitions.map({ $0.notificationCategory() }) + let categories: [UNNotificationCategory] = NoteCategoryDefinition.allDefinitions.map({ + $0.notificationCategory() + }) return Set(categories) } @@ -379,7 +418,7 @@ private extension InteractiveNotificationsManager { /// - Returns: True if successfule. Otherwise false. /// func approveAuthChallenge(_ userInfo: NSDictionary) -> Bool { - return PushNotificationsManager.shared.handleAuthenticationApprovedAction(userInfo) + PushNotificationsManager.shared.handleAuthenticationApprovedAction(userInfo) } } @@ -440,11 +479,11 @@ extension InteractiveNotificationsManager { } var identifier: String { - return rawValue + rawValue } var isLocalNotification: Bool { - return NoteCategoryDefinition.localDefinitions.contains(self) + NoteCategoryDefinition.localDefinitions.contains(self) } var notificationCategoryOptions: [UNNotificationCategoryOptions] { @@ -457,15 +496,23 @@ extension InteractiveNotificationsManager { } func notificationCategory() -> UNNotificationCategory { - return UNNotificationCategory( + UNNotificationCategory( identifier: identifier, actions: actions.map({ $0.notificationAction() }), intentIdentifiers: [], - options: UNNotificationCategoryOptions()) + options: UNNotificationCategoryOptions() + ) } - static var allDefinitions = [commentApprove, commentLike, commentReply, commentReplyWithLike, mediaUploadSuccess, mediaUploadFailure, postUploadSuccess, postUploadFailure, shareUploadSuccess, shareUploadFailure, login, bloggingReminderWeekly, bloggingPrompt] - static var localDefinitions = [mediaUploadSuccess, mediaUploadFailure, postUploadSuccess, postUploadFailure, shareUploadSuccess, shareUploadFailure, bloggingReminderWeekly, weeklyRoundup, bloggingPrompt] + static var allDefinitions = [ + commentApprove, commentLike, commentReply, commentReplyWithLike, mediaUploadSuccess, mediaUploadFailure, + postUploadSuccess, postUploadFailure, shareUploadSuccess, shareUploadFailure, login, bloggingReminderWeekly, + bloggingPrompt + ] + static var localDefinitions = [ + mediaUploadSuccess, mediaUploadFailure, postUploadSuccess, postUploadFailure, shareUploadSuccess, + shareUploadFailure, bloggingReminderWeekly, weeklyRoundup, bloggingPrompt + ] } /// Describes the custom actions that WPiOS can perform in response to a Push notification. @@ -496,11 +543,17 @@ extension InteractiveNotificationsManager { case .mediaRetry: return NSLocalizedString("Retry", comment: "Opens the media library .") case .postView: - return NSLocalizedString("View", comment: "Opens the post epilogue screen to allow sharing / viewing of a post.") + return NSLocalizedString( + "View", + comment: "Opens the post epilogue screen to allow sharing / viewing of a post." + ) case .shareEditPost: return NSLocalizedString("Edit Post", comment: "Opens the editor to edit an existing post.") case .approveLogin: - return NSLocalizedString("Approve", comment: "Verb. Approves a 2fa authentication challenge, and logs in a user.") + return NSLocalizedString( + "Approve", + comment: "Verb. Approves a 2fa authentication challenge, and logs in a user." + ) case .denyLogin: return NSLocalizedString("Deny", comment: "Verb. Denies a 2fa authentication challenge.") case .answerPrompt: @@ -515,11 +568,11 @@ extension InteractiveNotificationsManager { } var destructive: Bool { - return false + false } var identifier: String { - return rawValue + rawValue } var requiresAuthentication: Bool { @@ -556,13 +609,25 @@ extension InteractiveNotificationsManager { func notificationAction() -> UNNotificationAction { switch self { case .commentReply: - return UNTextInputNotificationAction(identifier: identifier, - title: description, - options: notificationActionOptions, - textInputButtonTitle: NSLocalizedString("Reply", comment: "Verb. Button title. Reply to a comment."), - textInputPlaceholder: NSLocalizedString("Write a reply…", comment: "Placeholder text for inline compose view")) + return UNTextInputNotificationAction( + identifier: identifier, + title: description, + options: notificationActionOptions, + textInputButtonTitle: NSLocalizedString( + "Reply", + comment: "Verb. Button title. Reply to a comment." + ), + textInputPlaceholder: NSLocalizedString( + "Write a reply…", + comment: "Placeholder text for inline compose view" + ) + ) default: - return UNNotificationAction(identifier: identifier, title: description, options: notificationActionOptions) + return UNNotificationAction( + identifier: identifier, + title: description, + options: notificationActionOptions + ) } } @@ -582,29 +647,39 @@ extension InteractiveNotificationsManager { } } - static var allDefinitions = [commentApprove, commentLike, commentReply, mediaWritePost, mediaRetry, postView, shareEditPost, approveLogin, denyLogin, answerPrompt, dismissPrompt] + static var allDefinitions = [ + commentApprove, commentLike, commentReply, mediaWritePost, mediaRetry, postView, shareEditPost, + approveLogin, denyLogin, answerPrompt, dismissPrompt + ] } } // MARK: - UNUserNotificationCenterDelegate Conformance // extension InteractiveNotificationsManager: UNUserNotificationCenterDelegate { - func userNotificationCenter(_ center: UNUserNotificationCenter, - willPresent notification: UNNotification, - withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Swift.Void) { + func userNotificationCenter( + _ center: UNUserNotificationCenter, + willPresent notification: UNNotification, + withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Swift.Void + ) { let userInfo = notification.request.content.userInfo as NSDictionary // If the app is open, and a Zendesk view is being shown, Zendesk will display an alert allowing the user to view the updated ticket. handleZendeskNotification(userInfo: userInfo) // Otherwise see if it's an auth notification - if PushNotificationsManager.shared.handleAuthenticationNotification(userInfo, userInteraction: true, completionHandler: nil) { + if PushNotificationsManager.shared.handleAuthenticationNotification( + userInfo, + userInteraction: true, + completionHandler: nil + ) { return } // If it's a blogging reminder notification, display it in-app if notification.request.content.categoryIdentifier == NoteCategoryDefinition.bloggingReminderWeekly.rawValue - || notification.request.content.categoryIdentifier == NoteCategoryDefinition.weeklyRoundup.rawValue { + || notification.request.content.categoryIdentifier == NoteCategoryDefinition.weeklyRoundup.rawValue + { completionHandler([.banner, .list, .sound]) return @@ -613,10 +688,13 @@ extension InteractiveNotificationsManager: UNUserNotificationCenterDelegate { // Otherwise a share notification let category = notification.request.content.categoryIdentifier - guard category == ShareNoticeConstants.categorySuccessIdentifier || category == ShareNoticeConstants.categoryFailureIdentifier, + guard + category == ShareNoticeConstants.categorySuccessIdentifier + || category == ShareNoticeConstants.categoryFailureIdentifier, (userInfo.object(forKey: ShareNoticeUserInfoKey.originatedFromAppExtension) as? Bool) == true, - let postUploadOpID = userInfo.object(forKey: ShareNoticeUserInfoKey.postUploadOpID) as? String else { - return + let postUploadOpID = userInfo.object(forKey: ShareNoticeUserInfoKey.postUploadOpID) as? String + else { + return } // If the notification originated from the share extension, disregard this current notification and resend a new one. @@ -626,25 +704,30 @@ extension InteractiveNotificationsManager: UNUserNotificationCenterDelegate { private func handleZendeskNotification(userInfo: NSDictionary) { if let type = userInfo.string(forKey: ZendeskUtils.PushNotificationIdentifiers.key), - type == ZendeskUtils.PushNotificationIdentifiers.type { + type == ZendeskUtils.PushNotificationIdentifiers.type + { ZendeskUtils.handlePushNotification(userInfo) } } - func userNotificationCenter(_ center: UNUserNotificationCenter, - didReceive response: UNNotificationResponse, - withCompletionHandler completionHandler: @escaping () -> Void) { + func userNotificationCenter( + _ center: UNUserNotificationCenter, + didReceive response: UNNotificationResponse, + withCompletionHandler completionHandler: @escaping () -> Void + ) { let userInfo = response.notification.request.content.userInfo as NSDictionary let textInputResponse = response as? UNTextInputNotificationResponse // Analytics PushNotificationsManager.shared.trackNotification(with: userInfo, response: response) - if handleAction(with: response.actionIdentifier, - category: response.notification.request.content.categoryIdentifier, - threadId: response.notification.request.content.threadIdentifier, - userInfo: userInfo, - responseText: textInputResponse?.userText) { + if handleAction( + with: response.actionIdentifier, + category: response.notification.request.content.categoryIdentifier, + threadId: response.notification.request.content.threadIdentifier, + userInfo: userInfo, + responseText: textInputResponse?.userText + ) { completionHandler() return } From 4847e333552b85fcf72982e41a631bcea16bbc23 Mon Sep 17 00:00:00 2001 From: Tony Li Date: Fri, 7 Aug 2026 21:39:54 +1200 Subject: [PATCH 2/2] Stop launching the WordPress app when Jetpack enables notifications When the user allowed notifications in the Jetpack app with the WordPress app installed, JetpackNotificationMigrationService opened wordpressnotificationmigration:// to launch WordPress, disable its notifications, and bounce back via jetpacknotificationmigration://. The user saw Jetpack slide away to a (often signed-out) WordPress app and return, without asking for any of it (CMM-2224). The round trip no longer serves a purpose. The WordPress app disables its own notifications on every launch since the Jetpack features removal (PushNotificationsManager.setupRemoteNotifications unregisters when shouldPresentNotifications is false), and a signed-out WordPress app never registers a device token in the first place. Remove the Jetpack-side trigger and the migration flow copy that promised to disable WordPress notifications. Keep the WordPress-side URL handler so older Jetpack versions that still open the scheme get redirected back to Jetpack instead of stranding the user in WordPress. Fixes CMM-2224 --- .../MigrationHeaderConfiguration.swift | 9 +--- .../JetpackNotificationMigrationService.swift | 51 +++---------------- .../InteractiveNotificationsManager.swift | 11 +--- 3 files changed, 9 insertions(+), 62 deletions(-) diff --git a/WordPress/Classes/Jetpack/JetpackMigration/Common/Views/Configuration/MigrationHeaderConfiguration.swift b/WordPress/Classes/Jetpack/JetpackMigration/Common/Views/Configuration/MigrationHeaderConfiguration.swift index 4231512a865d..a5b91d209346 100644 --- a/WordPress/Classes/Jetpack/JetpackMigration/Common/Views/Configuration/MigrationHeaderConfiguration.swift +++ b/WordPress/Classes/Jetpack/JetpackMigration/Common/Views/Configuration/MigrationHeaderConfiguration.swift @@ -65,8 +65,7 @@ private extension MigrationHeaderConfiguration { case .welcome: return welcomeSecondaryDescription(plural: multiSite) case .notifications: - return JetpackNotificationMigrationService.shared.isMigrationSupported - ? notificationsSecondaryDescription : nil + return nil case .done: return nil case .dismiss: @@ -116,12 +115,6 @@ private extension MigrationHeaderConfiguration { comment: "Secondary description (second paragraph) in the migration done screen." ) - static let notificationsSecondaryDescription = NSLocalizedString( - "migration.notifications.secondaryDescription", - value: "We’ll disable notifications for the WordPress app.", - comment: "Secondary description in the migration notifications screen" - ) - static func welcomeSecondaryDescription(plural: Bool) -> String { if plural { return NSLocalizedString( diff --git a/WordPress/Classes/Services/JetpackNotificationMigrationService.swift b/WordPress/Classes/Services/JetpackNotificationMigrationService.swift index 801a8a9791da..7b49d2028e3f 100644 --- a/WordPress/Classes/Services/JetpackNotificationMigrationService.swift +++ b/WordPress/Classes/Services/JetpackNotificationMigrationService.swift @@ -5,10 +5,13 @@ protocol JetpackNotificationMigrationServiceProtocol { func shouldPresentNotifications() -> Bool } -/// The service is created to support disabling WordPress notifications when Jetpack app enables notifications -/// The service uses URLScheme to determine from Jetpack app if WordPress app is installed, open it, disable notifications and come back to Jetpack app -/// This is a temporary solution to avoid duplicate notifications during the migration process from WordPress to Jetpack app -/// This service and its usage can be deleted once the migration is done +/// The service was created to support disabling WordPress notifications when the Jetpack app enables notifications, +/// as a temporary solution to avoid duplicate notifications during the migration process from WordPress to Jetpack app. +/// +/// The Jetpack app no longer initiates this migration: launching the WordPress app just to disable its notifications +/// visibly bounced the user between apps (CMM-2224), and the WordPress app has been disabling its own notifications +/// on every launch since the Jetpack features removal. The WordPress-side URL handler is kept so older Jetpack +/// versions that still open `wordpressnotificationmigration://` get redirected back instead of stranding the user. final class JetpackNotificationMigrationService: JetpackNotificationMigrationServiceProtocol { private let remoteNotificationRegister: RemoteNotificationRegister private let featureFlagStore: RemoteFeatureFlagStore @@ -20,7 +23,6 @@ final class JetpackNotificationMigrationService: JetpackNotificationMigrationSer static let wordPressScheme = "wordpressnotificationmigration" static let jetpackScheme = "jetpacknotificationmigration" private let wordPressNotificationsToggledDefaultsKey = "wordPressNotificationsToggledDefaultsKey" - private let jetpackNotificationMigrationDefaultsKey = "jetpackNotificationMigrationDefaultsKey" private var jetpackMigrationPreventDuplicateNotifications: Bool { RemoteFeatureFlag.jetpackMigrationPreventDuplicateNotifications.enabled(using: featureFlagStore) @@ -67,25 +69,6 @@ final class JetpackNotificationMigrationService: JetpackNotificationMigrationSer } } - /// Migration is supported if WordPress is compatible with the notification migration URLScheme - var isMigrationSupported: Bool { - guard let url = URL(string: "\(JetpackNotificationMigrationService.wordPressScheme)://") else { - return false - } - - return UIApplication.shared.canOpenURL(url) && jetpackMigrationPreventDuplicateNotifications - } - - /// disableWordPressNotificationsFromJetpack may get triggered multiple times from Jetpack app but it only needs to be executed the first time - private var isMigrationDone: Bool { - get { - userDefaults.bool(forKey: jetpackNotificationMigrationDefaultsKey) - } - set { - userDefaults.setValue(newValue, forKey: jetpackNotificationMigrationDefaultsKey) - } - } - init( remoteNotificationRegister: RemoteNotificationRegister = UIApplication.shared, featureFlagStore: RemoteFeatureFlagStore = RemoteFeatureFlagStore(), @@ -118,26 +101,6 @@ final class JetpackNotificationMigrationService: JetpackNotificationMigrationSer return !disableNotifications } - // MARK: - Only executed on Jetpack app - - func disableWordPressNotificationsFromJetpack() { - guard !isMigrationDone, jetpackMigrationPreventDuplicateNotifications, !isWordPress else { - return - } - - let wordPressUrl: URL? = { - var components = URLComponents() - components.scheme = JetpackNotificationMigrationService.wordPressScheme - return components.url - }() - - /// Open WordPress app to disable notifications - if let url = wordPressUrl, UIApplication.shared.canOpenURL(url) { - isMigrationDone = true - UIApplication.shared.open(url) - } - } - // MARK: - Only executed on WordPress app func handleNotificationMigrationOnWordPress() -> Bool { diff --git a/WordPress/Classes/Utility/Notifications/InteractiveNotificationsManager.swift b/WordPress/Classes/Utility/Notifications/InteractiveNotificationsManager.swift index cc7c26daf291..bf1720a85dc0 100644 --- a/WordPress/Classes/Utility/Notifications/InteractiveNotificationsManager.swift +++ b/WordPress/Classes/Utility/Notifications/InteractiveNotificationsManager.swift @@ -61,11 +61,10 @@ final class InteractiveNotificationsManager: NSObject { let options: UNAuthorizationOptions = [.badge, .sound, .alert, .providesAppNotificationSettings] let notificationCenter = UNUserNotificationCenter.current() - notificationCenter.requestAuthorization(options: options) { [weak self] allowed, _ in + notificationCenter.requestAuthorization(options: options) { allowed, _ in DispatchQueue.main.async { if allowed { WPAnalytics.track(.pushNotificationOSAlertAllowed) - self?.disableWordPressNotificationsIfNeeded() } else { WPAnalytics.track(.pushNotificationOSAlertDenied) } @@ -754,11 +753,3 @@ extension InteractiveNotificationsManager: UNUserNotificationCenterDelegate { MeNavigationAction.notificationSettings.perform(router: UniversalLinkRouter.shared) } } - -private extension InteractiveNotificationsManager { - /// A temporary setting to allow controlling WordPress notifications when they are disabled after Jetpack installation - /// Disable WordPress notifications when they are enabled on Jetpack - func disableWordPressNotificationsIfNeeded() { - JetpackNotificationMigrationService.shared.disableWordPressNotificationsFromJetpack() - } -}