import Foundation import UIKit import Lottie import CoreData import KeychainSwift enum UploadResult { case Success case SessionExpired case Failed case FailedUpload case InvalidCode } class UploadDataStep2VC: UIViewController, CodeInputViewDelegate { func codeDidChange(codeInputView: CodeInputView) { updateUploadButton() } func codeDidFinish(codeInputView: CodeInputView) { updateUploadButton() } @IBOutlet weak var codeInputView: CodeInputView! @IBOutlet weak var scrollView: UIScrollView! @IBOutlet weak var activityIndicator: UIActivityIndicatorView! @IBOutlet weak var uploadErrorMsgLbl: UILabel! @IBOutlet weak var uploadDataButton: GradientButton! @IBOutlet weak var uploadingView: UIView! @IBOutlet weak var uploadAnimatedviewContainer: UIView! @IBOutlet var animationLeadingConstraint: NSLayoutConstraint! @IBOutlet var animationTrailingConstraint: NSLayoutConstraint! @IBOutlet var centredContainerViewBottomConstraint: NSLayoutConstraint! @IBOutlet var centredContainerViewTopConstraint: NSLayoutConstraint! var currentKeyboardFrame: CGRect? var uploadAnimatedView: AnimationView? var uploadDisplayErrorMsg = "" let invalidPinErrMsg = "action_verify_invalid_pin".localizedString(comment: "Invalid PIN, please ask health official to send another PIN.") let verifyEnabledColor = UIColor.covidSafeButtonDarkerColor let verifyDisabledColor = UIColor(red: 219/255.0, green: 221/255.0, blue: 221.0/255.0, alpha: 1.0) lazy var countdownFormatter: DateComponentsFormatter = { let formatter = DateComponentsFormatter() formatter.allowedUnits = [.minute, .second] formatter.unitsStyle = .positional formatter.zeroFormattingBehavior = .pad return formatter }() override func viewDidLoad() { _ = codeInputView.becomeFirstResponder() codeInputView.delegate = self dismissKeyboardOnTap() updateUploadButton() updateConstrainstsTo(orientation: UIDevice.current.orientation) if #available(iOS 13.0, *) { isModalInPresentation = true } } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) setIsLoading(false) if uploadAnimatedView == nil { let uploadAnimation = AnimationView(name: "Spinner_upload") uploadAnimation.loopMode = .loop uploadAnimation.frame = CGRect(origin: CGPoint(x: 0, y: 0), size: self.uploadAnimatedviewContainer.frame.size) self.uploadAnimatedviewContainer.addSubview(uploadAnimation) uploadAnimatedView = uploadAnimation } } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notif:)), name: UIResponder.keyboardWillHideNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChangeFrame(notif:)), name: UIResponder.keyboardWillChangeFrameNotification, object: nil) } override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) NotificationCenter.default.removeObserver(self) } override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { updateConstrainstsTo(orientation: UIDevice.current.orientation) } func updateConstrainstsTo(orientation: UIDeviceOrientation) { if orientation.isLandscape { centredContainerViewTopConstraint.isActive = true centredContainerViewBottomConstraint.isActive = true animationLeadingConstraint.isActive = false animationTrailingConstraint.isActive = false } else { centredContainerViewTopConstraint.isActive = false centredContainerViewBottomConstraint.isActive = false animationLeadingConstraint.isActive = true animationTrailingConstraint.isActive = true } } @objc func keyboardWillHide(notif: Notification) { self.currentKeyboardFrame = nil let contentInset = UIEdgeInsets.zero scrollView.contentInset = contentInset scrollView.scrollIndicatorInsets = contentInset } @objc func keyboardWillChangeFrame(notif: Notification) { let userInfo = notif.userInfo guard let keyboardFrame = userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return } currentKeyboardFrame = keyboardFrame updateScrollviewContentInset() } func updateScrollviewContentInset() { guard let keyboardFrame = currentKeyboardFrame else { return } // the reason for the view.frame.height - scrollview.frame.height is because the scrollview is not full height of the view // so we don't need to inset the complete keyboard height let offsetKeyboardBottom = keyboardFrame.height - (view.frame.height - scrollView.frame.height) let contentInset = UIEdgeInsets(top: 0.0, left: 0.0, bottom: offsetKeyboardBottom, right: 0.0) scrollView.contentInset = contentInset scrollView.scrollIndicatorInsets = contentInset scrollView.scrollRectToVisible(codeInputView.frame, animated: true) } func updateUploadButton() { if let OTP = codeInputView?.text { uploadDataButton.isEnabled = OTP.count == 6 uploadDataButton.backgroundColor = OTP.count == 6 ? verifyEnabledColor : verifyDisabledColor } else { uploadDataButton.isEnabled = false uploadDataButton.backgroundColor = verifyDisabledColor } } @IBAction func backBtnTapped(_ sender: UIButton) { navigationController?.popViewController(animated: true) } @IBAction func uploadDataBtnTapped(_ sender: UIButton) { uploadErrorMsgLbl.isHidden = true let code = codeInputView.text guard code.range(of: "^[0-9]{6}$", options: .regularExpression) != nil else { uploadErrorMsgLbl.isHidden = false uploadErrorMsgLbl.text = invalidPinErrMsg return } setIsLoading(true) self.codeInputView.invalidCode = false UploadHelper.uploadEncounterData(pin: code) { result, errorMessage in self.setIsLoading(false) switch result{ case .InvalidCode: self.codeInputView.invalidCode = true self.uploadErrorMsgLbl.text = self.invalidPinErrMsg self.uploadErrorMsgLbl.isHidden = false case .Success: UserDefaults.standard.set(Date().timeIntervalSince1970, forKey: "uploadDataDate") let firstUploadDate = UserDefaults.standard.double(forKey: "firstUploadDataDate") if(firstUploadDate == 0){ UserDefaults.standard.set(Date().timeIntervalSince1970, forKey: "firstUploadDataDate") } Encounter.deleteAll() self.performSegue(withIdentifier: "showSuccessVCSegue", sender: nil) case .FailedUpload, .Failed: self.uploadDisplayErrorMsg = errorMessage ?? "No error code/message available" self.performSegue(withIdentifier: "uploadErrorSegue", sender: self) sender.isEnabled = true case .SessionExpired: NotificationCenter.default.post(name: .jwtExpired, object: nil) sender.isEnabled = true } } } private func setIsLoading(_ isLoading: Bool) { if isLoading { uploadAnimatedView?.play() uploadingView.alpha = 1 uploadingView.isHidden = false } else { uploadAnimatedView?.stop() uploadingView.alpha = 0 uploadingView.isHidden = true } } override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if let errorViewController = segue.destination as? UploadDataErrorViewController { errorViewController.uploadErrorMessage = self.uploadDisplayErrorMsg } } }