diff --git a/Meshtastic/Accessory/Accessory Manager/AccessoryManager+MQTT.swift b/Meshtastic/Accessory/Accessory Manager/AccessoryManager+MQTT.swift index 49416f1a..0a246f88 100644 --- a/Meshtastic/Accessory/Accessory Manager/AccessoryManager+MQTT.swift +++ b/Meshtastic/Accessory/Accessory Manager/AccessoryManager+MQTT.swift @@ -32,8 +32,8 @@ extension AccessoryManager { } } // Set initial unread message badge states - appState.unreadChannelMessages = fetchedNodeInfo[0].myInfo?.unreadMessages ?? 0 - appState.unreadDirectMessages = fetchedNodeInfo[0].user?.unreadMessages(in: context, skipLastMessageCheck: true) ?? 0 // skipLastMessageCheck=true because we don't update lastMessage on our own connected node + appState.unreadChannelMessages = fetchedNodeInfo[0].myInfo?.unreadMessages(context: context) ?? 0 + appState.unreadDirectMessages = fetchedNodeInfo[0].user?.unreadMessages(context: context, skipLastMessageCheck: true) ?? 0 // skipLastMessageCheck=true because we don't update lastMessage on our own connected node } if fetchedNodeInfo.count == 1 && fetchedNodeInfo[0].rangeTestConfig?.enabled == true { wantRangeTestPackets = true diff --git a/Meshtastic/Extensions/CoreData/ChannelEntityExtension.swift b/Meshtastic/Extensions/CoreData/ChannelEntityExtension.swift index 4453a4ab..b30970ac 100644 --- a/Meshtastic/Extensions/CoreData/ChannelEntityExtension.swift +++ b/Meshtastic/Extensions/CoreData/ChannelEntityExtension.swift @@ -30,7 +30,7 @@ extension ChannelEntity { return (try? context.fetch(fetchRequest))?.first } - var unreadMessages: Int { + func unreadMessages(context: NSManagedObjectContext) -> Int { let context = PersistenceController.shared.container.viewContext let fetchRequest = MessageEntity.fetchRequest() // sort is irrelvant. @@ -38,6 +38,9 @@ extension ChannelEntity { return (try? context.count(for: fetchRequest)) ?? 0 } + // Backwards-compatible property (uses viewContext) + var unreadMessages: Int { unreadMessages(context: PersistenceController.shared.container.viewContext) } + var protoBuf: Channel { var channel = Channel() channel.index = self.index diff --git a/Meshtastic/Extensions/CoreData/MyInfoEntityExtension.swift b/Meshtastic/Extensions/CoreData/MyInfoEntityExtension.swift index 68a48ee1..67bbbcad 100644 --- a/Meshtastic/Extensions/CoreData/MyInfoEntityExtension.swift +++ b/Meshtastic/Extensions/CoreData/MyInfoEntityExtension.swift @@ -6,6 +6,7 @@ // import Foundation +import CoreData extension MyInfoEntity { @@ -18,11 +19,17 @@ extension MyInfoEntity { return (try? context.fetch(fetchRequest)) ?? [MessageEntity]() } - var unreadMessages: Int { - let unreadMessages = messageList.filter { ($0 as AnyObject).read == false && ($0 as AnyObject).isEmoji == false } - return unreadMessages.count + func unreadMessages(context: NSManagedObjectContext) -> Int { + // Returns the count of unread *channel* messages + let fetchRequest = MessageEntity.fetchRequest() + // sort is irrelvant. + fetchRequest.predicate = NSPredicate(format: "toUser == nil AND isEmoji == false AND read == false") + return (try? context.count(for: fetchRequest)) ?? 0 } + // Backwards-compatible property (uses viewContext) + var unreadMessages: Int { unreadMessages(context: PersistenceController.shared.container.viewContext) } + var hasAdmin: Bool { let adminChannel = channels?.filter { ($0 as AnyObject).name?.lowercased() == "admin" } return adminChannel?.count ?? 0 > 0 diff --git a/Meshtastic/Extensions/CoreData/UserEntityExtension.swift b/Meshtastic/Extensions/CoreData/UserEntityExtension.swift index 28a49e21..71db77d6 100644 --- a/Meshtastic/Extensions/CoreData/UserEntityExtension.swift +++ b/Meshtastic/Extensions/CoreData/UserEntityExtension.swift @@ -43,12 +43,11 @@ extension UserEntity { return (try? context.fetch(fetchRequest)) ?? [MessageEntity]() } - func unreadMessages(in ctx: NSManagedObjectContext?, skipLastMessageCheck: Bool = false) -> Int { + func unreadMessages(context: NSManagedObjectContext, skipLastMessageCheck: Bool = false) -> Int { // Most contacts will have no DMs history, so we can return early. // (For our own node, set skipLastMessageCheck=true, because we don't update lastMessage on our own connected node.) guard self.lastMessage != nil || skipLastMessageCheck else { return 0; } - let context = ctx ?? PersistenceController.shared.container.viewContext // default to viewContext let fetchRequest = MessageEntity.fetchRequest() // sort is irrelvant. fetchRequest.predicate = NSPredicate(format: "((toUser == %@) OR (fromUser == %@)) AND toUser != nil AND fromUser != nil AND isEmoji == false AND admin = false AND portNum != 10 AND read == false", self, self) @@ -56,7 +55,7 @@ extension UserEntity { } // Backwards-compatible property (uses viewContext) - var unreadMessages: Int { unreadMessages(in: nil) } + var unreadMessages: Int { unreadMessages(context: PersistenceController.shared.container.viewContext) } /// SVG Images for Vendors who are signed project backers var hardwareImage: String? { diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index e030cd16..caffd8b4 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -1040,7 +1040,7 @@ func textMessageAppPacket( if newMessage.fromUser != nil && newMessage.toUser != nil { // Set Unread Message Indicators if packet.to == connectedNode { - let unreadCount = newMessage.toUser?.unreadMessages(in: context, skipLastMessageCheck: true) ?? 0 // skipLastMessageCheck=true because we don't update lastMessage on our own connected node + let unreadCount = newMessage.toUser?.unreadMessages(context: context, skipLastMessageCheck: true) ?? 0 // skipLastMessageCheck=true because we don't update lastMessage on our own connected node Task { @MainActor in appState?.unreadDirectMessages = unreadCount } @@ -1071,7 +1071,7 @@ func textMessageAppPacket( do { let fetchedMyInfo = try context.fetch(fetchMyInfoRequest) if !fetchedMyInfo.isEmpty { - appState?.unreadChannelMessages = fetchedMyInfo[0].unreadMessages + appState?.unreadChannelMessages = fetchedMyInfo[0].unreadMessages(context: context) for channel in (fetchedMyInfo[0].channels?.array ?? []) as? [ChannelEntity] ?? [] { if channel.index == newMessage.channel { context.refresh(channel, mergeChanges: true) diff --git a/Meshtastic/Views/Messages/UserMessageList.swift b/Meshtastic/Views/Messages/UserMessageList.swift index af283d0f..8e2b3699 100644 --- a/Meshtastic/Views/Messages/UserMessageList.swift +++ b/Meshtastic/Views/Messages/UserMessageList.swift @@ -43,7 +43,7 @@ struct UserMessageList: View { if let connectedPeripheralNum = accessoryManager.activeDeviceNum, let connectedNode = getNodeInfo(id: connectedPeripheralNum, context: context), let connectedUser = connectedNode.user { - appState.unreadDirectMessages = connectedUser.unreadMessages(in: context, skipLastMessageCheck: true) // skipLastMessageCheck=true because we don't update lastMessage on our own connected node + appState.unreadDirectMessages = connectedUser.unreadMessages(context: context, skipLastMessageCheck: true) // skipLastMessageCheck=true because we don't update lastMessage on our own connected node } context.refresh(user, mergeChanges: true)