ShareClientSettingsViewController.swift 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. //
  2. // ShareClientSettingsViewController.swift
  3. // Loop
  4. //
  5. // Copyright © 2018 LoopKit Authors. All rights reserved.
  6. //
  7. import UIKit
  8. import Combine
  9. import HealthKit
  10. import LoopKit
  11. import LoopKitUI
  12. import ShareClient
  13. public class ShareClientSettingsViewController: UITableViewController {
  14. public let cgmManager: ShareClientManager
  15. private let displayGlucoseUnitObservable: DisplayGlucoseUnitObservable
  16. private lazy var cancellables = Set<AnyCancellable>()
  17. private var glucoseUnit: HKUnit {
  18. displayGlucoseUnitObservable.displayGlucoseUnit
  19. }
  20. public let allowsDeletion: Bool
  21. public init(cgmManager: ShareClientManager, displayGlucoseUnitObservable: DisplayGlucoseUnitObservable, allowsDeletion: Bool) {
  22. self.cgmManager = cgmManager
  23. self.displayGlucoseUnitObservable = displayGlucoseUnitObservable
  24. self.allowsDeletion = allowsDeletion
  25. super.init(style: .grouped)
  26. displayGlucoseUnitObservable.$displayGlucoseUnit
  27. .sink { [weak self] _ in self?.tableView.reloadData() }
  28. .store(in: &cancellables)
  29. }
  30. required public init?(coder aDecoder: NSCoder) {
  31. fatalError("init(coder:) has not been implemented")
  32. }
  33. override public func viewDidLoad() {
  34. super.viewDidLoad()
  35. title = cgmManager.localizedTitle
  36. tableView.rowHeight = UITableView.automaticDimension
  37. tableView.estimatedRowHeight = 44
  38. tableView.sectionHeaderHeight = UITableView.automaticDimension
  39. tableView.estimatedSectionHeaderHeight = 55
  40. tableView.register(SettingsTableViewCell.self, forCellReuseIdentifier: SettingsTableViewCell.className)
  41. tableView.register(TextButtonTableViewCell.self, forCellReuseIdentifier: TextButtonTableViewCell.className)
  42. let button = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(doneTapped(_:)))
  43. self.navigationItem.setRightBarButton(button, animated: false)
  44. }
  45. @objc func doneTapped(_ sender: Any) {
  46. complete()
  47. }
  48. private func complete() {
  49. if let nav = navigationController as? SettingsNavigationViewController {
  50. nav.notifyComplete()
  51. }
  52. }
  53. // MARK: - UITableViewDataSource
  54. private enum Section: Int, CaseIterable {
  55. case authentication
  56. case latestReading
  57. case delete
  58. }
  59. override public func numberOfSections(in tableView: UITableView) -> Int {
  60. return allowsDeletion ? Section.allCases.count : Section.allCases.count - 1
  61. }
  62. private enum LatestReadingRow: Int, CaseIterable {
  63. case glucose
  64. case date
  65. case trend
  66. }
  67. override public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  68. switch Section(rawValue: section)! {
  69. case .authentication:
  70. return 1
  71. case .latestReading:
  72. return LatestReadingRow.allCases.count
  73. case .delete:
  74. return 1
  75. }
  76. }
  77. private lazy var glucoseFormatter: QuantityFormatter = {
  78. let formatter = QuantityFormatter()
  79. formatter.setPreferredNumberFormatter(for: glucoseUnit)
  80. return formatter
  81. }()
  82. private lazy var dateFormatter: DateFormatter = {
  83. let formatter = DateFormatter()
  84. formatter.dateStyle = .long
  85. formatter.timeStyle = .long
  86. formatter.doesRelativeDateFormatting = true
  87. return formatter
  88. }()
  89. public override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  90. switch Section(rawValue: indexPath.section)! {
  91. case .authentication:
  92. let cell = tableView.dequeueReusableCell(withIdentifier: SettingsTableViewCell.className, for: indexPath) as! SettingsTableViewCell
  93. let service = cgmManager.shareService
  94. cell.textLabel?.text = LocalizedString("Credentials", comment: "Title of cell to set credentials")
  95. cell.detailTextLabel?.text = service.username ?? SettingsTableViewCell.TapToSetString
  96. cell.accessoryType = .disclosureIndicator
  97. return cell
  98. case .latestReading:
  99. let cell = tableView.dequeueReusableCell(withIdentifier: SettingsTableViewCell.className, for: indexPath) as! SettingsTableViewCell
  100. let glucose = cgmManager.latestBackfill
  101. switch LatestReadingRow(rawValue: indexPath.row)! {
  102. case .glucose:
  103. cell.textLabel?.text = LocalizedString("Glucose", comment: "Title describing glucose value")
  104. if let quantity = glucose?.quantity, let formatted = glucoseFormatter.string(from: quantity, for: glucoseUnit) {
  105. cell.detailTextLabel?.text = formatted
  106. } else {
  107. cell.detailTextLabel?.text = SettingsTableViewCell.NoValueString
  108. }
  109. case .date:
  110. cell.textLabel?.text = LocalizedString("Date", comment: "Title describing glucose date")
  111. if let date = glucose?.timestamp {
  112. cell.detailTextLabel?.text = dateFormatter.string(from: date)
  113. } else {
  114. cell.detailTextLabel?.text = SettingsTableViewCell.NoValueString
  115. }
  116. case .trend:
  117. cell.textLabel?.text = LocalizedString("Trend", comment: "Title describing glucose trend")
  118. cell.detailTextLabel?.text = glucose?.trendType?.localizedDescription ?? SettingsTableViewCell.NoValueString
  119. }
  120. return cell
  121. case .delete:
  122. let cell = tableView.dequeueReusableCell(withIdentifier: TextButtonTableViewCell.className, for: indexPath) as! TextButtonTableViewCell
  123. cell.textLabel?.text = LocalizedString("Delete CGM", comment: "Title text for the button to remove a CGM from Loop")
  124. cell.textLabel?.textAlignment = .center
  125. cell.tintColor = .delete
  126. cell.isEnabled = true
  127. return cell
  128. }
  129. }
  130. public override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
  131. switch Section(rawValue: section)! {
  132. case .authentication:
  133. return nil
  134. case .latestReading:
  135. return LocalizedString("Latest Reading", comment: "Section title for latest glucose reading")
  136. case .delete:
  137. return nil
  138. }
  139. }
  140. public override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
  141. switch Section(rawValue: indexPath.section)! {
  142. case .authentication:
  143. let vc = AuthenticationViewController(authentication: cgmManager.shareService)
  144. vc.authenticationObserver = { [weak self] (service) in
  145. self?.cgmManager.shareService = service
  146. self?.tableView.reloadRows(at: [indexPath], with: .none)
  147. }
  148. show(vc, sender: nil)
  149. case .latestReading:
  150. tableView.deselectRow(at: indexPath, animated: true)
  151. case .delete:
  152. let confirmVC = UIAlertController(cgmDeletionHandler: {
  153. self.cgmManager.notifyDelegateOfDeletion {
  154. DispatchQueue.main.async {
  155. self.complete()
  156. }
  157. }
  158. })
  159. present(confirmVC, animated: true) {
  160. tableView.deselectRow(at: indexPath, animated: true)
  161. }
  162. }
  163. }
  164. }
  165. private extension UIAlertController {
  166. convenience init(cgmDeletionHandler handler: @escaping () -> Void) {
  167. self.init(
  168. title: nil,
  169. message: LocalizedString("Are you sure you want to delete this CGM?", comment: "Confirmation message for deleting a CGM"),
  170. preferredStyle: .actionSheet
  171. )
  172. addAction(UIAlertAction(
  173. title: LocalizedString("Delete CGM", comment: "Button title to delete CGM"),
  174. style: .destructive,
  175. handler: { (_) in
  176. handler()
  177. }
  178. ))
  179. let cancel = LocalizedString("Cancel", comment: "The title of the cancel action in an action sheet")
  180. addAction(UIAlertAction(title: cancel, style: .cancel, handler: nil))
  181. }
  182. }