mirror of
https://github.com/metabolist/metatext.git
synced 2024-11-21 15:50:59 +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 {
|
||||
static func databaseJSONDecoder(for column: String) -> JSONDecoder {
|
||||
APIDecoder()
|
||||
MastodonDecoder()
|
||||
}
|
||||
|
||||
static func databaseJSONEncoder(for column: String) -> JSONEncoder {
|
||||
APIEncoder()
|
||||
MastodonEncoder()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,10 +6,10 @@ import Mastodon
|
|||
|
||||
extension Account: FetchableRecord, PersistableRecord {
|
||||
public static func databaseJSONDecoder(for column: String) -> JSONDecoder {
|
||||
APIDecoder()
|
||||
MastodonDecoder()
|
||||
}
|
||||
|
||||
public static func databaseJSONEncoder(for column: String) -> JSONEncoder {
|
||||
APIEncoder()
|
||||
MastodonEncoder()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,10 +6,10 @@ import Mastodon
|
|||
|
||||
extension Filter: FetchableRecord, PersistableRecord {
|
||||
public static func databaseJSONDecoder(for column: String) -> JSONDecoder {
|
||||
APIDecoder()
|
||||
MastodonDecoder()
|
||||
}
|
||||
|
||||
public static func databaseJSONEncoder(for column: String) -> JSONEncoder {
|
||||
APIEncoder()
|
||||
MastodonEncoder()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,24 +11,13 @@ let package = Package(
|
|||
products: [
|
||||
.library(
|
||||
name: "Mastodon",
|
||||
targets: ["Mastodon"]),
|
||||
.library(
|
||||
name: "MastodonStubs",
|
||||
targets: ["MastodonStubs"])
|
||||
],
|
||||
dependencies: [
|
||||
.package(path: "HTTP")
|
||||
targets: ["Mastodon"])
|
||||
],
|
||||
dependencies: [],
|
||||
targets: [
|
||||
.target(
|
||||
name: "Mastodon",
|
||||
dependencies: ["HTTP"]),
|
||||
.target(
|
||||
name: "MastodonStubs",
|
||||
dependencies: ["Mastodon", .product(name: "Stubbing", package: "HTTP")],
|
||||
resources: [.process("Resources")]),
|
||||
.target(name: "Mastodon"),
|
||||
.testTarget(
|
||||
name: "MastodonTests",
|
||||
dependencies: ["MastodonStubs"])
|
||||
dependencies: ["Mastodon"])
|
||||
]
|
||||
)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
public final class APIDecoder: JSONDecoder {
|
||||
public final class MastodonDecoder: JSONDecoder {
|
||||
public override init() {
|
||||
super.init()
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
public final class APIEncoder: JSONEncoder {
|
||||
public final class MastodonEncoder: JSONEncoder {
|
||||
public override init() {
|
||||
super.init()
|
||||
|
|
@ -12,21 +12,6 @@ public enum Timeline: Hashable {
|
|||
|
||||
public extension Timeline {
|
||||
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 {
|
||||
|
|
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 HTTP
|
||||
import Mastodon
|
||||
|
||||
public enum AccessTokenEndpoint {
|
||||
case oauthToken(
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import Foundation
|
||||
import HTTP
|
||||
import Mastodon
|
||||
|
||||
public enum AccountEndpoint {
|
||||
case verifyCredentials
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import Foundation
|
||||
import HTTP
|
||||
import Mastodon
|
||||
|
||||
public enum AppAuthorizationEndpoint {
|
||||
case apps(clientName: String, redirectURI: String, scopes: String, website: URL?)
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import Foundation
|
||||
import HTTP
|
||||
import Mastodon
|
||||
|
||||
public enum ContextEndpoint {
|
||||
case context(id: String)
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import Foundation
|
||||
import HTTP
|
||||
import Mastodon
|
||||
|
||||
public enum DeletionEndpoint {
|
||||
case oauthRevoke(token: String, clientID: String, clientSecret: String)
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import Foundation
|
||||
import HTTP
|
||||
import Mastodon
|
||||
|
||||
public enum FilterEndpoint {
|
||||
case create(
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import Foundation
|
||||
import HTTP
|
||||
import Mastodon
|
||||
|
||||
public enum FiltersEndpoint {
|
||||
case filters
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import Foundation
|
||||
import HTTP
|
||||
import Mastodon
|
||||
|
||||
public enum InstanceEndpoint {
|
||||
case instance
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import Foundation
|
||||
import HTTP
|
||||
import Mastodon
|
||||
|
||||
public enum ListEndpoint {
|
||||
case create(title: String)
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import Foundation
|
||||
import HTTP
|
||||
import Mastodon
|
||||
|
||||
public enum ListsEndpoint {
|
||||
case lists
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import Foundation
|
||||
import HTTP
|
||||
import Mastodon
|
||||
|
||||
public struct Paged<T: Endpoint> {
|
||||
public let endpoint: T
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import Foundation
|
||||
import HTTP
|
||||
import Mastodon
|
||||
|
||||
public enum PreferencesEndpoint {
|
||||
case preferences
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import Foundation
|
||||
import HTTP
|
||||
import Mastodon
|
||||
|
||||
public enum PushSubscriptionEndpoint {
|
||||
case create(
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import Foundation
|
||||
import HTTP
|
||||
import Mastodon
|
||||
|
||||
public enum StatusEndpoint {
|
||||
case status(id: String)
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import Foundation
|
||||
import HTTP
|
||||
import Mastodon
|
||||
|
||||
public enum TimelinesEndpoint {
|
||||
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 Combine
|
||||
import HTTP
|
||||
import Mastodon
|
||||
|
||||
public final class APIClient: Client {
|
||||
public final class MastodonAPIClient: Client {
|
||||
public var instanceURL: URL?
|
||||
public var accessToken: String?
|
||||
|
||||
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> {
|
||||
|
@ -17,14 +18,14 @@ public final class APIClient: Client {
|
|||
}
|
||||
}
|
||||
|
||||
extension APIClient {
|
||||
extension MastodonAPIClient {
|
||||
public func request<E: Endpoint>(_ endpoint: E) -> AnyPublisher<E.ResultType, Error> {
|
||||
guard let instanceURL = instanceURL else {
|
||||
return Fail(error: URLError(.badURL)).eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
return super.request(
|
||||
APITarget(baseURL: instanceURL, endpoint: endpoint, accessToken: accessToken),
|
||||
MastodonAPITarget(baseURL: instanceURL, endpoint: endpoint, accessToken: accessToken),
|
||||
decodeErrorsAs: APIError.self)
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@
|
|||
import Foundation
|
||||
import HTTP
|
||||
|
||||
public struct APITarget<E: Endpoint> {
|
||||
public struct MastodonAPITarget<E: Endpoint> {
|
||||
public let baseURL: URL
|
||||
public let endpoint: E
|
||||
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 var pathComponents: [String] { endpoint.pathComponents }
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright © 2020 Metabolist. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import Mastodon
|
||||
import MastodonAPI
|
||||
import Stubbing
|
||||
|
||||
extension AccessTokenEndpoint: Stubbing {
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright © 2020 Metabolist. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import Mastodon
|
||||
import MastodonAPI
|
||||
import Stubbing
|
||||
|
||||
extension AccountEndpoint: Stubbing {
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright © 2020 Metabolist. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import Mastodon
|
||||
import MastodonAPI
|
||||
import Stubbing
|
||||
|
||||
extension AppAuthorizationEndpoint: Stubbing {
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright © 2020 Metabolist. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import Mastodon
|
||||
import MastodonAPI
|
||||
import Stubbing
|
||||
|
||||
extension ContextEndpoint: Stubbing {
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright © 2020 Metabolist. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import Mastodon
|
||||
import MastodonAPI
|
||||
import Stubbing
|
||||
|
||||
extension InstanceEndpoint: Stubbing {
|
|
@ -1,10 +1,10 @@
|
|||
// Copyright © 2020 Metabolist. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import Mastodon
|
||||
import MastodonAPI
|
||||
import Stubbing
|
||||
|
||||
extension APITarget: Stubbing {
|
||||
extension MastodonAPITarget: Stubbing {
|
||||
public func stub(url: URL) -> HTTPStub? {
|
||||
(endpoint as? Stubbing)?.stub(url: url)
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright © 2020 Metabolist. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import Mastodon
|
||||
import MastodonAPI
|
||||
import Stubbing
|
||||
|
||||
extension Paged: Stubbing where T: Stubbing {
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright © 2020 Metabolist. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import Mastodon
|
||||
import MastodonAPI
|
||||
import Stubbing
|
||||
|
||||
extension PreferencesEndpoint: Stubbing {
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright © 2020 Metabolist. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import Mastodon
|
||||
import MastodonAPI
|
||||
import 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>"; };
|
||||
D0BECB952501B3DD002C1B13 /* Keychain */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Keychain; 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>"; };
|
||||
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>"; };
|
||||
|
@ -188,6 +189,7 @@
|
|||
D0BECB952501B3DD002C1B13 /* Keychain */,
|
||||
D0C7D45624F76169001EBDBB /* Localizations */,
|
||||
D0E0F1E424FC49FC002C04BF /* Mastodon */,
|
||||
D0BECB9D2501CBC3002C1B13 /* MastodonAPI */,
|
||||
D0E5361A24E3EB4D00FB1CE1 /* Notification Service Extension */,
|
||||
D047FA8D24C3E21200AF17C5 /* Products */,
|
||||
D0BECB962501BCE0002C1B13 /* Secrets */,
|
||||
|
|
|
@ -24,7 +24,7 @@ class NotificationService: UNNotificationServiceExtension {
|
|||
do {
|
||||
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 {
|
||||
contentHandler(bestAttemptContent)
|
||||
|
||||
|
|
|
@ -20,18 +20,18 @@ let package = Package(
|
|||
.package(url: "https://github.com/groue/CombineExpectations.git", .upToNextMajor(from: "0.5.0")),
|
||||
.package(path: "DB"),
|
||||
.package(path: "Keychain"),
|
||||
.package(path: "Mastodon"),
|
||||
.package(path: "MastodonAPI"),
|
||||
.package(path: "Secrets")
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
name: "ServiceLayer",
|
||||
dependencies: ["DB", "Secrets"]),
|
||||
dependencies: ["DB", "MastodonAPI", "Secrets"]),
|
||||
.target(
|
||||
name: "ServiceLayerMocks",
|
||||
dependencies: [
|
||||
"ServiceLayer",
|
||||
.product(name: "MastodonStubs", package: "Mastodon"),
|
||||
.product(name: "MastodonAPIStubs", package: "MastodonAPI"),
|
||||
.product(name: "MockKeychain", package: "Keychain")]),
|
||||
.testTarget(
|
||||
name: "ServiceLayerTests",
|
||||
|
|
|
@ -4,6 +4,7 @@ import DB
|
|||
import Foundation
|
||||
import Combine
|
||||
import Mastodon
|
||||
import MastodonAPI
|
||||
import Secrets
|
||||
|
||||
public struct AllIdentitiesService {
|
||||
|
@ -53,9 +54,9 @@ public extension AllIdentitiesService {
|
|||
|
||||
func deleteIdentity(_ identity: Identity) -> AnyPublisher<Never, Error> {
|
||||
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)
|
||||
.collect()
|
||||
|
@ -65,7 +66,7 @@ public extension AllIdentitiesService {
|
|||
clientID: try secrets.item(.clientID),
|
||||
clientSecret: try secrets.item(.clientSecret))
|
||||
}
|
||||
.flatMap(networkClient.request)
|
||||
.flatMap(mastodonAPIClient.request)
|
||||
.collect()
|
||||
.tryMap { _ in
|
||||
try secrets.deleteAllItems()
|
|
@ -3,14 +3,15 @@
|
|||
import Foundation
|
||||
import Combine
|
||||
import Mastodon
|
||||
import MastodonAPI
|
||||
|
||||
public struct AuthenticationService {
|
||||
private let networkClient: APIClient
|
||||
private let mastodonAPIClient: MastodonAPIClient
|
||||
private let webAuthSessionType: WebAuthSession.Type
|
||||
private let webAuthSessionContextProvider = WebAuthSessionContextProvider()
|
||||
|
||||
public init(environment: AppEnvironment) {
|
||||
networkClient = APIClient(session: environment.session)
|
||||
mastodonAPIClient = MastodonAPIClient(session: environment.session)
|
||||
webAuthSessionType = environment.webAuthSessionType
|
||||
}
|
||||
}
|
||||
|
@ -22,9 +23,9 @@ public extension AuthenticationService {
|
|||
redirectURI: OAuth.callbackURL.absoluteString,
|
||||
scopes: OAuth.scopes,
|
||||
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> {
|
||||
|
@ -63,9 +64,9 @@ public extension AuthenticationService {
|
|||
grantType: OAuth.grantType,
|
||||
scopes: OAuth.scopes,
|
||||
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()
|
||||
}
|
|
@ -4,6 +4,7 @@ import DB
|
|||
import Foundation
|
||||
import Combine
|
||||
import Mastodon
|
||||
import MastodonAPI
|
||||
import Secrets
|
||||
|
||||
public class IdentityService {
|
||||
|
@ -13,7 +14,7 @@ public class IdentityService {
|
|||
private let identityDatabase: IdentityDatabase
|
||||
private let contentDatabase: ContentDatabase
|
||||
private let environment: AppEnvironment
|
||||
private let networkClient: APIClient
|
||||
private let mastodonAPIClient: MastodonAPIClient
|
||||
private let secrets: Secrets
|
||||
private let observationErrorsInput = PassthroughSubject<Error, Never>()
|
||||
|
||||
|
@ -37,9 +38,9 @@ public class IdentityService {
|
|||
secrets = Secrets(
|
||||
identityID: identityID,
|
||||
keychain: environment.keychain)
|
||||
networkClient = APIClient(session: environment.session)
|
||||
networkClient.instanceURL = identity.url
|
||||
networkClient.accessToken = try? secrets.item(.accessToken)
|
||||
mastodonAPIClient = MastodonAPIClient(session: environment.session)
|
||||
mastodonAPIClient.instanceURL = identity.url
|
||||
mastodonAPIClient.accessToken = try? secrets.item(.accessToken)
|
||||
|
||||
contentDatabase = try ContentDatabase(identityID: identityID, inMemory: environment.inMemoryContent)
|
||||
|
||||
|
@ -53,21 +54,21 @@ public class IdentityService {
|
|||
}
|
||||
|
||||
public extension IdentityService {
|
||||
var isAuthorized: Bool { networkClient.accessToken != nil }
|
||||
var isAuthorized: Bool { mastodonAPIClient.accessToken != nil }
|
||||
|
||||
func updateLastUse() -> AnyPublisher<Never, Error> {
|
||||
identityDatabase.updateLastUsedAt(identityID: identity.id)
|
||||
}
|
||||
|
||||
func verifyCredentials() -> AnyPublisher<Never, Error> {
|
||||
networkClient.request(AccountEndpoint.verifyCredentials)
|
||||
mastodonAPIClient.request(AccountEndpoint.verifyCredentials)
|
||||
.zip(Just(identity.id).first().setFailureType(to: Error.self))
|
||||
.flatMap(identityDatabase.updateAccount)
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func refreshServerPreferences() -> AnyPublisher<Never, Error> {
|
||||
networkClient.request(PreferencesEndpoint.preferences)
|
||||
mastodonAPIClient.request(PreferencesEndpoint.preferences)
|
||||
.zip(Just(self).first().setFailureType(to: Error.self))
|
||||
.map { ($1.identity.preferences.updated(from: $0), $1.identity.id) }
|
||||
.flatMap(identityDatabase.updatePreferences)
|
||||
|
@ -75,7 +76,7 @@ public extension IdentityService {
|
|||
}
|
||||
|
||||
func refreshInstance() -> AnyPublisher<Never, Error> {
|
||||
networkClient.request(InstanceEndpoint.instance)
|
||||
mastodonAPIClient.request(InstanceEndpoint.instance)
|
||||
.zip(Just(identity.id).first().setFailureType(to: Error.self))
|
||||
.flatMap(identityDatabase.updateInstance)
|
||||
.eraseToAnyPublisher()
|
||||
|
@ -90,19 +91,19 @@ public extension IdentityService {
|
|||
}
|
||||
|
||||
func refreshLists() -> AnyPublisher<Never, Error> {
|
||||
networkClient.request(ListsEndpoint.lists)
|
||||
mastodonAPIClient.request(ListsEndpoint.lists)
|
||||
.flatMap(contentDatabase.setLists(_:))
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func createList(title: String) -> AnyPublisher<Never, Error> {
|
||||
networkClient.request(ListEndpoint.create(title: title))
|
||||
mastodonAPIClient.request(ListEndpoint.create(title: title))
|
||||
.flatMap(contentDatabase.createList(_:))
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func deleteList(id: String) -> AnyPublisher<Never, Error> {
|
||||
networkClient.request(DeletionEndpoint.list(id: id))
|
||||
mastodonAPIClient.request(DeletionEndpoint.list(id: id))
|
||||
.map { _ in id }
|
||||
.flatMap(contentDatabase.deleteList(id:))
|
||||
.eraseToAnyPublisher()
|
||||
|
@ -113,13 +114,13 @@ public extension IdentityService {
|
|||
}
|
||||
|
||||
func refreshFilters() -> AnyPublisher<Never, Error> {
|
||||
networkClient.request(FiltersEndpoint.filters)
|
||||
mastodonAPIClient.request(FiltersEndpoint.filters)
|
||||
.flatMap(contentDatabase.setFilters(_:))
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func createFilter(_ filter: Filter) -> AnyPublisher<Never, Error> {
|
||||
networkClient.request(FilterEndpoint.create(phrase: filter.phrase,
|
||||
mastodonAPIClient.request(FilterEndpoint.create(phrase: filter.phrase,
|
||||
context: filter.context,
|
||||
irreversible: filter.irreversible,
|
||||
wholeWord: filter.wholeWord,
|
||||
|
@ -129,7 +130,7 @@ public extension IdentityService {
|
|||
}
|
||||
|
||||
func updateFilter(_ filter: Filter) -> AnyPublisher<Never, Error> {
|
||||
networkClient.request(FilterEndpoint.update(id: filter.id,
|
||||
mastodonAPIClient.request(FilterEndpoint.update(id: filter.id,
|
||||
phrase: filter.phrase,
|
||||
context: filter.context,
|
||||
irreversible: filter.irreversible,
|
||||
|
@ -140,7 +141,7 @@ public extension IdentityService {
|
|||
}
|
||||
|
||||
func deleteFilter(id: String) -> AnyPublisher<Never, Error> {
|
||||
networkClient.request(DeletionEndpoint.filter(id: id))
|
||||
mastodonAPIClient.request(DeletionEndpoint.filter(id: id))
|
||||
.map { _ in id }
|
||||
.flatMap(contentDatabase.deleteFilter(id:))
|
||||
.eraseToAnyPublisher()
|
||||
|
@ -180,7 +181,7 @@ public extension IdentityService {
|
|||
.appendingPathComponent(deviceToken)
|
||||
.appendingPathComponent(identityID.uuidString)
|
||||
|
||||
return networkClient.request(
|
||||
return mastodonAPIClient.request(
|
||||
PushSubscriptionEndpoint.create(
|
||||
endpoint: endpoint,
|
||||
publicKey: publicKey,
|
||||
|
@ -194,14 +195,14 @@ public extension IdentityService {
|
|||
func updatePushSubscription(alerts: PushSubscription.Alerts) -> AnyPublisher<Never, Error> {
|
||||
let identityID = identity.id
|
||||
|
||||
return networkClient.request(PushSubscriptionEndpoint.update(alerts: alerts))
|
||||
return mastodonAPIClient.request(PushSubscriptionEndpoint.update(alerts: alerts))
|
||||
.map { ($0.alerts, nil, identityID) }
|
||||
.flatMap(identityDatabase.updatePushSubscription(alerts:deviceToken:forIdentityID:))
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
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 Foundation
|
||||
import Mastodon
|
||||
import MastodonAPI
|
||||
|
||||
public struct StatusListService {
|
||||
public let statusSections: AnyPublisher<[[Status]], Error>
|
||||
|
@ -11,13 +12,13 @@ public struct StatusListService {
|
|||
public let contextParentID: String?
|
||||
|
||||
private let filterContext: Filter.Context
|
||||
private let networkClient: APIClient
|
||||
private let mastodonAPIClient: MastodonAPIClient
|
||||
private let contentDatabase: ContentDatabase
|
||||
private let requestClosure: (_ maxID: String?, _ minID: String?) -> AnyPublisher<Never, Error>
|
||||
}
|
||||
|
||||
extension StatusListService {
|
||||
init(timeline: Timeline, networkClient: APIClient, contentDatabase: ContentDatabase) {
|
||||
init(timeline: Timeline, mastodonAPIClient: MastodonAPIClient, contentDatabase: ContentDatabase) {
|
||||
let filterContext: Filter.Context
|
||||
|
||||
switch timeline {
|
||||
|
@ -31,9 +32,9 @@ extension StatusListService {
|
|||
paginates: true,
|
||||
contextParentID: nil,
|
||||
filterContext: filterContext,
|
||||
networkClient: networkClient,
|
||||
mastodonAPIClient: mastodonAPIClient,
|
||||
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) }
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
@ -50,7 +51,7 @@ public extension StatusListService {
|
|||
}
|
||||
|
||||
func statusService(status: Status) -> StatusService {
|
||||
StatusService(status: status, networkClient: networkClient, contentDatabase: contentDatabase)
|
||||
StatusService(status: status, networkClient: mastodonAPIClient, contentDatabase: contentDatabase)
|
||||
}
|
||||
|
||||
func contextService(statusID: String) -> Self {
|
||||
|
@ -58,13 +59,13 @@ public extension StatusListService {
|
|||
paginates: false,
|
||||
contextParentID: statusID,
|
||||
filterContext: .thread,
|
||||
networkClient: networkClient,
|
||||
mastodonAPIClient: mastodonAPIClient,
|
||||
contentDatabase: contentDatabase) { _, _ in
|
||||
Publishers.Merge(
|
||||
networkClient.request(StatusEndpoint.status(id: statusID))
|
||||
mastodonAPIClient.request(StatusEndpoint.status(id: statusID))
|
||||
.flatMap(contentDatabase.insert(status:))
|
||||
.eraseToAnyPublisher(),
|
||||
networkClient.request(ContextEndpoint.context(id: statusID))
|
||||
mastodonAPIClient.request(ContextEndpoint.context(id: statusID))
|
||||
.flatMap { contentDatabase.insert(context: $0, parentID: statusID) }
|
||||
.eraseToAnyPublisher())
|
||||
.eraseToAnyPublisher()
|
|
@ -4,22 +4,23 @@ import Foundation
|
|||
import Combine
|
||||
import DB
|
||||
import Mastodon
|
||||
import MastodonAPI
|
||||
|
||||
public struct StatusService {
|
||||
public let status: Status
|
||||
private let networkClient: APIClient
|
||||
private let mastodonAPIClient: MastodonAPIClient
|
||||
private let contentDatabase: ContentDatabase
|
||||
|
||||
init(status: Status, networkClient: APIClient, contentDatabase: ContentDatabase) {
|
||||
init(status: Status, networkClient: MastodonAPIClient, contentDatabase: ContentDatabase) {
|
||||
self.status = status
|
||||
self.networkClient = networkClient
|
||||
self.mastodonAPIClient = networkClient
|
||||
self.contentDatabase = contentDatabase
|
||||
}
|
||||
}
|
||||
|
||||
public extension StatusService {
|
||||
func toggleFavorited() -> AnyPublisher<Never, Error> {
|
||||
networkClient.request(status.displayStatus.favourited
|
||||
mastodonAPIClient.request(status.displayStatus.favourited
|
||||
? StatusEndpoint.unfavourite(id: status.displayStatus.id)
|
||||
: StatusEndpoint.favourite(id: status.displayStatus.id))
|
||||
.flatMap(contentDatabase.insert(status:))
|
|
@ -4,12 +4,13 @@ import Foundation
|
|||
import Combine
|
||||
import HTTP
|
||||
import Mastodon
|
||||
import MastodonStubs
|
||||
import MastodonAPI
|
||||
import MastodonAPIStubs
|
||||
import ServiceLayer
|
||||
import ServiceLayerMocks
|
||||
import ViewModels
|
||||
|
||||
private let decoder = APIDecoder()
|
||||
private let decoder = MastodonDecoder()
|
||||
private let devInstanceURL = URL(string: "https://mastodon.social")!
|
||||
|
||||
// swiftlint:disable force_try
|
||||
|
|
Loading…
Reference in a new issue