Fix not posting status special characters or dropping part of it (Post in JSON now)
This commit is contained in:
parent
9e569df417
commit
d05d9fbfff
|
@ -66,18 +66,29 @@ public class Client: ObservableObject, Equatable {
|
||||||
return components.url!
|
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)
|
var request = URLRequest(url: url)
|
||||||
request.httpMethod = httpMethod
|
request.httpMethod = httpMethod
|
||||||
if let oauthToken {
|
if let oauthToken {
|
||||||
request.setValue("Bearer \(oauthToken.accessToken)", forHTTPHeaderField: "Authorization")
|
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
|
return request
|
||||||
}
|
}
|
||||||
|
|
||||||
private func makeGet(endpoint: Endpoint) -> URLRequest {
|
private func makeGet(endpoint: Endpoint) -> URLRequest {
|
||||||
let url = makeURL(endpoint: endpoint)
|
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 {
|
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? {
|
public func post(endpoint: Endpoint) async throws -> HTTPURLResponse? {
|
||||||
let url = makeURL(endpoint: endpoint)
|
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)
|
let (_, httpResponse) = try await urlSession.data(for: request)
|
||||||
return httpResponse as? HTTPURLResponse
|
return httpResponse as? HTTPURLResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
public func patch(endpoint: Endpoint) async throws -> HTTPURLResponse? {
|
public func patch(endpoint: Endpoint) async throws -> HTTPURLResponse? {
|
||||||
let url = makeURL(endpoint: endpoint)
|
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)
|
let (_, httpResponse) = try await urlSession.data(for: request)
|
||||||
return httpResponse as? HTTPURLResponse
|
return httpResponse as? HTTPURLResponse
|
||||||
}
|
}
|
||||||
|
@ -119,7 +130,7 @@ public class Client: ObservableObject, Equatable {
|
||||||
|
|
||||||
public func delete(endpoint: Endpoint) async throws -> HTTPURLResponse? {
|
public func delete(endpoint: Endpoint) async throws -> HTTPURLResponse? {
|
||||||
let url = makeURL(endpoint: endpoint)
|
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)
|
let (_, httpResponse) = try await urlSession.data(for: request)
|
||||||
return httpResponse as? HTTPURLResponse
|
return httpResponse as? HTTPURLResponse
|
||||||
}
|
}
|
||||||
|
@ -128,7 +139,7 @@ public class Client: ObservableObject, Equatable {
|
||||||
method: String,
|
method: String,
|
||||||
forceVersion: Version? = nil) async throws -> Entity {
|
forceVersion: Version? = nil) async throws -> Entity {
|
||||||
let url = makeURL(endpoint: endpoint, forceVersion: forceVersion)
|
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)
|
let (data, httpResponse) = try await urlSession.data(for: request)
|
||||||
logResponseOnError(httpResponse: httpResponse, data: data)
|
logResponseOnError(httpResponse: httpResponse, data: data)
|
||||||
return try decoder.decode(Entity.self, from: data)
|
return try decoder.decode(Entity.self, from: data)
|
||||||
|
@ -157,7 +168,7 @@ public class Client: ObservableObject, Equatable {
|
||||||
|
|
||||||
public func makeWebSocketTask(endpoint: Endpoint) -> URLSessionWebSocketTask {
|
public func makeWebSocketTask(endpoint: Endpoint) -> URLSessionWebSocketTask {
|
||||||
let url = makeURL(scheme: "wss", endpoint: endpoint)
|
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)
|
return urlSession.webSocketTask(with: request)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,7 +179,7 @@ public class Client: ObservableObject, Equatable {
|
||||||
filename: String,
|
filename: String,
|
||||||
data: Data) async throws -> Entity {
|
data: Data) async throws -> Entity {
|
||||||
let url = makeURL(endpoint: endpoint, forceVersion: version)
|
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
|
let boundary = UUID().uuidString
|
||||||
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
|
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
|
||||||
let httpBody = NSMutableData()
|
let httpBody = NSMutableData()
|
||||||
|
|
|
@ -3,6 +3,13 @@ import Foundation
|
||||||
public protocol Endpoint {
|
public protocol Endpoint {
|
||||||
func path() -> String
|
func path() -> String
|
||||||
func queryItems() -> [URLQueryItem]?
|
func queryItems() -> [URLQueryItem]?
|
||||||
|
var jsonValue: Encodable? { get }
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Endpoint {
|
||||||
|
public var jsonValue: Encodable? {
|
||||||
|
nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Endpoint {
|
extension Endpoint {
|
||||||
|
|
|
@ -2,19 +2,8 @@ import Foundation
|
||||||
import Models
|
import Models
|
||||||
|
|
||||||
public enum Statuses: Endpoint {
|
public enum Statuses: Endpoint {
|
||||||
case postStatus(status: String,
|
case postStatus(json: StatusData)
|
||||||
inReplyTo: String?,
|
case editStatus(id: String, json: StatusData)
|
||||||
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 status(id: String)
|
case status(id: String)
|
||||||
case context(id: String)
|
case context(id: String)
|
||||||
case favourite(id: String)
|
case favourite(id: String)
|
||||||
|
@ -34,7 +23,7 @@ public enum Statuses: Endpoint {
|
||||||
return "statuses"
|
return "statuses"
|
||||||
case .status(let id):
|
case .status(let id):
|
||||||
return "statuses/\(id)"
|
return "statuses/\(id)"
|
||||||
case .editStatus(let id, _, _, _, _):
|
case .editStatus(let id, _):
|
||||||
return "statuses/\(id)"
|
return "statuses/\(id)"
|
||||||
case .context(let id):
|
case .context(let id):
|
||||||
return "statuses/\(id)/context"
|
return "statuses/\(id)/context"
|
||||||
|
@ -63,41 +52,6 @@ public enum Statuses: Endpoint {
|
||||||
|
|
||||||
public func queryItems() -> [URLQueryItem]? {
|
public func queryItems() -> [URLQueryItem]? {
|
||||||
switch self {
|
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):
|
case let .rebloggedBy(_, maxId):
|
||||||
return makePaginationParam(sinceId: nil, maxId: maxId, mindId: nil)
|
return makePaginationParam(sinceId: nil, maxId: maxId, mindId: nil)
|
||||||
case let .favouritedBy(_, maxId):
|
case let .favouritedBy(_, maxId):
|
||||||
|
@ -106,4 +60,50 @@ public enum Statuses: Endpoint {
|
||||||
return nil
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,8 +95,9 @@ public class StatusEditorViewModel: ObservableObject {
|
||||||
selectedRange = .init(location: text.utf16.count, length: 0)
|
selectedRange = .init(location: text.utf16.count, length: 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func getPollOptionsForAPI() -> [String] {
|
private func getPollOptionsForAPI() -> [String]? {
|
||||||
pollOptions.filter { !$0.trimmingCharacters(in: .whitespaces).isEmpty }
|
let options = pollOptions.filter { !$0.trimmingCharacters(in: .whitespaces).isEmpty }
|
||||||
|
return options.isEmpty ? nil : options
|
||||||
}
|
}
|
||||||
|
|
||||||
func postStatus() async -> Status? {
|
func postStatus() async -> Status? {
|
||||||
|
@ -104,22 +105,23 @@ public class StatusEditorViewModel: ObservableObject {
|
||||||
do {
|
do {
|
||||||
isPosting = true
|
isPosting = true
|
||||||
let postStatus: Status?
|
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 {
|
switch mode {
|
||||||
case .new, .replyTo, .quote, .mention:
|
case .new, .replyTo, .quote, .mention:
|
||||||
postStatus = try await client.post(endpoint: Statuses.postStatus(status: statusText.string,
|
postStatus = try await client.post(endpoint: Statuses.postStatus(json: data))
|
||||||
inReplyTo: mode.replyToStatus?.id,
|
|
||||||
mediaIds: mediasImages.compactMap{ $0.mediaAttachement?.id },
|
|
||||||
spoilerText: spoilerOn ? spoilerText : nil,
|
|
||||||
visibility: visibility,
|
|
||||||
pollOptions: getPollOptionsForAPI(),
|
|
||||||
pollVotingFrequency: pollVotingFrequency.canVoteMultipleTimes,
|
|
||||||
pollDuration: pollDuration.rawValue))
|
|
||||||
case let .edit(status):
|
case let .edit(status):
|
||||||
postStatus = try await client.put(endpoint: Statuses.editStatus(id: status.id,
|
postStatus = try await client.put(endpoint: Statuses.editStatus(id: status.id, json: data))
|
||||||
status: statusText.string,
|
|
||||||
mediaIds: mediasImages.compactMap{ $0.mediaAttachement?.id },
|
|
||||||
spoilerText: spoilerOn ? spoilerText : nil,
|
|
||||||
visibility: visibility))
|
|
||||||
}
|
}
|
||||||
generator.notificationOccurred(.success)
|
generator.notificationOccurred(.success)
|
||||||
isPosting = false
|
isPosting = false
|
||||||
|
|
Loading…
Reference in New Issue