Skip to content
Closed
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
8 changes: 8 additions & 0 deletions LoopFollow.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
DD16AF112C997B4600FB655A /* LoadingButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD16AF102C997B4600FB655A /* LoadingButtonView.swift */; };
DD1A97142D4294A5000DDC11 /* AdvancedSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1A97132D4294A4000DDC11 /* AdvancedSettingsView.swift */; };
DD1A97162D4294B3000DDC11 /* AdvancedSettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1A97152D4294B2000DDC11 /* AdvancedSettingsViewModel.swift */; };
DD1D52922E1D9E8200432050 /* TabSelection.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1D52912E1D9E8200432050 /* TabSelection.swift */; };
DD1D52942E1DA31000432050 /* TabCustomizationSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1D52932E1DA31000432050 /* TabCustomizationSettingsView.swift */; };
DD2C2E4F2D3B8AF1006413A5 /* NightscoutSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2C2E4E2D3B8AEC006413A5 /* NightscoutSettingsView.swift */; };
DD2C2E512D3B8B0C006413A5 /* NightscoutSettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2C2E502D3B8B0B006413A5 /* NightscoutSettingsViewModel.swift */; };
DD2C2E542D3C37DC006413A5 /* DexcomSettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2C2E532D3C37D7006413A5 /* DexcomSettingsViewModel.swift */; };
Expand Down Expand Up @@ -415,6 +417,8 @@
DD16AF102C997B4600FB655A /* LoadingButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingButtonView.swift; sourceTree = "<group>"; };
DD1A97132D4294A4000DDC11 /* AdvancedSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedSettingsView.swift; sourceTree = "<group>"; };
DD1A97152D4294B2000DDC11 /* AdvancedSettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedSettingsViewModel.swift; sourceTree = "<group>"; };
DD1D52912E1D9E8200432050 /* TabSelection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabSelection.swift; sourceTree = "<group>"; };
DD1D52932E1DA31000432050 /* TabCustomizationSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabCustomizationSettingsView.swift; sourceTree = "<group>"; };
DD2C2E4E2D3B8AEC006413A5 /* NightscoutSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutSettingsView.swift; sourceTree = "<group>"; };
DD2C2E502D3B8B0B006413A5 /* NightscoutSettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutSettingsViewModel.swift; sourceTree = "<group>"; };
DD2C2E532D3C37D7006413A5 /* DexcomSettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DexcomSettingsViewModel.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -863,6 +867,7 @@
DD1A97122D429495000DDC11 /* Settings */ = {
isa = PBXGroup;
children = (
DD1D52932E1DA31000432050 /* TabCustomizationSettingsView.swift */,
DD83164F2DE4E635004467AA /* SettingsMenuView.swift */,
DD2C2E552D3C3913006413A5 /* DexcomSettingsView.swift */,
DD2C2E532D3C37D7006413A5 /* DexcomSettingsViewModel.swift */,
Expand Down Expand Up @@ -1456,6 +1461,7 @@
FCC688542489367300A0279D /* Helpers */ = {
isa = PBXGroup;
children = (
DD1D52912E1D9E8200432050 /* TabSelection.swift */,
DD83164B2DE4DB3A004467AA /* BinaryFloatingPoint+localized.swift */,
DD4AFB3A2DB55CB600BB593F /* TimeOfDay.swift */,
DD7B0D432D730A320063DCB6 /* CycleHelper.swift */,
Expand Down Expand Up @@ -1825,6 +1831,7 @@
DD0B9D582DE1F3B20090C337 /* AlarmType+canAcknowledge.swift in Sources */,
DD2C2E562D3C3917006413A5 /* DexcomSettingsView.swift in Sources */,
DD7F4BC52DD3CE0700D449E9 /* AlarmBGLimitSection.swift in Sources */,
DD1D52942E1DA31000432050 /* TabCustomizationSettingsView.swift in Sources */,
DD7F4B9F2DD1F92700D449E9 /* AlarmActiveSection.swift in Sources */,
DD4AFB672DB68C5500BB593F /* UUID+Identifiable.swift in Sources */,
DD9ED0CA2D355257000D2A63 /* LogView.swift in Sources */,
Expand Down Expand Up @@ -2019,6 +2026,7 @@
DD4878032C7B297E0048F05C /* StorageValue.swift in Sources */,
DD4878192C7C56D60048F05C /* TrioNightscoutRemoteController.swift in Sources */,
FC1BDD2B24A22650001B652C /* Stats.swift in Sources */,
DD1D52922E1D9E8200432050 /* TabSelection.swift in Sources */,
DDA9ACAC2D6B317100E6F1A9 /* ContactType.swift in Sources */,
DDDF6F432D479A9900884336 /* LoopNightscoutRemoteView.swift in Sources */,
DDD10F052C529DA200D76A8E /* ObservableValue.swift in Sources */,
Expand Down
2 changes: 1 addition & 1 deletion LoopFollow/Application/Base.lproj/Main.storyboard
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@
<!--Nightscout-->
<scene sceneID="wg7-f3-ORb">
<objects>
<viewController id="8rJ-Kc-sve" customClass="NightscoutViewController" customModule="LoopFollow" customModuleProvider="target" sceneMemberID="viewController">
<viewController storyboardIdentifier="NightscoutViewController" id="8rJ-Kc-sve" customClass="NightscoutViewController" customModule="LoopFollow" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="QS5-Rx-YEW">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
Expand Down
33 changes: 33 additions & 0 deletions LoopFollow/Helpers/TabSelection.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// LoopFollow
// TabSelection.swift
// Created by Jonas Björkert.

enum TabSelection: String, CaseIterable, Codable {
case alarms
case remote
case nightscout

var displayName: String {
switch self {
case .alarms: return "Alarms"
case .remote: return "Remote"
case .nightscout: return "Nightscout"
}
}

var systemImage: String {
switch self {
case .alarms: return "alarm"
case .remote: return "antenna.radiowaves.left.and.right"
case .nightscout: return "safari"
}
}

var storyboardIdentifier: String {
switch self {
case .alarms: return "AlarmViewController"
case .remote: return "RemoteViewController"
case .nightscout: return "NightscoutViewController"
}
}
}
8 changes: 8 additions & 0 deletions LoopFollow/Settings/SettingsMenuView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ struct SettingsMenuView: View {
settingsPath.value.append(Sheet.graph)
}

NavigationRow(title: "Tab Customization",
icon: "rectangle.3.group")
{
settingsPath.value.append(Sheet.tabCustomization)
}

if !nightscoutURL.value.isEmpty {
NavigationRow(title: "Information Display Settings",
icon: "info.circle")
Expand Down Expand Up @@ -226,6 +232,7 @@ private enum Sheet: Hashable, Identifiable {
case calendar, contact
case advanced
case viewLog
case tabCustomization

var id: Self { self }

Expand All @@ -245,6 +252,7 @@ private enum Sheet: Hashable, Identifiable {
case .contact: ContactSettingsView(viewModel: .init())
case .advanced: AdvancedSettingsView(viewModel: .init())
case .viewLog: LogView(viewModel: .init())
case .tabCustomization: TabCustomizationSettingsView()
}
}
}
Expand Down
35 changes: 35 additions & 0 deletions LoopFollow/Settings/TabCustomizationSettingsView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// LoopFollow
// TabCustomizationSettingsView.swift
// Created by Jonas Björkert.

import SwiftUI

struct TabCustomizationSettingsView: View {
@ObservedObject var tab2Selection = Storage.shared.tab2Selection
@ObservedObject var tab4Selection = Storage.shared.tab4Selection

var body: some View {
Form {
Section("Tab Customization") {
Picker("Tab 2", selection: $tab2Selection.value) {
ForEach(TabSelection.allCases, id: \.self) { selection in
Text(selection.displayName).tag(selection)
}
}

Picker("Tab 4", selection: $tab4Selection.value) {
ForEach(TabSelection.allCases, id: \.self) { selection in
Text(selection.displayName).tag(selection)
}
}
}

Section {
Text("Note: Home (Tab 1), Snoozer (Tab 3), and Settings (Tab 5) cannot be changed")
.font(.caption)
.foregroundColor(.secondary)
}
}
.navigationBarTitle("Tab Settings", displayMode: .inline)
}
}
13 changes: 13 additions & 0 deletions LoopFollow/Storage/Storage+Migrate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,19 @@ extension Storage {
migrateRecBolusAlarm()
}

func migrateStep2() {
// Migrate from remoteType-based tab selection to user-configurable tabs
if remoteType.value == .none {
// If remote is disabled, set tab 2 to Alarms
tab2Selection.value = .alarms
} else {
// If remote is enabled (any type), set tab 2 to Remote
tab2Selection.value = .remote
}

// Tab 4 defaults to nightscout (already set by default value)
}

// MARK: - One-off alarm migrations

/// Reads *all* `alertUrgentLow*` keys, converts them into a single `Alarm`,
Expand Down
3 changes: 3 additions & 0 deletions LoopFollow/Storage/Storage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,9 @@ class Storage {

var lastLoopingChecked = StorageValue<Date?>(key: "lastLoopingChecked", defaultValue: nil)

var tab2Selection = StorageValue<TabSelection>(key: "tab2Selection", defaultValue: .alarms)
var tab4Selection = StorageValue<TabSelection>(key: "tab4Selection", defaultValue: .nightscout)

static let shared = Storage()
private init() {}
}
93 changes: 47 additions & 46 deletions LoopFollow/ViewControllers/MainViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,6 @@ func IsNightscoutEnabled() -> Bool {
return !Storage.shared.url.value.isEmpty
}

private enum SecondTab {
case remote
case alarms
}

class MainViewController: UIViewController, UITableViewDataSource, ChartViewDelegate, UNUserNotificationCenterDelegate, UIScrollViewDelegate {
@IBOutlet var BGText: UILabel!
@IBOutlet var DeltaText: UILabel!
Expand Down Expand Up @@ -124,11 +119,18 @@ class MainViewController: UIViewController, UITableViewDataSource, ChartViewDele

loadDebugData()

// This step1 was released with 3.0
if Storage.shared.migrationStep.value < 1 {
Storage.shared.migrateStep1()
Storage.shared.migrationStep.value = 1
}

// This step2 was released with 3.1
if Storage.shared.migrationStep.value < 2 {
Storage.shared.migrateStep2()
Storage.shared.migrationStep.value = 2
}

// Synchronize info types to ensure arrays are the correct size
synchronizeInfoTypes()

Expand Down Expand Up @@ -278,66 +280,72 @@ class MainViewController: UIViewController, UITableViewDataSource, ChartViewDele
}
.store(in: &cancellables)

Storage.shared.remoteType.$value
Storage.shared.tab2Selection.$value
.receive(on: DispatchQueue.main)
.sink { [weak self] remoteType in
if remoteType == .none {
// If remote is disabled, show the Alarms tab.
self?.updateSecondTab(to: .alarms)
} else {
// Otherwise, show the Remote tab.
self?.updateSecondTab(to: .remote)
}
.sink { [weak self] selection in
self?.updateTab(at: 1, to: selection)
self?.updateNightscoutTabState()
}
.store(in: &cancellables)

Storage.shared.tab4Selection.$value
.receive(on: DispatchQueue.main)
.sink { [weak self] selection in
self?.updateTab(at: 3, to: selection)
self?.updateNightscoutTabState()
}
.store(in: &cancellables)

Storage.shared.url.$value
.receive(on: DispatchQueue.main)
.sink { [weak self] value in
self?.tabBarController?.tabBar.items?[3].isEnabled = !value.isEmpty
.sink { [weak self] _ in
self?.updateNightscoutTabState()
}
.store(in: &cancellables)

updateQuickActions()
updateTab(at: 1, to: Storage.shared.tab2Selection.value)
updateTab(at: 3, to: Storage.shared.tab4Selection.value)
updateNightscoutTabState()

speechSynthesizer.delegate = self
}

private func updateSecondTab(to tab: SecondTab) {
private func updateTab(at index: Int, to selection: TabSelection) {
guard let tabBarController = tabBarController,
var viewControllers = tabBarController.viewControllers,
viewControllers.count > 1
viewControllers.count > index
else {
return
}

let storyboard = UIStoryboard(name: "Main", bundle: nil)
let newViewController: UIViewController
let newTabBarItem: UITabBarItem

switch tab {
case .remote:
newViewController = storyboard.instantiateViewController(withIdentifier: "RemoteViewController")
newTabBarItem = UITabBarItem(
title: "Remote",
image: UIImage(systemName: "antenna.radiowaves.left.and.right"),
tag: 1
)
case .alarms:
newViewController = storyboard.instantiateViewController(withIdentifier: "AlarmViewController")
newTabBarItem = UITabBarItem(
title: "Alarms",
image: UIImage(systemName: "alarm"),
tag: 1
)
}
let newViewController = storyboard.instantiateViewController(withIdentifier: selection.storyboardIdentifier)
let newTabBarItem = UITabBarItem(
title: selection.displayName,
image: UIImage(systemName: selection.systemImage),
tag: index
)

newViewController.tabBarItem = newTabBarItem
viewControllers[1] = newViewController
viewControllers[index] = newViewController

tabBarController.setViewControllers(viewControllers, animated: false)
}

private func updateNightscoutTabState() {
guard let tabBarController = tabBarController,
let viewControllers = tabBarController.viewControllers else { return }

let isNightscoutEnabled = !Storage.shared.url.value.isEmpty

for (index, vc) in viewControllers.enumerated() {
if vc is NightscoutViewController {
tabBarController.tabBar.items?[index].isEnabled = isNightscoutEnabled
}
}
}

// Update the Home Screen Quick Action for toggling the "Speak BG" feature based on the current speakBG setting.
func updateQuickActions() {
let iconName = Storage.shared.speakBG.value ? "pause.circle.fill" : "play.circle.fill"
Expand Down Expand Up @@ -539,10 +547,8 @@ class MainViewController: UIViewController, UITableViewDataSource, ChartViewDele

func showHideNSDetails() {
var isHidden = false
var isEnabled = true
if !IsNightscoutEnabled() {
isHidden = true
isEnabled = false
}

LoopStatusLabel.isHidden = isHidden
Expand All @@ -557,12 +563,7 @@ class MainViewController: UIViewController, UITableViewDataSource, ChartViewDele
infoTable.isHidden = true
}

if IsNightscoutEnabled() {
isEnabled = true
}

guard let nightscoutTab = tabBarController?.tabBar.items![3] else { return }
nightscoutTab.isEnabled = isEnabled
updateNightscoutTabState()
}

func updateBadge(val: Int) {
Expand Down