2021-02-08 11:29:27 +01:00
//
// A P I S e r v i c e + F a v o r i t e . s w i f t
// M a s t o d o n
//
// C r e a t e d b y s x i a o j i a n o n 2 0 2 1 / 2 / 8 .
//
import Foundation
import Combine
import MastodonSDK
import CoreData
import CoreDataStack
import CommonOSLog
extension APIService {
2022-01-27 14:23:39 +01:00
private struct MastodonFavoriteContext {
let statusID : Status . ID
let isFavorited : Bool
let favoritedCount : Int64
}
2021-04-07 08:24:28 +02:00
func favorite (
2022-01-27 14:23:39 +01:00
record : ManagedObjectRecord < Status > ,
authenticationBox : MastodonAuthenticationBox
) async throws -> Mastodon . Response . Content < Mastodon . Entity . Status > {
let logger = Logger ( subsystem : " APIService " , category : " Favorite " )
2021-02-08 11:29:27 +01:00
let managedObjectContext = backgroundManagedObjectContext
2022-01-27 14:23:39 +01:00
// u p d a t e l i k e s t a t e a n d r e t r i e v e l i k e c o n t e x t
let favoriteContext : MastodonFavoriteContext = try await managedObjectContext . performChanges {
guard let authentication = authenticationBox . authenticationRecord . object ( in : managedObjectContext ) ,
let _status = record . object ( in : managedObjectContext )
else {
throw APIError . implicit ( . badRequest )
2021-06-17 10:44:57 +02:00
}
2022-01-27 14:23:39 +01:00
let me = authentication . user
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
)
logger . log ( level : . debug , " \( ( #file as NSString ) . lastPathComponent , privacy : . public ) [ \( #line , privacy : . public ) ], \( #function , privacy : . public ) : update status favorite: \( ! isFavorited ) , \( favoriteCount ) " )
return context
}
2021-02-08 11:29:27 +01:00
2022-01-27 14:23:39 +01:00
// r e q u e s t l i k e o r u n d o l i k e
let result : Result < Mastodon . Response . Content < Mastodon . Entity . Status > , Error >
do {
let response = try await Mastodon . API . Favorites . favorites (
domain : authenticationBox . domain ,
statusID : favoriteContext . statusID ,
session : session ,
authorization : authenticationBox . userAuthorization ,
favoriteKind : favoriteContext . isFavorited ? . destroy : . create
) . singleOutput ( )
result = . success ( response )
} catch {
result = . failure ( error )
logger . log ( level : . debug , " \( ( #file as NSString ) . lastPathComponent , privacy : . public ) [ \( #line , privacy : . public ) ], \( #function , privacy : . public ) : update favorite failure: \( error . localizedDescription ) " )
2021-02-08 11:29:27 +01:00
}
2022-01-27 14:23:39 +01:00
// u p d a t e l i k e s t a t e
try await managedObjectContext . performChanges {
guard let authentication = authenticationBox . authenticationRecord . object ( in : managedObjectContext ) ,
let _status = record . object ( in : managedObjectContext )
else { return }
let me = authentication . user
let status = _status . reblog ? ? _status
2021-02-08 11:29:27 +01:00
switch result {
2022-01-27 14:23:39 +01:00
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 ) ) // u n d o A P I r e t u r n c o u n t h a s d e l a y . N e e d s - 1 l o c a l
2021-02-08 11:29:27 +01:00
}
2022-01-27 14:23:39 +01:00
logger . log ( level : . debug , " \( ( #file as NSString ) . lastPathComponent , privacy : . public ) [ \( #line , privacy : . public ) ], \( #function , privacy : . public ) : update status favorite: \( response . value . favourited . debugDescription ) " )
case . failure :
// r o l l b a c k
status . update ( liked : favoriteContext . isFavorited , by : me )
status . update ( favouritesCount : favoriteContext . favoritedCount )
logger . log ( level : . debug , " \( ( #file as NSString ) . lastPathComponent , privacy : . public ) [ \( #line , privacy : . public ) ], \( #function , privacy : . public ) : rollback status favorite " )
2021-02-08 11:29:27 +01:00
}
}
2022-01-27 14:23:39 +01:00
let response = try result . get ( )
return response
2021-02-08 11:29:27 +01:00
}
}
extension APIService {
2021-04-07 08:24:28 +02:00
func favoritedStatuses (
2021-04-01 08:39:15 +02:00
limit : Int = onceRequestStatusMaxCount ,
2021-02-08 11:29:27 +01:00
maxID : String ? = nil ,
2022-01-27 14:23:39 +01:00
authenticationBox : MastodonAuthenticationBox
) async throws -> Mastodon . Response . Content < [ Mastodon . Entity . Status ] > {
2021-04-07 08:24:28 +02:00
let query = Mastodon . API . Favorites . FavoriteStatusesQuery ( limit : limit , minID : nil , maxID : maxID )
2022-01-27 14:23:39 +01:00
let response = try await Mastodon . API . Favorites . favoritedStatus (
domain : authenticationBox . domain ,
2021-04-07 08:24:28 +02:00
session : session ,
2022-01-27 14:23:39 +01:00
authorization : authenticationBox . userAuthorization ,
2021-04-07 08:24:28 +02:00
query : query
2022-01-27 14:23:39 +01:00
) . singleOutput ( )
let managedObjectContext = self . backgroundManagedObjectContext
try await managedObjectContext . performChanges {
guard let me = authenticationBox . authenticationRecord . object ( in : managedObjectContext ) ? . user else {
assertionFailure ( )
return
2021-02-08 11:29:27 +01:00
}
2022-01-27 14:23:39 +01:00
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 )
} // e n d f o r … i n
}
return response
} // e n d f u n c
2021-02-08 11:29:27 +01:00
}