From 47314bd639ec8edb17728106bfd8936f32a79047 Mon Sep 17 00:00:00 2001 From: Morgan Pretty Date: Mon, 21 Feb 2022 14:48:53 +1100 Subject: [PATCH] Added a notification to indicate the user has a new message request Fixed a bug where the notification count could be increased for message requests Fixed a bug where an approved contact could be 'unapproved' due to an order of execution issue when generating the config sync message Fixed a check to avoid registering for push notifications when on the simulator (old check didn't cater for M1 Macs) Moved the 'hasHiddenMessageRequests' into the group user defaults so it can be accessed within the notification extension Added code to handle an edge case where an old client could incorrectly un-approve a contact via a legacy configuration message --- Session.xcodeproj/project.pbxproj | 4 - Session/Home/HomeVC.swift | 12 +- .../Translations/de.lproj/Localizable.strings | 1 + .../Translations/en.lproj/Localizable.strings | 1 + .../Translations/es.lproj/Localizable.strings | 1 + .../Translations/fa.lproj/Localizable.strings | 1 + .../Translations/fi.lproj/Localizable.strings | 1 + .../Translations/fr.lproj/Localizable.strings | 1 + .../Translations/hi.lproj/Localizable.strings | 1 + .../Translations/hr.lproj/Localizable.strings | 1 + .../id-ID.lproj/Localizable.strings | 1 + .../Translations/it.lproj/Localizable.strings | 1 + .../Translations/ja.lproj/Localizable.strings | 1 + .../Translations/nl.lproj/Localizable.strings | 1 + .../Translations/pl.lproj/Localizable.strings | 1 + .../pt_BR.lproj/Localizable.strings | 1 + .../Translations/ru.lproj/Localizable.strings | 1 + .../Translations/si.lproj/Localizable.strings | 1 + .../Translations/sk.lproj/Localizable.strings | 1 + .../Translations/sv.lproj/Localizable.strings | 1 + .../Translations/th.lproj/Localizable.strings | 1 + .../vi-VN.lproj/Localizable.strings | 1 + .../zh-Hant.lproj/Localizable.strings | 1 + .../zh_CN.lproj/Localizable.strings | 1 + Session/Notifications/AppNotifications.swift | 100 ++++++++---- .../PushRegistrationManager.swift | 6 +- Session/Utilities/Platform.swift | 14 -- .../ConfigurationMessage+Convenience.swift | 5 + .../ConfigurationMessage.swift | 34 +++- .../MessageReceiver+Handling.swift | 27 ++- .../NotificationServiceExtension.swift | 154 ++++++++++++------ .../Messaging/OWSMessageUtils.m | 3 +- 32 files changed, 255 insertions(+), 126 deletions(-) delete mode 100644 Session/Utilities/Platform.swift diff --git a/Session.xcodeproj/project.pbxproj b/Session.xcodeproj/project.pbxproj index 53893af46..dd0c4ad0d 100644 --- a/Session.xcodeproj/project.pbxproj +++ b/Session.xcodeproj/project.pbxproj @@ -65,7 +65,6 @@ 34F308A21ECB469700BB7697 /* OWSBezierPathView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34F308A11ECB469700BB7697 /* OWSBezierPathView.m */; }; 4503F1BE20470A5B00CEE724 /* classic-quiet.aifc in Resources */ = {isa = PBXBuildFile; fileRef = 4503F1BB20470A5B00CEE724 /* classic-quiet.aifc */; }; 4503F1BF20470A5B00CEE724 /* classic.aifc in Resources */ = {isa = PBXBuildFile; fileRef = 4503F1BC20470A5B00CEE724 /* classic.aifc */; }; - 450DF2051E0D74AC003D14BE /* Platform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 450DF2041E0D74AC003D14BE /* Platform.swift */; }; 450DF2091E0DD2C6003D14BE /* UserNotificationsAdaptee.swift in Sources */ = {isa = PBXBuildFile; fileRef = 450DF2081E0DD2C6003D14BE /* UserNotificationsAdaptee.swift */; }; 451166C01FD86B98000739BA /* AccountManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 451166BF1FD86B98000739BA /* AccountManager.swift */; }; 451A13B11E13DED2000A50FD /* AppNotifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 451A13B01E13DED2000A50FD /* AppNotifications.swift */; }; @@ -1052,7 +1051,6 @@ 4503F1BB20470A5B00CEE724 /* classic-quiet.aifc */ = {isa = PBXFileReference; lastKnownFileType = file; path = "classic-quiet.aifc"; sourceTree = ""; }; 4503F1BC20470A5B00CEE724 /* classic.aifc */ = {isa = PBXFileReference; lastKnownFileType = file; path = classic.aifc; sourceTree = ""; }; 4509E7991DD653700025A59F /* WebRTC.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebRTC.framework; path = ThirdParty/WebRTC/Build/WebRTC.framework; sourceTree = ""; }; - 450DF2041E0D74AC003D14BE /* Platform.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Platform.swift; sourceTree = ""; }; 450DF2081E0DD2C6003D14BE /* UserNotificationsAdaptee.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UserNotificationsAdaptee.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 451166BF1FD86B98000739BA /* AccountManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountManager.swift; sourceTree = ""; }; 451A13B01E13DED2000A50FD /* AppNotifications.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = AppNotifications.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; @@ -2063,7 +2061,6 @@ 45B5360D206DD8BB00D61655 /* UIResponder+OWS.swift */, 4C586924224FAB83003FD070 /* AVAudioSession+OWS.h */, 4C586925224FAB83003FD070 /* AVAudioSession+OWS.m */, - 450DF2041E0D74AC003D14BE /* Platform.swift */, 4521C3BF1F59F3BA00B4C582 /* TextFieldHelper.swift */, 34D1F0BF1F8EC1760066283D /* MessageRecipientStatusUtils.swift */, B8544E3223D50E4900299F14 /* SNAppearance.swift */, @@ -4916,7 +4913,6 @@ 34E3E5681EC4B19400495BAC /* AudioProgressView.swift in Sources */, B8D0A26925E4A2C200C1835E /* Onboarding.swift in Sources */, 34D1F0521F7E8EA30066283D /* GiphyDownloader.swift in Sources */, - 450DF2051E0D74AC003D14BE /* Platform.swift in Sources */, 4CC613362227A00400E21A3A /* ConversationSearch.swift in Sources */, B82149B825D60393009C0F2A /* BlockedModal.swift in Sources */, B82B408C239A068800A248E7 /* RegisterVC.swift in Sources */, diff --git a/Session/Home/HomeVC.swift b/Session/Home/HomeVC.swift index ca03fcfcd..99d285a71 100644 --- a/Session/Home/HomeVC.swift +++ b/Session/Home/HomeVC.swift @@ -181,7 +181,7 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, NewConv func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { switch section { case 0: - if messageRequestCount > 0 && !UserDefaults.standard[.hasHiddenMessageRequests] { + if messageRequestCount > 0 && !CurrentAppContext().appUserDefaults()[.hasHiddenMessageRequests] { return 1 } @@ -258,8 +258,8 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, NewConv .compactMap { $0 as? YapDatabaseViewRowChange } .filter { $0.finalGroup == TSMessageRequestGroup && $0.type == .insert } - if !messageRequestInserts.isEmpty && UserDefaults.standard[.hasHiddenMessageRequests] { - UserDefaults.standard[.hasHiddenMessageRequests] = false + if !messageRequestInserts.isEmpty && CurrentAppContext().appUserDefaults()[.hasHiddenMessageRequests] { + CurrentAppContext().appUserDefaults()[.hasHiddenMessageRequests] = false } } @@ -284,8 +284,8 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, NewConv tableView.beginUpdates() // If we need to unhide the message request row and then re-insert it - if !messageRequestInserts.isEmpty && UserDefaults.standard[.hasHiddenMessageRequests] { - UserDefaults.standard[.hasHiddenMessageRequests] = false + if !messageRequestInserts.isEmpty && CurrentAppContext().appUserDefaults()[.hasHiddenMessageRequests] { + CurrentAppContext().appUserDefaults()[.hasHiddenMessageRequests] = false tableView.insertRows(at: [IndexPath(row: 0, section: 0)], with: .automatic) } @@ -436,7 +436,7 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, NewConv switch indexPath.section { case 0: let hide = UITableViewRowAction(style: .destructive, title: NSLocalizedString("TXT_HIDE_TITLE", comment: "")) { [weak self] _, _ in - UserDefaults.standard[.hasHiddenMessageRequests] = true + CurrentAppContext().appUserDefaults()[.hasHiddenMessageRequests] = true // Animate the row removal self?.tableView.beginUpdates() diff --git a/Session/Meta/Translations/de.lproj/Localizable.strings b/Session/Meta/Translations/de.lproj/Localizable.strings index a437dd71b..fddd89f31 100644 --- a/Session/Meta/Translations/de.lproj/Localizable.strings +++ b/Session/Meta/Translations/de.lproj/Localizable.strings @@ -609,5 +609,6 @@ "MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?"; "MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request."; "MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted."; +"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request"; "TXT_HIDE_TITLE" = "Hide"; "TXT_DELETE_ACCEPT" = "Accept"; diff --git a/Session/Meta/Translations/en.lproj/Localizable.strings b/Session/Meta/Translations/en.lproj/Localizable.strings index 29e5c4feb..ec117fc3a 100644 --- a/Session/Meta/Translations/en.lproj/Localizable.strings +++ b/Session/Meta/Translations/en.lproj/Localizable.strings @@ -619,5 +619,6 @@ "MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?"; "MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request."; "MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted."; +"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request"; "TXT_HIDE_TITLE" = "Hide"; "TXT_DELETE_ACCEPT" = "Accept"; diff --git a/Session/Meta/Translations/es.lproj/Localizable.strings b/Session/Meta/Translations/es.lproj/Localizable.strings index 00d0b0536..bff670e6d 100644 --- a/Session/Meta/Translations/es.lproj/Localizable.strings +++ b/Session/Meta/Translations/es.lproj/Localizable.strings @@ -609,5 +609,6 @@ "MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?"; "MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request."; "MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted."; +"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request"; "TXT_HIDE_TITLE" = "Hide"; "TXT_DELETE_ACCEPT" = "Accept"; diff --git a/Session/Meta/Translations/fa.lproj/Localizable.strings b/Session/Meta/Translations/fa.lproj/Localizable.strings index 6677000b0..b3184f3a7 100644 --- a/Session/Meta/Translations/fa.lproj/Localizable.strings +++ b/Session/Meta/Translations/fa.lproj/Localizable.strings @@ -609,5 +609,6 @@ "MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?"; "MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request."; "MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted."; +"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request"; "TXT_HIDE_TITLE" = "Hide"; "TXT_DELETE_ACCEPT" = "Accept"; diff --git a/Session/Meta/Translations/fi.lproj/Localizable.strings b/Session/Meta/Translations/fi.lproj/Localizable.strings index d92daf974..31c293d3e 100644 --- a/Session/Meta/Translations/fi.lproj/Localizable.strings +++ b/Session/Meta/Translations/fi.lproj/Localizable.strings @@ -609,5 +609,6 @@ "MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?"; "MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request."; "MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted."; +"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request"; "TXT_HIDE_TITLE" = "Hide"; "TXT_DELETE_ACCEPT" = "Accept"; diff --git a/Session/Meta/Translations/fr.lproj/Localizable.strings b/Session/Meta/Translations/fr.lproj/Localizable.strings index e0b231a5f..9f908185c 100644 --- a/Session/Meta/Translations/fr.lproj/Localizable.strings +++ b/Session/Meta/Translations/fr.lproj/Localizable.strings @@ -609,5 +609,6 @@ "MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?"; "MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request."; "MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted."; +"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request"; "TXT_HIDE_TITLE" = "Hide"; "TXT_DELETE_ACCEPT" = "Accept"; diff --git a/Session/Meta/Translations/hi.lproj/Localizable.strings b/Session/Meta/Translations/hi.lproj/Localizable.strings index 9650b8aac..f102a724d 100644 --- a/Session/Meta/Translations/hi.lproj/Localizable.strings +++ b/Session/Meta/Translations/hi.lproj/Localizable.strings @@ -609,5 +609,6 @@ "MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?"; "MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request."; "MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted."; +"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request"; "TXT_HIDE_TITLE" = "Hide"; "TXT_DELETE_ACCEPT" = "Accept"; diff --git a/Session/Meta/Translations/hr.lproj/Localizable.strings b/Session/Meta/Translations/hr.lproj/Localizable.strings index 048bac922..84d1251b0 100644 --- a/Session/Meta/Translations/hr.lproj/Localizable.strings +++ b/Session/Meta/Translations/hr.lproj/Localizable.strings @@ -609,5 +609,6 @@ "MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?"; "MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request."; "MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted."; +"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request"; "TXT_HIDE_TITLE" = "Hide"; "TXT_DELETE_ACCEPT" = "Accept"; diff --git a/Session/Meta/Translations/id-ID.lproj/Localizable.strings b/Session/Meta/Translations/id-ID.lproj/Localizable.strings index 1f9c8ccc5..ad6a7199b 100644 --- a/Session/Meta/Translations/id-ID.lproj/Localizable.strings +++ b/Session/Meta/Translations/id-ID.lproj/Localizable.strings @@ -609,5 +609,6 @@ "MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?"; "MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request."; "MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted."; +"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request"; "TXT_HIDE_TITLE" = "Hide"; "TXT_DELETE_ACCEPT" = "Accept"; diff --git a/Session/Meta/Translations/it.lproj/Localizable.strings b/Session/Meta/Translations/it.lproj/Localizable.strings index 413d8f745..0696a7b73 100644 --- a/Session/Meta/Translations/it.lproj/Localizable.strings +++ b/Session/Meta/Translations/it.lproj/Localizable.strings @@ -609,5 +609,6 @@ "MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?"; "MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request."; "MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted."; +"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request"; "TXT_HIDE_TITLE" = "Hide"; "TXT_DELETE_ACCEPT" = "Accept"; diff --git a/Session/Meta/Translations/ja.lproj/Localizable.strings b/Session/Meta/Translations/ja.lproj/Localizable.strings index 0df7929e6..1cde2e9b7 100644 --- a/Session/Meta/Translations/ja.lproj/Localizable.strings +++ b/Session/Meta/Translations/ja.lproj/Localizable.strings @@ -609,5 +609,6 @@ "MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?"; "MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request."; "MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted."; +"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request"; "TXT_HIDE_TITLE" = "Hide"; "TXT_DELETE_ACCEPT" = "Accept"; diff --git a/Session/Meta/Translations/nl.lproj/Localizable.strings b/Session/Meta/Translations/nl.lproj/Localizable.strings index 8b4f949cd..9766d927e 100644 --- a/Session/Meta/Translations/nl.lproj/Localizable.strings +++ b/Session/Meta/Translations/nl.lproj/Localizable.strings @@ -609,5 +609,6 @@ "MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?"; "MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request."; "MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted."; +"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request"; "TXT_HIDE_TITLE" = "Hide"; "TXT_DELETE_ACCEPT" = "Accept"; diff --git a/Session/Meta/Translations/pl.lproj/Localizable.strings b/Session/Meta/Translations/pl.lproj/Localizable.strings index 8ad992cbf..9683ff5da 100644 --- a/Session/Meta/Translations/pl.lproj/Localizable.strings +++ b/Session/Meta/Translations/pl.lproj/Localizable.strings @@ -609,5 +609,6 @@ "MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?"; "MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request."; "MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted."; +"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request"; "TXT_HIDE_TITLE" = "Hide"; "TXT_DELETE_ACCEPT" = "Accept"; diff --git a/Session/Meta/Translations/pt_BR.lproj/Localizable.strings b/Session/Meta/Translations/pt_BR.lproj/Localizable.strings index bd69d0181..75edce1ff 100644 --- a/Session/Meta/Translations/pt_BR.lproj/Localizable.strings +++ b/Session/Meta/Translations/pt_BR.lproj/Localizable.strings @@ -609,5 +609,6 @@ "MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?"; "MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request."; "MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted."; +"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request"; "TXT_HIDE_TITLE" = "Hide"; "TXT_DELETE_ACCEPT" = "Accept"; diff --git a/Session/Meta/Translations/ru.lproj/Localizable.strings b/Session/Meta/Translations/ru.lproj/Localizable.strings index 86a931031..cb046af17 100644 --- a/Session/Meta/Translations/ru.lproj/Localizable.strings +++ b/Session/Meta/Translations/ru.lproj/Localizable.strings @@ -609,5 +609,6 @@ "MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?"; "MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request."; "MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted."; +"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request"; "TXT_HIDE_TITLE" = "Hide"; "TXT_DELETE_ACCEPT" = "Accept"; diff --git a/Session/Meta/Translations/si.lproj/Localizable.strings b/Session/Meta/Translations/si.lproj/Localizable.strings index a1a22115f..50b448a85 100644 --- a/Session/Meta/Translations/si.lproj/Localizable.strings +++ b/Session/Meta/Translations/si.lproj/Localizable.strings @@ -610,5 +610,6 @@ "MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?"; "MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request."; "MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted."; +"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request"; "TXT_HIDE_TITLE" = "Hide"; "TXT_DELETE_ACCEPT" = "Accept"; diff --git a/Session/Meta/Translations/sk.lproj/Localizable.strings b/Session/Meta/Translations/sk.lproj/Localizable.strings index 04f979b3f..00704d85e 100644 --- a/Session/Meta/Translations/sk.lproj/Localizable.strings +++ b/Session/Meta/Translations/sk.lproj/Localizable.strings @@ -609,5 +609,6 @@ "MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?"; "MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request."; "MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted."; +"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request"; "TXT_HIDE_TITLE" = "Hide"; "TXT_DELETE_ACCEPT" = "Accept"; diff --git a/Session/Meta/Translations/sv.lproj/Localizable.strings b/Session/Meta/Translations/sv.lproj/Localizable.strings index d451ce181..0bb42f93c 100644 --- a/Session/Meta/Translations/sv.lproj/Localizable.strings +++ b/Session/Meta/Translations/sv.lproj/Localizable.strings @@ -609,5 +609,6 @@ "MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?"; "MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request."; "MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted."; +"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request"; "TXT_HIDE_TITLE" = "Hide"; "TXT_DELETE_ACCEPT" = "Accept"; diff --git a/Session/Meta/Translations/th.lproj/Localizable.strings b/Session/Meta/Translations/th.lproj/Localizable.strings index 6e207f604..a0bc5d3ec 100644 --- a/Session/Meta/Translations/th.lproj/Localizable.strings +++ b/Session/Meta/Translations/th.lproj/Localizable.strings @@ -609,5 +609,6 @@ "MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?"; "MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request."; "MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted."; +"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request"; "TXT_HIDE_TITLE" = "Hide"; "TXT_DELETE_ACCEPT" = "Accept"; diff --git a/Session/Meta/Translations/vi-VN.lproj/Localizable.strings b/Session/Meta/Translations/vi-VN.lproj/Localizable.strings index a6bb4262a..97efb1c3f 100644 --- a/Session/Meta/Translations/vi-VN.lproj/Localizable.strings +++ b/Session/Meta/Translations/vi-VN.lproj/Localizable.strings @@ -609,5 +609,6 @@ "MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?"; "MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request."; "MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted."; +"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request"; "TXT_HIDE_TITLE" = "Hide"; "TXT_DELETE_ACCEPT" = "Accept"; diff --git a/Session/Meta/Translations/zh-Hant.lproj/Localizable.strings b/Session/Meta/Translations/zh-Hant.lproj/Localizable.strings index 6aaefe736..737c9d868 100644 --- a/Session/Meta/Translations/zh-Hant.lproj/Localizable.strings +++ b/Session/Meta/Translations/zh-Hant.lproj/Localizable.strings @@ -609,5 +609,6 @@ "MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?"; "MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request."; "MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted."; +"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request"; "TXT_HIDE_TITLE" = "Hide"; "TXT_DELETE_ACCEPT" = "Accept"; diff --git a/Session/Meta/Translations/zh_CN.lproj/Localizable.strings b/Session/Meta/Translations/zh_CN.lproj/Localizable.strings index facb104f7..4c53c1a86 100644 --- a/Session/Meta/Translations/zh_CN.lproj/Localizable.strings +++ b/Session/Meta/Translations/zh_CN.lproj/Localizable.strings @@ -609,5 +609,6 @@ "MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?"; "MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request."; "MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted."; +"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request"; "TXT_HIDE_TITLE" = "Hide"; "TXT_DELETE_ACCEPT" = "Accept"; diff --git a/Session/Notifications/AppNotifications.swift b/Session/Notifications/AppNotifications.swift index 3dc00f25e..f76313a11 100644 --- a/Session/Notifications/AppNotifications.swift +++ b/Session/Notifications/AppNotifications.swift @@ -4,6 +4,8 @@ import Foundation import PromiseKit +import SessionMessagingKit +import SignalUtilitiesKit /// There are two primary components in our system notification integration: /// @@ -88,7 +90,7 @@ let kNotificationDelayForBackgroumdPoll: TimeInterval = 5 let kAudioNotificationsThrottleCount = 2 let kAudioNotificationsThrottleInterval: TimeInterval = 5 -protocol NotificationPresenterAdaptee: class { +protocol NotificationPresenterAdaptee: AnyObject { func registerNotificationSettings() -> Promise @@ -157,11 +159,32 @@ public class NotificationPresenter: NSObject, NotificationsProtocol { } public func notifyUser(for incomingMessage: TSIncomingMessage, in thread: TSThread, transaction: YapDatabaseReadTransaction) { - guard !thread.isMuted else { return } - guard thread.isGroupThread() || !thread.isMessageRequest() else { return } guard let threadId = thread.uniqueId else { return } + // If the thread is a message request and the user hasn't hidden message requests then we need + // to check if this is the only message request thread (group threads can't be message requests + // so just ignore those and if the user has hidden message requests then we want to show the + // notification regardless of how many message requests there are) + if !thread.isGroupThread() && thread.isMessageRequest() && !CurrentAppContext().appUserDefaults()[.hasHiddenMessageRequests] { + let dbConnection: YapDatabaseConnection = OWSPrimaryStorage.shared().newDatabaseConnection() + dbConnection.objectCacheLimit = 2 + dbConnection.beginLongLivedReadTransaction() // Freeze the connection for use on the main thread (this gives us a stable data source that doesn't change until we tell it to) + let threads: YapDatabaseViewMappings = YapDatabaseViewMappings(groups: [ TSMessageRequestGroup ], view: TSThreadDatabaseViewExtensionName) + dbConnection.read { transaction in + threads.update(with: transaction) // Perform the initial update + } + + let numMessageRequests = threads.numberOfItems(inGroup: TSMessageRequestGroup) + dbConnection.endLongLivedReadTransaction() + + // Allow this to show a notification if there are no message requests (ie. this is the first one) + guard numMessageRequests == 0 else { return } + } + else if thread.isMessageRequest() && CurrentAppContext().appUserDefaults()[.hasHiddenMessageRequests] { + CurrentAppContext().appUserDefaults()[.hasHiddenMessageRequests] = false + } + let identifier: String = incomingMessage.notificationIdentifier ?? UUID().uuidString let isBackgroudPoll = identifier == threadId @@ -186,36 +209,44 @@ public class NotificationPresenter: NSObject, NotificationsProtocol { let senderName = Storage.shared.getContact(with: incomingMessage.authorId, using: transaction)?.displayName(for: context) ?? incomingMessage.authorId let notificationTitle: String? + var notificationBody: String? let previewType = preferences.notificationPreviewType(with: transaction) + switch previewType { - case .noNameNoPreview: - notificationTitle = "Session" - case .nameNoPreview, .namePreview: - switch thread { - case is TSContactThread: - notificationTitle = senderName - case is TSGroupThread: - var groupName = thread.name() - if groupName.count < 1 { - groupName = MessageStrings.newGroupDefaultTitle + case .noNameNoPreview: + notificationTitle = "Session" + + case .nameNoPreview, .namePreview: + switch thread { + case is TSContactThread: + notificationTitle = (thread.isMessageRequest() ? "Session" : senderName) + + case is TSGroupThread: + var groupName = thread.name() + if groupName.count < 1 { + groupName = MessageStrings.newGroupDefaultTitle + } + notificationTitle = isBackgroudPoll ? groupName : String(format: NotificationStrings.incomingGroupMessageTitleFormat, senderName, groupName) + + default: + owsFailDebug("unexpected thread: \(thread)") + return } - notificationTitle = isBackgroudPoll ? groupName : String(format: NotificationStrings.incomingGroupMessageTitleFormat, senderName, groupName) + default: - owsFailDebug("unexpected thread: \(thread)") - return - } - default: - notificationTitle = "Session" + notificationTitle = "Session" } - - var notificationBody: String? + switch previewType { - case .noNameNoPreview, .nameNoPreview: - notificationBody = NotificationStrings.incomingMessageBody - case .namePreview: - notificationBody = messageText - default: - notificationBody = NotificationStrings.incomingMessageBody + case .noNameNoPreview, .nameNoPreview: notificationBody = NotificationStrings.incomingMessageBody + case .namePreview: notificationBody = messageText + default: notificationBody = NotificationStrings.incomingMessageBody + } + + // If it's a message request then overwrite the body to be something generic (only show a notification + // when receiving a new message request if there aren't any others or the user had hidden them) + if thread.isMessageRequest() { + notificationBody = NSLocalizedString("MESSAGE_REQUESTS_NOTIFICATION", comment: "") } assert((notificationBody ?? notificationTitle) != nil) @@ -231,12 +262,15 @@ public class NotificationPresenter: NSObject, NotificationsProtocol { DispatchQueue.main.async { notificationBody = MentionUtilities.highlightMentions(in: notificationBody!, threadID: thread.uniqueId!) let sound = self.requestSound(thread: thread) - self.adaptee.notify(category: category, - title: notificationTitle, - body: notificationBody ?? "", - userInfo: userInfo, - sound: sound, - replacingIdentifier: identifier) + + self.adaptee.notify( + category: category, + title: notificationTitle, + body: notificationBody ?? "", + userInfo: userInfo, + sound: sound, + replacingIdentifier: identifier + ) } } diff --git a/Session/Notifications/PushRegistrationManager.swift b/Session/Notifications/PushRegistrationManager.swift index fc8b2f600..595e95c12 100644 --- a/Session/Notifications/PushRegistrationManager.swift +++ b/Session/Notifications/PushRegistrationManager.swift @@ -53,9 +53,9 @@ public enum PushRegistrationError: Error { return firstly { self.registerUserNotificationSettings() }.then { () -> Promise<(pushToken: String, voipToken: String)> in - guard !Platform.isSimulator else { - throw PushRegistrationError.pushNotSupported(description: "Push not supported on simulators") - } + #if targetEnvironment(simulator) + throw PushRegistrationError.pushNotSupported(description: "Push not supported on simulators") + #endif return self.registerForVanillaPushToken().map { vanillaPushToken -> (pushToken: String, voipToken: String) in return (pushToken: vanillaPushToken, voipToken: "") diff --git a/Session/Utilities/Platform.swift b/Session/Utilities/Platform.swift deleted file mode 100644 index ae24556e5..000000000 --- a/Session/Utilities/Platform.swift +++ /dev/null @@ -1,14 +0,0 @@ -// Created by Michael Kirk on 12/23/16. -// Copyright © 2016 Open Whisper Systems. All rights reserved. - -import Foundation - -struct Platform { - static let isSimulator: Bool = { - var isSim = false - #if arch(i386) || arch(x86_64) - isSim = true - #endif - return isSim - }() -} diff --git a/SessionMessagingKit/Messages/Control Messages/ConfigurationMessage+Convenience.swift b/SessionMessagingKit/Messages/Control Messages/ConfigurationMessage+Convenience.swift index 491054ac8..33e916eb4 100644 --- a/SessionMessagingKit/Messages/Control Messages/ConfigurationMessage+Convenience.swift +++ b/SessionMessagingKit/Messages/Control Messages/ConfigurationMessage+Convenience.swift @@ -65,6 +65,8 @@ extension ConfigurationMessage { return } + // Can just default the 'hasX' values to true as they will be set to this + // when converting to proto anyway let profilePictureURL = contact.profilePictureURL let profileKey = contact.profileEncryptionKey?.keyData let contact = ConfigurationMessage.Contact( @@ -72,8 +74,11 @@ extension ConfigurationMessage { displayName: (contact.name ?? publicKey), profilePictureURL: profilePictureURL, profileKey: profileKey, + hasIsApproved: true, isApproved: contact.isApproved, + hasIsBlocked: true, isBlocked: contact.isBlocked, + hasDidApproveMe: true, didApproveMe: contact.didApproveMe ) diff --git a/SessionMessagingKit/Messages/Control Messages/ConfigurationMessage.swift b/SessionMessagingKit/Messages/Control Messages/ConfigurationMessage.swift index 678691f6b..00eea0aa0 100644 --- a/SessionMessagingKit/Messages/Control Messages/ConfigurationMessage.swift +++ b/SessionMessagingKit/Messages/Control Messages/ConfigurationMessage.swift @@ -194,19 +194,36 @@ extension ConfigurationMessage { public var profilePictureURL: String? public var profileKey: Data? + public var hasIsApproved: Bool public var isApproved: Bool + public var hasIsBlocked: Bool public var isBlocked: Bool + public var hasDidApproveMe: Bool public var didApproveMe: Bool public var isValid: Bool { publicKey != nil && displayName != nil } - public init(publicKey: String, displayName: String, profilePictureURL: String?, profileKey: Data?, isApproved: Bool, isBlocked: Bool, didApproveMe: Bool) { + public init( + publicKey: String, + displayName: String, + profilePictureURL: String?, + profileKey: Data?, + hasIsApproved: Bool, + isApproved: Bool, + hasIsBlocked: Bool, + isBlocked: Bool, + hasDidApproveMe: Bool, + didApproveMe: Bool + ) { self.publicKey = publicKey self.displayName = displayName self.profilePictureURL = profilePictureURL self.profileKey = profileKey + self.hasIsApproved = hasIsApproved self.isApproved = isApproved + self.hasIsBlocked = hasIsBlocked self.isBlocked = isBlocked + self.hasDidApproveMe = hasDidApproveMe self.didApproveMe = didApproveMe } @@ -217,8 +234,11 @@ extension ConfigurationMessage { self.displayName = displayName self.profilePictureURL = coder.decodeObject(forKey: "profilePictureURL") as! String? self.profileKey = coder.decodeObject(forKey: "profileKey") as! Data? + self.hasIsApproved = (coder.decodeObject(forKey: "hasIsApproved") as? Bool ?? false) self.isApproved = (coder.decodeObject(forKey: "isApproved") as? Bool ?? false) + self.hasIsBlocked = (coder.decodeObject(forKey: "hasIsBlocked") as? Bool ?? false) self.isBlocked = (coder.decodeObject(forKey: "isBlocked") as? Bool ?? false) + self.hasDidApproveMe = (coder.decodeObject(forKey: "hasDidApproveMe") as? Bool ?? false) self.didApproveMe = (coder.decodeObject(forKey: "didApproveMe") as? Bool ?? false) } @@ -227,8 +247,11 @@ extension ConfigurationMessage { coder.encode(displayName, forKey: "displayName") coder.encode(profilePictureURL, forKey: "profilePictureURL") coder.encode(profileKey, forKey: "profileKey") + coder.encode(hasIsApproved, forKey: "hasIsApproved") coder.encode(isApproved, forKey: "isApproved") + coder.encode(hasIsBlocked, forKey: "hasIsBlocked") coder.encode(isBlocked, forKey: "isBlocked") + coder.encode(hasDidApproveMe, forKey: "hasDidApproveMe") coder.encode(didApproveMe, forKey: "didApproveMe") } @@ -238,8 +261,11 @@ extension ConfigurationMessage { displayName: proto.name, profilePictureURL: proto.profilePicture, profileKey: proto.profileKey, + hasIsApproved: proto.hasIsApproved, isApproved: proto.isApproved, + hasIsBlocked: proto.hasIsBlocked, isBlocked: proto.isBlocked, + hasDidApproveMe: proto.hasDidApproveMe, didApproveMe: proto.didApproveMe ) @@ -254,9 +280,9 @@ extension ConfigurationMessage { if let profilePictureURL = profilePictureURL { result.setProfilePicture(profilePictureURL) } if let profileKey = profileKey { result.setProfileKey(profileKey) } - result.setIsApproved(isApproved) - result.setIsBlocked(isBlocked) - result.setDidApproveMe(didApproveMe) + if hasIsApproved { result.setIsApproved(isApproved) } + if hasIsBlocked { result.setIsBlocked(isBlocked) } + if hasDidApproveMe { result.setDidApproveMe(didApproveMe) } do { return try result.build() diff --git a/SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift b/SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift index 6ceadfe27..9c4b25e6a 100644 --- a/SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift +++ b/SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift @@ -214,9 +214,14 @@ extension MessageReceiver { if let profileKey = contactInfo.profileKey { contact.profileEncryptionKey = OWSAES256Key(data: profileKey) } contact.profilePictureURL = contactInfo.profilePictureURL contact.name = contactInfo.displayName - contact.isApproved = contactInfo.isApproved - contact.isBlocked = contactInfo.isBlocked - contact.didApproveMe = contactInfo.didApproveMe + + // Note: We only update these values if the proto actually has values for them (this is to + // prevent an edge case where an old client could override the values with default values + // since they aren't included) + if contactInfo.hasIsApproved { contact.isApproved = contactInfo.isApproved } + if contactInfo.hasIsBlocked { contact.isBlocked = contactInfo.isBlocked } + if contactInfo.hasDidApproveMe { contact.didApproveMe = contactInfo.didApproveMe } + Storage.shared.setContact(contact, using: transaction) let thread = TSContactThread.getOrCreateThread(withContactSessionID: sessionID, transaction: transaction) thread.shouldBeVisible = true @@ -800,12 +805,18 @@ extension MessageReceiver { // Force a config sync to ensure all devices know the contact approval state if desired (Note: This logic // should match the behaviour in AppDelegate.forceSyncConfigurationNowIfNeeded()) guard forceConfigSync else { return } - guard Storage.shared.getUser()?.name != nil, let configurationMessage = ConfigurationMessage.getCurrent() else { - return - } - let destination: Message.Destination = Message.Destination.contact(publicKey: userPublicKey) - MessageSender.send(configurationMessage, to: destination, using: transaction).retainUntilComplete() + // Note: We MUST run this async as we need to ensure the database `transaction` has finished before we generate + // a new configuration message (otherwise the `contact` will be loaded direct from the database and the + // `didApproveMe` value won't have been updated) + DispatchQueue.global(qos: .background).async { + guard Storage.shared.getUser()?.name != nil, let configurationMessage = ConfigurationMessage.getCurrent() else { + return + } + + let destination: Message.Destination = Message.Destination.contact(publicKey: userPublicKey) + MessageSender.send(configurationMessage, to: destination, using: transaction).retainUntilComplete() + } } public static func handleMessageRequestResponse(_ message: MessageRequestResponse, using transaction: Any) { diff --git a/SessionNotificationServiceExtension/NotificationServiceExtension.swift b/SessionNotificationServiceExtension/NotificationServiceExtension.swift index 212b10c30..215accbe6 100644 --- a/SessionNotificationServiceExtension/NotificationServiceExtension.swift +++ b/SessionNotificationServiceExtension/NotificationServiceExtension.swift @@ -39,57 +39,91 @@ public final class NotificationServiceExtension : UNNotificationServiceExtension let senderPublicKey = message.sender! var senderDisplayName = Storage.shared.getContact(with: senderPublicKey)?.displayName(for: .regular) ?? senderPublicKey let snippet: String - var userInfo: [String:Any] = [ NotificationServiceExtension.isFromRemoteKey : true ] + var userInfo: [String: Any] = [ NotificationServiceExtension.isFromRemoteKey: true ] + var isMessageRequest: Bool = false + switch message { - case let visibleMessage as VisibleMessage: - let tsIncomingMessageID = try MessageReceiver.handleVisibleMessage(visibleMessage, associatedWithProto: proto, openGroupID: nil, isBackgroundPoll: false, using: transaction) - guard let tsMessage = TSMessage.fetch(uniqueId: tsIncomingMessageID, transaction: transaction) else { - return self.completeSilenty() - } - let thread = tsMessage.thread(with: transaction) - let threadID = thread.uniqueId! - userInfo[NotificationServiceExtension.threadIdKey] = threadID - snippet = tsMessage.previewText(with: transaction).filterForDisplay?.replacingMentions(for: threadID, using: transaction) - ?? "You've got a new message" - if let tsIncomingMessage = tsMessage as? TSIncomingMessage { - if thread.isMuted || !thread.isMessageRequest() { - // Ignore PNs if the thread is muted or the thread is a message request + case let visibleMessage as VisibleMessage: + let tsIncomingMessageID = try MessageReceiver.handleVisibleMessage(visibleMessage, associatedWithProto: proto, openGroupID: nil, isBackgroundPoll: false, using: transaction) + + guard let tsMessage = TSMessage.fetch(uniqueId: tsIncomingMessageID, transaction: transaction) else { return self.completeSilenty() } - if let thread = TSThread.fetch(uniqueId: threadID, transaction: transaction), let group = thread as? TSGroupThread, - group.groupModel.groupType == .closedGroup { // Should always be true because we don't get PNs for open groups - senderDisplayName = String(format: NotificationStrings.incomingGroupMessageTitleFormat, senderDisplayName, group.groupModel.groupName ?? MessageStrings.newGroupDefaultTitle) - if group.isOnlyNotifyingForMentions && !tsIncomingMessage.isUserMentioned { - // Ignore PNs if the group is set to only notify for mentions - return self.completeSilenty() + + let thread = tsMessage.thread(with: transaction) + let threadID = thread.uniqueId! + userInfo[NotificationServiceExtension.threadIdKey] = threadID + snippet = tsMessage.previewText(with: transaction).filterForDisplay?.replacingMentions(for: threadID, using: transaction) + ?? "You've got a new message" + + if let tsIncomingMessage = tsMessage as? TSIncomingMessage { + // Ignore PNs if the thread is muted + if thread.isMuted { return self.completeSilenty() } + if let thread = TSThread.fetch(uniqueId: threadID, transaction: transaction), let group = thread as? TSGroupThread, + group.groupModel.groupType == .closedGroup { // Should always be true because we don't get PNs for open groups + senderDisplayName = String(format: NotificationStrings.incomingGroupMessageTitleFormat, senderDisplayName, group.groupModel.groupName ?? MessageStrings.newGroupDefaultTitle) + if group.isOnlyNotifyingForMentions && !tsIncomingMessage.isUserMentioned { + // Ignore PNs if the group is set to only notify for mentions + return self.completeSilenty() + } + } + + // If the thread is a message request and the user hasn't hidden message requests then we need + // to check if this is the only message request thread (group threads can't be message requests + // so just ignore those and if the user has hidden message requests then we want to show the + // notification regardless of how many message requests there are) + if !thread.isGroupThread() && thread.isMessageRequest() && !CurrentAppContext().appUserDefaults()[.hasHiddenMessageRequests] { + let dbConnection: YapDatabaseConnection = OWSPrimaryStorage.shared().newDatabaseConnection() + dbConnection.objectCacheLimit = 2 + dbConnection.beginLongLivedReadTransaction() // Freeze the connection for use on the main thread (this gives us a stable data source that doesn't change until we tell it to) + let threads: YapDatabaseViewMappings = YapDatabaseViewMappings(groups: [ TSMessageRequestGroup ], view: TSThreadDatabaseViewExtensionName) + dbConnection.read { transaction in + threads.update(with: transaction) // Perform the initial update + } + + let numMessageRequests = threads.numberOfItems(inGroup: TSMessageRequestGroup) + dbConnection.endLongLivedReadTransaction() + + // Allow this to show a notification if there are no message requests (ie. this is the first one) + guard numMessageRequests == 0 else { return self.completeSilenty() } + } + else if thread.isMessageRequest() && CurrentAppContext().appUserDefaults()[.hasHiddenMessageRequests] { + CurrentAppContext().appUserDefaults()[.hasHiddenMessageRequests] = false + } + + isMessageRequest = thread.isMessageRequest() + + // Store the notification ID for unsend requests to later cancel this notification + tsIncomingMessage.setNotificationIdentifier(request.identifier, transaction: transaction) + } + else { + let semaphore = DispatchSemaphore(value: 0) + let center = UNUserNotificationCenter.current() + center.getDeliveredNotifications { notifications in + let matchingNotifications = notifications.filter({ $0.request.content.userInfo[NotificationServiceExtension.threadIdKey] as? String == threadID}) + center.removeDeliveredNotifications(withIdentifiers: matchingNotifications.map({ $0.request.identifier })) + // Hack: removeDeliveredNotifications seems to be async,need to wait for some time before the delivered notifications can be removed. + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { semaphore.signal() } } + semaphore.wait() } - // Store the notification ID for unsend requests to later cancel this notification - tsIncomingMessage.setNotificationIdentifier(request.identifier, transaction: transaction) - } else { - let semaphore = DispatchSemaphore(value: 0) - let center = UNUserNotificationCenter.current() - center.getDeliveredNotifications { notifications in - let matchingNotifications = notifications.filter({ $0.request.content.userInfo[NotificationServiceExtension.threadIdKey] as? String == threadID}) - center.removeDeliveredNotifications(withIdentifiers: matchingNotifications.map({ $0.request.identifier })) - // Hack: removeDeliveredNotifications seems to be async,need to wait for some time before the delivered notifications can be removed. - DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { semaphore.signal() } + notificationContent.sound = OWSSounds.notificationSound(for: thread).notificationSound(isQuiet: false) + + case let unsendRequest as UnsendRequest: + MessageReceiver.handleUnsendRequest(unsendRequest, using: transaction) + return self.completeSilenty() + + case let closedGroupControlMessage as ClosedGroupControlMessage: + // TODO: We could consider actually handling the update here. Not sure if there's enough time though, seeing as though + // in some cases we need to send messages (e.g. our sender key) to a number of other users. + switch closedGroupControlMessage.kind { + case .new(_, let name, _, _, _, _): snippet = "\(senderDisplayName) added you to \(name)" + default: return self.completeSilenty() } - semaphore.wait() - } - notificationContent.sound = OWSSounds.notificationSound(for: thread).notificationSound(isQuiet: false) - case let unsendRequest as UnsendRequest: - MessageReceiver.handleUnsendRequest(unsendRequest, using: transaction) - return self.completeSilenty() - case let closedGroupControlMessage as ClosedGroupControlMessage: - // TODO: We could consider actually handling the update here. Not sure if there's enough time though, seeing as though - // in some cases we need to send messages (e.g. our sender key) to a number of other users. - switch closedGroupControlMessage.kind { - case .new(_, let name, _, _, _, _): snippet = "\(senderDisplayName) added you to \(name)" + default: return self.completeSilenty() - } - default: return self.completeSilenty() } + if (senderPublicKey == userPublicKey) { // Ignore PNs for messages sent by the current user // after handling the message. Otherwise the closed @@ -98,21 +132,35 @@ public final class NotificationServiceExtension : UNNotificationServiceExtension } notificationContent.userInfo = userInfo notificationContent.badge = 1 + let notificationsPreference = Environment.shared.preferences!.notificationPreviewType() + switch notificationsPreference { - case .namePreview: - notificationContent.title = senderDisplayName - notificationContent.body = snippet - case .nameNoPreview: - notificationContent.title = senderDisplayName - notificationContent.body = NotificationStrings.incomingMessageBody - case .noNameNoPreview: + case .namePreview: + notificationContent.title = senderDisplayName + notificationContent.body = snippet + + case .nameNoPreview: + notificationContent.title = senderDisplayName + notificationContent.body = NotificationStrings.incomingMessageBody + + case .noNameNoPreview: + notificationContent.title = "Session" + notificationContent.body = NotificationStrings.incomingMessageBody + + default: break + } + + // If it's a message request then overwrite the body to be something generic (only show a notification + // when receiving a new message request if there aren't any others or the user had hidden them) + if isMessageRequest { notificationContent.title = "Session" - notificationContent.body = NotificationStrings.incomingMessageBody - default: break + notificationContent.body = NSLocalizedString("MESSAGE_REQUESTS_NOTIFICATION", comment: "") } + self.handleSuccess(for: notificationContent) - } catch { + } + catch { if let error = error as? MessageReceiver.Error, error.isRetryable { self.handleFailure(for: notificationContent) } diff --git a/SignalUtilitiesKit/Messaging/OWSMessageUtils.m b/SignalUtilitiesKit/Messaging/OWSMessageUtils.m index bde729467..fe8d0a4d1 100644 --- a/SignalUtilitiesKit/Messaging/OWSMessageUtils.m +++ b/SignalUtilitiesKit/Messaging/OWSMessageUtils.m @@ -76,7 +76,8 @@ NS_ASSUME_NONNULL_BEGIN for (NSString *groupID in allGroups) { TSThread *thread = [TSThread fetchObjectWithUniqueID:groupID transaction:transaction]; - if (thread.isMuted || !thread.isMessageRequest) { continue; } + // Don't increase the count for muted threads or message requests + if (thread.isMuted || thread.isMessageRequest) { continue; } BOOL isGroupThread = thread.isGroupThread;