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

103 lines
2.9 KiB
Swift
Raw Normal View History

//
// Attachments.swift
// kordophone2
//
// Created by Assistant on 8/31/25.
//
import Foundation
import UniformTypeIdentifiers
enum AttachmentState
{
case staged
case uploading(progress: Double)
2025-09-03 22:38:26 -07:00
case uploaded(String) // fileTransferGUID
case failed
}
2025-09-03 22:38:26 -07:00
@Observable
class OutgoingAttachment: Identifiable, Hashable
{
let id: String
let originalURL: URL
let stagedURL: URL
let fileName: String
let contentType: UTType?
var state: AttachmentState
init(id: String = UUID().uuidString,
originalURL: URL,
stagedURL: URL,
fileName: String? = nil,
contentType: UTType? = nil,
state: AttachmentState = .staged)
{
self.id = id
self.originalURL = originalURL
self.stagedURL = stagedURL
self.fileName = fileName ?? originalURL.lastPathComponent
self.contentType = contentType
self.state = state
}
static func ==(lhs: OutgoingAttachment, rhs: OutgoingAttachment) -> Bool {
return lhs.id == rhs.id
}
func hash(into hasher: inout Hasher) {
hasher.combine(id)
hasher.combine(originalURL)
hasher.combine(stagedURL)
hasher.combine(fileName)
}
}
final class AttachmentManager
{
static let shared = AttachmentManager()
private let fileManager = FileManager.default
private let stagingRoot: URL
private init() {
let base = fileManager.temporaryDirectory.appendingPathComponent("kordophone-staging", isDirectory: true)
try? fileManager.createDirectory(at: base, withIntermediateDirectories: true)
self.stagingRoot = base
}
func stageIfImage(url: URL) -> OutgoingAttachment? {
guard isImage(url: url) else { return nil }
let staged = stage(url: url)
let type = (try? url.resourceValues(forKeys: [.contentTypeKey]))?.contentType
?? UTType(filenameExtension: url.pathExtension)
return OutgoingAttachment(originalURL: url, stagedURL: staged, fileName: url.lastPathComponent, contentType: type)
}
// MARK: - Helpers
private func isImage(url: URL) -> Bool {
if let type = (try? url.resourceValues(forKeys: [.contentTypeKey]))?.contentType {
return type.conforms(to: .image)
}
return UTType(filenameExtension: url.pathExtension)?.conforms(to: .image) ?? false
}
private func stage(url: URL) -> URL {
let ext = url.pathExtension
let dest = stagingRoot.appendingPathComponent(UUID().uuidString).appendingPathExtension(ext)
do {
if fileManager.fileExists(atPath: dest.path) { try? fileManager.removeItem(at: dest) }
try fileManager.copyItem(at: url, to: dest)
return dest
} catch {
do {
try fileManager.moveItem(at: url, to: dest)
return dest
} catch {
return url
}
}
}
}