import AVFoundation import Combine import Foundation import GRDB import Photos @MainActor final class MessagesStore: ObservableObject { @Published private(set) var messages: [Message] = [] @Published var replyText = "" private(set) var roster: Roster private let client: Client private var messagesCancellable: AnyCancellable? init(roster: Roster, client: Client) { self.client = client self.roster = roster subscribe() } } extension MessagesStore { func sendMessage(_ message: String) { Task { var msg = Message.blank msg.from = roster.bareJid msg.to = roster.contactBareJid msg.body = message // store as pending on db, and send do { try await msg.save() try await client.sendMessage(msg) try await msg.setStatus(.sent) } catch { try? await msg.setStatus(.error) } } } func sendContact(_ jidStr: String) { sendMessage("contact:\(jidStr)") } func sendLocation(_ lat: Double, _ lon: Double) { sendMessage("geo:\(lat),\(lon)") } } private extension MessagesStore { func subscribe() { messagesCancellable = ValueObservation.tracking(Message .filter( (Column("to") == roster.bareJid && Column("from") == roster.contactBareJid) || (Column("from") == roster.bareJid && Column("to") == roster.contactBareJid) ) .order(Column("date").desc) .fetchAll ) .publisher(in: Database.shared.dbQueue, scheduling: .immediate) .receive(on: DispatchQueue.main) .sink { _ in } receiveValue: { [weak self] messages in self?.messages = messages } } }