2020-05-08 07:49:14 +00:00
|
|
|
// Copyright © 2020 Australian Government All rights reserved.
|
|
|
|
|
|
|
|
import UIKit
|
|
|
|
|
|
|
|
final class FeedbackViewController: UIViewController {
|
|
|
|
|
|
|
|
@IBOutlet var issueTextView: UITextView!
|
|
|
|
@IBOutlet var issuePlaceholderLabel: UILabel!
|
|
|
|
@IBOutlet var emailTextField: UITextField!
|
|
|
|
@IBOutlet var scrollView: UIScrollView!
|
|
|
|
@IBOutlet var thankYouView: UIView!
|
|
|
|
|
|
|
|
private var sendFeebackAction: SendFeedbackAction?
|
|
|
|
|
|
|
|
var settings: FeedbackSettings?
|
|
|
|
var onDidFinish: (() -> Void)?
|
|
|
|
var flowNavBarStyle: UIStatusBarStyle = UIApplication.shared.statusBarStyle
|
|
|
|
|
|
|
|
enum State {
|
|
|
|
case idle
|
|
|
|
case sending
|
|
|
|
case sent
|
|
|
|
}
|
|
|
|
|
|
|
|
private var state: State = .idle {
|
|
|
|
didSet {
|
|
|
|
updateUI()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private lazy var sendBarButtonItem: UIBarButtonItem = {
|
2020-06-19 07:43:33 +00:00
|
|
|
let item = UIBarButtonItem(title: "global_send_button_title".localizedString(comment: "Send Button"),
|
|
|
|
style: .done,
|
|
|
|
target: self,
|
|
|
|
action: #selector(sendButtonTapped))
|
|
|
|
item.tintColor = .covidSafeColor
|
2020-05-08 07:49:14 +00:00
|
|
|
return item
|
|
|
|
}()
|
|
|
|
|
|
|
|
private lazy var doneButtonItem: UIBarButtonItem = {
|
2020-06-19 07:43:33 +00:00
|
|
|
let item = UIBarButtonItem(title: "Done".localizedString(), style: .done, target: self, action: #selector(doneButtonTapped))
|
2020-05-08 07:49:14 +00:00
|
|
|
item.tintColor = .covidSafeColor
|
|
|
|
return item
|
|
|
|
}()
|
|
|
|
|
|
|
|
override func viewDidLoad() {
|
|
|
|
super.viewDidLoad()
|
|
|
|
if #available(iOS 13.0, *) {
|
|
|
|
isModalInPresentation = true
|
|
|
|
}
|
|
|
|
setup()
|
|
|
|
}
|
|
|
|
|
|
|
|
@objc func cancel() {
|
|
|
|
if issueTextView.isFirstResponder {
|
|
|
|
issueTextView.resignFirstResponder()
|
|
|
|
}
|
|
|
|
|
|
|
|
if emailTextField.isFirstResponder {
|
|
|
|
emailTextField.resignFirstResponder()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func presentKeyboard() {
|
|
|
|
issueTextView.becomeFirstResponder()
|
|
|
|
}
|
|
|
|
|
|
|
|
private func updateUI() {
|
|
|
|
switch state {
|
|
|
|
case .idle:
|
|
|
|
dismissKeyboard()
|
|
|
|
showSendButton()
|
|
|
|
issueTextView.isEditable = true
|
|
|
|
emailTextField.isEnabled = true
|
|
|
|
case .sending:
|
|
|
|
showSpinner()
|
|
|
|
issueTextView.isEditable = false
|
|
|
|
emailTextField.isEnabled = false
|
|
|
|
case .sent:
|
|
|
|
hideCancelButton()
|
|
|
|
showDoneButton()
|
|
|
|
showThankYouView()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private func showSendButton() {
|
|
|
|
navigationItem.rightBarButtonItem = sendBarButtonItem
|
|
|
|
}
|
|
|
|
|
|
|
|
private func showSpinner() {
|
|
|
|
let activityView = UIActivityIndicatorView(style: .gray)
|
|
|
|
activityView.startAnimating()
|
|
|
|
let spinnerBarItem = UIBarButtonItem(customView: activityView)
|
|
|
|
navigationItem.rightBarButtonItem = spinnerBarItem
|
|
|
|
}
|
|
|
|
|
|
|
|
private func showDoneButton() {
|
|
|
|
navigationItem.rightBarButtonItem = doneButtonItem
|
|
|
|
}
|
|
|
|
|
|
|
|
private func hideCancelButton() {
|
|
|
|
navigationItem.leftBarButtonItem = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
private func showThankYouView() {
|
|
|
|
UIView.animate(withDuration: 0.3) { [weak self] in
|
|
|
|
self?.scrollView.isHidden = true
|
|
|
|
self?.thankYouView.isHidden = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private func setup() {
|
2020-06-19 07:43:33 +00:00
|
|
|
self.title = "newFeedbackFlow_navigationTitle".localizedString(comment: "Title for feedback flow navigation")
|
2020-05-08 07:49:14 +00:00
|
|
|
|
|
|
|
issueTextView.textContainer.lineFragmentPadding = 0.0
|
|
|
|
setupDelegates()
|
|
|
|
setupKeyboardNotifications()
|
|
|
|
setupBarButtonItems()
|
|
|
|
}
|
|
|
|
|
|
|
|
private func setupDelegates() {
|
|
|
|
issueTextView.delegate = self
|
|
|
|
emailTextField.addTarget(self, action: #selector(updateSendButton), for: .editingChanged)
|
|
|
|
}
|
|
|
|
|
|
|
|
private func setupKeyboardNotifications() {
|
|
|
|
let notificationCenter = NotificationCenter.default
|
|
|
|
notificationCenter.addObserver(self, selector: #selector(adjustForKeyboard), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
|
|
|
|
notificationCenter.addObserver(self, selector: #selector(adjustForKeyboard), name: UIResponder.keyboardWillHideNotification, object: nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
private func setupBarButtonItems() {
|
|
|
|
navigationItem.rightBarButtonItem = sendBarButtonItem
|
|
|
|
sendBarButtonItem.isEnabled = false
|
|
|
|
}
|
|
|
|
|
|
|
|
@objc private func updateSendButton() {
|
|
|
|
sendBarButtonItem.isEnabled = !issueTextView.text.isEmpty && !(emailTextField.text?.isEmpty ?? true)
|
|
|
|
}
|
|
|
|
|
|
|
|
private func updatePlaceholder() {
|
|
|
|
issuePlaceholderLabel.isHidden = !issueTextView.text.isEmpty
|
|
|
|
}
|
|
|
|
|
|
|
|
@objc private func sendButtonTapped(_ sender: Any) {
|
|
|
|
guard emailTextField.isValid else {
|
2020-06-19 07:43:33 +00:00
|
|
|
let errorMessage = "newFeedback_invalidEmail_errorMessage".localizedString(comment: "Please enter a valid email address!")
|
2020-05-08 07:49:14 +00:00
|
|
|
showErrorMessage(errorMessage)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
state = .sending
|
|
|
|
send()
|
|
|
|
}
|
|
|
|
|
|
|
|
@objc private func doneButtonTapped(_ sender: Any) {
|
|
|
|
finish()
|
|
|
|
}
|
|
|
|
|
|
|
|
private func finish() {
|
|
|
|
onDidFinish?()
|
|
|
|
// Make sure we call the closure only once
|
|
|
|
onDidFinish = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
private func send() {
|
|
|
|
guard let settings = settings else {
|
|
|
|
assertionFailure("Feedback settings not provided, feedback will be lost")
|
|
|
|
state = .idle
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
let deviceInfo = UIDevice.current.infoAsDictionary
|
|
|
|
let modelName = UIDevice.modelName
|
|
|
|
let bundleInfo = Bundle.main.infoAsDictionary
|
|
|
|
|
|
|
|
var customFields = settings.customFields
|
|
|
|
|
|
|
|
if let email = emailTextField.text {
|
|
|
|
customFields["E-mail"] = email as AnyObject
|
|
|
|
}
|
|
|
|
|
|
|
|
if let osVersion = deviceInfo["systemVersion"] {
|
|
|
|
customFields["OS version"] = osVersion
|
|
|
|
}
|
|
|
|
|
|
|
|
if let appVersion = bundleInfo["appVersion"] {
|
|
|
|
customFields["App version"] = appVersion
|
|
|
|
}
|
|
|
|
|
|
|
|
customFields["Phone model"] = modelName as AnyObject
|
|
|
|
|
|
|
|
let issue = Issue(
|
|
|
|
feedback: issueTextView.text,
|
|
|
|
components: settings.issueComponents,
|
|
|
|
type: settings.issueType,
|
|
|
|
customFields: customFields,
|
|
|
|
reporterUsernameOrEmail: nil
|
|
|
|
)
|
|
|
|
let action = SendFeedbackAction(issue: issue, screenshotImageOrNil: nil) { outcome in
|
|
|
|
switch outcome {
|
|
|
|
case .success:
|
|
|
|
self.state = .sent
|
|
|
|
delayOnMainQueue(2) {
|
|
|
|
self.finish()
|
|
|
|
}
|
|
|
|
|
|
|
|
case.error:
|
|
|
|
self.state = .idle
|
2020-06-19 07:43:33 +00:00
|
|
|
let errorMessage = "newFeedback_send_errorMessage".localizedString(comment: "Generic error message shown when feedback could not be sent")
|
2020-05-08 07:49:14 +00:00
|
|
|
|
|
|
|
self.showErrorMessage(errorMessage)
|
|
|
|
|
|
|
|
case .cancelled:
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
action.start()
|
|
|
|
sendFeebackAction = action
|
|
|
|
}
|
|
|
|
|
|
|
|
private func showErrorMessage(_ message: String) {
|
|
|
|
let alert = AlertController(title: "COVIDSafe", message: message, preferredStyle: .alert)
|
2020-07-03 04:26:13 +00:00
|
|
|
let okActionTitle = "global_OK".localizedString()
|
2020-05-08 07:49:14 +00:00
|
|
|
let okAction = UIAlertAction(title: okActionTitle, style: .default)
|
|
|
|
alert.addAction(okAction)
|
|
|
|
present(alert, animated: true)
|
|
|
|
}
|
|
|
|
|
|
|
|
@objc func adjustForKeyboard(notification: Notification) {
|
|
|
|
guard let keyboardValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return }
|
|
|
|
let keyboardScreenEndFrame = keyboardValue.cgRectValue
|
|
|
|
let keyboardViewEndFrame = view.convert(keyboardScreenEndFrame, from: view.window)
|
|
|
|
|
|
|
|
if notification.name == UIResponder.keyboardWillHideNotification {
|
|
|
|
scrollView.contentInset = .zero
|
|
|
|
} else {
|
|
|
|
if #available(iOS 11.0, *) {
|
|
|
|
scrollView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: keyboardViewEndFrame.height - view.safeAreaInsets.bottom, right: 0)
|
|
|
|
} else {
|
|
|
|
scrollView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: keyboardViewEndFrame.height, right: 0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
scrollView.scrollIndicatorInsets = scrollView.contentInset
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func delayOnMainQueue(_ delay: Double, closure:@escaping () -> Void) {
|
|
|
|
DispatchQueue.main.asyncAfter(
|
|
|
|
deadline: DispatchTime.now() + Double(Int64(delay * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute: closure)
|
|
|
|
}
|
|
|
|
|
|
|
|
extension FeedbackViewController: UITextViewDelegate {
|
|
|
|
func textViewDidChange(_ textView: UITextView) {
|
|
|
|
updatePlaceholder()
|
|
|
|
updateSendButton()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private extension UITextField {
|
|
|
|
var isValid: Bool {
|
|
|
|
let emailRegEx = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
|
|
|
|
let emailPred = NSPredicate(format:"SELF MATCHES %@", emailRegEx)
|
|
|
|
return emailPred.evaluate(with: text)
|
|
|
|
}
|
|
|
|
}
|