2021-04-19 14:34:08 +02:00
//
// R e p o r t V i e w M o d e l . s w i f t
// M a s t o d o n
//
// C r e a t e d b y i h u g o o n 2 0 2 1 / 4 / 1 9 .
//
import Combine
import CoreData
import CoreDataStack
import Foundation
import MastodonSDK
import UIKit
import os . log
class ReportViewModel : NSObject , NeedsDependency {
typealias FileReportQuery = Mastodon . API . Reports . FileReportQuery
enum Step : Int {
case one
case two
}
// c o n f i r m s e t o n l y o n c e
weak var context : AppContext ! { willSet { precondition ( context = = nil ) } }
weak var coordinator : SceneCoordinator ! { willSet { precondition ( coordinator = = nil ) } }
var userId : String
var statusId : String ?
2021-04-22 17:02:24 +02:00
var reportQuery : FileReportQuery
var disposeBag = Set < AnyCancellable > ( )
let currentStep = CurrentValueSubject < Step , Never > ( . one )
let statusFetchedResultsController : StatusFetchedResultsController
var diffableDataSource : UITableViewDiffableDataSource < ReportSection , Item > ?
let continueEnableSubject = CurrentValueSubject < Bool , Never > ( false )
let sendEnableSubject = CurrentValueSubject < Bool , Never > ( false )
let reportSuccess = PassthroughSubject < Void , Never > ( )
2021-04-19 14:34:08 +02:00
struct Input {
let didToggleSelected : AnyPublisher < Item , Never >
let comment : AnyPublisher < String ? , Never >
let step1Continue : AnyPublisher < Void , Never >
let step1Skip : AnyPublisher < Void , Never >
let step2Continue : AnyPublisher < Void , Never >
let step2Skip : AnyPublisher < Void , Never >
let cancel : AnyPublisher < Void , Never >
let tableView : UITableView
}
struct Output {
let currentStep : AnyPublisher < Step , Never >
let continueEnableSubject : AnyPublisher < Bool , Never >
let sendEnableSubject : AnyPublisher < Bool , Never >
let reportSuccess : AnyPublisher < Void , Never >
}
init ( context : AppContext ,
coordinator : SceneCoordinator ,
domain : String ,
userId : String ,
statusId : String ?
) {
self . context = context
self . coordinator = coordinator
self . userId = userId
self . statusId = statusId
self . statusFetchedResultsController = StatusFetchedResultsController (
managedObjectContext : context . managedObjectContext ,
domain : domain ,
additionalTweetPredicate : Status . notDeleted ( )
)
self . reportQuery = FileReportQuery (
accountId : userId ,
2021-04-23 03:37:18 +02:00
statusIds : [ ] ,
2021-04-19 14:34:08 +02:00
comment : nil ,
forward : nil
)
super . init ( )
}
func transform ( input : Input ? ) -> Output ? {
guard let input = input else { return nil }
guard let activeMastodonAuthenticationBox = context . authenticationService . activeMastodonAuthenticationBox . value else {
return nil
}
let domain = activeMastodonAuthenticationBox . domain
setupDiffableDataSource (
for : input . tableView ,
2021-04-23 03:37:18 +02:00
dependency : self
2021-04-19 14:34:08 +02:00
)
// d a t a b i n d i n g
bindData ( input : input )
// s t e p 1 a n d s t e p 2 b i n d i n g
bindForStep1 ( input : input )
bindForStep2 (
input : input ,
domain : domain ,
activeMastodonAuthenticationBox : activeMastodonAuthenticationBox
)
requestRecentStatus (
domain : domain ,
accountId : self . userId ,
authorizationBox : activeMastodonAuthenticationBox
)
fetchStatus ( )
return Output (
currentStep : currentStep . eraseToAnyPublisher ( ) ,
continueEnableSubject : continueEnableSubject . eraseToAnyPublisher ( ) ,
sendEnableSubject : sendEnableSubject . eraseToAnyPublisher ( ) ,
reportSuccess : reportSuccess . eraseToAnyPublisher ( )
)
}
// MARK: - P r i v a t e m e t h o d s
func bindData ( input : Input ) {
input . didToggleSelected . sink { [ weak self ] ( item ) in
guard let self = self else { return }
2021-04-23 03:37:18 +02:00
guard case let . reportStatus ( objectID , attribute ) = item else { return }
2021-04-19 14:34:08 +02:00
guard var snapshot = self . diffableDataSource ? . snapshot ( ) else { return }
let managedObjectContext = self . statusFetchedResultsController . fetchedResultsController . managedObjectContext
guard let status = managedObjectContext . object ( with : objectID ) as ? Status else {
return
}
2021-04-23 03:37:18 +02:00
attribute . isSelected = ! attribute . isSelected
if attribute . isSelected {
self . reportQuery . append ( statusId : status . id )
2021-04-19 14:34:08 +02:00
} else {
2021-04-23 03:37:18 +02:00
self . reportQuery . remove ( statusId : status . id )
2021-04-19 14:34:08 +02:00
}
snapshot . reloadItems ( [ item ] )
self . diffableDataSource ? . apply ( snapshot , animatingDifferences : false )
2021-04-23 03:37:18 +02:00
let continueEnable = ( self . reportQuery . statusIds ? . count ? ? 0 ) > 0
2021-04-19 14:34:08 +02:00
self . continueEnableSubject . send ( continueEnable )
}
. store ( in : & disposeBag )
input . comment . assign (
to : \ . comment ,
2021-04-23 03:37:18 +02:00
on : self . reportQuery
2021-04-19 14:34:08 +02:00
)
. store ( in : & disposeBag )
input . comment . sink { [ weak self ] ( comment ) in
guard let self = self else { return }
let sendEnable = ( comment ? . length ? ? 0 ) > 0
self . sendEnableSubject . send ( sendEnable )
}
. store ( in : & disposeBag )
}
func bindForStep1 ( input : Input ) {
let skip = input . step1Skip . map { [ weak self ] value -> Void in
guard let self = self else { return value }
2021-04-23 03:37:18 +02:00
self . reportQuery . statusIds ? . removeAll ( )
2021-04-19 14:34:08 +02:00
return value
}
Publishers . Merge ( skip , input . step1Continue )
. sink { [ weak self ] _ in
self ? . currentStep . value = . two
self ? . sendEnableSubject . send ( false )
}
. store ( in : & disposeBag )
}
func bindForStep2 ( input : Input , domain : String , activeMastodonAuthenticationBox : AuthenticationService . MastodonAuthenticationBox ) {
let skip = input . step2Skip . map { [ weak self ] value -> Void in
guard let self = self else { return value }
2021-04-23 03:37:18 +02:00
self . reportQuery . comment = nil
2021-04-19 14:34:08 +02:00
return value
}
Publishers . Merge ( skip , input . step2Continue )
. sink { [ weak self ] _ in
guard let self = self else { return }
self . context . apiService . report (
domain : domain ,
query : self . reportQuery ,
mastodonAuthenticationBox : activeMastodonAuthenticationBox
)
. sink { [ weak self ] ( data ) in
switch data {
case . failure ( let error ) :
os_log ( . info , log : . debug , " %{public}s[%{public}ld], %{public}s: fail to file a report : %s " , ( ( #file as NSString ) . lastPathComponent ) , #line , #function , error . localizedDescription )
let alertController = UIAlertController ( for : error , title : nil , preferredStyle : . alert )
let okAction = UIAlertAction ( title : " OK " , style : . default , handler : nil )
alertController . addAction ( okAction )
self ? . coordinator . present (
scene : . alertController ( alertController : alertController ) ,
from : nil ,
transition : . alertController ( animated : true , completion : nil )
)
case . finished :
self ? . reportSuccess . send ( )
}
} receiveValue : { ( data ) in
}
. store ( in : & self . disposeBag )
}
. store ( in : & disposeBag )
}
}