import Foundation import GRDB import Martin enum MessageType: String, Codable, DatabaseValueConvertible { case chat case groupchat case error } enum MessageAttachmentType: Int, Stateable, DatabaseValueConvertible { case movie = 0 case image = 1 case audio = 2 case file = 3 } enum MessageContentType: String, Codable, DatabaseValueConvertible { case text case typing case invite case attachment } struct Message: DBStorable, Equatable { static let databaseTableName = "messages" let id: String let type: MessageType let contentType: MessageContentType let from: String let to: String? let body: String? let subject: String? let thread: String? let oobUrl: String? let date: Date let pending: Bool let sentError: Bool var attachmentType: MessageAttachmentType? var attachmentLocalName: String? var attachmentRemotePath: URL? var attachmentThumbnailName: String? var attachmentDownloadFailed: Bool = false } extension Message { // Universal mapping from Martin's Message to App Message static func map(_ martinMessage: Martin.Message) -> Message? { #if DEBUG print("---") print("Message received: \(martinMessage)") print("---") #endif // Check that the message type is supported let chatTypes: [StanzaType] = [.chat, .groupchat] guard let mType = martinMessage.type, chatTypes.contains(mType) else { #if DEBUG print("Unsupported message type: \(martinMessage.type?.rawValue ?? "nil")") #endif return nil } // Type let type = MessageType(rawValue: martinMessage.type?.rawValue ?? "") ?? .chat // Content type var contentType: MessageContentType = .text if martinMessage.oob != nil { contentType = .attachment } else if martinMessage.hints.contains(.noStore) { contentType = .typing } // From/To let from = martinMessage.from?.bareJid.stringValue ?? "" let to = martinMessage.to?.bareJid.stringValue // Extract date or set current var date = Date() if let timestampStr = martinMessage.attribute("archived_date"), let timeInterval = TimeInterval(timestampStr) { date = Date(timeIntervalSince1970: timeInterval) } // Msg var msg = Message( id: martinMessage.id ?? UUID().uuidString, type: type, contentType: contentType, from: from, to: to, body: martinMessage.body, subject: martinMessage.subject, thread: martinMessage.thread, oobUrl: martinMessage.oob, date: date, pending: false, sentError: false, attachmentType: nil, attachmentLocalName: nil, attachmentRemotePath: nil, attachmentThumbnailName: nil, attachmentDownloadFailed: false ) if let oob = martinMessage.oob { msg.attachmentType = oob.attachmentType msg.attachmentRemotePath = URL(string: oob) } return msg } } extension Message { var attachmentLocalPath: URL? { guard let attachmentLocalName = attachmentLocalName else { return nil } return FileProcessing.fileFolder.appendingPathComponent(attachmentLocalName) } var attachmentThumbnailPath: URL? { guard let attachmentThumbnailName = attachmentThumbnailName else { return nil } return FileProcessing.fileFolder.appendingPathComponent(attachmentThumbnailName) } }