mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-05-20 13:46:33 +00:00
ios: notification badge count (#822)
This commit is contained in:
committed by
GitHub
parent
add82d73fa
commit
252897d0ff
@@ -127,6 +127,7 @@ final class ChatModel: ObservableObject {
|
||||
addChat(Chat(c), at: i)
|
||||
}
|
||||
}
|
||||
NtfManager.shared.setNtfBadgeCount(totalUnreadCount())
|
||||
}
|
||||
|
||||
// func addGroup(_ group: SimpleXChat.Group) {
|
||||
@@ -139,6 +140,7 @@ final class ChatModel: ObservableObject {
|
||||
chats[i].chatItems = [cItem]
|
||||
if case .rcvNew = cItem.meta.itemStatus {
|
||||
chats[i].chatStats.unreadCount = chats[i].chatStats.unreadCount + 1
|
||||
NtfManager.shared.incNtfBadgeCount()
|
||||
}
|
||||
if i > 0 {
|
||||
if chatId == nil {
|
||||
@@ -203,6 +205,9 @@ final class ChatModel: ObservableObject {
|
||||
// remove from current chat
|
||||
if chatId == cInfo.id {
|
||||
if let i = chatItems.firstIndex(where: { $0.id == cItem.id }) {
|
||||
if chatItems[i].isRcvNew() == true {
|
||||
NtfManager.shared.decNtfBadgeCount()
|
||||
}
|
||||
_ = withAnimation {
|
||||
self.chatItems.remove(at: i)
|
||||
}
|
||||
@@ -213,6 +218,7 @@ final class ChatModel: ObservableObject {
|
||||
func markChatItemsRead(_ cInfo: ChatInfo) {
|
||||
// update preview
|
||||
if let chat = getChat(cInfo.id) {
|
||||
NtfManager.shared.decNtfBadgeCount(by: chat.chatStats.unreadCount)
|
||||
chat.chatStats = ChatStats()
|
||||
}
|
||||
// update current chat
|
||||
@@ -230,6 +236,7 @@ final class ChatModel: ObservableObject {
|
||||
func clearChat(_ cInfo: ChatInfo) {
|
||||
// clear preview
|
||||
if let chat = getChat(cInfo.id) {
|
||||
NtfManager.shared.decNtfBadgeCount(by: chat.chatStats.unreadCount)
|
||||
chat.chatItems = []
|
||||
chat.chatStats = ChatStats()
|
||||
chat.chatInfo = cInfo
|
||||
@@ -251,6 +258,10 @@ final class ChatModel: ObservableObject {
|
||||
}
|
||||
}
|
||||
|
||||
func totalUnreadCount() -> Int {
|
||||
chats.reduce(0, { count, chat in count + chat.chatStats.unreadCount })
|
||||
}
|
||||
|
||||
func getPrevChatItem(_ ci: ChatItem) -> ChatItem? {
|
||||
if let i = chatItems.firstIndex(where: { $0.id == ci.id }), i > 0 {
|
||||
return chatItems[i - 1]
|
||||
|
||||
@@ -188,6 +188,19 @@ class NtfManager: NSObject, UNUserNotificationCenterDelegate, ObservableObject {
|
||||
addNotification(createCallInvitationNtf(invitation))
|
||||
}
|
||||
|
||||
func setNtfBadgeCount(_ count: Int) {
|
||||
UIApplication.shared.applicationIconBadgeNumber = count
|
||||
ntfBadgeCountGroupDefault.set(count)
|
||||
}
|
||||
|
||||
func decNtfBadgeCount(by count: Int = 1) {
|
||||
setNtfBadgeCount(max(0, UIApplication.shared.applicationIconBadgeNumber - count))
|
||||
}
|
||||
|
||||
func incNtfBadgeCount(by count: Int = 1) {
|
||||
setNtfBadgeCount(UIApplication.shared.applicationIconBadgeNumber + count)
|
||||
}
|
||||
|
||||
private func addNotification(_ content: UNMutableNotificationContent) {
|
||||
if !granted { return }
|
||||
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: ntfTimeInterval, repeats: false)
|
||||
|
||||
@@ -594,6 +594,7 @@ func startChat() throws {
|
||||
m.userSMPServers = try getUserSMPServers()
|
||||
let chats = try apiGetChats()
|
||||
m.chats = chats.map { Chat.init($0) }
|
||||
NtfManager.shared.setNtfBadgeCount(m.totalUnreadCount())
|
||||
try refreshCallInvitations()
|
||||
(m.savedToken, m.tokenStatus, m.notificationMode) = apiGetNtfToken()
|
||||
if let token = m.deviceToken {
|
||||
|
||||
@@ -16,11 +16,15 @@ let suspendingDelay: UInt64 = 2_000_000_000
|
||||
|
||||
class NotificationService: UNNotificationServiceExtension {
|
||||
var contentHandler: ((UNNotificationContent) -> Void)?
|
||||
var bestAttemptContent: UNNotificationContent?
|
||||
var bestAttemptContent: UNMutableNotificationContent?
|
||||
var badgeCount: Int = 0
|
||||
|
||||
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
|
||||
logger.debug("NotificationService.didReceive")
|
||||
bestAttemptContent = request.content
|
||||
bestAttemptContent = request.content.mutableCopy() as? UNMutableNotificationContent
|
||||
badgeCount = ntfBadgeCountGroupDefault.get() + 1
|
||||
ntfBadgeCountGroupDefault.set(badgeCount)
|
||||
bestAttemptContent?.badge = badgeCount as NSNumber
|
||||
self.contentHandler = contentHandler
|
||||
let appState = appStateGroupDefault.get()
|
||||
switch appState {
|
||||
@@ -39,20 +43,22 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
logger.debug("NotificationService: app state is \(state.rawValue, privacy: .public)")
|
||||
if state.inactive {
|
||||
receiveNtfMessages(request, contentHandler)
|
||||
} else {
|
||||
contentHandler(request.content)
|
||||
} else if let content = bestAttemptContent {
|
||||
contentHandler(content)
|
||||
}
|
||||
}
|
||||
default:
|
||||
logger.debug("NotificationService: app state is \(appState.rawValue, privacy: .public)")
|
||||
contentHandler(request.content)
|
||||
if let content = bestAttemptContent {
|
||||
contentHandler(content)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func receiveNtfMessages(_ request: UNNotificationRequest, _ contentHandler: @escaping (UNNotificationContent) -> Void) {
|
||||
logger.debug("NotificationService: receiveNtfMessages")
|
||||
if case .documents = dbContainerGroupDefault.get() {
|
||||
contentHandler(request.content)
|
||||
if let content = bestAttemptContent { contentHandler(content) }
|
||||
return
|
||||
}
|
||||
let userInfo = request.content.userInfo
|
||||
@@ -65,9 +71,11 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
logger.debug("NotificationService: receiveNtfMessages: apiGetNtfMessage \(String(describing: ntfMsgInfo), privacy: .public)")
|
||||
if let connEntity = ntfMsgInfo.connEntity {
|
||||
bestAttemptContent = createConnectionEventNtf(connEntity)
|
||||
bestAttemptContent?.badge = badgeCount as NSNumber
|
||||
}
|
||||
if let content = receiveMessageForNotification() {
|
||||
logger.debug("NotificationService: receiveMessageForNotification: has message")
|
||||
content.badge = badgeCount as NSNumber
|
||||
contentHandler(content)
|
||||
} else if let content = bestAttemptContent {
|
||||
logger.debug("NotificationService: receiveMessageForNotification: no message")
|
||||
@@ -105,7 +113,7 @@ func startChat() -> User? {
|
||||
return nil
|
||||
}
|
||||
|
||||
func receiveMessageForNotification() -> UNNotificationContent? {
|
||||
func receiveMessageForNotification() -> UNMutableNotificationContent? {
|
||||
logger.debug("NotificationService receiveMessages started")
|
||||
while true {
|
||||
if let res = recvSimpleXMsg() {
|
||||
|
||||
@@ -14,6 +14,7 @@ let GROUP_DEFAULT_DB_CONTAINER = "dbContainer"
|
||||
public let GROUP_DEFAULT_CHAT_LAST_START = "chatLastStart"
|
||||
let GROUP_DEFAULT_NTF_PREVIEW_MODE = "ntfPreviewMode"
|
||||
let GROUP_DEFAULT_PRIVACY_ACCEPT_IMAGES = "privacyAcceptImages"
|
||||
let GROUP_DEFAULT_NTF_BADGE_COUNT = "ntgBadgeCount"
|
||||
|
||||
let APP_GROUP_NAME = "group.chat.simplex.app"
|
||||
|
||||
@@ -62,6 +63,8 @@ public let ntfPreviewModeGroupDefault = EnumDefault<NotificationPreviewMode>(
|
||||
|
||||
public let privacyAcceptImagesGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_PRIVACY_ACCEPT_IMAGES)
|
||||
|
||||
public let ntfBadgeCountGroupDefault = IntDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_NTF_BADGE_COUNT)
|
||||
|
||||
public class DateDefault {
|
||||
var defaults: UserDefaults
|
||||
var key: String
|
||||
@@ -107,7 +110,19 @@ public class EnumDefault<T: RawRepresentable> where T.RawValue == String {
|
||||
}
|
||||
}
|
||||
|
||||
public class BoolDefault {
|
||||
public class BoolDefault: Default<Bool> {
|
||||
public func get() -> Bool {
|
||||
self.defaults.bool(forKey: self.key)
|
||||
}
|
||||
}
|
||||
|
||||
public class IntDefault: Default<Int> {
|
||||
public func get() -> Int {
|
||||
self.defaults.integer(forKey: self.key)
|
||||
}
|
||||
}
|
||||
|
||||
public class Default<T> {
|
||||
var defaults: UserDefaults
|
||||
var key: String
|
||||
|
||||
@@ -116,11 +131,7 @@ public class BoolDefault {
|
||||
self.key = forKey
|
||||
}
|
||||
|
||||
public func get() -> Bool {
|
||||
defaults.bool(forKey: key)
|
||||
}
|
||||
|
||||
public func set(_ value: Bool) {
|
||||
public func set(_ value: T) {
|
||||
defaults.set(value, forKey: key)
|
||||
defaults.synchronize()
|
||||
}
|
||||
|
||||
+1
-1
@@ -702,7 +702,7 @@ processChatCommand = \case
|
||||
APIJoinGroup groupId -> withUser $ \user@User {userId} -> do
|
||||
ReceivedGroupInvitation {fromMember, connRequest, groupInfo = g@GroupInfo {membership}} <- withStore $ \db -> getGroupInvitation db user groupId
|
||||
withChatLock . procCmd $ do
|
||||
agentConnId <- withAgent $ \a -> joinConnection a connRequest . directMessage . XGrpAcpt $ memberId (membership:: GroupMember)
|
||||
agentConnId <- withAgent $ \a -> joinConnection a connRequest . directMessage . XGrpAcpt $ memberId (membership :: GroupMember)
|
||||
withStore' $ \db -> do
|
||||
createMemberConnection db userId fromMember agentConnId
|
||||
updateGroupMemberStatus db userId fromMember GSMemAccepted
|
||||
|
||||
Reference in New Issue
Block a user