Add 'osx/' from commit '46755a07ef2e7aa9852d74c30e2c12f9fe8f2278'
git-subtree-dir: osx git-subtree-mainline:034026e88agit-subtree-split:46755a07ef
This commit is contained in:
282
osx/kordophone2/Models.swift
Normal file
282
osx/kordophone2/Models.swift
Normal file
@@ -0,0 +1,282 @@
|
||||
//
|
||||
// Models.swift
|
||||
// kordophone2
|
||||
//
|
||||
// Created by James Magahern on 8/24/25.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import XPC
|
||||
|
||||
enum Display
|
||||
{
|
||||
struct Conversation: Identifiable, Hashable
|
||||
{
|
||||
let id: String
|
||||
let name: String?
|
||||
let participants: [String]
|
||||
let messagePreview: String
|
||||
let unreadCount: Int
|
||||
|
||||
var displayName: String {
|
||||
if let name, name.count > 0 { return name }
|
||||
else { return participants.joined(separator: ", ") }
|
||||
}
|
||||
|
||||
var isGroupChat: Bool {
|
||||
participants.count > 1
|
||||
}
|
||||
|
||||
init(from c: Serialized.Conversation) {
|
||||
self.id = c.guid
|
||||
self.name = c.displayName
|
||||
self.participants = c.participants
|
||||
self.messagePreview = c.lastMessagePreview ?? ""
|
||||
self.unreadCount = c.unreadCount
|
||||
}
|
||||
|
||||
init(id: String = UUID().uuidString, name: String? = nil, participants: [String], messagePreview: String) {
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.participants = participants
|
||||
self.messagePreview = messagePreview
|
||||
self.unreadCount = 0
|
||||
}
|
||||
}
|
||||
|
||||
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, dateSent: m.date, 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 dateSent: Date
|
||||
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, dateSent: Date, sender: Sender) {
|
||||
self.id = serialized.guid
|
||||
self.sender = sender
|
||||
self.data = serialized
|
||||
self.dateSent = dateSent
|
||||
}
|
||||
}
|
||||
|
||||
enum Sender: Identifiable, Equatable
|
||||
{
|
||||
case me
|
||||
case counterpart(String)
|
||||
|
||||
var id: String { displayName }
|
||||
|
||||
var isMe: Bool {
|
||||
if case .me = self { true } else { false }
|
||||
}
|
||||
|
||||
var displayName: String {
|
||||
switch self {
|
||||
case .me:
|
||||
"Me"
|
||||
case .counterpart(let string):
|
||||
string
|
||||
}
|
||||
}
|
||||
|
||||
static func ==(lhs: Sender, rhs: Sender) -> Bool {
|
||||
return lhs.displayName == rhs.displayName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user