ContactSettingsView.swift 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. // LoopFollow
  2. // ContactSettingsView.swift
  3. // Created by Jonas Björkert on 2024-12-10.
  4. import Contacts
  5. import SwiftUI
  6. struct ContactSettingsView: View {
  7. @ObservedObject var viewModel: ContactSettingsViewModel
  8. @State private var showAlert: Bool = false
  9. @State private var alertTitle: String = ""
  10. @State private var alertMessage: String = ""
  11. var body: some View {
  12. NavigationView {
  13. Form {
  14. Section(header: Text("Contact Integration")) {
  15. Text("Add the contact named '\(viewModel.contactName)' to your watch face to show the current BG value in real time. Make sure to give the app full access to Contacts when prompted.")
  16. .font(.footnote)
  17. .foregroundColor(.secondary)
  18. .padding(.vertical, 4)
  19. Toggle("Enable Contact BG Updates", isOn: $viewModel.contactEnabled)
  20. .toggleStyle(SwitchToggleStyle())
  21. .onChange(of: viewModel.contactEnabled) { isEnabled in
  22. if isEnabled {
  23. requestContactAccess()
  24. }
  25. }
  26. }
  27. if viewModel.contactEnabled {
  28. Section(header: Text("Color Options")) {
  29. Text("Select the colors for your BG values. Note: not all watch faces allow control over colors. Recommend options like Activity or Modular Duo if you want to customize colors.")
  30. .font(.footnote)
  31. .foregroundColor(.secondary)
  32. .padding(.vertical, 4)
  33. Picker("Select Background Color", selection: $viewModel.contactBackgroundColor) {
  34. ForEach(ContactColorOption.allCases, id: \.rawValue) { option in
  35. Text(option.rawValue.capitalized).tag(option.rawValue)
  36. }
  37. }
  38. Picker("Select Text Color", selection: $viewModel.contactTextColor) {
  39. ForEach(ContactColorOption.allCases, id: \.rawValue) { option in
  40. Text(option.rawValue.capitalized).tag(option.rawValue)
  41. }
  42. }
  43. }
  44. Section(header: Text("Additional Information")) {
  45. Text("To see your trend or delta, include one in the original '\(viewModel.contactName)' contact, or create separate contacts ending in '- Trend' and '- Delta' for up to three contacts on your watch face.")
  46. .font(.footnote)
  47. .foregroundColor(.secondary)
  48. .padding(.vertical, 4)
  49. Text("Show Trend")
  50. .font(.subheadline)
  51. Picker("Show Trend", selection: $viewModel.contactTrend) {
  52. ForEach(ContactIncludeOption.allCases, id: \.self) { option in
  53. Text(option.rawValue).tag(option)
  54. }
  55. }
  56. .pickerStyle(SegmentedPickerStyle())
  57. Text("Show Delta")
  58. .font(.subheadline)
  59. Picker("Show Delta", selection: $viewModel.contactDelta) {
  60. ForEach(ContactIncludeOption.allCases, id: \.self) { option in
  61. Text(option.rawValue).tag(option)
  62. }
  63. }
  64. .pickerStyle(SegmentedPickerStyle())
  65. }
  66. }
  67. }
  68. .alert(isPresented: $showAlert) {
  69. Alert(title: Text(alertTitle), message: Text(alertMessage), dismissButton: .default(Text("OK")))
  70. }
  71. }
  72. .preferredColorScheme(Storage.shared.forceDarkMode.value ? .dark : nil)
  73. .navigationBarTitle("Contact", displayMode: .inline)
  74. }
  75. private func requestContactAccess() {
  76. let contactStore = CNContactStore()
  77. let status = CNContactStore.authorizationStatus(for: .contacts)
  78. if status == .authorized {
  79. // Already authorized, do nothing
  80. } else if status == .notDetermined {
  81. contactStore.requestAccess(for: .contacts) { granted, _ in
  82. DispatchQueue.main.async {
  83. if !granted {
  84. viewModel.contactEnabled = false
  85. showAlert(title: "Access Denied", message: "Please allow access to Contacts in Settings to enable this feature.")
  86. }
  87. }
  88. }
  89. } else if status == .denied {
  90. viewModel.contactEnabled = false
  91. showAlert(title: "Access Denied", message: "Access to Contacts is denied. Please go to Settings and enable Contacts access.")
  92. } else if status == .restricted {
  93. viewModel.contactEnabled = false
  94. showAlert(title: "Access Restricted", message: "Access to Contacts is restricted.")
  95. } else {
  96. viewModel.contactEnabled = false
  97. showAlert(title: "Error", message: "An unknown error occurred while checking Contacts access.")
  98. }
  99. }
  100. private func showAlert(title: String, message: String) {
  101. alertTitle = title
  102. alertMessage = message
  103. showAlert = true
  104. }
  105. }