Improve compose posting error messages
This commit is contained in:
parent
e19a6528ad
commit
7c4bbfd730
|
@ -50,22 +50,22 @@ public class Client {
|
||||||
|
|
||||||
let task = session.dataTask(with: request) { data, response, error in
|
let task = session.dataTask(with: request) { data, response, error in
|
||||||
if let error = error {
|
if let error = error {
|
||||||
completion(.failure(error))
|
completion(.failure(.networkError(error)))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard let data = data,
|
guard let data = data,
|
||||||
let response = response as? HTTPURLResponse else {
|
let response = response as? HTTPURLResponse else {
|
||||||
completion(.failure(Error.invalidResponse))
|
completion(.failure(.invalidResponse))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard response.statusCode == 200 else {
|
guard response.statusCode == 200 else {
|
||||||
let mastodonError = try? self.decoder.decode(MastodonError.self, from: data)
|
let mastodonError = try? self.decoder.decode(MastodonError.self, from: data)
|
||||||
let error = mastodonError.flatMap { Error.mastodonError($0.description) } ?? Error.unknownError
|
let error: Error = mastodonError.flatMap { .mastodonError($0.description) } ?? .unexpectedStatus(response.statusCode)
|
||||||
completion(.failure(error))
|
completion(.failure(error))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard let result = try? self.decoder.decode(Result.self, from: data) else {
|
guard let result = try? self.decoder.decode(Result.self, from: data) else {
|
||||||
completion(.failure(Error.invalidModel))
|
completion(.failure(.invalidModel))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let pagination = response.allHeaderFields["Link"].flatMap { $0 as? String }.flatMap(Pagination.init)
|
let pagination = response.allHeaderFields["Link"].flatMap { $0 as? String }.flatMap(Pagination.init)
|
||||||
|
@ -315,7 +315,8 @@ public class Client {
|
||||||
|
|
||||||
extension Client {
|
extension Client {
|
||||||
public enum Error: LocalizedError {
|
public enum Error: LocalizedError {
|
||||||
case unknownError
|
case networkError(Swift.Error)
|
||||||
|
case unexpectedStatus(Int)
|
||||||
case invalidRequest
|
case invalidRequest
|
||||||
case invalidResponse
|
case invalidResponse
|
||||||
case invalidModel
|
case invalidModel
|
||||||
|
@ -323,8 +324,13 @@ extension Client {
|
||||||
|
|
||||||
public var localizedDescription: String {
|
public var localizedDescription: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .unknownError:
|
case .networkError(let error):
|
||||||
return "Unknown Error"
|
return "Network Error: \(error.localizedDescription)"
|
||||||
|
// todo: support more status codes
|
||||||
|
case .unexpectedStatus(413):
|
||||||
|
return "HTTP 413: Payload Too Large"
|
||||||
|
case .unexpectedStatus(let code):
|
||||||
|
return "HTTP Code \(code)"
|
||||||
case .invalidRequest:
|
case .invalidRequest:
|
||||||
return "Invalid Request"
|
return "Invalid Request"
|
||||||
case .invalidResponse:
|
case .invalidResponse:
|
||||||
|
|
|
@ -10,5 +10,5 @@ import Foundation
|
||||||
|
|
||||||
public enum Response<Result: Decodable> {
|
public enum Response<Result: Decodable> {
|
||||||
case success(Result, Pagination?)
|
case success(Result, Pagination?)
|
||||||
case failure(Error)
|
case failure(Client.Error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,16 +22,16 @@ public class InstanceSelector {
|
||||||
let request = URLRequest(url: url)
|
let request = URLRequest(url: url)
|
||||||
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
|
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
|
||||||
if let error = error {
|
if let error = error {
|
||||||
completion(.failure(error))
|
completion(.failure(.networkError(error)))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard let data = data,
|
guard let data = data,
|
||||||
let response = response as? HTTPURLResponse else {
|
let response = response as? HTTPURLResponse else {
|
||||||
completion(.failure(Client.Error.invalidResponse))
|
completion(.failure(.invalidResponse))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard response.statusCode == 200 else {
|
guard response.statusCode == 200 else {
|
||||||
completion(.failure(Client.Error.unknownError))
|
completion(.failure(.unexpectedStatus(response.statusCode)))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard let result = try? decoder.decode([Instance].self, from: data) else {
|
guard let result = try? decoder.decode([Instance].self, from: data) else {
|
||||||
|
|
|
@ -12,14 +12,14 @@ import Combine
|
||||||
|
|
||||||
struct ComposeView: View {
|
struct ComposeView: View {
|
||||||
@ObservedObject var draft: Draft
|
@ObservedObject var draft: Draft
|
||||||
|
|
||||||
@EnvironmentObject var mastodonController: MastodonController
|
@EnvironmentObject var mastodonController: MastodonController
|
||||||
@EnvironmentObject var uiState: ComposeUIState
|
@EnvironmentObject var uiState: ComposeUIState
|
||||||
@State var isPosting = false
|
|
||||||
@State var postProgress: Double = 0
|
@State private var isPosting = false
|
||||||
@State var postTotalProgress: Double = 0
|
@State private var postProgress: Double = 0
|
||||||
@State var isShowingPostErrorAlert = false
|
@State private var postTotalProgress: Double = 0
|
||||||
@State var postError: Error?
|
@State private var isShowingPostErrorAlert = false
|
||||||
|
@State private var postError: PostError?
|
||||||
|
|
||||||
private let stackPadding: CGFloat = 8
|
private let stackPadding: CGFloat = 8
|
||||||
|
|
||||||
|
@ -191,6 +191,9 @@ struct ComposeView: View {
|
||||||
case let .failure(error):
|
case let .failure(error):
|
||||||
self.isShowingPostErrorAlert = true
|
self.isShowingPostErrorAlert = true
|
||||||
self.postError = error
|
self.postError = error
|
||||||
|
self.postProgress = 0
|
||||||
|
self.postTotalProgress = 0
|
||||||
|
self.isPosting = false
|
||||||
|
|
||||||
case let .success(uploadedAttachments):
|
case let .success(uploadedAttachments):
|
||||||
let request = Client.createStatus(text: draft.text,
|
let request = Client.createStatus(text: draft.text,
|
||||||
|
@ -222,7 +225,7 @@ struct ComposeView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func uploadAttachments(_ completion: @escaping (Result<[Attachment], Error>) -> Void) {
|
private func uploadAttachments(_ completion: @escaping (Result<[Attachment], AttachmentUploadError>) -> Void) {
|
||||||
let group = DispatchGroup()
|
let group = DispatchGroup()
|
||||||
|
|
||||||
var anyFailed = false
|
var anyFailed = false
|
||||||
|
@ -239,13 +242,13 @@ struct ComposeView: View {
|
||||||
let formAttachment = FormAttachment(mimeType: mimeType, data: data, fileName: "file")
|
let formAttachment = FormAttachment(mimeType: mimeType, data: data, fileName: "file")
|
||||||
let request = Client.upload(attachment: formAttachment, description: compAttachment.attachmentDescription)
|
let request = Client.upload(attachment: formAttachment, description: compAttachment.attachmentDescription)
|
||||||
self.mastodonController.run(request) { (response) in
|
self.mastodonController.run(request) { (response) in
|
||||||
postProgress += 1
|
|
||||||
|
|
||||||
switch response {
|
switch response {
|
||||||
case let .failure(error):
|
case let .failure(error):
|
||||||
uploadedAttachments[index] = .failure(error)
|
uploadedAttachments[index] = .failure(error)
|
||||||
anyFailed = true
|
anyFailed = true
|
||||||
|
|
||||||
case let .success(attachment, _):
|
case let .success(attachment, _):
|
||||||
|
postProgress += 1
|
||||||
uploadedAttachments[index] = .success(attachment)
|
uploadedAttachments[index] = .success(attachment)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -274,14 +277,37 @@ struct ComposeView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate struct AttachmentUploadError: LocalizedError {
|
fileprivate protocol PostError: LocalizedError {}
|
||||||
|
|
||||||
|
extension PostError {
|
||||||
|
var localizedDescription: String {
|
||||||
|
if let self = self as? Client.Error {
|
||||||
|
return self.localizedDescription
|
||||||
|
} else if let self = self as? AttachmentUploadError {
|
||||||
|
return self.localizedDescription
|
||||||
|
} else {
|
||||||
|
return "Unknown Error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Client.Error: PostError {}
|
||||||
|
|
||||||
|
fileprivate struct AttachmentUploadError: PostError {
|
||||||
let errors: [Error?]
|
let errors: [Error?]
|
||||||
|
|
||||||
var localizedDescription: String {
|
var localizedDescription: String {
|
||||||
return errors.enumerated().compactMap { (index, error) -> String? in
|
return errors.enumerated().compactMap { (index, error) -> String? in
|
||||||
guard let error = error else { return nil }
|
guard let error = error else { return nil }
|
||||||
return "Attachment \(index + 1): \(error.localizedDescription)"
|
let description: String
|
||||||
}.joined(separator: ", ")
|
// need to downcast to use more specific localizedDescription impl from Pachyderm
|
||||||
|
if let error = error as? Client.Error {
|
||||||
|
description = error.localizedDescription
|
||||||
|
} else {
|
||||||
|
description = error.localizedDescription
|
||||||
|
}
|
||||||
|
return "Attachment \(index + 1): \(description)"
|
||||||
|
}.joined(separator: ",\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,8 @@ struct WrappedProgressView: UIViewRepresentable {
|
||||||
func updateUIView(_ uiView: UIProgressView, context: Context) {
|
func updateUIView(_ uiView: UIProgressView, context: Context) {
|
||||||
if total > 0 {
|
if total > 0 {
|
||||||
uiView.setProgress(Float(value / total), animated: true)
|
uiView.setProgress(Float(value / total), animated: true)
|
||||||
|
} else {
|
||||||
|
uiView.setProgress(0, animated: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue