Files
Kecalek_python/ios_client 0.8.5/Kecalek/Models/Message.swift
2026-03-14 12:43:56 +01:00

211 lines
7.1 KiB
Swift

import Foundation
struct MessageReaction: Equatable {
let userId: String
let reaction: String
let createdAt: Date
}
struct ForwardedFrom: Equatable {
let sender: String
let conversationId: String
let messageId: String
}
enum ReactionEmoji {
static let allowed = ["thumbsup", "heart", "laugh", "surprised", "sad", "thumbsdown"]
static let display: [String: String] = [
"thumbsup": "👍", "heart": "❤️", "laugh": "😂",
"surprised": "😮", "sad": "😢", "thumbsdown": "👎",
]
}
struct Message: Identifiable, Equatable {
let id: String
let conversationId: String
let senderId: String
var senderUsername: String
let createdAt: Date
var text: String?
var replyTo: String?
var imageFileId: String?
var file: FileInfo?
var image: ImageInfo?
var isDeleted: Bool
var readBy: Set<String>
var reactions: [MessageReaction]
var forwardedFrom: ForwardedFrom?
var pinnedAt: Date?
var pinnedBy: String?
/// Whether this is a self-sent message
func isMine(currentUserId: String) -> Bool {
senderId == currentUserId
}
static func == (lhs: Message, rhs: Message) -> Bool {
lhs.id == rhs.id
}
}
struct FileInfo: Equatable, Codable {
let fileId: String
let aesKey: String // base64
let iv: String // base64
let filename: String
let size: Int
let mimeType: String
}
struct ImageInfo: Equatable {
let fileId: String
let aesKey: String // base64
let iv: String // base64
let thumbnail: String? // base64 JPEG thumbnail
let filename: String
let size: Int
}
// MARK: - Cache Dictionary Conversion
extension Message {
/// Convert to dictionary matching server JSON format for MessageCache storage
func toCacheDict() -> [String: Any] {
var dict: [String: Any] = [
"message_id": id,
"conversation_id": conversationId,
"sender_id": senderId,
"sender_username": senderUsername,
"created_at": DateParsing.format(createdAt),
"is_deleted": isDeleted,
]
if let text = text { dict["text"] = text }
if let replyTo = replyTo { dict["reply_to"] = replyTo }
if let imageFileId = imageFileId { dict["image_file_id"] = imageFileId }
if let file = file {
dict["file"] = [
"file_id": file.fileId,
"aes_key": file.aesKey,
"iv": file.iv,
"filename": file.filename,
"size": file.size,
"mime_type": file.mimeType,
] as [String: Any]
}
if let image = image {
var imgDict: [String: Any] = [
"file_id": image.fileId,
"aes_key": image.aesKey,
"iv": image.iv,
"filename": image.filename,
"size": image.size,
]
if let thumbnail = image.thumbnail { imgDict["thumbnail"] = thumbnail }
dict["image"] = imgDict
}
if !readBy.isEmpty { dict["read_by"] = Array(readBy) }
if !reactions.isEmpty {
dict["reactions"] = reactions.map {
["user_id": $0.userId, "reaction": $0.reaction,
"created_at": DateParsing.format($0.createdAt)] as [String: Any]
}
}
if let fwd = forwardedFrom {
dict["forwarded_from"] = ["sender": fwd.sender,
"conversation_id": fwd.conversationId,
"message_id": fwd.messageId] as [String: Any]
}
if let pinnedAt { dict["pinned_at"] = DateParsing.format(pinnedAt) }
if let pinnedBy { dict["pinned_by"] = pinnedBy }
return dict
}
/// Create Message from cache dictionary (server JSON format)
static func fromCacheDict(_ dict: [String: Any]) -> Message? {
guard let id = dict["message_id"] as? String,
let conversationId = dict["conversation_id"] as? String,
let senderId = dict["sender_id"] as? String,
let createdAtStr = dict["created_at"] as? String,
let createdAt = DateParsing.parse(createdAtStr) else {
return nil
}
let senderUsername = dict["sender_username"] as? String ?? ""
var file: FileInfo?
if let fileDict = dict["file"] as? [String: Any],
let fileId = fileDict["file_id"] as? String {
file = FileInfo(
fileId: fileId,
aesKey: fileDict["aes_key"] as? String ?? "",
iv: fileDict["iv"] as? String ?? "",
filename: fileDict["filename"] as? String ?? "",
size: fileDict["size"] as? Int ?? 0,
mimeType: fileDict["mime_type"] as? String ?? ""
)
}
var image: ImageInfo?
if let imgDict = dict["image"] as? [String: Any],
let imgFileId = imgDict["file_id"] as? String {
image = ImageInfo(
fileId: imgFileId,
aesKey: imgDict["aes_key"] as? String ?? "",
iv: imgDict["iv"] as? String ?? "",
thumbnail: imgDict["thumbnail"] as? String,
filename: imgDict["filename"] as? String ?? "image.jpg",
size: imgDict["size"] as? Int ?? 0
)
}
let readBy: Set<String>
if let readByArray = dict["read_by"] as? [String] {
readBy = Set(readByArray)
} else {
readBy = []
}
var reactions: [MessageReaction] = []
if let reactionsArr = dict["reactions"] as? [[String: Any]] {
reactions = reactionsArr.compactMap { r in
guard let userId = r["user_id"] as? String,
let reaction = r["reaction"] as? String else { return nil }
let createdAt = (r["created_at"] as? String).flatMap { DateParsing.parse($0) } ?? Date()
return MessageReaction(userId: userId, reaction: reaction, createdAt: createdAt)
}
}
var forwardedFrom: ForwardedFrom?
if let fwd = dict["forwarded_from"] as? [String: Any],
let sender = fwd["sender"] as? String {
forwardedFrom = ForwardedFrom(
sender: sender,
conversationId: fwd["conversation_id"] as? String ?? "",
messageId: fwd["message_id"] as? String ?? ""
)
}
let pinnedAt = (dict["pinned_at"] as? String).flatMap { DateParsing.parse($0) }
let pinnedBy = dict["pinned_by"] as? String
return Message(
id: id,
conversationId: conversationId,
senderId: senderId,
senderUsername: senderUsername,
createdAt: createdAt,
text: dict["text"] as? String,
replyTo: dict["reply_to"] as? String,
imageFileId: dict["image_file_id"] as? String,
file: file,
image: image,
isDeleted: dict["is_deleted"] as? Bool ?? false,
readBy: readBy,
reactions: reactions,
forwardedFrom: forwardedFrom,
pinnedAt: pinnedAt,
pinnedBy: pinnedBy
)
}
}