refactor: remove UI part from ReportViewmodel
This commit is contained in:
parent
479f21fd9f
commit
cbc828eec2
|
@ -362,7 +362,6 @@ extension HomeTimelineViewController {
|
||||||
// 106093402888557459
|
// 106093402888557459
|
||||||
let viewModel = ReportViewModel(
|
let viewModel = ReportViewModel(
|
||||||
context: self.context,
|
context: self.context,
|
||||||
coordinator: self.coordinator,
|
|
||||||
domain: authenticationBox.domain,
|
domain: authenticationBox.domain,
|
||||||
userId: userId,
|
userId: userId,
|
||||||
statusId: statusId
|
statusId: statusId
|
||||||
|
|
|
@ -12,6 +12,7 @@ import CoreDataStack
|
||||||
import os.log
|
import os.log
|
||||||
import UIKit
|
import UIKit
|
||||||
import TwitterTextEditor
|
import TwitterTextEditor
|
||||||
|
import MastodonSDK
|
||||||
|
|
||||||
class ReportViewController: UIViewController, NeedsDependency {
|
class ReportViewController: UIViewController, NeedsDependency {
|
||||||
static let kAnimationDuration: TimeInterval = 0.33
|
static let kAnimationDuration: TimeInterval = 0.33
|
||||||
|
@ -84,6 +85,12 @@ class ReportViewController: UIViewController, NeedsDependency {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
setupView()
|
setupView()
|
||||||
|
|
||||||
|
viewModel.setupDiffableDataSource(
|
||||||
|
for: tableView,
|
||||||
|
dependency: self
|
||||||
|
)
|
||||||
|
|
||||||
bindViewModel()
|
bindViewModel()
|
||||||
bindActions()
|
bindActions()
|
||||||
}
|
}
|
||||||
|
@ -127,8 +134,7 @@ class ReportViewController: UIViewController, NeedsDependency {
|
||||||
step1Skip: step1Skip.eraseToAnyPublisher(),
|
step1Skip: step1Skip.eraseToAnyPublisher(),
|
||||||
step2Continue: step2Continue.eraseToAnyPublisher(),
|
step2Continue: step2Continue.eraseToAnyPublisher(),
|
||||||
step2Skip: step2Skip.eraseToAnyPublisher(),
|
step2Skip: step2Skip.eraseToAnyPublisher(),
|
||||||
cancel: cancel.eraseToAnyPublisher(),
|
cancel: cancel.eraseToAnyPublisher()
|
||||||
tableView: tableView
|
|
||||||
)
|
)
|
||||||
let output = viewModel.transform(input: input)
|
let output = viewModel.transform(input: input)
|
||||||
output?.currentStep
|
output?.currentStep
|
||||||
|
@ -161,10 +167,26 @@ class ReportViewController: UIViewController, NeedsDependency {
|
||||||
.assign(to: \.nextStepButton.isEnabled, on: footer)
|
.assign(to: \.nextStepButton.isEnabled, on: footer)
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
|
|
||||||
output?.reportSuccess
|
output?.reportResult
|
||||||
|
.print()
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
.sink(receiveValue: { [weak self] (_) in
|
.sink(receiveCompletion: { _ in
|
||||||
|
}, receiveValue: { [weak self] data in
|
||||||
|
let (success, error) = data
|
||||||
|
if success {
|
||||||
self?.dismiss(animated: true, completion: nil)
|
self?.dismiss(animated: true, completion: nil)
|
||||||
|
} else if let error = 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)
|
||||||
|
)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ import MastodonSDK
|
||||||
import UIKit
|
import UIKit
|
||||||
import os.log
|
import os.log
|
||||||
|
|
||||||
class ReportViewModel: NSObject, NeedsDependency {
|
class ReportViewModel: NSObject {
|
||||||
typealias FileReportQuery = Mastodon.API.Reports.FileReportQuery
|
typealias FileReportQuery = Mastodon.API.Reports.FileReportQuery
|
||||||
|
|
||||||
enum Step: Int {
|
enum Step: Int {
|
||||||
|
@ -23,7 +23,6 @@ class ReportViewModel: NSObject, NeedsDependency {
|
||||||
|
|
||||||
// confirm set only once
|
// confirm set only once
|
||||||
weak var context: AppContext! { willSet { precondition(context == nil) } }
|
weak var context: AppContext! { willSet { precondition(context == nil) } }
|
||||||
weak var coordinator: SceneCoordinator! { willSet { precondition(coordinator == nil) } }
|
|
||||||
var userId: String
|
var userId: String
|
||||||
var statusId: String?
|
var statusId: String?
|
||||||
|
|
||||||
|
@ -34,7 +33,6 @@ class ReportViewModel: NSObject, NeedsDependency {
|
||||||
var diffableDataSource: UITableViewDiffableDataSource<ReportSection, Item>?
|
var diffableDataSource: UITableViewDiffableDataSource<ReportSection, Item>?
|
||||||
let continueEnableSubject = CurrentValueSubject<Bool, Never>(false)
|
let continueEnableSubject = CurrentValueSubject<Bool, Never>(false)
|
||||||
let sendEnableSubject = CurrentValueSubject<Bool, Never>(false)
|
let sendEnableSubject = CurrentValueSubject<Bool, Never>(false)
|
||||||
let reportSuccess = PassthroughSubject<Void, Never>()
|
|
||||||
|
|
||||||
struct Input {
|
struct Input {
|
||||||
let didToggleSelected: AnyPublisher<Item, Never>
|
let didToggleSelected: AnyPublisher<Item, Never>
|
||||||
|
@ -44,24 +42,21 @@ class ReportViewModel: NSObject, NeedsDependency {
|
||||||
let step2Continue: AnyPublisher<Void, Never>
|
let step2Continue: AnyPublisher<Void, Never>
|
||||||
let step2Skip: AnyPublisher<Void, Never>
|
let step2Skip: AnyPublisher<Void, Never>
|
||||||
let cancel: AnyPublisher<Void, Never>
|
let cancel: AnyPublisher<Void, Never>
|
||||||
let tableView: UITableView
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Output {
|
struct Output {
|
||||||
let currentStep: AnyPublisher<Step, Never>
|
let currentStep: AnyPublisher<Step, Never>
|
||||||
let continueEnableSubject: AnyPublisher<Bool, Never>
|
let continueEnableSubject: AnyPublisher<Bool, Never>
|
||||||
let sendEnableSubject: AnyPublisher<Bool, Never>
|
let sendEnableSubject: AnyPublisher<Bool, Never>
|
||||||
let reportSuccess: AnyPublisher<Void, Never>
|
let reportResult: AnyPublisher<(Bool, Error?), Never>
|
||||||
}
|
}
|
||||||
|
|
||||||
init(context: AppContext,
|
init(context: AppContext,
|
||||||
coordinator: SceneCoordinator,
|
|
||||||
domain: String,
|
domain: String,
|
||||||
userId: String,
|
userId: String,
|
||||||
statusId: String?
|
statusId: String?
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.coordinator = coordinator
|
|
||||||
self.userId = userId
|
self.userId = userId
|
||||||
self.statusId = statusId
|
self.statusId = statusId
|
||||||
self.statusFetchedResultsController = StatusFetchedResultsController(
|
self.statusFetchedResultsController = StatusFetchedResultsController(
|
||||||
|
@ -86,17 +81,12 @@ class ReportViewModel: NSObject, NeedsDependency {
|
||||||
}
|
}
|
||||||
let domain = activeMastodonAuthenticationBox.domain
|
let domain = activeMastodonAuthenticationBox.domain
|
||||||
|
|
||||||
setupDiffableDataSource(
|
|
||||||
for: input.tableView,
|
|
||||||
dependency: self
|
|
||||||
)
|
|
||||||
|
|
||||||
// data binding
|
// data binding
|
||||||
bindData(input: input)
|
bindData(input: input)
|
||||||
|
|
||||||
// step1 and step2 binding
|
// step1 and step2 binding
|
||||||
bindForStep1(input: input)
|
bindForStep1(input: input)
|
||||||
bindForStep2(
|
let reportResult = bindForStep2(
|
||||||
input: input,
|
input: input,
|
||||||
domain: domain,
|
domain: domain,
|
||||||
activeMastodonAuthenticationBox: activeMastodonAuthenticationBox
|
activeMastodonAuthenticationBox: activeMastodonAuthenticationBox
|
||||||
|
@ -114,7 +104,7 @@ class ReportViewModel: NSObject, NeedsDependency {
|
||||||
currentStep: currentStep.eraseToAnyPublisher(),
|
currentStep: currentStep.eraseToAnyPublisher(),
|
||||||
continueEnableSubject: continueEnableSubject.eraseToAnyPublisher(),
|
continueEnableSubject: continueEnableSubject.eraseToAnyPublisher(),
|
||||||
sendEnableSubject: sendEnableSubject.eraseToAnyPublisher(),
|
sendEnableSubject: sendEnableSubject.eraseToAnyPublisher(),
|
||||||
reportSuccess: reportSuccess.eraseToAnyPublisher()
|
reportResult: reportResult
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,42 +162,35 @@ class ReportViewModel: NSObject, NeedsDependency {
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
}
|
}
|
||||||
|
|
||||||
func bindForStep2(input: Input, domain: String, activeMastodonAuthenticationBox: AuthenticationService.MastodonAuthenticationBox) {
|
func bindForStep2(input: Input, domain: String, activeMastodonAuthenticationBox: AuthenticationService.MastodonAuthenticationBox) -> AnyPublisher<(Bool, Error?), Never> {
|
||||||
let skip = input.step2Skip.map { [weak self] value -> Void in
|
let skip = input.step2Skip.map { [weak self] value -> Void in
|
||||||
guard let self = self else { return value }
|
guard let self = self else { return value }
|
||||||
self.reportQuery.comment = nil
|
self.reportQuery.comment = nil
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
Publishers.Merge(skip, input.step2Continue)
|
return Publishers.Merge(skip, input.step2Continue)
|
||||||
.sink { [weak self] _ in
|
.flatMap { [weak self] (_) -> AnyPublisher<(Bool, Error?), Never> in
|
||||||
guard let self = self else { return }
|
guard let self = self else {
|
||||||
self.context.apiService.report(
|
return Empty(completeImmediately: true).eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.context.apiService.report(
|
||||||
domain: domain,
|
domain: domain,
|
||||||
query: self.reportQuery,
|
query: self.reportQuery,
|
||||||
mastodonAuthenticationBox: activeMastodonAuthenticationBox
|
mastodonAuthenticationBox: activeMastodonAuthenticationBox
|
||||||
)
|
)
|
||||||
.sink { [weak self](data) in
|
.map({ (content) -> (Bool, Error?) in
|
||||||
switch data {
|
return (true, nil)
|
||||||
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)
|
.eraseToAnyPublisher()
|
||||||
|
.tryCatch({ (error) -> AnyPublisher<(Bool, Error?), Never> in
|
||||||
let alertController = UIAlertController(for: error, title: nil, preferredStyle: .alert)
|
return Just((false, error)).eraseToAnyPublisher()
|
||||||
let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
|
})
|
||||||
alertController.addAction(okAction)
|
// to covert to AnyPublisher<(Bool, Error?), Never>
|
||||||
self?.coordinator.present(
|
.replaceError(with: (false, nil))
|
||||||
scene: .alertController(alertController: alertController),
|
.eraseToAnyPublisher()
|
||||||
from: nil,
|
|
||||||
transition: .alertController(animated: true, completion: nil)
|
|
||||||
)
|
|
||||||
case .finished:
|
|
||||||
self?.reportSuccess.send()
|
|
||||||
}
|
}
|
||||||
|
.eraseToAnyPublisher()
|
||||||
} receiveValue: { (data) in
|
|
||||||
}
|
|
||||||
.store(in: &self.disposeBag)
|
|
||||||
}
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
import Combine
|
import Combine
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import enum NIOHTTP1.HTTPResponseStatus
|
||||||
|
|
||||||
extension Mastodon.API.Reports {
|
extension Mastodon.API.Reports {
|
||||||
static func reportsEndpointURL(domain: String) -> URL {
|
static func reportsEndpointURL(domain: String) -> URL {
|
||||||
|
@ -39,13 +40,23 @@ extension Mastodon.API.Reports {
|
||||||
)
|
)
|
||||||
return session.dataTaskPublisher(for: request)
|
return session.dataTaskPublisher(for: request)
|
||||||
.tryMap { data, response in
|
.tryMap { data, response in
|
||||||
if let response = response as? HTTPURLResponse {
|
guard let response = response as? HTTPURLResponse else {
|
||||||
|
assertionFailure()
|
||||||
|
throw NSError()
|
||||||
|
}
|
||||||
|
|
||||||
|
if response.statusCode == 200 {
|
||||||
return Mastodon.Response.Content(
|
return Mastodon.Response.Content(
|
||||||
value: response.statusCode == 200,
|
value: true,
|
||||||
response: response
|
response: response
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
let httpResponseStatus = HTTPResponseStatus(statusCode: response.statusCode)
|
||||||
|
throw Mastodon.API.Error(
|
||||||
|
httpResponseStatus: httpResponseStatus,
|
||||||
|
mastodonError: nil
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return Mastodon.Response.Content(value: false, response: response)
|
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue