Kurdtvs-Live-Kurdish-TV-Kur.../Mastodon/Service/APIService/APIService+Follow.swift

126 lines
5.6 KiB
Swift
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// APIService+Follow.swift
// Mastodon
//
// Created by MainasuK Cirno on 2021-4-2.
//
import UIKit
import Combine
import CoreData
import CoreDataStack
import CommonOSLog
import MastodonSDK
extension APIService {
private struct MastodonFollowContext {
let sourceUserID: MastodonUser.ID
let targetUserID: MastodonUser.ID
let isFollowing: Bool
let isPending: Bool
let needsUnfollow: Bool
}
/// Toggle friendship between target MastodonUser and current MastodonUser
///
/// Following / Following pending <-> Unfollow
///
/// - Parameters:
/// - mastodonUser: target MastodonUser
/// - activeMastodonAuthenticationBox: `AuthenticationService.MastodonAuthenticationBox`
/// - Returns: publisher for `Relationship`
func toggleFollow(
user: ManagedObjectRecord<MastodonUser>,
authenticationBox: MastodonAuthenticationBox
) async throws -> Mastodon.Response.Content<Mastodon.Entity.Relationship> {
let logger = Logger(subsystem: "APIService", category: "Follow")
let managedObjectContext = backgroundManagedObjectContext
let _followContext: MastodonFollowContext? = try await managedObjectContext.performChanges {
guard let me = authenticationBox.authenticationRecord.object(in: managedObjectContext)?.user 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)
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [Local] update user friendship: undo follow")
} else {
// follow
if user.locked {
user.update(isFollowing: false, by: me)
user.update(isFollowRequested: true, by: me)
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [Local] update user friendship: pending follow")
} else {
user.update(isFollowing: true, by: me)
user.update(isFollowRequested: false, by: me)
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [Local] update user friendship: following")
}
}
let context = MastodonFollowContext(
sourceUserID: me.id,
targetUserID: user.id,
isFollowing: isFollowing,
isPending: isPending,
needsUnfollow: needsUnfollow
)
return context
}
guard let followContext = _followContext else {
throw APIError.implicit(.badRequest)
}
// request follow or unfollow
let result: Result<Mastodon.Response.Content<Mastodon.Entity.Relationship>, Error>
do {
let response = try await Mastodon.API.Account.follow(
session: session,
domain: authenticationBox.domain,
accountID: followContext.targetUserID,
followQueryType: followContext.needsUnfollow ? .unfollow : .follow(query: .init()),
authorization: authenticationBox.userAuthorization
).singleOutput()
result = .success(response)
} catch {
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [Remote] update friendship failure: \(error.localizedDescription)")
result = .failure(error)
}
// update friendship state
try await managedObjectContext.performChanges {
guard let me = authenticationBox.authenticationRecord.object(in: managedObjectContext)?.user,
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
)
)
let following = response.value.following
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [Remote] update user friendship: following \(following)")
case .failure:
// rollback
user.update(isFollowing: followContext.isFollowing, by: me)
user.update(isFollowRequested: followContext.isPending, by: me)
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [Remote] rollback user friendship")
}
}
let response = try result.get()
return response
}
}