Refactor
This commit is contained in:
parent
c0deced651
commit
ae74c36350
|
@ -7,7 +7,10 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import OAuthSwift
|
import OAuthSwift
|
||||||
|
|
||||||
|
/// Access token returned by the server.
|
||||||
public struct AccessToken: Codable {
|
public struct AccessToken: Codable {
|
||||||
|
|
||||||
|
/// Access token.
|
||||||
public let token: String
|
public let token: String
|
||||||
|
|
||||||
private enum CodingKeys: String, CodingKey {
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
|
|
@ -6,21 +6,84 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
/// Represents a user of Mastodon and their associated profile.
|
||||||
public struct Account: Codable {
|
public struct Account: Codable {
|
||||||
|
|
||||||
|
/// The account id.
|
||||||
public let id: String
|
public let id: String
|
||||||
|
|
||||||
|
/// The username of the account, not including domain.
|
||||||
public let username: String
|
public let username: String
|
||||||
|
|
||||||
|
/// The Webfinger account URI. Equal to username for local users, or username@domain for remote users.
|
||||||
public let acct: String
|
public let acct: String
|
||||||
|
|
||||||
|
/// The profile’s display name.
|
||||||
public let displayName: String?
|
public let displayName: String?
|
||||||
|
|
||||||
|
/// The profile’s bio or description.
|
||||||
public let note: String?
|
public let note: String?
|
||||||
|
|
||||||
|
/// The location of the user’s profile page.
|
||||||
public let url: URL?
|
public let url: URL?
|
||||||
|
|
||||||
|
/// An image icon that is shown next to statuses and in the profile.
|
||||||
public let avatar: URL?
|
public let avatar: URL?
|
||||||
|
|
||||||
|
/// A static version of the avatar. Equal to avatar if its value is a static image; different if avatar is an animated GIF.
|
||||||
|
public let avatarStatic: URL?
|
||||||
|
|
||||||
|
/// An image banner that is shown above the profile and in profile cards.
|
||||||
public let header: URL?
|
public let header: URL?
|
||||||
|
|
||||||
|
/// A static version of the header. Equal to header if its value is a static image; different if header is an animated GIF.
|
||||||
|
public let headerStatic: URL?
|
||||||
|
|
||||||
|
/// Whether the account manually approves follow requests.
|
||||||
public let locked: Bool
|
public let locked: Bool
|
||||||
|
|
||||||
|
/// Additional metadata attached to a profile as name-value pairs.
|
||||||
|
public let fields: [Field] = []
|
||||||
|
|
||||||
|
/// When the account was created. String (ISO 8601 Datetime).
|
||||||
public let createdAt: String
|
public let createdAt: String
|
||||||
|
|
||||||
|
/// The reported followers of this profile.
|
||||||
public let followersCount: Int
|
public let followersCount: Int
|
||||||
|
|
||||||
|
/// The reported follows of this profile.
|
||||||
public let followingCount: Int
|
public let followingCount: Int
|
||||||
|
|
||||||
|
/// How many statuses are attached to this account.
|
||||||
public let statusesCount: Int
|
public let statusesCount: Int
|
||||||
public let emojis: [Emoji] = []
|
|
||||||
|
/// Custom emoji entities to be used when rendering the profile.
|
||||||
|
public let emojis: [CustomEmoji] = []
|
||||||
|
|
||||||
|
/// Indicates that the account may perform automated actions, may not be monitored, or identifies as a robot.
|
||||||
|
public let bot: Bool = false
|
||||||
|
|
||||||
|
/// Indicates that the account represents a Group actor.
|
||||||
|
public let group: Bool = false
|
||||||
|
|
||||||
|
/// Whether the account has opted into discovery features such as the profile directory.
|
||||||
|
public let discoverable: Bool?
|
||||||
|
|
||||||
|
/// Whether the local user has opted out of being indexed by search engines.
|
||||||
|
public let noindex: Bool?
|
||||||
|
|
||||||
|
/// Indicates that the profile is currently inactive and that its user has moved to a new account.
|
||||||
|
public let moved: Bool?
|
||||||
|
|
||||||
|
/// An extra attribute returned only when an account is suspended.
|
||||||
|
public let suspended: Bool?
|
||||||
|
|
||||||
|
/// An extra attribute returned only when an account is silenced. If true, indicates that the account should be hidden behind a warning screen.
|
||||||
|
public let limited: Bool?
|
||||||
|
|
||||||
|
/// When the most recent status was posted.
|
||||||
|
/// NULLABLE String (ISO 8601 Date), or null if no statuses
|
||||||
|
public let lastStatusAt: String?
|
||||||
|
|
||||||
private enum CodingKeys: String, CodingKey {
|
private enum CodingKeys: String, CodingKey {
|
||||||
case id
|
case id
|
||||||
|
@ -32,17 +95,32 @@ public struct Account: Codable {
|
||||||
case followingCount = "following_count"
|
case followingCount = "following_count"
|
||||||
case statusesCount = "statuses_count"
|
case statusesCount = "statuses_count"
|
||||||
case displayName = "display_name"
|
case displayName = "display_name"
|
||||||
|
case avatarStatic = "avatar_static"
|
||||||
|
case headerStatic = "header_static"
|
||||||
case note
|
case note
|
||||||
case url
|
case url
|
||||||
case avatar
|
case avatar
|
||||||
case header
|
case header
|
||||||
case emojis
|
case emojis
|
||||||
|
case fields
|
||||||
|
case bot
|
||||||
|
case group
|
||||||
|
case discoverable
|
||||||
|
case noindex
|
||||||
|
case moved
|
||||||
|
case suspended
|
||||||
|
case limited
|
||||||
|
case lastStatusAt = "last_status_at"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Account {
|
extension Account {
|
||||||
public var safeDisplayName: String {
|
public var safeDisplayName: String {
|
||||||
return self.displayName ?? self.acct
|
if let trimmed = self.displayName?.trimmingCharacters(in: .whitespacesAndNewlines), trimmed.count > 0 {
|
||||||
|
return trimmed
|
||||||
|
}
|
||||||
|
|
||||||
|
return "@\(self.acct)"
|
||||||
}
|
}
|
||||||
|
|
||||||
public var displayNameWithoutEmojis: String {
|
public var displayNameWithoutEmojis: String {
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
//
|
|
||||||
// https://mczachurski.dev
|
|
||||||
// Copyright © 2022 Marcin Czachurski and the repository contributors.
|
|
||||||
// Licensed under the MIT License.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
public struct App: Codable {
|
|
||||||
public let id: String
|
|
||||||
public let name: String
|
|
||||||
public let redirectUri: String
|
|
||||||
public let clientId: String
|
|
||||||
public let clientSecret: String
|
|
||||||
public let website: String?
|
|
||||||
public let vapidKey: String?
|
|
||||||
|
|
||||||
public init(clientId: String, clientSecret: String, vapidKey: String = "") {
|
|
||||||
self.id = ""
|
|
||||||
self.name = ""
|
|
||||||
self.redirectUri = "urn:ietf:wg:oauth:2.0:oob"
|
|
||||||
self.clientId = clientId
|
|
||||||
self.clientSecret = clientSecret
|
|
||||||
self.website = nil
|
|
||||||
self.vapidKey = vapidKey
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum CodingKeys: String, CodingKey {
|
|
||||||
case id
|
|
||||||
case name
|
|
||||||
case redirectUri = "redirect_uri"
|
|
||||||
case clientId = "client_id"
|
|
||||||
case clientSecret = "client_secret"
|
|
||||||
case website
|
|
||||||
case vapidKey = "vapid_key"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,7 +6,68 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public struct Application: Codable {
|
/// Represents an application that interfaces with the REST API to access accounts or post statuses.
|
||||||
public let name: String
|
public class Application: BaseApplication {
|
||||||
public let website: URL?
|
|
||||||
|
/// The application id.
|
||||||
|
public let id: String
|
||||||
|
|
||||||
|
/// Where the user should be redirected after authorization. To display the authorization code to the user instead of redirecting to a web page, use urn:ietf:wg:oauth:2.0:oob in this parameter.
|
||||||
|
public let redirectUri: String
|
||||||
|
|
||||||
|
/// Client ID key, to be used for obtaining OAuth tokens.
|
||||||
|
public let clientId: String
|
||||||
|
|
||||||
|
/// Client secret key, to be used for obtaining OAuth tokens.
|
||||||
|
public let clientSecret: String
|
||||||
|
|
||||||
|
/// Used for Push Streaming API. Returned with POST /api/v1/apps. Equivalent to WebPushSubscription#server_key.
|
||||||
|
public let vapidKey: String?
|
||||||
|
|
||||||
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
case id
|
||||||
|
case redirectUri = "redirect_uri"
|
||||||
|
case clientId = "client_id"
|
||||||
|
case clientSecret = "client_secret"
|
||||||
|
case vapidKey = "vapid_key"
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(clientId: String, clientSecret: String, vapidKey: String = "") {
|
||||||
|
self.id = ""
|
||||||
|
self.redirectUri = "urn:ietf:wg:oauth:2.0:oob"
|
||||||
|
self.clientId = clientId
|
||||||
|
self.clientSecret = clientSecret
|
||||||
|
self.vapidKey = vapidKey
|
||||||
|
|
||||||
|
super.init(name: "", website: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
public required init(from decoder: Decoder) throws {
|
||||||
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
|
|
||||||
|
self.id = try container.decode(String.self, forKey: .id)
|
||||||
|
self.redirectUri = try container.decode(String.self, forKey: .redirectUri)
|
||||||
|
self.clientId = try container.decode(String.self, forKey: .clientId)
|
||||||
|
self.clientSecret = try container.decode(String.self, forKey: .clientSecret)
|
||||||
|
self.vapidKey = try? container.decode(String.self, forKey: .vapidKey)
|
||||||
|
|
||||||
|
let superDecoder = try container.superDecoder()
|
||||||
|
try super.init(from: superDecoder)
|
||||||
|
}
|
||||||
|
|
||||||
|
public override func encode(to encoder: Encoder) throws {
|
||||||
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||||
|
|
||||||
|
try container.encode(id, forKey: .id)
|
||||||
|
try container.encode(redirectUri, forKey: .redirectUri)
|
||||||
|
try container.encode(clientId, forKey: .clientId)
|
||||||
|
try container.encode(clientSecret, forKey: .clientSecret)
|
||||||
|
|
||||||
|
if let vapidKey {
|
||||||
|
try container.encode(vapidKey, forKey: .vapidKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
let superEncoder = container.superEncoder()
|
||||||
|
try super.encode(to: superEncoder)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
//
|
||||||
|
// https://mczachurski.dev
|
||||||
|
// Copyright © 2023 Marcin Czachurski and the repository contributors.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// Represents an application that interfaces with the REST API to access accounts or post statuses.
|
||||||
|
/// Base object is used in statuses etc.
|
||||||
|
public class BaseApplication: Codable {
|
||||||
|
|
||||||
|
/// The name of your application.
|
||||||
|
public let name: String
|
||||||
|
|
||||||
|
// The website associated with your application.
|
||||||
|
public let website: URL?
|
||||||
|
|
||||||
|
init(name: String, website: URL?) {
|
||||||
|
self.name = name
|
||||||
|
self.website = website
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
case name
|
||||||
|
case website
|
||||||
|
}
|
||||||
|
|
||||||
|
public required init(from decoder: Decoder) throws {
|
||||||
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
|
|
||||||
|
self.name = try container.decode(String.self, forKey: .name)
|
||||||
|
self.website = try? container.decode(URL.self, forKey: .website)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func encode(to encoder: Encoder) throws {
|
||||||
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||||
|
|
||||||
|
try container.encode(name, forKey: .name)
|
||||||
|
|
||||||
|
if let website {
|
||||||
|
try container.encode(website, forKey: .website)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,50 +0,0 @@
|
||||||
//
|
|
||||||
// https://mczachurski.dev
|
|
||||||
// Copyright © 2022 Marcin Czachurski and the repository contributors.
|
|
||||||
// Licensed under the MIT License.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
public struct Card: Codable {
|
|
||||||
public enum CardType: String, Codable {
|
|
||||||
case link = "link" // Link OEmbed
|
|
||||||
case photo = "photo" // Photo OEmbed
|
|
||||||
case video = "video" // Video OEmbed
|
|
||||||
case rich = "rich" // iframe OEmbed. Not currently accepted, so won't show up in practice.
|
|
||||||
}
|
|
||||||
|
|
||||||
public let url: URL
|
|
||||||
public let title: String
|
|
||||||
public let description: String
|
|
||||||
public let type: CardType
|
|
||||||
|
|
||||||
public let authorName: String?
|
|
||||||
public let authorUrl: String?
|
|
||||||
public let providerName: String?
|
|
||||||
public let providerUrl: String?
|
|
||||||
public let html: String?
|
|
||||||
public let width: Int?
|
|
||||||
public let height: Int?
|
|
||||||
public let image: String?
|
|
||||||
public let embedUrl: String?
|
|
||||||
public let blurhash: String?
|
|
||||||
|
|
||||||
private enum CodingKeys: String, CodingKey {
|
|
||||||
case url
|
|
||||||
case title
|
|
||||||
case description
|
|
||||||
case type
|
|
||||||
|
|
||||||
case authorName = "author_name"
|
|
||||||
case authorUrl = "author_url"
|
|
||||||
case providerName = "provider_name"
|
|
||||||
case providerUrl = "provider_url"
|
|
||||||
case html
|
|
||||||
case width
|
|
||||||
case height
|
|
||||||
case image
|
|
||||||
case embedUrl = "embed_url"
|
|
||||||
case blurhash
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,144 @@
|
||||||
|
//
|
||||||
|
// https://mczachurski.dev
|
||||||
|
// Copyright © 2023 Marcin Czachurski and the repository contributors.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// Configured values and limits for this website.
|
||||||
|
public struct Configuration: Codable {
|
||||||
|
|
||||||
|
/// URLs of interest for clients apps.
|
||||||
|
public let urls: ConfigurationUrls?
|
||||||
|
|
||||||
|
/// Limits related to accounts.
|
||||||
|
public let accounts: ConfigurationAccounts?
|
||||||
|
|
||||||
|
/// Limits related to authoring statuses.
|
||||||
|
public let statuses: ConfigurationStatuses?
|
||||||
|
|
||||||
|
/// Hints for which attachments will be accepted.
|
||||||
|
public let mediaAttachments: ConfigurationMediaAttachments?
|
||||||
|
|
||||||
|
/// Limits related to polls.
|
||||||
|
public let polls: ConfigurationPolls?
|
||||||
|
|
||||||
|
/// Hints related to translation.
|
||||||
|
public let translation: ConfigurationTranslation?
|
||||||
|
|
||||||
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
case urls
|
||||||
|
case accounts
|
||||||
|
case statuses
|
||||||
|
case mediaAttachments = "media_attachments"
|
||||||
|
case polls
|
||||||
|
case translation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// URLs of interest for clients apps.
|
||||||
|
public struct ConfigurationUrls: Codable {
|
||||||
|
|
||||||
|
/// The Websockets URL for connecting to the streaming API.
|
||||||
|
public let streaming: String?
|
||||||
|
|
||||||
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
case streaming
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Limits related to accounts.
|
||||||
|
public struct ConfigurationAccounts: Codable {
|
||||||
|
|
||||||
|
/// The maximum number of featured tags allowed for each account.
|
||||||
|
public let maxFeaturedTags: Int?
|
||||||
|
|
||||||
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
case maxFeaturedTags = "max_featured_tags"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Limits related to authoring statuses.
|
||||||
|
public struct ConfigurationStatuses: Codable {
|
||||||
|
|
||||||
|
/// The maximum number of allowed characters per status.
|
||||||
|
public let maxCharacters: Int
|
||||||
|
|
||||||
|
/// The maximum number of media attachments that can be added to a status.
|
||||||
|
public let maxMediaAttachments: Int
|
||||||
|
|
||||||
|
/// Each URL in a status will be assumed to be exactly this many characters.
|
||||||
|
public let charactersReservedPerUrl: Int
|
||||||
|
|
||||||
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
case maxCharacters = "max_characters"
|
||||||
|
case maxMediaAttachments = "max_media_attachments"
|
||||||
|
case charactersReservedPerUrl = "characters_reserved_per_url"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hints for which attachments will be accepted.
|
||||||
|
public struct ConfigurationMediaAttachments: Codable {
|
||||||
|
|
||||||
|
/// Contains MIME types that can be uploaded.
|
||||||
|
public let supportedMimeTypes: [String]?
|
||||||
|
|
||||||
|
/// The maximum size of any uploaded image, in bytes.
|
||||||
|
public let imageSizeLimit: Int
|
||||||
|
|
||||||
|
/// The maximum number of pixels (width times height) for image uploads.
|
||||||
|
public let imageMatrixLimit: Int
|
||||||
|
|
||||||
|
/// The maximum size of any uploaded video, in bytes.
|
||||||
|
public let videoSizeLimit: Int
|
||||||
|
|
||||||
|
/// The maximum frame rate for any uploaded video.
|
||||||
|
public let videoFrameRateLimit: Int
|
||||||
|
|
||||||
|
/// The maximum number of pixels (width times height) for video uploads.
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Limits related to polls.
|
||||||
|
public struct ConfigurationPolls: Codable {
|
||||||
|
|
||||||
|
/// Each poll is allowed to have up to this many options.
|
||||||
|
public let maxOptions: Int
|
||||||
|
|
||||||
|
/// Each poll option is allowed to have this many characters.
|
||||||
|
public let maxCharactersPerOption: Int
|
||||||
|
|
||||||
|
/// The shortest allowed poll duration, in seconds.
|
||||||
|
public let minExpiration: Int
|
||||||
|
|
||||||
|
/// The longest allowed poll duration, in seconds.
|
||||||
|
public let maxExpiration: Int
|
||||||
|
|
||||||
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
case maxOptions = "max_options"
|
||||||
|
case maxCharactersPerOption = "max_characters_per_option"
|
||||||
|
case minExpiration = "min_expiration"
|
||||||
|
case maxExpiration = "max_expiration"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hints related to translation.
|
||||||
|
public struct ConfigurationTranslation: Codable {
|
||||||
|
|
||||||
|
/// Whether the Translations API is available on this instance.
|
||||||
|
public let enabled: Bool
|
||||||
|
|
||||||
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
case enabled
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
//
|
||||||
|
// https://mczachurski.dev
|
||||||
|
// Copyright © 2023 Marcin Czachurski and the repository contributors.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// Hints related to contacting a representative of the website.
|
||||||
|
public struct Contact: Codable {
|
||||||
|
|
||||||
|
/// An email address that can be messaged regarding inquiries or issues.
|
||||||
|
public let email: String;
|
||||||
|
|
||||||
|
/// An account that can be contacted natively over the network regarding inquiries or issues.
|
||||||
|
public let account: Account
|
||||||
|
|
||||||
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
case email
|
||||||
|
case account
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,8 +6,13 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
/// Represents the tree around a given status. Used for reconstructing threads of statuses.
|
||||||
public struct Context: Codable {
|
public struct Context: Codable {
|
||||||
|
|
||||||
|
/// Parents in the thread.
|
||||||
public let ancestors: [Status]
|
public let ancestors: [Status]
|
||||||
|
|
||||||
|
/// Children in the thread.
|
||||||
public let descendants: [Status]
|
public let descendants: [Status]
|
||||||
|
|
||||||
public enum CodingKeys: CodingKey {
|
public enum CodingKeys: CodingKey {
|
||||||
|
|
|
@ -6,16 +6,29 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public struct Emoji: Codable {
|
/// Represents a custom emoji.
|
||||||
|
public struct CustomEmoji: Codable {
|
||||||
|
|
||||||
|
/// The name of the custom emoji.
|
||||||
public let shortcode: String
|
public let shortcode: String
|
||||||
|
|
||||||
|
/// A link to the custom emoji.
|
||||||
public let url: URL
|
public let url: URL
|
||||||
|
|
||||||
|
/// A link to a static copy of the custom emoji.
|
||||||
public let staticUrl: URL
|
public let staticUrl: URL
|
||||||
|
|
||||||
|
/// Whether this Emoji should be visible in the picker or unlisted.
|
||||||
public let visibleInPicker: Bool
|
public let visibleInPicker: Bool
|
||||||
|
|
||||||
|
/// Used for sorting custom emoji in the picker.
|
||||||
|
public let category: String?
|
||||||
|
|
||||||
private enum CodingKeys: String, CodingKey {
|
private enum CodingKeys: String, CodingKey {
|
||||||
case shortcode
|
case shortcode
|
||||||
case url
|
case url
|
||||||
case staticUrl = "static_url"
|
case staticUrl = "static_url"
|
||||||
case visibleInPicker = "visible_in_picker"
|
case visibleInPicker = "visible_in_picker"
|
||||||
|
case category
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,11 +0,0 @@
|
||||||
//
|
|
||||||
// https://mczachurski.dev
|
|
||||||
// Copyright © 2022 Marcin Czachurski and the repository contributors.
|
|
||||||
// Licensed under the MIT License.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
public struct ErrorMessage: Codable {
|
|
||||||
public let error: String
|
|
||||||
}
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
//
|
||||||
|
// https://mczachurski.dev
|
||||||
|
// Copyright © 2023 Marcin Czachurski and the repository contributors.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// Additional metadata attached to a profile as name-value pairs.
|
||||||
|
public struct Field: Codable {
|
||||||
|
|
||||||
|
/// The key of a given field’s key-value pair.
|
||||||
|
public let name: String
|
||||||
|
|
||||||
|
/// The value associated with the name key. Type: String (HTML).
|
||||||
|
public let value: String
|
||||||
|
|
||||||
|
/// Timestamp of when the server verified a URL value for a rel=“me” link.
|
||||||
|
/// NULLABLE String (ISO 8601 Datetime) if value is a verified URL. Otherwise, null.
|
||||||
|
public let verifiedAt: String?
|
||||||
|
|
||||||
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
case name
|
||||||
|
case value
|
||||||
|
case verifiedAt = "verified_at"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
//
|
||||||
|
// https://mczachurski.dev
|
||||||
|
// Copyright © 2023 Marcin Czachurski and the repository contributors.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// Represents a user-defined filter for determining which statuses should not be shown to the user.
|
||||||
|
public struct Filter: Codable {
|
||||||
|
public enum FilterContext: String, Codable {
|
||||||
|
/// Home timeline and lists.
|
||||||
|
case home = "home"
|
||||||
|
/// Notifications timeline.
|
||||||
|
case notifications = "notifications"
|
||||||
|
/// Public timelines.
|
||||||
|
case `public` = "public"
|
||||||
|
/// Expanded thread of a detailed status.
|
||||||
|
case thread = "thread"
|
||||||
|
/// When viewing a profile.
|
||||||
|
case account = "account"
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum FilterAction: String, Codable {
|
||||||
|
/// Show a warning that identifies the matching filter by title, and allow the user to expand the filtered status.
|
||||||
|
/// This is the default (and unknown values should be treated as equivalent to warn).
|
||||||
|
case warn = "warn"
|
||||||
|
/// do not show this status if it is received
|
||||||
|
case hide = "hide"
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The ID of the Filter in the database.
|
||||||
|
public let id: EntityId
|
||||||
|
|
||||||
|
/// A title given by the user to name the filter.
|
||||||
|
public let title: String
|
||||||
|
|
||||||
|
/// The contexts in which the filter should be applied.
|
||||||
|
public let context: FilterContext
|
||||||
|
|
||||||
|
/// When the filter should no longer be applied. NULLABLE String (ISO 8601 Datetime), or null if the filter does not expire.
|
||||||
|
public let expiresAt: String?
|
||||||
|
|
||||||
|
/// The action to be taken when a status matches this filter.
|
||||||
|
public let filterAction: FilterAction
|
||||||
|
|
||||||
|
/// The keywords grouped under this filter.
|
||||||
|
public let keywords: [FilterKeyword]
|
||||||
|
|
||||||
|
/// The statuses grouped under this filter.
|
||||||
|
public let statuses: [FilterStatus]
|
||||||
|
|
||||||
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
case id
|
||||||
|
case title
|
||||||
|
case context
|
||||||
|
case expiresAt = "expires_at"
|
||||||
|
case filterAction = "filter_action"
|
||||||
|
case keywords
|
||||||
|
case statuses
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
//
|
||||||
|
// https://mczachurski.dev
|
||||||
|
// Copyright © 2023 Marcin Czachurski and the repository contributors.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// Represents a keyword that, if matched, should cause the filter action to be taken.
|
||||||
|
public struct FilterKeyword: Codable {
|
||||||
|
|
||||||
|
/// The ID of the FilterKeyword in the database.
|
||||||
|
public let id: EntityId
|
||||||
|
|
||||||
|
/// The phrase to be matched against.
|
||||||
|
public let keyword: String
|
||||||
|
|
||||||
|
/// Should the filter consider word boundaries? See [implementation guidelines for filters](https://docs.joinmastodon.org/api/guidelines/#filters).
|
||||||
|
public let wholeWord: Bool
|
||||||
|
|
||||||
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
case id
|
||||||
|
case keyword
|
||||||
|
case wholeWord = "whole_word"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
//
|
||||||
|
// https://mczachurski.dev
|
||||||
|
// Copyright © 2023 Marcin Czachurski and the repository contributors.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// Represents a filter whose keywords matched a given status.
|
||||||
|
public struct FilterResult: Codable {
|
||||||
|
|
||||||
|
/// The filter that was matched.
|
||||||
|
public let filter: Filter
|
||||||
|
|
||||||
|
/// The keyword within the filter that was matched.
|
||||||
|
public let keywordMatches: [String]?
|
||||||
|
|
||||||
|
/// The status ID within the filter that was matched.
|
||||||
|
public let statusMatches: [EntityId]?
|
||||||
|
|
||||||
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
case filter
|
||||||
|
case keywordMatches = "keyword_matches"
|
||||||
|
case statusMatches = "status_matches"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
//
|
||||||
|
// https://mczachurski.dev
|
||||||
|
// Copyright © 2023 Marcin Czachurski and the repository contributors.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// Represents a status ID that, if matched, should cause the filter action to be taken.
|
||||||
|
public struct FilterStatus: Codable {
|
||||||
|
|
||||||
|
/// The ID of the FilterStatus in the database.
|
||||||
|
public let id: EntityId
|
||||||
|
|
||||||
|
/// The ID of the filtered Status in the database.
|
||||||
|
public let statusId: EntityId
|
||||||
|
|
||||||
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
case id
|
||||||
|
case statusId = "status_id"
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,8 +6,14 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
/// Focal points for cropping media thumbnails.
|
||||||
|
/// https://docs.joinmastodon.org/api/guidelines/#focal-points
|
||||||
public struct Focus: Codable {
|
public struct Focus: Codable {
|
||||||
|
|
||||||
|
/// X position int he image.
|
||||||
public let x: Int
|
public let x: Int
|
||||||
|
|
||||||
|
/// Y position in the image.
|
||||||
public let y: Int
|
public let y: Int
|
||||||
|
|
||||||
private enum CodingKeys: String, CodingKey {
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
|
|
@ -6,10 +6,19 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
/// Infor about image stored in metadata.
|
||||||
public struct ImageInfo: Codable {
|
public struct ImageInfo: Codable {
|
||||||
|
|
||||||
|
/// Width of the image.
|
||||||
public let width: Int
|
public let width: Int
|
||||||
|
|
||||||
|
/// Height of the image.
|
||||||
public let height: Int
|
public let height: Int
|
||||||
|
|
||||||
|
/// Size of the image.
|
||||||
public let size: String
|
public let size: String
|
||||||
|
|
||||||
|
/// Aspect ratio of the image.
|
||||||
public let aspect: Double
|
public let aspect: Double
|
||||||
|
|
||||||
private enum CodingKeys: String, CodingKey {
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
|
|
@ -6,9 +6,18 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
/// Metadata returned by Paperclip. May contain subtrees small and original, as well as various other top-level properties.
|
||||||
|
/// More importantly, there may be another topl-level focus Hash object on images as of 2.3.0,
|
||||||
|
/// with coordinates can be used for smart thumbnail cropping – see Focal points for cropped media thumbnails for more.
|
||||||
public struct ImageMetadata: Metadata {
|
public struct ImageMetadata: Metadata {
|
||||||
|
|
||||||
|
/// Metadata about orginal image.
|
||||||
public let original: ImageInfo?
|
public let original: ImageInfo?
|
||||||
|
|
||||||
|
/// Metadata about small version of image.
|
||||||
public let small: ImageInfo?
|
public let small: ImageInfo?
|
||||||
|
|
||||||
|
/// Focal points for cropping media thumbnails.
|
||||||
public let focus: Focus?
|
public let focus: Focus?
|
||||||
|
|
||||||
private enum CodingKeys: String, CodingKey {
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
|
|
@ -6,27 +6,72 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
/// Represents the software instance of Mastodon running on this domain.
|
||||||
public struct Instance: Codable {
|
public struct Instance: Codable {
|
||||||
public let uri: String
|
/// The domain name of the instance.
|
||||||
|
public let domain: String
|
||||||
|
|
||||||
|
/// The title of the website.
|
||||||
public let title: String?
|
public let title: String?
|
||||||
public let description: String?
|
|
||||||
public let email: String?
|
|
||||||
public let thumbnail: String?
|
|
||||||
|
|
||||||
enum CodingKeys: CodingKey {
|
/// The version of Mastodon installed on the instance.
|
||||||
case uri
|
public let version: String
|
||||||
|
|
||||||
|
/// The URL for the source code of the software running on this instance, in keeping with AGPL license requirements.
|
||||||
|
public let sourceUrl: URL?
|
||||||
|
|
||||||
|
/// A short, plain-text description defined by the admin.
|
||||||
|
public let description: String?
|
||||||
|
|
||||||
|
/// Usage data for this instance.
|
||||||
|
public let usage: Usage?
|
||||||
|
|
||||||
|
/// The URL for the thumbnail image.
|
||||||
|
public let thumbnail: Thumbnail?
|
||||||
|
|
||||||
|
/// Primary languages of the website and its staff. Array of String (ISO 639-1 two-letter code).
|
||||||
|
public let languages: [String]?
|
||||||
|
|
||||||
|
/// Configured values and limits for this website.
|
||||||
|
public let configuration: Configuration?
|
||||||
|
|
||||||
|
/// Information about registering for this website.
|
||||||
|
public let registrations: Registration?
|
||||||
|
|
||||||
|
/// Hints related to contacting a representative of the website.
|
||||||
|
public let contact: Contact?
|
||||||
|
|
||||||
|
/// An itemized list of rules for this website.
|
||||||
|
public let rules: [Rule]?
|
||||||
|
|
||||||
|
enum CodingKeys: String, CodingKey {
|
||||||
|
case domain
|
||||||
case title
|
case title
|
||||||
|
case version
|
||||||
|
case sourceUrl = "source_url"
|
||||||
case description
|
case description
|
||||||
case email
|
case usage
|
||||||
case thumbnail
|
case thumbnail
|
||||||
|
case languages
|
||||||
|
case configuration
|
||||||
|
case registrations
|
||||||
|
case contact
|
||||||
|
case rules
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(from decoder: Decoder) throws {
|
public init(from decoder: Decoder) throws {
|
||||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
self.uri = try container.decode(String.self, forKey: .uri)
|
self.domain = try container.decode(String.self, forKey: .domain)
|
||||||
self.title = try? container.decodeIfPresent(String.self, forKey: .title)
|
self.title = try? container.decodeIfPresent(String.self, forKey: .title)
|
||||||
|
self.version = try container.decode(String.self, forKey: .version)
|
||||||
|
self.sourceUrl = try? container.decodeIfPresent(URL.self, forKey: .sourceUrl)
|
||||||
self.description = try? container.decodeIfPresent(String.self, forKey: .description)
|
self.description = try? container.decodeIfPresent(String.self, forKey: .description)
|
||||||
self.email = try? container.decodeIfPresent(String.self, forKey: .email)
|
self.usage = try? container.decodeIfPresent(Usage.self, forKey: .usage)
|
||||||
self.thumbnail = try? container.decodeIfPresent(String.self, forKey: .thumbnail)
|
self.thumbnail = try? container.decodeIfPresent(Thumbnail.self, forKey: .thumbnail)
|
||||||
|
self.languages = try? container.decodeIfPresent([String].self, forKey: .languages)
|
||||||
|
self.configuration = try? container.decodeIfPresent(Configuration.self, forKey: .configuration)
|
||||||
|
self.registrations = try? container.decodeIfPresent(Registration.self, forKey: .registrations)
|
||||||
|
self.contact = try? container.decodeIfPresent(Contact.self, forKey: .contact)
|
||||||
|
self.rules = try? container.decodeIfPresent([Rule].self, forKey: .rules)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,10 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import RegexBuilder
|
import RegexBuilder
|
||||||
|
|
||||||
|
/// Link returned in header for paging feature/
|
||||||
public struct Link {
|
public struct Link {
|
||||||
|
|
||||||
|
/// Raw value of header link.
|
||||||
public let rawLink: String
|
public let rawLink: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,13 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
/// Some of endpoint returns JSON data and additional information in header, like link for paging functionality.
|
||||||
public struct Linkable<T> where T: Codable {
|
public struct Linkable<T> where T: Codable {
|
||||||
|
|
||||||
|
/// Data retunred in HTTP reponse body (mostly JSON data/entities).
|
||||||
public let data: T
|
public let data: T
|
||||||
|
|
||||||
|
/// Link returned in the HTTP header.
|
||||||
public let link: Link?
|
public let link: Link?
|
||||||
|
|
||||||
public init(data: T, link: Link? = nil) where T: Codable {
|
public init(data: T, link: Link? = nil) where T: Codable {
|
||||||
|
|
|
@ -6,9 +6,16 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
/// Represents the last read position within a user's timelines.
|
||||||
public struct Marker: Codable {
|
public struct Marker: Codable {
|
||||||
|
|
||||||
|
/// The ID of the most recently viewed entity.
|
||||||
public let lastReadId: EntityId
|
public let lastReadId: EntityId
|
||||||
|
|
||||||
|
/// An incrementing counter, used for locking to prevent write conflicts.
|
||||||
public let version: Int64
|
public let version: Int64
|
||||||
|
|
||||||
|
/// The timestamp of when the marker was set. String (ISO 8601 Datetime).
|
||||||
public let updatedAt: String
|
public let updatedAt: String
|
||||||
|
|
||||||
private enum CodingKeys: String, CodingKey {
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
|
|
@ -6,8 +6,10 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public class Attachment: Codable {
|
/// Represents a file or media attachment that can be added to a status.
|
||||||
public enum AttachmentType: String, Codable {
|
public class MediaAttachment: Codable {
|
||||||
|
|
||||||
|
public enum MediaAttachmentType: String, Codable {
|
||||||
case unknown = "unknown"
|
case unknown = "unknown"
|
||||||
case image = "image"
|
case image = "image"
|
||||||
case gifv = "gifv"
|
case gifv = "gifv"
|
||||||
|
@ -15,14 +17,29 @@ public class Attachment: Codable {
|
||||||
case audio = "audio"
|
case audio = "audio"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The ID of the attachment in the database.
|
||||||
public let id: String
|
public let id: String
|
||||||
public let type: AttachmentType
|
|
||||||
|
/// The type of the attachment.
|
||||||
|
public let type: MediaAttachmentType
|
||||||
|
|
||||||
|
/// The location of the original full-size attachment.
|
||||||
public let url: URL
|
public let url: URL
|
||||||
|
|
||||||
|
/// The location of a scaled-down preview of the attachment.
|
||||||
public let previewUrl: URL?
|
public let previewUrl: URL?
|
||||||
|
|
||||||
|
/// The location of the full-size original attachment on the remote website.
|
||||||
public let remoteUrl: URL?
|
public let remoteUrl: URL?
|
||||||
|
|
||||||
|
/// Alternate text that describes what is in the media attachment, to be used for the visually impaired or when media attachments do not load.
|
||||||
public let description: String?
|
public let description: String?
|
||||||
|
|
||||||
|
/// A hash computed by the [BlurHash](https://github.com/woltapp/blurhash) algorithm, for generating colorful preview thumbnails when media has not been downloaded yet.
|
||||||
public let blurhash: String?
|
public let blurhash: String?
|
||||||
|
|
||||||
|
/// Metadata returned by Paperclip.
|
||||||
|
/// May contain subtrees small and original, as well as various other top-level properties.
|
||||||
public let meta: Metadata?
|
public let meta: Metadata?
|
||||||
|
|
||||||
private enum CodingKeys: String, CodingKey {
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
@ -41,7 +58,7 @@ public class Attachment: Codable {
|
||||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
|
|
||||||
self.id = try container.decode(EntityId.self, forKey: .id)
|
self.id = try container.decode(EntityId.self, forKey: .id)
|
||||||
self.type = try container.decode(AttachmentType.self, forKey: .type)
|
self.type = try container.decode(MediaAttachmentType.self, forKey: .type)
|
||||||
self.url = try container.decode(URL.self, forKey: .url)
|
self.url = try container.decode(URL.self, forKey: .url)
|
||||||
self.previewUrl = try? container.decode(URL.self, forKey: .previewUrl)
|
self.previewUrl = try? container.decode(URL.self, forKey: .previewUrl)
|
||||||
self.remoteUrl = try? container.decode(URL.self, forKey: .remoteUrl)
|
self.remoteUrl = try? container.decode(URL.self, forKey: .remoteUrl)
|
|
@ -6,9 +6,18 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
/// Mentions of users within the status content.
|
||||||
public struct Mention: Codable {
|
public struct Mention: Codable {
|
||||||
public let url: String
|
|
||||||
public let username: String
|
/// The account ID of the mentioned user.
|
||||||
public let acct: String
|
|
||||||
public let id: String
|
public let id: String
|
||||||
|
|
||||||
|
/// The location of the mentioned user’s profile.
|
||||||
|
public let url: String
|
||||||
|
|
||||||
|
/// The username of the mentioned user.
|
||||||
|
public let username: String
|
||||||
|
|
||||||
|
/// The webfinger acct: URI of the mentioned user. Equivalent to username for local users, or username@domain for remote users.
|
||||||
|
public let acct: String
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,6 @@
|
||||||
// Licensed under the MIT License.
|
// Licensed under the MIT License.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
/// Generic protocol for different kind of media attachment metadata types.
|
||||||
public protocol Metadata: Codable {
|
public protocol Metadata: Codable {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,34 +6,75 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
/// Represents a notification of an event relevant to the user.
|
||||||
public struct Notification: Codable {
|
public struct Notification: Codable {
|
||||||
public enum NotificationType: String, Codable {
|
public enum NotificationType: String, Codable {
|
||||||
|
/// Someone mentioned you in their status.
|
||||||
case mention = "mention"
|
case mention = "mention"
|
||||||
|
|
||||||
|
/// Someone boosted one of your statuses.
|
||||||
case reblog = "reblog"
|
case reblog = "reblog"
|
||||||
|
|
||||||
|
/// Someone favourited one of your statuses.
|
||||||
case favourite = "favourite"
|
case favourite = "favourite"
|
||||||
|
|
||||||
|
/// Someone followed you.
|
||||||
case follow = "follow"
|
case follow = "follow"
|
||||||
|
|
||||||
|
/// Someone you enabled notifications for has posted a status.
|
||||||
|
case status = "status"
|
||||||
|
|
||||||
|
/// Someone requested to follow you.
|
||||||
|
case followRequest = "follow_request"
|
||||||
|
|
||||||
|
/// A poll you have voted in or created has ended.
|
||||||
|
case poll = "poll"
|
||||||
|
|
||||||
|
/// A status you interacted with has been edited.
|
||||||
|
case update = "update"
|
||||||
|
|
||||||
|
/// Someone signed up (optionally sent to admins).
|
||||||
|
case adminSignUp = "admin.sign_up"
|
||||||
|
|
||||||
|
/// A new report has been filed.
|
||||||
|
case adminReport = "admin.report"
|
||||||
}
|
}
|
||||||
public let id: String
|
|
||||||
|
/// The id of the notification in the database.
|
||||||
|
public let id: EntityId
|
||||||
|
|
||||||
|
/// The type of event that resulted in the notification.
|
||||||
public let type: NotificationType
|
public let type: NotificationType
|
||||||
|
|
||||||
|
/// The timestamp of the notification. String (ISO 8601 Datetime).
|
||||||
public let createdAt: String
|
public let createdAt: String
|
||||||
|
|
||||||
|
/// The account that performed the action that generated the notification.
|
||||||
public let account: Account
|
public let account: Account
|
||||||
|
|
||||||
|
/// Status that was the object of the notification. Attached when type of the notification is favourite, reblog, status, mention, poll, or update.
|
||||||
public let status: Status?
|
public let status: Status?
|
||||||
|
|
||||||
|
/// Report that was the object of the notification. Attached when type of the notification is admin.report.
|
||||||
|
public let report: Report?
|
||||||
|
|
||||||
private enum CodingKeys: String, CodingKey {
|
private enum CodingKeys: String, CodingKey {
|
||||||
case id
|
case id
|
||||||
case type
|
case type
|
||||||
case createdAat = "created_at"
|
case createdAat = "created_at"
|
||||||
case account
|
case account
|
||||||
case status
|
case status
|
||||||
|
case report
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(from decoder: Decoder) throws {
|
public init(from decoder: Decoder) throws {
|
||||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
self.id = try container.decode(String.self, forKey: .id)
|
self.id = try container.decode(EntityId.self, forKey: .id)
|
||||||
self.type = try container.decode(NotificationType.self, forKey: .type)
|
self.type = try container.decode(NotificationType.self, forKey: .type)
|
||||||
self.createdAt = try container.decode(String.self, forKey: .createdAat)
|
self.createdAt = try container.decode(String.self, forKey: .createdAat)
|
||||||
self.account = try container.decode(Account.self, forKey: .account)
|
self.account = try container.decode(Account.self, forKey: .account)
|
||||||
self.status = try? container.decode(Status.self, forKey: .status)
|
self.status = try? container.decodeIfPresent(Status.self, forKey: .status)
|
||||||
|
self.report = try? container.decodeIfPresent(Report.self, forKey: .report)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode(to encoder: Encoder) throws {
|
public func encode(to encoder: Encoder) throws {
|
||||||
|
@ -46,5 +87,9 @@ public struct Notification: Codable {
|
||||||
if let status {
|
if let status {
|
||||||
try container.encode(status, forKey: .status)
|
try container.encode(status, forKey: .status)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let report {
|
||||||
|
try container.encode(report, forKey: .report)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,19 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
/// Location where image has been taken.
|
||||||
|
/// Entity specific for Pixelfed.
|
||||||
public struct Place: Codable {
|
public struct Place: Codable {
|
||||||
|
|
||||||
|
/// Id of the entity.
|
||||||
public let id: Int32
|
public let id: Int32
|
||||||
|
|
||||||
|
/// City where picture has been taken.
|
||||||
public let slug: String?
|
public let slug: String?
|
||||||
|
|
||||||
|
/// City where picture has been taken.
|
||||||
public let name: String?
|
public let name: String?
|
||||||
|
|
||||||
|
/// Country where picture has been taken.
|
||||||
public let country: String?
|
public let country: String?
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
//
|
||||||
|
// https://mczachurski.dev
|
||||||
|
// Copyright © 2023 Marcin Czachurski and the repository contributors.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// Represents a poll attached to a status.
|
||||||
|
public struct Poll: Codable {
|
||||||
|
|
||||||
|
/// The ID of the poll in the database.
|
||||||
|
public let id: EntityId
|
||||||
|
|
||||||
|
/// When the poll ends. NULLABLE String (ISO 8601 Datetime), or null if the poll does not end.
|
||||||
|
public let expiresAt: String?
|
||||||
|
|
||||||
|
/// Is the poll currently expired?
|
||||||
|
public let expired: Bool
|
||||||
|
|
||||||
|
/// Does the poll allow multiple-choice answers?
|
||||||
|
public let multiple: Bool
|
||||||
|
|
||||||
|
/// How many votes have been received.
|
||||||
|
public let votesCount: Int
|
||||||
|
|
||||||
|
/// How many unique accounts have voted on a multiple-choice poll.
|
||||||
|
public let votersCount: Int?
|
||||||
|
|
||||||
|
/// Possible answers for the poll.
|
||||||
|
public let options: [PollOption]
|
||||||
|
|
||||||
|
/// Custom emoji to be used for rendering poll options.
|
||||||
|
public let emojis: [CustomEmoji]?
|
||||||
|
|
||||||
|
/// When called with a user token, has the authorized user voted?
|
||||||
|
public let voted: Bool?
|
||||||
|
|
||||||
|
/// When called with a user token, which options has the authorized user chosen? Contains an array of index values for options.
|
||||||
|
public let ownVotes: [Int]?
|
||||||
|
|
||||||
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
case id
|
||||||
|
case expiresAt = "expires_at"
|
||||||
|
case expired
|
||||||
|
case multiple
|
||||||
|
case votesCount = "votes_count"
|
||||||
|
case votersCount = "voters_count"
|
||||||
|
case options
|
||||||
|
case emojis
|
||||||
|
case voted
|
||||||
|
case ownVotes = "own_votes"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
//
|
||||||
|
// https://mczachurski.dev
|
||||||
|
// Copyright © 2023 Marcin Czachurski and the repository contributors.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// Possible answers for the poll.
|
||||||
|
public struct PollOption: Codable {
|
||||||
|
|
||||||
|
/// The text value of the poll option.
|
||||||
|
public let title: String
|
||||||
|
|
||||||
|
/// he total number of received votes for this option. NULLABLE Integer, or null if results are not published yet.
|
||||||
|
public let votesCount: Int?
|
||||||
|
|
||||||
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
case title
|
||||||
|
case votesCount = "votes_count"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
//
|
||||||
|
// https://mczachurski.dev
|
||||||
|
// Copyright © 2022 Marcin Czachurski and the repository contributors.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// Represents a rich preview card that is generated using OpenGraph tags from a URL.
|
||||||
|
public struct PreviewCard: Codable {
|
||||||
|
public enum PreviewCardType: String, Codable {
|
||||||
|
case link = "link"
|
||||||
|
case photo = "photo"
|
||||||
|
case video = "video"
|
||||||
|
case rich = "rich"
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Location of linked resource.
|
||||||
|
public let url: URL
|
||||||
|
|
||||||
|
/// Title of linked resource.
|
||||||
|
public let title: String
|
||||||
|
|
||||||
|
/// Description of preview.
|
||||||
|
public let description: String
|
||||||
|
|
||||||
|
/// The type of the preview card.
|
||||||
|
public let type: PreviewCardType
|
||||||
|
|
||||||
|
/// The author of the original resource.
|
||||||
|
public let authorName: String?
|
||||||
|
|
||||||
|
/// A link to the author of the original resource.
|
||||||
|
public let authorUrl: String?
|
||||||
|
|
||||||
|
/// The provider of the original resource.
|
||||||
|
public let providerName: String?
|
||||||
|
|
||||||
|
/// A link to the provider of the original resource.
|
||||||
|
public let providerUrl: String?
|
||||||
|
|
||||||
|
/// HTML to be used for generating the preview card.
|
||||||
|
public let html: String?
|
||||||
|
|
||||||
|
/// Width of preview, in pixels.
|
||||||
|
public let width: Int?
|
||||||
|
|
||||||
|
/// Height of preview, in pixels.
|
||||||
|
public let height: Int?
|
||||||
|
|
||||||
|
/// Preview thumbnail.
|
||||||
|
public let image: URL?
|
||||||
|
|
||||||
|
/// Used for photo embeds, instead of custom html.
|
||||||
|
public let embedUrl: URL?
|
||||||
|
|
||||||
|
/// A hash computed by the [BlurHash](https://github.com/woltapp/blurhash) algorithm, for generating colorful preview thumbnails when media has not been downloaded yet.
|
||||||
|
public let blurhash: String?
|
||||||
|
|
||||||
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
case url
|
||||||
|
case title
|
||||||
|
case description
|
||||||
|
case type
|
||||||
|
|
||||||
|
case authorName = "author_name"
|
||||||
|
case authorUrl = "author_url"
|
||||||
|
case providerName = "provider_name"
|
||||||
|
case providerUrl = "provider_url"
|
||||||
|
case html
|
||||||
|
case width
|
||||||
|
case height
|
||||||
|
case image
|
||||||
|
case embedUrl = "embed_url"
|
||||||
|
case blurhash
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
//
|
||||||
|
// https://mczachurski.dev
|
||||||
|
// Copyright © 2023 Marcin Czachurski and the repository contributors.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// Information about registering for this website.
|
||||||
|
public struct Registration: Codable {
|
||||||
|
|
||||||
|
/// Whether registrations are enabled.
|
||||||
|
public let enabled: Bool
|
||||||
|
|
||||||
|
/// Whether registrations require moderator approval.
|
||||||
|
public let approvalRequired: Bool
|
||||||
|
|
||||||
|
/// A custom message to be shown when registrations are closed. String (HTML) or null.
|
||||||
|
public let message: String?
|
||||||
|
|
||||||
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
case enabled
|
||||||
|
case approvalRequired = "approval_required"
|
||||||
|
case message
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,20 +6,50 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
/// Represents the relationship between accounts, such as following / blocking / muting / etc.
|
||||||
public struct Relationship: Codable {
|
public struct Relationship: Codable {
|
||||||
public let id: String
|
|
||||||
|
/// The account ID.
|
||||||
|
public let id: EntityId
|
||||||
|
|
||||||
|
/// Are you following this user?
|
||||||
public let following: Bool
|
public let following: Bool
|
||||||
|
|
||||||
|
/// Are you followed by this user?
|
||||||
public let followedBy: Bool
|
public let followedBy: Bool
|
||||||
|
|
||||||
|
/// Are you blocking this user?
|
||||||
public let blocking: Bool
|
public let blocking: Bool
|
||||||
|
|
||||||
|
/// Is this user blocking you?
|
||||||
public let blockedBy: Bool
|
public let blockedBy: Bool
|
||||||
|
|
||||||
|
/// Are you muting this user?
|
||||||
public let muting: Bool
|
public let muting: Bool
|
||||||
|
|
||||||
|
/// Are you muting notifications from this user?
|
||||||
public let mutingNotifications: Bool
|
public let mutingNotifications: Bool
|
||||||
|
|
||||||
|
/// Do you have a pending follow request for this user?
|
||||||
public let requested: Bool
|
public let requested: Bool
|
||||||
|
|
||||||
|
/// Are you receiving this user’s boosts in your home timeline?
|
||||||
public let showingReblogs: Bool
|
public let showingReblogs: Bool
|
||||||
|
|
||||||
|
/// Have you enabled notifications for this user?
|
||||||
public let notifying: Bool
|
public let notifying: Bool
|
||||||
|
|
||||||
|
/// Are you blocking this user’s domain?
|
||||||
public let domainBlocking: Bool
|
public let domainBlocking: Bool
|
||||||
|
|
||||||
|
/// Are you featuring this user on your profile?
|
||||||
public let endorsed: Bool
|
public let endorsed: Bool
|
||||||
|
|
||||||
|
/// Which languages are you following from this user? Array of String (ISO 639-1 language two-letter code).
|
||||||
|
public let languages: [String]?
|
||||||
|
|
||||||
|
/// This user’s profile bio.
|
||||||
|
public let note: String?
|
||||||
|
|
||||||
private enum CodingKeys: String, CodingKey {
|
private enum CodingKeys: String, CodingKey {
|
||||||
case id
|
case id
|
||||||
|
@ -34,6 +64,8 @@ public struct Relationship: Codable {
|
||||||
case notifying
|
case notifying
|
||||||
case domainBlocking = "domain_blocking"
|
case domainBlocking = "domain_blocking"
|
||||||
case endorsed
|
case endorsed
|
||||||
|
case languages
|
||||||
|
case note
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(from decoder: Decoder) throws {
|
public init(from decoder: Decoder) throws {
|
||||||
|
@ -50,6 +82,8 @@ public struct Relationship: Codable {
|
||||||
self.notifying = (try? container.decode(Bool.self, forKey: .notifying)) ?? false
|
self.notifying = (try? container.decode(Bool.self, forKey: .notifying)) ?? false
|
||||||
self.domainBlocking = (try? container.decode(Bool.self, forKey: .domainBlocking)) ?? false
|
self.domainBlocking = (try? container.decode(Bool.self, forKey: .domainBlocking)) ?? false
|
||||||
self.endorsed = (try? container.decode(Bool.self, forKey: .endorsed)) ?? false
|
self.endorsed = (try? container.decode(Bool.self, forKey: .endorsed)) ?? false
|
||||||
|
self.languages = try? container.decodeIfPresent([String].self, forKey: .languages)
|
||||||
|
self.note = try? container.decodeIfPresent(String.self, forKey: .note)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode(to encoder: Encoder) throws {
|
public func encode(to encoder: Encoder) throws {
|
||||||
|
@ -66,5 +100,13 @@ public struct Relationship: Codable {
|
||||||
try container.encode(notifying, forKey: .notifying)
|
try container.encode(notifying, forKey: .notifying)
|
||||||
try container.encode(domainBlocking, forKey: .domainBlocking)
|
try container.encode(domainBlocking, forKey: .domainBlocking)
|
||||||
try container.encode(endorsed, forKey: .endorsed)
|
try container.encode(endorsed, forKey: .endorsed)
|
||||||
|
|
||||||
|
if let languages {
|
||||||
|
try container.encode(languages, forKey: .languages)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let note {
|
||||||
|
try container.encode(note, forKey: .note)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,18 +6,57 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
/// Reports filed against users and/or statuses, to be taken action on by moderators.
|
||||||
public struct Report: Codable {
|
public struct Report: Codable {
|
||||||
public let id: String
|
public enum ReportCategoryTye: String, Codable {
|
||||||
public let actionTaken: String?
|
/// Unwanted or repetitive content
|
||||||
|
case spam = "spam"
|
||||||
public enum CodingKeys: CodingKey {
|
/// A specific rule was violated
|
||||||
case id
|
case violation = "violation"
|
||||||
case actionTaken
|
/// Some other reason
|
||||||
|
case other = "other"
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(from decoder: Decoder) throws {
|
/// The ID of the report in the database.
|
||||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
public let id: EntityId
|
||||||
self.id = try container.decode(String.self, forKey: .id)
|
|
||||||
self.actionTaken = try? container.decodeIfPresent(String.self, forKey: .actionTaken)
|
/// Whether an action was taken yet.
|
||||||
|
public let actionTaken: String?
|
||||||
|
|
||||||
|
/// When an action was taken against the report. NULLABLE String (ISO 8601 Datetime) or null.
|
||||||
|
public let actionTakenAt: String?
|
||||||
|
|
||||||
|
/// The generic reason for the report.
|
||||||
|
public let category: ReportCategoryTye
|
||||||
|
|
||||||
|
/// The reason for the report.
|
||||||
|
public let comment: String
|
||||||
|
|
||||||
|
/// Whether the report was forwarded to a remote domain.
|
||||||
|
public let forwarded: Bool
|
||||||
|
|
||||||
|
/// When the report was created. String (ISO 8601 Datetime).
|
||||||
|
public let createdAt: String
|
||||||
|
|
||||||
|
/// List od statuses in the report.
|
||||||
|
public let statusIds: [EntityId]?
|
||||||
|
|
||||||
|
/// List of the rules in ther report.
|
||||||
|
public let ruleIds: [EntityId]?
|
||||||
|
|
||||||
|
/// The account that was reported.
|
||||||
|
public let targetAccount: Account
|
||||||
|
|
||||||
|
public enum CodingKeys: String, CodingKey {
|
||||||
|
case id
|
||||||
|
case actionTaken
|
||||||
|
case actionTakenAt = "action_taken_at"
|
||||||
|
case category
|
||||||
|
case comment
|
||||||
|
case forwarded
|
||||||
|
case createdAt = "created_at"
|
||||||
|
case statusIds = "status_ids"
|
||||||
|
case ruleIds = "rule_ids"
|
||||||
|
case targetAccount = "target_account"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,12 +6,17 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public typealias Hashtag = String
|
/// Search results.
|
||||||
|
|
||||||
public struct Result: Codable {
|
public struct Result: Codable {
|
||||||
|
|
||||||
|
/// List of accoutns.
|
||||||
public let accounts: [Account]
|
public let accounts: [Account]
|
||||||
|
|
||||||
|
/// List od statuses.
|
||||||
public let statuses: [Status]
|
public let statuses: [Status]
|
||||||
public let hashtags: [Hashtag]
|
|
||||||
|
|
||||||
|
public let hashtags: [Tag]
|
||||||
|
|
||||||
public enum CodingKeys: CodingKey {
|
public enum CodingKeys: CodingKey {
|
||||||
case accounts
|
case accounts
|
||||||
|
@ -23,6 +28,6 @@ public struct Result: Codable {
|
||||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
self.accounts = (try? container.decode([Account].self, forKey: .accounts)) ?? []
|
self.accounts = (try? container.decode([Account].self, forKey: .accounts)) ?? []
|
||||||
self.statuses = (try? container.decode([Status].self, forKey: .statuses)) ?? []
|
self.statuses = (try? container.decode([Status].self, forKey: .statuses)) ?? []
|
||||||
self.hashtags = (try? container.decode([Hashtag].self, forKey: .hashtags)) ?? []
|
self.hashtags = (try? container.decode([Tag].self, forKey: .hashtags)) ?? []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
//
|
||||||
|
// https://mczachurski.dev
|
||||||
|
// Copyright © 2023 Marcin Czachurski and the repository contributors.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// Represents a rule that server users should follow.
|
||||||
|
public struct Rule: Codable {
|
||||||
|
|
||||||
|
/// An identifier for the rule.
|
||||||
|
public let id: EntityId
|
||||||
|
|
||||||
|
/// The rule to be followed.
|
||||||
|
public let text: String
|
||||||
|
|
||||||
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
case id
|
||||||
|
case text
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,9 +6,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public typealias EntityId = String
|
/// Represents a status posted by an account.
|
||||||
public typealias Html = String
|
|
||||||
|
|
||||||
public class Status: Codable {
|
public class Status: Codable {
|
||||||
public enum Visibility: String, Codable {
|
public enum Visibility: String, Codable {
|
||||||
case pub = "public"
|
case pub = "public"
|
||||||
|
@ -17,34 +15,103 @@ public class Status: Codable {
|
||||||
case direct = "direct"
|
case direct = "direct"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ID of the status in the database.
|
||||||
public let id: EntityId
|
public let id: EntityId
|
||||||
|
|
||||||
|
/// HTML-encoded status content.
|
||||||
public let content: Html
|
public let content: Html
|
||||||
|
|
||||||
|
/// URI of the status used for federation.
|
||||||
public let uri: String?
|
public let uri: String?
|
||||||
|
|
||||||
|
/// A link to the status’s HTML representation.
|
||||||
public let url: URL?
|
public let url: URL?
|
||||||
public let account: Account
|
|
||||||
public let inReplyToId: AccountId?
|
|
||||||
public let inReplyToAccount: EntityId?
|
|
||||||
public let reblog: Status?
|
|
||||||
public let createdAt: String
|
|
||||||
public let reblogsCount: Int
|
|
||||||
public let favouritesCount: Int
|
|
||||||
public let repliesCount: Int
|
|
||||||
public let reblogged: Bool
|
|
||||||
public let favourited: Bool
|
|
||||||
public let sensitive: Bool
|
|
||||||
public let bookmarked: Bool
|
|
||||||
public let pinned: Bool
|
|
||||||
public let muted: Bool
|
|
||||||
public let spoilerText: String?
|
|
||||||
public let visibility: Visibility
|
|
||||||
public let mediaAttachments: [Attachment]
|
|
||||||
public let card: Card?
|
|
||||||
public let mentions: [Mention]
|
|
||||||
public let tags: [Tag]
|
|
||||||
public let application: Application?
|
|
||||||
public let place: Place?
|
|
||||||
|
|
||||||
|
/// The account that authored this status.
|
||||||
|
public let account: Account
|
||||||
|
|
||||||
|
/// ID of the status being replied to.
|
||||||
|
public let inReplyToId: EntityId?
|
||||||
|
|
||||||
|
/// ID of the account that authored the status being replied to.
|
||||||
|
public let inReplyToAccount: EntityId?
|
||||||
|
|
||||||
|
/// The status being reblogged.
|
||||||
|
public let reblog: Status?
|
||||||
|
|
||||||
|
/// The date when this status was created. String (ISO 8601 Datetime).
|
||||||
|
public let createdAt: String
|
||||||
|
|
||||||
|
/// How many boosts this status has received.
|
||||||
|
public let reblogsCount: Int
|
||||||
|
|
||||||
|
/// How many favourites this status has received.
|
||||||
|
public let favouritesCount: Int
|
||||||
|
|
||||||
|
/// How many replies this status has received.
|
||||||
|
public let repliesCount: Int
|
||||||
|
|
||||||
|
/// If the current token has an authorized user: Have you boosted this status?
|
||||||
|
public let reblogged: Bool
|
||||||
|
|
||||||
|
/// If the current token has an authorized user: Have you favourited this status?
|
||||||
|
public let favourited: Bool
|
||||||
|
|
||||||
|
/// Is this status marked as sensitive content?
|
||||||
|
public let sensitive: Bool
|
||||||
|
|
||||||
|
/// If the current token has an authorized user: Have you bookmarked this status?
|
||||||
|
public let bookmarked: Bool
|
||||||
|
|
||||||
|
/// If the current token has an authorized user: Have you pinned this status? Only appears if the status is pinnable.
|
||||||
|
public let pinned: Bool
|
||||||
|
|
||||||
|
/// If the current token has an authorized user: Have you muted notifications for this status’s conversation?
|
||||||
|
public let muted: Bool
|
||||||
|
|
||||||
|
/// Subject or summary line, below which status content is collapsed until expanded.
|
||||||
|
public let spoilerText: String?
|
||||||
|
|
||||||
|
/// Visibility of this status.
|
||||||
|
public let visibility: Visibility
|
||||||
|
|
||||||
|
/// Media that is attached to this status.
|
||||||
|
public let mediaAttachments: [MediaAttachment]
|
||||||
|
|
||||||
|
/// Preview card for links included within status content.
|
||||||
|
public let card: PreviewCard?
|
||||||
|
|
||||||
|
/// Mentions of users within the status content.
|
||||||
|
public let mentions: [Mention]
|
||||||
|
|
||||||
|
/// Hashtags used within the status content.
|
||||||
|
public let tags: [Tag]
|
||||||
|
|
||||||
|
/// The application used to post this status.
|
||||||
|
public let application: BaseApplication?
|
||||||
|
|
||||||
|
/// Place where photo has been taken (specific for Pixelfed).
|
||||||
|
public let place: Place?
|
||||||
|
|
||||||
|
/// Custom emoji to be used when rendering status content.
|
||||||
|
public let emojis: [CustomEmoji]?
|
||||||
|
|
||||||
|
/// The poll attached to the status.
|
||||||
|
public let poll: Poll?
|
||||||
|
|
||||||
|
/// Primary language of this status. NULLABLE String (ISO 639 Part 1 two-letter language code) or null.
|
||||||
|
public let language: String?
|
||||||
|
|
||||||
|
/// Plain-text source of a status. Returned instead of content when status is deleted, so the user may redraft from the source text
|
||||||
|
/// without the client having to reverse-engineer the original text from the HTML content.
|
||||||
|
public let text: String?
|
||||||
|
|
||||||
|
/// Timestamp of when the status was last edited. NULLABLE String (ISO 8601 Datetime).
|
||||||
|
public let editedAt: String?
|
||||||
|
|
||||||
|
/// If the current token has an authorized user: The filter and keywords that matched this status.
|
||||||
|
public let filtered: FilterResult?
|
||||||
|
|
||||||
private enum CodingKeys: String, CodingKey {
|
private enum CodingKeys: String, CodingKey {
|
||||||
case id
|
case id
|
||||||
case uri
|
case uri
|
||||||
|
@ -72,6 +139,12 @@ public class Status: Codable {
|
||||||
case tags
|
case tags
|
||||||
case application
|
case application
|
||||||
case place
|
case place
|
||||||
|
case emojis
|
||||||
|
case poll
|
||||||
|
case language
|
||||||
|
case text
|
||||||
|
case editedAt = "edited_at"
|
||||||
|
case filtered
|
||||||
}
|
}
|
||||||
|
|
||||||
public required init(from decoder: Decoder) throws {
|
public required init(from decoder: Decoder) throws {
|
||||||
|
@ -83,7 +156,7 @@ public class Status: Codable {
|
||||||
self.account = try container.decode(Account.self, forKey: .account)
|
self.account = try container.decode(Account.self, forKey: .account)
|
||||||
self.content = try container.decode(Html.self, forKey: .content)
|
self.content = try container.decode(Html.self, forKey: .content)
|
||||||
self.createdAt = try container.decode(String.self, forKey: .createdAt)
|
self.createdAt = try container.decode(String.self, forKey: .createdAt)
|
||||||
self.inReplyToId = try? container.decode(AccountId.self, forKey: .inReplyToId)
|
self.inReplyToId = try? container.decode(EntityId.self, forKey: .inReplyToId)
|
||||||
self.inReplyToAccount = try? container.decode(EntityId.self, forKey: .inReplyToAccount)
|
self.inReplyToAccount = try? container.decode(EntityId.self, forKey: .inReplyToAccount)
|
||||||
self.reblog = try? container.decode(Status.self, forKey: .reblog)
|
self.reblog = try? container.decode(Status.self, forKey: .reblog)
|
||||||
self.spoilerText = try? container.decode(String.self, forKey: .spoilerText)
|
self.spoilerText = try? container.decode(String.self, forKey: .spoilerText)
|
||||||
|
@ -97,12 +170,18 @@ public class Status: Codable {
|
||||||
self.pinned = (try? container.decode(Bool.self, forKey: .pinned)) ?? false
|
self.pinned = (try? container.decode(Bool.self, forKey: .pinned)) ?? false
|
||||||
self.muted = (try? container.decode(Bool.self, forKey: .muted)) ?? false
|
self.muted = (try? container.decode(Bool.self, forKey: .muted)) ?? false
|
||||||
self.visibility = try container.decode(Visibility.self, forKey: .visibility)
|
self.visibility = try container.decode(Visibility.self, forKey: .visibility)
|
||||||
self.mediaAttachments = (try? container.decode([Attachment].self, forKey: .mediaAttachments)) ?? []
|
self.mediaAttachments = (try? container.decode([MediaAttachment].self, forKey: .mediaAttachments)) ?? []
|
||||||
self.card = try? container.decode(Card.self, forKey: .card)
|
self.card = try? container.decode(PreviewCard.self, forKey: .card)
|
||||||
self.mentions = (try? container.decode([Mention].self, forKey: .mentions)) ?? []
|
self.mentions = (try? container.decode([Mention].self, forKey: .mentions)) ?? []
|
||||||
self.tags = (try? container.decode([Tag].self, forKey: .tags)) ?? []
|
self.tags = (try? container.decode([Tag].self, forKey: .tags)) ?? []
|
||||||
self.application = try? container.decode(Application.self, forKey: .application)
|
self.application = try? container.decode(BaseApplication.self, forKey: .application)
|
||||||
self.place = try? container.decode(Place.self, forKey: .place)
|
self.place = try? container.decodeIfPresent(Place.self, forKey: .place)
|
||||||
|
self.emojis = try? container.decodeIfPresent([CustomEmoji].self, forKey: .emojis)
|
||||||
|
self.poll = try? container.decodeIfPresent(Poll.self, forKey: .poll)
|
||||||
|
self.language = try? container.decodeIfPresent(String.self, forKey: .language)
|
||||||
|
self.text = try? container.decodeIfPresent(String.self, forKey: .text)
|
||||||
|
self.editedAt = try? container.decodeIfPresent(String.self, forKey: .editedAt)
|
||||||
|
self.filtered = try? container.decodeIfPresent(FilterResult.self, forKey: .filtered)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode(to encoder: Encoder) throws {
|
public func encode(to encoder: Encoder) throws {
|
||||||
|
@ -110,6 +189,7 @@ public class Status: Codable {
|
||||||
|
|
||||||
try container.encode(id, forKey: .id)
|
try container.encode(id, forKey: .id)
|
||||||
try container.encode(uri, forKey: .uri)
|
try container.encode(uri, forKey: .uri)
|
||||||
|
|
||||||
if let url {
|
if let url {
|
||||||
try container.encode(url, forKey: .url)
|
try container.encode(url, forKey: .url)
|
||||||
}
|
}
|
||||||
|
@ -117,18 +197,23 @@ public class Status: Codable {
|
||||||
try container.encode(account, forKey: .account)
|
try container.encode(account, forKey: .account)
|
||||||
try container.encode(content, forKey: .content)
|
try container.encode(content, forKey: .content)
|
||||||
try container.encode(createdAt, forKey: .createdAt)
|
try container.encode(createdAt, forKey: .createdAt)
|
||||||
|
|
||||||
if let inReplyToId {
|
if let inReplyToId {
|
||||||
try container.encode(inReplyToId, forKey: .inReplyToId)
|
try container.encode(inReplyToId, forKey: .inReplyToId)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let inReplyToAccount {
|
if let inReplyToAccount {
|
||||||
try container.encode(inReplyToAccount, forKey: .inReplyToAccount)
|
try container.encode(inReplyToAccount, forKey: .inReplyToAccount)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let reblog {
|
if let reblog {
|
||||||
try container.encode(reblog, forKey: .reblog)
|
try container.encode(reblog, forKey: .reblog)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let spoilerText {
|
if let spoilerText {
|
||||||
try container.encode(spoilerText, forKey: .spoilerText)
|
try container.encode(spoilerText, forKey: .spoilerText)
|
||||||
}
|
}
|
||||||
|
|
||||||
try container.encode(reblogsCount, forKey: .reblogsCount)
|
try container.encode(reblogsCount, forKey: .reblogsCount)
|
||||||
try container.encode(favouritesCount, forKey: .favouritesCount)
|
try container.encode(favouritesCount, forKey: .favouritesCount)
|
||||||
try container.encode(repliesCount, forKey: .repliesCount)
|
try container.encode(repliesCount, forKey: .repliesCount)
|
||||||
|
@ -140,16 +225,44 @@ public class Status: Codable {
|
||||||
try container.encode(sensitive, forKey: .sensitive)
|
try container.encode(sensitive, forKey: .sensitive)
|
||||||
try container.encode(visibility, forKey: .visibility)
|
try container.encode(visibility, forKey: .visibility)
|
||||||
try container.encode(mediaAttachments, forKey: .mediaAttachments)
|
try container.encode(mediaAttachments, forKey: .mediaAttachments)
|
||||||
|
|
||||||
if let card {
|
if let card {
|
||||||
try container.encode(card, forKey: .card)
|
try container.encode(card, forKey: .card)
|
||||||
}
|
}
|
||||||
|
|
||||||
try container.encode(mentions, forKey: .mentions)
|
try container.encode(mentions, forKey: .mentions)
|
||||||
try container.encode(tags, forKey: .tags)
|
try container.encode(tags, forKey: .tags)
|
||||||
|
|
||||||
if let application {
|
if let application {
|
||||||
try container.encode(application, forKey: .application)
|
try container.encode(application, forKey: .application)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let place {
|
if let place {
|
||||||
try container.encode(place, forKey: .place)
|
try container.encode(place, forKey: .place)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let emojis {
|
||||||
|
try container.encode(emojis, forKey: .emojis)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let poll {
|
||||||
|
try container.encode(poll, forKey: .poll)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let language {
|
||||||
|
try container.encode(language, forKey: .language)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let text {
|
||||||
|
try container.encode(text, forKey: .text)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let editedAt {
|
||||||
|
try container.encode(editedAt, forKey: .editedAt)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let filtered {
|
||||||
|
try container.encode(filtered, forKey: .filtered)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,31 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
/// Represents a hashtag used within the content of a status.
|
||||||
public struct Tag: Codable {
|
public struct Tag: Codable {
|
||||||
|
|
||||||
|
/// The value of the hashtag after the # sign.
|
||||||
public let name: String
|
public let name: String
|
||||||
|
|
||||||
|
/// A link to the hashtag on the instance.
|
||||||
public let url: URL?
|
public let url: URL?
|
||||||
|
|
||||||
|
/// Usage statistics for given days (typically the past week).
|
||||||
|
public let history: [TagHistory]?
|
||||||
|
|
||||||
|
/// Whether the current token’s authorized user is following this tag.
|
||||||
|
public let following: Bool?
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Usage statistics for given days.
|
||||||
|
public struct TagHistory: Codable {
|
||||||
|
|
||||||
|
/// UNIX timestamp on midnight of the given day.
|
||||||
|
public let day: String
|
||||||
|
|
||||||
|
/// The counted usage of the tag within that day.
|
||||||
|
public let uses: String
|
||||||
|
|
||||||
|
/// he total of accounts using the tag within that day (cast from an integer).
|
||||||
|
public let accounts: String
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
//
|
||||||
|
// https://mczachurski.dev
|
||||||
|
// Copyright © 2023 Marcin Czachurski and the repository contributors.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// An image used to represent this instance.
|
||||||
|
public struct Thumbnail: Codable {
|
||||||
|
|
||||||
|
/// The URL for the thumbnail image.
|
||||||
|
public let url: URL
|
||||||
|
|
||||||
|
/// A hash computed by the [BlurHash](https://github.com/woltapp/blurhash) algorithm, for generating colorful preview thumbnails when media has not been downloaded yet.
|
||||||
|
public let blurhash: String?
|
||||||
|
|
||||||
|
/// Links to scaled resolution images, for high DPI screens.
|
||||||
|
public let versions: ThumbnailVersions?
|
||||||
|
|
||||||
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
case url
|
||||||
|
case blurhash
|
||||||
|
case versions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Links to scaled resolution images, for high DPI screens.
|
||||||
|
public struct ThumbnailVersions: Codable {
|
||||||
|
|
||||||
|
/// The URL for the thumbnail image at 1x resolution.
|
||||||
|
public let x1: URL?
|
||||||
|
|
||||||
|
/// The URL for the thumbnail image at 2x resolution.
|
||||||
|
public let x2: URL?
|
||||||
|
|
||||||
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
case x1 = "@1x"
|
||||||
|
case x2 = "@2x"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
//
|
||||||
|
// https://mczachurski.dev
|
||||||
|
// Copyright © 2023 Marcin Czachurski and the repository contributors.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public typealias EntityId = String
|
||||||
|
public typealias Html = String
|
||||||
|
public typealias SearchQuery = String
|
||||||
|
|
||||||
|
public typealias ClientId = String
|
||||||
|
public typealias ClientSecret = String
|
||||||
|
public typealias UsernameType = String
|
||||||
|
public typealias PasswordType = String
|
||||||
|
|
||||||
|
public typealias SinceId = EntityId
|
||||||
|
public typealias MaxId = EntityId
|
||||||
|
public typealias MinId = EntityId
|
||||||
|
public typealias Limit = Int
|
||||||
|
public typealias Page = Int
|
||||||
|
|
||||||
|
public typealias Scope = String
|
||||||
|
public typealias Scopes = [Scope]
|
||||||
|
public typealias Token = String
|
|
@ -0,0 +1,29 @@
|
||||||
|
//
|
||||||
|
// https://mczachurski.dev
|
||||||
|
// Copyright © 2023 Marcin Czachurski and the repository contributors.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// Usage data for this instance.
|
||||||
|
public struct Usage: Codable {
|
||||||
|
|
||||||
|
/// Usage data related to users on this instance.
|
||||||
|
public let users: UsageUsers
|
||||||
|
|
||||||
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
case users
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage data related to users on this instance.
|
||||||
|
public struct UsageUsers: Codable {
|
||||||
|
|
||||||
|
/// The number of active users in the past 4 weeks.
|
||||||
|
public let activeMonth: Int
|
||||||
|
|
||||||
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
case activeMonth = "active_month"
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,7 +11,7 @@ public extension MastodonClient {
|
||||||
func createApp(named name: String,
|
func createApp(named name: String,
|
||||||
redirectUri: String = "urn:ietf:wg:oauth:2.0:oob",
|
redirectUri: String = "urn:ietf:wg:oauth:2.0:oob",
|
||||||
scopes: Scopes,
|
scopes: Scopes,
|
||||||
website: URL) async throws -> App {
|
website: URL) async throws -> Application {
|
||||||
|
|
||||||
let request = try Self.request(
|
let request = try Self.request(
|
||||||
for: baseURL,
|
for: baseURL,
|
||||||
|
@ -23,10 +23,10 @@ public extension MastodonClient {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return try await downloadJson(App.self, request: request)
|
return try await downloadJson(Application.self, request: request)
|
||||||
}
|
}
|
||||||
|
|
||||||
func authenticate(app: App, scope: Scopes) async throws -> OAuthSwiftCredential { // todo: we should not load OAuthSwift objects here
|
func authenticate(app: Application, scope: Scopes) async throws -> OAuthSwiftCredential { // todo: we should not load OAuthSwift objects here
|
||||||
oauthClient = OAuth2Swift(
|
oauthClient = OAuth2Swift(
|
||||||
consumerKey: app.clientId,
|
consumerKey: app.clientId,
|
||||||
consumerSecret: app.clientSecret,
|
consumerSecret: app.clientSecret,
|
||||||
|
@ -59,7 +59,7 @@ public extension MastodonClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
@available(*, deprecated, message: "The password flow is discoured and won't support 2FA. Please use authentiate(app:, scope:)")
|
@available(*, deprecated, message: "The password flow is discoured and won't support 2FA. Please use authentiate(app:, scope:)")
|
||||||
func getToken(withApp app: App,
|
func getToken(withApp app: Application,
|
||||||
username: String,
|
username: String,
|
||||||
password: String,
|
password: String,
|
||||||
scope: Scopes) async throws -> AccessToken {
|
scope: Scopes) async throws -> AccessToken {
|
||||||
|
|
|
@ -7,10 +7,6 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import OAuthSwift
|
import OAuthSwift
|
||||||
|
|
||||||
public typealias Scope = String
|
|
||||||
public typealias Scopes = [Scope]
|
|
||||||
public typealias Token = String
|
|
||||||
|
|
||||||
public enum MastodonClientError: Swift.Error {
|
public enum MastodonClientError: Swift.Error {
|
||||||
case oAuthCancelled
|
case oAuthCancelled
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,18 +8,18 @@ import Foundation
|
||||||
|
|
||||||
extension Mastodon {
|
extension Mastodon {
|
||||||
public enum Account {
|
public enum Account {
|
||||||
case account(AccountId)
|
case account(EntityId)
|
||||||
case verifyCredentials
|
case verifyCredentials
|
||||||
case followers(AccountId, MaxId?, SinceId?, MinId?, Limit?, Page?)
|
case followers(EntityId, MaxId?, SinceId?, MinId?, Limit?, Page?)
|
||||||
case following(AccountId, MaxId?, SinceId?, MinId?, Limit?, Page?)
|
case following(EntityId, MaxId?, SinceId?, MinId?, Limit?, Page?)
|
||||||
case statuses(AccountId, Bool, Bool, MaxId?, SinceId?, MinId?, Limit?)
|
case statuses(EntityId, Bool, Bool, MaxId?, SinceId?, MinId?, Limit?)
|
||||||
case follow(AccountId)
|
case follow(EntityId)
|
||||||
case unfollow(AccountId)
|
case unfollow(EntityId)
|
||||||
case block(AccountId)
|
case block(EntityId)
|
||||||
case unblock(AccountId)
|
case unblock(EntityId)
|
||||||
case mute(AccountId)
|
case mute(EntityId)
|
||||||
case unmute(AccountId)
|
case unmute(EntityId)
|
||||||
case relationships([AccountId])
|
case relationships([EntityId])
|
||||||
case search(SearchQuery, Int)
|
case search(SearchQuery, Int)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,5 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public typealias AccountId = String
|
public class Mastodon {
|
||||||
public typealias SearchQuery = String
|
|
||||||
|
|
||||||
public class Mastodon {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,14 +6,9 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public typealias ClientId = String
|
|
||||||
public typealias ClientSecret = String
|
|
||||||
public typealias UsernameType = String
|
|
||||||
public typealias PasswordType = String
|
|
||||||
|
|
||||||
extension Mastodon {
|
extension Mastodon {
|
||||||
public enum OAuth {
|
public enum OAuth {
|
||||||
case authenticate(App, UsernameType, PasswordType, String?)
|
case authenticate(Application, UsernameType, PasswordType, String?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,12 +6,6 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public typealias SinceId = EntityId
|
|
||||||
public typealias MaxId = EntityId
|
|
||||||
public typealias MinId = EntityId
|
|
||||||
public typealias Limit = Int
|
|
||||||
public typealias Page = Int
|
|
||||||
|
|
||||||
extension Mastodon {
|
extension Mastodon {
|
||||||
public enum Timelines {
|
public enum Timelines {
|
||||||
case home(MaxId?, SinceId?, MinId?, Limit?)
|
case home(MaxId?, SinceId?, MinId?, Limit?)
|
||||||
|
|
|
@ -96,7 +96,6 @@
|
||||||
F89D6C4429718092001DA3D4 /* AccentsSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = F89D6C4329718092001DA3D4 /* AccentsSection.swift */; };
|
F89D6C4429718092001DA3D4 /* AccentsSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = F89D6C4329718092001DA3D4 /* AccentsSection.swift */; };
|
||||||
F89D6C4629718193001DA3D4 /* ThemeSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = F89D6C4529718193001DA3D4 /* ThemeSection.swift */; };
|
F89D6C4629718193001DA3D4 /* ThemeSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = F89D6C4529718193001DA3D4 /* ThemeSection.swift */; };
|
||||||
F89D6C4A297196FF001DA3D4 /* ImagesViewer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F89D6C49297196FF001DA3D4 /* ImagesViewer.swift */; };
|
F89D6C4A297196FF001DA3D4 /* ImagesViewer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F89D6C49297196FF001DA3D4 /* ImagesViewer.swift */; };
|
||||||
F89D6C4C297197FE001DA3D4 /* ImageViewerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F89D6C4B297197FE001DA3D4 /* ImageViewerViewModel.swift */; };
|
|
||||||
F8A93D7E2965FD89001D8331 /* UserProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A93D7D2965FD89001D8331 /* UserProfileView.swift */; };
|
F8A93D7E2965FD89001D8331 /* UserProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A93D7D2965FD89001D8331 /* UserProfileView.swift */; };
|
||||||
F8A93D802965FED4001D8331 /* AccountService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A93D7F2965FED4001D8331 /* AccountService.swift */; };
|
F8A93D802965FED4001D8331 /* AccountService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A93D7F2965FED4001D8331 /* AccountService.swift */; };
|
||||||
F8B1E64F2973F61400EE0D10 /* Drops in Frameworks */ = {isa = PBXBuildFile; productRef = F8B1E64E2973F61400EE0D10 /* Drops */; };
|
F8B1E64F2973F61400EE0D10 /* Drops in Frameworks */ = {isa = PBXBuildFile; productRef = F8B1E64E2973F61400EE0D10 /* Drops */; };
|
||||||
|
@ -196,7 +195,6 @@
|
||||||
F89D6C4329718092001DA3D4 /* AccentsSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccentsSection.swift; sourceTree = "<group>"; };
|
F89D6C4329718092001DA3D4 /* AccentsSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccentsSection.swift; sourceTree = "<group>"; };
|
||||||
F89D6C4529718193001DA3D4 /* ThemeSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeSection.swift; sourceTree = "<group>"; };
|
F89D6C4529718193001DA3D4 /* ThemeSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeSection.swift; sourceTree = "<group>"; };
|
||||||
F89D6C49297196FF001DA3D4 /* ImagesViewer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImagesViewer.swift; sourceTree = "<group>"; };
|
F89D6C49297196FF001DA3D4 /* ImagesViewer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImagesViewer.swift; sourceTree = "<group>"; };
|
||||||
F89D6C4B297197FE001DA3D4 /* ImageViewerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageViewerViewModel.swift; sourceTree = "<group>"; };
|
|
||||||
F8A93D7D2965FD89001D8331 /* UserProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserProfileView.swift; sourceTree = "<group>"; };
|
F8A93D7D2965FD89001D8331 /* UserProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserProfileView.swift; sourceTree = "<group>"; };
|
||||||
F8A93D7F2965FED4001D8331 /* AccountService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountService.swift; sourceTree = "<group>"; };
|
F8A93D7F2965FED4001D8331 /* AccountService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountService.swift; sourceTree = "<group>"; };
|
||||||
F8AF2A61297073FE00D2DA3F /* Vernissage20230112-001.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Vernissage20230112-001.xcdatamodel"; sourceTree = "<group>"; };
|
F8AF2A61297073FE00D2DA3F /* Vernissage20230112-001.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Vernissage20230112-001.xcdatamodel"; sourceTree = "<group>"; };
|
||||||
|
@ -457,7 +455,6 @@
|
||||||
children = (
|
children = (
|
||||||
F89992CB296D9231005994BF /* StatusViewModel.swift */,
|
F89992CB296D9231005994BF /* StatusViewModel.swift */,
|
||||||
F89992CD296D92E7005994BF /* AttachmentViewModel.swift */,
|
F89992CD296D92E7005994BF /* AttachmentViewModel.swift */,
|
||||||
F89D6C4B297197FE001DA3D4 /* ImageViewerViewModel.swift */,
|
|
||||||
F898DE7129728CB2004B4A6A /* CommentViewModel.swift */,
|
F898DE7129728CB2004B4A6A /* CommentViewModel.swift */,
|
||||||
);
|
);
|
||||||
path = ViewModels;
|
path = ViewModels;
|
||||||
|
@ -650,7 +647,6 @@
|
||||||
F88FAD25295F3FF7009B20C9 /* FederatedFeedView.swift in Sources */,
|
F88FAD25295F3FF7009B20C9 /* FederatedFeedView.swift in Sources */,
|
||||||
F88FAD32295F5029009B20C9 /* RemoteFileService.swift in Sources */,
|
F88FAD32295F5029009B20C9 /* RemoteFileService.swift in Sources */,
|
||||||
F88FAD27295F400E009B20C9 /* NotificationsView.swift in Sources */,
|
F88FAD27295F400E009B20C9 /* NotificationsView.swift in Sources */,
|
||||||
F89D6C4C297197FE001DA3D4 /* ImageViewerViewModel.swift in Sources */,
|
|
||||||
F86B7216296BFFDA00EE59EC /* UserProfileStatuses.swift in Sources */,
|
F86B7216296BFFDA00EE59EC /* UserProfileStatuses.swift in Sources */,
|
||||||
F897978F29684BCB00B22335 /* LoadingView.swift in Sources */,
|
F897978F29684BCB00B22335 /* LoadingView.swift in Sources */,
|
||||||
F89992C9296D6DC7005994BF /* CommentBody.swift in Sources */,
|
F89992C9296D6DC7005994BF /* CommentBody.swift in Sources */,
|
||||||
|
|
|
@ -8,7 +8,7 @@ import Foundation
|
||||||
import MastodonKit
|
import MastodonKit
|
||||||
|
|
||||||
extension AttachmentData {
|
extension AttachmentData {
|
||||||
func copyFrom(_ attachment: Attachment) {
|
func copyFrom(_ attachment: MediaAttachment) {
|
||||||
self.id = attachment.id
|
self.id = attachment.id
|
||||||
self.url = attachment.url
|
self.url = attachment.url
|
||||||
self.blurhash = attachment.blurhash
|
self.blurhash = attachment.blurhash
|
||||||
|
|
|
@ -112,7 +112,7 @@ public class AuthorizationService {
|
||||||
let client = MastodonClient(baseURL: accountData.serverUrl)
|
let client = MastodonClient(baseURL: accountData.serverUrl)
|
||||||
|
|
||||||
// Create application (we will get clientId amd clientSecret).
|
// Create application (we will get clientId amd clientSecret).
|
||||||
let oAuthApp = App(clientId: accountData.clientId, clientSecret: accountData.clientSecret)
|
let oAuthApp = Application(clientId: accountData.clientId, clientSecret: accountData.clientSecret)
|
||||||
|
|
||||||
// Authorize a user (browser, we will get clientCode).
|
// Authorize a user (browser, we will get clientCode).
|
||||||
let oAuthSwiftCredential = try await client.authenticate(app: oAuthApp, scope: Scopes(["read", "write", "follow", "push"]))
|
let oAuthSwiftCredential = try await client.authenticate(app: oAuthApp, scope: Scopes(["read", "write", "follow", "push"]))
|
||||||
|
|
|
@ -9,7 +9,7 @@ import MastodonKit
|
||||||
|
|
||||||
public class AttachmentViewModel {
|
public class AttachmentViewModel {
|
||||||
public let id: String
|
public let id: String
|
||||||
public let type: Attachment.AttachmentType
|
public let type: MediaAttachment.MediaAttachmentType
|
||||||
public let url: URL
|
public let url: URL
|
||||||
|
|
||||||
public let previewUrl: URL?
|
public let previewUrl: URL?
|
||||||
|
@ -28,7 +28,7 @@ public class AttachmentViewModel {
|
||||||
public var exifLens: String?
|
public var exifLens: String?
|
||||||
|
|
||||||
init(id: String,
|
init(id: String,
|
||||||
type: Attachment.AttachmentType,
|
type: MediaAttachment.MediaAttachmentType,
|
||||||
url: URL,
|
url: URL,
|
||||||
previewUrl: URL? = nil,
|
previewUrl: URL? = nil,
|
||||||
remoteUrl: URL? = nil,
|
remoteUrl: URL? = nil,
|
||||||
|
@ -60,7 +60,7 @@ public class AttachmentViewModel {
|
||||||
self.data = data
|
self.data = data
|
||||||
}
|
}
|
||||||
|
|
||||||
init(attachment: Attachment) {
|
init(attachment: MediaAttachment) {
|
||||||
self.id = attachment.id
|
self.id = attachment.id
|
||||||
self.type = attachment.type
|
self.type = attachment.type
|
||||||
self.url = attachment.url
|
self.url = attachment.url
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
//
|
|
||||||
// https://mczachurski.dev
|
|
||||||
// Copyright © 2023 Marcin Czachurski and the repository contributors.
|
|
||||||
// Licensed under the MIT License.
|
|
||||||
//
|
|
||||||
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
public struct ImageViewerViewModel {
|
|
||||||
// public var images: []
|
|
||||||
}
|
|
|
@ -16,7 +16,7 @@ public class StatusViewModel {
|
||||||
public let uri: String?
|
public let uri: String?
|
||||||
public let url: URL?
|
public let url: URL?
|
||||||
public let account: Account
|
public let account: Account
|
||||||
public let inReplyToId: AccountId?
|
public let inReplyToId: EntityId?
|
||||||
public let inReplyToAccount: EntityId?
|
public let inReplyToAccount: EntityId?
|
||||||
public let reblog: Status?
|
public let reblog: Status?
|
||||||
public let createdAt: String
|
public let createdAt: String
|
||||||
|
@ -32,10 +32,10 @@ public class StatusViewModel {
|
||||||
public let spoilerText: String?
|
public let spoilerText: String?
|
||||||
public let visibility: Status.Visibility
|
public let visibility: Status.Visibility
|
||||||
public let mediaAttachments: [AttachmentViewModel]
|
public let mediaAttachments: [AttachmentViewModel]
|
||||||
public let card: Card?
|
public let card: PreviewCard?
|
||||||
public let mentions: [Mention]
|
public let mentions: [Mention]
|
||||||
public let tags: [Tag]
|
public let tags: [Tag]
|
||||||
public let application: Application?
|
public let application: BaseApplication?
|
||||||
public let place: Place?
|
public let place: Place?
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
|
@ -43,9 +43,9 @@ public class StatusViewModel {
|
||||||
content: Html,
|
content: Html,
|
||||||
uri: String,
|
uri: String,
|
||||||
account: Account,
|
account: Account,
|
||||||
application: Application,
|
application: BaseApplication,
|
||||||
url: URL? = nil,
|
url: URL? = nil,
|
||||||
inReplyToId: AccountId? = nil,
|
inReplyToId: EntityId? = nil,
|
||||||
inReplyToAccount: EntityId? = nil,
|
inReplyToAccount: EntityId? = nil,
|
||||||
reblog: Status? = nil,
|
reblog: Status? = nil,
|
||||||
createdAt: String? = nil,
|
createdAt: String? = nil,
|
||||||
|
@ -61,7 +61,7 @@ public class StatusViewModel {
|
||||||
spoilerText: String? = nil,
|
spoilerText: String? = nil,
|
||||||
visibility: Status.Visibility = Status.Visibility.pub,
|
visibility: Status.Visibility = Status.Visibility.pub,
|
||||||
mediaAttachments: [AttachmentViewModel] = [],
|
mediaAttachments: [AttachmentViewModel] = [],
|
||||||
card: Card? = nil,
|
card: PreviewCard? = nil,
|
||||||
mentions: [Mention] = [],
|
mentions: [Mention] = [],
|
||||||
tags: [Tag] = [],
|
tags: [Tag] = [],
|
||||||
place: Place? = nil
|
place: Place? = nil
|
||||||
|
|
|
@ -26,7 +26,7 @@ struct FollowersView: View {
|
||||||
.environmentObject(applicationState)) {
|
.environmentObject(applicationState)) {
|
||||||
UsernameRow(accountId: account.id,
|
UsernameRow(accountId: account.id,
|
||||||
accountAvatar: account.avatar,
|
accountAvatar: account.avatar,
|
||||||
accountDisplayName: account.displayName,
|
accountDisplayName: account.displayNameWithoutEmojis,
|
||||||
accountUsername: account.acct)
|
accountUsername: account.acct)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ struct FollowingView: View {
|
||||||
.environmentObject(applicationState)) {
|
.environmentObject(applicationState)) {
|
||||||
UsernameRow(accountId: account.id,
|
UsernameRow(accountId: account.id,
|
||||||
accountAvatar: account.avatar,
|
accountAvatar: account.avatar,
|
||||||
accountDisplayName: account.displayName,
|
accountDisplayName: account.displayNameWithoutEmojis,
|
||||||
accountUsername: account.acct)
|
accountUsername: account.acct)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,21 +24,31 @@ struct NotificationsView: View {
|
||||||
List {
|
List {
|
||||||
ForEach(notifications, id: \.id) { notification in
|
ForEach(notifications, id: \.id) { notification in
|
||||||
switch notification.type {
|
switch notification.type {
|
||||||
case .mention, .reblog, .favourite:
|
case .favourite, .reblog, .mention, .status, .poll, .update:
|
||||||
if let status = notification.status {
|
if let status = notification.status {
|
||||||
NavigationLink(destination: StatusView(statusId: status.id)
|
NavigationLink(destination: StatusView(statusId: status.id)
|
||||||
.environmentObject(applicationState)) {
|
.environmentObject(applicationState)) {
|
||||||
NotificationRow(notification: notification)
|
NotificationRow(notification: notification)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case .follow:
|
case .follow, .followRequest, .adminSignUp:
|
||||||
NavigationLink(destination: UserProfileView(
|
NavigationLink(destination: UserProfileView(
|
||||||
accountId: notification.account.id,
|
accountId: notification.account.id,
|
||||||
accountDisplayName: notification.account.displayName,
|
accountDisplayName: notification.account.displayNameWithoutEmojis,
|
||||||
accountUserName: notification.account.acct)
|
accountUserName: notification.account.acct)
|
||||||
.environmentObject(applicationState)) {
|
.environmentObject(applicationState)) {
|
||||||
NotificationRow(notification: notification)
|
NotificationRow(notification: notification)
|
||||||
}
|
}
|
||||||
|
case .adminReport:
|
||||||
|
if let targetAccount = notification.report?.targetAccount {
|
||||||
|
NavigationLink(destination: UserProfileView(
|
||||||
|
accountId: targetAccount.id,
|
||||||
|
accountDisplayName: targetAccount.displayNameWithoutEmojis,
|
||||||
|
accountUserName: targetAccount.acct)
|
||||||
|
.environmentObject(applicationState)) {
|
||||||
|
NotificationRow(notification: notification)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,7 +174,7 @@ struct NotificationsView: View {
|
||||||
images.append(contentsOf:
|
images.append(contentsOf:
|
||||||
mediaAttachment
|
mediaAttachment
|
||||||
.filter({ attachment in
|
.filter({ attachment in
|
||||||
attachment.type == Attachment.AttachmentType.image
|
attachment.type == MediaAttachment.MediaAttachmentType.image
|
||||||
})
|
})
|
||||||
.map({
|
.map({
|
||||||
attachment in (id: attachment.id, url: attachment.url)
|
attachment in (id: attachment.id, url: attachment.url)
|
||||||
|
|
|
@ -47,12 +47,12 @@ struct NotificationRow: View {
|
||||||
|
|
||||||
|
|
||||||
switch self.notification.type {
|
switch self.notification.type {
|
||||||
case .favourite, .reblog, .mention:
|
case .favourite, .reblog, .mention, .status, .poll, .update:
|
||||||
if let status = self.notification.status, let statusViewModel = StatusViewModel(status: status) {
|
if let status = self.notification.status, let statusViewModel = StatusViewModel(status: status) {
|
||||||
HStack(alignment: .top) {
|
HStack(alignment: .top) {
|
||||||
Spacer()
|
Spacer()
|
||||||
if let attachment = statusViewModel.mediaAttachments.filter({ attachment in
|
if let attachment = statusViewModel.mediaAttachments.filter({ attachment in
|
||||||
attachment.type == Attachment.AttachmentType.image
|
attachment.type == MediaAttachment.MediaAttachmentType.image
|
||||||
}).first {
|
}).first {
|
||||||
if let cachedImage = CacheImageService.shared.getImage(for: attachment.id) {
|
if let cachedImage = CacheImageService.shared.getImage(for: attachment.id) {
|
||||||
cachedImage
|
cachedImage
|
||||||
|
@ -67,7 +67,7 @@ struct NotificationRow: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case .follow:
|
case .follow, .followRequest, .adminSignUp:
|
||||||
if let note = self.notification.account.note {
|
if let note = self.notification.account.note {
|
||||||
HTMLFormattedText(note, withFontSize: 12, andWidth: contentWidth)
|
HTMLFormattedText(note, withFontSize: 12, andWidth: contentWidth)
|
||||||
.padding(.top, -4)
|
.padding(.top, -4)
|
||||||
|
@ -75,6 +75,9 @@ struct NotificationRow: View {
|
||||||
} else {
|
} else {
|
||||||
EmptyView()
|
EmptyView()
|
||||||
}
|
}
|
||||||
|
case .adminReport:
|
||||||
|
Text(self.notification.report?.comment ?? "")
|
||||||
|
.multilineTextAlignment(.leading)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,6 +112,18 @@ struct NotificationRow: View {
|
||||||
return "boosted"
|
return "boosted"
|
||||||
case .favourite:
|
case .favourite:
|
||||||
return "favourited"
|
return "favourited"
|
||||||
|
case .status:
|
||||||
|
return "posted status"
|
||||||
|
case .followRequest:
|
||||||
|
return "follow request"
|
||||||
|
case .poll:
|
||||||
|
return "poll"
|
||||||
|
case .update:
|
||||||
|
return "updated post"
|
||||||
|
case .adminSignUp:
|
||||||
|
return "signed up"
|
||||||
|
case .adminReport:
|
||||||
|
return "new report"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,6 +137,18 @@ struct NotificationRow: View {
|
||||||
return "paperplane"
|
return "paperplane"
|
||||||
case .favourite:
|
case .favourite:
|
||||||
return "hand.thumbsup"
|
return "hand.thumbsup"
|
||||||
|
case .status:
|
||||||
|
return "photo.on.rectangle.angled"
|
||||||
|
case .followRequest:
|
||||||
|
return "person.badge.clock"
|
||||||
|
case .poll:
|
||||||
|
return "checklist"
|
||||||
|
case .update:
|
||||||
|
return "text.below.photo"
|
||||||
|
case .adminSignUp:
|
||||||
|
return "person.badge.key"
|
||||||
|
case .adminReport:
|
||||||
|
return "exclamationmark.bubble"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,6 +162,18 @@ struct NotificationRow: View {
|
||||||
return Color.accentColor5
|
return Color.accentColor5
|
||||||
case .favourite:
|
case .favourite:
|
||||||
return Color.accentColor6
|
return Color.accentColor6
|
||||||
|
case .status:
|
||||||
|
return Color.accentColor1
|
||||||
|
case .followRequest:
|
||||||
|
return Color.accentColor2
|
||||||
|
case .poll:
|
||||||
|
return Color.accentColor7
|
||||||
|
case .update:
|
||||||
|
return Color.accentColor8
|
||||||
|
case .adminSignUp:
|
||||||
|
return Color.accentColor9
|
||||||
|
case .adminReport:
|
||||||
|
return Color.accentColor10
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue