wip
This commit is contained in:
parent
1780360fb4
commit
7666b71ef9
|
@ -6,8 +6,7 @@ enum XMPPAction: Codable {
|
|||
case xmppMessageSendFailed(msgId: String)
|
||||
case xmppMessageSendSuccess(msgId: String)
|
||||
|
||||
case xmppAttachmentUpload(Message)
|
||||
// case xmppAttachmentSlotRequestDone(String) //TODO: ???
|
||||
case xmppAttachmentTryUpload(Message)
|
||||
case xmppAttachmentUploadFailed(msgId: String, reason: String)
|
||||
case xmppAttachmentUploadSuccess(msgId: String, attachmentRemotePath: String)
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ private extension Database {
|
|||
// verbose and debugging in DEBUG builds only.
|
||||
config.publicStatementArguments = true
|
||||
config.prepareDatabase { db in
|
||||
db.trace { print("SQL> \($0)") }
|
||||
db.trace { print("SQL> \($0)\n") }
|
||||
}
|
||||
#endif
|
||||
return config
|
||||
|
|
|
@ -355,6 +355,7 @@ final class DatabaseMiddleware {
|
|||
}
|
||||
.eraseToAnyPublisher()
|
||||
|
||||
// MARK: Sharing
|
||||
case .conversationAction(.sendMediaMessages(let from, let to, let messageIds, let localFilesNames)):
|
||||
return Future<AppAction, Never> { promise in
|
||||
Task(priority: .background) { [weak self] in
|
||||
|
@ -378,6 +379,7 @@ final class DatabaseMiddleware {
|
|||
date: Date(),
|
||||
pending: true,
|
||||
sentError: false,
|
||||
attachmentType: localFilesNames[index].attachmentType,
|
||||
attachmentLocalName: localFilesNames[index]
|
||||
)
|
||||
try database._db.write { db in
|
||||
|
@ -393,6 +395,52 @@ final class DatabaseMiddleware {
|
|||
}
|
||||
.eraseToAnyPublisher()
|
||||
|
||||
case .xmppAction(.xmppAttachmentUploadSuccess(let messageId, let remotePath)):
|
||||
return Future<AppAction, Never> { promise in
|
||||
Task(priority: .background) { [weak self] in
|
||||
guard let database = self?.database else {
|
||||
promise(.success(.databaseAction(.updateAttachmentFailed(id: messageId, reason: L10n.Global.Error.genericDbError)))
|
||||
)
|
||||
return
|
||||
}
|
||||
do {
|
||||
_ = try database._db.write { db in
|
||||
try Message
|
||||
.filter(Column("id") == messageId)
|
||||
.updateAll(db, Column("attachmentRemotePath").set(to: remotePath), Column("pending").set(to: false))
|
||||
}
|
||||
promise(.success(.empty))
|
||||
} catch {
|
||||
promise(.success(.databaseAction(.updateAttachmentFailed(id: messageId, reason: error.localizedDescription)))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
.eraseToAnyPublisher()
|
||||
|
||||
case .xmppAction(.xmppAttachmentUploadFailed(let messageId, _)):
|
||||
return Future<AppAction, Never> { promise in
|
||||
Task(priority: .background) { [weak self] in
|
||||
guard let database = self?.database else {
|
||||
promise(.success(.databaseAction(.updateAttachmentFailed(id: messageId, reason: L10n.Global.Error.genericDbError)))
|
||||
)
|
||||
return
|
||||
}
|
||||
do {
|
||||
_ = try database._db.write { db in
|
||||
try Message
|
||||
.filter(Column("id") == messageId)
|
||||
.updateAll(db, Column("pending").set(to: false), Column("sentError").set(to: true))
|
||||
}
|
||||
promise(.success(.empty))
|
||||
} catch {
|
||||
promise(.success(.databaseAction(.updateAttachmentFailed(id: messageId, reason: error.localizedDescription)))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
.eraseToAnyPublisher()
|
||||
|
||||
default:
|
||||
return Empty().eraseToAnyPublisher()
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@ final class FileMiddleware {
|
|||
promise(.success(.empty))
|
||||
return
|
||||
}
|
||||
|
||||
// for incoming messages with attachments
|
||||
for message in messages where message.attachmentRemotePath != nil && message.attachmentLocalPath == nil {
|
||||
if wSelf.downloadingMessageIDs.contains(message.id) {
|
||||
continue
|
||||
|
@ -25,6 +27,27 @@ final class FileMiddleware {
|
|||
store.dispatch(.fileAction(.downloadAttachmentFile(messageId: message.id, attachmentRemotePath: message.attachmentRemotePath!)))
|
||||
}
|
||||
}
|
||||
|
||||
// for outgoing messages with shared attachments
|
||||
for message in messages where message.attachmentLocalPath != nil && message.attachmentRemotePath == nil && message.pending {
|
||||
if wSelf.downloadingMessageIDs.contains(message.id) {
|
||||
continue
|
||||
}
|
||||
wSelf.downloadingMessageIDs.insert(message.id)
|
||||
DispatchQueue.main.async {
|
||||
store.dispatch(.xmppAction(.xmppAttachmentTryUpload(message)))
|
||||
}
|
||||
}
|
||||
|
||||
// for outgoing messages with shared attachments which are already uploaded
|
||||
// but have no thumbnail
|
||||
for message in messages where message.attachmentLocalName != nil && message.attachmentRemotePath != nil && message.attachmentThumbnailName == nil && !message.pending && !message.sentError {
|
||||
DispatchQueue.main.async {
|
||||
// swiftlint:disable:next force_unwrapping
|
||||
store.dispatch(.fileAction(.createAttachmentThumbnail(messageId: message.id, localName: message.attachmentLocalName!)))
|
||||
}
|
||||
}
|
||||
|
||||
promise(.success(.empty))
|
||||
}.eraseToAnyPublisher()
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ import UIKit
|
|||
final class SharingMiddleware {
|
||||
static let shared = SharingMiddleware()
|
||||
|
||||
// swiftlint:disable:next function_body_length
|
||||
func middleware(state: AppState, action: AppAction) -> AnyPublisher<AppAction, Never> {
|
||||
switch action {
|
||||
// MARK: - Camera and Gallery Access
|
||||
|
|
|
@ -6,6 +6,7 @@ final class XMPPMiddleware {
|
|||
static let shared = XMPPMiddleware()
|
||||
private let service = XMPPService(manager: Database.shared)
|
||||
private var cancellables: Set<AnyCancellable> = []
|
||||
private var uploadingMessageIDs = ThreadSafeSet<String>()
|
||||
|
||||
private init() {
|
||||
service.clientState.sink { client, state in
|
||||
|
@ -100,14 +101,20 @@ final class XMPPMiddleware {
|
|||
}
|
||||
.eraseToAnyPublisher()
|
||||
|
||||
case .xmppAction(.xmppAttachmentUpload(let message)):
|
||||
case .xmppAction(.xmppAttachmentTryUpload(let message)):
|
||||
return Future<AppAction, Never> { [weak self] promise in
|
||||
DispatchQueue.global().async {
|
||||
self?.service.uploadAttachment(message: message) { done, remotePath in
|
||||
if done {
|
||||
promise(.success(.xmppAction(.xmppAttachmentUploadSuccess(msgId: message.id, attachmentRemotePath: remotePath))))
|
||||
} else {
|
||||
promise(.success(.xmppAction(.xmppAttachmentUploadFailed(msgId: message.id, reason: "Upload failed"))))
|
||||
if self?.uploadingMessageIDs.contains(message.id) ?? false {
|
||||
return promise(.success(.empty))
|
||||
} else {
|
||||
self?.uploadingMessageIDs.insert(message.id)
|
||||
DispatchQueue.global().async {
|
||||
self?.service.uploadAttachment(message: message) { error, remotePath in
|
||||
self?.uploadingMessageIDs.remove(message.id)
|
||||
if let error {
|
||||
promise(.success(.xmppAction(.xmppAttachmentUploadFailed(msgId: message.id, reason: error.localizedDescription))))
|
||||
} else {
|
||||
promise(.success(.xmppAction(.xmppAttachmentUploadSuccess(msgId: message.id, attachmentRemotePath: remotePath))))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -143,6 +143,10 @@ final class XMPPService: ObservableObject {
|
|||
completion(XMPPError.bad_request("No such file"), "")
|
||||
return
|
||||
}
|
||||
guard let chat = client.module(MessageModule.self).chatManager.chat(for: client.context, with: BareJID(to)) else {
|
||||
completion(XMPPError.bad_request("No such chat"), "")
|
||||
return
|
||||
}
|
||||
|
||||
let httpModule = client.module(HttpFileUploadModule.self)
|
||||
httpModule.findHttpUploadComponent { res in
|
||||
|
@ -173,73 +177,28 @@ final class XMPPService: ObservableObject {
|
|||
if code == 200 {
|
||||
completion(XMPPError.bad_request("Invalid response code"), "")
|
||||
} else {
|
||||
completion(nil, slot.getUri.absoluteString)
|
||||
let mesg = chat.createMessage(text: slot.getUri.absoluteString, id: message.id)
|
||||
mesg.oob = slot.getUri.absoluteString
|
||||
chat.send(message: mesg) { res in
|
||||
switch res {
|
||||
case .success:
|
||||
completion(nil, slot.getUri.absoluteString)
|
||||
|
||||
case .failure:
|
||||
completion(XMPPError.bad_request("File uploaded, but message sent failed"), slot.getUri.absoluteString)
|
||||
}
|
||||
}
|
||||
}
|
||||
}.resume()
|
||||
|
||||
case .failure:
|
||||
completion(XMPPError.bad_request("Upload failed"), "")
|
||||
case .failure(let error):
|
||||
completion(error, "")
|
||||
}
|
||||
}
|
||||
|
||||
case .failure:
|
||||
completion(XMPPError.bad_request("No such component"), "")
|
||||
case .failure(let error):
|
||||
completion(error, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// open class HTTPFileUploadHelper {
|
||||
//
|
||||
// private static let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "HTTPFileUploadHelper")
|
||||
//
|
||||
// public static func upload(for context: Context, filename: String, inputStream: InputStream, filesize size: Int, mimeType: String, delegate: URLSessionDelegate?, completionHandler: @escaping (Result<URL,ShareError>)->Void) {
|
||||
// let httpUploadModule = context.module(.httpFileUpload);
|
||||
// httpUploadModule.findHttpUploadComponent(completionHandler: { result in
|
||||
// switch result {
|
||||
// case .success(let components):
|
||||
// guard let component = components.first(where: { $0.maxSize > size }) else {
|
||||
// completionHandler(.failure(.fileTooBig));
|
||||
// return;
|
||||
// }
|
||||
// httpUploadModule.requestUploadSlot(componentJid: component.jid, filename: filename, size: size, contentType: mimeType, completionHandler: { result in
|
||||
// switch result {
|
||||
// case .success(let slot):
|
||||
// var request = URLRequest(url: slot.putUri);
|
||||
// slot.putHeaders.forEach({ (k,v) in
|
||||
// request.addValue(v, forHTTPHeaderField: k);
|
||||
// });
|
||||
// request.httpMethod = "PUT";
|
||||
// request.httpBodyStream = inputStream;
|
||||
// request.addValue(String(size), forHTTPHeaderField: "Content-Length");
|
||||
// request.addValue(mimeType, forHTTPHeaderField: "Content-Type");
|
||||
// let session = URLSession(configuration: URLSessionConfiguration.default, delegate: delegate, delegateQueue: OperationQueue.main);
|
||||
// session.dataTask(with: request) { (data, response, error) in
|
||||
// let code = (response as? HTTPURLResponse)?.statusCode ?? 500;
|
||||
// guard error == nil && (code == 200 || code == 201) else {
|
||||
// logger.error("upload of file \(filename) failed, error: \(error as Any), response: \(response as Any)");
|
||||
// completionHandler(.failure(.httpError));
|
||||
// return;
|
||||
// }
|
||||
// if code == 200 {
|
||||
// completionHandler(.failure(.invalidResponseCode(url: slot.getUri)));
|
||||
// } else {
|
||||
// completionHandler(.success(slot.getUri));
|
||||
// }
|
||||
// }.resume();
|
||||
// case .failure(let error):
|
||||
// logger.error("upload of file \(filename) failed, upload component returned error: \(error as Any)");
|
||||
// completionHandler(.failure(.unknownError));
|
||||
// }
|
||||
// });
|
||||
// case .failure(let error):
|
||||
// completionHandler(.failure(error.errorCondition == .item_not_found ? .notSupported : .unknownError));
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// public enum UploadResult {
|
||||
// case success(url: URL, filesize: Int, mimeType: String?)
|
||||
// case failure(ShareError)
|
||||
// }
|
||||
// }
|
||||
|
|
Loading…
Reference in a new issue