// // ExistingUserKycPresenter.swift // GME Remit // // Created by Shiran on 8/18/20. //Copyright © 2020 Gobal Money Express Co. Ltd. All rights reserved. // import RxSwift import RxCocoa import CoreLocation class ExistingUserKycPresenter { // MARK: Properties enum Step: Int { case innerStep1 case innerStep2 } var interactor: ExistingUserKycInteractorInput? var wireframe: ExistingUserKycWireframeInput? struct Input { let viewWillAppear: Driver let sendLocation: Driver let sourceOfFund: Driver let occupation: Driver let monthlyIncome: Driver let businessType: Driver let visaStatus: Driver let idType: Driver let purpose: Driver let employerName: Driver let mobile: Driver let email: Driver let address: Driver let isValidPersonalInfo: Driver let personalInfoSave: Driver let editingPersonalInfo: Driver let frontIDImage: Driver let backIDImage: Driver let sideIDImage: Driver let isValidPictures: Driver let editingPictureInfo: Driver let submit: Driver } struct Output { let isError: Driver let isProgress: Driver let gpsAddress: Driver let model: Driver let idTypes: Driver<[KeyValue]?> let purposes: Driver<[KeyValue]?> let busniessType: Driver<[KeyValue]?> let sourceOfFund: Driver<[KeyValue]?> let occupation: Driver<[KeyValue]?> // let monthlyIncome: Driver<[KeyValue]?> let visaStatus: Driver<[KeyValue]?> let isSetPersonalInfoModel: Driver let isSetPictureInfoModel: Driver let storePersonalSuccess: Driver let storedSuccess: Driver } private let disposeBag = DisposeBag() private let progressLinker = PublishSubject() private let errorLinker = PublishSubject() private let gpsAddress = PublishSubject() private let model = PublishSubject() private var storedPersonalInfo = PublishSubject() private var storedPictureInformation = PublishSubject() private var storedPersonalSuccess = PublishSubject() private let storedSuccess = PublishSubject() func transform(input: Input) -> Output { let newPicturesInfo = Driver.combineLatest( input.isValidPictures, input.frontIDImage, input.backIDImage, input.sideIDImage ).map { (isValid, frontIdImage, backIdImage, sideIdImage) -> PictureInfoModel? in return isValid ? PictureInfoModel( idFront: frontIdImage, idBack: backIdImage, idSide: sideIdImage, additionalId: nil, facePicture: nil, additionalIdBack: nil ) : nil } let newPersonalInfo = Driver.combineLatest( input.isValidPersonalInfo, input.idType, input.visaStatus, input.occupation, input.purpose, Driver.combineLatest( input.employerName, input.sourceOfFund, input.monthlyIncome, input.mobile, input.businessType, input.email, input.address, model.map{$0.personalInformation?.nativeCountry}.asDriverOnErrorJustComplete() ).map({ (employerName, sourceOfFund, monthlyIncome, mobile, businessType, email, address, nativeCountry) -> PersonalInfoModel in return PersonalInfoModel( email: email, address: address, visaStatus: nil, sourceOfFund: sourceOfFund?.id, monthlyIncome: monthlyIncome, mobile: mobile, idType: nil, additionalIdType: nil, businessType: businessType?.id, nativeCountry: nativeCountry, employeerName: employerName, occupation: nil, purpose: nil, isBackIDRequired: nil ) }) ).map { (isValid, idType, visaStatus, occupation, purpose, info) -> PersonalInfoModel? in if isValid{ var personalInfo = info personalInfo.idType = idType?.id personalInfo.visaStatus = visaStatus?.id personalInfo.occupation = occupation?.id personalInfo.purpose = purpose?.id return personalInfo } return nil } let isSetPersonalInformation = Driver .combineLatest(input.isValidPersonalInfo, storedPersonalInfo.asDriverOnErrorJustComplete()) .map { $0 && $1 != nil } let isSetPictureInformation = Driver .combineLatest(input.isValidPictures, storedPictureInformation.asDriverOnErrorJustComplete()) .map {$0 && $1 != nil} let storePersonalSuccess = Driver .combineLatest(input.isValidPersonalInfo, storedPersonalSuccess.asDriverOnErrorJustComplete()) .map {$0 && $1 != nil} input.viewWillAppear.drive(onNext: { [weak self] (value) in self?.progressLinker.onNext(value) self?.interactor?.fetchInformation() }).disposed(by: disposeBag) input.sendLocation.drive(onNext: { [weak self] in self?.progressLinker.onNext(true) let geocoder = CLGeocoder() let locale = Locale(identifier: "en-US") geocoder.reverseGeocodeLocation($0, preferredLocale: locale) {(placemarks, error) in if let error = error { self?.progressLinker.onNext(false) self?.errorLinker.onNext(error) } if let address: [CLPlacemark] = placemarks { let name = address.last?.name ?? "" let locality = address.last?.locality ?? "" self?.progressLinker.onNext(false) let mergedAddress = "\(name) \(locality)"//.extract(regex: "[A-Z0-9a-z\\s]") self?.gpsAddress.onNext(mergedAddress) } } }).disposed(by: disposeBag) input.editingPersonalInfo .withLatestFrom(newPersonalInfo) { $1 } .withLatestFrom(storedPersonalInfo.asDriverOnErrorJustComplete()) {($0, $1)} .drive(onNext: {[weak self] in guard let new = $0, let stored = $1 else { return } if new != stored { self?.storedPersonalInfo.onNext(nil) } }) .disposed(by: disposeBag) input.editingPictureInfo .withLatestFrom(newPicturesInfo) { $1 } .withLatestFrom(storedPictureInformation.asDriverOnErrorJustComplete()) {($0, $1)} .drive(onNext: {[weak self] in guard let new = $0, let stored = $1 else { return } if new != stored { self?.storedPictureInformation.onNext(nil) } }) .disposed(by: disposeBag) input.personalInfoSave .withLatestFrom(newPersonalInfo) { $1 } .drive(onNext: {[weak self] in let saveModel = KYCSaveInformation( type: Step.innerStep1.rawValue, personalInformation: $0, pictures: nil ) self?.progressLinker.onNext(true) self?.interactor?.saveInformation(with: saveModel) }).disposed(by: disposeBag) input.submit .withLatestFrom(newPersonalInfo) { $1 } .withLatestFrom(newPicturesInfo) { ($0, $1) } .drive(onNext: {[weak self] in let saveModel = KYCSaveInformation( type: Step.innerStep2.rawValue, personalInformation: $0, pictures: $1 ) self?.progressLinker.onNext(true) self?.interactor?.saveInformation(with: saveModel) }).disposed(by: disposeBag) return Output( isError: errorLinker.asDriverOnErrorJustComplete(), isProgress: progressLinker.asDriverOnErrorJustComplete(), gpsAddress: gpsAddress.asDriverOnErrorJustComplete(), model: model.asDriverOnErrorJustComplete(), idTypes: model.map {$0.idType}.asDriverOnErrorJustComplete(), purposes: model.map {$0.purpose}.asDriverOnErrorJustComplete(), busniessType: model.map {$0.businessType}.asDriverOnErrorJustComplete(), sourceOfFund: model.map {$0.sourceOfFund}.asDriverOnErrorJustComplete(), occupation: model.map {$0.occupation}.asDriverOnErrorJustComplete(), // monthlyIncome: model.map {$0.monthlyIncome}.asDriverOnErrorJustComplete(), visaStatus: model.map {$0.visaStatus}.asDriverOnErrorJustComplete(), isSetPersonalInfoModel: isSetPersonalInformation, isSetPictureInfoModel: isSetPictureInformation, storePersonalSuccess: storePersonalSuccess, storedSuccess: storedSuccess.asDriverOnErrorJustComplete() ) } } // MARK: ExistingUserKyc interactor output interface extension ExistingUserKycPresenter: ExistingUserKycInteractorOutput { func setModel(with model: KYCInfoModel) { progressLinker.onNext(false) self.model.onNext(model) guard let personalInformation = model.personalInformation, !personalInformation.isHasEmptyValue() else { storedPersonalInfo.onNext(nil) return } storedPersonalInfo.onNext(model.personalInformation) guard let pictures = model.pictures, !pictures.isHasEmptyValue() else { storedPictureInformation.onNext(nil) return } storedPictureInformation.onNext(pictures) } func setSaveResult(with storedModel: KYCSaveInformation?, message: String) { progressLinker.onNext(false) guard let storedModel = storedModel, let type = Step(rawValue: storedModel.type) else { return } switch type { case .innerStep1: storedPersonalInfo.onNext(storedModel.personalInformation) storedPersonalSuccess.onNext(message) DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { self.storedPersonalSuccess.onNext(nil) } case .innerStep2: storedPictureInformation.onNext(storedModel.pictures) storedSuccess.onNext(message) } } func setError(with error: Error) { progressLinker.onNext(false) errorLinker.onNext(error) } }