aboutsummaryrefslogtreecommitdiff
path: root/Hotline/Models
diff options
context:
space:
mode:
authorDustin Mierau <dustin@mierau.me>2025-10-25 16:46:52 -0700
committerDustin Mierau <dustin@mierau.me>2025-10-25 16:46:52 -0700
commit3b3b965842c47939ca54d0d1cbdf469346847f14 (patch)
tree22b5ee17d5fa8196a2d6b1bd95d7749c71cd69bc /Hotline/Models
parent53686e30592fee566585e391738adee8b2ef2137 (diff)
Move chat input string to view model so its not lost when switching between tabs. Add chat search/filtering with suppport for live filtering.
Diffstat (limited to 'Hotline/Models')
-rw-r--r--Hotline/Models/Hotline.swift64
1 files changed, 64 insertions, 0 deletions
diff --git a/Hotline/Models/Hotline.swift b/Hotline/Models/Hotline.swift
index 3783ba0..3d1f309 100644
--- a/Hotline/Models/Hotline.swift
+++ b/Hotline/Models/Hotline.swift
@@ -163,6 +163,7 @@ class Hotline: Equatable, HotlineClientDelegate, HotlineFileDownloadClientDelega
var users: [User] = []
var accounts: [HotlineAccount] = []
var chat: [ChatMessage] = []
+ var chatInput: String = ""
var messageBoard: [String] = []
var messageBoardLoaded: Bool = false
var files: [FileInfo] = []
@@ -1464,6 +1465,69 @@ class Hotline: Equatable, HotlineClientDelegate, HotlineFileDownloadClientDelega
self.lastPersistedMessageType = nil
}
+ @MainActor func searchChat(query: String) -> [ChatMessage] {
+ guard !query.isEmpty else {
+ return []
+ }
+
+ // Create a map of all messages by ID to deduplicate (current chat includes restored history)
+ var messageMap: [UUID: ChatMessage] = [:]
+
+ // Add current in-memory messages (includes both restored history and new messages)
+ for message in self.chat {
+ messageMap[message.id] = message
+ }
+
+ // Filter messages based on query
+ let filteredMessages = messageMap.values.filter { message in
+ // Never include agreement messages
+ if message.type == .agreement {
+ return false
+ }
+
+ // Always include disconnect messages to show session boundaries
+ let isDisconnect = message.type == .signOut
+
+ // Search in text and username
+ let matchesText = message.text.localizedCaseInsensitiveContains(query)
+ let matchesUsername = message.username?.localizedCaseInsensitiveContains(query) == true
+ let matchesQuery = matchesText || matchesUsername
+
+ return isDisconnect || matchesQuery
+ }
+
+ // Sort by date to maintain chronological order
+ let sortedMessages = filteredMessages.sorted { $0.date < $1.date }
+
+ // Remove consecutive disconnect messages to avoid visual clutter
+ var deduplicated: [ChatMessage] = []
+ var lastWasDisconnect = false
+
+ for message in sortedMessages {
+ let isDisconnect = message.type == .signOut
+
+ if isDisconnect && lastWasDisconnect {
+ // Skip consecutive disconnect messages
+ continue
+ }
+
+ deduplicated.append(message)
+ lastWasDisconnect = isDisconnect
+ }
+
+ // Remove leading disconnect message
+ if deduplicated.first?.type == .signOut {
+ deduplicated.removeFirst()
+ }
+
+ // Remove trailing disconnect message
+ if deduplicated.last?.type == .signOut {
+ deduplicated.removeLast()
+ }
+
+ return deduplicated
+ }
+
func updateServerTitle() {
self.serverTitle = self.serverName ?? self.server?.name ?? server?.address ?? "Server"
}