Private
Public Access
1
0
Files
Kordophone/kordophone2/Models.swift

259 lines
7.1 KiB
Swift

//
// Models.swift
// kordophone2
//
// Created by James Magahern on 8/24/25.
//
import Foundation
import XPC
enum Display
{
struct Conversation: Identifiable
{
let id: String
let name: String?
let participants: [String]
let messagePreview: String
var displayName: String {
if let name, name.count > 0 { return name }
else { return participants.joined(separator: ", ") }
}
init(from c: Serialized.Conversation) {
self.id = c.guid
self.name = c.displayName
self.participants = c.participants
self.messagePreview = c.lastMessagePreview ?? ""
}
init(id: String = UUID().uuidString, name: String? = nil, participants: [String], messagePreview: String) {
self.id = id
self.name = name
self.participants = participants
self.messagePreview = messagePreview
}
}
struct Message: Identifiable, Hashable
{
let id: String
let sender: Sender
let text: String
let date: Date
let attachments: [ImageAttachment]
var isFromMe: Bool { sender.isMe }
init(from m: Serialized.Message) {
self.id = m.guid
self.text = m.text
self.date = m.date
let sender: Sender = if m.sender == "(Me)" {
.me
} else {
.counterpart(m.sender)
}
self.attachments = m.attachments.map { attachment in
ImageAttachment(from: attachment, sender: sender)
}
self.sender = sender
}
init(id: String = UUID().uuidString, sender: Sender = .me, date: Date = .now, text: String) {
self.id = id
self.sender = sender
self.text = text
self.date = date
self.attachments = []
}
static func == (lhs: Message, rhs: Message) -> Bool {
lhs.id == rhs.id
}
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
}
struct ImageAttachment: Identifiable
{
let id: String
let sender: Sender
let data: Serialized.Attachment
var size: CGSize? {
if let attr = data.metadata?.attributionInfo, let width = attr.width, let height = attr.height {
return CGSize(width: width, height: height)
}
return nil
}
var isPreviewDownloaded: Bool {
data.isPreviewDownloaded
}
var previewPath: String {
data.previewPath
}
init(from serialized: Serialized.Attachment, sender: Sender) {
self.id = serialized.guid
self.sender = sender
self.data = serialized
}
}
enum Sender
{
case me
case counterpart(String)
var isMe: Bool {
if case .me = self { true } else { false }
}
}
}
enum Serialized
{
struct Conversation: Decodable
{
let guid: String
let displayName: String?
let participants: [String]
let lastMessagePreview: String?
let unreadCount: Int
let date: Date
init?(xpc dict: xpc_object_t)
{
guard let d = XPCDictionary(dict), let g: String = d["guid"] else { return nil }
let dn: String? = d["display_name"]
let lmp: String? = d["last_message_preview"]
let names: [String] = d["participants"] ?? []
let unread: Int = d["unread_count"] ?? 0
let dt: Date = d["date"] ?? Date(timeIntervalSince1970: 0)
self.guid = g
self.displayName = dn
self.participants = names
self.lastMessagePreview = lmp
self.unreadCount = unread
self.date = dt
}
}
struct Message: Decodable
{
let guid: String
let sender: String
let text: String
let date: Date
let attachments: [Attachment]
init?(xpc dict: xpc_object_t)
{
guard let d = XPCDictionary(dict), let g: String = d["id"] else { return nil }
let s: String = d["sender"] ?? ""
let t: String = d["text"] ?? ""
let dd: Date = d["date"] ?? Date(timeIntervalSince1970: 0)
let atts: [Attachment] = d["attachments"] ?? []
self.guid = g
self.sender = s
self.text = t
self.date = dd
self.attachments = atts
}
}
struct Attachment: Decodable
{
let guid: String
let path: String
let previewPath: String
let isDownloaded: Bool
let isPreviewDownloaded: Bool
let metadata: Metadata?
struct Metadata: Decodable
{
let attributionInfo: AttributionInfo?
}
struct AttributionInfo: Decodable
{
let width: Int?
let height: Int?
}
}
struct Settings: Decodable
{
let serverUrl: String
let username: String
}
}
extension Serialized.Settings: XPCConvertible
{
static func fromXPC(_ value: xpc_object_t) -> Serialized.Settings? {
guard let d = XPCDictionary(value) else { return nil }
let su: String = d["server_url"] ?? ""
let un: String = d["username"] ?? ""
return Serialized.Settings(
serverUrl: su,
username: un
)
}
}
extension Serialized.Attachment: XPCConvertible
{
static func fromXPC(_ value: xpc_object_t) -> Serialized.Attachment? {
guard let d = XPCDictionary(value), let guid: String = d["guid"] else { return nil }
let path: String = d["path"] ?? ""
let previewPath: String = d["preview_path"] ?? ""
// Booleans are encoded as strings in XPC
let downloadedStr: String = d["downloaded"] ?? "false"
let previewDownloadedStr: String = d["preview_downloaded"] ?? "false"
let isDownloaded = downloadedStr == "true"
let isPreviewDownloaded = previewDownloadedStr == "true"
var metadata: Serialized.Attachment.Metadata? = nil
if let metadataObj = d.object("metadata"), let md = XPCDictionary(metadataObj) {
var attribution: Serialized.Attachment.AttributionInfo? = nil
if let attrObj = md.object("attribution_info"), let ad = XPCDictionary(attrObj) {
let width: Int? = ad["width"]
let height: Int? = ad["height"]
attribution = Serialized.Attachment.AttributionInfo(width: width, height: height)
}
metadata = Serialized.Attachment.Metadata(attributionInfo: attribution)
}
return Serialized.Attachment(
guid: guid,
path: path,
previewPath: previewPath,
isDownloaded: isDownloaded,
isPreviewDownloaded: isPreviewDownloaded,
metadata: metadata
)
}
}