Jonas Björkert hai 1 ano
pai
achega
570d0a016c

+ 16 - 0
LoopFollow.xcodeproj/project.pbxproj

@@ -192,6 +192,10 @@
 		DDDF6F472D479AD200884336 /* LoopOverrideViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDDF6F462D479AD100884336 /* LoopOverrideViewModel.swift */; };
 		DDDF6F492D479AF000884336 /* NoRemoteView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDDF6F482D479AEF00884336 /* NoRemoteView.swift */; };
 		DDE69ED22C7256260013EAEC /* RemoteType.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE69ED12C7256260013EAEC /* RemoteType.swift */; };
+		DDE75D232DE5E505007C1FC1 /* Glyph.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE75D222DE5E505007C1FC1 /* Glyph.swift */; };
+		DDE75D272DE5E539007C1FC1 /* ActionRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE75D262DE5E539007C1FC1 /* ActionRow.swift */; };
+		DDE75D292DE5E56C007C1FC1 /* LinkRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE75D282DE5E56C007C1FC1 /* LinkRow.swift */; };
+		DDE75D2B2DE5E613007C1FC1 /* NavigationRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE75D2A2DE5E613007C1FC1 /* NavigationRow.swift */; };
 		DDEF503A2D31615000999A5D /* LogManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDEF50392D31614200999A5D /* LogManager.swift */; };
 		DDEF503C2D31BE2D00999A5D /* TaskScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDEF503B2D31BE2A00999A5D /* TaskScheduler.swift */; };
 		DDEF503F2D32754F00999A5D /* ProfileTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDEF503E2D32754A00999A5D /* ProfileTask.swift */; };
