diff --git a/Meshtastic/Extensions/CoreData/UserEntityExtension.swift b/Meshtastic/Extensions/CoreData/UserEntityExtension.swift index 71db77d6..7774ba80 100644 --- a/Meshtastic/Extensions/CoreData/UserEntityExtension.swift +++ b/Meshtastic/Extensions/CoreData/UserEntityExtension.swift @@ -10,12 +10,20 @@ import CoreData import MeshtasticProtobufs extension UserEntity { + var messagePredicate: NSPredicate { + return NSPredicate(format: "((toUser == %@) OR (fromUser == %@)) AND toUser != nil AND fromUser != nil AND isEmoji == false AND admin = false AND portNum != 10", self, self) + } + + var messageFetchRequest: NSFetchRequest { + let fetchRequest = MessageEntity.fetchRequest() + fetchRequest.sortDescriptors = [NSSortDescriptor(key: "messageTimestamp", ascending: true)] + fetchRequest.predicate = messagePredicate + return fetchRequest + } var messageList: [MessageEntity] { let context = PersistenceController.shared.container.viewContext - let fetchRequest = MessageEntity.fetchRequest() - fetchRequest.sortDescriptors = [NSSortDescriptor(key: "messageTimestamp", ascending: true)] - fetchRequest.predicate = NSPredicate(format: "((toUser == %@) OR (fromUser == %@)) AND toUser != nil AND fromUser != nil AND isEmoji == false AND admin = false AND portNum != 10", self, self) + let fetchRequest = messageFetchRequest return (try? context.fetch(fetchRequest)) ?? [MessageEntity]() } @@ -26,9 +34,8 @@ extension UserEntity { // Most recent DM for this user (descending, limit 1) let context = PersistenceController.shared.container.viewContext - let fetchRequest = MessageEntity.fetchRequest() + let fetchRequest = messageFetchRequest fetchRequest.sortDescriptors = [NSSortDescriptor(key: "messageTimestamp", ascending: false)] - fetchRequest.predicate = NSPredicate(format: "((toUser == %@) OR (fromUser == %@)) AND toUser != nil AND fromUser != nil AND isEmoji == false AND admin = false AND portNum != 10", self, self) fetchRequest.fetchLimit = 1 return (try? context.fetch(fetchRequest))?.first @@ -48,9 +55,10 @@ extension UserEntity { // (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 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) + let fetchRequest = messageFetchRequest + fetchRequest.sortDescriptors = [] // sort is irrelvant. + fetchRequest.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [fetchRequest.predicate!, NSPredicate(format: "read == false")]) + return (try? context.count(for: fetchRequest)) ?? 0 } diff --git a/Meshtastic/Views/Messages/UserMessageList.swift b/Meshtastic/Views/Messages/UserMessageList.swift index 8e2b3699..68e0a04f 100644 --- a/Meshtastic/Views/Messages/UserMessageList.swift +++ b/Meshtastic/Views/Messages/UserMessageList.swift @@ -21,17 +21,21 @@ struct UserMessageList: View { @State private var messageToHighlight: Int64 = 0 @State private var redrawTapbacksTrigger = UUID() @AppStorage("preferredPeripheralNum") private var preferredPeripheralNum = -1 - - private var allPrivateMessages: [MessageEntity] { - // Cast user.messageList to an array for easier indexing and ForEach. - return user.messageList.compactMap { $0 as MessageEntity } + @FetchRequest private var allPrivateMessages: FetchedResults + + init(user: UserEntity) { + self.user = user + + // Configure fetch request here + let request: NSFetchRequest = user.messageFetchRequest + _allPrivateMessages = FetchRequest(fetchRequest: request) } - + func handleInteractionComplete() { markMessagesAsRead() redrawTapbacksTrigger = UUID() } - + func markMessagesAsRead() { do { for unreadMessage in allPrivateMessages.filter({ !$0.read }) { @@ -51,19 +55,22 @@ struct UserMessageList: View { Logger.data.error("Failed to read direct messages: \(error.localizedDescription, privacy: .public)") } } - + var body: some View { + // Cast user.messageList to an array for easier indexing and ForEach. + let messages = allPrivateMessages.compactMap { $0 as MessageEntity } + VStack { ScrollViewReader { scrollView in ScrollView { LazyVStack { - ForEach(allPrivateMessages.indices, id: \.self) { index in - let message = allPrivateMessages[index] - let previousMessage = index > 0 ? allPrivateMessages[index - 1] : nil + ForEach(messages.indices, id: \.self) { index in + let message = messages[index] + let previousMessage = index > 0 ? messages[index - 1] : nil UserMessageRow( message: message, - allMessages: allPrivateMessages, + allMessages: messages, previousMessage: previousMessage, preferredPeripheralNum: preferredPeripheralNum, user: user,