import Combine import Foundation import GRDB @MainActor final class ConversationStore: ObservableObject { @Published private(set) var messages: [Message] = [] @Published var replyText = "" private(set) var roster: Roster private let client: Client private let blockSize = Const.messagesPageSize private let messagesMax = Const.messagesMaxSize private var messagesCancellable: AnyCancellable? init(roster: Roster, client: Client) { self.client = client self.roster = roster subscribe() } } extension ConversationStore { func sendMessage(_ message: String) async { // prepare message let message = Message( id: UUID().uuidString, type: .chat, date: Date(), contentType: .text, status: .pending, from: roster.bareJid, to: roster.contactBareJid, body: message, subject: nil, thread: nil, oobUrl: nil ) // store as pending on db, and send do { try await message.save() try await client.sendMessage(message) try await message.setStatus(.sent) } catch { try? await message.setStatus(.error) } } } private extension ConversationStore { 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 } } } // var isAuthorized: Bool { // get async { // let status = AVCaptureDevice.authorizationStatus(for: .video) // // // Determine if the user previously authorized camera access. // var isAuthorized = status == .authorized // // // If the system hasn't determined the user's authorization status, // // explicitly prompt them for approval. // if status == .notDetermined { // isAuthorized = await AVCaptureDevice.requestAccess(for: .video) // } // // return isAuthorized // } // } // // // func setUpCaptureSession() async { // guard await isAuthorized else { return } // // Set up the capture session. // }