@@ -558,6 +562,10 @@
 		DDDF6F462D479AD100884336 /* LoopOverrideViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoopOverrideViewModel.swift; sourceTree = "<group>"; };
 		DDDF6F482D479AEF00884336 /* NoRemoteView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoRemoteView.swift; sourceTree = "<group>"; };
 		DDE69ED12C7256260013EAEC /* RemoteType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteType.swift; sourceTree = "<group>"; };
+		DDE75D222DE5E505007C1FC1 /* Glyph.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Glyph.swift; sourceTree = "<group>"; };
+		DDE75D262DE5E539007C1FC1 /* ActionRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionRow.swift; sourceTree = "<group>"; };
+		DDE75D282DE5E56C007C1FC1 /* LinkRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkRow.swift; sourceTree = "<group>"; };
+		DDE75D2A2DE5E613007C1FC1 /* NavigationRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationRow.swift; sourceTree = "<group>"; };
 		DDEF50392D31614200999A5D /* LogManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogManager.swift; sourceTree = "<group>"; };
 		DDEF503B2D31BE2A00999A5D /* TaskScheduler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskScheduler.swift; sourceTree = "<group>"; };
 		DDEF503E2D32754A00999A5D /* ProfileTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileTask.swift; sourceTree = "<group>"; };
@@ -1172,11 +1180,15 @@
 		DDF6999C2C5AAA4C0058A8D9 /* Views */ = {
 			isa = PBXGroup;
 			children = (
+				DDE75D222DE5E505007C1FC1 /* Glyph.swift */,
 				DD8316492DE4C504004467AA /* SettingsStepperRow.swift */,
 				DD8316432DE47CA9004467AA /* BGPicker.swift */,
 				DDF6999D2C5AAA640058A8D9 /* ErrorMessageView.swift */,
 				DD16AF0E2C99592F00FB655A /* HKQuantityInputView.swift */,
 				DD16AF102C997B4600FB655A /* LoadingButtonView.swift */,
+				DDE75D262DE5E539007C1FC1 /* ActionRow.swift */,
+				DDE75D282DE5E56C007C1FC1 /* LinkRow.swift */,
+				DDE75D2A2DE5E613007C1FC1 /* NavigationRow.swift */,
 			);
 			path = Views;
 			sourceTree = "<group>";
@@ -1811,6 +1823,7 @@
 				DD4AFB492DB576C200BB593F /* AlarmSettingsView.swift in Sources */,
 				DD7F4C032DD4B92E00D449E9 /* NotLoopingAlarmEditor.swift in Sources */,
 				DD7F4C1B2DD6501D00D449E9 /* COBCondition.swift in Sources */,
+				DDE75D232DE5E505007C1FC1 /* Glyph.swift in Sources */,
 				DD4878202C7DAF890048F05C /* PushMessage.swift in Sources */,
 				DD0247712DB4337700FCADF6 /* BuildExpireCondition.swift in Sources */,
 				DDF6999B2C5AA32E0058A8D9 /* TempTargetPreset.swift in Sources */,
@@ -1865,6 +1878,7 @@
 				DDB0AF522BB1A8BE00AFA48B /* BuildDetails.swift in Sources */,
 				DDA9ACAA2D6A6B8300E6F1A9 /* ContactIncludeOption.swift in Sources */,
 				DD7F4C1D2DD650D500D449E9 /* COBAlarmEditor.swift in Sources */,
+				DDE75D272DE5E539007C1FC1 /* ActionRow.swift in Sources */,
 				DD0C0C622C4175FD00DBADDF /* NSProfile.swift in Sources */,
 				DD8316182DE3633D004467AA /* GeneralSettingsView.swift in Sources */,
 				DD58171E2D299FCA0041FB98 /* BluetoothDeviceDelegate.swift in Sources */,
@@ -1884,6 +1898,7 @@
 				FCFEECA2248857A600402A7F /* SettingsViewController.swift in Sources */,
 				DD7F4C232DD7A62200D449E9 /* AlarmType+SortDirection.swift in Sources */,
 				DD0650F72DCFDA26004D3B41 /* InfoBanner.swift in Sources */,
+				DDE75D292DE5E56C007C1FC1 /* LinkRow.swift in Sources */,
 				DD4AFB612DB68BBC00BB593F /* AlarmListView.swift in Sources */,
 				DD7B0D442D730A3B0063DCB6 /* CycleHelper.swift in Sources */,
 				DD7F4BA12DD2193F00D449E9 /* AlarmSnoozeSection.swift in Sources */,
@@ -1951,6 +1966,7 @@
 				DD608A0C2C27415C00F91132 /* BackgroundAlertManager.swift in Sources */,
 				DD50C7532D0828D10057AE6F /* ContactSettingsView.swift in Sources */,
 				DD4878082C7B30BF0048F05C /* RemoteSettingsView.swift in Sources */,
+				DDE75D2B2DE5E613007C1FC1 /* NavigationRow.swift in Sources */,
 				DD4AFB3B2DB55CB600BB593F /* TimeOfDay.swift in Sources */,
 				DDEF503C2D31BE2D00999A5D /* TaskScheduler.swift in Sources */,
 				DD0650EB2DCE8385004D3B41 /* LowBGCondition.swift in Sources */,

+ 25 - 0
LoopFollow/Helpers/Views/ActionRow.swift

@@ -0,0 +1,25 @@
+// LoopFollow
+// ActionRow.swift
+// Created by Jonas Björkert on 2025-05-27.
+
+import SwiftUI
+
+@ViewBuilder
+func ActionRow(
+    title: String,
+    icon: String,
+    tint: Color = .white,
+    action: @escaping () -> Void
+) -> some View {
+    Button { action() } label: {
+        HStack {
+            Glyph(symbol: icon, tint: tint)
+            Text(title)
+            Spacer()
+            Image(systemName: "chevron.right")
+                .foregroundColor(Color(uiColor: .tertiaryLabel))
+        }
+        .contentShape(Rectangle())
+    }
+    .buttonStyle(.plain)
+}

+ 25 - 0
LoopFollow/Helpers/Views/Glyph.swift

@@ -0,0 +1,25 @@
+// LoopFollow
+// Glyph.swift
+// Created by Jonas Björkert on 2025-05-27.
+
+import SwiftUICore
+
+struct Glyph: View {
+    let symbol: String
+    let tint: Color
+
+    @Environment(\.colorScheme) private var scheme
+
+    var body: some View {
+        ZStack {
+            RoundedRectangle(cornerRadius: 8, style: .continuous)
+                .fill(Color(uiColor: .systemGray))
+                .frame(width: 28, height: 28)
+
+            Image(systemName: symbol)
+                .font(.system(size: 16, weight: .regular))
+                .foregroundStyle(tint)
+        }
+        .frame(width: 36, height: 36)
+    }
+}

+ 18 - 0
LoopFollow/Helpers/Views/LinkRow.swift

@@ -0,0 +1,18 @@
+// LoopFollow
+// LinkRow.swift
+// Created by Jonas Björkert on 2025-05-27.
+
+import Foundation
+import SwiftUI
+
+@ViewBuilder
+func LinkRow(
+    title: String,
+    icon: String,
+    tint: Color = .white,
+    url: URL
+) -> some View {
+    ActionRow(title: title, icon: icon, tint: tint) {
+        UIApplication.shared.open(url)
+    }
+}

+ 26 - 0
LoopFollow/Helpers/Views/NavigationRow.swift

@@ -0,0 +1,26 @@
+// LoopFollow
+// NavigationRow.swift
+// Created by Jonas Björkert on 2025-05-27.
+
+import SwiftUI
+
+struct NavigationRow: View {
+    let title: String
+    let icon: String
+    var iconTint: Color = .white
+    let action: () -> Void
+
+    var body: some View {
+        Button(action: action) {
+            HStack {
+                Glyph(symbol: icon, tint: iconTint)
+                Text(title)
+                Spacer()
+                Image(systemName: "chevron.right")
+                    .foregroundColor(Color(uiColor: .tertiaryLabel))
+            }
+            .contentShape(Rectangle())
+        }
+        .buttonStyle(.plain)
+    }
+}

+ 94 - 147
LoopFollow/Settings/SettingsMenuView.swift

@@ -6,91 +6,114 @@ import SwiftUI
 import UIKit
 
 struct SettingsMenuView: View {
-    // MARK: – Call-backs -----------------------------------------------------
+    // MARK: – Call-backs
 
     let onNightscoutVisibilityChange: (_ enabled: Bool) -> Void
 
-    // MARK: – Local state ----------------------------------------------------
+    // MARK: – Local state
 
     @State private var sheet: Sheet?
     @State private var latestVersion: String?
     @State private var versionTint: Color = .secondary
 
-    // MARK: – Body -----------------------------------------------------------
+    // MARK: – Body
 
     var body: some View {
         NavigationStack {
             List {
-                // ────────────── Data settings ──────────────
+                // ───────── Data settings ─────────
                 dataSection
 
-                // ────────────── App settings ──────────────
+                // ───────── App settings ─────────
                 Section("App Settings") {
-                    navRow(title: "Background Refresh Settings",
-                           icon: "arrow.clockwise",
-                           destination: .backgroundRefresh)
+                    NavigationRow(title: "Background Refresh Settings",
+                                  icon: "arrow.clockwise")
+                    {
+                        sheet = .backgroundRefresh
+                    }
 
-                    navRow(title: "General Settings",
-                           icon: "gearshape",
-                           destination: .general)
+                    NavigationRow(title: "General Settings",
+                                  icon: "gearshape")
+                    {
+                        sheet = .general
+                    }
 
-                    navRow(title: "Graph Settings",
-                           icon: "chart.xyaxis.line",
-                           destination: .graph)
+                    NavigationRow(title: "Graph Settings",
+                                  icon: "chart.xyaxis.line")
+                    {
+                        sheet = .graph
+                    }
 
                     if IsNightscoutEnabled() {
-                        navRow(title: "Information Display Settings",
-                               icon: "info.circle",
-                               destination: .infoDisplay)
+                        NavigationRow(title: "Information Display Settings",
+                                      icon: "info.circle")
+                        {
+                            sheet = .infoDisplay
+                        }
                     }
                 }
 
-                // ────────────── Alarms ──────────────
+                // ───────── Alarms ─────────
                 Section {
-                    navRow(title: "Alarms",
-                           icon: "bell",
-                           destination: .alarmsList)
+                    NavigationRow(title: "Alarms",
+                                  icon: "bell")
+                    {
+                        sheet = .alarmsList
+                    }
 
-                    navRow(title: "Alarm Settings",
-                           icon: "bell.badge",
-                           destination: .alarmSettings)
+                    NavigationRow(title: "Alarm Settings",
+                                  icon: "bell.badge")
+                    {
+                        sheet = .alarmSettings
+                    }
                 }
 
-                // ────────────── Integrations ──────────────
+                // ───────── Integrations ─────────
                 Section("Integrations") {
-                    navRow(title: "Calendar",
-                           icon: "calendar",
-                           destination: .calendar)
+                    NavigationRow(title: "Calendar",
+                                  icon: "calendar")
+                    {
+                        sheet = .calendar
+                    }
 
-                    navRow(title: "Contact",
-                           icon: "person.circle",
-                           destination: .contact)
+                    NavigationRow(title: "Contact",
+                                  icon: "person.circle")
+                    {
+                        sheet = .contact
+                    }
                 }
 
-                // ────────────── Advanced / Logs ──────────────
+                // ───────── Advanced / Logs ─────────
                 Section("Advanced Settings") {
-                    navRow(title: "Advanced Settings",
-                           icon: "exclamationmark.shield",
-                           destination: .advanced)
+                    NavigationRow(title: "Advanced Settings",
+                                  icon: "exclamationmark.shield")
+                    {
+                        sheet = .advanced
+                    }
                 }
 
                 Section("Logging") {
-                    navRow(title: "View Log",
-                           icon: "doc.text.magnifyingglass",
-                           destination: .viewLog)
+                    NavigationRow(title: "View Log",
+                                  icon: "doc.text.magnifyingglass")
+                    {
+                        sheet = .viewLog
+                    }
 
-                    actionRow(title: "Share Logs",
-                              icon: "square.and.arrow.up") { shareLogs() }
+                    ActionRow(title: "Share Logs",
+                              icon: "square.and.arrow.up")
+                    {
+                        shareLogs()
+                    }
                 }
 
-                // ────────────── Community ──────────────
+                // ───────── Community ─────────
                 Section("Community") {
-                    linkRow(title: "LoopFollow Facebook Group",
+                    LinkRow(title: "LoopFollow Facebook Group",
                             icon: "person.2.fill",
                             url: URL(string: "https://www.facebook.com/groups/loopfollowlnl")!)
                 }
 
-                // ────────────── Build info ──────────────
+                // ───────── Build info ─────────
                 buildInfoSection
             }
             .navigationTitle("Settings")
@@ -99,9 +122,8 @@ struct SettingsMenuView: View {
         .sheet(item: $sheet) { $0.destination }
     }
 
-    // MARK: – Section builders ----------------------------------------------
+    // MARK: – Section builders
 
-    /// “Data Settings”
     @ViewBuilder
     private var dataSection: some View {
         Section("Data Settings") {
@@ -115,20 +137,23 @@ struct SettingsMenuView: View {
             }
             .pickerStyle(.segmented)
 
-            navRow(title: "Nightscout Settings",
-                   icon: "network",
-                   destination: .nightscout)
+            NavigationRow(title: "Nightscout Settings",
+                          icon: "network")
+            {
+                sheet = .nightscout
+            }
 
-            navRow(title: "Dexcom Settings",
-                   icon: "sensor.tag.radiowaves.forward",
-                   destination: .dexcom)
+            NavigationRow(title: "Dexcom Settings",
+                          icon: "sensor.tag.radiowaves.forward")
+            {
+                sheet = .dexcom
+            }
         }
         .onAppear {
             onNightscoutVisibilityChange(IsNightscoutEnabled())
         }
     }
 
-    /// version / build info
     @ViewBuilder
     private var buildInfoSection: some View {
         let build = BuildDetails.default
@@ -137,45 +162,22 @@ struct SettingsMenuView: View {
         Section("Build Information") {
             keyValue("Version", ver, tint: versionTint)
             keyValue("Latest version", latestVersion ?? "Fetching…")
-
             if !(build.isMacApp() || build.isSimulatorBuild()) {
                 keyValue(build.expirationHeaderString,
                          dateTimeUtils.formattedDate(from: build.calculateExpirationDate()))
             }
-            keyValue("Built", dateTimeUtils.formattedDate(from: build.buildDate()))
+            keyValue("Built",
+                     dateTimeUtils.formattedDate(from: build.buildDate()))
             keyValue("Branch", build.branchAndSha)
         }
     }
 
-    // MARK: – Row helpers ----------------------------------------------------
+    // MARK: – Helpers
 
-    /// Standard row with icon, chevron and sheet presentation
-    /// One tappable row, styled like the iOS Settings app
-    @ViewBuilder
-    private func navRow(
-        title: String,
-        icon: String,
-        tint: Color = .white,
-        destination: Sheet
-    ) -> some View {
-        Button {
-            sheet = destination
-        } label: {
-            HStack {
-                Glyph(symbol: icon, tint: tint)
-                Text(title)
-                Spacer()
-                Image(systemName: "chevron.right")
-                    .foregroundColor(Color(uiColor: .tertiaryLabel))
-            }
-            .contentShape(Rectangle())
-        }
-        .buttonStyle(.plain)
-    }
-
-    /// Simple key-value row
-    @ViewBuilder
-    private func keyValue(_ key: String, _ value: String, tint: Color = .secondary) -> some View {
+    private func keyValue(_ key: String,
+                          _ value: String,
+                          tint: Color = .secondary) -> some View
+    {
         HStack {
             Text(key)
             Spacer()
@@ -183,22 +185,18 @@ struct SettingsMenuView: View {
         }
     }
 
-    // MARK: – Version check --------------------------------------------------
-
     private func refreshVersionInfo() async {
-        let manager = AppVersionManager()
-        let (latest, newer, blacklisted) = await manager.checkForNewVersionAsync()
+        let mgr = AppVersionManager()
+        let (latest, newer, blacklisted) = await mgr.checkForNewVersionAsync()
         latestVersion = latest ?? "Unknown"
 
-        // match old colour logic
-        let current = manager.version()
-        versionTint = blacklisted ? .red :
-            newer ? .orange :
-            latest == current ? .green : .secondary
+        let current = mgr.version()
+        versionTint = blacklisted ? .red
+            : newer ? .orange
+            : latest == current ? .green
+            : .secondary
     }
 
-    // MARK: – Share logs -----------------------------------------------------
-
     private func shareLogs() {
         let files = LogManager.shared.logFilesForTodayAndYesterday()
         guard !files.isEmpty else {
@@ -208,12 +206,13 @@ struct SettingsMenuView: View {
             )
             return
         }
-        let avc = UIActivityViewController(activityItems: files, applicationActivities: nil)
+        let avc = UIActivityViewController(activityItems: files,
+                                           applicationActivities: nil)
         UIApplication.shared.topMost?.present(avc, animated: true)
     }
 }
 
-// MARK: – Sheet routing identical to earlier -------------------------------
+// MARK: – Sheet routing (unchanged)
 
 private enum Sheet: Identifiable {
     case nightscout, dexcom
@@ -267,55 +266,3 @@ extension UIViewController {
         present(a, animated: true)
     }
 }
-
-struct Glyph: View {
-    let symbol: String
-    let tint: Color
-
-    @Environment(\.colorScheme) private var scheme
-
-    var body: some View {
-        ZStack {
-            RoundedRectangle(cornerRadius: 8, style: .continuous)
-                .fill(Color(uiColor: .systemGray))
-                .frame(width: 28, height: 28)
-
-            Image(systemName: symbol)
-                .font(.system(size: 16, weight: .regular))
-                .foregroundStyle(tint)
-        }
-        .frame(width: 36, height: 36)
-    }
-}
-
-@ViewBuilder
-private func actionRow(
-    title: String,
-    icon: String,
-    tint: Color = .primary,
-    action: @escaping () -> Void
-) -> some View {
-    Button { action() } label: {
-        HStack {
-            Glyph(symbol: icon, tint: tint)
-            Text(title)
-            Spacer()
-            Image(systemName: "chevron.right")
-                .foregroundColor(Color(uiColor: .tertiaryLabel))
-        }
-        .contentShape(Rectangle())
-    }
-    .buttonStyle(.plain)
-}
-
-@ViewBuilder
-private func linkRow(
-    title: String,
-    icon: String,
-    tint: Color = .primary,
-    url: URL
-) -> some View {
-    actionRow(title: title, icon: icon, tint: tint) {
-        UIApplication.shared.open(url)
-    }
-}