Begin completely replacing CoreDataStack entities by Mastodon.Entity
This commit is contained in:
parent
c80a590306
commit
ec5ace4601
@ -6,8 +6,6 @@
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import CoreDataStack
|
||||
import class CoreDataStack.Notification
|
||||
import MastodonCore
|
||||
import MastodonSDK
|
||||
import MastodonLocalization
|
||||
@ -15,7 +13,7 @@ import MastodonLocalization
|
||||
extension DataSourceFacade {
|
||||
static func responseToUserFollowAction(
|
||||
dependency: NeedsDependency & AuthContextProvider,
|
||||
user: ManagedObjectRecord<MastodonUser>
|
||||
user: Mastodon.Entity.Account
|
||||
) async throws {
|
||||
let selectionFeedbackGenerator = await UISelectionFeedbackGenerator()
|
||||
await selectionFeedbackGenerator.selectionChanged()
|
||||
@ -31,41 +29,33 @@ extension DataSourceFacade {
|
||||
extension DataSourceFacade {
|
||||
static func responseToUserFollowRequestAction(
|
||||
dependency: NeedsDependency & AuthContextProvider,
|
||||
notification: ManagedObjectRecord<Notification>,
|
||||
notification: Mastodon.Entity.Notification,
|
||||
query: Mastodon.API.Account.FollowRequestQuery
|
||||
) async throws {
|
||||
let selectionFeedbackGenerator = await UISelectionFeedbackGenerator()
|
||||
await selectionFeedbackGenerator.selectionChanged()
|
||||
|
||||
let managedObjectContext = dependency.context.managedObjectContext
|
||||
let _userID: MastodonUser.ID? = try await managedObjectContext.perform {
|
||||
guard let notification = notification.object(in: managedObjectContext) else { return nil }
|
||||
return notification.account.id
|
||||
}
|
||||
let userID = notification.account.id
|
||||
|
||||
// let state: MastodonFollowRequestState = try await managedObjectContext.perform {
|
||||
// guard let notification = notification.object(in: managedObjectContext) else { return .init(state: .none) }
|
||||
// return notification.followRequestState
|
||||
// }
|
||||
//
|
||||
// guard state.state == .none else {
|
||||
// return
|
||||
// }
|
||||
|
||||
guard let userID = _userID else {
|
||||
assertionFailure()
|
||||
throw APIService.APIError.implicit(.badRequest)
|
||||
}
|
||||
|
||||
let state: MastodonFollowRequestState = try await managedObjectContext.perform {
|
||||
guard let notification = notification.object(in: managedObjectContext) else { return .init(state: .none) }
|
||||
return notification.followRequestState
|
||||
}
|
||||
|
||||
guard state.state == .none else {
|
||||
return
|
||||
}
|
||||
|
||||
try? await managedObjectContext.performChanges {
|
||||
guard let notification = notification.object(in: managedObjectContext) else { return }
|
||||
switch query {
|
||||
case .accept:
|
||||
notification.transientFollowRequestState = .init(state: .isAccepting)
|
||||
case .reject:
|
||||
notification.transientFollowRequestState = .init(state: .isRejecting)
|
||||
}
|
||||
}
|
||||
// try? await managedObjectContext.performChanges {
|
||||
// guard let notification = notification.object(in: managedObjectContext) else { return }
|
||||
// switch query {
|
||||
// case .accept:
|
||||
// notification.transientFollowRequestState = .init(state: .isAccepting)
|
||||
// case .reject:
|
||||
// notification.transientFollowRequestState = .init(state: .isRejecting)
|
||||
// }
|
||||
// }
|
||||
|
||||
do {
|
||||
_ = try await dependency.context.apiService.followRequest(
|
||||
@ -75,22 +65,23 @@ extension DataSourceFacade {
|
||||
)
|
||||
} catch {
|
||||
// reset state when failure
|
||||
try? await managedObjectContext.performChanges {
|
||||
guard let notification = notification.object(in: managedObjectContext) else { return }
|
||||
notification.transientFollowRequestState = .init(state: .none)
|
||||
}
|
||||
// try? await managedObjectContext.performChanges {
|
||||
// guard let notification = notification.object(in: managedObjectContext) else { return }
|
||||
// notification.transientFollowRequestState = .init(state: .none)
|
||||
// }
|
||||
|
||||
if let error = error as? Mastodon.API.Error {
|
||||
switch error.httpResponseStatus {
|
||||
case .notFound:
|
||||
let backgroundManagedObjectContext = dependency.context.backgroundManagedObjectContext
|
||||
try await backgroundManagedObjectContext.performChanges {
|
||||
guard let notification = notification.object(in: backgroundManagedObjectContext) else { return }
|
||||
for feed in notification.feeds {
|
||||
backgroundManagedObjectContext.delete(feed)
|
||||
}
|
||||
backgroundManagedObjectContext.delete(notification)
|
||||
}
|
||||
break
|
||||
// let backgroundManagedObjectContext = dependency.context.backgroundManagedObjectContext
|
||||
// try await backgroundManagedObjectContext.performChanges {
|
||||
// guard let notification = notification.object(in: backgroundManagedObjectContext) else { return }
|
||||
// for feed in notification.feeds {
|
||||
// backgroundManagedObjectContext.delete(feed)
|
||||
// }
|
||||
// backgroundManagedObjectContext.delete(notification)
|
||||
// }
|
||||
default:
|
||||
let alertController = await UIAlertController(for: error, title: nil, preferredStyle: .alert)
|
||||
let okAction = await UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default)
|
||||
@ -106,38 +97,38 @@ extension DataSourceFacade {
|
||||
return
|
||||
}
|
||||
|
||||
try? await managedObjectContext.performChanges {
|
||||
guard let notification = notification.object(in: managedObjectContext) else { return }
|
||||
switch query {
|
||||
case .accept:
|
||||
notification.transientFollowRequestState = .init(state: .isAccept)
|
||||
case .reject:
|
||||
// do nothing due to will delete notification
|
||||
break
|
||||
}
|
||||
}
|
||||
// try? await managedObjectContext.performChanges {
|
||||
// guard let notification = notification.object(in: managedObjectContext) else { return }
|
||||
// switch query {
|
||||
// case .accept:
|
||||
// notification.transientFollowRequestState = .init(state: .isAccept)
|
||||
// case .reject:
|
||||
// // do nothing due to will delete notification
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
|
||||
let backgroundManagedObjectContext = dependency.context.backgroundManagedObjectContext
|
||||
try? await backgroundManagedObjectContext.performChanges {
|
||||
guard let notification = notification.object(in: backgroundManagedObjectContext) else { return }
|
||||
switch query {
|
||||
case .accept:
|
||||
notification.followRequestState = .init(state: .isAccept)
|
||||
case .reject:
|
||||
// delete notification
|
||||
for feed in notification.feeds {
|
||||
backgroundManagedObjectContext.delete(feed)
|
||||
}
|
||||
backgroundManagedObjectContext.delete(notification)
|
||||
}
|
||||
}
|
||||
// let backgroundManagedObjectContext = dependency.context.backgroundManagedObjectContext
|
||||
// try? await backgroundManagedObjectContext.performChanges {
|
||||
// guard let notification = notification.object(in: backgroundManagedObjectContext) else { return }
|
||||
// switch query {
|
||||
// case .accept:
|
||||
// notification.followRequestState = .init(state: .isAccept)
|
||||
// case .reject:
|
||||
// // delete notification
|
||||
// for feed in notification.feeds {
|
||||
// backgroundManagedObjectContext.delete(feed)
|
||||
// }
|
||||
// backgroundManagedObjectContext.delete(notification)
|
||||
// }
|
||||
// }
|
||||
} // end func
|
||||
}
|
||||
|
||||
extension DataSourceFacade {
|
||||
static func responseToShowHideReblogAction(
|
||||
dependency: NeedsDependency & AuthContextProvider,
|
||||
user: ManagedObjectRecord<MastodonUser>
|
||||
user: Mastodon.Entity.Account
|
||||
) async throws {
|
||||
_ = try await dependency.context.apiService.toggleShowReblogs(
|
||||
for: user,
|
||||
|
@ -24,7 +24,7 @@ extension DataSourceFacade {
|
||||
let managedObjectContext = provider.context.backgroundManagedObjectContext
|
||||
|
||||
try? await managedObjectContext.performChanges {
|
||||
guard let me = authenticationBox.authentication.user(in: managedObjectContext) else { return }
|
||||
guard let me = authenticationBox.authentication.user else { return }
|
||||
guard let user = record.object(in: managedObjectContext) else { return }
|
||||
_ = Persistence.SearchHistory.createOrMerge(
|
||||
in: managedObjectContext,
|
||||
@ -42,7 +42,7 @@ extension DataSourceFacade {
|
||||
switch tag {
|
||||
case .entity(let entity):
|
||||
try? await managedObjectContext.performChanges {
|
||||
guard let me = authenticationBox.authentication.user(in: managedObjectContext) else { return }
|
||||
guard let me = authenticationBox.authentication.user else { return }
|
||||
|
||||
let now = Date()
|
||||
|
||||
@ -68,7 +68,7 @@ extension DataSourceFacade {
|
||||
case .record(let record):
|
||||
try? await managedObjectContext.performChanges {
|
||||
let authenticationBox = provider.authContext.mastodonAuthenticationBox
|
||||
guard let me = authenticationBox.authentication.user(in: managedObjectContext) else { return }
|
||||
guard let me = authenticationBox.authentication.user else { return }
|
||||
guard let tag = record.object(in: managedObjectContext) else { return }
|
||||
|
||||
let now = Date()
|
||||
@ -99,7 +99,7 @@ extension DataSourceFacade {
|
||||
let managedObjectContext = provider.context.backgroundManagedObjectContext
|
||||
|
||||
try await managedObjectContext.performChanges {
|
||||
guard let _ = authenticationBox.authentication.user(in: managedObjectContext) else { return }
|
||||
guard let _ = authenticationBox.authentication.user else { return }
|
||||
let request = SearchHistory.sortedFetchRequest
|
||||
request.predicate = SearchHistory.predicate(
|
||||
domain: authenticationBox.domain,
|
||||
|
@ -212,7 +212,7 @@ extension HomeTimelineViewController {
|
||||
let userDoesntFollowPeople: Bool
|
||||
if let managedObjectContext = self?.context.managedObjectContext,
|
||||
let authContext = self?.authContext,
|
||||
let me = authContext.mastodonAuthenticationBox.authentication.user(in: managedObjectContext){
|
||||
let me = authContext.mastodonAuthenticationBox.authentication.user{
|
||||
userDoesntFollowPeople = me.followersCount == 0
|
||||
} else {
|
||||
userDoesntFollowPeople = true
|
||||
|
@ -79,7 +79,7 @@ extension SearchResultOverviewCoordinator: SearchResultsOverviewTableViewControl
|
||||
return Persistence.Status.fetch(in: managedObjectContext, context: Persistence.Status.PersistContext(
|
||||
domain: authContext.mastodonAuthenticationBox.domain,
|
||||
entity: status,
|
||||
me: authContext.mastodonAuthenticationBox.authentication.user(in: managedObjectContext),
|
||||
me: authContext.mastodonAuthenticationBox.authentication.user,
|
||||
statusCache: nil,
|
||||
userCache: nil,
|
||||
networkDate: Date()))
|
||||
|
@ -29,7 +29,6 @@ final class SendPostIntentHandler: NSObject {
|
||||
|
||||
// MARK: - SendPostIntentHandling
|
||||
extension SendPostIntentHandler: SendPostIntentHandling {
|
||||
|
||||
func handle(intent: SendPostIntent) async -> SendPostIntentResponse {
|
||||
guard let content = intent.content else {
|
||||
return SendPostIntentResponse(code: .failure, userActivity: nil)
|
||||
@ -59,7 +58,7 @@ extension SendPostIntentHandler: SendPostIntentHandling {
|
||||
}
|
||||
mastodonAuthentications = [authentication]
|
||||
} else {
|
||||
mastodonAuthentications = try accounts.mastodonAuthentication(in: managedObjectContext)
|
||||
mastodonAuthentications = AuthenticationServiceProvider.shared.authentications.sorted(by: { $0.activedAt > $1.activedAt })
|
||||
}
|
||||
|
||||
let authenticationBoxes = mastodonAuthentications.map { authentication in
|
||||
|
@ -19,17 +19,15 @@ extension Account {
|
||||
let accounts: [Account] = try await managedObjectContext.perform {
|
||||
let results = AuthenticationServiceProvider.shared.authentications
|
||||
let accounts = results.compactMap { mastodonAuthentication -> Account? in
|
||||
guard let user = mastodonAuthentication.user(in: managedObjectContext) else {
|
||||
return nil
|
||||
}
|
||||
let user = mastodonAuthentication.user
|
||||
let account = Account(
|
||||
identifier: mastodonAuthentication.identifier.uuidString,
|
||||
display: user.displayNameWithFallback,
|
||||
subtitle: user.acctWithDomain,
|
||||
subtitle: user.acct, // TODO: CD check
|
||||
image: user.avatarImageURL().flatMap { INImage(url: $0) }
|
||||
)
|
||||
account.name = user.displayNameWithFallback
|
||||
account.username = user.acctWithDomain
|
||||
account.username = user.acctWithDomainIfMissing(mastodonAuthentication.domain)
|
||||
return account
|
||||
}
|
||||
return accounts
|
||||
|
@ -6,13 +6,12 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreDataStack
|
||||
import MastodonSDK
|
||||
|
||||
public struct MastodonAuthenticationBox: UserIdentifier {
|
||||
public let authentication: MastodonAuthentication
|
||||
public let domain: String
|
||||
public let userID: MastodonUser.ID
|
||||
public let userID: Mastodon.Entity.Account.ID
|
||||
public let appAuthorization: Mastodon.API.OAuth.Authorization
|
||||
public let userAuthorization: Mastodon.API.OAuth.Authorization
|
||||
public let inMemoryCache: MastodonAccountInMemoryCache
|
||||
@ -20,7 +19,7 @@ public struct MastodonAuthenticationBox: UserIdentifier {
|
||||
public init(
|
||||
authentication: MastodonAuthentication,
|
||||
domain: String,
|
||||
userID: MastodonUser.ID,
|
||||
userID: Mastodon.Entity.Account.ID,
|
||||
appAuthorization: Mastodon.API.OAuth.Authorization,
|
||||
userAuthorization: Mastodon.API.OAuth.Authorization,
|
||||
inMemoryCache: MastodonAccountInMemoryCache
|
||||
|
@ -2,11 +2,11 @@
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
import CoreDataStack
|
||||
import MastodonSDK
|
||||
import KeychainAccess
|
||||
import MastodonCommon
|
||||
import os.log
|
||||
import CoreDataStack
|
||||
|
||||
public class AuthenticationServiceProvider: ObservableObject {
|
||||
private let logger = Logger(subsystem: "AuthenticationServiceProvider", category: "Authentication")
|
||||
@ -23,15 +23,22 @@ public class AuthenticationServiceProvider: ObservableObject {
|
||||
}
|
||||
}
|
||||
|
||||
func update(instance: Instance, where domain: String) {
|
||||
func update(instance: Mastodon.Entity.Instance, where domain: String) {
|
||||
authentications = authentications.map { authentication in
|
||||
guard authentication.domain == domain else { return authentication }
|
||||
return authentication.updating(instance: instance)
|
||||
}
|
||||
}
|
||||
|
||||
func update(instanceV2: Mastodon.Entity.V2.Instance, where domain: String) {
|
||||
authentications = authentications.map { authentication in
|
||||
guard authentication.domain == domain else { return authentication }
|
||||
return authentication.updating(instanceV2: instanceV2)
|
||||
}
|
||||
}
|
||||
|
||||
func delete(authentication: MastodonAuthentication) {
|
||||
authentications.removeAll(where: { $0 == authentication })
|
||||
authentications.removeAll(where: { $0.identifier == authentication.identifier })
|
||||
}
|
||||
|
||||
func activateAuthentication(in domain: String, for userID: String) {
|
||||
@ -69,33 +76,47 @@ public extension AuthenticationServiceProvider {
|
||||
}
|
||||
|
||||
func migrateLegacyAuthentications(in context: NSManagedObjectContext) {
|
||||
do {
|
||||
let legacyAuthentications = try context.fetch(MastodonAuthenticationLegacy.sortedFetchRequest)
|
||||
let migratedAuthentications = legacyAuthentications.compactMap { auth -> MastodonAuthentication? in
|
||||
return MastodonAuthentication(
|
||||
identifier: auth.identifier,
|
||||
domain: auth.domain,
|
||||
username: auth.username,
|
||||
appAccessToken: auth.appAccessToken,
|
||||
userAccessToken: auth.userAccessToken,
|
||||
clientID: auth.clientID,
|
||||
clientSecret: auth.clientSecret,
|
||||
createdAt: auth.createdAt,
|
||||
updatedAt: auth.updatedAt,
|
||||
activedAt: auth.activedAt,
|
||||
userID: auth.userID
|
||||
)
|
||||
}
|
||||
Task {
|
||||
do {
|
||||
let legacyAuthentications = try context.fetch(MastodonAuthenticationLegacy.sortedFetchRequest)
|
||||
|
||||
var migratedAuthentications = [MastodonAuthentication]()
|
||||
|
||||
for auth in legacyAuthentications {
|
||||
let user = try await Mastodon.API.Account.accountInfo(
|
||||
session: URLSession.shared,
|
||||
domain: auth.domain,
|
||||
userID: auth.userID,
|
||||
authorization: .init(accessToken: auth.userAccessToken)
|
||||
).singleOutput().value
|
||||
|
||||
let newAuth = MastodonAuthentication(
|
||||
user: user,
|
||||
identifier: auth.identifier,
|
||||
domain: auth.domain,
|
||||
username: auth.username,
|
||||
appAccessToken: auth.appAccessToken,
|
||||
userAccessToken: auth.userAccessToken,
|
||||
clientID: auth.clientID,
|
||||
clientSecret: auth.clientSecret,
|
||||
createdAt: auth.createdAt,
|
||||
updatedAt: auth.updatedAt,
|
||||
activedAt: auth.activedAt,
|
||||
userID: auth.userID
|
||||
)
|
||||
migratedAuthentications.append(newAuth)
|
||||
}
|
||||
|
||||
if migratedAuthentications.count != legacyAuthentications.count {
|
||||
logger.log(level: .default, "Not all account authentications could be migrated.")
|
||||
}
|
||||
if migratedAuthentications.count != legacyAuthentications.count {
|
||||
logger.log(level: .default, "Not all account authentications could be migrated.")
|
||||
}
|
||||
|
||||
self.authentications = migratedAuthentications
|
||||
userDefaults.didMigrateAuthentications = true
|
||||
} catch {
|
||||
userDefaults.didMigrateAuthentications = false
|
||||
logger.log(level: .error, "Could not migrate legacy authentications")
|
||||
self.authentications = migratedAuthentications
|
||||
userDefaults.didMigrateAuthentications = true
|
||||
} catch {
|
||||
userDefaults.didMigrateAuthentications = false
|
||||
logger.log(level: .error, "Could not migrate legacy authentications")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,57 +6,8 @@
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import CoreDataStack
|
||||
import MastodonSDK
|
||||
|
||||
extension Instance {
|
||||
public var configuration: Mastodon.Entity.Instance.Configuration? {
|
||||
guard let configurationRaw = configurationRaw else { return nil }
|
||||
guard let configuration = try? JSONDecoder().decode(Mastodon.Entity.Instance.Configuration.self, from: configurationRaw) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return configuration
|
||||
}
|
||||
|
||||
static func encode(configuration: Mastodon.Entity.Instance.Configuration) -> Data? {
|
||||
return try? JSONEncoder().encode(configuration)
|
||||
}
|
||||
}
|
||||
|
||||
extension Instance {
|
||||
public var configurationV2: Mastodon.Entity.V2.Instance.Configuration? {
|
||||
guard
|
||||
let configurationRaw = configurationV2Raw,
|
||||
let configuration = try? JSONDecoder().decode(
|
||||
Mastodon.Entity.V2.Instance.Configuration.self,
|
||||
from: configurationRaw
|
||||
)
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return configuration
|
||||
}
|
||||
|
||||
static func encodeV2(configuration: Mastodon.Entity.V2.Instance.Configuration) -> Data? {
|
||||
return try? JSONEncoder().encode(configuration)
|
||||
}
|
||||
}
|
||||
|
||||
extension Instance {
|
||||
public var canFollowTags: Bool {
|
||||
version?.majorServerVersion(greaterThanOrEquals: 4) ?? false // following Tags is support beginning with Mastodon v4.0.0
|
||||
}
|
||||
|
||||
var isTranslationEnabled: Bool {
|
||||
if let configuration = configurationV2 {
|
||||
return configuration.translation?.enabled == true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
public func majorServerVersion(greaterThanOrEquals comparedVersion: Int) -> Bool {
|
||||
guard
|
||||
|
@ -1,12 +1,13 @@
|
||||
// Copyright © 2023 Mastodon gGmbH. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import CoreDataStack
|
||||
import MastodonSDK
|
||||
|
||||
public struct MastodonAuthentication: Codable, Hashable {
|
||||
public struct MastodonAuthentication: Codable {
|
||||
public typealias ID = UUID
|
||||
|
||||
public private(set) var user: Mastodon.Entity.Account
|
||||
|
||||
public private(set) var identifier: ID
|
||||
public private(set) var domain: String
|
||||
public private(set) var username: String
|
||||
@ -21,13 +22,16 @@ public struct MastodonAuthentication: Codable, Hashable {
|
||||
public private(set) var activedAt: Date
|
||||
|
||||
public private(set) var userID: String
|
||||
public private(set) var instanceObjectIdURI: URL?
|
||||
|
||||
public private(set) var instance: Mastodon.Entity.Instance?
|
||||
public private(set) var instanceV2: Mastodon.Entity.V2.Instance?
|
||||
|
||||
internal var persistenceIdentifier: String {
|
||||
"\(username)@\(domain)"
|
||||
}
|
||||
|
||||
public static func createFrom(
|
||||
user: Mastodon.Entity.Account,
|
||||
domain: String,
|
||||
userID: String,
|
||||
username: String,
|
||||
@ -38,6 +42,7 @@ public struct MastodonAuthentication: Codable, Hashable {
|
||||
) -> Self {
|
||||
let now = Date()
|
||||
return MastodonAuthentication(
|
||||
user: user,
|
||||
identifier: .init(),
|
||||
domain: domain,
|
||||
username: username,
|
||||
@ -49,11 +54,13 @@ public struct MastodonAuthentication: Codable, Hashable {
|
||||
updatedAt: now,
|
||||
activedAt: now,
|
||||
userID: userID,
|
||||
instanceObjectIdURI: nil
|
||||
instance: nil,
|
||||
instanceV2: nil
|
||||
)
|
||||
}
|
||||
|
||||
func copy(
|
||||
user: Mastodon.Entity.Account? = nil,
|
||||
identifier: ID? = nil,
|
||||
domain: String? = nil,
|
||||
username: String? = nil,
|
||||
@ -65,9 +72,11 @@ public struct MastodonAuthentication: Codable, Hashable {
|
||||
updatedAt: Date? = nil,
|
||||
activedAt: Date? = nil,
|
||||
userID: String? = nil,
|
||||
instanceObjectIdURI: URL? = nil
|
||||
instance: Mastodon.Entity.Instance? = nil,
|
||||
instanceV2: Mastodon.Entity.V2.Instance? = nil
|
||||
) -> Self {
|
||||
MastodonAuthentication(
|
||||
user: user ?? self.user,
|
||||
identifier: identifier ?? self.identifier,
|
||||
domain: domain ?? self.domain,
|
||||
username: username ?? self.username,
|
||||
@ -79,28 +88,19 @@ public struct MastodonAuthentication: Codable, Hashable {
|
||||
updatedAt: updatedAt ?? self.updatedAt,
|
||||
activedAt: activedAt ?? self.activedAt,
|
||||
userID: userID ?? self.userID,
|
||||
instanceObjectIdURI: instanceObjectIdURI ?? self.instanceObjectIdURI
|
||||
instance: instance ?? self.instance,
|
||||
instanceV2: instanceV2 ?? self.instanceV2
|
||||
)
|
||||
}
|
||||
|
||||
public func instance(in context: NSManagedObjectContext) -> Instance? {
|
||||
guard
|
||||
let instanceObjectIdURI = instanceObjectIdURI,
|
||||
let objectID = context.persistentStoreCoordinator?.managedObjectID(forURIRepresentation: instanceObjectIdURI)
|
||||
else { return nil }
|
||||
|
||||
return try? context.existingObject(with: objectID) as? Instance
|
||||
func updating(instance: Mastodon.Entity.Instance) -> Self {
|
||||
copy(instance: instance)
|
||||
}
|
||||
|
||||
public func user(in context: NSManagedObjectContext) -> MastodonUser? {
|
||||
let userPredicate = MastodonUser.predicate(domain: domain, id: userID)
|
||||
return MastodonUser.findOrFetch(in: context, matching: userPredicate)
|
||||
func updating(instanceV2: Mastodon.Entity.V2.Instance) -> Self {
|
||||
copy(instanceV2: instanceV2)
|
||||
}
|
||||
|
||||
func updating(instance: Instance) -> Self {
|
||||
copy(instanceObjectIdURI: instance.objectID.uriRepresentation())
|
||||
}
|
||||
|
||||
|
||||
func updating(activatedAt: Date) -> Self {
|
||||
copy(activedAt: activatedAt)
|
||||
}
|
||||
|
@ -164,24 +164,7 @@ extension APIService {
|
||||
query: query,
|
||||
authorization: authorization
|
||||
).singleOutput()
|
||||
|
||||
let managedObjectContext = self.backgroundManagedObjectContext
|
||||
try await managedObjectContext.performChanges {
|
||||
let me = authenticationBox.authentication.user(in: managedObjectContext)
|
||||
|
||||
for entity in response.value {
|
||||
_ = Persistence.Tag.createOrMerge(
|
||||
in: managedObjectContext,
|
||||
context: Persistence.Tag.PersistContext(
|
||||
domain: domain,
|
||||
entity: entity,
|
||||
me: me,
|
||||
networkDate: response.networkDate
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return response
|
||||
} // end func
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ extension APIService {
|
||||
let targetUserID: MastodonUser.ID
|
||||
let targetUsername: String
|
||||
let isBlocking: Bool
|
||||
let isFollowing: Bool
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
@ -61,39 +60,25 @@ extension APIService {
|
||||
}
|
||||
|
||||
public func toggleBlock(
|
||||
user: ManagedObjectRecord<MastodonUser>,
|
||||
user: Mastodon.Entity.Account,
|
||||
authenticationBox: MastodonAuthenticationBox
|
||||
) async throws -> Mastodon.Response.Content<Mastodon.Entity.Relationship> {
|
||||
let me = authenticationBox.authentication.user
|
||||
|
||||
let managedObjectContext = backgroundManagedObjectContext
|
||||
let blockContext: MastodonBlockContext = try await managedObjectContext.performChanges {
|
||||
let authentication = authenticationBox.authentication
|
||||
|
||||
guard
|
||||
let user = user.object(in: managedObjectContext),
|
||||
let me = authentication.user(in: managedObjectContext)
|
||||
else {
|
||||
throw APIError.implicit(.badRequest)
|
||||
}
|
||||
let blockedUsers = try await Mastodon.API.Account.blocks(
|
||||
session: session,
|
||||
domain: authenticationBox.domain,
|
||||
sinceID: nil,
|
||||
limit: nil,
|
||||
authorization: authenticationBox.userAuthorization
|
||||
).singleOutput()
|
||||
|
||||
let isBlocking = user.blockingBy.contains(me)
|
||||
let isFollowing = user.followingBy.contains(me)
|
||||
// toggle block state
|
||||
user.update(isBlocking: !isBlocking, by: me)
|
||||
// update follow state implicitly
|
||||
if !isBlocking {
|
||||
// will do block action. set to unfollow
|
||||
user.update(isFollowing: false, by: me)
|
||||
}
|
||||
|
||||
return MastodonBlockContext(
|
||||
sourceUserID: me.id,
|
||||
targetUserID: user.id,
|
||||
targetUsername: user.username,
|
||||
isBlocking: isBlocking,
|
||||
isFollowing: isFollowing
|
||||
)
|
||||
}
|
||||
let blockContext = MastodonBlockContext(
|
||||
sourceUserID: me.id,
|
||||
targetUserID: user.id,
|
||||
targetUsername: user.username,
|
||||
isBlocking: blockedUsers.value.contains(user)
|
||||
)
|
||||
|
||||
let result: Result<Mastodon.Response.Content<Mastodon.Entity.Relationship>, Error>
|
||||
do {
|
||||
@ -117,34 +102,7 @@ extension APIService {
|
||||
} catch {
|
||||
result = .failure(error)
|
||||
}
|
||||
|
||||
try await managedObjectContext.performChanges {
|
||||
let authentication = authenticationBox.authentication
|
||||
|
||||
guard
|
||||
let user = user.object(in: managedObjectContext),
|
||||
let me = authentication.user(in: managedObjectContext)
|
||||
else { return }
|
||||
|
||||
|
||||
switch result {
|
||||
case .success(let response):
|
||||
let relationship = response.value
|
||||
Persistence.MastodonUser.update(
|
||||
mastodonUser: user,
|
||||
context: Persistence.MastodonUser.RelationshipContext(
|
||||
entity: relationship,
|
||||
me: me,
|
||||
networkDate: response.networkDate
|
||||
)
|
||||
)
|
||||
case .failure:
|
||||
// rollback
|
||||
user.update(isBlocking: blockContext.isBlocking, by: me)
|
||||
user.update(isFollowing: blockContext.isFollowing, by: me)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let response = try result.get()
|
||||
return response
|
||||
}
|
||||
|
@ -19,32 +19,23 @@ extension APIService {
|
||||
}
|
||||
|
||||
public func bookmark(
|
||||
record: ManagedObjectRecord<Status>,
|
||||
status: Mastodon.Entity.Status,
|
||||
authenticationBox: MastodonAuthenticationBox
|
||||
) async throws -> Mastodon.Response.Content<Mastodon.Entity.Status> {
|
||||
|
||||
let managedObjectContext = backgroundManagedObjectContext
|
||||
let authentication = authenticationBox.authentication
|
||||
let me = authentication.user
|
||||
|
||||
// update bookmark state and retrieve bookmark context
|
||||
let bookmarkContext: MastodonBookmarkContext = try await managedObjectContext.performChanges {
|
||||
let authentication = authenticationBox.authentication
|
||||
|
||||
guard
|
||||
let _status = record.object(in: managedObjectContext),
|
||||
let me = authentication.user(in: managedObjectContext)
|
||||
else {
|
||||
throw APIError.implicit(.badRequest)
|
||||
}
|
||||
let isBookmarked = try await Mastodon.API.Bookmarks.bookmarkedStatus(
|
||||
domain: authenticationBox.domain,
|
||||
session: session,
|
||||
authorization: authenticationBox.userAuthorization,
|
||||
query: .init()
|
||||
).singleOutput().value.contains(where: { $0.id == status.id }) // TODO: CD is this sufficient? Do we need to check the domain as well?
|
||||
|
||||
let status = _status.reblog ?? _status
|
||||
let isBookmarked = status.bookmarkedBy.contains(me)
|
||||
status.update(bookmarked: !isBookmarked, by: me)
|
||||
let context = MastodonBookmarkContext(
|
||||
statusID: status.id,
|
||||
isBookmarked: isBookmarked
|
||||
)
|
||||
return context
|
||||
}
|
||||
let bookmarkContext = MastodonBookmarkContext(
|
||||
statusID: status.id,
|
||||
isBookmarked: isBookmarked
|
||||
)
|
||||
|
||||
// request bookmark or undo bookmark
|
||||
let result: Result<Mastodon.Response.Content<Mastodon.Entity.Status>, Error>
|
||||
@ -60,37 +51,7 @@ extension APIService {
|
||||
} catch {
|
||||
result = .failure(error)
|
||||
}
|
||||
|
||||
// update bookmark state
|
||||
try await managedObjectContext.performChanges {
|
||||
let authentication = authenticationBox.authentication
|
||||
|
||||
guard
|
||||
let _status = record.object(in: managedObjectContext),
|
||||
let me = authentication.user(in: managedObjectContext)
|
||||
else { return }
|
||||
|
||||
let status = _status.reblog ?? _status
|
||||
|
||||
switch result {
|
||||
case .success(let response):
|
||||
_ = Persistence.Status.createOrMerge(
|
||||
in: managedObjectContext,
|
||||
context: Persistence.Status.PersistContext(
|
||||
domain: authenticationBox.domain,
|
||||
entity: response.value,
|
||||
me: me,
|
||||
statusCache: nil,
|
||||
userCache: nil,
|
||||
networkDate: response.networkDate
|
||||
)
|
||||
)
|
||||
case .failure:
|
||||
// rollback
|
||||
status.update(bookmarked: bookmarkContext.isBookmarked, by: me)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let response = try result.get()
|
||||
return response
|
||||
}
|
||||
@ -111,35 +72,7 @@ extension APIService {
|
||||
authorization: authenticationBox.userAuthorization,
|
||||
query: query
|
||||
).singleOutput()
|
||||
|
||||
let managedObjectContext = self.backgroundManagedObjectContext
|
||||
try await managedObjectContext.performChanges {
|
||||
|
||||
guard
|
||||
let me = authenticationBox.authentication.user(in: managedObjectContext)
|
||||
else {
|
||||
assertionFailure()
|
||||
return
|
||||
}
|
||||
|
||||
for entity in response.value {
|
||||
let result = Persistence.Status.createOrMerge(
|
||||
in: managedObjectContext,
|
||||
context: Persistence.Status.PersistContext(
|
||||
domain: authenticationBox.domain,
|
||||
entity: entity,
|
||||
me: me,
|
||||
statusCache: nil,
|
||||
userCache: nil,
|
||||
networkDate: response.networkDate
|
||||
)
|
||||
)
|
||||
|
||||
result.status.update(bookmarked: true, by: me)
|
||||
result.status.reblog?.update(bookmarked: true, by: me)
|
||||
} // end for … in
|
||||
}
|
||||
|
||||
|
||||
return response
|
||||
} // end func
|
||||
}
|
||||
|
@ -16,40 +16,24 @@ extension APIService {
|
||||
private struct MastodonFavoriteContext {
|
||||
let statusID: Status.ID
|
||||
let isFavorited: Bool
|
||||
let favoritedCount: Int64
|
||||
let favoritedCount: Int
|
||||
}
|
||||
|
||||
public func favorite(
|
||||
record: ManagedObjectRecord<Status>,
|
||||
status: Mastodon.Entity.Status,
|
||||
authenticationBox: MastodonAuthenticationBox
|
||||
) async throws -> Mastodon.Response.Content<Mastodon.Entity.Status> {
|
||||
let authentication = authenticationBox.authentication
|
||||
let me = authentication.user
|
||||
|
||||
let managedObjectContext = backgroundManagedObjectContext
|
||||
|
||||
// update like state and retrieve like context
|
||||
let favoriteContext: MastodonFavoriteContext = try await managedObjectContext.performChanges {
|
||||
let authentication = authenticationBox.authentication
|
||||
|
||||
guard
|
||||
let _status = record.object(in: managedObjectContext),
|
||||
let me = authentication.user(in: managedObjectContext)
|
||||
else {
|
||||
throw APIError.implicit(.badRequest)
|
||||
}
|
||||
|
||||
let status = _status.reblog ?? _status
|
||||
let isFavorited = status.favouritedBy.contains(me)
|
||||
let favoritedCount = status.favouritesCount
|
||||
let favoriteCount = isFavorited ? favoritedCount - 1 : favoritedCount + 1
|
||||
status.update(liked: !isFavorited, by: me)
|
||||
status.update(favouritesCount: favoriteCount)
|
||||
let context = MastodonFavoriteContext(
|
||||
statusID: status.id,
|
||||
isFavorited: isFavorited,
|
||||
favoritedCount: favoritedCount
|
||||
)
|
||||
return context
|
||||
}
|
||||
let _status = status.reblog ?? status
|
||||
let isFavorited = status.favourited ?? false
|
||||
let favoritedCount = status.favouritesCount
|
||||
let favoriteContext = MastodonFavoriteContext(
|
||||
statusID: status.id,
|
||||
isFavorited: isFavorited,
|
||||
favoritedCount: favoritedCount
|
||||
)
|
||||
|
||||
// request like or undo like
|
||||
let result: Result<Mastodon.Response.Content<Mastodon.Entity.Status>, Error>
|
||||
@ -66,40 +50,6 @@ extension APIService {
|
||||
result = .failure(error)
|
||||
}
|
||||
|
||||
// update like state
|
||||
try await managedObjectContext.performChanges {
|
||||
let authentication = authenticationBox.authentication
|
||||
|
||||
guard
|
||||
let _status = record.object(in: managedObjectContext),
|
||||
let me = authentication.user(in: managedObjectContext)
|
||||
else { return }
|
||||
|
||||
let status = _status.reblog ?? _status
|
||||
|
||||
switch result {
|
||||
case .success(let response):
|
||||
_ = Persistence.Status.createOrMerge(
|
||||
in: managedObjectContext,
|
||||
context: Persistence.Status.PersistContext(
|
||||
domain: authenticationBox.domain,
|
||||
entity: response.value,
|
||||
me: me,
|
||||
statusCache: nil,
|
||||
userCache: nil,
|
||||
networkDate: response.networkDate
|
||||
)
|
||||
)
|
||||
if favoriteContext.isFavorited {
|
||||
status.update(favouritesCount: max(0, status.favouritesCount - 1)) // undo API return count has delay. Needs -1 local
|
||||
}
|
||||
case .failure:
|
||||
// rollback
|
||||
status.update(liked: favoriteContext.isFavorited, by: me)
|
||||
status.update(favouritesCount: favoriteContext.favoritedCount)
|
||||
}
|
||||
}
|
||||
|
||||
let response = try result.get()
|
||||
return response
|
||||
}
|
||||
@ -120,74 +70,25 @@ extension APIService {
|
||||
authorization: authenticationBox.userAuthorization,
|
||||
query: query
|
||||
).singleOutput()
|
||||
|
||||
let managedObjectContext = self.backgroundManagedObjectContext
|
||||
try await managedObjectContext.performChanges {
|
||||
guard let me = authenticationBox.authentication.user(in: managedObjectContext) else {
|
||||
assertionFailure()
|
||||
return
|
||||
}
|
||||
|
||||
for entity in response.value {
|
||||
let result = Persistence.Status.createOrMerge(
|
||||
in: managedObjectContext,
|
||||
context: Persistence.Status.PersistContext(
|
||||
domain: authenticationBox.domain,
|
||||
entity: entity,
|
||||
me: me,
|
||||
statusCache: nil,
|
||||
userCache: nil,
|
||||
networkDate: response.networkDate
|
||||
)
|
||||
)
|
||||
|
||||
result.status.update(liked: true, by: me)
|
||||
result.status.reblog?.update(liked: true, by: me)
|
||||
} // end for … in
|
||||
}
|
||||
|
||||
|
||||
return response
|
||||
} // end func
|
||||
}
|
||||
|
||||
extension APIService {
|
||||
public func favoritedBy(
|
||||
status: ManagedObjectRecord<Status>,
|
||||
status: Mastodon.Entity.Status,
|
||||
query: Mastodon.API.Statuses.FavoriteByQuery,
|
||||
authenticationBox: MastodonAuthenticationBox
|
||||
) async throws -> Mastodon.Response.Content<[Mastodon.Entity.Account]> {
|
||||
let managedObjectContext = backgroundManagedObjectContext
|
||||
let _statusID: Status.ID? = try? await managedObjectContext.perform {
|
||||
guard let _status = status.object(in: managedObjectContext) else { return nil }
|
||||
let status = _status.reblog ?? _status
|
||||
return status.id
|
||||
}
|
||||
guard let statusID = _statusID else {
|
||||
throw APIError.implicit(.badRequest)
|
||||
}
|
||||
|
||||
let response = try await Mastodon.API.Statuses.favoriteBy(
|
||||
session: session,
|
||||
domain: authenticationBox.domain,
|
||||
statusID: statusID,
|
||||
statusID: status.reblog?.id ?? status.id,
|
||||
query: query,
|
||||
authorization: authenticationBox.userAuthorization
|
||||
).singleOutput()
|
||||
|
||||
try await managedObjectContext.performChanges {
|
||||
for entity in response.value {
|
||||
_ = Persistence.MastodonUser.createOrMerge(
|
||||
in: managedObjectContext,
|
||||
context: .init(
|
||||
domain: authenticationBox.domain,
|
||||
entity: entity,
|
||||
cache: nil,
|
||||
networkDate: response.networkDate
|
||||
)
|
||||
)
|
||||
} // end for … in
|
||||
}
|
||||
|
||||
return response
|
||||
} // end func
|
||||
}
|
||||
|
@ -17,7 +17,6 @@ extension APIService {
|
||||
let sourceUserID: MastodonUser.ID
|
||||
let targetUserID: MastodonUser.ID
|
||||
let isFollowing: Bool
|
||||
let isPending: Bool
|
||||
let needsUnfollow: Bool
|
||||
}
|
||||
|
||||
@ -30,46 +29,28 @@ extension APIService {
|
||||
/// - activeMastodonAuthenticationBox: `AuthenticationService.MastodonAuthenticationBox`
|
||||
/// - Returns: publisher for `Relationship`
|
||||
public func toggleFollow(
|
||||
user: ManagedObjectRecord<MastodonUser>,
|
||||
user: Mastodon.Entity.Account,
|
||||
authenticationBox: MastodonAuthenticationBox
|
||||
) async throws -> Mastodon.Response.Content<Mastodon.Entity.Relationship> {
|
||||
|
||||
let managedObjectContext = backgroundManagedObjectContext
|
||||
let _followContext: MastodonFollowContext? = try await managedObjectContext.performChanges {
|
||||
guard let me = authenticationBox.authentication.user(in: managedObjectContext) else { return nil }
|
||||
guard let user = user.object(in: managedObjectContext) else { return nil }
|
||||
|
||||
let isFollowing = user.followingBy.contains(me)
|
||||
let isPending = user.followRequestedBy.contains(me)
|
||||
let needsUnfollow = isFollowing || isPending
|
||||
|
||||
if needsUnfollow {
|
||||
// unfollow
|
||||
user.update(isFollowing: false, by: me)
|
||||
user.update(isFollowRequested: false, by: me)
|
||||
} else {
|
||||
// follow
|
||||
if user.locked {
|
||||
user.update(isFollowing: false, by: me)
|
||||
user.update(isFollowRequested: true, by: me)
|
||||
} else {
|
||||
user.update(isFollowing: true, by: me)
|
||||
user.update(isFollowRequested: false, by: me)
|
||||
}
|
||||
}
|
||||
let context = MastodonFollowContext(
|
||||
sourceUserID: me.id,
|
||||
targetUserID: user.id,
|
||||
isFollowing: isFollowing,
|
||||
isPending: isPending,
|
||||
needsUnfollow: needsUnfollow
|
||||
)
|
||||
return context
|
||||
}
|
||||
let authentication = authenticationBox.authentication
|
||||
let me = authentication.user
|
||||
|
||||
guard let followContext = _followContext else {
|
||||
throw APIError.implicit(.badRequest)
|
||||
}
|
||||
let otherUser = try await Mastodon.API.Account.followers(
|
||||
session: session,
|
||||
domain: authentication.domain,
|
||||
userID: user.id,
|
||||
query: .init(maxID: nil, limit: nil),
|
||||
authorization: authenticationBox.userAuthorization
|
||||
).singleOutput()
|
||||
|
||||
let isFollowing = otherUser.value.contains(me)
|
||||
|
||||
let followContext = MastodonFollowContext(
|
||||
sourceUserID: me.id,
|
||||
targetUserID: user.id,
|
||||
isFollowing: isFollowing,
|
||||
needsUnfollow: isFollowing
|
||||
)
|
||||
|
||||
// request follow or unfollow
|
||||
let result: Result<Mastodon.Response.Content<Mastodon.Entity.Relationship>, Error>
|
||||
@ -85,49 +66,29 @@ extension APIService {
|
||||
} catch {
|
||||
result = .failure(error)
|
||||
}
|
||||
|
||||
// update friendship state
|
||||
try await managedObjectContext.performChanges {
|
||||
guard let me = authenticationBox.authentication.user(in: managedObjectContext),
|
||||
let user = user.object(in: managedObjectContext)
|
||||
else { return }
|
||||
|
||||
switch result {
|
||||
case .success(let response):
|
||||
Persistence.MastodonUser.update(
|
||||
mastodonUser: user,
|
||||
context: Persistence.MastodonUser.RelationshipContext(
|
||||
entity: response.value,
|
||||
me: me,
|
||||
networkDate: response.networkDate
|
||||
)
|
||||
)
|
||||
case .failure:
|
||||
// rollback
|
||||
user.update(isFollowing: followContext.isFollowing, by: me)
|
||||
user.update(isFollowRequested: followContext.isPending, by: me)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let response = try result.get()
|
||||
return response
|
||||
}
|
||||
|
||||
public func toggleShowReblogs(
|
||||
for user: ManagedObjectRecord<MastodonUser>,
|
||||
for user: Mastodon.Entity.Account,
|
||||
authenticationBox: MastodonAuthenticationBox
|
||||
) async throws -> Mastodon.Response.Content<Mastodon.Entity.Relationship> {
|
||||
|
||||
let managedObjectContext = backgroundManagedObjectContext
|
||||
guard let user = user.object(in: managedObjectContext),
|
||||
let me = authenticationBox.authentication.user(in: managedObjectContext)
|
||||
else { throw APIError.implicit(.badRequest) }
|
||||
let me = authenticationBox.authentication.user
|
||||
|
||||
let relationship = try await Mastodon.API.Account.relationships(
|
||||
session: session,
|
||||
domain: authenticationBox.domain,
|
||||
query: .init(ids: [user.id]),
|
||||
authorization: authenticationBox.userAuthorization
|
||||
).singleOutput()
|
||||
|
||||
let oldShowReblogs = relationship.value.first?.showingReblogs ?? false
|
||||
let newShowReblogs = !oldShowReblogs
|
||||
|
||||
let result: Result<Mastodon.Response.Content<Mastodon.Entity.Relationship>, Error>
|
||||
|
||||
let oldShowReblogs = me.showingReblogsBy.contains(user)
|
||||
let newShowReblogs = (oldShowReblogs == false)
|
||||
|
||||
do {
|
||||
let response = try await Mastodon.API.Account.follow(
|
||||
session: session,
|
||||
@ -142,25 +103,6 @@ extension APIService {
|
||||
result = .failure(error)
|
||||
}
|
||||
|
||||
try await managedObjectContext.performChanges {
|
||||
guard let me = authenticationBox.authentication.user(in: managedObjectContext) else { return }
|
||||
|
||||
switch result {
|
||||
case .success(let response):
|
||||
Persistence.MastodonUser.update(
|
||||
mastodonUser: user,
|
||||
context: Persistence.MastodonUser.RelationshipContext(
|
||||
entity: response.value,
|
||||
me: me,
|
||||
networkDate: response.networkDate
|
||||
)
|
||||
)
|
||||
case .failure:
|
||||
// rollback
|
||||
user.update(isShowingReblogs: oldShowReblogs, by: me)
|
||||
}
|
||||
}
|
||||
|
||||
return try result.get()
|
||||
}
|
||||
}
|
||||
|
@ -25,28 +25,7 @@ extension APIService {
|
||||
query: query,
|
||||
authorization: authenticationBox.userAuthorization
|
||||
).singleOutput()
|
||||
|
||||
let managedObjectContext = self.backgroundManagedObjectContext
|
||||
try await managedObjectContext.performChanges {
|
||||
let request = MastodonUser.sortedFetchRequest
|
||||
request.predicate = MastodonUser.predicate(
|
||||
domain: authenticationBox.domain,
|
||||
id: authenticationBox.userID
|
||||
)
|
||||
request.fetchLimit = 1
|
||||
guard let user = managedObjectContext.safeFetch(request).first else { return }
|
||||
guard let me = authenticationBox.authentication.user(in: managedObjectContext) else { return }
|
||||
|
||||
Persistence.MastodonUser.update(
|
||||
mastodonUser: user,
|
||||
context: Persistence.MastodonUser.RelationshipContext(
|
||||
entity: response.value,
|
||||
me: me,
|
||||
networkDate: response.networkDate
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
|
@ -32,27 +32,7 @@ extension APIService {
|
||||
query: query,
|
||||
authorization: authorization
|
||||
).singleOutput()
|
||||
|
||||
let managedObjectContext = self.backgroundManagedObjectContext
|
||||
try await managedObjectContext.performChanges {
|
||||
let me = authenticationBox.authentication.user(in: managedObjectContext)
|
||||
|
||||
for entity in response.value {
|
||||
let result = Persistence.MastodonUser.createOrMerge(
|
||||
in: managedObjectContext,
|
||||
context: Persistence.MastodonUser.PersistContext(
|
||||
domain: domain,
|
||||
entity: entity,
|
||||
cache: nil,
|
||||
networkDate: response.networkDate
|
||||
)
|
||||
)
|
||||
|
||||
let user = result.user
|
||||
me?.update(isFollowing: true, by: user)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
|
@ -33,30 +33,7 @@ extension APIService {
|
||||
query: query,
|
||||
authorization: authorization
|
||||
).singleOutput()
|
||||
|
||||
let managedObjectContext = self.backgroundManagedObjectContext
|
||||
try await managedObjectContext.performChanges {
|
||||
let me = authenticationBox.authentication.user(in: managedObjectContext)
|
||||
|
||||
for entity in response.value {
|
||||
let result = Persistence.MastodonUser.createOrMerge(
|
||||
in: managedObjectContext,
|
||||
context: Persistence.MastodonUser.PersistContext(
|
||||
domain: domain,
|
||||
entity: entity,
|
||||
cache: nil,
|
||||
networkDate: response.networkDate
|
||||
)
|
||||
)
|
||||
|
||||
if let me = me {
|
||||
let user = result.user
|
||||
user.update(isFollowing: true, by: me)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
|
@ -41,25 +41,6 @@ extension APIService {
|
||||
hashtag: hashtag,
|
||||
authorization: authorization
|
||||
).singleOutput()
|
||||
|
||||
let managedObjectContext = self.backgroundManagedObjectContext
|
||||
try await managedObjectContext.performChanges {
|
||||
let me = authenticationBox.authentication.user(in: managedObjectContext)
|
||||
|
||||
for entity in response.value {
|
||||
_ = Persistence.Status.createOrMerge(
|
||||
in: managedObjectContext,
|
||||
context: Persistence.Status.PersistContext(
|
||||
domain: domain,
|
||||
entity: entity,
|
||||
me: me,
|
||||
statusCache: nil,
|
||||
userCache: nil,
|
||||
networkDate: response.networkDate
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
@ -53,72 +53,7 @@ extension APIService {
|
||||
}
|
||||
|
||||
NotificationCenter.default.post(name: .userFetched, object: nil)
|
||||
|
||||
try await managedObjectContext.performChanges {
|
||||
guard let me = authenticationBox.authentication.user(in: managedObjectContext) else {
|
||||
assertionFailure()
|
||||
return
|
||||
}
|
||||
|
||||
// persist status
|
||||
var statuses: [Status] = []
|
||||
for entity in response.value {
|
||||
let result = Persistence.Status.createOrMerge(
|
||||
in: managedObjectContext,
|
||||
context: Persistence.Status.PersistContext(
|
||||
domain: domain,
|
||||
entity: entity,
|
||||
me: me,
|
||||
statusCache: nil, // TODO: add cache
|
||||
userCache: nil, // TODO: add cache
|
||||
networkDate: response.networkDate
|
||||
)
|
||||
)
|
||||
statuses.append(result.status)
|
||||
}
|
||||
|
||||
// locate anchor status
|
||||
let anchorStatus: Status? = {
|
||||
guard let maxID = maxID else { return nil }
|
||||
let request = Status.sortedFetchRequest
|
||||
request.predicate = Status.predicate(domain: domain, id: maxID)
|
||||
request.fetchLimit = 1
|
||||
return try? managedObjectContext.fetch(request).first
|
||||
}()
|
||||
|
||||
// update hasMore flag for anchor status
|
||||
let acct = Feed.Acct.mastodon(domain: authenticationBox.domain, userID: authenticationBox.userID)
|
||||
if let anchorStatus = anchorStatus,
|
||||
let feed = anchorStatus.feed(kind: .home, acct: acct) {
|
||||
feed.update(hasMore: false)
|
||||
}
|
||||
|
||||
// persist Feed relationship
|
||||
let sortedStatuses = statuses.sorted(by: { $0.createdAt < $1.createdAt })
|
||||
let oldestStatus = sortedStatuses.first
|
||||
for status in sortedStatuses {
|
||||
let _feed = status.feed(kind: .home, acct: acct)
|
||||
if let feed = _feed {
|
||||
feed.update(updatedAt: response.networkDate)
|
||||
} else {
|
||||
let feedProperty = Feed.Property(
|
||||
acct: acct,
|
||||
kind: .home,
|
||||
hasMore: false,
|
||||
createdAt: status.createdAt,
|
||||
updatedAt: response.networkDate
|
||||
)
|
||||
let feed = Feed.insert(into: managedObjectContext, property: feedProperty)
|
||||
status.attach(feed: feed)
|
||||
|
||||
// set hasMore on oldest status if is new feed
|
||||
if status === oldestStatus {
|
||||
feed.update(hasMore: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
|
@ -14,8 +14,8 @@ import MastodonSDK
|
||||
extension APIService {
|
||||
|
||||
private struct MastodonMuteContext {
|
||||
let sourceUserID: MastodonUser.ID
|
||||
let targetUserID: MastodonUser.ID
|
||||
let sourceUserID: Mastodon.Entity.Account.ID
|
||||
let targetUserID: Mastodon.Entity.Account.ID
|
||||
let targetUsername: String
|
||||
let isMuting: Bool
|
||||
}
|
||||
@ -32,7 +32,6 @@ extension APIService {
|
||||
limit: Int?,
|
||||
authenticationBox: MastodonAuthenticationBox
|
||||
) async throws -> Mastodon.Response.Content<[Mastodon.Entity.Account]> {
|
||||
let managedObjectContext = backgroundManagedObjectContext
|
||||
let response = try await Mastodon.API.Account.mutes(
|
||||
session: session,
|
||||
domain: authenticationBox.domain,
|
||||
@ -41,51 +40,24 @@ extension APIService {
|
||||
authorization: authenticationBox.userAuthorization
|
||||
).singleOutput()
|
||||
|
||||
let userIDs = response.value.map { $0.id }
|
||||
let predicate = MastodonUser.predicate(domain: authenticationBox.domain, ids: userIDs)
|
||||
|
||||
let fetchRequest = MastodonUser.fetchRequest()
|
||||
fetchRequest.predicate = predicate
|
||||
fetchRequest.includesPropertyValues = false
|
||||
|
||||
try await managedObjectContext.performChanges {
|
||||
let users = try managedObjectContext.fetch(fetchRequest) as! [MastodonUser]
|
||||
|
||||
for user in users {
|
||||
user.deleteStatusAndNotificationFeeds(in: managedObjectContext)
|
||||
}
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
public func toggleMute(
|
||||
user: ManagedObjectRecord<MastodonUser>,
|
||||
authenticationBox: MastodonAuthenticationBox
|
||||
user: Mastodon.Entity.Account,
|
||||
authenticationBox: MastodonAuthenticationBox,
|
||||
isMuting: Bool
|
||||
) async throws -> Mastodon.Response.Content<Mastodon.Entity.Relationship> {
|
||||
|
||||
let managedObjectContext = backgroundManagedObjectContext
|
||||
let muteContext: MastodonMuteContext = try await managedObjectContext.performChanges {
|
||||
let authentication = authenticationBox.authentication
|
||||
|
||||
guard
|
||||
let user = user.object(in: managedObjectContext),
|
||||
let me = authentication.user(in: managedObjectContext)
|
||||
else {
|
||||
throw APIError.implicit(.badRequest)
|
||||
}
|
||||
|
||||
let isMuting = user.mutingBy.contains(me)
|
||||
|
||||
// toggle mute state
|
||||
user.update(isMuting: !isMuting, by: me)
|
||||
return MastodonMuteContext(
|
||||
sourceUserID: me.id,
|
||||
targetUserID: user.id,
|
||||
targetUsername: user.username,
|
||||
isMuting: isMuting
|
||||
)
|
||||
}
|
||||
let me = authenticationBox.authentication.user
|
||||
|
||||
let muteContext: MastodonMuteContext = MastodonMuteContext(
|
||||
sourceUserID: me.id,
|
||||
targetUserID: user.id,
|
||||
targetUsername: user.username,
|
||||
isMuting: isMuting
|
||||
)
|
||||
|
||||
let result: Result<Mastodon.Response.Content<Mastodon.Entity.Relationship>, Error>
|
||||
do {
|
||||
@ -111,29 +83,7 @@ extension APIService {
|
||||
} catch {
|
||||
result = .failure(error)
|
||||
}
|
||||
|
||||
try await managedObjectContext.performChanges {
|
||||
guard let user = user.object(in: managedObjectContext),
|
||||
let me = authenticationBox.authentication.user(in: managedObjectContext)
|
||||
else { return }
|
||||
|
||||
switch result {
|
||||
case .success(let response):
|
||||
let relationship = response.value
|
||||
Persistence.MastodonUser.update(
|
||||
mastodonUser: user,
|
||||
context: Persistence.MastodonUser.RelationshipContext(
|
||||
entity: relationship,
|
||||
me: me,
|
||||
networkDate: response.networkDate
|
||||
)
|
||||
)
|
||||
case .failure:
|
||||
// rollback
|
||||
user.update(isMuting: muteContext.isMuting, by: me)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let response = try result.get()
|
||||
return response
|
||||
}
|
||||
|
@ -86,74 +86,6 @@ extension APIService {
|
||||
authorization: authorization
|
||||
).singleOutput()
|
||||
|
||||
let managedObjectContext = self.backgroundManagedObjectContext
|
||||
try await managedObjectContext.performChanges {
|
||||
guard let me = authenticationBox.authentication.user(in: managedObjectContext) else {
|
||||
assertionFailure()
|
||||
return
|
||||
}
|
||||
|
||||
var notifications: [Notification] = []
|
||||
for entity in response.value {
|
||||
let result = Persistence.Notification.createOrMerge(
|
||||
in: managedObjectContext,
|
||||
context: Persistence.Notification.PersistContext(
|
||||
domain: authenticationBox.domain,
|
||||
entity: entity,
|
||||
me: me,
|
||||
networkDate: response.networkDate
|
||||
)
|
||||
)
|
||||
notifications.append(result.notification)
|
||||
}
|
||||
|
||||
// locate anchor notification
|
||||
let anchorNotification: Notification? = {
|
||||
guard let maxID = query.maxID else { return nil }
|
||||
let request = Notification.sortedFetchRequest
|
||||
request.predicate = Notification.predicate(
|
||||
domain: authenticationBox.domain,
|
||||
userID: authenticationBox.userID,
|
||||
id: maxID
|
||||
)
|
||||
request.fetchLimit = 1
|
||||
return try? managedObjectContext.fetch(request).first
|
||||
}()
|
||||
|
||||
// update hasMore flag for anchor status
|
||||
let acct = Feed.Acct.mastodon(domain: authenticationBox.domain, userID: authenticationBox.userID)
|
||||
let kind: Feed.Kind = scope == .everything ? .notificationAll : .notificationMentions
|
||||
if let anchorNotification = anchorNotification,
|
||||
let feed = anchorNotification.feed(kind: kind, acct: acct) {
|
||||
feed.update(hasMore: false)
|
||||
}
|
||||
|
||||
// persist Feed relationship
|
||||
let sortedNotifications = notifications.sorted(by: { $0.createAt < $1.createAt })
|
||||
let oldestNotification = sortedNotifications.first
|
||||
for notification in notifications {
|
||||
let _feed = notification.feed(kind: kind, acct: acct)
|
||||
if let feed = _feed {
|
||||
feed.update(updatedAt: response.networkDate)
|
||||
} else {
|
||||
let feedProperty = Feed.Property(
|
||||
acct: acct,
|
||||
kind: kind,
|
||||
hasMore: false,
|
||||
createdAt: notification.createAt,
|
||||
updatedAt: response.networkDate
|
||||
)
|
||||
let feed = Feed.insert(into: managedObjectContext, property: feedProperty)
|
||||
notification.attach(feed: feed)
|
||||
|
||||
// set hasMore on oldest notification if is new feed
|
||||
if notification === oldestNotification {
|
||||
feed.update(hasMore: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
}
|
||||
@ -173,21 +105,7 @@ extension APIService {
|
||||
notificationID: notificationID,
|
||||
authorization: authorization
|
||||
).singleOutput()
|
||||
|
||||
let managedObjectContext = self.backgroundManagedObjectContext
|
||||
try await managedObjectContext.performChanges {
|
||||
guard let me = authenticationBox.authentication.user(in: managedObjectContext) else { return }
|
||||
_ = Persistence.Notification.createOrMerge(
|
||||
in: managedObjectContext,
|
||||
context: Persistence.Notification.PersistContext(
|
||||
domain: domain,
|
||||
entity: response.value,
|
||||
me: me,
|
||||
networkDate: response.networkDate
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
|
@ -14,39 +14,18 @@ import MastodonSDK
|
||||
extension APIService {
|
||||
|
||||
public func poll(
|
||||
poll: ManagedObjectRecord<Poll>,
|
||||
poll: Mastodon.Entity.Poll,
|
||||
authenticationBox: MastodonAuthenticationBox
|
||||
) async throws -> Mastodon.Response.Content<Mastodon.Entity.Poll> {
|
||||
let authorization = authenticationBox.userAuthorization
|
||||
|
||||
let managedObjectContext = self.backgroundManagedObjectContext
|
||||
let pollID: Poll.ID = try await managedObjectContext.perform {
|
||||
guard let poll = poll.object(in: managedObjectContext) else {
|
||||
throw APIError.implicit(.badRequest)
|
||||
}
|
||||
return poll.id
|
||||
}
|
||||
|
||||
|
||||
let response = try await Mastodon.API.Polls.poll(
|
||||
session: session,
|
||||
domain: authenticationBox.domain,
|
||||
pollID: pollID,
|
||||
pollID: poll.id,
|
||||
authorization: authorization
|
||||
).singleOutput()
|
||||
|
||||
try await managedObjectContext.performChanges {
|
||||
let me = authenticationBox.authentication.user(in: managedObjectContext)
|
||||
_ = Persistence.Poll.createOrMerge(
|
||||
in: managedObjectContext,
|
||||
context: Persistence.Poll.PersistContext(
|
||||
domain: authenticationBox.domain,
|
||||
entity: response.value,
|
||||
me: me,
|
||||
networkDate: response.networkDate
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
@ -55,41 +34,19 @@ extension APIService {
|
||||
extension APIService {
|
||||
|
||||
public func vote(
|
||||
poll: ManagedObjectRecord<Poll>,
|
||||
poll: Mastodon.Entity.Poll,
|
||||
choices: [Int],
|
||||
authenticationBox: MastodonAuthenticationBox
|
||||
) async throws -> Mastodon.Response.Content<Mastodon.Entity.Poll> {
|
||||
let managedObjectContext = backgroundManagedObjectContext
|
||||
let _pollID: Poll.ID? = try await managedObjectContext.perform {
|
||||
guard let poll = poll.object(in: managedObjectContext) else { return nil }
|
||||
return poll.id
|
||||
}
|
||||
|
||||
guard let pollID = _pollID else {
|
||||
throw APIError.implicit(.badRequest)
|
||||
}
|
||||
|
||||
let response = try await Mastodon.API.Polls.vote(
|
||||
session: session,
|
||||
domain: authenticationBox.domain,
|
||||
pollID: pollID,
|
||||
pollID: poll.id,
|
||||
query: Mastodon.API.Polls.VoteQuery(choices: choices),
|
||||
authorization: authenticationBox.userAuthorization
|
||||
).singleOutput()
|
||||
|
||||
try await managedObjectContext.performChanges {
|
||||
let me = authenticationBox.authentication.user(in: managedObjectContext)
|
||||
_ = Persistence.Poll.createOrMerge(
|
||||
in: managedObjectContext,
|
||||
context: Persistence.Poll.PersistContext(
|
||||
domain: authenticationBox.domain,
|
||||
entity: response.value,
|
||||
me: me,
|
||||
networkDate: response.networkDate
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
|
@ -27,24 +27,6 @@ extension APIService {
|
||||
authorization: authorization
|
||||
).singleOutput()
|
||||
|
||||
let managedObjectContext = self.backgroundManagedObjectContext
|
||||
try await managedObjectContext.performChanges {
|
||||
let me = authenticationBox.authentication.user(in: managedObjectContext)
|
||||
for entity in response.value {
|
||||
_ = Persistence.Status.createOrMerge(
|
||||
in: managedObjectContext,
|
||||
context: Persistence.Status.PersistContext(
|
||||
domain: domain,
|
||||
entity: entity,
|
||||
me: me,
|
||||
statusCache: nil,
|
||||
userCache: nil,
|
||||
networkDate: response.networkDate
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return response
|
||||
} // end func
|
||||
|
||||
|
@ -16,40 +16,24 @@ extension APIService {
|
||||
private struct MastodonReblogContext {
|
||||
let statusID: Status.ID
|
||||
let isReblogged: Bool
|
||||
let rebloggedCount: Int64
|
||||
let rebloggedCount: Int
|
||||
}
|
||||
|
||||
public func reblog(
|
||||
record: ManagedObjectRecord<Status>,
|
||||
status: Mastodon.Entity.Status,
|
||||
authenticationBox: MastodonAuthenticationBox
|
||||
) async throws -> Mastodon.Response.Content<Mastodon.Entity.Status> {
|
||||
let managedObjectContext = backgroundManagedObjectContext
|
||||
|
||||
// update repost state and retrieve repost context
|
||||
let _reblogContext: MastodonReblogContext? = try await managedObjectContext.performChanges {
|
||||
let authentication = authenticationBox.authentication
|
||||
|
||||
guard
|
||||
let me = authentication.user(in: managedObjectContext),
|
||||
let _status = record.object(in: managedObjectContext)
|
||||
else { return nil }
|
||||
|
||||
let status = _status.reblog ?? _status
|
||||
let isReblogged = status.rebloggedBy.contains(me)
|
||||
let rebloggedCount = status.reblogsCount
|
||||
let reblogCount = isReblogged ? rebloggedCount - 1 : rebloggedCount + 1
|
||||
status.update(reblogged: !isReblogged, by: me)
|
||||
status.update(reblogsCount: Int64(max(0, reblogCount)))
|
||||
let reblogContext = MastodonReblogContext(
|
||||
statusID: status.id,
|
||||
isReblogged: isReblogged,
|
||||
rebloggedCount: rebloggedCount
|
||||
)
|
||||
return reblogContext
|
||||
}
|
||||
guard let reblogContext = _reblogContext else {
|
||||
throw APIError.implicit(.badRequest)
|
||||
}
|
||||
let _status = status.reblog ?? status
|
||||
let isReblogged = status.reblogged ?? false
|
||||
let rebloggedCount = status.reblogsCount
|
||||
let reblogCount = isReblogged ? rebloggedCount - 1 : rebloggedCount + 1
|
||||
let reblogContext = MastodonReblogContext(
|
||||
statusID: status.id,
|
||||
isReblogged: isReblogged,
|
||||
rebloggedCount: rebloggedCount
|
||||
)
|
||||
|
||||
// request repost or undo repost
|
||||
let result: Result<Mastodon.Response.Content<Mastodon.Entity.Status>, Error>
|
||||
@ -65,41 +49,7 @@ extension APIService {
|
||||
} catch {
|
||||
result = .failure(error)
|
||||
}
|
||||
|
||||
// update repost state
|
||||
try await managedObjectContext.performChanges {
|
||||
let authentication = authenticationBox.authentication
|
||||
|
||||
guard
|
||||
let me = authentication.user(in: managedObjectContext),
|
||||
let _status = record.object(in: managedObjectContext)
|
||||
else { return }
|
||||
|
||||
let status = _status.reblog ?? _status
|
||||
|
||||
switch result {
|
||||
case .success(let response):
|
||||
_ = Persistence.Status.createOrMerge(
|
||||
in: managedObjectContext,
|
||||
context: Persistence.Status.PersistContext(
|
||||
domain: authentication.domain,
|
||||
entity: response.value,
|
||||
me: me,
|
||||
statusCache: nil,
|
||||
userCache: nil,
|
||||
networkDate: response.networkDate
|
||||
)
|
||||
)
|
||||
if reblogContext.isReblogged {
|
||||
status.update(reblogsCount: max(0, status.reblogsCount - 1)) // undo API return count has delay. Needs -1 local
|
||||
}
|
||||
case .failure:
|
||||
// rollback
|
||||
status.update(reblogged: reblogContext.isReblogged, by: me)
|
||||
status.update(reblogsCount: reblogContext.rebloggedCount)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let response = try result.get()
|
||||
return response
|
||||
}
|
||||
@ -108,42 +58,19 @@ extension APIService {
|
||||
|
||||
extension APIService {
|
||||
public func rebloggedBy(
|
||||
status: ManagedObjectRecord<Status>,
|
||||
status: Mastodon.Entity.Status,
|
||||
query: Mastodon.API.Statuses.RebloggedByQuery,
|
||||
authenticationBox: MastodonAuthenticationBox
|
||||
) async throws -> Mastodon.Response.Content<[Mastodon.Entity.Account]> {
|
||||
let managedObjectContext = backgroundManagedObjectContext
|
||||
let _statusID: Status.ID? = try? await managedObjectContext.perform {
|
||||
guard let _status = status.object(in: managedObjectContext) else { return nil }
|
||||
let status = _status.reblog ?? _status
|
||||
return status.id
|
||||
}
|
||||
guard let statusID = _statusID else {
|
||||
throw APIError.implicit(.badRequest)
|
||||
}
|
||||
|
||||
let response = try await Mastodon.API.Statuses.rebloggedBy(
|
||||
session: session,
|
||||
domain: authenticationBox.domain,
|
||||
statusID: statusID,
|
||||
statusID: status.reblog?.id ?? status.id,
|
||||
query: query,
|
||||
authorization: authenticationBox.userAuthorization
|
||||
).singleOutput()
|
||||
|
||||
try await managedObjectContext.performChanges {
|
||||
for entity in response.value {
|
||||
_ = Persistence.MastodonUser.createOrMerge(
|
||||
in: managedObjectContext,
|
||||
context: .init(
|
||||
domain: authenticationBox.domain,
|
||||
entity: entity,
|
||||
cache: nil,
|
||||
networkDate: response.networkDate
|
||||
)
|
||||
)
|
||||
} // end for … in
|
||||
}
|
||||
|
||||
|
||||
return response
|
||||
} // end func
|
||||
}
|
||||
|
@ -14,17 +14,16 @@ import MastodonSDK
|
||||
extension APIService {
|
||||
|
||||
public func relationship(
|
||||
records: [ManagedObjectRecord<MastodonUser>],
|
||||
accounts: [Mastodon.Entity.Account],
|
||||
authenticationBox: MastodonAuthenticationBox
|
||||
) async throws -> Mastodon.Response.Content<[Mastodon.Entity.Relationship]> {
|
||||
let managedObjectContext = backgroundManagedObjectContext
|
||||
|
||||
let _query: Mastodon.API.Account.RelationshipQuery? = try? await managedObjectContext.perform {
|
||||
var ids: [MastodonUser.ID] = []
|
||||
for record in records {
|
||||
guard let user = record.object(in: managedObjectContext) else { continue }
|
||||
guard user.id != authenticationBox.userID else { continue }
|
||||
ids.append(user.id)
|
||||
for account in accounts {
|
||||
guard account.id != authenticationBox.userID else { continue }
|
||||
ids.append(account.id)
|
||||
}
|
||||
guard !ids.isEmpty else { return nil }
|
||||
return Mastodon.API.Account.RelationshipQuery(ids: ids)
|
||||
@ -39,28 +38,6 @@ extension APIService {
|
||||
query: query,
|
||||
authorization: authenticationBox.userAuthorization
|
||||
).singleOutput()
|
||||
|
||||
try await managedObjectContext.performChanges {
|
||||
guard let me = authenticationBox.authentication.user(in: managedObjectContext) else {
|
||||
// assertionFailure()
|
||||
return
|
||||
}
|
||||
|
||||
let relationships = response.value
|
||||
for record in records {
|
||||
guard let user = record.object(in: managedObjectContext) else { continue }
|
||||
guard let relationship = relationships.first(where: { $0.id == user.id }) else { continue }
|
||||
|
||||
Persistence.MastodonUser.update(
|
||||
mastodonUser: user,
|
||||
context: Persistence.MastodonUser.RelationshipContext(
|
||||
entity: relationship,
|
||||
me: me,
|
||||
networkDate: response.networkDate
|
||||
)
|
||||
)
|
||||
} // end for in
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
@ -24,40 +24,7 @@ extension APIService {
|
||||
query: query,
|
||||
authorization: authorization
|
||||
).singleOutput()
|
||||
|
||||
let managedObjectContext = self.backgroundManagedObjectContext
|
||||
try await managedObjectContext.performChanges {
|
||||
let me = authenticationBox.authentication.user(in: managedObjectContext)
|
||||
|
||||
// user
|
||||
for entity in response.value.accounts {
|
||||
_ = Persistence.MastodonUser.createOrMerge(
|
||||
in: managedObjectContext,
|
||||
context: Persistence.MastodonUser.PersistContext(
|
||||
domain: domain,
|
||||
entity: entity,
|
||||
cache: nil,
|
||||
networkDate: response.networkDate
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// statuses
|
||||
for entity in response.value.statuses {
|
||||
_ = Persistence.Status.createOrMerge(
|
||||
in: managedObjectContext,
|
||||
context: Persistence.Status.PersistContext(
|
||||
domain: domain,
|
||||
entity: entity,
|
||||
me: me,
|
||||
statusCache: nil,
|
||||
userCache: nil,
|
||||
networkDate: response.networkDate
|
||||
)
|
||||
)
|
||||
}
|
||||
} // ent try await managedObjectContext.performChanges { … }
|
||||
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
|
@ -71,32 +71,7 @@ extension APIService {
|
||||
domain: domain,
|
||||
authorization: authorization
|
||||
).singleOutput()
|
||||
|
||||
#if !APP_EXTENSION
|
||||
let managedObjectContext = self.backgroundManagedObjectContext
|
||||
|
||||
try await managedObjectContext.performChanges {
|
||||
let me = authenticationBox.authentication.user(in: managedObjectContext)
|
||||
let status = Persistence.Status.createOrMerge(
|
||||
in: managedObjectContext,
|
||||
context: Persistence.Status.PersistContext(
|
||||
domain: domain,
|
||||
entity: response.value,
|
||||
me: me,
|
||||
statusCache: nil,
|
||||
userCache: nil,
|
||||
networkDate: response.networkDate
|
||||
)
|
||||
)
|
||||
|
||||
Persistence.StatusEdit.createOrMerge(
|
||||
in: managedObjectContext,
|
||||
statusEdits: responseHistory.value,
|
||||
forStatus: status.status
|
||||
)
|
||||
}
|
||||
#endif
|
||||
|
||||
return response
|
||||
}
|
||||
}
|
||||
|
@ -29,25 +29,7 @@ extension APIService {
|
||||
query: query,
|
||||
authorization: authorization
|
||||
).singleOutput()
|
||||
|
||||
#if !APP_EXTENSION
|
||||
let managedObjectContext = self.backgroundManagedObjectContext
|
||||
try await managedObjectContext.performChanges {
|
||||
let me = authenticationBox.authentication.user(in: managedObjectContext)
|
||||
_ = Persistence.Status.createOrMerge(
|
||||
in: managedObjectContext,
|
||||
context: Persistence.Status.PersistContext(
|
||||
domain: domain,
|
||||
entity: response.value,
|
||||
me: me,
|
||||
statusCache: nil,
|
||||
userCache: nil,
|
||||
networkDate: response.networkDate
|
||||
)
|
||||
)
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
|
@ -26,54 +26,23 @@ extension APIService {
|
||||
statusID: statusID,
|
||||
authorization: authorization
|
||||
).singleOutput()
|
||||
|
||||
let managedObjectContext = self.backgroundManagedObjectContext
|
||||
try await managedObjectContext.performChanges {
|
||||
let me = authenticationBox.authentication.user(in: managedObjectContext)
|
||||
_ = Persistence.Status.createOrMerge(
|
||||
in: managedObjectContext,
|
||||
context: Persistence.Status.PersistContext(
|
||||
domain: domain,
|
||||
entity: response.value,
|
||||
me: me,
|
||||
statusCache: nil,
|
||||
userCache: nil,
|
||||
networkDate: response.networkDate
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
public func deleteStatus(
|
||||
status: ManagedObjectRecord<Status>,
|
||||
status: Mastodon.Entity.Status,
|
||||
authenticationBox: MastodonAuthenticationBox
|
||||
) async throws -> Mastodon.Response.Content<Mastodon.Entity.Status> {
|
||||
let authorization = authenticationBox.userAuthorization
|
||||
|
||||
let managedObjectContext = backgroundManagedObjectContext
|
||||
let _query: Mastodon.API.Statuses.DeleteStatusQuery? = try? await managedObjectContext.perform {
|
||||
guard let _status = status.object(in: managedObjectContext) else { return nil }
|
||||
let status = _status.reblog ?? _status
|
||||
return Mastodon.API.Statuses.DeleteStatusQuery(id: status.id)
|
||||
}
|
||||
guard let query = _query else {
|
||||
throw APIError.implicit(.badRequest)
|
||||
}
|
||||
|
||||
let response = try await Mastodon.API.Statuses.deleteStatus(
|
||||
session: session,
|
||||
domain: authenticationBox.domain,
|
||||
query: query,
|
||||
query: Mastodon.API.Statuses.DeleteStatusQuery(id: status.id),
|
||||
authorization: authorization
|
||||
).singleOutput()
|
||||
|
||||
try await managedObjectContext.performChanges {
|
||||
guard let status = status.object(in: managedObjectContext) else { return }
|
||||
managedObjectContext.delete(status)
|
||||
}
|
||||
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@ extension APIService {
|
||||
authorization: authorization
|
||||
).singleOutput()
|
||||
|
||||
return try await persistTag(from: response, domain: domain, authenticationBox: authenticationBox)
|
||||
return response
|
||||
} // end func
|
||||
|
||||
public func followTag(
|
||||
@ -44,7 +44,7 @@ extension APIService {
|
||||
authorization: authorization
|
||||
).singleOutput()
|
||||
|
||||
return try await persistTag(from: response, domain: domain, authenticationBox: authenticationBox)
|
||||
return response
|
||||
} // end func
|
||||
|
||||
public func unfollowTag(
|
||||
@ -61,31 +61,6 @@ extension APIService {
|
||||
authorization: authorization
|
||||
).singleOutput()
|
||||
|
||||
return try await persistTag(from: response, domain: domain, authenticationBox: authenticationBox)
|
||||
return response
|
||||
} // end func
|
||||
}
|
||||
|
||||
fileprivate extension APIService {
|
||||
func persistTag(
|
||||
from response: Mastodon.Response.Content<Mastodon.Entity.Tag>,
|
||||
domain: String,
|
||||
authenticationBox: MastodonAuthenticationBox
|
||||
) async throws -> Mastodon.Response.Content<Mastodon.Entity.Tag> {
|
||||
let managedObjectContext = self.backgroundManagedObjectContext
|
||||
try await managedObjectContext.performChanges {
|
||||
let me = authenticationBox.authentication.user(in: managedObjectContext)
|
||||
|
||||
_ = Persistence.Tag.createOrMerge(
|
||||
in: managedObjectContext,
|
||||
context: Persistence.Tag.PersistContext(
|
||||
domain: domain,
|
||||
entity: response.value,
|
||||
me: me,
|
||||
networkDate: response.networkDate
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
}
|
||||
|
@ -26,27 +26,7 @@ extension APIService {
|
||||
statusID: statusID,
|
||||
authorization: authorization
|
||||
).singleOutput()
|
||||
|
||||
let managedObjectContext = self.backgroundManagedObjectContext
|
||||
try await managedObjectContext.performChanges {
|
||||
let me = authenticationBox.authentication.user(in: managedObjectContext)
|
||||
let value = response.value.ancestors + response.value.descendants
|
||||
|
||||
for entity in value {
|
||||
_ = Persistence.Status.createOrMerge(
|
||||
in: managedObjectContext,
|
||||
context: Persistence.Status.PersistContext(
|
||||
domain: domain,
|
||||
entity: entity,
|
||||
me: me,
|
||||
statusCache: nil,
|
||||
userCache: nil,
|
||||
networkDate: response.networkDate
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return response
|
||||
} // end func
|
||||
}
|
||||
|
@ -42,25 +42,7 @@ extension APIService {
|
||||
query: query,
|
||||
authorization: authorization
|
||||
).singleOutput()
|
||||
|
||||
let managedObjectContext = self.backgroundManagedObjectContext
|
||||
try await managedObjectContext.performChanges {
|
||||
let me = authenticationBox.authentication.user(in: managedObjectContext)
|
||||
for entity in response.value {
|
||||
_ = Persistence.Status.createOrMerge(
|
||||
in: managedObjectContext,
|
||||
context: Persistence.Status.PersistContext(
|
||||
domain: domain,
|
||||
entity: entity,
|
||||
me: me,
|
||||
statusCache: nil,
|
||||
userCache: nil,
|
||||
networkDate: response.networkDate
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return response
|
||||
} // end func
|
||||
|
||||
|
@ -1,75 +1,74 @@
|
||||
////
|
||||
//// APIService+CoreData+Instance.swift
|
||||
//// Mastodon
|
||||
////
|
||||
//// Created by Cirno MainasuK on 2021-10-9.
|
||||
////
|
||||
//
|
||||
// APIService+CoreData+Instance.swift
|
||||
// Mastodon
|
||||
//import Foundation
|
||||
//import CoreData
|
||||
//import MastodonSDK
|
||||
//
|
||||
// Created by Cirno MainasuK on 2021-10-9.
|
||||
//extension APIService.CoreData {
|
||||
//
|
||||
// static func createOrMergeInstance(
|
||||
// into managedObjectContext: NSManagedObjectContext,
|
||||
// domain: String,
|
||||
// entity: Mastodon.Entity.Instance,
|
||||
// networkDate: Date
|
||||
// ) -> (instance: Instance, isCreated: Bool) {
|
||||
// // fetch old mastodon user
|
||||
// let old: Instance? = {
|
||||
// let request = Instance.sortedFetchRequest
|
||||
// request.predicate = Instance.predicate(domain: domain)
|
||||
// request.fetchLimit = 1
|
||||
// request.returnsObjectsAsFaults = false
|
||||
// do {
|
||||
// return try managedObjectContext.fetch(request).first
|
||||
// } catch {
|
||||
// assertionFailure(error.localizedDescription)
|
||||
// return nil
|
||||
// }
|
||||
// }()
|
||||
//
|
||||
// if let old = old {
|
||||
// // merge old
|
||||
// APIService.CoreData.merge(
|
||||
// instance: old,
|
||||
// entity: entity,
|
||||
// domain: domain,
|
||||
// networkDate: networkDate
|
||||
// )
|
||||
// return (old, false)
|
||||
// } else {
|
||||
// let instance = Instance.insert(
|
||||
// into: managedObjectContext,
|
||||
// property: Instance.Property(domain: domain, version: entity.version)
|
||||
// )
|
||||
// let configurationRaw = entity.configuration.flatMap { Instance.encode(configuration: $0) }
|
||||
// instance.update(configurationRaw: configurationRaw)
|
||||
//
|
||||
// return (instance, true)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//}
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
import CoreDataStack
|
||||
import MastodonSDK
|
||||
|
||||
extension APIService.CoreData {
|
||||
|
||||
static func createOrMergeInstance(
|
||||
into managedObjectContext: NSManagedObjectContext,
|
||||
domain: String,
|
||||
entity: Mastodon.Entity.Instance,
|
||||
networkDate: Date
|
||||
) -> (instance: Instance, isCreated: Bool) {
|
||||
// fetch old mastodon user
|
||||
let old: Instance? = {
|
||||
let request = Instance.sortedFetchRequest
|
||||
request.predicate = Instance.predicate(domain: domain)
|
||||
request.fetchLimit = 1
|
||||
request.returnsObjectsAsFaults = false
|
||||
do {
|
||||
return try managedObjectContext.fetch(request).first
|
||||
} catch {
|
||||
assertionFailure(error.localizedDescription)
|
||||
return nil
|
||||
}
|
||||
}()
|
||||
|
||||
if let old = old {
|
||||
// merge old
|
||||
APIService.CoreData.merge(
|
||||
instance: old,
|
||||
entity: entity,
|
||||
domain: domain,
|
||||
networkDate: networkDate
|
||||
)
|
||||
return (old, false)
|
||||
} else {
|
||||
let instance = Instance.insert(
|
||||
into: managedObjectContext,
|
||||
property: Instance.Property(domain: domain, version: entity.version)
|
||||
)
|
||||
let configurationRaw = entity.configuration.flatMap { Instance.encode(configuration: $0) }
|
||||
instance.update(configurationRaw: configurationRaw)
|
||||
|
||||
return (instance, true)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension APIService.CoreData {
|
||||
|
||||
static func merge(
|
||||
instance: Instance,
|
||||
entity: Mastodon.Entity.Instance,
|
||||
domain: String,
|
||||
networkDate: Date
|
||||
) {
|
||||
guard networkDate > instance.updatedAt else { return }
|
||||
|
||||
let configurationRaw = entity.configuration.flatMap { Instance.encode(configuration: $0) }
|
||||
instance.update(configurationRaw: configurationRaw)
|
||||
instance.version = entity.version
|
||||
|
||||
instance.didUpdate(at: networkDate)
|
||||
}
|
||||
|
||||
}
|
||||
//extension APIService.CoreData {
|
||||
//
|
||||
// static func merge(
|
||||
// instance: Instance,
|
||||
// entity: Mastodon.Entity.Instance,
|
||||
// domain: String,
|
||||
// networkDate: Date
|
||||
// ) {
|
||||
// guard networkDate > instance.updatedAt else { return }
|
||||
//
|
||||
// let configurationRaw = entity.configuration.flatMap { Instance.encode(configuration: $0) }
|
||||
// instance.update(configurationRaw: configurationRaw)
|
||||
// instance.version = entity.version
|
||||
//
|
||||
// instance.didUpdate(at: networkDate)
|
||||
// }
|
||||
//
|
||||
//}
|
||||
|
@ -1,77 +1,76 @@
|
||||
import Foundation
|
||||
import CoreData
|
||||
import CoreDataStack
|
||||
import MastodonSDK
|
||||
|
||||
extension APIService.CoreData {
|
||||
|
||||
public struct PersistContext {
|
||||
public let domain: String
|
||||
public let entity: Mastodon.Entity.V2.Instance
|
||||
public let networkDate: Date
|
||||
|
||||
public init(
|
||||
domain: String,
|
||||
entity: Mastodon.Entity.V2.Instance,
|
||||
networkDate: Date
|
||||
) {
|
||||
self.domain = domain
|
||||
self.entity = entity
|
||||
self.networkDate = networkDate
|
||||
}
|
||||
}
|
||||
|
||||
static func createOrMergeInstance(
|
||||
in managedObjectContext: NSManagedObjectContext,
|
||||
context: PersistContext
|
||||
) -> (instance: Instance, isCreated: Bool) {
|
||||
// fetch old mastodon user
|
||||
let old: Instance? = {
|
||||
let request = Instance.sortedFetchRequest
|
||||
request.predicate = Instance.predicate(domain: context.domain)
|
||||
request.fetchLimit = 1
|
||||
request.returnsObjectsAsFaults = false
|
||||
do {
|
||||
return try managedObjectContext.fetch(request).first
|
||||
} catch {
|
||||
assertionFailure(error.localizedDescription)
|
||||
return nil
|
||||
}
|
||||
}()
|
||||
|
||||
if let old = old {
|
||||
APIService.CoreData.merge(
|
||||
instance: old,
|
||||
context: context
|
||||
)
|
||||
return (old, false)
|
||||
} else {
|
||||
let instance = Instance.insert(
|
||||
into: managedObjectContext,
|
||||
property: Instance.Property(domain: context.domain, version: context.entity.version)
|
||||
)
|
||||
let configurationRaw = context.entity.configuration.flatMap { Instance.encodeV2(configuration: $0) }
|
||||
instance.update(configurationV2Raw: configurationRaw)
|
||||
|
||||
return (instance, true)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension APIService.CoreData {
|
||||
|
||||
static func merge(
|
||||
instance: Instance,
|
||||
context: PersistContext
|
||||
) {
|
||||
guard context.networkDate > instance.updatedAt else { return }
|
||||
|
||||
let configurationRaw = context.entity.configuration.flatMap { Instance.encodeV2(configuration: $0) }
|
||||
instance.update(configurationV2Raw: configurationRaw)
|
||||
instance.version = context.entity.version
|
||||
|
||||
instance.didUpdate(at: context.networkDate)
|
||||
}
|
||||
|
||||
}
|
||||
//import Foundation
|
||||
//import CoreData
|
||||
//import MastodonSDK
|
||||
//
|
||||
//extension APIService.CoreData {
|
||||
//
|
||||
// public struct PersistContext {
|
||||
// public let domain: String
|
||||
// public let entity: Mastodon.Entity.V2.Instance
|
||||
// public let networkDate: Date
|
||||
//
|
||||
// public init(
|
||||
// domain: String,
|
||||
// entity: Mastodon.Entity.V2.Instance,
|
||||
// networkDate: Date
|
||||
// ) {
|
||||
// self.domain = domain
|
||||
// self.entity = entity
|
||||
// self.networkDate = networkDate
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// static func createOrMergeInstance(
|
||||
// in managedObjectContext: NSManagedObjectContext,
|
||||
// context: PersistContext
|
||||
// ) -> (instance: Instance, isCreated: Bool) {
|
||||
// // fetch old mastodon user
|
||||
// let old: Instance? = {
|
||||
// let request = Instance.sortedFetchRequest
|
||||
// request.predicate = Instance.predicate(domain: context.domain)
|
||||
// request.fetchLimit = 1
|
||||
// request.returnsObjectsAsFaults = false
|
||||
// do {
|
||||
// return try managedObjectContext.fetch(request).first
|
||||
// } catch {
|
||||
// assertionFailure(error.localizedDescription)
|
||||
// return nil
|
||||
// }
|
||||
// }()
|
||||
//
|
||||
// if let old = old {
|
||||
// APIService.CoreData.merge(
|
||||
// instance: old,
|
||||
// context: context
|
||||
// )
|
||||
// return (old, false)
|
||||
// } else {
|
||||
// let instance = Instance.insert(
|
||||
// into: managedObjectContext,
|
||||
// property: Instance.Property(domain: context.domain, version: context.entity.version)
|
||||
// )
|
||||
// let configurationRaw = context.entity.configuration.flatMap { Instance.encodeV2(configuration: $0) }
|
||||
// instance.update(configurationV2Raw: configurationRaw)
|
||||
//
|
||||
// return (instance, true)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//}
|
||||
//
|
||||
//extension APIService.CoreData {
|
||||
//
|
||||
// static func merge(
|
||||
// instance: Instance,
|
||||
// context: PersistContext
|
||||
// ) {
|
||||
// guard context.networkDate > instance.updatedAt else { return }
|
||||
//
|
||||
// let configurationRaw = context.entity.configuration.flatMap { Instance.encodeV2(configuration: $0) }
|
||||
// instance.update(configurationV2Raw: configurationRaw)
|
||||
// instance.version = context.entity.version
|
||||
//
|
||||
// instance.didUpdate(at: context.networkDate)
|
||||
// }
|
||||
//
|
||||
//}
|
||||
|
@ -8,7 +8,6 @@
|
||||
import Foundation
|
||||
import Combine
|
||||
import CoreData
|
||||
import CoreDataStack
|
||||
import MastodonSDK
|
||||
|
||||
public final class InstanceService {
|
||||
@ -16,7 +15,6 @@ public final class InstanceService {
|
||||
var disposeBag = Set<AnyCancellable>()
|
||||
|
||||
// input
|
||||
let backgroundManagedObjectContext: NSManagedObjectContext
|
||||
weak var apiService: APIService?
|
||||
weak var authenticationService: AuthenticationService?
|
||||
|
||||
@ -26,7 +24,6 @@ public final class InstanceService {
|
||||
apiService: APIService,
|
||||
authenticationService: AuthenticationService
|
||||
) {
|
||||
self.backgroundManagedObjectContext = apiService.backgroundManagedObjectContext
|
||||
self.apiService = apiService
|
||||
self.authenticationService = authenticationService
|
||||
|
||||
@ -68,56 +65,38 @@ extension InstanceService {
|
||||
}
|
||||
|
||||
private func updateInstance(domain: String, response: Mastodon.Response.Content<Mastodon.Entity.Instance>) -> AnyPublisher<Void, Error> {
|
||||
let managedObjectContext = self.backgroundManagedObjectContext
|
||||
return managedObjectContext.performChanges {
|
||||
// get instance
|
||||
let (instance, _) = APIService.CoreData.createOrMergeInstance(
|
||||
into: managedObjectContext,
|
||||
domain: domain,
|
||||
entity: response.value,
|
||||
networkDate: response.networkDate
|
||||
)
|
||||
|
||||
return Future<Void, Error> { promise in
|
||||
// update instance
|
||||
AuthenticationServiceProvider.shared.update(instance: instance, where: domain)
|
||||
}
|
||||
.setFailureType(to: Error.self)
|
||||
.tryMap { result in
|
||||
switch result {
|
||||
case .success:
|
||||
break
|
||||
case .failure(let error):
|
||||
throw error
|
||||
}
|
||||
AuthenticationServiceProvider.shared.update(instance: response.value, where: domain)
|
||||
promise(.success(()))
|
||||
}
|
||||
// .setFailureType(to: Error.self)
|
||||
// .tryMap { result in
|
||||
// switch result {
|
||||
// case .success:
|
||||
// break
|
||||
// case .failure(let error):
|
||||
// throw error
|
||||
// }
|
||||
// }
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
private func updateInstanceV2(domain: String, response: Mastodon.Response.Content<Mastodon.Entity.V2.Instance>) -> AnyPublisher<Void, Error> {
|
||||
let managedObjectContext = self.backgroundManagedObjectContext
|
||||
return managedObjectContext.performChanges {
|
||||
// get instance
|
||||
let (instance, _) = APIService.CoreData.createOrMergeInstance(
|
||||
in: managedObjectContext,
|
||||
context: .init(
|
||||
domain: domain,
|
||||
entity: response.value,
|
||||
networkDate: response.networkDate
|
||||
)
|
||||
)
|
||||
|
||||
return Future<Void, Error> { promise in
|
||||
// update instance
|
||||
AuthenticationServiceProvider.shared.update(instance: instance, where: domain)
|
||||
}
|
||||
.setFailureType(to: Error.self)
|
||||
.tryMap { result in
|
||||
switch result {
|
||||
case .success:
|
||||
break
|
||||
case .failure(let error):
|
||||
throw error
|
||||
}
|
||||
AuthenticationServiceProvider.shared.update(instanceV2: response.value, where: domain)
|
||||
promise(.success(()))
|
||||
}
|
||||
// .setFailureType(to: Error.self)
|
||||
// .tryMap { result in
|
||||
// switch result {
|
||||
// case .success:
|
||||
// break
|
||||
// case .failure(let error):
|
||||
// throw error
|
||||
// }
|
||||
// }
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
}
|
||||
|
@ -101,12 +101,12 @@ extension NotificationService {
|
||||
return try await managedObjectContext.perform {
|
||||
var items: [UIApplicationShortcutItem] = []
|
||||
for authentication in AuthenticationServiceProvider.shared.authentications {
|
||||
guard let user = authentication.user(in: managedObjectContext) else { continue }
|
||||
let user = authentication.user
|
||||
let accessToken = authentication.userAccessToken
|
||||
let count = UserDefaults.shared.getNotificationCountWithAccessToken(accessToken: accessToken)
|
||||
guard count > 0 else { continue }
|
||||
|
||||
let title = "@\(user.acctWithDomain)"
|
||||
let title = "@\(user.acctWithDomainIfMissing(authentication.domain))"
|
||||
let subtitle = L10n.A11y.Plural.Count.Unread.notification(count)
|
||||
|
||||
let item = UIApplicationShortcutItem(
|
||||
|
@ -61,8 +61,8 @@ extension ComposeContentViewModel {
|
||||
|
||||
// configure status
|
||||
context.managedObjectContext.performAndWait {
|
||||
guard let replyTo = status.object(in: context.managedObjectContext) else { return }
|
||||
cell.statusView.configure(status: replyTo)
|
||||
|
||||
cell.statusView.configure(status: status)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,6 @@
|
||||
|
||||
import UIKit
|
||||
import Combine
|
||||
import CoreDataStack
|
||||
import Meta
|
||||
import MetaTextKit
|
||||
import MastodonMeta
|
||||
@ -23,7 +22,7 @@ public final class ComposeContentViewModel: NSObject, ObservableObject {
|
||||
|
||||
public enum ComposeContext {
|
||||
case composeStatus
|
||||
case editStatus(status: Status, statusSource: Mastodon.Entity.StatusSource)
|
||||
case editStatus(status: Mastodon.Entity.Status, statusSource: Mastodon.Entity.StatusSource)
|
||||
}
|
||||
|
||||
var disposeBag = Set<AnyCancellable>()
|
||||
@ -156,31 +155,25 @@ public final class ComposeContentViewModel: NSObject, ObservableObject {
|
||||
self.visibility = {
|
||||
// default private when user locked
|
||||
var visibility: Mastodon.Entity.Status.Visibility = {
|
||||
guard let author = authContext.mastodonAuthenticationBox.authentication.user(in: context.managedObjectContext) else {
|
||||
return .public
|
||||
}
|
||||
let author = authContext.mastodonAuthenticationBox.authentication.user
|
||||
return author.locked ? .private : .public
|
||||
}()
|
||||
// set visibility for reply post
|
||||
if case .reply(let record) = destination {
|
||||
context.managedObjectContext.performAndWait {
|
||||
guard let status = record.object(in: context.managedObjectContext) else {
|
||||
assertionFailure()
|
||||
return
|
||||
}
|
||||
let repliedStatusVisibility = status.visibility
|
||||
switch repliedStatusVisibility {
|
||||
case .public, .unlisted:
|
||||
// keep default
|
||||
break
|
||||
case .private:
|
||||
visibility = .private
|
||||
case .direct:
|
||||
visibility = .direct
|
||||
case ._other:
|
||||
assertionFailure()
|
||||
break
|
||||
}
|
||||
if case .reply(let status) = destination {
|
||||
let repliedStatusVisibility = status.visibility
|
||||
switch repliedStatusVisibility {
|
||||
case .public, .unlisted:
|
||||
// keep default
|
||||
break
|
||||
case .private:
|
||||
visibility = .private
|
||||
case .direct:
|
||||
visibility = .direct
|
||||
case ._other:
|
||||
assertionFailure()
|
||||
break
|
||||
case .none:
|
||||
break
|
||||
}
|
||||
}
|
||||
return visibility
|
||||
@ -191,7 +184,7 @@ public final class ComposeContentViewModel: NSObject, ObservableObject {
|
||||
)
|
||||
|
||||
if case let ComposeContext.editStatus(status, _) = composeContext {
|
||||
if status.isContentSensitive {
|
||||
if status.sensitive == true {
|
||||
isContentWarningActive = true
|
||||
contentWarning = status.spoilerText ?? ""
|
||||
}
|
||||
@ -201,7 +194,7 @@ public final class ComposeContentViewModel: NSObject, ObservableObject {
|
||||
if let pollExpiresAt = poll.expiresAt {
|
||||
pollExpireConfigurationOption = .init(closestDateToExpiry: pollExpiresAt)
|
||||
}
|
||||
pollOptions = poll.options.sortedByIndex().map {
|
||||
pollOptions = poll.options.map {
|
||||
let option = PollComposeItem.Option()
|
||||
option.text = $0.title
|
||||
return option
|
||||
@ -218,52 +211,40 @@ public final class ComposeContentViewModel: NSObject, ObservableObject {
|
||||
// setup initial value
|
||||
let initialContentWithSpace = initialContent.isEmpty ? "" : initialContent + " "
|
||||
switch destination {
|
||||
case .reply(let record):
|
||||
context.managedObjectContext.performAndWait {
|
||||
guard let status = record.object(in: context.managedObjectContext) else {
|
||||
assertionFailure()
|
||||
return
|
||||
}
|
||||
let author = authContext.mastodonAuthenticationBox.authentication.user(in: context.managedObjectContext)
|
||||
case .reply(let status):
|
||||
let author = authContext.mastodonAuthenticationBox.authentication.user
|
||||
|
||||
var mentionAccts: [String] = []
|
||||
if author?.id != status.author.id {
|
||||
mentionAccts.append("@" + status.author.acct)
|
||||
}
|
||||
let mentions = status.mentions
|
||||
.filter { author?.id != $0.id }
|
||||
for mention in mentions {
|
||||
let acct = "@" + mention.acct
|
||||
guard !mentionAccts.contains(acct) else { continue }
|
||||
mentionAccts.append(acct)
|
||||
}
|
||||
for acct in mentionAccts {
|
||||
UITextChecker.learnWord(acct)
|
||||
}
|
||||
if let spoilerText = status.spoilerText, !spoilerText.isEmpty {
|
||||
self.isContentWarningActive = true
|
||||
self.contentWarning = spoilerText
|
||||
}
|
||||
|
||||
let initialComposeContent = mentionAccts.joined(separator: " ")
|
||||
let preInsertedContent = initialComposeContent.isEmpty ? "" : initialComposeContent + " "
|
||||
self.initialContent = preInsertedContent + initialContentWithSpace
|
||||
self.content = preInsertedContent + initialContentWithSpace
|
||||
var mentionAccts: [String] = []
|
||||
if author.id != status.account.id {
|
||||
mentionAccts.append("@" + status.account.acct)
|
||||
}
|
||||
let mentions = status.mentions?
|
||||
.filter { author.id != $0.id } ?? []
|
||||
for mention in mentions {
|
||||
let acct = "@" + mention.acct
|
||||
guard !mentionAccts.contains(acct) else { continue }
|
||||
mentionAccts.append(acct)
|
||||
}
|
||||
for acct in mentionAccts {
|
||||
UITextChecker.learnWord(acct)
|
||||
}
|
||||
if let spoilerText = status.spoilerText, !spoilerText.isEmpty {
|
||||
self.isContentWarningActive = true
|
||||
self.contentWarning = spoilerText
|
||||
}
|
||||
|
||||
let initialComposeContent = mentionAccts.joined(separator: " ")
|
||||
let preInsertedContent = initialComposeContent.isEmpty ? "" : initialComposeContent + " "
|
||||
self.initialContent = preInsertedContent + initialContentWithSpace
|
||||
self.content = preInsertedContent + initialContentWithSpace
|
||||
case .topLevel:
|
||||
self.initialContent = initialContentWithSpace
|
||||
self.content = initialContentWithSpace
|
||||
}
|
||||
|
||||
// set limit
|
||||
let _configuration: Mastodon.Entity.Instance.Configuration? = {
|
||||
var configuration: Mastodon.Entity.Instance.Configuration? = nil
|
||||
context.managedObjectContext.performAndWait {
|
||||
let authentication = authContext.mastodonAuthenticationBox.authentication
|
||||
configuration = authentication.instance(in: context.managedObjectContext)?.configuration
|
||||
}
|
||||
return configuration
|
||||
}()
|
||||
let _configuration: Mastodon.Entity.Instance.Configuration? = authContext.mastodonAuthenticationBox.authentication.instance?.configuration
|
||||
|
||||
if let configuration = _configuration {
|
||||
// set character limit
|
||||
if let maxCharacters = configuration.statuses?.maxCharacters {
|
||||
@ -288,22 +269,22 @@ public final class ComposeContentViewModel: NSObject, ObservableObject {
|
||||
case .composeStatus:
|
||||
self.isVisibilityButtonEnabled = true
|
||||
case let .editStatus(status, _):
|
||||
if let visibility = Mastodon.Entity.Status.Visibility(rawValue: status.visibility.rawValue) {
|
||||
if let visibility = status.visibility {
|
||||
self.visibility = visibility
|
||||
}
|
||||
self.isVisibilityButtonEnabled = false
|
||||
self.attachmentViewModels = status.attachments.compactMap {
|
||||
guard let assetURL = $0.assetURL, let url = URL(string: assetURL) else { return nil }
|
||||
self.attachmentViewModels = status.mediaAttachments?.compactMap { attachment -> AttachmentViewModel? in
|
||||
guard let assetURL = attachment.url, let url = URL(string: assetURL) else { return nil }
|
||||
let attachmentViewModel = AttachmentViewModel(
|
||||
api: context.apiService,
|
||||
authContext: authContext,
|
||||
input: .mastodonAssetUrl(url, $0.id),
|
||||
input: .mastodonAssetUrl(url, attachment.id),
|
||||
sizeLimit: sizeLimit,
|
||||
delegate: self
|
||||
)
|
||||
attachmentViewModel.caption = $0.altDescription ?? ""
|
||||
attachmentViewModel.caption = attachment.description ?? ""
|
||||
return attachmentViewModel
|
||||
}
|
||||
} ?? []
|
||||
}
|
||||
|
||||
bind()
|
||||
@ -318,10 +299,10 @@ extension ComposeContentViewModel {
|
||||
$authContext
|
||||
.sink { [weak self] authContext in
|
||||
guard let self = self else { return }
|
||||
guard let user = authContext.mastodonAuthenticationBox.authentication.user(in: self.context.managedObjectContext) else { return }
|
||||
let user = authContext.mastodonAuthenticationBox.authentication.user
|
||||
self.avatarURL = user.avatarImageURL()
|
||||
self.name = user.nameMetaContent ?? PlaintextMetaContent(string: user.displayNameWithFallback)
|
||||
self.username = user.acctWithDomain
|
||||
self.username = user.acctWithDomainIfMissing(authContext.mastodonAuthenticationBox.domain)
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
|
||||
@ -503,7 +484,7 @@ extension ComposeContentViewModel {
|
||||
extension ComposeContentViewModel {
|
||||
public enum Destination {
|
||||
case topLevel
|
||||
case reply(parent: ManagedObjectRecord<Status>)
|
||||
case reply(parent: Mastodon.Entity.Status)
|
||||
}
|
||||
|
||||
public enum ScrollViewState {
|
||||
@ -562,10 +543,8 @@ extension ComposeContentViewModel {
|
||||
|
||||
// author
|
||||
let managedObjectContext = self.context.managedObjectContext
|
||||
var _author: ManagedObjectRecord<MastodonUser>?
|
||||
managedObjectContext.performAndWait {
|
||||
_author = authContext.mastodonAuthenticationBox.authentication.user(in: managedObjectContext)?.asRecord
|
||||
}
|
||||
var _author: Mastodon.Entity.Account? = authContext.mastodonAuthenticationBox.authentication.user
|
||||
|
||||
guard let author = _author else {
|
||||
throw AppError.badAuthentication
|
||||
}
|
||||
@ -618,13 +597,7 @@ extension ComposeContentViewModel {
|
||||
|
||||
// author
|
||||
let managedObjectContext = self.context.managedObjectContext
|
||||
var _author: ManagedObjectRecord<MastodonUser>?
|
||||
managedObjectContext.performAndWait {
|
||||
_author = authContext.mastodonAuthenticationBox.authentication.user(in: managedObjectContext)?.asRecord
|
||||
}
|
||||
guard let author = _author else {
|
||||
throw AppError.badAuthentication
|
||||
}
|
||||
var _author = authContext.mastodonAuthenticationBox.authentication.user
|
||||
|
||||
// poll
|
||||
_ = try {
|
||||
@ -645,7 +618,7 @@ extension ComposeContentViewModel {
|
||||
}
|
||||
|
||||
return MastodonEditStatusPublisher(statusID: status.id,
|
||||
author: author,
|
||||
author: _author,
|
||||
isContentWarningComposing: isContentWarningActive,
|
||||
contentWarning: contentWarning,
|
||||
content: content,
|
||||
@ -817,3 +790,27 @@ extension ComposeContentViewModel: AttachmentViewModelDelegate {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Mastodon.Entity.Account {
|
||||
public var nameMetaContent: MastodonMetaContent? {
|
||||
do {
|
||||
let content = MastodonContent(content: displayNameWithFallback, emojis: emojis?.asDictionary ?? [:])
|
||||
let metaContent = try MastodonMetaContent.convert(document: content)
|
||||
return metaContent
|
||||
} catch {
|
||||
assertionFailure()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
public var bioMetaContent: MastodonMetaContent? {
|
||||
do {
|
||||
let content = MastodonContent(content: note, emojis: emojis?.asDictionary ?? [:])
|
||||
let metaContent = try MastodonMetaContent.convert(document: content)
|
||||
return metaContent
|
||||
} catch {
|
||||
assertionFailure()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
// Copyright © 2023 Mastodon gGmbH. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
import CoreDataStack
|
||||
import MastodonCore
|
||||
import MastodonSDK
|
||||
import Combine
|
||||
@ -10,8 +8,8 @@ import Combine
|
||||
public final class MastodonEditStatusPublisher: NSObject, ProgressReporting {
|
||||
|
||||
// Input
|
||||
public let statusID: Status.ID
|
||||
public let author: ManagedObjectRecord<MastodonUser>
|
||||
public let statusID: Mastodon.Entity.Status.ID
|
||||
public let author: Mastodon.Entity.Account
|
||||
|
||||
// content warning
|
||||
public let isContentWarningComposing: Bool
|
||||
@ -40,8 +38,8 @@ public final class MastodonEditStatusPublisher: NSObject, ProgressReporting {
|
||||
public var reactor: StatusPublisherReactor?
|
||||
|
||||
public init(
|
||||
statusID: Status.ID,
|
||||
author: ManagedObjectRecord<MastodonUser>,
|
||||
statusID: Mastodon.Entity.Status.ID,
|
||||
author: Mastodon.Entity.Account,
|
||||
isContentWarningComposing: Bool,
|
||||
contentWarning: String,
|
||||
content: String,
|
||||
|
@ -7,8 +7,6 @@
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
import CoreData
|
||||
import CoreDataStack
|
||||
import MastodonCore
|
||||
import MastodonSDK
|
||||
|
||||
@ -17,9 +15,9 @@ public final class MastodonStatusPublisher: NSObject, ProgressReporting {
|
||||
// Input
|
||||
|
||||
// author
|
||||
public let author: ManagedObjectRecord<MastodonUser>
|
||||
public let author: Mastodon.Entity.Account
|
||||
// refer
|
||||
public let replyTo: ManagedObjectRecord<Status>?
|
||||
public let replyTo: Mastodon.Entity.Status?
|
||||
// content warning
|
||||
public let isContentWarningComposing: Bool
|
||||
public let contentWarning: String
|
||||
@ -47,8 +45,8 @@ public final class MastodonStatusPublisher: NSObject, ProgressReporting {
|
||||
public var reactor: StatusPublisherReactor?
|
||||
|
||||
public init(
|
||||
author: ManagedObjectRecord<MastodonUser>,
|
||||
replyTo: ManagedObjectRecord<Status>?,
|
||||
author: Mastodon.Entity.Account,
|
||||
replyTo: Mastodon.Entity.Status?,
|
||||
isContentWarningComposing: Bool,
|
||||
contentWarning: String,
|
||||
content: String,
|
||||
@ -161,10 +159,7 @@ extension MastodonStatusPublisher: StatusPublisher {
|
||||
guard pollOptions != nil else { return nil }
|
||||
return self.pollExpireConfigurationOption.seconds
|
||||
}()
|
||||
let inReplyToID: Mastodon.Entity.Status.ID? = try await api.backgroundManagedObjectContext.perform {
|
||||
guard let replyTo = self.replyTo?.object(in: api.backgroundManagedObjectContext) else { return nil }
|
||||
return replyTo.id
|
||||
}
|
||||
let inReplyToID: Mastodon.Entity.Status.ID? = self.replyTo?.id
|
||||
|
||||
let query = Mastodon.API.Statuses.PublishStatusQuery(
|
||||
status: content,
|
||||
|
@ -225,23 +225,10 @@ extension NotificationView.ViewModel {
|
||||
notificationView.menuButton.menu = nil
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
let (isMyself, isTranslated, isFollowed) = isMyselfIsTranslatedIsFollowed
|
||||
|
||||
lazy var instanceConfigurationV2: Mastodon.Entity.V2.Instance.Configuration? = {
|
||||
guard
|
||||
let self = self,
|
||||
let context = self.context,
|
||||
let authContext = self.authContext
|
||||
else { return nil }
|
||||
|
||||
var configuration: Mastodon.Entity.V2.Instance.Configuration? = nil
|
||||
context.managedObjectContext.performAndWait {
|
||||
let authentication = authContext.mastodonAuthenticationBox.authentication
|
||||
configuration = authentication.instance(in: context.managedObjectContext)?.configurationV2
|
||||
}
|
||||
return configuration
|
||||
}()
|
||||
lazy var instanceConfigurationV2: Mastodon.Entity.V2.Instance.Configuration? = self?.authContext?.mastodonAuthenticationBox.authentication.instanceV2?.configuration
|
||||
|
||||
let menuContext = NotificationView.AuthorMenuContext(
|
||||
name: name,
|
||||
|
@ -7,7 +7,6 @@
|
||||
|
||||
import UIKit
|
||||
import Combine
|
||||
import CoreDataStack
|
||||
import MastodonSDK
|
||||
import MastodonCore
|
||||
import MastodonLocalization
|
||||
@ -18,29 +17,11 @@ import NaturalLanguage
|
||||
extension StatusView {
|
||||
|
||||
static let statusFilterWorkingQueue = DispatchQueue(label: "StatusFilterWorkingQueue")
|
||||
|
||||
public func configure(feed: Feed) {
|
||||
switch feed.kind {
|
||||
case .home:
|
||||
guard let status = feed.status else {
|
||||
assertionFailure()
|
||||
return
|
||||
}
|
||||
configure(status: status)
|
||||
case .notificationAll:
|
||||
assertionFailure("TODO")
|
||||
case .notificationMentions:
|
||||
assertionFailure("TODO")
|
||||
case .none:
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
extension StatusView {
|
||||
|
||||
public func configure(status: Status, statusEdit: StatusEdit) {
|
||||
public func configure(status: Mastodon.Entity.Status, statusEdit: Mastodon.Entity.StatusEdit) {
|
||||
viewModel.objects.insert(status)
|
||||
if let reblog = status.reblog {
|
||||
viewModel.objects.insert(reblog)
|
||||
@ -66,7 +47,7 @@ extension StatusView {
|
||||
viewModel.isContentReveal = true
|
||||
}
|
||||
|
||||
public func configure(status: Status) {
|
||||
public func configure(status: Mastodon.Entity.Status) {
|
||||
viewModel.objects.insert(status)
|
||||
if let reblog = status.reblog {
|
||||
viewModel.objects.insert(reblog)
|
||||
@ -99,7 +80,7 @@ extension StatusView {
|
||||
}
|
||||
|
||||
extension StatusView {
|
||||
private func configureHeader(status: Status) {
|
||||
private func configureHeader(status: Mastodon.Entity.Status) {
|
||||
if let _ = status.reblog {
|
||||
Publishers.CombineLatest(
|
||||
status.author.publisher(for: \.displayName),
|
||||
@ -189,7 +170,7 @@ extension StatusView {
|
||||
}
|
||||
}
|
||||
|
||||
public func configureAuthor(author: MastodonUser) {
|
||||
public func configureAuthor(author: Mastodon.Entity.Account) {
|
||||
// author avatar
|
||||
Publishers.CombineLatest(
|
||||
author.publisher(for: \.avatar),
|
||||
@ -300,7 +281,7 @@ extension StatusView {
|
||||
configure(status: originalStatus)
|
||||
}
|
||||
|
||||
func configureTranslated(status: Status) {
|
||||
func configureTranslated(status: Mastodon.Entity.Status) {
|
||||
let translatedContent: Status.TranslatedContent? = {
|
||||
if let translatedContent = status.reblog?.translatedContent {
|
||||
return translatedContent
|
||||
@ -330,7 +311,7 @@ extension StatusView {
|
||||
}
|
||||
}
|
||||
|
||||
private func configureContent(statusEdit: StatusEdit, status: Status) {
|
||||
private func configureContent(statusEdit: Mastodon.Entity.StatusEdit, status: Mastodon.Entity.Status) {
|
||||
statusEdit.spoilerText.map {
|
||||
viewModel.spoilerContent = PlaintextMetaContent(string: $0)
|
||||
}
|
||||
@ -350,7 +331,7 @@ extension StatusView {
|
||||
}
|
||||
}
|
||||
|
||||
private func configureContent(status: Status) {
|
||||
private func configureContent(status: Mastodon.Entity.Status) {
|
||||
guard status.translatedContent == nil else {
|
||||
return configureTranslated(status: status)
|
||||
}
|
||||
@ -404,7 +385,7 @@ extension StatusView {
|
||||
viewModel.mediaViewConfigurations = configurations
|
||||
}
|
||||
|
||||
private func configurePollHistory(statusEdit: StatusEdit) {
|
||||
private func configurePollHistory(statusEdit: Mastodon.Entity.StatusEdit) {
|
||||
guard let poll = statusEdit.poll else { return }
|
||||
|
||||
let pollItems = poll.options.map { PollItem.history(option: $0) }
|
||||
@ -417,7 +398,7 @@ extension StatusView {
|
||||
pollTableViewDiffableDataSource?.applySnapshotUsingReloadData(_snapshot)
|
||||
}
|
||||
|
||||
private func configurePoll(status: Status) {
|
||||
private func configurePoll(status: Mastodon.Entity.Status) {
|
||||
let status = status.reblog ?? status
|
||||
|
||||
if let poll = status.poll {
|
||||
@ -488,7 +469,7 @@ extension StatusView {
|
||||
.store(in: &disposeBag)
|
||||
}
|
||||
|
||||
private func configureCard(status: Status) {
|
||||
private func configureCard(status: Mastodon.Entity.Status) {
|
||||
let status = status.reblog ?? status
|
||||
if viewModel.mediaViewConfigurations.isEmpty {
|
||||
status.publisher(for: \.card)
|
||||
@ -499,7 +480,7 @@ extension StatusView {
|
||||
}
|
||||
}
|
||||
|
||||
private func configureToolbar(status: Status) {
|
||||
private func configureToolbar(status: Mastodon.Entity.Status) {
|
||||
let status = status.reblog ?? status
|
||||
|
||||
status.publisher(for: \.repliesCount)
|
||||
@ -560,7 +541,7 @@ extension StatusView {
|
||||
.store(in: &disposeBag)
|
||||
}
|
||||
|
||||
private func configureFilter(status: Status) {
|
||||
private func configureFilter(status: Mastodon.Entity.Status) {
|
||||
let status = status.reblog ?? status
|
||||
|
||||
let content = status.content.lowercased()
|
||||
|
@ -671,7 +671,7 @@ extension StatusView.ViewModel {
|
||||
publishersTwo.eraseToAnyPublisher(),
|
||||
publishersThree.eraseToAnyPublisher()
|
||||
).eraseToAnyPublisher()
|
||||
.sink { tupleOne, tupleTwo, tupleThree in
|
||||
.sink { [weak self] tupleOne, tupleTwo, tupleThree in
|
||||
let (authorName, isMyself) = tupleOne
|
||||
let (isMuting, isBlocking, isBookmark, isFollowed) = tupleTwo
|
||||
let (translatedFromLanguage, language) = tupleThree
|
||||
@ -681,22 +681,9 @@ extension StatusView.ViewModel {
|
||||
return
|
||||
}
|
||||
|
||||
lazy var instanceConfigurationV2: Mastodon.Entity.V2.Instance.Configuration? = {
|
||||
guard
|
||||
let context = self.context,
|
||||
let authContext = self.authContext
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
|
||||
var configuration: Mastodon.Entity.V2.Instance.Configuration? = nil
|
||||
context.managedObjectContext.performAndWait {
|
||||
let authentication = authContext.mastodonAuthenticationBox.authentication
|
||||
configuration = authentication.instance(in: context.managedObjectContext)?.configurationV2
|
||||
}
|
||||
return configuration
|
||||
}()
|
||||
|
||||
lazy var instanceConfigurationV2: Mastodon.Entity.V2.Instance.Configuration? =
|
||||
self?.authContext?.mastodonAuthenticationBox.authentication.instanceV2?.configuration
|
||||
|
||||
let menuContext = StatusAuthorView.AuthorMenuContext(
|
||||
name: name,
|
||||
isMuting: isMuting,
|
||||
|
@ -85,13 +85,7 @@ private extension FollowersCountWidgetProvider {
|
||||
return completion(.unconfigured)
|
||||
}
|
||||
|
||||
guard
|
||||
let desiredAccount = configuration.account ?? authBox.authentication.user(
|
||||
in: WidgetExtension.appContext.managedObjectContext
|
||||
)?.acctWithDomain
|
||||
else {
|
||||
return completion(.unconfigured)
|
||||
}
|
||||
let desiredAccount = configuration.account ?? authBox.authentication.user.acctWithDomainIfMissing(authBox.domain)
|
||||
|
||||
guard
|
||||
let resultingAccount = try await WidgetExtension.appContext
|
||||
|
@ -86,12 +86,9 @@ private extension MultiFollowersCountWidgetProvider {
|
||||
|
||||
if let configuredAccounts = configuration.accounts?.compactMap({ $0 }) {
|
||||
desiredAccounts = configuredAccounts
|
||||
} else if let currentlyLoggedInAccount = authBox.authentication.user(
|
||||
in: WidgetExtension.appContext.managedObjectContext
|
||||
)?.acctWithDomain {
|
||||
desiredAccounts = [currentlyLoggedInAccount]
|
||||
} else {
|
||||
return completion(.unconfigured)
|
||||
let currentlyLoggedInAccount = authBox.authentication.user.acctWithDomainIfMissing(authBox.domain)
|
||||
desiredAccounts = [currentlyLoggedInAccount]
|
||||
}
|
||||
|
||||
var accounts = [MultiFollowersEntryAccountable]()
|
||||
|
Loading…
x
Reference in New Issue
Block a user