// // SetupRecipientViewController.swift // GME Remit // // Created by InKwon James Kim on 09/08/2019. //Copyright © 2019 Gobal Money Express Co. Ltd. All rights reserved. // import UIKit import RxSwift import RxCocoa enum DynamicFieldRequire: String { case mandatory = "M" case optional = "O" case hidden = "H" } class SetupRecipientViewController: UIViewController { // MARK: Properties var viewModel: SetupRecipientViewModel! weak var delegate: SetupRecipientDelegate? private var dynamicFieldDic: [String: ValidationTextField]? private let disposeBag = DisposeBag() // MARK: Computed Properties // MARK: IBOutlets @IBOutlet private weak var countryTextField: ValidationTextField! @IBOutlet private weak var paymentModeTextField: ValidationTextField! @IBOutlet private weak var countryContainerView: ShadowView! @IBOutlet private weak var recipientBankInfomationContainerView: UIView! @IBOutlet private weak var recipientInformationContainerView: UIStackView! @IBOutlet private weak var idContainerView: ShadowView! @IBOutlet private weak var addressContainerView: ShadowView! @IBOutlet private weak var otherContainerView: ShadowView! @IBOutlet private weak var bankTextField: ValidationTextField! @IBOutlet private weak var branchTextField: ValidationTextField! @IBOutlet private weak var accountTextField: ValidationTextField! @IBOutlet private weak var idTextField: ValidationTextField! @IBOutlet private weak var idNumberTextField: ValidationTextField! @IBOutlet private weak var firstNameTextField: ValidationTextField! @IBOutlet private weak var middleNameTextField: ValidationTextField! @IBOutlet private weak var lastNameTextField: ValidationTextField! @IBOutlet private weak var fullNameTextField: ValidationTextField! @IBOutlet private weak var firstNameLocalTextField: ValidationTextField! @IBOutlet private weak var middleNameLocalTextField: ValidationTextField! @IBOutlet private weak var lastNameLocalTextField: ValidationTextField! @IBOutlet private weak var fullNameLocalTextField: ValidationTextField! @IBOutlet private weak var nativeCountryTextField: ValidationTextField! @IBOutlet private weak var mobileTextField: ValidationTextField! @IBOutlet private weak var emailTextField: ValidationTextField! @IBOutlet private weak var stateProvinceTextField: ValidationTextField! @IBOutlet private weak var districtTextField: ValidationTextField! @IBOutlet private weak var cityTextField: ValidationTextField! @IBOutlet private weak var addressTextField: ValidationTextField! @IBOutlet private weak var relationTextField: ValidationTextField! @IBOutlet private weak var reasonTextField: ValidationTextField! @IBOutlet private weak var saveButton: UIButton! // MARK: VC's Life cycle override func viewDidLoad() { super.viewDidLoad() setup() } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) title = "recipient_text".localized() setupNormalNavigation() } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) view.endEditing(true) } // MARK: IBActions @IBAction func touchCloseButton(_ sender: UIBarButtonItem) { dismiss(animated: true, completion: nil) } } // MARK: Other Functions extension SetupRecipientViewController { private func setup() { // all setup should be done here countryContainerView.hero.id = "setupRecipient" countryContainerView.hero.modifiers = [.fade, .scale(0.2)] setUI() setMultiLanguage() setBinding() } private func setMultiLanguage() { countryTextField.titleText = "country_text".localized() nativeCountryTextField.titleText = "native_country_text".localized() paymentModeTextField.titleText = "payment_mode_text".localized() bankTextField.titleText = "bank_text".localized() branchTextField.titleText = "branch_text".localized() accountTextField.titleText = "account_text".localized() firstNameTextField.titleText = "first_name_text".localized() middleNameTextField.titleText = "middle_name_text".localized() lastNameTextField.titleText = "last_name_text".localized() fullNameTextField.titleText = "full_name_text".localized() firstNameLocalTextField.titleText = "first_name_local_text".localized() middleNameLocalTextField.titleText = "middle_name_local_text".localized() lastNameLocalTextField.titleText = "last_name_local_text".localized() fullNameLocalTextField.titleText = "full_name_local_text".localized() idTextField.titleText = "id_type_text".localized() idNumberTextField.titleText = "id_number_text".localized() mobileTextField.titleText = "mobile_text".localized() emailTextField.titleText = "email_text".localized() stateProvinceTextField.titleText = "state_province_text".localized() districtTextField.titleText = "district_text".localized() cityTextField.titleText = "city_text".localized() addressTextField.titleText = "address_text".localized() relationTextField.titleText = "relation_text".localized() reasonTextField.titleText = "reason_text".localized() countryTextField.placeholder = "country_placeholder_text".localized() nativeCountryTextField.placeholder = "native_country_placeholder_text".localized() paymentModeTextField.placeholder = "payment_mode_placeholder_text".localized() bankTextField.placeholder = "bank_placeholder_text".localized() branchTextField.placeholder = "branch_placeholder_text".localized() accountTextField.placeholder = "account_placeholder_text".localized() firstNameTextField.placeholder = "first_name_placeholder_text".localized() middleNameTextField.placeholder = "middle_name_placeholder_text".localized() lastNameTextField.placeholder = "last_name_placeholder_text".localized() fullNameTextField.placeholder = "full_name_placeholder_text".localized() firstNameLocalTextField.placeholder = "first_name_local_placeholder_text".localized() middleNameLocalTextField.placeholder = "middle_name_local_placeholder_text".localized() lastNameLocalTextField.placeholder = "last_name_local_placeholder_text".localized() fullNameLocalTextField.placeholder = "full_name_local_placeholder_text".localized() idTextField.placeholder = "id_type_placeholder_text".localized() idNumberTextField.placeholder = "id_number_placeholder_text".localized() mobileTextField.placeholder = "mobile_placeholder_text".localized() emailTextField.placeholder = "email_placeholder_text".localized() stateProvinceTextField.placeholder = "stateProvince_placeholder_text".localized() districtTextField.placeholder = "district_placeholder_text".localized() cityTextField.placeholder = "city_placeholder_text".localized() addressTextField.placeholder = "address_placeholder_text".localized() relationTextField.placeholder = "relation_placeholder_text".localized() reasonTextField.placeholder = "reason_placeholder_text".localized() saveButton.setTitle("save_text".localized(), for: .normal) } private func setSelectTextFields(of textFields: [UITextField]) { textFields.forEach { $0.inputView = UIView() $0.keyboardToolbar.isHidden = true } } private func setUI() { saveButton.layer.cornerRadius = 5 countryTextField.statusImageView.isHidden = true paymentModeTextField.statusImageView.isHidden = true paymentModeTextField.isHidden = true paymentModeTextField.alpha = 0 setSelectTextFields(of:[ countryTextField, paymentModeTextField, bankTextField, branchTextField, idTextField, stateProvinceTextField, districtTextField, relationTextField, reasonTextField, nativeCountryTextField ] ) recipientBankInfomationContainerView.isHidden = true recipientBankInfomationContainerView.alpha = 0 recipientInformationContainerView.isHidden = true recipientInformationContainerView.alpha = 0 recipientBankInfomationContainerView.travelSubView { (view, _) in if let textField = view as? ValidationTextField { textField.statusImageView.isHidden = true textField.isHidden = true } } recipientInformationContainerView.travelSubView { (view, _) in if let textField = view as? ValidationTextField { textField.statusImageView.isHidden = true textField.isHidden = true } } dynamicFieldDic = [ "Bank Name": bankTextField, "Branch Name": branchTextField, "Account No.": accountTextField, "First Name": firstNameTextField, "Middle Name": middleNameTextField, "Last Name": lastNameTextField, "Full Name": fullNameTextField, "First Name in Local": firstNameLocalTextField, "Middle Name in Local": middleNameLocalTextField, "Last Name in Local": lastNameLocalTextField, "Local Name": fullNameLocalTextField, "Native Country": nativeCountryTextField, "Id Type": idTextField, "ID Number": idNumberTextField, "Mobile Number": mobileTextField, "Email": emailTextField, "Province": stateProvinceTextField, "District": districtTextField, "City": cityTextField, "Address": addressTextField, "Realation Group": relationTextField, "Transfer Reason": reasonTextField ] } private func setBinding() { let viewWillAppear = rx.sentMessage(#selector(UIViewController.viewWillAppear(_:))) .mapToVoid() .asDriverOnErrorJustComplete() let saveTrigger = saveButton.rx.tap.flatMap { [weak self] _ -> Observable in var recipient = Recipient() recipient.firstName = self?.firstNameTextField.text recipient.middleName = self?.middleNameTextField.text recipient.lastName = self?.lastNameTextField.text recipient.localFirstName = self?.firstNameLocalTextField.text recipient.localMiddleName = self?.middleNameLocalTextField.text recipient.localLastName = self?.lastNameLocalTextField.text recipient.fullName = self?.fullNameTextField.text recipient.localFullName = self?.fullNameLocalTextField.text recipient.address = self?.addressTextField.text recipient.city = self?.cityTextField.text recipient.email = self?.emailTextField.text recipient.mobile = self?.mobileTextField.mobileNumberText recipient.idNumber = self?.idNumberTextField.text recipient.agent?.accountNumber = self?.accountTextField.text return Observable.just(recipient) }.asDriverOnErrorJustComplete() let input = SetupRecipientViewModel.Input( initialTrigger: viewWillAppear, openCountryTrigger: countryTextField.rx.controlEvent(.editingDidBegin).asDriver(), openPaymentTrigger: paymentModeTextField.rx.controlEvent(.editingDidBegin).asDriver(), openBankTrigger: bankTextField.rx.controlEvent(.editingDidBegin).asDriver(), openBranchTrigger: branchTextField.rx.controlEvent(.editingDidBegin).asDriver(), openIDTypeTrigger: idTextField.rx.controlEvent(.editingDidBegin).asDriver(), openStateProvinceTrigger: stateProvinceTextField.rx.controlEvent(.editingDidBegin).asDriver(), openDistrictTrigger: districtTextField.rx.controlEvent(.editingDidBegin).asDriver(), openRelationTrigger: relationTextField.rx.controlEvent(.editingDidBegin).asDriver(), openReasonTrigger: reasonTextField.rx.controlEvent(.editingDidBegin).asDriver(), openNativeCountryTrigger: nativeCountryTextField.rx.controlEvent(.editingDidBegin).asDriver(), saveTrigger: saveTrigger ) let output = viewModel.transform(input: input) output.isError .drive(onNext: { [weak self] in guard let `self` = self else { return } self.alert(type: .error, message: $0.localizedDescription) }) .disposed(by: disposeBag) output.isProgress .drive(onNext: { [weak self] in guard let `self` = self else { return } $0 ? self.showProgressHud() : self.hideProgressHud() }) .disposed(by: disposeBag) output.success .drive(onNext: { successObject in let message = "recipient_add_success_message_text".localized().replacingOccurrences( of: "xxx", with: successObject.data?.fullName ?? "" ) self.alertWithOkCancel( type: .normal, message: "\(successObject.message ?? "")\n\(message)", title: "success_text".localized(), okTitle: "yes_text".localized(), cancelTitle: "no_text".localized(), okAction: { [weak self] in guard let `self` = self else { return } self.dismiss(animated: true) { [weak self] in guard let `self` = self else { return } self.delegate?.setupRecieient( self, didSelectRecipientID: successObject.data?.receiverID ?? "" ) } }, cancelAction: { [weak self] in guard let `self` = self else { return } self.dismiss(animated: true) { [weak self] in guard let `self` = self else { return } self.delegate?.setupRecieient( self, didSelectRecipientID: nil ) } }) }).disposed(by: disposeBag) bindUI(output: output) } private func bindUI(output: SetupRecipientViewModel.Output) { output.selectedRecipient .drive(onNext: { [weak self] in guard let `self` = self else { return } self.setText(with: $0) }).disposed(by: disposeBag) output.selectedCountry .drive(onNext: { [weak self] in guard let `self` = self else { return } guard let country = $0 else { return } self.countryTextField.text = country.countryName self.countryTextField.setCountry(with: country.countryCode, isShowCode: false) self.countryTextField.sendActions(for: .editingChanged) self.mobileTextField.setCountry(with: country.countryCode) self.paymentModeTextField.animateHidden(isHidden: false, duration: 0.5) self.recipientBankInfomationContainerView.animateHidden(isHidden: true, duration: 0.5) self.recipientInformationContainerView.animateHidden(isHidden: true, duration: 0.5) }).disposed(by: disposeBag) output.selectedPaymentMode.drive(onNext: { [weak self] in guard let `self` = self else { return } self.paymentModeTextField.text = $0?.subtitle self.paymentModeTextField.sendActions(for: .editingChanged) }).disposed(by: disposeBag) output.selectedBank.drive(onNext: { [weak self] in guard let `self` = self else { return } self.bankTextField.text = $0?.name self.bankTextField.sendActions(for: .editingChanged) if $0?.branchRequired?.lowercased() == "false" { self.branchTextField.isHidden = true self.branchTextField.text = "" self.branchTextField.valid.onNext(true) } else { self.branchTextField.isHidden = false self.branchTextField.valid.onNext(false) } if $0?.accountRequired?.lowercased() == "false" { self.accountTextField.isHidden = true self.accountTextField.text = "" self.accountTextField.valid.onNext(true) } else { self.accountTextField.isHidden = false if self.accountTextField.text != "" { self.accountTextField.valid.onNext(true) } else { self.accountTextField.valid.onNext(false) } } }).disposed(by: disposeBag) output.selectedBranch.drive(onNext: { [weak self] in guard let `self` = self else { return } self.branchTextField.text = $0?.name self.branchTextField.sendActions(for: .editingChanged) }).disposed(by: disposeBag) output.selectedIDType.drive(onNext: { [weak self] in guard let `self` = self else { return } self.idTextField.text = $0?.text self.idTextField.sendActions(for: .editingChanged) }).disposed(by: disposeBag) output.selectedStateProvince.drive(onNext: { [weak self] in guard let `self` = self else { return } self.stateProvinceTextField.text = $0?.name self.stateProvinceTextField.sendActions(for: .editingChanged) let count = $0?.districts?.count ?? 0 if count == 0 { self.districtTextField.isHidden = true self.districtTextField.valid.onNext(true) } else { self.districtTextField.isHidden = false self.districtTextField.valid.onNext(false) } }).disposed(by: disposeBag) output.selectedDistrict.drive(onNext: { [weak self] in guard let `self` = self else { return } self.districtTextField.text = $0?.name self.districtTextField.sendActions(for: .editingChanged) }).disposed(by: disposeBag) output.selectedRelation.drive(onNext: { [weak self] in guard let `self` = self else { return } self.relationTextField.text = $0?.title self.relationTextField.sendActions(for: .editingChanged) }).disposed(by: disposeBag) output.selectedReason.drive(onNext: { [weak self] in guard let `self` = self else { return } self.reasonTextField.text = $0?.title self.reasonTextField.sendActions(for: .editingChanged) }).disposed(by: disposeBag) output.selectedNativeCountry.drive(onNext: { [weak self] in guard let `self` = self else { return } self.nativeCountryTextField.text = $0?.text self.nativeCountryTextField.sendActions(for: .editingChanged) }).disposed(by: disposeBag) output.models.dynamicFieldsModel.drive(onNext: { [weak self] in guard let `self` = self else { return } self.setDynamicFields(dynamicFieldInfo: $0) self.recipientBankInfomationContainerView.animateHidden(isHidden: false, duration: 0.5) self.recipientInformationContainerView.animateHidden(isHidden: false, duration: 0.5) }).disposed(by: disposeBag) } private func setDynamicFields(dynamicFieldInfo: [FieldModel]?) { dynamicFieldInfo?.forEach { [weak self] property in guard let `self` = self else { return } guard let key = property.fieldName, let textField = self.dynamicFieldDic?[key], let isRequire = DynamicFieldRequire(rawValue: property.required ?? ""), let min = property.minLength, let max = property.maxLength, let keyboardType = property.keyBoardType else { return } textField.errorMessage = "general_length_error_text".localized().replacingOccurrences( of: "xxx", with: "\(min)" ).replacingOccurrences(of: "ooo", with: "\(max)") switch isRequire { case .mandatory: textField.isHidden = false if max == min && min == -1 { textField.validCondition = { !$0.isEmpty } textField.errorMessage = nil } else { textField.validCondition = { ($0.count >= min && $0.count <= max) && !$0.isEmpty } } case .optional: textField.isHidden = false if max == min && min == -1 { textField.validCondition = nil textField.errorMessage = nil } else { textField.validCondition = { ($0.count >= min && $0.count <= max) } } if !textField.titleText.contains("optional_hint_text".localized()) { textField.titleText += "optional_hint_text".localized() textField.placeholder = "\(textField.placeholder ?? "") \("optional_hint_text".localized())" } case .hidden: textField.isHidden = true textField.text = "" } if key.contains("Local") { textField.keyboardType = .default } else { let keyType = GMEKeyboardType(rawValue: keyboardType) ?? GMEKeyboardType.alphabetNumbericSpecial switch keyType { case .numberic: textField.keyboardType = .numberPad default: textField.keyboardType = .asciiCapable } textField.rx.controlEvent(.editingChanged).asDriver() .drive(onNext: { _ in switch keyType { case .numberic: textField.fitlerBy(type: .alphabetNumberic, isSendAction: false) default: textField.fitlerBy(type: keyType, isSendAction: false) } }).disposed(by: disposeBag) } } guard let isValids = self.dynamicFieldDic?.values.filter({!$0.isHidden}).compactMap({$0.valid}) else {return} Observable .combineLatest(isValids).flatMap { Observable.just($0.allSatisfy {$0}) } .bind { [weak self] in guard let `self` = self else { return } self.saveButton.isEnabled = $0 self.saveButton.backgroundColor = $0 ? .themeRed : .lightGray } .disposed(by: disposeBag) idContainerView.isHidden = idTextField.isHidden && idNumberTextField.isHidden addressContainerView.isHidden = stateProvinceTextField.isHidden && districtTextField.isHidden && cityTextField.isHidden && addressTextField.isHidden otherContainerView.isHidden = mobileTextField.isHidden && emailTextField.isHidden && relationTextField.isHidden && reasonTextField.isHidden } private func setText(with recipient: Recipient?) { fullNameTextField.text = recipient?.fullName fullNameTextField.sendActions(for: .editingChanged) firstNameTextField.text = recipient?.firstName firstNameTextField.sendActions(for: .editingChanged) middleNameTextField.text = recipient?.middleName middleNameTextField.sendActions(for: .editingChanged) lastNameTextField.text = recipient?.lastName lastNameTextField.sendActions(for: .editingChanged) fullNameLocalTextField.text = recipient?.localFullName fullNameLocalTextField.sendActions(for: .editingChanged) firstNameLocalTextField.text = recipient?.localFirstName firstNameLocalTextField.sendActions(for: .editingChanged) middleNameLocalTextField.text = recipient?.localMiddleName middleNameLocalTextField.sendActions(for: .editingChanged) lastNameLocalTextField.text = recipient?.localLastName lastNameLocalTextField.sendActions(for: .editingChanged) accountTextField.text = recipient?.agent?.accountNumber accountTextField.sendActions(for: .editingChanged) idNumberTextField.text = recipient?.idNumber idNumberTextField.sendActions(for: .editingChanged) emailTextField.text = recipient?.email emailTextField.sendActions(for: .editingChanged) mobileTextField.setCoutryFlag(with: recipient?.countryCode) mobileTextField.mobileNumberText = recipient?.mobile mobileTextField.sendActions(for: .editingChanged) addressTextField.text = recipient?.address addressTextField.sendActions(for: .editingChanged) cityTextField.text = recipient?.city cityTextField.sendActions(for: .editingChanged) } }