Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
302 changes: 302 additions & 0 deletions LoopFollow.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "BB01000000000003000000AA"
BuildableName = "LoopFollowWatch.app"
BlueprintName = "LoopFollowWatch"
ReferencedContainer = "container:LoopFollow.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "BB01000000000003000000AA"
BuildableName = "LoopFollowWatch.app"
BlueprintName = "LoopFollowWatch"
ReferencedContainer = "container:LoopFollow.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "BB01000000000003000000AA"
BuildableName = "LoopFollowWatch.app"
BlueprintName = "LoopFollowWatch"
ReferencedContainer = "container:LoopFollow.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
2 changes: 2 additions & 0 deletions LoopFollow/Application/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
}
}

PhoneSessionManager.shared.startSession()

return true
}

Expand Down
3 changes: 3 additions & 0 deletions LoopFollow/Charts/BGChartModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,9 @@ final class BGChartModel: ObservableObject {
}

private func colorFor(_ sgv: Int, thresholds: (low: Double, high: Double)) -> Color {
if Storage.shared.dynamicBGColor.value {
return bgDynamicColor(Double(sgv))
}
if Double(sgv) >= thresholds.high {
return .yellow
} else if Double(sgv) <= thresholds.low {
Expand Down
1 change: 1 addition & 0 deletions LoopFollow/Controllers/Nightscout/BGData.swift
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@ extension MainViewController {
)
}
Storage.shared.lastBGChecked.value = Date()
PhoneSessionManager.shared.sendConfig()
}
}
}
31 changes: 31 additions & 0 deletions LoopFollow/Helpers/BGDynamicColor.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// LoopFollow
// BGDynamicColor.swift

import SwiftUI

/// Returns a hue-based color for a given BG value in mg/dL.
/// Low (≤55) = red, target (100) = green, high (≥220) = purple.
func bgDynamicColor(_ bg: Double) -> Color {
let low = 55.0
let target = 100.0
let high = 220.0

let redHue: CGFloat = 0.0 / 360.0
let greenHue: CGFloat = 120.0 / 360.0
let purpleHue: CGFloat = 270.0 / 360.0

let hue: CGFloat
if bg <= low {
hue = redHue
} else if bg >= high {
hue = purpleHue
} else if bg <= target {
let ratio = CGFloat((bg - low) / (target - low))
hue = redHue + ratio * (greenHue - redHue)
} else {
let ratio = CGFloat((bg - target) / (high - target))
hue = greenHue + ratio * (purpleHue - greenHue)
}

return Color(hue: Double(hue), saturation: 0.6, brightness: 0.9)
}
2 changes: 2 additions & 0 deletions LoopFollow/Nightscout/NightscoutSettingsViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class NightscoutSettingsViewModel: ObservableObject {
if newValue != nightscoutURL {
Storage.shared.url.value = newValue
triggerCheckStatus()
PhoneSessionManager.shared.sendConfig()
}
}
}
Expand All @@ -29,6 +30,7 @@ class NightscoutSettingsViewModel: ObservableObject {
if newValue != nightscoutToken {
Storage.shared.token.value = NightscoutUtils.sanitizeConnectionInput(newValue)
triggerCheckStatus()
PhoneSessionManager.shared.sendConfig()
}
}
}
Expand Down
5 changes: 4 additions & 1 deletion LoopFollow/Remote/Settings/RemoteSettingsViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,10 @@ class RemoteSettingsViewModel: ObservableObject {

$mealWithFatProtein
.dropFirst()
.sink { [weak self] in self?.storage.mealWithFatProtein.value = $0 }
.sink { [weak self] in
self?.storage.mealWithFatProtein.value = $0
PhoneSessionManager.shared.sendConfig()
}
.store(in: &cancellables)

// Device type monitoring
Expand Down
3 changes: 3 additions & 0 deletions LoopFollow/Settings/DexcomSettingsViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class DexcomSettingsViewModel: ObservableObject {
if newValue != userName {
Storage.shared.shareUserName.value = newValue
scheduleVerification()
PhoneSessionManager.shared.sendConfig()
}
}
}
Expand All @@ -30,6 +31,7 @@ class DexcomSettingsViewModel: ObservableObject {
if newValue != password {
Storage.shared.sharePassword.value = newValue
scheduleVerification()
PhoneSessionManager.shared.sendConfig()
}
}
}
Expand All @@ -39,6 +41,7 @@ class DexcomSettingsViewModel: ObservableObject {
if newValue != server {
Storage.shared.shareServer.value = newValue
scheduleVerification()
PhoneSessionManager.shared.sendConfig()
}
}
}
Expand Down
15 changes: 14 additions & 1 deletion LoopFollow/Settings/GeneralSettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import SwiftUI

struct GeneralSettingsView: View {
@ObservedObject var colorBGText = Storage.shared.colorBGText
@ObservedObject var dynamicBGColor = Storage.shared.dynamicBGColor
@ObservedObject var appBadge = Storage.shared.appBadge
@ObservedObject var appearanceMode = Storage.shared.appearanceMode
@ObservedObject var showStats = Storage.shared.showStats
Expand Down Expand Up @@ -46,7 +47,19 @@ struct GeneralSettingsView: View {
}
Toggle("Display Stats", isOn: $showStats.value)
Toggle("Display Small Graph", isOn: $showSmallGraph.value)
Toggle("Color BG Text", isOn: $colorBGText.value)
Toggle("Color BG Text", isOn: $colorBGText.value.animation())
.onChange(of: colorBGText.value) { _ in
PhoneSessionManager.shared.sendConfig()
}

if colorBGText.value {
Toggle("Dynamic BG Color", isOn: $dynamicBGColor.value)
.onChange(of: dynamicBGColor.value) { _ in
PhoneSessionManager.shared.sendConfig()
markChartSettingsDirty()
}
}

Toggle("Keep Screen Active", isOn: $screenlockSwitchState.value)
Toggle("Show Display Name", isOn: $showDisplayName.value)
Toggle("Snoozer emoji", isOn: $snoozerEmoji.value)
Expand Down
3 changes: 3 additions & 0 deletions LoopFollow/Settings/UnitsConfigurationView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ struct UnitsConfigurationView: View {
.pickerStyle(.segmented)
.onChange(of: glucoseUnit) { newValue in
UnitSettingsStore.shared.glucoseUnit = newValue
PhoneSessionManager.shared.sendConfig()
}
}
}
Expand Down Expand Up @@ -89,6 +90,7 @@ struct UnitsConfigurationView: View {
.onChange(of: lowValue) { newValue in
Storage.shared.lowLine.value = newValue
Observable.shared.chartSettingsChanged.value = true
PhoneSessionManager.shared.sendConfig()
}
BGPicker(
title: "High",
Expand All @@ -99,6 +101,7 @@ struct UnitsConfigurationView: View {
.onChange(of: highValue) { newValue in
Storage.shared.highLine.value = newValue
Observable.shared.chartSettingsChanged.value = true
PhoneSessionManager.shared.sendConfig()
}
}
} header: {
Expand Down
1 change: 1 addition & 0 deletions LoopFollow/Storage/Storage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ class Storage {
// General Settings [BEGIN]
var appBadge = StorageValue<Bool>(key: "appBadge", defaultValue: true)
var colorBGText = StorageValue<Bool>(key: "colorBGText", defaultValue: true)
var dynamicBGColor = StorageValue<Bool>(key: "dynamicBGColor", defaultValue: false)
var appearanceMode = StorageValue<AppearanceMode>(key: "appearanceMode", defaultValue: .dark)
var showStats = StorageValue<Bool>(key: "showStats", defaultValue: true)
var useIFCC = StorageValue<Bool>(key: "useIFCC", defaultValue: false)
Expand Down
18 changes: 11 additions & 7 deletions LoopFollow/ViewControllers/MainViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ class MainViewController: UIViewController, UNUserNotificationCenterDelegate {
}
.store(in: &cancellables)

Storage.shared.colorBGText.$value
Publishers.Merge(Storage.shared.colorBGText.$value, Storage.shared.dynamicBGColor.$value)
.receive(on: DispatchQueue.main)
.sink { [weak self] _ in
self?.updateBGTextAppearance()
Expand Down Expand Up @@ -812,13 +812,17 @@ class MainViewController: UIViewController, UNUserNotificationCenterDelegate {
if bgData.count > 0 {
let latestBG = bgData[bgData.count - 1].sgv
if Storage.shared.colorBGText.value {
let thresholds = UnitSettingsStore.shared.effectiveThresholds()
if Double(latestBG) >= thresholds.high {
Observable.shared.bgTextColor.value = .yellow
} else if Double(latestBG) <= thresholds.low {
Observable.shared.bgTextColor.value = .red
if Storage.shared.dynamicBGColor.value {
Observable.shared.bgTextColor.value = bgDynamicColor(Double(latestBG))
} else {
Observable.shared.bgTextColor.value = .green
let thresholds = UnitSettingsStore.shared.effectiveThresholds()
if Double(latestBG) >= thresholds.high {
Observable.shared.bgTextColor.value = .yellow
} else if Double(latestBG) <= thresholds.low {
Observable.shared.bgTextColor.value = .red
} else {
Observable.shared.bgTextColor.value = .green
}
}
} else {
Observable.shared.bgTextColor.value = .primary
Expand Down
Loading
Loading