diff --git a/Swiftgram/SGDebugUI/Sources/SGDebugUI.swift b/Swiftgram/SGDebugUI/Sources/SGDebugUI.swift index c155a79421..bbf9a83fc5 100644 --- a/Swiftgram/SGDebugUI/Sources/SGDebugUI.swift +++ b/Swiftgram/SGDebugUI/Sources/SGDebugUI.swift @@ -827,6 +827,7 @@ public func sgMessageFilterController(presentationData: PresentationData? = nil) private enum SGDebugControllerSection: Int32, SGItemListSection { case base + case notifications } private enum SGDebugDisclosureLink: String { @@ -847,7 +848,12 @@ private enum SGDebugToggles: String { } -private typealias SGDebugControllerEntry = SGItemListUIEntry +private enum SGDebugOneFromManySetting: String { + case pinnedMessageNotifications + case mentionsAndRepliesNotifications +} + +private typealias SGDebugControllerEntry = SGItemListUIEntry private func SGDebugControllerEntries(presentationData: PresentationData) -> [SGDebugControllerEntry] { var entries: [SGDebugControllerEntry] = [] @@ -867,8 +873,12 @@ private func SGDebugControllerEntries(presentationData: PresentationData) -> [SG } entries.append(.action(id: id.count, section: .base, actionType: .clearRegDateCache, text: "Clear Regdate cache", kind: .generic)) entries.append(.toggle(id: id.count, section: .base, settingName: .forceImmediateShareSheet, value: SGSimpleSettings.shared.forceSystemSharing, text: "Force System Share Sheet", enabled: true)) - entries.append(.toggle(id: id.count, section: .base, settingName: .legacyNotificationsFix, value: SGSimpleSettings.shared.legacyNotificationsFix, text: "[Legacy] Fix empty notifications", enabled: true)) - + + entries.append(.header(id: id.count, section: .notifications, text: "NOTIFICATIONS", badge: nil)) + entries.append(.toggle(id: id.count, section: .notifications, settingName: .legacyNotificationsFix, value: SGSimpleSettings.shared.legacyNotificationsFix, text: "[Legacy] Fix empty notifications", enabled: true)) + entries.append(.oneFromManySelector(id: id.count, section: .notifications, settingName: .pinnedMessageNotifications, text: "Pinned Messages", value: SGSimpleSettings.shared.pinnedMessageNotifications, enabled: true)) + entries.append(.oneFromManySelector(id: id.count, section: .notifications, settingName: .mentionsAndRepliesNotifications, text: "@Mentions and Replies", value: SGSimpleSettings.shared.mentionsAndRepliesNotifications, enabled: true)) + return entries } private func okUndoController(_ text: String, _ presentationData: PresentationData) -> UndoOverlayController { @@ -882,7 +892,7 @@ public func sgDebugController(context: AccountContext) -> ViewController { let simplePromise = ValuePromise(true, ignoreRepeated: false) - let arguments = SGItemListArguments(context: context, setBoolValue: { toggleName, value in + let arguments = SGItemListArguments(context: context, setBoolValue: { toggleName, value in switch toggleName { case .forceImmediateShareSheet: SGSimpleSettings.shared.forceSystemSharing = value @@ -891,6 +901,52 @@ public func sgDebugController(context: AccountContext) -> ViewController { case .inputToolbar: SGSimpleSettings.shared.inputToolbar = value } + }, setOneFromManyValue: { setting in + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let actionSheet = ActionSheetController(presentationData: presentationData) + var items: [ActionSheetItem] = [] + + switch (setting) { + case .pinnedMessageNotifications: + let setAction: (String) -> Void = { value in + SGSimpleSettings.shared.pinnedMessageNotifications = value + simplePromise.set(true) + } + + for value in SGSimpleSettings.PinnedMessageNotificationsSettings.allCases { + items.append(ActionSheetButtonItem(title: value.rawValue, color: .accent, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + if SGSimpleSettings.shared.b { + setAction(value.rawValue) + } else { + setAction(SGSimpleSettings.PinnedMessageNotificationsSettings.default.rawValue) + } + })) + } + case .mentionsAndRepliesNotifications: + let setAction: (String) -> Void = { value in + SGSimpleSettings.shared.mentionsAndRepliesNotifications = value + simplePromise.set(true) + } + + for value in SGSimpleSettings.MentionsAndRepliesNotificationsSettings.allCases { + items.append(ActionSheetButtonItem(title: value.rawValue, color: .accent, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + if SGSimpleSettings.shared.b { + setAction(value.rawValue) + } else { + setAction(SGSimpleSettings.MentionsAndRepliesNotificationsSettings.default.rawValue) + } + })) + } + } + + actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [ + ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + }) + ])]) + presentControllerImpl?(actionSheet, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) }, openDisclosureLink: { link in let presentationData = context.sharedContext.currentPresentationData.with { $0 } switch (link) { diff --git a/Swiftgram/SGSimpleSettings/Sources/SimpleSettings.swift b/Swiftgram/SGSimpleSettings/Sources/SimpleSettings.swift index dc8b8aaf8d..582e31c8f6 100644 --- a/Swiftgram/SGSimpleSettings/Sources/SimpleSettings.swift +++ b/Swiftgram/SGSimpleSettings/Sources/SimpleSettings.swift @@ -1,6 +1,8 @@ import Foundation +let appGroupIdentifier = "group.app.swiftgram.ios" + public class SGSimpleSettings { public static let shared = SGSimpleSettings() @@ -12,6 +14,11 @@ public class SGSimpleSettings { private func setDefaultValues() { UserDefaults.standard.register(defaults: SGSimpleSettings.defaultValues) + // Just in case group defaults will be nil + UserDefaults.standard.register(defaults: SGSimpleSettings.groupDefaultValues) + if let groupUserDefaults = UserDefaults(suiteName: appGroupIdentifier) { + groupUserDefaults.register(defaults: SGSimpleSettings.groupDefaultValues) + } } private func preCacheValues() { @@ -111,6 +118,8 @@ public class SGSimpleSettings { case legacyNotificationsFix case messageFilterKeywords case inputToolbar + case pinnedMessageNotifications + case mentionsAndRepliesNotifications } public enum DownloadSpeedBoostValues: String, CaseIterable { @@ -148,6 +157,18 @@ public class SGSimpleSettings { case none } + public enum PinnedMessageNotificationsSettings: String, CaseIterable { + case `default` + case silenced + case disabled + } + + public enum MentionsAndRepliesNotificationsSettings: String, CaseIterable { + case `default` + case silenced + case disabled + } + public static let defaultValues: [String: Any] = [ Keys.hidePhoneInSettings.rawValue: true, Keys.showTabNames.rawValue: true, @@ -208,11 +229,16 @@ public class SGSimpleSettings { Keys.forceSystemSharing.rawValue: false, Keys.confirmCalls.rawValue: true, Keys.videoPIPSwipeDirection.rawValue: VideoPIPSwipeDirection.up.rawValue, - Keys.legacyNotificationsFix.rawValue: false, Keys.messageFilterKeywords.rawValue: [], Keys.inputToolbar.rawValue: false ] + public static let groupDefaultValues: [String: Any] = [ + Keys.legacyNotificationsFix.rawValue: false, + Keys.pinnedMessageNotifications.rawValue: PinnedMessageNotificationsSettings.default.rawValue, + Keys.mentionsAndRepliesNotifications.rawValue: MentionsAndRepliesNotificationsSettings.default.rawValue + ] + @UserDefault(key: Keys.hidePhoneInSettings.rawValue) public var hidePhoneInSettings: Bool @@ -389,7 +415,7 @@ public class SGSimpleSettings { @UserDefault(key: Keys.videoPIPSwipeDirection.rawValue) public var videoPIPSwipeDirection: String - @UserDefault(key: Keys.legacyNotificationsFix.rawValue) + @UserDefault(key: Keys.legacyNotificationsFix.rawValue, userDefaults: UserDefaults(suiteName: appGroupIdentifier) ?? .standard) public var legacyNotificationsFix: Bool public var b: Bool = true @@ -399,6 +425,12 @@ public class SGSimpleSettings { @UserDefault(key: Keys.inputToolbar.rawValue) public var inputToolbar: Bool + + @UserDefault(key: Keys.pinnedMessageNotifications.rawValue, userDefaults: UserDefaults(suiteName: appGroupIdentifier) ?? .standard) + public var pinnedMessageNotifications: String + + @UserDefault(key: Keys.mentionsAndRepliesNotifications.rawValue, userDefaults: UserDefaults(suiteName: appGroupIdentifier) ?? .standard) + public var mentionsAndRepliesNotifications: String } extension SGSimpleSettings { diff --git a/Telegram/NotificationService/Sources/NotificationService.swift b/Telegram/NotificationService/Sources/NotificationService.swift index 5affe995fe..d99604de87 100644 --- a/Telegram/NotificationService/Sources/NotificationService.swift +++ b/Telegram/NotificationService/Sources/NotificationService.swift @@ -18,7 +18,10 @@ import NotificationsPresentationData import RangeSet import ConvertOpusToAAC -private let LEGACY_NOTIFICATIONS_FIX: Bool = UserDefaults.standard.bool(forKey: "legacyNotificationsFix") +private let groupUserDefaults: UserDefaults? = UserDefaults(suiteName: "group.app.swiftgram.ios") +private let LEGACY_NOTIFICATIONS_FIX: Bool = groupUserDefaults?.bool(forKey: "legacyNotificationsFix") ?? false +private let PINNED_MESSAGE_ACTION: String = groupUserDefaults?.string(forKey: "pinnedMessageNotifications") ?? "default" +private let MENTION_AND_REPLY_ACTION: String = groupUserDefaults?.string(forKey: "mentionsAndRepliesNotifications") ?? "default" private let queue = Queue() @@ -498,16 +501,20 @@ private struct NotificationContent: CustomStringConvertible { var userInfo: [AnyHashable: Any] = [:] var attachments: [UNNotificationAttachment] = [] var silent = false + // MARK: Swiftgram var isEmpty: Bool + var isMentionOrReply: Bool + var isPinned: Bool = false var senderPerson: INPerson? var senderImage: INImage? var isLockedMessage: String? - init(isLockedMessage: String?, isEmpty: Bool = false) { + init(isLockedMessage: String?, isEmpty: Bool = false, isMentionOrReply: Bool = false) { self.isLockedMessage = isLockedMessage self.isEmpty = isEmpty + self.isMentionOrReply = isMentionOrReply } var description: String { @@ -524,6 +531,10 @@ private struct NotificationContent: CustomStringConvertible { string += " isLockedMessage: \(String(describing: self.isLockedMessage)),\n" string += " attachments: \(self.attachments),\n" string += " isEmpty: \(self.isEmpty),\n" + string += " isMentionOrReply: \(self.isMentionOrReply),\n" + string += " isPinned: \(self.isPinned),\n" + string += " forceIsEmpty: \(self.forceIsEmpty),\n" + string += " forceIsSilent: \(self.forceIsSilent),\n" string += "}" return string } @@ -538,7 +549,7 @@ private struct NotificationContent: CustomStringConvertible { if let topicTitle { displayName = "\(topicTitle) (\(displayName))" } - if self.silent { + if self.silent || self.forceIsSilent { displayName = "\(displayName) 🔕" } @@ -562,9 +573,15 @@ private struct NotificationContent: CustomStringConvertible { var content = UNMutableNotificationContent() //Logger.shared.log("NotificationService", "Generating final content: \(self.description)") - + // MARK: Swiftgram + #if DEBUG + print("body:\(content.body) silent:\(self.silent) isMentionOrReply:\(self.isMentionOrReply) MENTION_AND_REPLY_ACTION:\(MENTION_AND_REPLY_ACTION) isPinned:\(self.isPinned) PINNED_MESSAGE_ACTION:\(PINNED_MESSAGE_ACTION)" + " forceIsEmpty:\(self.forceIsEmpty) forceIsSilent:\(self.forceIsSilent)") + #endif + if self.forceIsEmpty && !LEGACY_NOTIFICATIONS_FIX { + return UNNotificationContent() + } if let title = self.title { - if self.silent { + if self.silent || self.forceIsSilent { content.title = "\(title) 🔕" } else { content.title = title @@ -645,7 +662,7 @@ private struct NotificationContent: CustomStringConvertible { } // MARK: Swiftgram - if self.isEmpty && LEGACY_NOTIFICATIONS_FIX { + if (self.isEmpty || self.forceIsEmpty) && LEGACY_NOTIFICATIONS_FIX { content.title = " " content.threadIdentifier = "empty-notification" if #available(iOSApplicationExtension 15.0, iOS 15.0, *) { @@ -653,7 +670,10 @@ private struct NotificationContent: CustomStringConvertible { content.relevanceScore = 0.0 } } - + + if self.forceIsSilent { + content.sound = nil + } return content } } @@ -919,6 +939,7 @@ private final class NotificationServiceHandler { return } + let isMentionOrReply: Bool = payloadJson["mention"] as? String == "1" Logger.shared.log("NotificationService \(episode)", "Decrypted payload: \(payloadJson)") @@ -1016,7 +1037,7 @@ private final class NotificationServiceHandler { action = .logout case "MESSAGE_MUTED": if let peerId = peerId { - action = .poll(peerId: peerId, content: NotificationContent(isLockedMessage: nil, isEmpty: true), messageId: nil, reportDelivery: false) + action = .poll(peerId: peerId, content: NotificationContent(isLockedMessage: nil, isEmpty: true, isMentionOrReply: isMentionOrReply), messageId: nil, reportDelivery: false) } case "MESSAGE_DELETED": if let peerId = peerId { @@ -1067,7 +1088,7 @@ private final class NotificationServiceHandler { } } else { if let aps = payloadJson["aps"] as? [String: Any], var peerId = peerId { - var content: NotificationContent = NotificationContent(isLockedMessage: isLockedMessage) + var content: NotificationContent = NotificationContent(isLockedMessage: isLockedMessage, isMentionOrReply: isMentionOrReply) if let alert = aps["alert"] as? [String: Any] { if let topicTitleValue = payloadJson["topic_title"] as? String { topicTitle = topicTitleValue @@ -1277,6 +1298,11 @@ private final class NotificationServiceHandler { let pollCompletion: (NotificationContent, Media?) -> Void = { content, customMedia in var content = content + // MARK: Swiftgram + if let mediaAction = customMedia as? TelegramMediaAction, case .pinnedMessageUpdated = mediaAction.action { + content.isPinned = true + } + queue.async { guard let strongSelf = self, let stateManager = strongSelf.stateManager else { let content = NotificationContent(isLockedMessage: isLockedMessage) @@ -1585,7 +1611,7 @@ private final class NotificationServiceHandler { Logger.shared.log("NotificationService \(episode)", "Updating content to \(content)") if wasDisplayed { - content = NotificationContent(isLockedMessage: nil) + content = NotificationContent(isLockedMessage: nil, isMentionOrReply: isMentionOrReply) Logger.shared.log("NotificationService \(episode)", "Was already displayed, skipping content") } else if let messageId { let _ = (stateManager.postbox.transaction { transaction -> Void in @@ -1672,7 +1698,7 @@ private final class NotificationServiceHandler { case let .idBased(maxIncomingReadId, _, _, _, _): if maxIncomingReadId >= messageId.id { Logger.shared.log("NotificationService \(episode)", "maxIncomingReadId: \(maxIncomingReadId), messageId: \(messageId.id), skipping") - content = NotificationContent(isLockedMessage: nil) + content = NotificationContent(isLockedMessage: nil, isMentionOrReply: isMentionOrReply) } else { Logger.shared.log("NotificationService \(episode)", "maxIncomingReadId: \(maxIncomingReadId), messageId: \(messageId.id), not skipping") } @@ -2337,3 +2363,23 @@ final class NotificationService: UNNotificationServiceExtension { } } } + + +extension NotificationContent { + var forceIsEmpty: Bool { + if !self.isEmpty { + if PINNED_MESSAGE_ACTION == "disabled" && self.isPinned || MENTION_AND_REPLY_ACTION == "disabled" && self.isMentionOrReply { + return true + } + } + return false + } + var forceIsSilent: Bool { + if !self.silent { + if PINNED_MESSAGE_ACTION == "silenced" && self.isPinned || MENTION_AND_REPLY_ACTION == "silenced" && self.isMentionOrReply { + return true + } + } + return false + } +}