2021-02-08 11:29:27 +01:00
//
// S t a t u s P r o v i d e r F a c a d 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 os . log
import UIKit
import Combine
import CoreData
import CoreDataStack
import MastodonSDK
import ActiveLabel
enum StatusProviderFacade {
}
2021-03-09 08:18:43 +01:00
2021-02-08 11:29:27 +01:00
extension StatusProviderFacade {
static func responseToStatusLikeAction ( provider : StatusProvider ) {
_responseToStatusLikeAction (
provider : provider ,
toot : provider . toot ( )
)
}
static func responseToStatusLikeAction ( provider : StatusProvider , cell : UITableViewCell ) {
_responseToStatusLikeAction (
provider : provider ,
toot : provider . toot ( for : cell , indexPath : nil )
)
}
private static func _responseToStatusLikeAction ( provider : StatusProvider , toot : Future < Toot ? , Never > ) {
// p r e p a r e a u t h e n t i c a t i o n
guard let activeMastodonAuthenticationBox = provider . context . authenticationService . activeMastodonAuthenticationBox . value else {
assertionFailure ( )
return
}
// p r e p a r e c u r r e n t u s e r i n f o s
guard let _currentMastodonUser = provider . context . authenticationService . activeMastodonAuthentication . value ? . user else {
assertionFailure ( )
return
}
let mastodonUserID = activeMastodonAuthenticationBox . userID
assert ( _currentMastodonUser . id = = mastodonUserID )
let mastodonUserObjectID = _currentMastodonUser . objectID
guard let context = provider . context else { return }
// h a p t i c f e e d b a c k g e n e r a t o r
let generator = UIImpactFeedbackGenerator ( style : . light )
let responseFeedbackGenerator = UIImpactFeedbackGenerator ( style : . medium )
toot
. compactMap { toot -> ( NSManagedObjectID , Mastodon . API . Favorites . FavoriteKind ) ? in
2021-03-09 08:18:43 +01:00
guard let toot = toot ? . reblog ? ? toot else { return nil }
2021-02-08 11:29:27 +01:00
let favoriteKind : Mastodon . API . Favorites . FavoriteKind = {
2021-03-09 08:18:43 +01:00
let isLiked = toot . favouritedBy . flatMap { $0 . contains ( where : { $0 . id = = mastodonUserID } ) } ? ? false
2021-02-08 11:29:27 +01:00
return isLiked ? . destroy : . create
} ( )
return ( toot . objectID , favoriteKind )
}
. map { tootObjectID , favoriteKind -> AnyPublisher < ( Toot . ID , Mastodon . API . Favorites . FavoriteKind ) , Error > in
return context . apiService . like (
tootObjectID : tootObjectID ,
mastodonUserObjectID : mastodonUserObjectID ,
favoriteKind : favoriteKind
)
. map { tootID in ( tootID , favoriteKind ) }
. eraseToAnyPublisher ( )
}
. setFailureType ( to : Error . self )
. eraseToAnyPublisher ( )
. switchToLatest ( )
. receive ( on : DispatchQueue . main )
. handleEvents { _ in
generator . prepare ( )
responseFeedbackGenerator . prepare ( )
} receiveOutput : { _ , favoriteKind in
generator . impactOccurred ( )
os_log ( " %{public}s[%{public}ld], %{public}s: [Like] update local toot like status to: %s " , ( ( #file as NSString ) . lastPathComponent ) , #line , #function , favoriteKind = = . create ? " like " : " unlike " )
} receiveCompletion : { completion in
switch completion {
2021-02-24 11:40:47 +01:00
case . failure :
2021-02-08 11:29:27 +01:00
// TODO: h a n d l e e r r o r
break
case . finished :
break
}
}
. map { tootID , favoriteKind in
return context . apiService . like (
statusID : tootID ,
favoriteKind : favoriteKind ,
mastodonAuthenticationBox : activeMastodonAuthenticationBox
)
}
. switchToLatest ( )
. receive ( on : DispatchQueue . main )
. sink { [ weak provider ] completion in
guard let provider = provider else { return }
if provider . view . window != nil {
responseFeedbackGenerator . impactOccurred ( )
}
switch completion {
case . failure ( let error ) :
os_log ( " %{public}s[%{public}ld], %{public}s: [Like] remote like request fail: %{public}s " , ( ( #file as NSString ) . lastPathComponent ) , #line , #function , error . localizedDescription )
case . finished :
os_log ( " %{public}s[%{public}ld], %{public}s: [Like] remote like request success " , ( ( #file as NSString ) . lastPathComponent ) , #line , #function )
}
} receiveValue : { response in
// d o n o t h i n g
}
. store ( in : & provider . disposeBag )
}
}
2021-03-09 08:18:43 +01:00
extension StatusProviderFacade {
static func responseToStatusBoostAction ( provider : StatusProvider ) {
_responseToStatusBoostAction (
provider : provider ,
toot : provider . toot ( )
)
}
2021-03-15 11:19:45 +01:00
static func responseToStatusReblogAction ( provider : StatusProvider , cell : UITableViewCell ) {
2021-03-09 08:18:43 +01:00
_responseToStatusBoostAction (
provider : provider ,
toot : provider . toot ( for : cell , indexPath : nil )
)
}
private static func _responseToStatusBoostAction ( provider : StatusProvider , toot : Future < Toot ? , Never > ) {
// p r e p a r e a u t h e n t i c a t i o n
guard let activeMastodonAuthenticationBox = provider . context . authenticationService . activeMastodonAuthenticationBox . value else {
assertionFailure ( )
return
}
// p r e p a r e c u r r e n t u s e r i n f o s
guard let _currentMastodonUser = provider . context . authenticationService . activeMastodonAuthentication . value ? . user else {
assertionFailure ( )
return
}
let mastodonUserID = activeMastodonAuthenticationBox . userID
assert ( _currentMastodonUser . id = = mastodonUserID )
let mastodonUserObjectID = _currentMastodonUser . objectID
guard let context = provider . context else { return }
// h a p t i c f e e d b a c k g e n e r a t o r
let generator = UIImpactFeedbackGenerator ( style : . light )
let responseFeedbackGenerator = UIImpactFeedbackGenerator ( style : . medium )
toot
2021-03-15 11:19:45 +01:00
. compactMap { toot -> ( NSManagedObjectID , Mastodon . API . Reblog . ReblogKind ) ? in
2021-03-09 08:18:43 +01:00
guard let toot = toot ? . reblog ? ? toot else { return nil }
2021-03-15 11:19:45 +01:00
let reblogKind : Mastodon . API . Reblog . ReblogKind = {
let isReblogged = toot . rebloggedBy . flatMap { $0 . contains ( where : { $0 . id = = mastodonUserID } ) } ? ? false
return isReblogged ? . undoReblog : . reblog ( query : . init ( visibility : nil ) )
2021-03-09 08:18:43 +01:00
} ( )
2021-03-15 11:19:45 +01:00
return ( toot . objectID , reblogKind )
2021-03-09 08:18:43 +01:00
}
2021-03-15 11:19:45 +01:00
. map { tootObjectID , reblogKind -> AnyPublisher < ( Toot . ID , Mastodon . API . Reblog . ReblogKind ) , Error > in
return context . apiService . reblog (
2021-03-09 08:18:43 +01:00
tootObjectID : tootObjectID ,
mastodonUserObjectID : mastodonUserObjectID ,
2021-03-15 11:19:45 +01:00
reblogKind : reblogKind
2021-03-09 08:18:43 +01:00
)
2021-03-15 11:19:45 +01:00
. map { tootID in ( tootID , reblogKind ) }
2021-03-09 08:18:43 +01:00
. eraseToAnyPublisher ( )
}
. setFailureType ( to : Error . self )
. eraseToAnyPublisher ( )
. switchToLatest ( )
. receive ( on : DispatchQueue . main )
. handleEvents { _ in
generator . prepare ( )
responseFeedbackGenerator . prepare ( )
2021-03-15 11:19:45 +01:00
} receiveOutput : { _ , reblogKind in
2021-03-09 08:18:43 +01:00
generator . impactOccurred ( )
2021-03-15 11:19:45 +01:00
switch reblogKind {
case . reblog :
os_log ( " %{public}s[%{public}ld], %{public}s: [Reblog] update local toot reblog status to: %s " , ( ( #file as NSString ) . lastPathComponent ) , #line , #function , " reblog " )
case . undoReblog :
os_log ( " %{public}s[%{public}ld], %{public}s: [Reblog] update local toot reblog status to: %s " , ( ( #file as NSString ) . lastPathComponent ) , #line , #function , " unboost " )
}
2021-03-09 08:18:43 +01:00
} receiveCompletion : { completion in
switch completion {
case . failure :
// TODO: h a n d l e e r r o r
break
case . finished :
break
}
}
2021-03-15 11:19:45 +01:00
. map { tootID , reblogKind in
return context . apiService . reblog (
2021-03-09 08:18:43 +01:00
statusID : tootID ,
2021-03-15 11:19:45 +01:00
reblogKind : reblogKind ,
2021-03-09 08:18:43 +01:00
mastodonAuthenticationBox : activeMastodonAuthenticationBox
)
}
. switchToLatest ( )
. receive ( on : DispatchQueue . main )
. sink { [ weak provider ] completion in
guard let provider = provider else { return }
if provider . view . window != nil {
responseFeedbackGenerator . impactOccurred ( )
}
switch completion {
case . failure ( let error ) :
2021-03-15 11:19:45 +01:00
os_log ( " %{public}s[%{public}ld], %{public}s: [Reblog] remote reblog request fail: %{public}s " , ( ( #file as NSString ) . lastPathComponent ) , #line , #function , error . localizedDescription )
2021-03-09 08:18:43 +01:00
case . finished :
2021-03-15 11:19:45 +01:00
os_log ( " %{public}s[%{public}ld], %{public}s: [Reblog] remote reblog request success " , ( ( #file as NSString ) . lastPathComponent ) , #line , #function )
2021-03-09 08:18:43 +01:00
}
} receiveValue : { response in
// d o n o t h i n g
}
. store ( in : & provider . disposeBag )
}
}
2021-02-08 11:29:27 +01:00
extension StatusProviderFacade {
enum Target {
case toot
case reblog
}
}