FeedbackViewController.swift 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. // Copyright 2023 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. import UIKit
  15. class FeedbackViewController: UIViewController, UITextViewDelegate {
  16. // TODO: Consider the situations where this instance is initiated once, and used
  17. // multiple times.
  18. var viewDidDisappearCallback: () -> Void = {}
  19. // (TODO) Can we make feedbackName and additionalFormText non-null?
  20. var releaseName: String?
  21. var additionalFormText: String?
  22. var image: UIImage?
  23. @IBOutlet var screenshotUIImageView: UIImageView!
  24. @IBOutlet var additionalFormTextLabel: UILabel!
  25. @IBOutlet var feedbackTextView: UITextView!
  26. @IBOutlet var navigationBar: UINavigationBar!
  27. @IBOutlet var scrollView: UIScrollView!
  28. // MARK: - UIViewController
  29. override func viewDidLoad() {
  30. super.viewDidLoad()
  31. // Do any additional setup after loading the view.
  32. feedbackTextView.isScrollEnabled = false
  33. setScrollViewConstraints()
  34. setAdditionalFormTextConstraints()
  35. setFeedbackInputConstraints()
  36. setScreenshotImageConstrains()
  37. let additionalFormText = additionalFormText
  38. if additionalFormText != nil {
  39. additionalFormTextLabel.text = additionalFormText
  40. }
  41. feedbackTextView.delegate = self
  42. resetFeedbackTextViewWithPlaceholderText()
  43. if let image = image {
  44. screenshotUIImageView.image = image
  45. self.image = nil
  46. }
  47. }
  48. override func viewDidDisappear(_ animated: Bool) {
  49. viewDidDisappearCallback()
  50. }
  51. // MARK: - Actions
  52. @IBAction func tappedSend(_ sender: Any) {
  53. guard let releaseName = releaseName else {
  54. // TODO(tundeagboola) throw error or
  55. return
  56. }
  57. ApiService
  58. .createFeedback(releaseName: releaseName,
  59. feedbackText: feedbackTextView.text) { feedbackName, error in
  60. if error != nil {
  61. // TODO(tundeaboola) handle error if create feedback fails
  62. return
  63. }
  64. guard let feedbackName = feedbackName else {
  65. // TODO(tundeaboola) handle error if create feedback fails
  66. return
  67. }
  68. guard let image = self.screenshotUIImageView.image else {
  69. return self.commitFeedback(feedbackName: feedbackName)
  70. }
  71. ApiService.uploadImage(feedbackName: feedbackName, image: image) { error in
  72. if error != nil {
  73. // TODO(tundeaboola) handle error if upload image fails
  74. return
  75. }
  76. self.commitFeedback(feedbackName: feedbackName)
  77. }
  78. }
  79. }
  80. @IBAction func tappedCancel(_ sender: Any) {
  81. dismiss(animated: true)
  82. }
  83. // MARK: - Utilties
  84. private func commitFeedback(feedbackName: String) {
  85. ApiService.commitFeedback(feedbackName: feedbackName) { error in
  86. if error != nil {
  87. // TODO(tundeaboola) handle error if commit feedback fails
  88. }
  89. self.feedbackSubmitted()
  90. }
  91. }
  92. func feedbackSubmitted() {
  93. // TODO(tundeagboola) show success toast
  94. dismiss(animated: true)
  95. }
  96. // MARK: - UI Constraints
  97. func setScrollViewConstraints() {
  98. scrollView.translatesAutoresizingMaskIntoConstraints = false
  99. scrollView.alwaysBounceVertical = true
  100. let bottomConstraint = NSLayoutConstraint(
  101. item: scrollView!,
  102. attribute: .bottom,
  103. relatedBy: .equal,
  104. toItem: view,
  105. attribute: .bottom,
  106. multiplier: 1,
  107. constant: 0
  108. )
  109. let leftConstraint = NSLayoutConstraint(
  110. item: scrollView!,
  111. attribute: .left,
  112. relatedBy: .equal,
  113. toItem: navigationBar,
  114. attribute: .left,
  115. multiplier: 1,
  116. constant: 0
  117. )
  118. let rightConstraint = NSLayoutConstraint(
  119. item: scrollView!,
  120. attribute: .right,
  121. relatedBy: .equal,
  122. toItem: navigationBar,
  123. attribute: .right,
  124. multiplier: 1,
  125. constant: 0
  126. )
  127. view.addConstraints([bottomConstraint, leftConstraint, rightConstraint])
  128. }
  129. func setAdditionalFormTextConstraints() {
  130. additionalFormTextLabel.translatesAutoresizingMaskIntoConstraints = false
  131. additionalFormTextLabel.numberOfLines = 0
  132. let topConstraint = NSLayoutConstraint(
  133. item: additionalFormTextLabel!,
  134. attribute: .top,
  135. relatedBy: .equal,
  136. toItem: scrollView,
  137. attribute: .top,
  138. multiplier: 1,
  139. constant: 0
  140. )
  141. let bottomConstraint = NSLayoutConstraint(
  142. item: additionalFormTextLabel!,
  143. attribute: .bottom,
  144. relatedBy: .greaterThanOrEqual,
  145. toItem: scrollView,
  146. attribute: .top,
  147. multiplier: 1,
  148. constant: 40
  149. )
  150. let leftConstraint = NSLayoutConstraint(
  151. item: additionalFormTextLabel!,
  152. attribute: .left,
  153. relatedBy: .equal,
  154. toItem: scrollView,
  155. attribute: .left,
  156. multiplier: 1,
  157. constant: 0
  158. )
  159. let rightConstraint = NSLayoutConstraint(
  160. item: additionalFormTextLabel!,
  161. attribute: .right,
  162. relatedBy: .equal,
  163. toItem: scrollView,
  164. attribute: .right,
  165. multiplier: 1,
  166. constant: 0
  167. )
  168. scrollView.addConstraints([topConstraint, bottomConstraint, leftConstraint, rightConstraint])
  169. let widthConstraint = NSLayoutConstraint(
  170. item: additionalFormTextLabel!,
  171. attribute: .width,
  172. relatedBy: .equal,
  173. toItem: navigationBar,
  174. attribute: .width,
  175. multiplier: 1,
  176. constant: 0
  177. )
  178. view
  179. .addConstraints([topConstraint, bottomConstraint, leftConstraint, rightConstraint,
  180. widthConstraint])
  181. // TODO: Better color
  182. additionalFormTextLabel.backgroundColor = .lightGray
  183. }
  184. func setFeedbackInputConstraints() {
  185. feedbackTextView.translatesAutoresizingMaskIntoConstraints = false
  186. let topConstraint = NSLayoutConstraint(
  187. item: feedbackTextView!,
  188. attribute: .top,
  189. relatedBy: .equal,
  190. toItem: additionalFormTextLabel,
  191. attribute: .bottom,
  192. multiplier: 1,
  193. constant: 0
  194. )
  195. let bottomConstraint = NSLayoutConstraint(
  196. item: feedbackTextView!,
  197. attribute: .bottom,
  198. relatedBy: .greaterThanOrEqual,
  199. toItem: additionalFormTextLabel,
  200. attribute: .top,
  201. multiplier: 1,
  202. constant: 100
  203. )
  204. let leftConstraint = NSLayoutConstraint(
  205. item: feedbackTextView!,
  206. attribute: .left,
  207. relatedBy: .equal,
  208. toItem: scrollView,
  209. attribute: .left,
  210. multiplier: 1,
  211. constant: 0
  212. )
  213. let rightConstraint = NSLayoutConstraint(
  214. item: feedbackTextView!,
  215. attribute: .right,
  216. relatedBy: .equal,
  217. toItem: scrollView,
  218. attribute: .right,
  219. multiplier: 1,
  220. constant: 0
  221. )
  222. let widthConstraint = NSLayoutConstraint(
  223. item: feedbackTextView!,
  224. attribute: .width,
  225. relatedBy: .equal,
  226. toItem: navigationBar,
  227. attribute: .width,
  228. multiplier: 1,
  229. constant: 0
  230. )
  231. view
  232. .addConstraints([topConstraint, bottomConstraint, leftConstraint, rightConstraint,
  233. widthConstraint])
  234. feedbackTextView.textContainerInset = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
  235. }
  236. func setScreenshotImageConstrains() {
  237. screenshotUIImageView.translatesAutoresizingMaskIntoConstraints = false
  238. let topConstraint = NSLayoutConstraint(
  239. item: screenshotUIImageView!,
  240. attribute: .top,
  241. relatedBy: .equal,
  242. toItem: feedbackTextView,
  243. attribute: .bottom,
  244. multiplier: 1,
  245. constant: 0
  246. )
  247. let bottomConstraint = NSLayoutConstraint(
  248. item: screenshotUIImageView!,
  249. attribute: .bottom,
  250. relatedBy: .greaterThanOrEqual,
  251. toItem: scrollView,
  252. attribute: .bottom,
  253. multiplier: 1,
  254. constant: 10
  255. )
  256. let leftConstraint = NSLayoutConstraint(
  257. item: screenshotUIImageView!,
  258. attribute: .left,
  259. relatedBy: .equal,
  260. toItem: scrollView,
  261. attribute: .left,
  262. multiplier: 1,
  263. constant: 0
  264. )
  265. let rightConstraint = NSLayoutConstraint(
  266. item: screenshotUIImageView!,
  267. attribute: .right,
  268. relatedBy: .equal,
  269. toItem: scrollView,
  270. attribute: .right,
  271. multiplier: 1,
  272. constant: 0
  273. )
  274. let widthConstraint = NSLayoutConstraint(
  275. item: screenshotUIImageView!,
  276. attribute: .width,
  277. relatedBy: .equal,
  278. toItem: feedbackTextView,
  279. attribute: .width,
  280. multiplier: 1,
  281. constant: 0
  282. )
  283. scrollView
  284. .addConstraints([topConstraint, bottomConstraint, leftConstraint, rightConstraint,
  285. widthConstraint])
  286. }
  287. // MARK: - UITextViewDelegate
  288. func textViewDidBeginEditing(_ textView: UITextView) {
  289. textView.text = nil
  290. textView.textColor = .black
  291. }
  292. func textViewDidEndEditing(_ textView: UITextView) {
  293. if textView.text.isEmpty {
  294. resetFeedbackTextViewWithPlaceholderText()
  295. }
  296. }
  297. func resetFeedbackTextViewWithPlaceholderText() {
  298. feedbackTextView.text = "Do you have any feedback?"
  299. feedbackTextView.textColor = .lightGray
  300. }
  301. }