ios_client
This commit is contained in:
210
ios_client 0.8.5/Kecalek/Models/Message.swift
Normal file
210
ios_client 0.8.5/Kecalek/Models/Message.swift
Normal file
@@ -0,0 +1,210 @@
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user