forked from shadowfacts/Tusker
215 lines
9.3 KiB
Swift
215 lines
9.3 KiB
Swift
//
|
|
// Status.swift
|
|
// Pachyderm
|
|
//
|
|
// Created by Shadowfacts on 9/9/18.
|
|
// Copyright © 2018 Shadowfacts. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
import WebURL
|
|
|
|
public final class Status: StatusProtocol, Decodable, Sendable {
|
|
/// The pseudo-visibility used by instance types (Akkoma) that overload the visibility for local-only posts.
|
|
public static let localPostVisibility: String = "local"
|
|
|
|
public let id: String
|
|
public let uri: String
|
|
public let url: WebURL?
|
|
public let account: Account
|
|
public let inReplyToID: String?
|
|
public let inReplyToAccountID: String?
|
|
public let reblog: Status?
|
|
public let content: String
|
|
public let createdAt: Date
|
|
public let emojis: [Emoji]
|
|
// TODO: missing from pleroma
|
|
// public let repliesCount: Int
|
|
public let reblogsCount: Int
|
|
public let favouritesCount: Int
|
|
public let reblogged: Bool?
|
|
public let favourited: Bool?
|
|
public let muted: Bool?
|
|
public let sensitive: Bool
|
|
public let spoilerText: String
|
|
public let visibility: Visibility
|
|
public let attachments: [Attachment]
|
|
public let mentions: [Mention]
|
|
public let hashtags: [Hashtag]
|
|
public let application: Application?
|
|
public let language: String?
|
|
public let pinned: Bool?
|
|
public let bookmarked: Bool?
|
|
public let card: Card?
|
|
public let poll: Poll?
|
|
// Hometown, Glitch only
|
|
public let localOnly: Bool?
|
|
public let editedAt: Date?
|
|
|
|
public var applicationName: String? { application?.name }
|
|
|
|
public init(from decoder: Decoder) throws {
|
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
self.id = try container.decode(String.self, forKey: .id)
|
|
self.uri = try container.decode(String.self, forKey: .uri)
|
|
do {
|
|
self.url = try container.decodeIfPresent(WebURL.self, forKey: .url)
|
|
} catch {
|
|
let s = try? container.decode(String.self, forKey: .url)
|
|
if s == "" {
|
|
self.url = nil
|
|
} else {
|
|
throw DecodingError.dataCorrupted(.init(codingPath: container.codingPath + [CodingKeys.url], debugDescription: "Could not decode URL '\(s ?? "<failed to decode string>")'", underlyingError: error))
|
|
}
|
|
}
|
|
self.account = try container.decode(Account.self, forKey: .account)
|
|
self.inReplyToID = try container.decodeIfPresent(String.self, forKey: .inReplyToID)
|
|
self.inReplyToAccountID = try container.decodeIfPresent(String.self, forKey: .inReplyToAccountID)
|
|
self.reblog = try container.decodeIfPresent(Status.self, forKey: .reblog)
|
|
// pixelfed statuses may have null content
|
|
self.content = try container.decodeIfPresent(String.self, forKey: .content) ?? ""
|
|
self.createdAt = try container.decode(Date.self, forKey: .createdAt)
|
|
self.emojis = try container.decodeIfPresent([Emoji].self, forKey: .emojis) ?? []
|
|
self.reblogsCount = try container.decode(Int.self, forKey: .reblogsCount)
|
|
self.favouritesCount = try container.decode(Int.self, forKey: .favouritesCount)
|
|
self.reblogged = try container.decodeIfPresent(Bool.self, forKey: .reblogged)
|
|
self.favourited = try container.decodeIfPresent(Bool.self, forKey: .favourited)
|
|
self.muted = try container.decodeIfPresent(Bool.self, forKey: .muted)
|
|
self.sensitive = try container.decode(Bool.self, forKey: .sensitive)
|
|
self.spoilerText = try container.decode(String.self, forKey: .spoilerText)
|
|
if let visibility = try? container.decode(Visibility.self, forKey: .visibility) {
|
|
self.visibility = visibility
|
|
self.localOnly = try container.decodeIfPresent(Bool.self, forKey: .localOnly)
|
|
} else if let s = try? container.decode(String.self, forKey: .visibility),
|
|
s == Status.localPostVisibility {
|
|
// hacky workaround for #332, akkoma describes local posts with a separate visibility
|
|
self.visibility = .public
|
|
self.localOnly = true
|
|
} else {
|
|
throw DecodingError.dataCorruptedError(forKey: .visibility, in: container, debugDescription: "Could not decode visibility")
|
|
}
|
|
self.attachments = try container.decode([Attachment].self, forKey: .attachments)
|
|
self.mentions = try container.decode([Mention].self, forKey: .mentions)
|
|
self.hashtags = try container.decode([Hashtag].self, forKey: .hashtags)
|
|
self.application = try container.decodeIfPresent(Application.self, forKey: .application)
|
|
self.language = try container.decodeIfPresent(String.self, forKey: .language)
|
|
self.pinned = try container.decodeIfPresent(Bool.self, forKey: .pinned)
|
|
self.bookmarked = try container.decodeIfPresent(Bool.self, forKey: .bookmarked)
|
|
self.card = try container.decodeIfPresent(Card.self, forKey: .card)
|
|
self.poll = try container.decodeIfPresent(Poll.self, forKey: .poll)
|
|
self.editedAt = try container.decodeIfPresent(Date.self, forKey: .editedAt)
|
|
}
|
|
|
|
public static func getContext(_ statusID: String) -> Request<ConversationContext> {
|
|
return Request<ConversationContext>(method: .get, path: "/api/v1/statuses/\(statusID)/context")
|
|
}
|
|
|
|
public static func getCard(_ status: Status) -> Request<Card> {
|
|
return Request<Card>(method: .get, path: "/api/v1/statuses/\(status.id)/card")
|
|
}
|
|
|
|
public static func getFavourites(_ statusID: String, range: RequestRange = .default) -> Request<[Account]> {
|
|
var request = Request<[Account]>(method: .get, path: "/api/v1/statuses/\(statusID)/favourited_by")
|
|
request.range = range
|
|
return request
|
|
}
|
|
|
|
public static func getReblogs(_ statusID: String, range: RequestRange = .default) -> Request<[Account]> {
|
|
var request = Request<[Account]>(method: .get, path: "/api/v1/statuses/\(statusID)/reblogged_by")
|
|
request.range = range
|
|
return request
|
|
}
|
|
|
|
public static func delete(_ statusID: String) -> Request<Empty> {
|
|
return Request<Empty>(method: .delete, path: "/api/v1/statuses/\(statusID)")
|
|
}
|
|
|
|
public static func reblog(_ statusID: String, visibility: Visibility? = nil) -> Request<Status> {
|
|
var params: [Parameter] = []
|
|
if let visibility {
|
|
assert([.public, .unlisted, .private].contains(visibility))
|
|
params.append("visibility" => visibility.rawValue)
|
|
}
|
|
return Request<Status>(method: .post, path: "/api/v1/statuses/\(statusID)/reblog", queryParameters: params)
|
|
}
|
|
|
|
public static func unreblog(_ statusID: String) -> Request<Status> {
|
|
return Request<Status>(method: .post, path: "/api/v1/statuses/\(statusID)/unreblog")
|
|
}
|
|
|
|
public static func favourite(_ statusID: String) -> Request<Status> {
|
|
return Request<Status>(method: .post, path: "/api/v1/statuses/\(statusID)/favourite")
|
|
}
|
|
|
|
public static func unfavourite(_ statusID: String) -> Request<Status> {
|
|
return Request<Status>(method: .post, path: "/api/v1/statuses/\(statusID)/unfavourite")
|
|
}
|
|
|
|
public static func pin(_ statusID: String) -> Request<Status> {
|
|
return Request<Status>(method: .post, path: "/api/v1/statuses/\(statusID)/pin")
|
|
}
|
|
|
|
public static func unpin(_ statusID: String) -> Request<Status> {
|
|
return Request<Status>(method: .post, path: "/api/v1/statuses/\(statusID)/unpin")
|
|
}
|
|
|
|
public static func bookmark(_ statusID: String) -> Request<Status> {
|
|
return Request<Status>(method: .post, path: "/api/v1/statuses/\(statusID)/bookmark")
|
|
}
|
|
|
|
public static func unbookmark(_ statusID: String) -> Request<Status> {
|
|
return Request<Status>(method: .post, path: "/api/v1/statuses/\(statusID)/unbookmark")
|
|
}
|
|
|
|
public static func muteConversation(_ statusID: String) -> Request<Status> {
|
|
return Request<Status>(method: .post, path: "/api/v1/statuses/\(statusID)/mute")
|
|
}
|
|
|
|
public static func unmuteConversation(_ statusID: String) -> Request<Status> {
|
|
return Request<Status>(method: .post, path: "/api/v1/statuses/\(statusID)/unmute")
|
|
}
|
|
|
|
public static func source(_ statusID: String) -> Request<StatusSource> {
|
|
return Request(method: .get, path: "/api/v1/statuses/\(statusID)/source")
|
|
}
|
|
|
|
public static func history(_ statusID: String) -> Request<[StatusEdit]> {
|
|
return Request(method: .get, path: "/api/v1/statuses/\(statusID)/history")
|
|
}
|
|
|
|
private enum CodingKeys: String, CodingKey {
|
|
case id
|
|
case uri
|
|
case url
|
|
case account
|
|
case inReplyToID = "in_reply_to_id"
|
|
case inReplyToAccountID = "in_reply_to_account_id"
|
|
case reblog
|
|
case content
|
|
case createdAt = "created_at"
|
|
case emojis
|
|
// case repliesCount = "replies_count"
|
|
case reblogsCount = "reblogs_count"
|
|
case favouritesCount = "favourites_count"
|
|
case reblogged
|
|
case favourited
|
|
case muted
|
|
case sensitive
|
|
case spoilerText = "spoiler_text"
|
|
case visibility
|
|
case attachments = "media_attachments"
|
|
case mentions
|
|
case hashtags = "tags"
|
|
case application
|
|
case language
|
|
case pinned
|
|
case bookmarked
|
|
case card
|
|
case poll
|
|
case localOnly = "local_only"
|
|
case editedAt = "edited_at"
|
|
}
|
|
}
|
|
|
|
extension Status: Identifiable {}
|