wip
This commit is contained in:
parent
f972de1c9a
commit
e0c2146a6d
|
@ -98,6 +98,40 @@ extension Client {
|
||||||
let msg = chat.createMessage(text: message.body ?? "??", id: message.id)
|
let msg = chat.createMessage(text: message.body ?? "??", id: message.id)
|
||||||
try await chat.send(message: msg)
|
try await chat.send(message: msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func uploadFile(_ localPath: URL) async throws -> String {
|
||||||
|
guard let data = try? Data(contentsOf: localPath) else {
|
||||||
|
throw ClientStoreError.noData
|
||||||
|
}
|
||||||
|
let httpModule = connection.module(HttpFileUploadModule.self)
|
||||||
|
|
||||||
|
let components = try await httpModule.findHttpUploadComponents()
|
||||||
|
guard let component = components.first(where: { $0.maxSize > data.count }) else {
|
||||||
|
throw ClientStoreError.fileTooBig
|
||||||
|
}
|
||||||
|
|
||||||
|
let slot = try await httpModule.requestUploadSlot(
|
||||||
|
componentJid: component.jid,
|
||||||
|
filename: localPath.lastPathComponent,
|
||||||
|
size: data.count,
|
||||||
|
contentType: localPath.mimeType
|
||||||
|
)
|
||||||
|
var request = URLRequest(url: slot.putUri)
|
||||||
|
for (key, value) in slot.putHeaders {
|
||||||
|
request.addValue(value, forHTTPHeaderField: key)
|
||||||
|
}
|
||||||
|
request.httpMethod = "PUT"
|
||||||
|
request.httpBody = data
|
||||||
|
request.addValue(String(data.count), forHTTPHeaderField: "Content-Length")
|
||||||
|
request.addValue(localPath.mimeType, forHTTPHeaderField: "Content-Type")
|
||||||
|
let (_, response) = try await URLSession.shared.data(for: request)
|
||||||
|
switch response {
|
||||||
|
case let httpResponse as HTTPURLResponse where httpResponse.statusCode == 200:
|
||||||
|
return slot.getUri.absoluteString
|
||||||
|
default:
|
||||||
|
throw URLError(.badServerResponse)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Client {
|
extension Client {
|
||||||
|
@ -125,14 +159,14 @@ private extension Client {
|
||||||
client.modulesManager.register(RosterModule(rosterManager: roster))
|
client.modulesManager.register(RosterModule(rosterManager: roster))
|
||||||
client.modulesManager.register(PresenceModule())
|
client.modulesManager.register(PresenceModule())
|
||||||
|
|
||||||
// client.modulesManager.register(PubSubModule())
|
client.modulesManager.register(PubSubModule())
|
||||||
client.modulesManager.register(MessageModule(chatManager: chat))
|
client.modulesManager.register(MessageModule(chatManager: chat))
|
||||||
// client.modulesManager.register(MessageArchiveManagementModule())
|
// client.modulesManager.register(MessageArchiveManagementModule())
|
||||||
|
|
||||||
// client.modulesManager.register(MessageCarbonsModule())
|
// client.modulesManager.register(MessageCarbonsModule())
|
||||||
|
|
||||||
// file transfer modules
|
// file transfer modules
|
||||||
// client.modulesManager.register(HttpFileUploadModule())
|
client.modulesManager.register(HttpFileUploadModule())
|
||||||
|
|
||||||
// extensions
|
// extensions
|
||||||
client.modulesManager.register(SoftwareVersionModule())
|
client.modulesManager.register(SoftwareVersionModule())
|
||||||
|
|
|
@ -8,9 +8,25 @@ enum MessageType: String, Codable, DatabaseValueConvertible {
|
||||||
case error
|
case error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum AttachmentType: Int, Codable, DatabaseValueConvertible {
|
||||||
|
case image
|
||||||
|
case video
|
||||||
|
case audio
|
||||||
|
case file
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Attachment: Codable & Equatable, DatabaseValueConvertible {
|
||||||
|
let type: AttachmentType
|
||||||
|
var localName: String?
|
||||||
|
var thumbnailName: String?
|
||||||
|
var remotePath: String?
|
||||||
|
}
|
||||||
|
|
||||||
enum MessageContentType: Codable & Equatable, DatabaseValueConvertible {
|
enum MessageContentType: Codable & Equatable, DatabaseValueConvertible {
|
||||||
case text
|
case text
|
||||||
case typing
|
case typing
|
||||||
|
case invite
|
||||||
|
case attachment(Attachment)
|
||||||
}
|
}
|
||||||
|
|
||||||
enum MessageStatus: Int, Codable, DatabaseValueConvertible {
|
enum MessageStatus: Int, Codable, DatabaseValueConvertible {
|
||||||
|
@ -25,7 +41,7 @@ struct Message: DBStorable, Equatable {
|
||||||
let id: String
|
let id: String
|
||||||
let type: MessageType
|
let type: MessageType
|
||||||
let date: Date
|
let date: Date
|
||||||
let contentType: MessageContentType
|
var contentType: MessageContentType
|
||||||
var status: MessageStatus
|
var status: MessageStatus
|
||||||
|
|
||||||
let from: String
|
let from: String
|
||||||
|
|
389
ConversationsClassic/AppData/Services/FileStore.swift
Normal file
389
ConversationsClassic/AppData/Services/FileStore.swift
Normal file
|
@ -0,0 +1,389 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
final class FileStore {
|
||||||
|
static let shared = FileStore()
|
||||||
|
|
||||||
|
static var fileFolder: URL {
|
||||||
|
// swiftlint:disable:next force_unwrapping
|
||||||
|
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
|
||||||
|
let subdirectoryURL = documentsURL.appendingPathComponent(Const.fileFolder)
|
||||||
|
if !FileManager.default.fileExists(atPath: subdirectoryURL.path) {
|
||||||
|
try? FileManager.default.createDirectory(at: subdirectoryURL, withIntermediateDirectories: true, attributes: nil)
|
||||||
|
}
|
||||||
|
return subdirectoryURL
|
||||||
|
}
|
||||||
|
|
||||||
|
func storeCaptured(messageId: String, _ data: Data, _ type: GalleryMediaType) async throws -> (String, AttachmentType) {
|
||||||
|
try await Task {
|
||||||
|
// local name
|
||||||
|
let fileId = UUID().uuidString
|
||||||
|
let localName: String
|
||||||
|
let msgType: AttachmentType
|
||||||
|
switch type {
|
||||||
|
case .photo:
|
||||||
|
localName = "\(messageId)_\(fileId).jpg"
|
||||||
|
msgType = .image
|
||||||
|
|
||||||
|
case .video:
|
||||||
|
localName = "\(messageId)_\(fileId).mov"
|
||||||
|
msgType = .video
|
||||||
|
}
|
||||||
|
|
||||||
|
// save
|
||||||
|
let localUrl = FileStore.fileFolder.appendingPathComponent(localName)
|
||||||
|
try data.write(to: localUrl)
|
||||||
|
return (localName, msgType)
|
||||||
|
}.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// import Foundation
|
||||||
|
// import Photos
|
||||||
|
// import UIKit
|
||||||
|
//
|
||||||
|
// final class FileProcessing {
|
||||||
|
// static let shared = FileProcessing()
|
||||||
|
//
|
||||||
|
// static var fileFolder: URL {
|
||||||
|
// // swiftlint:disable:next force_unwrapping
|
||||||
|
// let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
|
||||||
|
// let subdirectoryURL = documentsURL.appendingPathComponent(Const.fileFolder)
|
||||||
|
// if !FileManager.default.fileExists(atPath: subdirectoryURL.path) {
|
||||||
|
// try? FileManager.default.createDirectory(at: subdirectoryURL, withIntermediateDirectories: true, attributes: nil)
|
||||||
|
// }
|
||||||
|
// return subdirectoryURL
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func createThumbnail(localName: String) -> String? {
|
||||||
|
// let thumbnailFileName = "thumb_\(localName)"
|
||||||
|
// let thumbnailUrl = FileProcessing.fileFolder.appendingPathComponent(thumbnailFileName)
|
||||||
|
// let localUrl = FileProcessing.fileFolder.appendingPathComponent(localName)
|
||||||
|
//
|
||||||
|
// // check if thumbnail already exists
|
||||||
|
// if FileManager.default.fileExists(atPath: thumbnailUrl.path) {
|
||||||
|
// return thumbnailFileName
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // create thumbnail if not exists
|
||||||
|
// switch localName.attachmentType {
|
||||||
|
// case .image:
|
||||||
|
// guard let image = UIImage(contentsOfFile: localUrl.path) else {
|
||||||
|
// print("FileProcessing: Error loading image: \(localUrl)")
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
// let targetSize = CGSize(width: Const.attachmentPreviewSize, height: Const.attachmentPreviewSize)
|
||||||
|
// guard let thumbnail = scaleAndCropImage(image, targetSize) else {
|
||||||
|
// print("FileProcessing: Error scaling image: \(localUrl)")
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
// guard let data = thumbnail.pngData() else {
|
||||||
|
// print("FileProcessing: Error converting thumbnail of \(localUrl) to data")
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
// do {
|
||||||
|
// try data.write(to: thumbnailUrl)
|
||||||
|
// return thumbnailFileName
|
||||||
|
// } catch {
|
||||||
|
// print("FileProcessing: Error writing thumbnail: \(error)")
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// default:
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func fetchGallery() -> [SharingGalleryItem] {
|
||||||
|
// let items = syncGalleryEnumerate()
|
||||||
|
// .map {
|
||||||
|
// SharingGalleryItem(
|
||||||
|
// id: $0.localIdentifier,
|
||||||
|
// type: $0.mediaType == .image ? .photo : .video,
|
||||||
|
// duration: $0.mediaType == .video ? $0.duration.minAndSec : nil
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
// return items
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func fillGalleryItemsThumbnails(items: [SharingGalleryItem]) -> [SharingGalleryItem] {
|
||||||
|
// let ids = items
|
||||||
|
// .filter { $0.thumbnail == nil }
|
||||||
|
// .map { $0.id }
|
||||||
|
//
|
||||||
|
// let assets = syncGalleryEnumerate(ids)
|
||||||
|
// return assets.compactMap { asset in
|
||||||
|
// if asset.mediaType == .image {
|
||||||
|
// return syncGalleryProcessImage(asset) { [weak self] image in
|
||||||
|
// if let thumbnail = self?.scaleAndCropImage(image, CGSize(width: Const.galleryGridSize, height: Const.galleryGridSize)) {
|
||||||
|
// let data = thumbnail.jpegData(compressionQuality: 1.0) ?? Data()
|
||||||
|
// return SharingGalleryItem(id: asset.localIdentifier, type: .photo, thumbnail: data)
|
||||||
|
// } else {
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } else if asset.mediaType == .video {
|
||||||
|
// return syncGalleryProcessVideo(asset) { [weak self] avAsset in
|
||||||
|
// // swiftlint:disable:next force_cast
|
||||||
|
// let assetURL = avAsset as! AVURLAsset
|
||||||
|
// let url = assetURL.url
|
||||||
|
// if let thumbnail = self?.generateVideoThumbnail(url, CGSize(width: Const.galleryGridSize, height: Const.galleryGridSize)) {
|
||||||
|
// let data = thumbnail.jpegData(compressionQuality: 1.0) ?? Data()
|
||||||
|
// return SharingGalleryItem(
|
||||||
|
// id: asset.localIdentifier,
|
||||||
|
// type: .video,
|
||||||
|
// thumbnail: data,
|
||||||
|
// duration: asset.duration.minAndSec
|
||||||
|
// )
|
||||||
|
// } else {
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // This function also creates new ids for messages for each new attachment
|
||||||
|
// func copyGalleryItemsForUploading(items: [SharingGalleryItem]) -> [(String, String)] {
|
||||||
|
// let assets = syncGalleryEnumerate(items.map { $0.id })
|
||||||
|
// return assets
|
||||||
|
// .compactMap { asset in
|
||||||
|
// let newMessageId = UUID().uuidString
|
||||||
|
// let fileId = UUID().uuidString
|
||||||
|
// if asset.mediaType == .image {
|
||||||
|
// return syncGalleryProcessImage(asset) { image in
|
||||||
|
// let localName = "\(newMessageId)_\(fileId).jpg"
|
||||||
|
// let localUrl = FileProcessing.fileFolder.appendingPathComponent(localName)
|
||||||
|
// if let data = image.jpegData(compressionQuality: 1.0) {
|
||||||
|
// do {
|
||||||
|
// try data.write(to: localUrl)
|
||||||
|
// return (newMessageId, localName)
|
||||||
|
// } catch {
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } else if asset.mediaType == .video {
|
||||||
|
// return syncGalleryProcessVideo(asset) { avAsset in
|
||||||
|
// // swiftlint:disable:next force_cast
|
||||||
|
// let assetURL = avAsset as! AVURLAsset
|
||||||
|
// let url = assetURL.url
|
||||||
|
// let localName = "\(newMessageId)_\(fileId).mov"
|
||||||
|
// let localUrl = FileProcessing.fileFolder.appendingPathComponent(localName)
|
||||||
|
// do {
|
||||||
|
// try FileManager.default.copyItem(at: url, to: localUrl)
|
||||||
|
// return (newMessageId, localName)
|
||||||
|
// } catch {
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // This function also creates new id for file from camera capturing
|
||||||
|
// func copyCameraCapturedForUploading(media: Data, type: SharingCameraMediaType) -> (String, String)? {
|
||||||
|
// let newMessageId = UUID().uuidString
|
||||||
|
// let fileId = UUID().uuidString
|
||||||
|
// let localName: String
|
||||||
|
// switch type {
|
||||||
|
// case .photo:
|
||||||
|
// localName = "\(newMessageId)_\(fileId).jpg"
|
||||||
|
// case .video:
|
||||||
|
// localName = "\(newMessageId)_\(fileId).mov"
|
||||||
|
// }
|
||||||
|
// let localUrl = FileProcessing.fileFolder.appendingPathComponent(localName)
|
||||||
|
// do {
|
||||||
|
// try media.write(to: localUrl)
|
||||||
|
// return (newMessageId, localName)
|
||||||
|
// } catch {
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // This function also creates new id for file from document sharing
|
||||||
|
// func copyDocumentsForUploading(data: [Data], extensions: [String]) -> [(String, String)] {
|
||||||
|
// data.enumerated().compactMap { index, data in
|
||||||
|
// let newMessageId = UUID().uuidString
|
||||||
|
// let fileId = UUID().uuidString
|
||||||
|
// let localName = "\(newMessageId)_\(fileId).\(extensions[index])"
|
||||||
|
// let localUrl = FileProcessing.fileFolder.appendingPathComponent(localName)
|
||||||
|
// do {
|
||||||
|
// try data.write(to: localUrl)
|
||||||
|
// return (newMessageId, localName)
|
||||||
|
// } catch {
|
||||||
|
// print("FileProcessing: Error writing document: \(error)")
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private extension FileProcessing {
|
||||||
|
// func scaleAndCropImage(_ img: UIImage, _ size: CGSize) -> UIImage? {
|
||||||
|
// let aspect = img.size.width / img.size.height
|
||||||
|
// let targetAspect = size.width / size.height
|
||||||
|
// var newWidth: CGFloat
|
||||||
|
// var newHeight: CGFloat
|
||||||
|
// if aspect < targetAspect {
|
||||||
|
// newWidth = size.width
|
||||||
|
// newHeight = size.width / aspect
|
||||||
|
// } else {
|
||||||
|
// newHeight = size.height
|
||||||
|
// newWidth = size.height * aspect
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
|
||||||
|
// img.draw(in: CGRect(x: (size.width - newWidth) / 2, y: (size.height - newHeight) / 2, width: newWidth, height: newHeight))
|
||||||
|
// let newImage = UIGraphicsGetImageFromCurrentImageContext()
|
||||||
|
// UIGraphicsEndImageContext()
|
||||||
|
//
|
||||||
|
// return newImage
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func syncGalleryEnumerate(_ ids: [String]? = nil) -> [PHAsset] {
|
||||||
|
// var result: [PHAsset] = []
|
||||||
|
//
|
||||||
|
// let group = DispatchGroup()
|
||||||
|
// DispatchQueue.global(qos: .userInitiated).sync {
|
||||||
|
// let fetchOptions = PHFetchOptions()
|
||||||
|
// fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
|
||||||
|
// if let ids {
|
||||||
|
// fetchOptions.predicate = NSPredicate(format: "localIdentifier IN %@", ids)
|
||||||
|
// }
|
||||||
|
// let assets = PHAsset.fetchAssets(with: fetchOptions)
|
||||||
|
// assets.enumerateObjects { asset, _, _ in
|
||||||
|
// group.enter()
|
||||||
|
// result.append(asset)
|
||||||
|
// group.leave()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// group.wait()
|
||||||
|
// return result
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func syncGalleryProcess<T>(_ assets: [PHAsset], _ block: @escaping (PHAsset) -> T) -> [T] {
|
||||||
|
// var result: [T] = []
|
||||||
|
// let group = DispatchGroup()
|
||||||
|
// DispatchQueue.global(qos: .userInitiated).sync {
|
||||||
|
// for asset in assets {
|
||||||
|
// group.enter()
|
||||||
|
// let res = block(asset)
|
||||||
|
// result.append(res)
|
||||||
|
// group.leave()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// group.wait()
|
||||||
|
// return result
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func syncGalleryProcessImage<T>(_ asset: PHAsset, _ block: @escaping (UIImage) -> T?) -> T? {
|
||||||
|
// var result: T?
|
||||||
|
// let semaphore = DispatchSemaphore(value: 0)
|
||||||
|
// DispatchQueue.global(qos: .userInitiated).sync {
|
||||||
|
// let options = PHImageRequestOptions()
|
||||||
|
// options.version = .original
|
||||||
|
// options.isSynchronous = true
|
||||||
|
// PHImageManager.default().requestImage(
|
||||||
|
// for: asset,
|
||||||
|
// targetSize: PHImageManagerMaximumSize,
|
||||||
|
// contentMode: .aspectFill,
|
||||||
|
// options: options
|
||||||
|
// ) { image, _ in
|
||||||
|
// if let image {
|
||||||
|
// result = block(image)
|
||||||
|
// } else {
|
||||||
|
// result = nil
|
||||||
|
// }
|
||||||
|
// semaphore.signal()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// semaphore.wait()
|
||||||
|
// return result
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func syncGalleryProcessVideo<T>(_ asset: PHAsset, _ block: @escaping (AVAsset) -> T?) -> T? {
|
||||||
|
// var result: T?
|
||||||
|
// let semaphore = DispatchSemaphore(value: 0)
|
||||||
|
// _ = DispatchQueue.global(qos: .userInitiated).sync {
|
||||||
|
// PHImageManager.default().requestAVAsset(forVideo: asset, options: nil) { avAsset, _, _ in
|
||||||
|
// if let avAsset {
|
||||||
|
// result = block(avAsset)
|
||||||
|
// } else {
|
||||||
|
// result = nil
|
||||||
|
// }
|
||||||
|
// semaphore.signal()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// semaphore.wait()
|
||||||
|
// return result
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func generateVideoThumbnail(_ url: URL, _ size: CGSize) -> UIImage? {
|
||||||
|
// let asset = AVAsset(url: url)
|
||||||
|
// let assetImgGenerate = AVAssetImageGenerator(asset: asset)
|
||||||
|
// assetImgGenerate.appliesPreferredTrackTransform = true
|
||||||
|
// let time = CMTimeMakeWithSeconds(Float64(1), preferredTimescale: 600)
|
||||||
|
// do {
|
||||||
|
// let cgImage = try assetImgGenerate.copyCGImage(at: time, actualTime: nil)
|
||||||
|
// let image = UIImage(cgImage: cgImage)
|
||||||
|
// return scaleAndCropImage(image, size)
|
||||||
|
// } catch {
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// import Foundation
|
||||||
|
//
|
||||||
|
// final class DownloadManager {
|
||||||
|
// static let shared = DownloadManager()
|
||||||
|
//
|
||||||
|
// private let urlSession: URLSession
|
||||||
|
// private let downloadQueue = DispatchQueue(label: "com.example.downloadQueue")
|
||||||
|
// private var activeDownloads = Set<URL>()
|
||||||
|
//
|
||||||
|
// init() {
|
||||||
|
// let configuration = URLSessionConfiguration.default
|
||||||
|
// urlSession = URLSession(configuration: configuration)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func enqueueDownload(from url: URL, to localUrl: URL, completion: @escaping (Error?) -> Void) {
|
||||||
|
// downloadQueue.async {
|
||||||
|
// if self.activeDownloads.contains(url) {
|
||||||
|
// print("Download for this file is already in queue.")
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// self.activeDownloads.insert(url)
|
||||||
|
//
|
||||||
|
// let task = self.urlSession.downloadTask(with: url) { tempLocalUrl, _, error in
|
||||||
|
// self.downloadQueue.async {
|
||||||
|
// self.activeDownloads.remove(url)
|
||||||
|
//
|
||||||
|
// guard let tempLocalUrl = tempLocalUrl, error == nil else {
|
||||||
|
// completion(error)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// do {
|
||||||
|
// if FileManager.default.fileExists(atPath: localUrl.path) {
|
||||||
|
// try FileManager.default.removeItem(at: localUrl)
|
||||||
|
// }
|
||||||
|
// let data = try Data(contentsOf: tempLocalUrl)
|
||||||
|
// try data.write(to: localUrl)
|
||||||
|
// completion(nil)
|
||||||
|
// } catch let writeError {
|
||||||
|
// completion(writeError)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// task.resume()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
|
@ -3,4 +3,7 @@ enum ClientStoreError: Error {
|
||||||
case rosterNotFound
|
case rosterNotFound
|
||||||
case imageNotFound
|
case imageNotFound
|
||||||
case videoNotFound
|
case videoNotFound
|
||||||
|
case fileStoreError
|
||||||
|
case noData
|
||||||
|
case fileTooBig
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,13 +66,49 @@ extension ConversationStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendCaptured(_ data: Data, _ type: GalleryMediaType) async {
|
func sendCaptured(_ data: Data, _ type: GalleryMediaType) async {
|
||||||
print("captured!", data, type)
|
// save locally and make message
|
||||||
//
|
let messageId = UUID().uuidString
|
||||||
//
|
do {
|
||||||
//
|
let (localName, msgType) = try await FileStore.shared.storeCaptured(messageId: messageId, data, type)
|
||||||
|
let message = Message(
|
||||||
|
id: UUID().uuidString,
|
||||||
|
type: .chat,
|
||||||
|
date: Date(),
|
||||||
|
contentType: .attachment(
|
||||||
|
Attachment(
|
||||||
|
type: msgType,
|
||||||
|
localName: localName,
|
||||||
|
thumbnailName: nil,
|
||||||
|
remotePath: nil
|
||||||
|
)
|
||||||
|
),
|
||||||
|
status: .pending,
|
||||||
|
from: roster.bareJid,
|
||||||
|
to: roster.contactBareJid,
|
||||||
|
body: nil,
|
||||||
|
subject: nil,
|
||||||
|
thread: nil,
|
||||||
|
oobUrl: nil
|
||||||
|
)
|
||||||
|
try await message.save()
|
||||||
|
} catch {
|
||||||
|
logIt(.error, "Can't save file for uploading: \(error)")
|
||||||
|
}
|
||||||
|
|
||||||
|
// upload and save
|
||||||
|
upload(message: message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendDocuments(_ data: [Data], _ extensions: [String]) async {
|
func sendDocuments(_ data: [Data], _ extensions: [String]) async {
|
||||||
|
// do {
|
||||||
|
// let newMessageId = UUID().uuidString
|
||||||
|
// let fileId = UUID().uuidString
|
||||||
|
// let localName = "\(newMessageId)_\(fileId).\(ext)"
|
||||||
|
// try await FileStore.shared.storeForUploading(data, localName)
|
||||||
|
//
|
||||||
|
// } catch {
|
||||||
|
// print("error", error)
|
||||||
|
// }
|
||||||
print("documents!", data, extensions)
|
print("documents!", data, extensions)
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
|
@ -86,6 +122,25 @@ extension ConversationStore {
|
||||||
func sendLocation(_ lat: Double, _ lon: Double) async {
|
func sendLocation(_ lat: Double, _ lon: Double) async {
|
||||||
await sendMessage("geo:\(lat),\(lon)")
|
await sendMessage("geo:\(lat),\(lon)")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func upload(message: Message) async {
|
||||||
|
let remotePath: String
|
||||||
|
do {
|
||||||
|
remotePath = try await client.uploadFile(localPath)
|
||||||
|
message.contentType = .attachment(
|
||||||
|
Attachment(
|
||||||
|
type: msgType,
|
||||||
|
localName: localName,
|
||||||
|
thumbnailName: nil,
|
||||||
|
remotePath: remotePath
|
||||||
|
)
|
||||||
|
)
|
||||||
|
try await message.save()
|
||||||
|
} catch {
|
||||||
|
message.status = .error
|
||||||
|
try? await message.save()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ConversationStore {
|
extension ConversationStore {
|
||||||
|
|
|
@ -36,28 +36,28 @@ extension String {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// extension String {
|
extension String {
|
||||||
// var attachmentType: MessageAttachmentType {
|
var attachmentType: AttachmentType {
|
||||||
// let ext = (self as NSString).pathExtension.lowercased()
|
let ext = (self as NSString).pathExtension.lowercased()
|
||||||
//
|
|
||||||
// switch ext {
|
switch ext {
|
||||||
// case "mov", "mp4", "avi":
|
case "mov", "mp4", "avi":
|
||||||
// return .movie
|
return .video
|
||||||
//
|
|
||||||
// case "jpg", "png", "gif":
|
case "jpg", "png", "gif":
|
||||||
// return .image
|
return .image
|
||||||
//
|
|
||||||
// case "mp3", "wav", "m4a":
|
case "mp3", "wav", "m4a":
|
||||||
// return .audio
|
return .audio
|
||||||
//
|
|
||||||
// case "txt", "doc", "pdf":
|
case "txt", "doc", "pdf":
|
||||||
// return .file
|
return .file
|
||||||
//
|
|
||||||
// default:
|
default:
|
||||||
// return .file
|
return .file
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
extension String {
|
extension String {
|
||||||
var firstLetterColor: Color {
|
var firstLetterColor: Color {
|
||||||
|
|
Loading…
Reference in a new issue