diff --git a/Pachyderm/Model/Instance.swift b/Pachyderm/Model/Instance.swift index 77034bab..c41835d1 100644 --- a/Pachyderm/Model/Instance.swift +++ b/Pachyderm/Model/Instance.swift @@ -18,12 +18,21 @@ public class Instance: Decodable { public let thumbnail: URL? public let languages: [String]? public let stats: Stats? + public let configuration: Configuration? // pleroma doesn't currently implement these public let contactAccount: Account? - // MARK: Unofficial additions to the Mastodon API. - public let maxStatusCharacters: Int? + // superseded by mastodon's configuration.statuses.max_characters, still used by older instances & pleroma + let maxTootCharacters: Int? + let pollLimits: PollsConfiguration? + + public var maxStatusCharacters: Int? { + configuration?.statuses.maxCharacters ?? maxTootCharacters + } + public var pollsConfiguration: PollsConfiguration? { + configuration?.polls ?? pollLimits + } // we need a custom decoder, because all API-compatible implementations don't return some data in the same format public required init(from decoder: Decoder) throws { @@ -44,14 +53,18 @@ public class Instance: Decodable { self.stats = try? container.decodeIfPresent(Stats.self, forKey: .stats) self.thumbnail = try? container.decodeIfPresent(URL.self, forKey: .thumbnail) - if let maxStatusCharacters = try? container.decodeIfPresent(Int.self, forKey: .maxStatusCharacters) { - self.maxStatusCharacters = maxStatusCharacters - } else if let str = try? container.decodeIfPresent(String.self, forKey: .maxStatusCharacters), - let maxStatusCharacters = Int(str, radix: 10) { - self.maxStatusCharacters = maxStatusCharacters + + self.configuration = try? container.decodeIfPresent(Configuration.self, forKey: .configuration) + + if let maxTootCharacters = try? container.decodeIfPresent(Int.self, forKey: .maxTootCharacters) { + self.maxTootCharacters = maxTootCharacters + } else if let str = try? container.decodeIfPresent(String.self, forKey: .maxTootCharacters), + let maxTootCharacters = Int(str, radix: 10) { + self.maxTootCharacters = maxTootCharacters } else { - self.maxStatusCharacters = nil + self.maxTootCharacters = nil } + self.pollLimits = try? container.decodeIfPresent(PollsConfiguration.self, forKey: .pollLimits) } @@ -65,15 +78,16 @@ public class Instance: Decodable { case thumbnail case languages case stats - + case configuration case contactAccount = "contact_account" - case maxStatusCharacters = "max_toot_chars" + case maxTootCharacters = "max_toot_chars" + case pollLimits = "poll_limits" } } extension Instance { - public class Stats: Decodable { + public struct Stats: Decodable { public let domainCount: Int? public let statusCount: Int? public let userCount: Int? @@ -85,3 +99,68 @@ extension Instance { } } } + +extension Instance { + public struct Configuration: Decodable { + public let statuses: StatusesConfiguration + public let mediaAttachments: MediaAttachmentsConfiguration + /// Use Instance.pollsConfiguration to support older instance that don't have this nested + let polls: PollsConfiguration + + private enum CodingKeys: String, CodingKey { + case statuses + case mediaAttachments = "media_attachments" + case polls + } + } +} + +extension Instance { + public struct StatusesConfiguration: Decodable { + public let maxCharacters: Int + public let maxMediaAttachments: Int + public let charactersReservedPerURL: Int + + private enum CodingKeys: String, CodingKey { + case maxCharacters = "max_characters" + case maxMediaAttachments = "max_media_attachments" + case charactersReservedPerURL = "characters_reserved_per_url" + } + } +} + +extension Instance { + public struct MediaAttachmentsConfiguration: Decodable { + public let supportedMIMETypes: [String] + public let imageSizeLimit: Int + public let imageMatrixLimit: Int + public let videoSizeLimit: Int + public let videoFrameRateLimit: Int + public let videoMatrixLimit: Int + + private enum CodingKeys: String, CodingKey { + case supportedMIMETypes = "supported_mime_types" + case imageSizeLimit = "image_size_limit" + case imageMatrixLimit = "image_matrix_limit" + case videoSizeLimit = "video_size_limit" + case videoFrameRateLimit = "video_frame_rate_limit" + case videoMatrixLimit = "video_matrix_limit" + } + } +} + +extension Instance { + public struct PollsConfiguration: Decodable { + public let maxOptions: Int + public let maxCharactersPerOption: Int + public let minExpiration: TimeInterval + public let maxExpiration: TimeInterval + + private enum CodingKeys: String, CodingKey { + case maxOptions = "max_options" + case maxCharactersPerOption = "max_characters_per_option" + case minExpiration = "min_expiration" + case maxExpiration = "max_expiration" + } + } +}