Fix not posting status special characters or dropping part of it (Post in JSON now)

This commit is contained in:
Thomas Ricouard 2023-01-15 14:51:09 +01:00
parent 9e569df417
commit d05d9fbfff
4 changed files with 92 additions and 72 deletions

View file

@ -66,18 +66,29 @@ public class Client: ObservableObject, Equatable {
return components.url!
}
private func makeURLRequest(url: URL, httpMethod: String) -> URLRequest {
private func makeURLRequest(url: URL, endpoint: Endpoint, httpMethod: String) -> URLRequest {
var request = URLRequest(url: url)
request.httpMethod = httpMethod
if let oauthToken {
request.setValue("Bearer \(oauthToken.accessToken)", forHTTPHeaderField: "Authorization")
}
if let json = endpoint.jsonValue {
let encoder = JSONEncoder()
encoder.keyEncodingStrategy = .convertToSnakeCase
do {
let jsonData = try encoder.encode(json)
request.httpBody = jsonData
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
} catch {
print("Client Error encoding JSON: \(error.localizedDescription)")
}
}
return request
}
private func makeGet(endpoint: Endpoint) -> URLRequest {
let url = makeURL(endpoint: endpoint)
return makeURLRequest(url: url, httpMethod: "GET")
return makeURLRequest(url: url, endpoint: endpoint, httpMethod: "GET")
}
public func get<Entity: Decodable>(endpoint: Endpoint, forceVersion: Version? = nil) async throws -> Entity {
@ -101,14 +112,14 @@ public class Client: ObservableObject, Equatable {
public func post(endpoint: Endpoint) async throws -> HTTPURLResponse? {
let url = makeURL(endpoint: endpoint)
let request = makeURLRequest(url: url, httpMethod: "POST")
let request = makeURLRequest(url: url, endpoint: endpoint, httpMethod: "POST")
let (_, httpResponse) = try await urlSession.data(for: request)
return httpResponse as? HTTPURLResponse
}
public func patch(endpoint: Endpoint) async throws -> HTTPURLResponse? {
let url = makeURL(endpoint: endpoint)
let request = makeURLRequest(url: url, httpMethod: "PATCH")
let request = makeURLRequest(url: url, endpoint: endpoint, httpMethod: "PATCH")
let (_, httpResponse) = try await urlSession.data(for: request)
return httpResponse as? HTTPURLResponse
}
@ -119,7 +130,7 @@ public class Client: ObservableObject, Equatable {
public func delete(endpoint: Endpoint) async throws -> HTTPURLResponse? {
let url = makeURL(endpoint: endpoint)
let request = makeURLRequest(url: url, httpMethod: "DELETE")
let request = makeURLRequest(url: url, endpoint: endpoint, httpMethod: "DELETE")
let (_, httpResponse) = try await urlSession.data(for: request)
return httpResponse as? HTTPURLResponse
}
@ -128,7 +139,7 @@ public class Client: ObservableObject, Equatable {
method: String,
forceVersion: Version? = nil) async throws -> Entity {
let url = makeURL(endpoint: endpoint, forceVersion: forceVersion)
let request = makeURLRequest(url: url, httpMethod: method)
let request = makeURLRequest(url: url, endpoint: endpoint, httpMethod: method)
let (data, httpResponse) = try await urlSession.data(for: request)
logResponseOnError(httpResponse: httpResponse, data: data)
return try decoder.decode(Entity.self, from: data)
@ -157,7 +168,7 @@ public class Client: ObservableObject, Equatable {
public func makeWebSocketTask(endpoint: Endpoint) -> URLSessionWebSocketTask {
let url = makeURL(scheme: "wss", endpoint: endpoint)
let request = makeURLRequest(url: url, httpMethod: "GET")
let request = makeURLRequest(url: url, endpoint: endpoint, httpMethod: "GET")
return urlSession.webSocketTask(with: request)
}
@ -168,7 +179,7 @@ public class Client: ObservableObject, Equatable {
filename: String,
data: Data) async throws -> Entity {
let url = makeURL(endpoint: endpoint, forceVersion: version)
var request = makeURLRequest(url: url, httpMethod: method)
var request = makeURLRequest(url: url, endpoint: endpoint, httpMethod: method)
let boundary = UUID().uuidString
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
let httpBody = NSMutableData()

View file

@ -3,6 +3,13 @@ import Foundation
public protocol Endpoint {
func path() -> String
func queryItems() -> [URLQueryItem]?
var jsonValue: Encodable? { get }
}
extension Endpoint {
public var jsonValue: Encodable? {
nil
}
}
extension Endpoint {

View file

@ -2,19 +2,8 @@ import Foundation
import Models
public enum Statuses: Endpoint {
case postStatus(status: String,
inReplyTo: String?,
mediaIds: [String]?,
spoilerText: String?,
visibility: Visibility,
pollOptions: [String],
pollVotingFrequency: Bool?,
pollDuration: Int?)
case editStatus(id: String,
status: String,
mediaIds: [String]?,
spoilerText: String?,
visibility: Visibility)
case postStatus(json: StatusData)
case editStatus(id: String, json: StatusData)
case status(id: String)
case context(id: String)
case favourite(id: String)
@ -34,7 +23,7 @@ public enum Statuses: Endpoint {
return "statuses"
case .status(let id):
return "statuses/\(id)"
case .editStatus(let id, _, _, _, _):
case .editStatus(let id, _):
return "statuses/\(id)"
case .context(let id):
return "statuses/\(id)/context"
@ -63,41 +52,6 @@ public enum Statuses: Endpoint {
public func queryItems() -> [URLQueryItem]? {
switch self {
case let .postStatus(status, inReplyTo, mediaIds, spoilerText, visibility, pollOptions, pollVotingFrequency, pollDuration):
var params: [URLQueryItem] = [.init(name: "status", value: status),
.init(name: "visibility", value: visibility.rawValue)]
if let inReplyTo {
params.append(.init(name: "in_reply_to_id", value: inReplyTo))
}
if let mediaIds {
for mediaId in mediaIds {
params.append(.init(name: "media_ids[]", value: mediaId))
}
}
if let spoilerText {
params.append(.init(name: "spoiler_text", value: spoilerText))
}
if !pollOptions.isEmpty, let pollVotingFrequency, let pollDuration {
for option in pollOptions {
params.append(.init(name: "poll[options][]", value: option))
}
params.append(.init(name: "poll[multiple]", value: pollVotingFrequency ? "true" : "false"))
params.append(.init(name: "poll[expires_in]", value: "\(pollDuration)"))
}
return params
case let .editStatus(_, status, mediaIds, spoilerText, visibility):
var params: [URLQueryItem] = [.init(name: "status", value: status),
.init(name: "visibility", value: visibility.rawValue)]
if let mediaIds {
for mediaId in mediaIds {
params.append(.init(name: "media_ids[]", value: mediaId))
}
}
if let spoilerText {
params.append(.init(name: "spoiler_text", value: spoilerText))
}
return params
case let .rebloggedBy(_, maxId):
return makePaginationParam(sinceId: nil, maxId: maxId, mindId: nil)
case let .favouritedBy(_, maxId):
@ -106,4 +60,50 @@ public enum Statuses: Endpoint {
return nil
}
}
public var jsonValue: Encodable? {
switch self {
case let .postStatus(json):
return json
case let .editStatus(_, json):
return json
default:
return nil
}
}
}
public struct StatusData: Encodable {
public let status: String
public let visibility: Visibility
public let inReplyToId: String?
public let spoilerText: String?
public let mediaIds: [String]?
public let poll: PollData?
public struct PollData: Encodable {
public let options: [String]
public let multiple: Bool
public let expires_in: Int
public init(options: [String], multiple: Bool, expires_in: Int) {
self.options = options
self.multiple = multiple
self.expires_in = expires_in
}
}
public init(status: String,
visibility: Visibility,
inReplyToId: String? = nil,
spoilerText: String? = nil,
mediaIds: [String]? = nil,
poll: PollData? = nil) {
self.status = status
self.visibility = visibility
self.inReplyToId = inReplyToId
self.spoilerText = spoilerText
self.mediaIds = mediaIds
self.poll = poll
}
}

View file

@ -95,8 +95,9 @@ public class StatusEditorViewModel: ObservableObject {
selectedRange = .init(location: text.utf16.count, length: 0)
}
private func getPollOptionsForAPI() -> [String] {
pollOptions.filter { !$0.trimmingCharacters(in: .whitespaces).isEmpty }
private func getPollOptionsForAPI() -> [String]? {
let options = pollOptions.filter { !$0.trimmingCharacters(in: .whitespaces).isEmpty }
return options.isEmpty ? nil : options
}
func postStatus() async -> Status? {
@ -104,22 +105,23 @@ public class StatusEditorViewModel: ObservableObject {
do {
isPosting = true
let postStatus: Status?
var pollData: StatusData.PollData?
if let pollOptions = getPollOptionsForAPI() {
pollData = .init(options: pollOptions,
multiple: pollVotingFrequency.canVoteMultipleTimes,
expires_in: pollDuration.rawValue)
}
let data = StatusData(status: statusText.string,
visibility: visibility,
inReplyToId: mode.replyToStatus?.id,
spoilerText: spoilerOn ? spoilerText : nil,
mediaIds: mediasImages.compactMap{ $0.mediaAttachement?.id },
poll: pollData)
switch mode {
case .new, .replyTo, .quote, .mention:
postStatus = try await client.post(endpoint: Statuses.postStatus(status: statusText.string,
inReplyTo: mode.replyToStatus?.id,
mediaIds: mediasImages.compactMap{ $0.mediaAttachement?.id },
spoilerText: spoilerOn ? spoilerText : nil,
visibility: visibility,
pollOptions: getPollOptionsForAPI(),
pollVotingFrequency: pollVotingFrequency.canVoteMultipleTimes,
pollDuration: pollDuration.rawValue))
postStatus = try await client.post(endpoint: Statuses.postStatus(json: data))
case let .edit(status):
postStatus = try await client.put(endpoint: Statuses.editStatus(id: status.id,
status: statusText.string,
mediaIds: mediasImages.compactMap{ $0.mediaAttachement?.id },
spoilerText: spoilerOn ? spoilerText : nil,
visibility: visibility))
postStatus = try await client.put(endpoint: Statuses.editStatus(id: status.id, json: data))
}
generator.notificationOccurred(.success)
isPosting = false