diff --git a/Tusker/Screens/Compose/ComposeView.swift b/Tusker/Screens/Compose/ComposeView.swift index 604acdefb5..e6ac0e7a02 100644 --- a/Tusker/Screens/Compose/ComposeView.swift +++ b/Tusker/Screens/Compose/ComposeView.swift @@ -229,36 +229,57 @@ struct ComposeView: View { private func uploadAttachments(_ completion: @escaping (Result<[Attachment], AttachmentUploadError>) -> Void) { let group = DispatchGroup() - var anyFailed = false - var uploadedAttachments = [Result?]() + var attachmentDatas = [(Data, String)?]() for (index, compAttachment) in draft.attachments.enumerated() { group.enter() - uploadedAttachments.append(nil) + attachmentDatas.append(nil) compAttachment.data.getData { (data, mimeType) in postProgress += 1 + attachmentDatas[index] = (data, mimeType) + group.leave() + } + } + + group.notify(queue: .global(qos: .userInitiated)) { + + var anyFailed = false + var uploadedAttachments = [Result?]() + + // Mastodon does not respect the order of the `media_ids` parameter in the create post request, + // it determines attachment order by which was uploaded first. Since the upload attachment request + // does not include any timestamp data, and requests may arrive at the server out-of-order, + // attachments need to be uploaded serially in order to ensure the order of attachments in the + // posted status reflects order the user set. + // Pleroma does respect the order of the `media_ids` parameter. + + for (index, (data, mimeType)) in attachmentDatas.map(\.unsafelyUnwrapped).enumerated() { + group.enter() + + let compAttachment = draft.attachments[index] let formAttachment = FormAttachment(mimeType: mimeType, data: data, fileName: "file") let request = Client.upload(attachment: formAttachment, description: compAttachment.attachmentDescription) self.mastodonController.run(request) { (response) in switch response { case let .failure(error): - uploadedAttachments[index] = .failure(error) + uploadedAttachments.append(.failure(error)) anyFailed = true case let .success(attachment, _): - postProgress += 1 - uploadedAttachments[index] = .success(attachment) + self.postProgress += 1 + uploadedAttachments.append(.success(attachment)) } group.leave() } + + group.wait() } - } - - group.notify(queue: .main) { + + if anyFailed { let errors = uploadedAttachments.map { (result) -> Error? in if case let .failure(error) = result { @@ -274,6 +295,7 @@ struct ComposeView: View { } completion(.success(uploadedAttachments)) } + } } }