mirror of
https://github.com/metabolist/metatext.git
synced 2024-11-22 00:01:00 +00:00
Put Mastodon API code in separate module from entities
This commit is contained in:
parent
f6f065e143
commit
f921d154b3
53 changed files with 170 additions and 101 deletions
|
@ -38,11 +38,11 @@ struct StoredStatus: Codable, Hashable {
|
||||||
|
|
||||||
extension StoredStatus: FetchableRecord, PersistableRecord {
|
extension StoredStatus: FetchableRecord, PersistableRecord {
|
||||||
static func databaseJSONDecoder(for column: String) -> JSONDecoder {
|
static func databaseJSONDecoder(for column: String) -> JSONDecoder {
|
||||||
APIDecoder()
|
MastodonDecoder()
|
||||||
}
|
}
|
||||||
|
|
||||||
static func databaseJSONEncoder(for column: String) -> JSONEncoder {
|
static func databaseJSONEncoder(for column: String) -> JSONEncoder {
|
||||||
APIEncoder()
|
MastodonEncoder()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,10 +6,10 @@ import Mastodon
|
||||||
|
|
||||||
extension Account: FetchableRecord, PersistableRecord {
|
extension Account: FetchableRecord, PersistableRecord {
|
||||||
public static func databaseJSONDecoder(for column: String) -> JSONDecoder {
|
public static func databaseJSONDecoder(for column: String) -> JSONDecoder {
|
||||||
APIDecoder()
|
MastodonDecoder()
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func databaseJSONEncoder(for column: String) -> JSONEncoder {
|
public static func databaseJSONEncoder(for column: String) -> JSONEncoder {
|
||||||
APIEncoder()
|
MastodonEncoder()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,10 +6,10 @@ import Mastodon
|
||||||
|
|
||||||
extension Filter: FetchableRecord, PersistableRecord {
|
extension Filter: FetchableRecord, PersistableRecord {
|
||||||
public static func databaseJSONDecoder(for column: String) -> JSONDecoder {
|
public static func databaseJSONDecoder(for column: String) -> JSONDecoder {
|
||||||
APIDecoder()
|
MastodonDecoder()
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func databaseJSONEncoder(for column: String) -> JSONEncoder {
|
public static func databaseJSONEncoder(for column: String) -> JSONEncoder {
|
||||||
APIEncoder()
|
MastodonEncoder()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,24 +11,13 @@ let package = Package(
|
||||||
products: [
|
products: [
|
||||||
.library(
|
.library(
|
||||||
name: "Mastodon",
|
name: "Mastodon",
|
||||||
targets: ["Mastodon"]),
|
targets: ["Mastodon"])
|
||||||
.library(
|
|
||||||
name: "MastodonStubs",
|
|
||||||
targets: ["MastodonStubs"])
|
|
||||||
],
|
|
||||||
dependencies: [
|
|
||||||
.package(path: "HTTP")
|
|
||||||
],
|
],
|
||||||
|
dependencies: [],
|
||||||
targets: [
|
targets: [
|
||||||
.target(
|
.target(name: "Mastodon"),
|
||||||
name: "Mastodon",
|
|
||||||
dependencies: ["HTTP"]),
|
|
||||||
.target(
|
|
||||||
name: "MastodonStubs",
|
|
||||||
dependencies: ["Mastodon", .product(name: "Stubbing", package: "HTTP")],
|
|
||||||
resources: [.process("Resources")]),
|
|
||||||
.testTarget(
|
.testTarget(
|
||||||
name: "MastodonTests",
|
name: "MastodonTests",
|
||||||
dependencies: ["MastodonStubs"])
|
dependencies: ["Mastodon"])
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public final class APIDecoder: JSONDecoder {
|
public final class MastodonDecoder: JSONDecoder {
|
||||||
public override init() {
|
public override init() {
|
||||||
super.init()
|
super.init()
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public final class APIEncoder: JSONEncoder {
|
public final class MastodonEncoder: JSONEncoder {
|
||||||
public override init() {
|
public override init() {
|
||||||
super.init()
|
super.init()
|
||||||
|
|
|
@ -12,21 +12,6 @@ public enum Timeline: Hashable {
|
||||||
|
|
||||||
public extension Timeline {
|
public extension Timeline {
|
||||||
static let nonLists: [Timeline] = [.home, .local, .federated]
|
static let nonLists: [Timeline] = [.home, .local, .federated]
|
||||||
|
|
||||||
var endpoint: TimelinesEndpoint {
|
|
||||||
switch self {
|
|
||||||
case .home:
|
|
||||||
return .home
|
|
||||||
case .local:
|
|
||||||
return .public(local: true)
|
|
||||||
case .federated:
|
|
||||||
return .public(local: false)
|
|
||||||
case let .list(list):
|
|
||||||
return .list(id: list.id)
|
|
||||||
case let .tag(tag):
|
|
||||||
return .tag(tag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Timeline: Identifiable {
|
extension Timeline: Identifiable {
|
||||||
|
|
5
MastodonAPI/.gitignore
vendored
Normal file
5
MastodonAPI/.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
.DS_Store
|
||||||
|
/.build
|
||||||
|
/Packages
|
||||||
|
/*.xcodeproj
|
||||||
|
xcuserdata/
|
35
MastodonAPI/Package.swift
Normal file
35
MastodonAPI/Package.swift
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
// swift-tools-version:5.3
|
||||||
|
|
||||||
|
import PackageDescription
|
||||||
|
|
||||||
|
let package = Package(
|
||||||
|
name: "MastodonAPI",
|
||||||
|
platforms: [
|
||||||
|
.iOS(.v14),
|
||||||
|
.macOS(.v11)
|
||||||
|
],
|
||||||
|
products: [
|
||||||
|
.library(
|
||||||
|
name: "MastodonAPI",
|
||||||
|
targets: ["MastodonAPI"]),
|
||||||
|
.library(
|
||||||
|
name: "MastodonAPIStubs",
|
||||||
|
targets: ["MastodonAPIStubs"])
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
.package(path: "HTTP"),
|
||||||
|
.package(path: "Mastodon")
|
||||||
|
],
|
||||||
|
targets: [
|
||||||
|
.target(
|
||||||
|
name: "MastodonAPI",
|
||||||
|
dependencies: ["HTTP", "Mastodon"]),
|
||||||
|
.target(
|
||||||
|
name: "MastodonAPIStubs",
|
||||||
|
dependencies: ["MastodonAPI", .product(name: "Stubbing", package: "HTTP")],
|
||||||
|
resources: [.process("Resources")]),
|
||||||
|
.testTarget(
|
||||||
|
name: "MastodonAPITests",
|
||||||
|
dependencies: ["MastodonAPIStubs"])
|
||||||
|
]
|
||||||
|
)
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import HTTP
|
import HTTP
|
||||||
|
import Mastodon
|
||||||
|
|
||||||
public enum AccessTokenEndpoint {
|
public enum AccessTokenEndpoint {
|
||||||
case oauthToken(
|
case oauthToken(
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import HTTP
|
import HTTP
|
||||||
|
import Mastodon
|
||||||
|
|
||||||
public enum AccountEndpoint {
|
public enum AccountEndpoint {
|
||||||
case verifyCredentials
|
case verifyCredentials
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import HTTP
|
import HTTP
|
||||||
|
import Mastodon
|
||||||
|
|
||||||
public enum AppAuthorizationEndpoint {
|
public enum AppAuthorizationEndpoint {
|
||||||
case apps(clientName: String, redirectURI: String, scopes: String, website: URL?)
|
case apps(clientName: String, redirectURI: String, scopes: String, website: URL?)
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import HTTP
|
import HTTP
|
||||||
|
import Mastodon
|
||||||
|
|
||||||
public enum ContextEndpoint {
|
public enum ContextEndpoint {
|
||||||
case context(id: String)
|
case context(id: String)
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import HTTP
|
import HTTP
|
||||||
|
import Mastodon
|
||||||
|
|
||||||
public enum DeletionEndpoint {
|
public enum DeletionEndpoint {
|
||||||
case oauthRevoke(token: String, clientID: String, clientSecret: String)
|
case oauthRevoke(token: String, clientID: String, clientSecret: String)
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import HTTP
|
import HTTP
|
||||||
|
import Mastodon
|
||||||
|
|
||||||
public enum FilterEndpoint {
|
public enum FilterEndpoint {
|
||||||
case create(
|
case create(
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import HTTP
|
import HTTP
|
||||||
|
import Mastodon
|
||||||
|
|
||||||
public enum FiltersEndpoint {
|
public enum FiltersEndpoint {
|
||||||
case filters
|
case filters
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import HTTP
|
import HTTP
|
||||||
|
import Mastodon
|
||||||
|
|
||||||
public enum InstanceEndpoint {
|
public enum InstanceEndpoint {
|
||||||
case instance
|
case instance
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import HTTP
|
import HTTP
|
||||||
|
import Mastodon
|
||||||
|
|
||||||
public enum ListEndpoint {
|
public enum ListEndpoint {
|
||||||
case create(title: String)
|
case create(title: String)
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import HTTP
|
import HTTP
|
||||||
|
import Mastodon
|
||||||
|
|
||||||
public enum ListsEndpoint {
|
public enum ListsEndpoint {
|
||||||
case lists
|
case lists
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import HTTP
|
import HTTP
|
||||||
|
import Mastodon
|
||||||
|
|
||||||
public struct Paged<T: Endpoint> {
|
public struct Paged<T: Endpoint> {
|
||||||
public let endpoint: T
|
public let endpoint: T
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import HTTP
|
import HTTP
|
||||||
|
import Mastodon
|
||||||
|
|
||||||
public enum PreferencesEndpoint {
|
public enum PreferencesEndpoint {
|
||||||
case preferences
|
case preferences
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import HTTP
|
import HTTP
|
||||||
|
import Mastodon
|
||||||
|
|
||||||
public enum PushSubscriptionEndpoint {
|
public enum PushSubscriptionEndpoint {
|
||||||
case create(
|
case create(
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import HTTP
|
import HTTP
|
||||||
|
import Mastodon
|
||||||
|
|
||||||
public enum StatusEndpoint {
|
public enum StatusEndpoint {
|
||||||
case status(id: String)
|
case status(id: String)
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import HTTP
|
import HTTP
|
||||||
|
import Mastodon
|
||||||
|
|
||||||
public enum TimelinesEndpoint {
|
public enum TimelinesEndpoint {
|
||||||
case `public`(local: Bool)
|
case `public`(local: Bool)
|
|
@ -0,0 +1,21 @@
|
||||||
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Mastodon
|
||||||
|
|
||||||
|
public extension Timeline {
|
||||||
|
var endpoint: TimelinesEndpoint {
|
||||||
|
switch self {
|
||||||
|
case .home:
|
||||||
|
return .home
|
||||||
|
case .local:
|
||||||
|
return .public(local: true)
|
||||||
|
case .federated:
|
||||||
|
return .public(local: false)
|
||||||
|
case let .list(list):
|
||||||
|
return .list(id: list.id)
|
||||||
|
case let .tag(tag):
|
||||||
|
return .tag(tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,13 +3,14 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import Combine
|
import Combine
|
||||||
import HTTP
|
import HTTP
|
||||||
|
import Mastodon
|
||||||
|
|
||||||
public final class APIClient: Client {
|
public final class MastodonAPIClient: Client {
|
||||||
public var instanceURL: URL?
|
public var instanceURL: URL?
|
||||||
public var accessToken: String?
|
public var accessToken: String?
|
||||||
|
|
||||||
public required init(session: Session) {
|
public required init(session: Session) {
|
||||||
super.init(session: session, decoder: APIDecoder())
|
super.init(session: session, decoder: MastodonDecoder())
|
||||||
}
|
}
|
||||||
|
|
||||||
public override func request<T: DecodableTarget>(_ target: T) -> AnyPublisher<T.ResultType, Error> {
|
public override func request<T: DecodableTarget>(_ target: T) -> AnyPublisher<T.ResultType, Error> {
|
||||||
|
@ -17,14 +18,14 @@ public final class APIClient: Client {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension APIClient {
|
extension MastodonAPIClient {
|
||||||
public func request<E: Endpoint>(_ endpoint: E) -> AnyPublisher<E.ResultType, Error> {
|
public func request<E: Endpoint>(_ endpoint: E) -> AnyPublisher<E.ResultType, Error> {
|
||||||
guard let instanceURL = instanceURL else {
|
guard let instanceURL = instanceURL else {
|
||||||
return Fail(error: URLError(.badURL)).eraseToAnyPublisher()
|
return Fail(error: URLError(.badURL)).eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.request(
|
return super.request(
|
||||||
APITarget(baseURL: instanceURL, endpoint: endpoint, accessToken: accessToken),
|
MastodonAPITarget(baseURL: instanceURL, endpoint: endpoint, accessToken: accessToken),
|
||||||
decodeErrorsAs: APIError.self)
|
decodeErrorsAs: APIError.self)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,7 +3,7 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import HTTP
|
import HTTP
|
||||||
|
|
||||||
public struct APITarget<E: Endpoint> {
|
public struct MastodonAPITarget<E: Endpoint> {
|
||||||
public let baseURL: URL
|
public let baseURL: URL
|
||||||
public let endpoint: E
|
public let endpoint: E
|
||||||
public let accessToken: String?
|
public let accessToken: String?
|
||||||
|
@ -15,7 +15,7 @@ public struct APITarget<E: Endpoint> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension APITarget: DecodableTarget {
|
extension MastodonAPITarget: DecodableTarget {
|
||||||
public typealias ResultType = E.ResultType
|
public typealias ResultType = E.ResultType
|
||||||
|
|
||||||
public var pathComponents: [String] { endpoint.pathComponents }
|
public var pathComponents: [String] { endpoint.pathComponents }
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright © 2020 Metabolist. All rights reserved.
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import Mastodon
|
import MastodonAPI
|
||||||
import Stubbing
|
import Stubbing
|
||||||
|
|
||||||
extension AccessTokenEndpoint: Stubbing {
|
extension AccessTokenEndpoint: Stubbing {
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright © 2020 Metabolist. All rights reserved.
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import Mastodon
|
import MastodonAPI
|
||||||
import Stubbing
|
import Stubbing
|
||||||
|
|
||||||
extension AccountEndpoint: Stubbing {
|
extension AccountEndpoint: Stubbing {
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright © 2020 Metabolist. All rights reserved.
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import Mastodon
|
import MastodonAPI
|
||||||
import Stubbing
|
import Stubbing
|
||||||
|
|
||||||
extension AppAuthorizationEndpoint: Stubbing {
|
extension AppAuthorizationEndpoint: Stubbing {
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright © 2020 Metabolist. All rights reserved.
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import Mastodon
|
import MastodonAPI
|
||||||
import Stubbing
|
import Stubbing
|
||||||
|
|
||||||
extension ContextEndpoint: Stubbing {
|
extension ContextEndpoint: Stubbing {
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright © 2020 Metabolist. All rights reserved.
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import Mastodon
|
import MastodonAPI
|
||||||
import Stubbing
|
import Stubbing
|
||||||
|
|
||||||
extension InstanceEndpoint: Stubbing {
|
extension InstanceEndpoint: Stubbing {
|
|
@ -1,10 +1,10 @@
|
||||||
// Copyright © 2020 Metabolist. All rights reserved.
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import Mastodon
|
import MastodonAPI
|
||||||
import Stubbing
|
import Stubbing
|
||||||
|
|
||||||
extension APITarget: Stubbing {
|
extension MastodonAPITarget: Stubbing {
|
||||||
public func stub(url: URL) -> HTTPStub? {
|
public func stub(url: URL) -> HTTPStub? {
|
||||||
(endpoint as? Stubbing)?.stub(url: url)
|
(endpoint as? Stubbing)?.stub(url: url)
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright © 2020 Metabolist. All rights reserved.
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import Mastodon
|
import MastodonAPI
|
||||||
import Stubbing
|
import Stubbing
|
||||||
|
|
||||||
extension Paged: Stubbing where T: Stubbing {
|
extension Paged: Stubbing where T: Stubbing {
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright © 2020 Metabolist. All rights reserved.
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import Mastodon
|
import MastodonAPI
|
||||||
import Stubbing
|
import Stubbing
|
||||||
|
|
||||||
extension PreferencesEndpoint: Stubbing {
|
extension PreferencesEndpoint: Stubbing {
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright © 2020 Metabolist. All rights reserved.
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import Mastodon
|
import MastodonAPI
|
||||||
import Stubbing
|
import Stubbing
|
||||||
|
|
||||||
extension TimelinesEndpoint: Stubbing {
|
extension TimelinesEndpoint: Stubbing {
|
10
MastodonAPI/Tests/MastodonAPITests/MastodonAPITests.swift
Normal file
10
MastodonAPI/Tests/MastodonAPITests/MastodonAPITests.swift
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import XCTest
|
||||||
|
@testable import MastodonAPI
|
||||||
|
|
||||||
|
final class MastodonAPITests: XCTestCase {
|
||||||
|
func testExample() {
|
||||||
|
// This is an example of a functional test case.
|
||||||
|
// Use XCTAssert and related functions to verify your tests produce the correct
|
||||||
|
// results.
|
||||||
|
}
|
||||||
|
}
|
|
@ -95,6 +95,7 @@
|
||||||
D0BEB21024FA2A90001B0F04 /* EditFilterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditFilterView.swift; sourceTree = "<group>"; };
|
D0BEB21024FA2A90001B0F04 /* EditFilterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditFilterView.swift; sourceTree = "<group>"; };
|
||||||
D0BECB952501B3DD002C1B13 /* Keychain */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Keychain; sourceTree = "<group>"; };
|
D0BECB952501B3DD002C1B13 /* Keychain */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Keychain; sourceTree = "<group>"; };
|
||||||
D0BECB962501BCE0002C1B13 /* Secrets */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Secrets; sourceTree = "<group>"; };
|
D0BECB962501BCE0002C1B13 /* Secrets */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Secrets; sourceTree = "<group>"; };
|
||||||
|
D0BECB9D2501CBC3002C1B13 /* MastodonAPI */ = {isa = PBXFileReference; lastKnownFileType = folder; path = MastodonAPI; sourceTree = "<group>"; };
|
||||||
D0BFDAF524FC7C5300C86618 /* HTTP */ = {isa = PBXFileReference; lastKnownFileType = folder; path = HTTP; sourceTree = "<group>"; };
|
D0BFDAF524FC7C5300C86618 /* HTTP */ = {isa = PBXFileReference; lastKnownFileType = folder; path = HTTP; sourceTree = "<group>"; };
|
||||||
D0C7D41E24F76169001EBDBB /* Metatext.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Metatext.entitlements; sourceTree = "<group>"; };
|
D0C7D41E24F76169001EBDBB /* Metatext.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Metatext.entitlements; sourceTree = "<group>"; };
|
||||||
D0C7D41F24F76169001EBDBB /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
D0C7D41F24F76169001EBDBB /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
@ -188,6 +189,7 @@
|
||||||
D0BECB952501B3DD002C1B13 /* Keychain */,
|
D0BECB952501B3DD002C1B13 /* Keychain */,
|
||||||
D0C7D45624F76169001EBDBB /* Localizations */,
|
D0C7D45624F76169001EBDBB /* Localizations */,
|
||||||
D0E0F1E424FC49FC002C04BF /* Mastodon */,
|
D0E0F1E424FC49FC002C04BF /* Mastodon */,
|
||||||
|
D0BECB9D2501CBC3002C1B13 /* MastodonAPI */,
|
||||||
D0E5361A24E3EB4D00FB1CE1 /* Notification Service Extension */,
|
D0E5361A24E3EB4D00FB1CE1 /* Notification Service Extension */,
|
||||||
D047FA8D24C3E21200AF17C5 /* Products */,
|
D047FA8D24C3E21200AF17C5 /* Products */,
|
||||||
D0BECB962501BCE0002C1B13 /* Secrets */,
|
D0BECB962501BCE0002C1B13 /* Secrets */,
|
||||||
|
|
|
@ -24,7 +24,7 @@ class NotificationService: UNNotificationServiceExtension {
|
||||||
do {
|
do {
|
||||||
let decryptedJSON = try Self.extractAndDecrypt(userInfo: request.content.userInfo)
|
let decryptedJSON = try Self.extractAndDecrypt(userInfo: request.content.userInfo)
|
||||||
|
|
||||||
pushNotification = try APIDecoder().decode(PushNotification.self, from: decryptedJSON)
|
pushNotification = try MastodonDecoder().decode(PushNotification.self, from: decryptedJSON)
|
||||||
} catch {
|
} catch {
|
||||||
contentHandler(bestAttemptContent)
|
contentHandler(bestAttemptContent)
|
||||||
|
|
||||||
|
|
|
@ -20,18 +20,18 @@ let package = Package(
|
||||||
.package(url: "https://github.com/groue/CombineExpectations.git", .upToNextMajor(from: "0.5.0")),
|
.package(url: "https://github.com/groue/CombineExpectations.git", .upToNextMajor(from: "0.5.0")),
|
||||||
.package(path: "DB"),
|
.package(path: "DB"),
|
||||||
.package(path: "Keychain"),
|
.package(path: "Keychain"),
|
||||||
.package(path: "Mastodon"),
|
.package(path: "MastodonAPI"),
|
||||||
.package(path: "Secrets")
|
.package(path: "Secrets")
|
||||||
],
|
],
|
||||||
targets: [
|
targets: [
|
||||||
.target(
|
.target(
|
||||||
name: "ServiceLayer",
|
name: "ServiceLayer",
|
||||||
dependencies: ["DB", "Secrets"]),
|
dependencies: ["DB", "MastodonAPI", "Secrets"]),
|
||||||
.target(
|
.target(
|
||||||
name: "ServiceLayerMocks",
|
name: "ServiceLayerMocks",
|
||||||
dependencies: [
|
dependencies: [
|
||||||
"ServiceLayer",
|
"ServiceLayer",
|
||||||
.product(name: "MastodonStubs", package: "Mastodon"),
|
.product(name: "MastodonAPIStubs", package: "MastodonAPI"),
|
||||||
.product(name: "MockKeychain", package: "Keychain")]),
|
.product(name: "MockKeychain", package: "Keychain")]),
|
||||||
.testTarget(
|
.testTarget(
|
||||||
name: "ServiceLayerTests",
|
name: "ServiceLayerTests",
|
||||||
|
|
|
@ -4,6 +4,7 @@ import DB
|
||||||
import Foundation
|
import Foundation
|
||||||
import Combine
|
import Combine
|
||||||
import Mastodon
|
import Mastodon
|
||||||
|
import MastodonAPI
|
||||||
import Secrets
|
import Secrets
|
||||||
|
|
||||||
public struct AllIdentitiesService {
|
public struct AllIdentitiesService {
|
||||||
|
@ -53,9 +54,9 @@ public extension AllIdentitiesService {
|
||||||
|
|
||||||
func deleteIdentity(_ identity: Identity) -> AnyPublisher<Never, Error> {
|
func deleteIdentity(_ identity: Identity) -> AnyPublisher<Never, Error> {
|
||||||
let secrets = Secrets(identityID: identity.id, keychain: environment.keychain)
|
let secrets = Secrets(identityID: identity.id, keychain: environment.keychain)
|
||||||
let networkClient = APIClient(session: environment.session)
|
let mastodonAPIClient = MastodonAPIClient(session: environment.session)
|
||||||
|
|
||||||
networkClient.instanceURL = identity.url
|
mastodonAPIClient.instanceURL = identity.url
|
||||||
|
|
||||||
return identityDatabase.deleteIdentity(id: identity.id)
|
return identityDatabase.deleteIdentity(id: identity.id)
|
||||||
.collect()
|
.collect()
|
||||||
|
@ -65,7 +66,7 @@ public extension AllIdentitiesService {
|
||||||
clientID: try secrets.item(.clientID),
|
clientID: try secrets.item(.clientID),
|
||||||
clientSecret: try secrets.item(.clientSecret))
|
clientSecret: try secrets.item(.clientSecret))
|
||||||
}
|
}
|
||||||
.flatMap(networkClient.request)
|
.flatMap(mastodonAPIClient.request)
|
||||||
.collect()
|
.collect()
|
||||||
.tryMap { _ in
|
.tryMap { _ in
|
||||||
try secrets.deleteAllItems()
|
try secrets.deleteAllItems()
|
|
@ -3,14 +3,15 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import Combine
|
import Combine
|
||||||
import Mastodon
|
import Mastodon
|
||||||
|
import MastodonAPI
|
||||||
|
|
||||||
public struct AuthenticationService {
|
public struct AuthenticationService {
|
||||||
private let networkClient: APIClient
|
private let mastodonAPIClient: MastodonAPIClient
|
||||||
private let webAuthSessionType: WebAuthSession.Type
|
private let webAuthSessionType: WebAuthSession.Type
|
||||||
private let webAuthSessionContextProvider = WebAuthSessionContextProvider()
|
private let webAuthSessionContextProvider = WebAuthSessionContextProvider()
|
||||||
|
|
||||||
public init(environment: AppEnvironment) {
|
public init(environment: AppEnvironment) {
|
||||||
networkClient = APIClient(session: environment.session)
|
mastodonAPIClient = MastodonAPIClient(session: environment.session)
|
||||||
webAuthSessionType = environment.webAuthSessionType
|
webAuthSessionType = environment.webAuthSessionType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,9 +23,9 @@ public extension AuthenticationService {
|
||||||
redirectURI: OAuth.callbackURL.absoluteString,
|
redirectURI: OAuth.callbackURL.absoluteString,
|
||||||
scopes: OAuth.scopes,
|
scopes: OAuth.scopes,
|
||||||
website: OAuth.website)
|
website: OAuth.website)
|
||||||
let target = APITarget(baseURL: instanceURL, endpoint: endpoint, accessToken: nil)
|
let target = MastodonAPITarget(baseURL: instanceURL, endpoint: endpoint, accessToken: nil)
|
||||||
|
|
||||||
return networkClient.request(target)
|
return mastodonAPIClient.request(target)
|
||||||
}
|
}
|
||||||
|
|
||||||
func authenticate(instanceURL: URL, appAuthorization: AppAuthorization) -> AnyPublisher<AccessToken, Error> {
|
func authenticate(instanceURL: URL, appAuthorization: AppAuthorization) -> AnyPublisher<AccessToken, Error> {
|
||||||
|
@ -63,9 +64,9 @@ public extension AuthenticationService {
|
||||||
grantType: OAuth.grantType,
|
grantType: OAuth.grantType,
|
||||||
scopes: OAuth.scopes,
|
scopes: OAuth.scopes,
|
||||||
redirectURI: OAuth.callbackURL.absoluteString)
|
redirectURI: OAuth.callbackURL.absoluteString)
|
||||||
let target = APITarget(baseURL: instanceURL, endpoint: endpoint, accessToken: nil)
|
let target = MastodonAPITarget(baseURL: instanceURL, endpoint: endpoint, accessToken: nil)
|
||||||
|
|
||||||
return networkClient.request(target)
|
return mastodonAPIClient.request(target)
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
|
@ -4,6 +4,7 @@ import DB
|
||||||
import Foundation
|
import Foundation
|
||||||
import Combine
|
import Combine
|
||||||
import Mastodon
|
import Mastodon
|
||||||
|
import MastodonAPI
|
||||||
import Secrets
|
import Secrets
|
||||||
|
|
||||||
public class IdentityService {
|
public class IdentityService {
|
||||||
|
@ -13,7 +14,7 @@ public class IdentityService {
|
||||||
private let identityDatabase: IdentityDatabase
|
private let identityDatabase: IdentityDatabase
|
||||||
private let contentDatabase: ContentDatabase
|
private let contentDatabase: ContentDatabase
|
||||||
private let environment: AppEnvironment
|
private let environment: AppEnvironment
|
||||||
private let networkClient: APIClient
|
private let mastodonAPIClient: MastodonAPIClient
|
||||||
private let secrets: Secrets
|
private let secrets: Secrets
|
||||||
private let observationErrorsInput = PassthroughSubject<Error, Never>()
|
private let observationErrorsInput = PassthroughSubject<Error, Never>()
|
||||||
|
|
||||||
|
@ -37,9 +38,9 @@ public class IdentityService {
|
||||||
secrets = Secrets(
|
secrets = Secrets(
|
||||||
identityID: identityID,
|
identityID: identityID,
|
||||||
keychain: environment.keychain)
|
keychain: environment.keychain)
|
||||||
networkClient = APIClient(session: environment.session)
|
mastodonAPIClient = MastodonAPIClient(session: environment.session)
|
||||||
networkClient.instanceURL = identity.url
|
mastodonAPIClient.instanceURL = identity.url
|
||||||
networkClient.accessToken = try? secrets.item(.accessToken)
|
mastodonAPIClient.accessToken = try? secrets.item(.accessToken)
|
||||||
|
|
||||||
contentDatabase = try ContentDatabase(identityID: identityID, inMemory: environment.inMemoryContent)
|
contentDatabase = try ContentDatabase(identityID: identityID, inMemory: environment.inMemoryContent)
|
||||||
|
|
||||||
|
@ -53,21 +54,21 @@ public class IdentityService {
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension IdentityService {
|
public extension IdentityService {
|
||||||
var isAuthorized: Bool { networkClient.accessToken != nil }
|
var isAuthorized: Bool { mastodonAPIClient.accessToken != nil }
|
||||||
|
|
||||||
func updateLastUse() -> AnyPublisher<Never, Error> {
|
func updateLastUse() -> AnyPublisher<Never, Error> {
|
||||||
identityDatabase.updateLastUsedAt(identityID: identity.id)
|
identityDatabase.updateLastUsedAt(identityID: identity.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifyCredentials() -> AnyPublisher<Never, Error> {
|
func verifyCredentials() -> AnyPublisher<Never, Error> {
|
||||||
networkClient.request(AccountEndpoint.verifyCredentials)
|
mastodonAPIClient.request(AccountEndpoint.verifyCredentials)
|
||||||
.zip(Just(identity.id).first().setFailureType(to: Error.self))
|
.zip(Just(identity.id).first().setFailureType(to: Error.self))
|
||||||
.flatMap(identityDatabase.updateAccount)
|
.flatMap(identityDatabase.updateAccount)
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
func refreshServerPreferences() -> AnyPublisher<Never, Error> {
|
func refreshServerPreferences() -> AnyPublisher<Never, Error> {
|
||||||
networkClient.request(PreferencesEndpoint.preferences)
|
mastodonAPIClient.request(PreferencesEndpoint.preferences)
|
||||||
.zip(Just(self).first().setFailureType(to: Error.self))
|
.zip(Just(self).first().setFailureType(to: Error.self))
|
||||||
.map { ($1.identity.preferences.updated(from: $0), $1.identity.id) }
|
.map { ($1.identity.preferences.updated(from: $0), $1.identity.id) }
|
||||||
.flatMap(identityDatabase.updatePreferences)
|
.flatMap(identityDatabase.updatePreferences)
|
||||||
|
@ -75,7 +76,7 @@ public extension IdentityService {
|
||||||
}
|
}
|
||||||
|
|
||||||
func refreshInstance() -> AnyPublisher<Never, Error> {
|
func refreshInstance() -> AnyPublisher<Never, Error> {
|
||||||
networkClient.request(InstanceEndpoint.instance)
|
mastodonAPIClient.request(InstanceEndpoint.instance)
|
||||||
.zip(Just(identity.id).first().setFailureType(to: Error.self))
|
.zip(Just(identity.id).first().setFailureType(to: Error.self))
|
||||||
.flatMap(identityDatabase.updateInstance)
|
.flatMap(identityDatabase.updateInstance)
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
@ -90,19 +91,19 @@ public extension IdentityService {
|
||||||
}
|
}
|
||||||
|
|
||||||
func refreshLists() -> AnyPublisher<Never, Error> {
|
func refreshLists() -> AnyPublisher<Never, Error> {
|
||||||
networkClient.request(ListsEndpoint.lists)
|
mastodonAPIClient.request(ListsEndpoint.lists)
|
||||||
.flatMap(contentDatabase.setLists(_:))
|
.flatMap(contentDatabase.setLists(_:))
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
func createList(title: String) -> AnyPublisher<Never, Error> {
|
func createList(title: String) -> AnyPublisher<Never, Error> {
|
||||||
networkClient.request(ListEndpoint.create(title: title))
|
mastodonAPIClient.request(ListEndpoint.create(title: title))
|
||||||
.flatMap(contentDatabase.createList(_:))
|
.flatMap(contentDatabase.createList(_:))
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteList(id: String) -> AnyPublisher<Never, Error> {
|
func deleteList(id: String) -> AnyPublisher<Never, Error> {
|
||||||
networkClient.request(DeletionEndpoint.list(id: id))
|
mastodonAPIClient.request(DeletionEndpoint.list(id: id))
|
||||||
.map { _ in id }
|
.map { _ in id }
|
||||||
.flatMap(contentDatabase.deleteList(id:))
|
.flatMap(contentDatabase.deleteList(id:))
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
@ -113,13 +114,13 @@ public extension IdentityService {
|
||||||
}
|
}
|
||||||
|
|
||||||
func refreshFilters() -> AnyPublisher<Never, Error> {
|
func refreshFilters() -> AnyPublisher<Never, Error> {
|
||||||
networkClient.request(FiltersEndpoint.filters)
|
mastodonAPIClient.request(FiltersEndpoint.filters)
|
||||||
.flatMap(contentDatabase.setFilters(_:))
|
.flatMap(contentDatabase.setFilters(_:))
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
func createFilter(_ filter: Filter) -> AnyPublisher<Never, Error> {
|
func createFilter(_ filter: Filter) -> AnyPublisher<Never, Error> {
|
||||||
networkClient.request(FilterEndpoint.create(phrase: filter.phrase,
|
mastodonAPIClient.request(FilterEndpoint.create(phrase: filter.phrase,
|
||||||
context: filter.context,
|
context: filter.context,
|
||||||
irreversible: filter.irreversible,
|
irreversible: filter.irreversible,
|
||||||
wholeWord: filter.wholeWord,
|
wholeWord: filter.wholeWord,
|
||||||
|
@ -129,7 +130,7 @@ public extension IdentityService {
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateFilter(_ filter: Filter) -> AnyPublisher<Never, Error> {
|
func updateFilter(_ filter: Filter) -> AnyPublisher<Never, Error> {
|
||||||
networkClient.request(FilterEndpoint.update(id: filter.id,
|
mastodonAPIClient.request(FilterEndpoint.update(id: filter.id,
|
||||||
phrase: filter.phrase,
|
phrase: filter.phrase,
|
||||||
context: filter.context,
|
context: filter.context,
|
||||||
irreversible: filter.irreversible,
|
irreversible: filter.irreversible,
|
||||||
|
@ -140,7 +141,7 @@ public extension IdentityService {
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteFilter(id: String) -> AnyPublisher<Never, Error> {
|
func deleteFilter(id: String) -> AnyPublisher<Never, Error> {
|
||||||
networkClient.request(DeletionEndpoint.filter(id: id))
|
mastodonAPIClient.request(DeletionEndpoint.filter(id: id))
|
||||||
.map { _ in id }
|
.map { _ in id }
|
||||||
.flatMap(contentDatabase.deleteFilter(id:))
|
.flatMap(contentDatabase.deleteFilter(id:))
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
@ -180,7 +181,7 @@ public extension IdentityService {
|
||||||
.appendingPathComponent(deviceToken)
|
.appendingPathComponent(deviceToken)
|
||||||
.appendingPathComponent(identityID.uuidString)
|
.appendingPathComponent(identityID.uuidString)
|
||||||
|
|
||||||
return networkClient.request(
|
return mastodonAPIClient.request(
|
||||||
PushSubscriptionEndpoint.create(
|
PushSubscriptionEndpoint.create(
|
||||||
endpoint: endpoint,
|
endpoint: endpoint,
|
||||||
publicKey: publicKey,
|
publicKey: publicKey,
|
||||||
|
@ -194,14 +195,14 @@ public extension IdentityService {
|
||||||
func updatePushSubscription(alerts: PushSubscription.Alerts) -> AnyPublisher<Never, Error> {
|
func updatePushSubscription(alerts: PushSubscription.Alerts) -> AnyPublisher<Never, Error> {
|
||||||
let identityID = identity.id
|
let identityID = identity.id
|
||||||
|
|
||||||
return networkClient.request(PushSubscriptionEndpoint.update(alerts: alerts))
|
return mastodonAPIClient.request(PushSubscriptionEndpoint.update(alerts: alerts))
|
||||||
.map { ($0.alerts, nil, identityID) }
|
.map { ($0.alerts, nil, identityID) }
|
||||||
.flatMap(identityDatabase.updatePushSubscription(alerts:deviceToken:forIdentityID:))
|
.flatMap(identityDatabase.updatePushSubscription(alerts:deviceToken:forIdentityID:))
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
func service(timeline: Timeline) -> StatusListService {
|
func service(timeline: Timeline) -> StatusListService {
|
||||||
StatusListService(timeline: timeline, networkClient: networkClient, contentDatabase: contentDatabase)
|
StatusListService(timeline: timeline, mastodonAPIClient: mastodonAPIClient, contentDatabase: contentDatabase)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import Combine
|
||||||
import DB
|
import DB
|
||||||
import Foundation
|
import Foundation
|
||||||
import Mastodon
|
import Mastodon
|
||||||
|
import MastodonAPI
|
||||||
|
|
||||||
public struct StatusListService {
|
public struct StatusListService {
|
||||||
public let statusSections: AnyPublisher<[[Status]], Error>
|
public let statusSections: AnyPublisher<[[Status]], Error>
|
||||||
|
@ -11,13 +12,13 @@ public struct StatusListService {
|
||||||
public let contextParentID: String?
|
public let contextParentID: String?
|
||||||
|
|
||||||
private let filterContext: Filter.Context
|
private let filterContext: Filter.Context
|
||||||
private let networkClient: APIClient
|
private let mastodonAPIClient: MastodonAPIClient
|
||||||
private let contentDatabase: ContentDatabase
|
private let contentDatabase: ContentDatabase
|
||||||
private let requestClosure: (_ maxID: String?, _ minID: String?) -> AnyPublisher<Never, Error>
|
private let requestClosure: (_ maxID: String?, _ minID: String?) -> AnyPublisher<Never, Error>
|
||||||
}
|
}
|
||||||
|
|
||||||
extension StatusListService {
|
extension StatusListService {
|
||||||
init(timeline: Timeline, networkClient: APIClient, contentDatabase: ContentDatabase) {
|
init(timeline: Timeline, mastodonAPIClient: MastodonAPIClient, contentDatabase: ContentDatabase) {
|
||||||
let filterContext: Filter.Context
|
let filterContext: Filter.Context
|
||||||
|
|
||||||
switch timeline {
|
switch timeline {
|
||||||
|
@ -31,9 +32,9 @@ extension StatusListService {
|
||||||
paginates: true,
|
paginates: true,
|
||||||
contextParentID: nil,
|
contextParentID: nil,
|
||||||
filterContext: filterContext,
|
filterContext: filterContext,
|
||||||
networkClient: networkClient,
|
mastodonAPIClient: mastodonAPIClient,
|
||||||
contentDatabase: contentDatabase) { maxID, minID in
|
contentDatabase: contentDatabase) { maxID, minID in
|
||||||
networkClient.request(Paged(timeline.endpoint, maxID: maxID, minID: minID))
|
mastodonAPIClient.request(Paged(timeline.endpoint, maxID: maxID, minID: minID))
|
||||||
.flatMap { contentDatabase.insert(statuses: $0, timeline: timeline) }
|
.flatMap { contentDatabase.insert(statuses: $0, timeline: timeline) }
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
@ -50,7 +51,7 @@ public extension StatusListService {
|
||||||
}
|
}
|
||||||
|
|
||||||
func statusService(status: Status) -> StatusService {
|
func statusService(status: Status) -> StatusService {
|
||||||
StatusService(status: status, networkClient: networkClient, contentDatabase: contentDatabase)
|
StatusService(status: status, networkClient: mastodonAPIClient, contentDatabase: contentDatabase)
|
||||||
}
|
}
|
||||||
|
|
||||||
func contextService(statusID: String) -> Self {
|
func contextService(statusID: String) -> Self {
|
||||||
|
@ -58,13 +59,13 @@ public extension StatusListService {
|
||||||
paginates: false,
|
paginates: false,
|
||||||
contextParentID: statusID,
|
contextParentID: statusID,
|
||||||
filterContext: .thread,
|
filterContext: .thread,
|
||||||
networkClient: networkClient,
|
mastodonAPIClient: mastodonAPIClient,
|
||||||
contentDatabase: contentDatabase) { _, _ in
|
contentDatabase: contentDatabase) { _, _ in
|
||||||
Publishers.Merge(
|
Publishers.Merge(
|
||||||
networkClient.request(StatusEndpoint.status(id: statusID))
|
mastodonAPIClient.request(StatusEndpoint.status(id: statusID))
|
||||||
.flatMap(contentDatabase.insert(status:))
|
.flatMap(contentDatabase.insert(status:))
|
||||||
.eraseToAnyPublisher(),
|
.eraseToAnyPublisher(),
|
||||||
networkClient.request(ContextEndpoint.context(id: statusID))
|
mastodonAPIClient.request(ContextEndpoint.context(id: statusID))
|
||||||
.flatMap { contentDatabase.insert(context: $0, parentID: statusID) }
|
.flatMap { contentDatabase.insert(context: $0, parentID: statusID) }
|
||||||
.eraseToAnyPublisher())
|
.eraseToAnyPublisher())
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
|
@ -4,22 +4,23 @@ import Foundation
|
||||||
import Combine
|
import Combine
|
||||||
import DB
|
import DB
|
||||||
import Mastodon
|
import Mastodon
|
||||||
|
import MastodonAPI
|
||||||
|
|
||||||
public struct StatusService {
|
public struct StatusService {
|
||||||
public let status: Status
|
public let status: Status
|
||||||
private let networkClient: APIClient
|
private let mastodonAPIClient: MastodonAPIClient
|
||||||
private let contentDatabase: ContentDatabase
|
private let contentDatabase: ContentDatabase
|
||||||
|
|
||||||
init(status: Status, networkClient: APIClient, contentDatabase: ContentDatabase) {
|
init(status: Status, networkClient: MastodonAPIClient, contentDatabase: ContentDatabase) {
|
||||||
self.status = status
|
self.status = status
|
||||||
self.networkClient = networkClient
|
self.mastodonAPIClient = networkClient
|
||||||
self.contentDatabase = contentDatabase
|
self.contentDatabase = contentDatabase
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension StatusService {
|
public extension StatusService {
|
||||||
func toggleFavorited() -> AnyPublisher<Never, Error> {
|
func toggleFavorited() -> AnyPublisher<Never, Error> {
|
||||||
networkClient.request(status.displayStatus.favourited
|
mastodonAPIClient.request(status.displayStatus.favourited
|
||||||
? StatusEndpoint.unfavourite(id: status.displayStatus.id)
|
? StatusEndpoint.unfavourite(id: status.displayStatus.id)
|
||||||
: StatusEndpoint.favourite(id: status.displayStatus.id))
|
: StatusEndpoint.favourite(id: status.displayStatus.id))
|
||||||
.flatMap(contentDatabase.insert(status:))
|
.flatMap(contentDatabase.insert(status:))
|
|
@ -4,12 +4,13 @@ import Foundation
|
||||||
import Combine
|
import Combine
|
||||||
import HTTP
|
import HTTP
|
||||||
import Mastodon
|
import Mastodon
|
||||||
import MastodonStubs
|
import MastodonAPI
|
||||||
|
import MastodonAPIStubs
|
||||||
import ServiceLayer
|
import ServiceLayer
|
||||||
import ServiceLayerMocks
|
import ServiceLayerMocks
|
||||||
import ViewModels
|
import ViewModels
|
||||||
|
|
||||||
private let decoder = APIDecoder()
|
private let decoder = MastodonDecoder()
|
||||||
private let devInstanceURL = URL(string: "https://mastodon.social")!
|
private let devInstanceURL = URL(string: "https://mastodon.social")!
|
||||||
|
|
||||||
// swiftlint:disable force_try
|
// swiftlint:disable force_try
|
||||||
|
|
Loading…
Reference in a new issue