summaryrefslogtreecommitdiff
path: root/Hutch/Views/Home/HomeViewModel.swift
diff options
context:
space:
mode:
authorChristian Cleberg <[email protected]>2026-04-20 13:15:04 -0500
committerChristian Cleberg <[email protected]>2026-04-20 13:15:04 -0500
commit380fb866d090c5f13b2ad3f88f1fe23c4ade4801 (patch)
treedfb90664e32c5f318a2fd1f107fed5ad9fff6e78 /Hutch/Views/Home/HomeViewModel.swift
parent8b73fd9cd43753b36b3ab3ca5e4dc83a47ea3480 (diff)
downloadhutch-3.2.0.tar.gz
hutch-3.2.0.tar.bz2
hutch-3.2.0.zip
fix: collapse work inbox messages into thread summariesv3.2.0
Diffstat (limited to 'Hutch/Views/Home/HomeViewModel.swift')
-rw-r--r--Hutch/Views/Home/HomeViewModel.swift48
1 files changed, 45 insertions, 3 deletions
diff --git a/Hutch/Views/Home/HomeViewModel.swift b/Hutch/Views/Home/HomeViewModel.swift
index 6016a11..7ef82e9 100644
--- a/Hutch/Views/Home/HomeViewModel.swift
+++ b/Hutch/Views/Home/HomeViewModel.swift
@@ -758,9 +758,11 @@ final class HomeViewModel {
throw SRHTError.graphQLErrors([GraphQLError(message: "Failed to load inbox threads", locations: nil)])
}
+ let deduplicatedThreads = Self.deduplicateInboxThreads(unreadThreads)
+
return HomeInboxUnreadSnapshot(
- unreadCount: unreadCount,
- threads: unreadThreads.sorted(by: Self.sortInboxThreadsForTriage)
+ unreadCount: deduplicatedThreads.count,
+ threads: deduplicatedThreads
)
}
@@ -824,7 +826,7 @@ final class HomeViewModel {
latestSender: thread.sender,
lastActivityAt: thread.updated,
messageCount: thread.replies + 1,
- repo: InboxViewModel.deriveRepositoryName(from: mailingList.name),
+ repo: InboxThreadUtilities.deriveRepositoryName(from: mailingList.name),
containsPatch: thread.root.patch != nil || thread.subject.localizedCaseInsensitiveContains("[patch"),
isUnread: InboxReadStateStore.isUnread(
threadID: "\(mailingList.rid)#\(InboxThreadSummary.normalizationKey(for: thread.subject))",
@@ -1099,6 +1101,46 @@ final class HomeViewModel {
.localizedCaseInsensitiveCompare(InboxThreadSummary.normalizationKey(for: rhs.subject)) == .orderedAscending
}
+ nonisolated static func deduplicateInboxThreads(_ threads: [InboxThreadSummary]) -> [InboxThreadSummary] {
+ var grouped: [String: InboxThreadSummary] = [:]
+
+ for thread in threads {
+ guard let existing = grouped[thread.threadGroupingKey] else {
+ grouped[thread.threadGroupingKey] = thread
+ continue
+ }
+
+ let latest = thread.lastActivityAt >= existing.lastActivityAt ? thread : existing
+ let mergedRootEmailIDs = Array(Set(existing.threadRootEmailIDs + thread.threadRootEmailIDs)).sorted()
+ let mergedRootMessageIDs = Array(Set(existing.threadRootMessageIDs + thread.threadRootMessageIDs)).sorted()
+ let mergedMessageCount = max(
+ existing.messageCount ?? existing.threadRootMessageIDs.count,
+ thread.messageCount ?? thread.threadRootMessageIDs.count,
+ mergedRootMessageIDs.count
+ )
+
+ grouped[thread.threadGroupingKey] = InboxThreadSummary(
+ rootEmailID: latest.rootEmailID,
+ rootMessageID: latest.rootMessageID,
+ threadRootEmailIDs: mergedRootEmailIDs,
+ threadRootMessageIDs: mergedRootMessageIDs,
+ listID: latest.listID,
+ listRID: latest.listRID,
+ listName: latest.listName,
+ listOwner: latest.listOwner,
+ subject: latest.subject,
+ latestSender: latest.latestSender,
+ lastActivityAt: max(existing.lastActivityAt, thread.lastActivityAt),
+ messageCount: mergedMessageCount,
+ repo: latest.repo ?? existing.repo,
+ containsPatch: latest.containsPatch || existing.containsPatch,
+ isUnread: latest.isUnread || existing.isUnread
+ )
+ }
+
+ return grouped.values.sorted(by: sortInboxThreadsForTriage)
+ }
+
nonisolated static func matchesCurrentUserAssignee(_ entity: Entity, currentUser: User) -> Bool {
let assigneeCanonical = normalizedCanonicalName(entity.canonicalName)
let currentCanonical = normalizedCanonicalName(currentUser.canonicalName)