You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

302 lines
11 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. //
  2. // ExistingUserKycPresenter.swift
  3. // GME Remit
  4. //
  5. // Created by Shiran on 8/18/20.
  6. //Copyright © 2020 Gobal Money Express Co. Ltd. All rights reserved.
  7. //
  8. import RxSwift
  9. import RxCocoa
  10. import CoreLocation
  11. class ExistingUserKycPresenter {
  12. // MARK: Properties
  13. enum Step: Int {
  14. case innerStep1
  15. case innerStep2
  16. }
  17. var interactor: ExistingUserKycInteractorInput?
  18. var wireframe: ExistingUserKycWireframeInput?
  19. struct Input {
  20. let viewWillAppear: Driver<Bool>
  21. let sendLocation: Driver<CLLocation>
  22. let sourceOfFund: Driver<KeyValue?>
  23. let occupation: Driver<KeyValue?>
  24. let monthlyIncome: Driver<String>
  25. let businessType: Driver<KeyValue?>
  26. let visaStatus: Driver<KeyValue?>
  27. let idType: Driver<KeyValue?>
  28. let purpose: Driver<KeyValue?>
  29. let employerName: Driver<String>
  30. let mobile: Driver<String>
  31. let email: Driver<String>
  32. let address: Driver<String>
  33. let isValidPersonalInfo: Driver<Bool>
  34. let personalInfoSave: Driver<Void>
  35. let editingPersonalInfo: Driver<Void>
  36. let frontIDImage: Driver<String?>
  37. let backIDImage: Driver<String?>
  38. let sideIDImage: Driver<String?>
  39. let isValidPictures: Driver<Bool>
  40. let editingPictureInfo: Driver<Void>
  41. let submit: Driver<Void>
  42. }
  43. struct Output {
  44. let isError: Driver<Error>
  45. let isProgress: Driver<Bool>
  46. let gpsAddress: Driver<String>
  47. let model: Driver<KYCInfoModel>
  48. let idTypes: Driver<[KeyValue]?>
  49. let purposes: Driver<[KeyValue]?>
  50. let busniessType: Driver<[KeyValue]?>
  51. let sourceOfFund: Driver<[KeyValue]?>
  52. let occupation: Driver<[KeyValue]?>
  53. // let monthlyIncome: Driver<[KeyValue]?>
  54. let visaStatus: Driver<[KeyValue]?>
  55. let isSetPersonalInfoModel: Driver<Bool>
  56. let isSetPictureInfoModel: Driver<Bool>
  57. let storePersonalSuccess: Driver<Bool>
  58. let storedSuccess: Driver<String>
  59. }
  60. private let disposeBag = DisposeBag()
  61. private let progressLinker = PublishSubject<Bool>()
  62. private let errorLinker = PublishSubject<Error>()
  63. private let gpsAddress = PublishSubject<String>()
  64. private let model = PublishSubject<KYCInfoModel>()
  65. private var storedPersonalInfo = PublishSubject<PersonalInfoModel?>()
  66. private var storedPictureInformation = PublishSubject<PictureInfoModel?>()
  67. private var storedPersonalSuccess = PublishSubject<String?>()
  68. private let storedSuccess = PublishSubject<String>()
  69. func transform(input: Input) -> Output {
  70. let newPicturesInfo = Driver.combineLatest(
  71. input.isValidPictures,
  72. input.frontIDImage,
  73. input.backIDImage,
  74. input.sideIDImage
  75. ).map { (isValid, frontIdImage, backIdImage, sideIdImage) -> PictureInfoModel? in
  76. return isValid ? PictureInfoModel(
  77. idFront: frontIdImage,
  78. idBack: backIdImage,
  79. idSide: sideIdImage,
  80. additionalId: nil,
  81. facePicture: nil,
  82. additionalIdBack: nil
  83. ) : nil
  84. }
  85. let newPersonalInfo = Driver.combineLatest(
  86. input.isValidPersonalInfo,
  87. input.idType,
  88. input.visaStatus,
  89. input.occupation,
  90. input.purpose,
  91. Driver.combineLatest(
  92. input.employerName,
  93. input.sourceOfFund,
  94. input.monthlyIncome,
  95. input.mobile,
  96. input.businessType,
  97. input.email,
  98. input.address,
  99. model.map{$0.personalInformation?.nativeCountry}.asDriverOnErrorJustComplete()
  100. ).map({ (employerName, sourceOfFund, monthlyIncome, mobile, businessType, email, address, nativeCountry) -> PersonalInfoModel in
  101. return PersonalInfoModel(
  102. email: email,
  103. address: address,
  104. visaStatus: nil,
  105. sourceOfFund: sourceOfFund?.id,
  106. monthlyIncome: monthlyIncome,
  107. mobile: mobile,
  108. idType: nil,
  109. additionalIdType: nil,
  110. businessType: businessType?.id,
  111. nativeCountry: nativeCountry,
  112. employeerName: employerName,
  113. occupation: nil,
  114. purpose: nil,
  115. isBackIDRequired: nil
  116. )
  117. })
  118. ).map { (isValid, idType, visaStatus, occupation, purpose, info) -> PersonalInfoModel? in
  119. if isValid{
  120. var personalInfo = info
  121. personalInfo.idType = idType?.id
  122. personalInfo.visaStatus = visaStatus?.id
  123. personalInfo.occupation = occupation?.id
  124. personalInfo.purpose = purpose?.id
  125. return personalInfo
  126. }
  127. return nil
  128. }
  129. let isSetPersonalInformation = Driver
  130. .combineLatest(input.isValidPersonalInfo, storedPersonalInfo.asDriverOnErrorJustComplete())
  131. .map { $0 && $1 != nil }
  132. let isSetPictureInformation = Driver
  133. .combineLatest(input.isValidPictures, storedPictureInformation.asDriverOnErrorJustComplete())
  134. .map {$0 && $1 != nil}
  135. let storePersonalSuccess = Driver
  136. .combineLatest(input.isValidPersonalInfo, storedPersonalSuccess.asDriverOnErrorJustComplete())
  137. .map {$0 && $1 != nil}
  138. input.viewWillAppear.drive(onNext: { [weak self] (value) in
  139. self?.progressLinker.onNext(value)
  140. self?.interactor?.fetchInformation()
  141. }).disposed(by: disposeBag)
  142. input.sendLocation.drive(onNext: { [weak self] in
  143. self?.progressLinker.onNext(true)
  144. let geocoder = CLGeocoder()
  145. let locale = Locale(identifier: "en-US")
  146. geocoder.reverseGeocodeLocation($0, preferredLocale: locale) {(placemarks, error) in
  147. if let error = error {
  148. self?.progressLinker.onNext(false)
  149. self?.errorLinker.onNext(error)
  150. }
  151. if let address: [CLPlacemark] = placemarks {
  152. let name = address.last?.name ?? ""
  153. let locality = address.last?.locality ?? ""
  154. self?.progressLinker.onNext(false)
  155. let mergedAddress = "\(name) \(locality)"//.extract(regex: "[A-Z0-9a-z\\s]")
  156. self?.gpsAddress.onNext(mergedAddress)
  157. }
  158. }
  159. }).disposed(by: disposeBag)
  160. input.editingPersonalInfo
  161. .withLatestFrom(newPersonalInfo) { $1 }
  162. .withLatestFrom(storedPersonalInfo.asDriverOnErrorJustComplete()) {($0, $1)}
  163. .drive(onNext: {[weak self] in
  164. guard let new = $0, let stored = $1 else { return }
  165. if new != stored {
  166. self?.storedPersonalInfo.onNext(nil)
  167. }
  168. })
  169. .disposed(by: disposeBag)
  170. input.editingPictureInfo
  171. .withLatestFrom(newPicturesInfo) { $1 }
  172. .withLatestFrom(storedPictureInformation.asDriverOnErrorJustComplete()) {($0, $1)}
  173. .drive(onNext: {[weak self] in
  174. guard let new = $0, let stored = $1 else { return }
  175. if new != stored {
  176. self?.storedPictureInformation.onNext(nil)
  177. }
  178. })
  179. .disposed(by: disposeBag)
  180. input.personalInfoSave
  181. .withLatestFrom(newPersonalInfo) { $1 }
  182. .drive(onNext: {[weak self] in
  183. let saveModel = KYCSaveInformation(
  184. type: Step.innerStep1.rawValue,
  185. personalInformation: $0,
  186. pictures: nil
  187. )
  188. self?.progressLinker.onNext(true)
  189. self?.interactor?.saveInformation(with: saveModel)
  190. }).disposed(by: disposeBag)
  191. input.submit
  192. .withLatestFrom(newPersonalInfo) { $1 }
  193. .withLatestFrom(newPicturesInfo) { ($0, $1) }
  194. .drive(onNext: {[weak self] in
  195. let saveModel = KYCSaveInformation(
  196. type: Step.innerStep2.rawValue,
  197. personalInformation: $0,
  198. pictures: $1
  199. )
  200. self?.progressLinker.onNext(true)
  201. self?.interactor?.saveInformation(with: saveModel)
  202. }).disposed(by: disposeBag)
  203. return Output(
  204. isError: errorLinker.asDriverOnErrorJustComplete(),
  205. isProgress: progressLinker.asDriverOnErrorJustComplete(),
  206. gpsAddress: gpsAddress.asDriverOnErrorJustComplete(),
  207. model: model.asDriverOnErrorJustComplete(),
  208. idTypes: model.map {$0.idType}.asDriverOnErrorJustComplete(),
  209. purposes: model.map {$0.purpose}.asDriverOnErrorJustComplete(),
  210. busniessType: model.map {$0.businessType}.asDriverOnErrorJustComplete(),
  211. sourceOfFund: model.map {$0.sourceOfFund}.asDriverOnErrorJustComplete(),
  212. occupation: model.map {$0.occupation}.asDriverOnErrorJustComplete(),
  213. // monthlyIncome: model.map {$0.monthlyIncome}.asDriverOnErrorJustComplete(),
  214. visaStatus: model.map {$0.visaStatus}.asDriverOnErrorJustComplete(),
  215. isSetPersonalInfoModel: isSetPersonalInformation,
  216. isSetPictureInfoModel: isSetPictureInformation,
  217. storePersonalSuccess: storePersonalSuccess,
  218. storedSuccess: storedSuccess.asDriverOnErrorJustComplete()
  219. )
  220. }
  221. }
  222. // MARK: ExistingUserKyc interactor output interface
  223. extension ExistingUserKycPresenter: ExistingUserKycInteractorOutput {
  224. func setModel(with model: KYCInfoModel) {
  225. progressLinker.onNext(false)
  226. self.model.onNext(model)
  227. guard let personalInformation = model.personalInformation, !personalInformation.isHasEmptyValue() else {
  228. storedPersonalInfo.onNext(nil)
  229. return
  230. }
  231. storedPersonalInfo.onNext(model.personalInformation)
  232. guard let pictures = model.pictures, !pictures.isHasEmptyValue() else {
  233. storedPictureInformation.onNext(nil)
  234. return
  235. }
  236. storedPictureInformation.onNext(pictures)
  237. }
  238. func setSaveResult(with storedModel: KYCSaveInformation?, message: String) {
  239. progressLinker.onNext(false)
  240. guard let storedModel = storedModel, let type = Step(rawValue: storedModel.type) else { return }
  241. switch type {
  242. case .innerStep1:
  243. storedPersonalInfo.onNext(storedModel.personalInformation)
  244. storedPersonalSuccess.onNext(message)
  245. DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
  246. self.storedPersonalSuccess.onNext(nil)
  247. }
  248. case .innerStep2:
  249. storedPictureInformation.onNext(storedModel.pictures)
  250. storedSuccess.onNext(message)
  251. }
  252. }
  253. func setError(with error: Error) {
  254. progressLinker.onNext(false)
  255. errorLinker.onNext(error)
  256. }
  257. }