forked from shadowfacts/Tusker
First pass at strict sendability checking
This commit is contained in:
parent
762d298c06
commit
aaa031f212
|
@ -484,7 +484,7 @@ public class Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Client {
|
extension Client {
|
||||||
public struct Error: LocalizedError {
|
public struct Error: LocalizedError, Sendable {
|
||||||
public let requestMethod: Method
|
public let requestMethod: Method
|
||||||
public let requestEndpoint: Endpoint
|
public let requestEndpoint: Endpoint
|
||||||
public let type: ErrorType
|
public let type: ErrorType
|
||||||
|
@ -519,7 +519,7 @@ extension Client {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public enum ErrorType: LocalizedError {
|
public enum ErrorType: LocalizedError, Sendable {
|
||||||
case networkError(Swift.Error)
|
case networkError(Swift.Error)
|
||||||
case unexpectedStatus(Int)
|
case unexpectedStatus(Int)
|
||||||
case invalidRequest
|
case invalidRequest
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public final class Account: AccountProtocol, Decodable {
|
public final class Account: AccountProtocol, Decodable, Sendable {
|
||||||
public let id: String
|
public let id: String
|
||||||
public let username: String
|
public let username: String
|
||||||
public let acct: String
|
public let acct: String
|
||||||
|
@ -25,7 +25,7 @@ public final class Account: AccountProtocol, Decodable {
|
||||||
public let avatarStatic: URL?
|
public let avatarStatic: URL?
|
||||||
public let header: URL?
|
public let header: URL?
|
||||||
public let headerStatic: URL?
|
public let headerStatic: URL?
|
||||||
public private(set) var emojis: [Emoji]
|
public let emojis: [Emoji]
|
||||||
public let moved: Bool?
|
public let moved: Bool?
|
||||||
public let movedTo: Account?
|
public let movedTo: Account?
|
||||||
public let fields: [Field]
|
public let fields: [Field]
|
||||||
|
@ -171,7 +171,7 @@ extension Account: CustomDebugStringConvertible {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Account {
|
extension Account {
|
||||||
public struct Field: Codable, Equatable {
|
public struct Field: Codable, Equatable, Sendable {
|
||||||
public let name: String
|
public let name: String
|
||||||
public let value: String
|
public let value: String
|
||||||
public let verifiedAt: Date?
|
public let verifiedAt: Date?
|
||||||
|
|
|
@ -8,11 +8,11 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public class Application: Decodable {
|
public struct Application: Decodable, Sendable {
|
||||||
public let name: String
|
public let name: String
|
||||||
public let website: URL?
|
public let website: URL?
|
||||||
|
|
||||||
public required 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.name = try container.decode(String.self, forKey: .name)
|
self.name = try container.decode(String.self, forKey: .name)
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public class Attachment: Codable {
|
public struct Attachment: Codable, Sendable {
|
||||||
public let id: String
|
public let id: String
|
||||||
public let kind: Kind
|
public let kind: Kind
|
||||||
public let url: URL
|
public let url: URL
|
||||||
|
@ -25,7 +25,7 @@ public class Attachment: Codable {
|
||||||
], nil))
|
], nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
required 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(String.self, forKey: .id)
|
||||||
self.kind = try container.decode(Kind.self, forKey: .kind)
|
self.kind = try container.decode(Kind.self, forKey: .kind)
|
||||||
|
@ -50,7 +50,7 @@ public class Attachment: Codable {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Attachment {
|
extension Attachment {
|
||||||
public enum Kind: String, Codable {
|
public enum Kind: String, Codable, Sendable {
|
||||||
case image
|
case image
|
||||||
case video
|
case video
|
||||||
case gifv
|
case gifv
|
||||||
|
@ -77,7 +77,7 @@ extension Attachment {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Attachment {
|
extension Attachment {
|
||||||
public struct Metadata: Codable {
|
public struct Metadata: Codable, Sendable {
|
||||||
public let length: String?
|
public let length: String?
|
||||||
public let duration: Float?
|
public let duration: Float?
|
||||||
public let audioEncoding: String?
|
public let audioEncoding: String?
|
||||||
|
@ -108,7 +108,7 @@ extension Attachment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct ImageMetadata: Codable {
|
public struct ImageMetadata: Codable, Sendable {
|
||||||
public let width: Int?
|
public let width: Int?
|
||||||
public let height: Int?
|
public let height: Int?
|
||||||
public let size: String?
|
public let size: String?
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import WebURL
|
import WebURL
|
||||||
|
|
||||||
public class Card: Codable {
|
public struct Card: Codable, Sendable {
|
||||||
public let url: WebURL
|
public let url: WebURL
|
||||||
public let title: String
|
public let title: String
|
||||||
public let description: String
|
public let description: String
|
||||||
|
@ -26,7 +26,7 @@ public class Card: Codable {
|
||||||
/// Only present when returned from the trending links endpoint
|
/// Only present when returned from the trending links endpoint
|
||||||
public let history: [History]?
|
public let history: [History]?
|
||||||
|
|
||||||
public required 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.url = try container.decode(WebURL.self, forKey: .url)
|
self.url = try container.decode(WebURL.self, forKey: .url)
|
||||||
|
@ -75,7 +75,7 @@ public class Card: Codable {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Card {
|
extension Card {
|
||||||
public enum Kind: String, Codable {
|
public enum Kind: String, Codable, Sendable {
|
||||||
case link
|
case link
|
||||||
case photo
|
case photo
|
||||||
case video
|
case video
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public class ConversationContext: Decodable {
|
public struct ConversationContext: Decodable, Sendable {
|
||||||
public let ancestors: [Status]
|
public let ancestors: [Status]
|
||||||
public let descendants: [Status]
|
public let descendants: [Status]
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public enum DirectoryOrder: String, CaseIterable {
|
public enum DirectoryOrder: String, CaseIterable, Sendable {
|
||||||
case active
|
case active
|
||||||
case new
|
case new
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import WebURL
|
import WebURL
|
||||||
|
|
||||||
public class Emoji: Codable {
|
public struct Emoji: Codable, Sendable {
|
||||||
public let shortcode: String
|
public let shortcode: String
|
||||||
// these shouldn't need to be WebURLs as they're not external resources,
|
// these shouldn't need to be WebURLs as they're not external resources,
|
||||||
// but some instances (pleroma?) has emoji urls that Foundation considers malformed so we use WebURL to be more lenient
|
// but some instances (pleroma?) has emoji urls that Foundation considers malformed so we use WebURL to be more lenient
|
||||||
|
@ -18,7 +18,7 @@ public class Emoji: Codable {
|
||||||
public let visibleInPicker: Bool
|
public let visibleInPicker: Bool
|
||||||
public let category: String?
|
public let category: String?
|
||||||
|
|
||||||
public required 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.shortcode = try container.decode(String.self, forKey: .shortcode)
|
self.shortcode = try container.decode(String.self, forKey: .shortcode)
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public struct FilterV1: Decodable {
|
public struct FilterV1: Decodable, Sendable {
|
||||||
public let id: String
|
public let id: String
|
||||||
public let phrase: String
|
public let phrase: String
|
||||||
private let context: [String]
|
private let context: [String]
|
||||||
|
@ -45,7 +45,7 @@ public struct FilterV1: Decodable {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension FilterV1 {
|
extension FilterV1 {
|
||||||
public enum Context: String, Decodable, CaseIterable {
|
public enum Context: String, Decodable, CaseIterable, Sendable {
|
||||||
case home
|
case home
|
||||||
case notifications
|
case notifications
|
||||||
case `public`
|
case `public`
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public struct FilterV2: Decodable {
|
public struct FilterV2: Decodable, Sendable {
|
||||||
public let id: String
|
public let id: String
|
||||||
public let title: String
|
public let title: String
|
||||||
public let context: [FilterV1.Context]
|
public let context: [FilterV1.Context]
|
||||||
|
@ -80,14 +80,14 @@ public struct FilterV2: Decodable {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension FilterV2 {
|
extension FilterV2 {
|
||||||
public enum Action: String, Decodable, Hashable, CaseIterable {
|
public enum Action: String, Decodable, Hashable, CaseIterable, Sendable {
|
||||||
case warn
|
case warn
|
||||||
case hide
|
case hide
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension FilterV2 {
|
extension FilterV2 {
|
||||||
public struct Keyword: Decodable {
|
public struct Keyword: Decodable, Sendable {
|
||||||
public let id: String
|
public let id: String
|
||||||
public let keyword: String
|
public let keyword: String
|
||||||
public let wholeWord: Bool
|
public let wholeWord: Bool
|
||||||
|
|
|
@ -10,7 +10,7 @@ import Foundation
|
||||||
import WebURL
|
import WebURL
|
||||||
import WebURLFoundationExtras
|
import WebURLFoundationExtras
|
||||||
|
|
||||||
public class Hashtag: Codable {
|
public struct Hashtag: Codable, Sendable {
|
||||||
public let name: String
|
public let name: String
|
||||||
public let url: WebURL
|
public let url: WebURL
|
||||||
/// Only present when returned from the trending hashtags endpoint
|
/// Only present when returned from the trending hashtags endpoint
|
||||||
|
@ -25,7 +25,7 @@ public class Hashtag: Codable {
|
||||||
self.following = nil
|
self.following = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
public required 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.name = try container.decode(String.self, forKey: .name)
|
self.name = try container.decode(String.self, forKey: .name)
|
||||||
// pixelfed (possibly others) don't fully escape special characters in the hashtag url
|
// pixelfed (possibly others) don't fully escape special characters in the hashtag url
|
||||||
|
|
|
@ -8,12 +8,12 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public class History: Codable {
|
public struct History: Codable, Sendable {
|
||||||
public let day: Date
|
public let day: Date
|
||||||
public let uses: Int
|
public let uses: Int
|
||||||
public let accounts: Int
|
public let accounts: Int
|
||||||
|
|
||||||
public required 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)
|
||||||
|
|
||||||
if let day = try? container.decode(Date.self, forKey: .day) {
|
if let day = try? container.decode(Date.self, forKey: .day) {
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public class Instance: Decodable {
|
public struct Instance: Decodable, Sendable {
|
||||||
public let uri: String
|
public let uri: String
|
||||||
public let title: String
|
public let title: String
|
||||||
public let description: String
|
public let description: String
|
||||||
|
@ -37,7 +37,7 @@ public class Instance: Decodable {
|
||||||
}
|
}
|
||||||
|
|
||||||
// we need a custom decoder, because all API-compatible implementations don't return some data in the same format
|
// we need a custom decoder, because all API-compatible implementations don't return some data in the same format
|
||||||
public required init(from decoder: Decoder) throws {
|
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.uri = try container.decode(String.self, forKey: .uri)
|
||||||
self.title = try container.decode(String.self, forKey: .title)
|
self.title = try container.decode(String.self, forKey: .title)
|
||||||
|
@ -93,7 +93,7 @@ public class Instance: Decodable {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Instance {
|
extension Instance {
|
||||||
public struct Stats: Decodable {
|
public struct Stats: Decodable, Sendable {
|
||||||
public let domainCount: Int?
|
public let domainCount: Int?
|
||||||
public let statusCount: Int?
|
public let statusCount: Int?
|
||||||
public let userCount: Int?
|
public let userCount: Int?
|
||||||
|
@ -107,7 +107,7 @@ extension Instance {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Instance {
|
extension Instance {
|
||||||
public struct Configuration: Decodable {
|
public struct Configuration: Decodable, Sendable {
|
||||||
public let statuses: StatusesConfiguration
|
public let statuses: StatusesConfiguration
|
||||||
public let mediaAttachments: MediaAttachmentsConfiguration
|
public let mediaAttachments: MediaAttachmentsConfiguration
|
||||||
/// Use Instance.pollsConfiguration to support older instance that don't have this nested
|
/// Use Instance.pollsConfiguration to support older instance that don't have this nested
|
||||||
|
@ -122,7 +122,7 @@ extension Instance {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Instance {
|
extension Instance {
|
||||||
public struct StatusesConfiguration: Decodable {
|
public struct StatusesConfiguration: Decodable, Sendable {
|
||||||
public let maxCharacters: Int
|
public let maxCharacters: Int
|
||||||
public let maxMediaAttachments: Int
|
public let maxMediaAttachments: Int
|
||||||
public let charactersReservedPerURL: Int
|
public let charactersReservedPerURL: Int
|
||||||
|
@ -136,7 +136,7 @@ extension Instance {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Instance {
|
extension Instance {
|
||||||
public struct MediaAttachmentsConfiguration: Decodable {
|
public struct MediaAttachmentsConfiguration: Decodable, Sendable {
|
||||||
public let supportedMIMETypes: [String]
|
public let supportedMIMETypes: [String]
|
||||||
public let imageSizeLimit: Int
|
public let imageSizeLimit: Int
|
||||||
public let imageMatrixLimit: Int
|
public let imageMatrixLimit: Int
|
||||||
|
@ -156,7 +156,7 @@ extension Instance {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Instance {
|
extension Instance {
|
||||||
public struct PollsConfiguration: Decodable {
|
public struct PollsConfiguration: Decodable, Sendable {
|
||||||
public let maxOptions: Int
|
public let maxOptions: Int
|
||||||
public let maxCharactersPerOption: Int
|
public let maxCharactersPerOption: Int
|
||||||
public let minExpiration: TimeInterval
|
public let minExpiration: TimeInterval
|
||||||
|
@ -172,7 +172,7 @@ extension Instance {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Instance {
|
extension Instance {
|
||||||
public struct Rule: Decodable, Identifiable {
|
public struct Rule: Decodable, Identifiable, Sendable {
|
||||||
public let id: String
|
public let id: String
|
||||||
public let text: String
|
public let text: String
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public class List: Decodable, Equatable, Hashable {
|
public struct List: Decodable, Equatable, Hashable, Sendable {
|
||||||
public let id: String
|
public let id: String
|
||||||
public let title: String
|
public let title: String
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public class LoginSettings: Decodable {
|
public struct LoginSettings: Decodable, Sendable {
|
||||||
public let accessToken: String
|
public let accessToken: String
|
||||||
private let scope: String?
|
private let scope: String?
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import WebURL
|
import WebURL
|
||||||
|
|
||||||
public struct Mention: Codable {
|
public struct Mention: Codable, Sendable {
|
||||||
public let url: WebURL
|
public let url: WebURL
|
||||||
public let username: String
|
public let username: String
|
||||||
public let acct: String
|
public let acct: String
|
||||||
|
|
|
@ -8,11 +8,11 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public struct NodeInfo: Decodable {
|
public struct NodeInfo: Decodable, Sendable {
|
||||||
public let version: String
|
public let version: String
|
||||||
public let software: Software
|
public let software: Software
|
||||||
|
|
||||||
public struct Software: Decodable {
|
public struct Software: Decodable, Sendable {
|
||||||
public let name: String
|
public let name: String
|
||||||
public let version: String
|
public let version: String
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,14 +8,14 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public class Notification: Decodable {
|
public struct Notification: Decodable, Sendable {
|
||||||
public let id: String
|
public let id: String
|
||||||
public let kind: Kind
|
public let kind: Kind
|
||||||
public let createdAt: Date
|
public let createdAt: Date
|
||||||
public let account: Account
|
public let account: Account
|
||||||
public let status: Status?
|
public let status: Status?
|
||||||
|
|
||||||
public required 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(String.self, forKey: .id)
|
||||||
|
@ -45,7 +45,7 @@ public class Notification: Decodable {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Notification {
|
extension Notification {
|
||||||
public enum Kind: String, Decodable, CaseIterable {
|
public enum Kind: String, Decodable, CaseIterable, Sendable {
|
||||||
case mention
|
case mention
|
||||||
case reblog
|
case reblog
|
||||||
case favourite
|
case favourite
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public final class Poll: Codable {
|
public struct Poll: Codable, Sendable {
|
||||||
public let id: String
|
public let id: String
|
||||||
public let expiresAt: Date?
|
public let expiresAt: Date?
|
||||||
public let expired: Bool
|
public let expired: Bool
|
||||||
|
@ -43,7 +43,7 @@ public final class Poll: Codable {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Poll {
|
extension Poll {
|
||||||
public final class Option: Codable {
|
public struct Option: Codable, Sendable {
|
||||||
public let title: String
|
public let title: String
|
||||||
public let votesCount: Int?
|
public let votesCount: Int?
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public class PushSubscription: Decodable {
|
public struct PushSubscription: Decodable, Sendable {
|
||||||
public let id: String
|
public let id: String
|
||||||
public let endpoint: URL
|
public let endpoint: URL
|
||||||
public let serverKey: String
|
public let serverKey: String
|
||||||
|
|
|
@ -8,12 +8,12 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public class RegisteredApplication: Decodable {
|
public struct RegisteredApplication: Decodable, Sendable {
|
||||||
public let id: String
|
public let id: String
|
||||||
public let clientID: String
|
public let clientID: String
|
||||||
public let clientSecret: String
|
public let clientSecret: String
|
||||||
|
|
||||||
public required 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)
|
||||||
|
|
||||||
// Pixelfed API returns id/client_id as numbers instead of strings
|
// Pixelfed API returns id/client_id as numbers instead of strings
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public class Relationship: Decodable {
|
public struct Relationship: Decodable, Sendable {
|
||||||
public let id: String
|
public let id: String
|
||||||
public let following: Bool
|
public let following: Bool
|
||||||
public let followedBy: Bool
|
public let followedBy: Bool
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public class Report: Decodable {
|
public struct Report: Decodable, Sendable {
|
||||||
public let id: String
|
public let id: String
|
||||||
public let actionTaken: Bool
|
public let actionTaken: Bool
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public enum Scope: String {
|
public enum Scope: String, Sendable {
|
||||||
case read
|
case read
|
||||||
case write
|
case write
|
||||||
case follow
|
case follow
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public enum SearchResultType: String {
|
public enum SearchResultType: String, Sendable {
|
||||||
case accounts
|
case accounts
|
||||||
case hashtags
|
case hashtags
|
||||||
case statuses
|
case statuses
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public class SearchResults: Decodable {
|
public struct SearchResults: Decodable, Sendable {
|
||||||
public let accounts: [Account]
|
public let accounts: [Account]
|
||||||
public let statuses: [Status]
|
public let statuses: [Status]
|
||||||
public let hashtags: [Hashtag]
|
public let hashtags: [Hashtag]
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import WebURL
|
import WebURL
|
||||||
|
|
||||||
public final class Status: StatusProtocol, Decodable {
|
public final class Status: StatusProtocol, Decodable, Sendable {
|
||||||
public let id: String
|
public let id: String
|
||||||
public let uri: String
|
public let uri: String
|
||||||
public let url: WebURL?
|
public let url: WebURL?
|
||||||
|
@ -188,7 +188,7 @@ public final class Status: StatusProtocol, Decodable {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Status {
|
extension Status {
|
||||||
public enum Visibility: String, Codable, CaseIterable {
|
public enum Visibility: String, Codable, CaseIterable, Sendable {
|
||||||
case `public`
|
case `public`
|
||||||
case unlisted
|
case unlisted
|
||||||
case `private`
|
case `private`
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public enum StatusContentType: String, Codable, CaseIterable {
|
public enum StatusContentType: String, Codable, CaseIterable, Sendable {
|
||||||
case plain, markdown, html
|
case plain, markdown, html
|
||||||
|
|
||||||
var mimeType: String {
|
var mimeType: String {
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public struct Suggestion: Decodable {
|
public struct Suggestion: Decodable, Sendable {
|
||||||
public let source: Source
|
public let source: Source
|
||||||
public let account: Account
|
public let account: Account
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ public struct Suggestion: Decodable {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Suggestion {
|
extension Suggestion {
|
||||||
public enum Source: String, Decodable {
|
public enum Source: String, Decodable, Sendable {
|
||||||
case staff
|
case staff
|
||||||
case pastInteractions = "past_interactions"
|
case pastInteractions = "past_interactions"
|
||||||
case global
|
case global
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public enum Timeline: Equatable, Hashable {
|
public enum Timeline: Equatable, Hashable, Sendable {
|
||||||
case home
|
case home
|
||||||
case `public`(local: Bool)
|
case `public`(local: Bool)
|
||||||
case tag(hashtag: String)
|
case tag(hashtag: String)
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public struct TimelineMarkers: Decodable {
|
public struct TimelineMarkers: Decodable, Sendable {
|
||||||
public let home: Marker?
|
public let home: Marker?
|
||||||
public let notifications: Marker?
|
public let notifications: Marker?
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ public struct TimelineMarkers: Decodable {
|
||||||
case notifications
|
case notifications
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct Marker: Decodable {
|
public struct Marker: Decodable, Sendable {
|
||||||
public let lastReadID: String
|
public let lastReadID: String
|
||||||
public let version: Int
|
public let version: Int
|
||||||
public let updatedAt: Date
|
public let updatedAt: Date
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct WellKnown: Decodable {
|
struct WellKnown: Decodable, Sendable {
|
||||||
let links: [Link]
|
let links: [Link]
|
||||||
|
|
||||||
struct Link: Decodable {
|
struct Link: Decodable {
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
protocol Body {
|
protocol Body: Sendable {
|
||||||
var mimeType: String? { get }
|
var mimeType: String? { get }
|
||||||
var data: Data? { get }
|
var data: Data? { get }
|
||||||
}
|
}
|
||||||
|
@ -76,7 +76,7 @@ struct FormDataBody: Body {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct JsonBody<T: Encodable>: Body {
|
struct JsonBody<T: Encodable & Sendable>: Body {
|
||||||
let value: T
|
let value: T
|
||||||
|
|
||||||
init(_ value: T) {
|
init(_ value: T) {
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public struct Endpoint: ExpressibleByStringInterpolation, CustomStringConvertible {
|
public struct Endpoint: ExpressibleByStringInterpolation, CustomStringConvertible, Sendable {
|
||||||
let components: [Component]
|
let components: [Component]
|
||||||
|
|
||||||
public init(stringLiteral value: StringLiteralType) {
|
public init(stringLiteral value: StringLiteralType) {
|
||||||
|
@ -54,7 +54,7 @@ public struct Endpoint: ExpressibleByStringInterpolation, CustomStringConvertibl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Component {
|
enum Component: Sendable {
|
||||||
case literal(String)
|
case literal(String)
|
||||||
case interpolated(String)
|
case interpolated(String)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public struct FormAttachment {
|
public struct FormAttachment: Sendable {
|
||||||
let mimeType: String
|
let mimeType: String
|
||||||
let data: Data
|
let data: Data
|
||||||
let fileName: String
|
let fileName: String
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public enum Method {
|
public enum Method: Sendable {
|
||||||
case get, post, put, patch, delete
|
case get, post, put, patch, delete
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct Parameter {
|
struct Parameter: Sendable {
|
||||||
let name: String
|
let name: String
|
||||||
let value: String?
|
let value: String?
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public struct Request<ResultType: Decodable> {
|
public struct Request<ResultType: Decodable>: Sendable {
|
||||||
let method: Method
|
let method: Method
|
||||||
let endpoint: Endpoint
|
let endpoint: Endpoint
|
||||||
let body: Body
|
let body: Body
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public enum RequestRange {
|
public enum RequestRange: Sendable {
|
||||||
case `default`
|
case `default`
|
||||||
case count(Int)
|
case count(Int)
|
||||||
/// Chronologically immediately before the given ID
|
/// Chronologically immediately before the given ID
|
||||||
|
|
|
@ -8,6 +8,6 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public struct Empty: Decodable {
|
public struct Empty: Decodable, Sendable {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public struct Pagination {
|
public struct Pagination: Sendable {
|
||||||
public let older: RequestRange?
|
public let older: RequestRange?
|
||||||
public let newer: RequestRange?
|
public let newer: RequestRange?
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public enum Response<Result: Decodable> {
|
public enum Response<Result: Decodable & Sendable>: Sendable {
|
||||||
case success(Result, Pagination?)
|
case success(Result, Pagination?)
|
||||||
case failure(Client.Error)
|
case failure(Client.Error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,8 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public class CollapseState: Equatable {
|
@MainActor
|
||||||
|
public final class CollapseState: Sendable {
|
||||||
public var collapsible: Bool?
|
public var collapsible: Bool?
|
||||||
public var collapsed: Bool?
|
public var collapsed: Bool?
|
||||||
|
|
||||||
|
@ -33,8 +34,4 @@ public class CollapseState: Equatable {
|
||||||
public static var unknown: CollapseState {
|
public static var unknown: CollapseState {
|
||||||
CollapseState(collapsible: nil, collapsed: nil)
|
CollapseState(collapsible: nil, collapsed: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func == (lhs: CollapseState, rhs: CollapseState) -> Bool {
|
|
||||||
lhs.collapsible == rhs.collapsible && lhs.collapsed == rhs.collapsed
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ public struct NotificationGroup: Identifiable, Hashable {
|
||||||
public let kind: Notification.Kind
|
public let kind: Notification.Kind
|
||||||
public let statusState: CollapseState?
|
public let statusState: CollapseState?
|
||||||
|
|
||||||
|
@MainActor
|
||||||
init?(notifications: [Notification]) {
|
init?(notifications: [Notification]) {
|
||||||
guard !notifications.isEmpty else { return nil }
|
guard !notifications.isEmpty else { return nil }
|
||||||
self.notifications = notifications
|
self.notifications = notifications
|
||||||
|
@ -51,6 +52,7 @@ public struct NotificationGroup: Identifiable, Hashable {
|
||||||
notifications.append(contentsOf: group.notifications)
|
notifications.append(contentsOf: group.notifications)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
public static func createGroups(notifications: [Notification], only allowedTypes: [Notification.Kind]) -> [NotificationGroup] {
|
public static func createGroups(notifications: [Notification], only allowedTypes: [Notification.Kind]) -> [NotificationGroup] {
|
||||||
var groups = [NotificationGroup]()
|
var groups = [NotificationGroup]()
|
||||||
for notification in notifications {
|
for notification in notifications {
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import Combine
|
||||||
|
|
||||||
public class GameController: ObservableObject {
|
public class GameController: ObservableObject {
|
||||||
|
|
||||||
|
|
|
@ -220,6 +220,7 @@
|
||||||
D68E6F5F253C9B2D001A1B4C /* BaseEmojiLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68E6F5E253C9B2D001A1B4C /* BaseEmojiLabel.swift */; };
|
D68E6F5F253C9B2D001A1B4C /* BaseEmojiLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68E6F5E253C9B2D001A1B4C /* BaseEmojiLabel.swift */; };
|
||||||
D68FEC4F232C5BC300C84F23 /* SegmentedPageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68FEC4E232C5BC300C84F23 /* SegmentedPageViewController.swift */; };
|
D68FEC4F232C5BC300C84F23 /* SegmentedPageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68FEC4E232C5BC300C84F23 /* SegmentedPageViewController.swift */; };
|
||||||
D690797324A4EF9700023A34 /* UIBezierPath+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D690797224A4EF9700023A34 /* UIBezierPath+Helpers.swift */; };
|
D690797324A4EF9700023A34 /* UIBezierPath+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D690797224A4EF9700023A34 /* UIBezierPath+Helpers.swift */; };
|
||||||
|
D691771129A2B76A0054D7EF /* MainActor+Unsafe.swift in Sources */ = {isa = PBXBuildFile; fileRef = D691771029A2B76A0054D7EF /* MainActor+Unsafe.swift */; };
|
||||||
D693A72825CF282E003A14E2 /* TrendingHashtagsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D693A72725CF282E003A14E2 /* TrendingHashtagsViewController.swift */; };
|
D693A72825CF282E003A14E2 /* TrendingHashtagsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D693A72725CF282E003A14E2 /* TrendingHashtagsViewController.swift */; };
|
||||||
D693A72A25CF8C1E003A14E2 /* ProfileDirectoryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D693A72925CF8C1E003A14E2 /* ProfileDirectoryViewController.swift */; };
|
D693A72A25CF8C1E003A14E2 /* ProfileDirectoryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D693A72925CF8C1E003A14E2 /* ProfileDirectoryViewController.swift */; };
|
||||||
D693A72F25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D693A72D25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.swift */; };
|
D693A72F25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D693A72D25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.swift */; };
|
||||||
|
@ -252,7 +253,6 @@
|
||||||
D6A5BB2D23BBA9C4003BF21D /* MyProfileTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A5BB2C23BBA9C4003BF21D /* MyProfileTests.swift */; };
|
D6A5BB2D23BBA9C4003BF21D /* MyProfileTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A5BB2C23BBA9C4003BF21D /* MyProfileTests.swift */; };
|
||||||
D6A5BB2F23BBAC97003BF21D /* DelegatingResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A5BB2E23BBAC97003BF21D /* DelegatingResponse.swift */; };
|
D6A5BB2F23BBAC97003BF21D /* DelegatingResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A5BB2E23BBAC97003BF21D /* DelegatingResponse.swift */; };
|
||||||
D6A5BB3123BBAD87003BF21D /* JSONResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A5BB3023BBAD87003BF21D /* JSONResponse.swift */; };
|
D6A5BB3123BBAD87003BF21D /* JSONResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A5BB3023BBAD87003BF21D /* JSONResponse.swift */; };
|
||||||
D6A6C10525B6138A00298D0F /* StatusTablePrefetching.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A6C10425B6138A00298D0F /* StatusTablePrefetching.swift */; };
|
|
||||||
D6A6C10F25B62D2400298D0F /* DiskCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A6C10E25B62D2400298D0F /* DiskCache.swift */; };
|
D6A6C10F25B62D2400298D0F /* DiskCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A6C10E25B62D2400298D0F /* DiskCache.swift */; };
|
||||||
D6A6C11525B62E9700298D0F /* CacheExpiry.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A6C11425B62E9700298D0F /* CacheExpiry.swift */; };
|
D6A6C11525B62E9700298D0F /* CacheExpiry.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A6C11425B62E9700298D0F /* CacheExpiry.swift */; };
|
||||||
D6A6C11B25B63CEE00298D0F /* MemoryCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A6C11A25B63CEE00298D0F /* MemoryCache.swift */; };
|
D6A6C11B25B63CEE00298D0F /* MemoryCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A6C11A25B63CEE00298D0F /* MemoryCache.swift */; };
|
||||||
|
@ -636,6 +636,7 @@
|
||||||
D68E6F5E253C9B2D001A1B4C /* BaseEmojiLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseEmojiLabel.swift; sourceTree = "<group>"; };
|
D68E6F5E253C9B2D001A1B4C /* BaseEmojiLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseEmojiLabel.swift; sourceTree = "<group>"; };
|
||||||
D68FEC4E232C5BC300C84F23 /* SegmentedPageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SegmentedPageViewController.swift; sourceTree = "<group>"; };
|
D68FEC4E232C5BC300C84F23 /* SegmentedPageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SegmentedPageViewController.swift; sourceTree = "<group>"; };
|
||||||
D690797224A4EF9700023A34 /* UIBezierPath+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIBezierPath+Helpers.swift"; sourceTree = "<group>"; };
|
D690797224A4EF9700023A34 /* UIBezierPath+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIBezierPath+Helpers.swift"; sourceTree = "<group>"; };
|
||||||
|
D691771029A2B76A0054D7EF /* MainActor+Unsafe.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainActor+Unsafe.swift"; sourceTree = "<group>"; };
|
||||||
D693A72725CF282E003A14E2 /* TrendingHashtagsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrendingHashtagsViewController.swift; sourceTree = "<group>"; };
|
D693A72725CF282E003A14E2 /* TrendingHashtagsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrendingHashtagsViewController.swift; sourceTree = "<group>"; };
|
||||||
D693A72925CF8C1E003A14E2 /* ProfileDirectoryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileDirectoryViewController.swift; sourceTree = "<group>"; };
|
D693A72925CF8C1E003A14E2 /* ProfileDirectoryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileDirectoryViewController.swift; sourceTree = "<group>"; };
|
||||||
D693A72D25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeaturedProfileCollectionViewCell.swift; sourceTree = "<group>"; };
|
D693A72D25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeaturedProfileCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||||
|
@ -668,7 +669,6 @@
|
||||||
D6A5BB2C23BBA9C4003BF21D /* MyProfileTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyProfileTests.swift; sourceTree = "<group>"; };
|
D6A5BB2C23BBA9C4003BF21D /* MyProfileTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyProfileTests.swift; sourceTree = "<group>"; };
|
||||||
D6A5BB2E23BBAC97003BF21D /* DelegatingResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DelegatingResponse.swift; sourceTree = "<group>"; };
|
D6A5BB2E23BBAC97003BF21D /* DelegatingResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DelegatingResponse.swift; sourceTree = "<group>"; };
|
||||||
D6A5BB3023BBAD87003BF21D /* JSONResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONResponse.swift; sourceTree = "<group>"; };
|
D6A5BB3023BBAD87003BF21D /* JSONResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONResponse.swift; sourceTree = "<group>"; };
|
||||||
D6A6C10425B6138A00298D0F /* StatusTablePrefetching.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusTablePrefetching.swift; sourceTree = "<group>"; };
|
|
||||||
D6A6C10E25B62D2400298D0F /* DiskCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiskCache.swift; sourceTree = "<group>"; };
|
D6A6C10E25B62D2400298D0F /* DiskCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiskCache.swift; sourceTree = "<group>"; };
|
||||||
D6A6C11425B62E9700298D0F /* CacheExpiry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CacheExpiry.swift; sourceTree = "<group>"; };
|
D6A6C11425B62E9700298D0F /* CacheExpiry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CacheExpiry.swift; sourceTree = "<group>"; };
|
||||||
D6A6C11A25B63CEE00298D0F /* MemoryCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoryCache.swift; sourceTree = "<group>"; };
|
D6A6C11A25B63CEE00298D0F /* MemoryCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoryCache.swift; sourceTree = "<group>"; };
|
||||||
|
@ -1316,6 +1316,7 @@
|
||||||
D61F758F29353B4300C0B37F /* FileManager+Size.swift */,
|
D61F758F29353B4300C0B37F /* FileManager+Size.swift */,
|
||||||
D61F75AC293AF39000C0B37F /* Filter+Helpers.swift */,
|
D61F75AC293AF39000C0B37F /* Filter+Helpers.swift */,
|
||||||
D6C3F5162991C1A00009FCFF /* View+AppListStyle.swift */,
|
D6C3F5162991C1A00009FCFF /* View+AppListStyle.swift */,
|
||||||
|
D691771029A2B76A0054D7EF /* MainActor+Unsafe.swift */,
|
||||||
);
|
);
|
||||||
path = Extensions;
|
path = Extensions;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -1495,7 +1496,6 @@
|
||||||
D6B81F3B2560365300F6E31D /* RefreshableViewController.swift */,
|
D6B81F3B2560365300F6E31D /* RefreshableViewController.swift */,
|
||||||
D68FEC4E232C5BC300C84F23 /* SegmentedPageViewController.swift */,
|
D68FEC4E232C5BC300C84F23 /* SegmentedPageViewController.swift */,
|
||||||
D63CC7112911F57C000E19DE /* StatusBarTappableViewController.swift */,
|
D63CC7112911F57C000E19DE /* StatusBarTappableViewController.swift */,
|
||||||
D6A6C10425B6138A00298D0F /* StatusTablePrefetching.swift */,
|
|
||||||
D6412B0224AFF6A600F5412E /* TabBarScrollableViewController.swift */,
|
D6412B0224AFF6A600F5412E /* TabBarScrollableViewController.swift */,
|
||||||
D6B22A0E2560D52D004D82EF /* TabbedPageViewController.swift */,
|
D6B22A0E2560D52D004D82EF /* TabbedPageViewController.swift */,
|
||||||
D6538944214D6D7500E3CEFC /* TableViewSwipeActionProvider.swift */,
|
D6538944214D6D7500E3CEFC /* TableViewSwipeActionProvider.swift */,
|
||||||
|
@ -2200,6 +2200,7 @@
|
||||||
D65B4B58297203A700DABDFB /* ReportSelectRulesView.swift in Sources */,
|
D65B4B58297203A700DABDFB /* ReportSelectRulesView.swift in Sources */,
|
||||||
D6C3F4FB299035650009FCFF /* TrendsViewController.swift in Sources */,
|
D6C3F4FB299035650009FCFF /* TrendsViewController.swift in Sources */,
|
||||||
D6B81F442560390300F6E31D /* MenuController.swift in Sources */,
|
D6B81F442560390300F6E31D /* MenuController.swift in Sources */,
|
||||||
|
D691771129A2B76A0054D7EF /* MainActor+Unsafe.swift in Sources */,
|
||||||
D65B4B542971F71D00DABDFB /* EditedReport.swift in Sources */,
|
D65B4B542971F71D00DABDFB /* EditedReport.swift in Sources */,
|
||||||
D65234E12561AA68001AF9CF /* NotificationsTableViewController.swift in Sources */,
|
D65234E12561AA68001AF9CF /* NotificationsTableViewController.swift in Sources */,
|
||||||
D6A6C11525B62E9700298D0F /* CacheExpiry.swift in Sources */,
|
D6A6C11525B62E9700298D0F /* CacheExpiry.swift in Sources */,
|
||||||
|
@ -2260,7 +2261,6 @@
|
||||||
D6412B0924B0291E00F5412E /* MyProfileViewController.swift in Sources */,
|
D6412B0924B0291E00F5412E /* MyProfileViewController.swift in Sources */,
|
||||||
D601FA5D297B2E6F00A8E8B5 /* ConversationCollectionViewController.swift in Sources */,
|
D601FA5D297B2E6F00A8E8B5 /* ConversationCollectionViewController.swift in Sources */,
|
||||||
D6D12B56292D57E800D528E1 /* AccountCollectionViewCell.swift in Sources */,
|
D6D12B56292D57E800D528E1 /* AccountCollectionViewCell.swift in Sources */,
|
||||||
D6A6C10525B6138A00298D0F /* StatusTablePrefetching.swift in Sources */,
|
|
||||||
D60088F22980DAA0005B4D00 /* TipJarView.swift in Sources */,
|
D60088F22980DAA0005B4D00 /* TipJarView.swift in Sources */,
|
||||||
D6C82B4125C5BB7E0017F1E6 /* ExploreViewController.swift in Sources */,
|
D6C82B4125C5BB7E0017F1E6 /* ExploreViewController.swift in Sources */,
|
||||||
D6B053A423BD2C8100A066FA /* AssetCollectionsListViewController.swift in Sources */,
|
D6B053A423BD2C8100A066FA /* AssetCollectionsListViewController.swift in Sources */,
|
||||||
|
|
|
@ -13,11 +13,11 @@ import Pachyderm
|
||||||
class CreateListService {
|
class CreateListService {
|
||||||
private let mastodonController: MastodonController
|
private let mastodonController: MastodonController
|
||||||
private let present: (UIViewController) -> Void
|
private let present: (UIViewController) -> Void
|
||||||
private let didCreateList: (@MainActor (List) -> Void)?
|
private let didCreateList: (@MainActor (List) async -> Void)?
|
||||||
|
|
||||||
private var createAction: UIAlertAction?
|
private var createAction: UIAlertAction?
|
||||||
|
|
||||||
init(mastodonController: MastodonController, present: @escaping (UIViewController) -> Void, didCreateList: (@MainActor (List) -> Void)?) {
|
init(mastodonController: MastodonController, present: @escaping (UIViewController) -> Void, didCreateList: (@MainActor (List) async -> Void)?) {
|
||||||
self.mastodonController = mastodonController
|
self.mastodonController = mastodonController
|
||||||
self.present = present
|
self.present = present
|
||||||
self.didCreateList = didCreateList
|
self.didCreateList = didCreateList
|
||||||
|
@ -50,7 +50,7 @@ class CreateListService {
|
||||||
let request = Client.createList(title: title)
|
let request = Client.createList(title: title)
|
||||||
let (list, _) = try await mastodonController.run(request)
|
let (list, _) = try await mastodonController.run(request)
|
||||||
mastodonController.addedList(list)
|
mastodonController.addedList(list)
|
||||||
self.didCreateList?(list)
|
await self.didCreateList?(list)
|
||||||
} catch {
|
} catch {
|
||||||
let alert = UIAlertController(title: "Error Creating List", message: error.localizedDescription, preferredStyle: .alert)
|
let alert = UIAlertController(title: "Error Creating List", message: error.localizedDescription, preferredStyle: .alert)
|
||||||
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
|
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
|
||||||
|
|
|
@ -40,7 +40,7 @@ class MastodonController: ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
private let transient: Bool
|
private let transient: Bool
|
||||||
private(set) lazy var persistentContainer = MastodonCachePersistentStore(for: accountInfo, transient: transient)
|
private(set) nonisolated lazy var persistentContainer = MastodonCachePersistentStore(for: accountInfo, transient: transient)
|
||||||
|
|
||||||
let instanceURL: URL
|
let instanceURL: URL
|
||||||
var accountInfo: LocalData.UserAccountInfo?
|
var accountInfo: LocalData.UserAccountInfo?
|
||||||
|
@ -110,7 +110,7 @@ class MastodonController: ObservableObject {
|
||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
|
|
||||||
func run<Result>(_ request: Request<Result>) async throws -> (Result, Pagination?) {
|
func run<Result: Sendable>(_ request: Request<Result>) async throws -> (Result, Pagination?) {
|
||||||
let response = await runResponse(request)
|
let response = await runResponse(request)
|
||||||
try Task.checkCancellation()
|
try Task.checkCancellation()
|
||||||
switch response {
|
switch response {
|
||||||
|
@ -181,7 +181,7 @@ class MastodonController: ObservableObject {
|
||||||
_ = try await (ownAccount, ownInstance)
|
_ = try await (ownAccount, ownInstance)
|
||||||
|
|
||||||
loadLists()
|
loadLists()
|
||||||
async let _ = await loadFilters()
|
_ = await loadFilters()
|
||||||
} catch {
|
} catch {
|
||||||
Logging.general.error("MastodonController initialization failed: \(String(describing: error))")
|
Logging.general.error("MastodonController initialization failed: \(String(describing: error))")
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,9 +39,9 @@ class OpenInSafariActivity: UIActivity {
|
||||||
static func completionHandler(navigator: TuskerNavigationDelegate, url: URL) -> UIActivityViewController.CompletionWithItemsHandler {
|
static func completionHandler(navigator: TuskerNavigationDelegate, url: URL) -> UIActivityViewController.CompletionWithItemsHandler {
|
||||||
return { (activityType, _, _, _) in
|
return { (activityType, _, _, _) in
|
||||||
if activityType == .openInSafari {
|
if activityType == .openInSafari {
|
||||||
let vc = SFSafariViewController(url: url)
|
MainActor.runUnsafely {
|
||||||
vc.preferredControlTintColor = Preferences.shared.accentColor.color
|
navigator.selected(url: url, allowResolveStatuses: false, allowUniversalLinks: false)
|
||||||
navigator.show(vc)
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,6 +77,7 @@ class ImageDataCache {
|
||||||
try? disk?.removeAll()
|
try? disk?.removeAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: consider removing this and letting ImageCache just use the UIImage thumbnailing API
|
||||||
private func scaleImageIfDesired(data: Data) -> UIImage? {
|
private func scaleImageIfDesired(data: Data) -> UIImage? {
|
||||||
guard let desiredPixelSize = desiredPixelSize,
|
guard let desiredPixelSize = desiredPixelSize,
|
||||||
let source = CGImageSourceCreateWithData(data as CFData, [kCGImageSourceShouldCache: false] as CFDictionary) else {
|
let source = CGImageSourceCreateWithData(data as CFData, [kCGImageSourceShouldCache: false] as CFDictionary) else {
|
||||||
|
@ -84,14 +85,14 @@ class ImageDataCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
let maxDimension = max(desiredPixelSize.width, desiredPixelSize.height)
|
let maxDimension = max(desiredPixelSize.width, desiredPixelSize.height)
|
||||||
let downsampleOptions = [
|
let downsampleOptions: [CFString: Any] = [
|
||||||
kCGImageSourceCreateThumbnailFromImageAlways: true,
|
kCGImageSourceCreateThumbnailFromImageAlways: true,
|
||||||
kCGImageSourceShouldCacheImmediately: true,
|
kCGImageSourceShouldCacheImmediately: true,
|
||||||
kCGImageSourceCreateThumbnailWithTransform: true,
|
kCGImageSourceCreateThumbnailWithTransform: true,
|
||||||
kCGImageSourceThumbnailMaxPixelSize: maxDimension
|
kCGImageSourceThumbnailMaxPixelSize: maxDimension
|
||||||
] as CFDictionary
|
]
|
||||||
|
|
||||||
if let downsampled = CGImageSourceCreateThumbnailAtIndex(source, 0, downsampleOptions) {
|
if let downsampled = CGImageSourceCreateThumbnailAtIndex(source, 0, downsampleOptions as CFDictionary) {
|
||||||
return UIImage(cgImage: downsampled)
|
return UIImage(cgImage: downsampled)
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
//
|
||||||
|
// MainActor+Unsafe.swift
|
||||||
|
// Tusker
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 2/19/23.
|
||||||
|
// Copyright © 2023 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copied from https://github.com/ChimeHQ/ConcurrencyPlus/blob/fe3b3fd5436b196d8c5211ab2cc4b69fc35524fe/Sources/ConcurrencyPlus/MainActor%2BUnsafe.swift
|
||||||
|
|
||||||
|
Copyright (c) 2022, Chime
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public extension MainActor {
|
||||||
|
/// Execute the given body closure on the main actor without enforcing MainActor isolation.
|
||||||
|
///
|
||||||
|
/// This function exists to work around libraries with incorrect/inconsistent concurrency annotations. You should be **extremely** careful when using it, and only as a last resort.
|
||||||
|
///
|
||||||
|
/// It will crash if run on any non-main thread.
|
||||||
|
@MainActor(unsafe)
|
||||||
|
static func runUnsafely<T>(_ body: @MainActor () throws -> T) rethrows -> T {
|
||||||
|
dispatchPrecondition(condition: .onQueue(.main))
|
||||||
|
|
||||||
|
return try body()
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,7 +11,7 @@ import Pachyderm
|
||||||
import Combine
|
import Combine
|
||||||
|
|
||||||
/// An opaque object that serves as the cache for the filtered-ness of a particular status.
|
/// An opaque object that serves as the cache for the filtered-ness of a particular status.
|
||||||
class FilterState {
|
class FilterState: @unchecked Sendable {
|
||||||
static var unknown: FilterState { FilterState(state: .unknown) }
|
static var unknown: FilterState { FilterState(state: .unknown) }
|
||||||
|
|
||||||
fileprivate var state: State
|
fileprivate var state: State
|
||||||
|
|
|
@ -128,7 +128,9 @@ private func createReblogAction(status: StatusMO, container: StatusSwipeActionCo
|
||||||
|
|
||||||
private func createShareAction(status: StatusMO, container: StatusSwipeActionContainer) -> UIContextualAction {
|
private func createShareAction(status: StatusMO, container: StatusSwipeActionContainer) -> UIContextualAction {
|
||||||
let action = UIContextualAction(style: .normal, title: "Share") { [unowned container] _, _, completion in
|
let action = UIContextualAction(style: .normal, title: "Share") { [unowned container] _, _, completion in
|
||||||
|
MainActor.runUnsafely {
|
||||||
container.navigationDelegate.showMoreOptions(forStatus: status.id, source: .view(container))
|
container.navigationDelegate.showMoreOptions(forStatus: status.id, source: .view(container))
|
||||||
|
}
|
||||||
completion(true)
|
completion(true)
|
||||||
}
|
}
|
||||||
// bold to more closesly match other action symbols
|
// bold to more closesly match other action symbols
|
||||||
|
@ -166,7 +168,9 @@ private func createBookmarkAction(status: StatusMO, container: StatusSwipeAction
|
||||||
|
|
||||||
private func createOpenInSafariAction(status: StatusMO, container: StatusSwipeActionContainer) -> UIContextualAction {
|
private func createOpenInSafariAction(status: StatusMO, container: StatusSwipeActionContainer) -> UIContextualAction {
|
||||||
let action = UIContextualAction(style: .normal, title: "Open in Safari") { [unowned container] _, _, completion in
|
let action = UIContextualAction(style: .normal, title: "Open in Safari") { [unowned container] _, _, completion in
|
||||||
|
MainActor.runUnsafely {
|
||||||
container.navigationDelegate.selected(url: status.url!, allowUniversalLinks: false)
|
container.navigationDelegate.selected(url: status.url!, allowUniversalLinks: false)
|
||||||
|
}
|
||||||
completion(true)
|
completion(true)
|
||||||
}
|
}
|
||||||
action.image = UIImage(systemName: "safari")
|
action.image = UIImage(systemName: "safari")
|
||||||
|
|
|
@ -49,7 +49,7 @@ class SavedDataManager: Codable {
|
||||||
var changed = false
|
var changed = false
|
||||||
|
|
||||||
if let hashtags = savedHashtags[accountID] {
|
if let hashtags = savedHashtags[accountID] {
|
||||||
let objects = hashtags.map {
|
let objects: [[String: Any]] = hashtags.map {
|
||||||
["url": $0.url, "name": $0.name]
|
["url": $0.url, "name": $0.name]
|
||||||
}
|
}
|
||||||
let hashtagsReq = NSBatchInsertRequest(entity: SavedHashtag.entity(), objects: objects)
|
let hashtagsReq = NSBatchInsertRequest(entity: SavedHashtag.entity(), objects: objects)
|
||||||
|
|
|
@ -102,7 +102,7 @@ class AccountFollowsListViewController: UIViewController, CollectionViewControll
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func request(for range: RequestRange) -> Request<[Account]> {
|
private nonisolated func request(for range: RequestRange) -> Request<[Account]> {
|
||||||
switch mode {
|
switch mode {
|
||||||
case .following:
|
case .following:
|
||||||
return Account.getFollowing(accountID, range: range.withCount(Self.pageSize))
|
return Account.getFollowing(accountID, range: range.withCount(Self.pageSize))
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import Pachyderm
|
import Pachyderm
|
||||||
import AVFoundation
|
@preconcurrency import AVFoundation
|
||||||
import VisionKit
|
@preconcurrency import VisionKit
|
||||||
|
|
||||||
protocol LargeImageContentView: UIView {
|
protocol LargeImageContentView: UIView {
|
||||||
var animationImage: UIImage? { get }
|
var animationImage: UIImage? { get }
|
||||||
|
|
|
@ -29,6 +29,37 @@ The above copyright notice and this permission notice shall be included in all c
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
^[[ConcurrencyPlus](https://github.com/ChimeHQ/ConcurrencyPlus)](headingLevel: 2)
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2022, Chime
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
^[[SwiftSoup](https://github.com/scinfu/swiftsoup)](headingLevel: 2)
|
^[[SwiftSoup](https://github.com/scinfu/swiftsoup)](headingLevel: 2)
|
||||||
Copyright (c) 2016 Nabil Chatbi
|
Copyright (c) 2016 Nabil Chatbi
|
||||||
|
|
||||||
|
|
|
@ -79,12 +79,11 @@ struct AdvancedPrefsView : View {
|
||||||
}
|
}
|
||||||
.appGroupedListRowBackground()
|
.appGroupedListRowBackground()
|
||||||
.task {
|
.task {
|
||||||
CKContainer.default().accountStatus { status, error in
|
do {
|
||||||
if let error {
|
let status = try await CKContainer.default().accountStatus()
|
||||||
Logging.general.error("Unable to get CloudKit status: \(String(describing: error))")
|
|
||||||
} else {
|
|
||||||
self.cloudKitStatus = status
|
self.cloudKitStatus = status
|
||||||
}
|
} catch {
|
||||||
|
Logging.general.error("Unable to get CloudKit status: \(String(describing: error))")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import Pachyderm
|
||||||
import WebURLFoundationExtras
|
import WebURLFoundationExtras
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
|
@MainActor
|
||||||
protocol MenuActionProvider: AnyObject {
|
protocol MenuActionProvider: AnyObject {
|
||||||
var navigationDelegate: TuskerNavigationDelegate? { get }
|
var navigationDelegate: TuskerNavigationDelegate? { get }
|
||||||
var toastableViewController: ToastableViewController? { get }
|
var toastableViewController: ToastableViewController? { get }
|
||||||
|
@ -86,12 +87,11 @@ extension MenuActionProvider {
|
||||||
self.navigationDelegate!.present($0, animated: true)
|
self.navigationDelegate!.present($0, animated: true)
|
||||||
}) { list in
|
}) { list in
|
||||||
let req = List.add(list, accounts: [accountID])
|
let req = List.add(list, accounts: [accountID])
|
||||||
mastodonController.run(req) { response in
|
let response = await mastodonController.runResponse(req)
|
||||||
if case .failure(let error) = response {
|
if case .failure(let error) = response {
|
||||||
self.handleError(error, title: "Error Adding to List")
|
self.handleError(error, title: "Error Adding to List")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
service.run()
|
service.run()
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
//
|
|
||||||
// StatusTablePrefetching.swift
|
|
||||||
// Tusker
|
|
||||||
//
|
|
||||||
// Created by Shadowfacts on 1/18/21.
|
|
||||||
// Copyright © 2021 Shadowfacts. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import CoreData
|
|
||||||
|
|
||||||
protocol StatusTablePrefetching: TuskerNavigationDelegate {
|
|
||||||
}
|
|
||||||
|
|
||||||
extension StatusTablePrefetching {
|
|
||||||
|
|
||||||
func prefetchStatuses(with ids: [String]) {
|
|
||||||
let context = apiController.persistentContainer.prefetchBackgroundContext
|
|
||||||
context.perform {
|
|
||||||
guard let statuses = getStatusesWith(ids: ids, in: context) else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for status in statuses {
|
|
||||||
guard let avatar = status.account.avatar else { continue }
|
|
||||||
ImageCache.avatars.fetchIfNotCached(avatar)
|
|
||||||
for attachment in status.attachments where attachment.kind == .image {
|
|
||||||
ImageCache.attachments.fetchIfNotCached(attachment.url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fileprivate func getStatusesWith(ids: [String], in context: NSManagedObjectContext) -> [StatusMO]? {
|
|
||||||
let request: NSFetchRequest<StatusMO> = StatusMO.fetchRequest()
|
|
||||||
request.predicate = NSPredicate(format: "id IN %@", ids)
|
|
||||||
return try? context.fetch(request)
|
|
||||||
}
|
|
|
@ -23,17 +23,18 @@ protocol TimelineLikeCollectionViewController: UIViewController, TimelineLikeCon
|
||||||
var dataSource: UICollectionViewDiffableDataSource<Section, Item>! { get }
|
var dataSource: UICollectionViewDiffableDataSource<Section, Item>! { get }
|
||||||
}
|
}
|
||||||
|
|
||||||
protocol TimelineLikeCollectionViewSection: Hashable {
|
protocol TimelineLikeCollectionViewSection: Hashable, Sendable {
|
||||||
static var entries: Self { get }
|
static var entries: Self { get }
|
||||||
static var footer: Self { get }
|
static var footer: Self { get }
|
||||||
}
|
}
|
||||||
|
|
||||||
protocol TimelineLikeCollectionViewItem: Hashable {
|
protocol TimelineLikeCollectionViewItem: Hashable, Sendable {
|
||||||
associatedtype TimelineItem
|
associatedtype TimelineItem
|
||||||
|
|
||||||
static var loadingIndicator: Self { get }
|
static var loadingIndicator: Self { get }
|
||||||
static var confirmLoadMore: Self { get }
|
static var confirmLoadMore: Self { get }
|
||||||
|
|
||||||
|
@MainActor
|
||||||
static func fromTimelineItem(_ item: TimelineItem) -> Self
|
static func fromTimelineItem(_ item: TimelineItem) -> Self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ import Foundation
|
||||||
import OSLog
|
import OSLog
|
||||||
|
|
||||||
protocol TimelineLikeControllerDelegate<TimelineItem>: AnyObject {
|
protocol TimelineLikeControllerDelegate<TimelineItem>: AnyObject {
|
||||||
associatedtype TimelineItem
|
associatedtype TimelineItem: Sendable
|
||||||
|
|
||||||
func loadInitial() async throws -> [TimelineItem]
|
func loadInitial() async throws -> [TimelineItem]
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ protocol TimelineLikeControllerDelegate<TimelineItem>: AnyObject {
|
||||||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "TimelineLikeController")
|
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "TimelineLikeController")
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
class TimelineLikeController<Item> {
|
class TimelineLikeController<Item: Sendable> {
|
||||||
|
|
||||||
private unowned var delegate: any TimelineLikeControllerDelegate<Item>
|
private unowned var delegate: any TimelineLikeControllerDelegate<Item>
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ import UIKit
|
||||||
import SafariServices
|
import SafariServices
|
||||||
import Pachyderm
|
import Pachyderm
|
||||||
|
|
||||||
|
@MainActor
|
||||||
protocol TuskerNavigationDelegate: UIViewController, ToastableViewController {
|
protocol TuskerNavigationDelegate: UIViewController, ToastableViewController {
|
||||||
var apiController: MastodonController! { get }
|
var apiController: MastodonController! { get }
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,8 +90,10 @@ extension BaseEmojiLabel {
|
||||||
// even though the closures is invoked on the same thread that withLock is called, so it's unclear why it needs to be @Sendable (FB11494878)
|
// even though the closures is invoked on the same thread that withLock is called, so it's unclear why it needs to be @Sendable (FB11494878)
|
||||||
// so, just ignore the warnings
|
// so, just ignore the warnings
|
||||||
let emojiAttachments = emojiImages.withLock {
|
let emojiAttachments = emojiImages.withLock {
|
||||||
$0.mapValues { image in
|
let emojiFont = self.emojiFont
|
||||||
NSTextAttachment(emojiImage: image, in: self.emojiFont, with: self.emojiTextColor)
|
let emojiTextColor = self.emojiTextColor
|
||||||
|
return $0.mapValues { image in
|
||||||
|
NSTextAttachment(emojiImage: image, in: emojiFont, with: emojiTextColor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let placeholder = usePlaceholders ? NSTextAttachment(emojiPlaceholderIn: self.emojiFont) : nil
|
let placeholder = usePlaceholders ? NSTextAttachment(emojiPlaceholderIn: self.emojiFont) : nil
|
||||||
|
|
|
@ -266,6 +266,7 @@ private class ProfileFieldValueView: UIView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
private struct ProfileFieldVerificationView: View {
|
private struct ProfileFieldVerificationView: View {
|
||||||
let acct: String
|
let acct: String
|
||||||
let verifiedAt: Date
|
let verifiedAt: Date
|
||||||
|
|
Loading…
Reference in New Issue