tutanota/app-ios/TutanotaSharedFramework/Notifications/NotificationStorage.swift
ivk 9100edc7b8 [ios] Add notification counter badge to app icon
Add a persistent counter that is incremented from notification
extension. The counter is reset on app being put into foreground.

Close #1757
2025-10-16 18:01:17 +02:00

128 lines
5.2 KiB
Swift

import DictionaryCoding
import Foundation
private let SSE_INFO_KEY = "sseInfo"
private let ALARMS_KEY = "repeatingAlarmNotification"
private let LAST_PROCESSED_NOTIFICAION_ID_KEY = "lastProcessedNotificationId"
private let LAST_MISSED_NOTIFICATION_CHECK_TIME = "lastMissedNotificationCheckTime"
private let EXTENDED_NOTIFICATION_MODE = "extendedNotificationMode"
private let RECEIVE_CALENDAR_NOTIFICATION_CONFIG = "receiveCalendarNotificationConfig"
private let NOTIFICAITON_COUNT_KEY = "notificationCount"
public class NotificationStorage {
private let userPreferencesProvider: UserPreferencesProvider
public init(userPreferencesProvider: UserPreferencesProvider) { self.userPreferencesProvider = userPreferencesProvider }
public var sseInfo: SSEInfo? {
get {
let dict = self.userPreferencesProvider.getObject(forKey: SSE_INFO_KEY)
return dict.map { try! DictionaryDecoder().decode(SSEInfo.self, from: $0 as! NSDictionary) }
}
}
public func store(pushIdentifier: String, userId: String, sseOrigin: String) throws {
// Provide right defaults for extended notification mode.
// - Start with "nothing" as a conservative default
// - If notifications were not used before, enable extended notifications
if var sseInfo = self.sseInfo {
sseInfo.pushIdentifier = pushIdentifier
sseInfo.sseOrigin = sseOrigin
var userIds = sseInfo.userIds
if !userIds.contains(userId) {
userIds.append(userId)
try self.setExtendedNotificationConfig(userId, .sender_and_subject)
}
sseInfo.userIds = userIds
self.put(sseInfo: sseInfo)
} else {
let sseInfo = SSEInfo(pushIdentifier: pushIdentifier, sseOrigin: sseOrigin, userIds: [userId])
try self.setExtendedNotificationConfig(userId, .sender_and_subject)
self.put(sseInfo: sseInfo)
}
}
public func store(alarms: [EncryptedAlarmNotification]) {
let jsonData = try! JSONEncoder().encode(alarms)
self.userPreferencesProvider.setValue(jsonData, forKey: ALARMS_KEY)
}
public var alarms: [EncryptedAlarmNotification] {
get {
let notificationsJsonData = self.userPreferencesProvider.getObject(forKey: ALARMS_KEY)
if let notificationsJsonData {
do { return try JSONDecoder().decode(Array<EncryptedAlarmNotification>.self, from: notificationsJsonData as! Data) } catch {
TUTSLog("Could not schedule alarms")
}
}
return []
}
}
public func removeUser(_ userId: String) {
guard var sseInfo = self.sseInfo else {
TUTSLog("Removing userId but there's no SSEInfo stored")
return
}
var userIds = sseInfo.userIds
if let index = userIds.firstIndex(of: userId) { userIds.remove(at: index) }
sseInfo.userIds = userIds
self.put(sseInfo: sseInfo)
}
public var lastProcessedNotificationId: String? {
get { self.userPreferencesProvider.getObject(forKey: LAST_PROCESSED_NOTIFICAION_ID_KEY) as! String? }
set { return self.userPreferencesProvider.setValue(newValue, forKey: LAST_PROCESSED_NOTIFICAION_ID_KEY) }
}
public var lastMissedNotificationCheckTime: Date? {
get { self.userPreferencesProvider.getObject(forKey: LAST_MISSED_NOTIFICATION_CHECK_TIME) as! Date? }
set { return self.userPreferencesProvider.setValue(newValue, forKey: LAST_MISSED_NOTIFICATION_CHECK_TIME) }
}
/// Count of email notifications received but not viewed
public var notificationCount: Int { get { self.userPreferencesProvider.getObject(forKey: NOTIFICAITON_COUNT_KEY) as! Int? ?? 0 } }
public func incrementNotificationCount() { self.userPreferencesProvider.setValue(self.notificationCount + 1, forKey: NOTIFICAITON_COUNT_KEY) }
public func resetNotificaitonCount() { self.userPreferencesProvider.setValue(0, forKey: NOTIFICAITON_COUNT_KEY) }
public func clear() {
TUTSLog("UserPreference clear")
let sseInfo = self.sseInfo
if var sseInfo {
sseInfo.userIds = []
self.put(sseInfo: nil)
self.lastMissedNotificationCheckTime = nil
self.store(alarms: [])
}
}
public func setExtendedNotificationConfig(_ userId: String, _ mode: TutanotaSharedFramework.ExtendedNotificationMode) throws {
self.userPreferencesProvider.setValue(mode.rawValue, forKey: "\(EXTENDED_NOTIFICATION_MODE):\(userId)")
}
public func getExtendedNotificationConfig(_ userId: String) throws -> TutanotaSharedFramework.ExtendedNotificationMode {
// This default gets overwritten later when we store the pushIdentifier
self.userPreferencesProvider.getObject(forKey: "\(EXTENDED_NOTIFICATION_MODE):\(userId)")
.map { mode in ExtendedNotificationMode(rawValue: mode as! String)! } ?? .sender_and_subject
}
public func setReceiveCalendarNotificationConfig(_ pushIdentifier: String, _ value: Bool) {
self.userPreferencesProvider.setValue(value, forKey: "\(RECEIVE_CALENDAR_NOTIFICATION_CONFIG):\(pushIdentifier)")
}
public func getReceiveCalendarNotificationConfig(_ pushIdentifier: String) -> Bool {
self.userPreferencesProvider.getObject(forKey: "\(RECEIVE_CALENDAR_NOTIFICATION_CONFIG):\(pushIdentifier)").map { enabled in enabled as! Bool == true }
?? true
}
private func put(sseInfo: SSEInfo?) {
if let sseInfo {
let dict: NSDictionary = try! DictionaryEncoder().encode(sseInfo)
self.userPreferencesProvider.setValue(dict, forKey: SSE_INFO_KEY)
} else {
self.userPreferencesProvider.setValue(nil, forKey: SSE_INFO_KEY)
}
}
}