Skip to content

Commit 86404f7

Browse files
committed
[LOOP-4699] StoredFavoriteFood, main screen design updates
1 parent fd8f1f9 commit 86404f7

File tree

6 files changed

+148
-183
lines changed

6 files changed

+148
-183
lines changed

Loop.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
14B1737F28AEDC6C006CCD7C /* PluginManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C16DA84122E8E112008624C2 /* PluginManager.swift */; };
3434
14B1738028AEDC6C006CCD7C /* Debug.swift in Sources */ = {isa = PBXBuildFile; fileRef = 892A5D58222F0A27008961AB /* Debug.swift */; };
3535
14B1738128AEDC70006CCD7C /* StatusExtensionContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F70C2111DE900EA006380B7 /* StatusExtensionContext.swift */; };
36+
14D906F42A846510006EB79A /* FavoriteFoodsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14D906F32A846510006EB79A /* FavoriteFoodsViewModel.swift */; };
3637
1D05219B2469E9DF000EBBDE /* StoredAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D05219A2469E9DF000EBBDE /* StoredAlert.swift */; };
3738
1D05219D2469F1F5000EBBDE /* AlertStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D05219C2469F1F5000EBBDE /* AlertStore.swift */; };
3839
1D080CBD2473214A00356610 /* AlertStore.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 1D080CBB2473214A00356610 /* AlertStore.xcdatamodeld */; };
@@ -742,6 +743,7 @@
742743
14B1736F28AEDBF6006CCD7C /* SystemStatusWidget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SystemStatusWidget.swift; sourceTree = "<group>"; };
743744
14B1737028AEDBF6006CCD7C /* GlucoseView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GlucoseView.swift; sourceTree = "<group>"; };
744745
14B1737128AEDBF6006CCD7C /* LoopCircleView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoopCircleView.swift; sourceTree = "<group>"; };
746+
14D906F32A846510006EB79A /* FavoriteFoodsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoriteFoodsViewModel.swift; sourceTree = "<group>"; };
745747
1D05219A2469E9DF000EBBDE /* StoredAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoredAlert.swift; sourceTree = "<group>"; };
746748
1D05219C2469F1F5000EBBDE /* AlertStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertStore.swift; sourceTree = "<group>"; };
747749
1D080CBC2473214A00356610 /* AlertStore.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = AlertStore.xcdatamodel; sourceTree = "<group>"; };
@@ -2583,6 +2585,7 @@
25832585
children = (
25842586
897A5A9824C22DE800C4E71D /* BolusEntryViewModel.swift */,
25852587
A9A056B424B94123007CF06D /* CriticalEventLogExportViewModel.swift */,
2588+
14D906F32A846510006EB79A /* FavoriteFoodsViewModel.swift */,
25862589
C11BD0542523CFED00236B08 /* SimpleBolusViewModel.swift */,
25872590
1DB1CA4E24A56D7600B3B94C /* SettingsViewModel.swift */,
25882591
1D49795724E7289700948F05 /* ServicesViewModel.swift */,
@@ -3769,6 +3772,7 @@
37693772
431A8C401EC6E8AB00823B9C /* CircleMaskView.swift in Sources */,
37703773
1D05219D2469F1F5000EBBDE /* AlertStore.swift in Sources */,
37713774
439897371CD2F80600223065 /* AnalyticsServicesManager.swift in Sources */,
3775+
14D906F42A846510006EB79A /* FavoriteFoodsViewModel.swift in Sources */,
37723776
DDC389FE2A2C4C830066E2E8 /* GlucoseBasedApplicationFactorSelectionView.swift in Sources */,
37733777
A9C62D842331700E00535612 /* DiagnosticLog+Subsystem.swift in Sources */,
37743778
895FE0952201234000FCF18A /* OverrideSelectionViewController.swift in Sources */,

Loop/Extensions/UserDefaults+Loop.swift

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ extension UserDefaults {
1616
case legacyServicesState = "com.loopkit.Loop.ServicesState"
1717
case loopNotRunningNotifications = "com.loopkit.Loop.loopNotRunningNotifications"
1818
case inFlightAutomaticDose = "com.loopkit.Loop.inFlightAutomaticDose"
19-
case favoriteFoodsEnabled = "com.loopkit.Loop.favoriteFoodsEnabled"
19+
case favoriteFoods = "com.loopkit.Loop.favoriteFoods"
2020
}
2121

2222
var legacyPumpManagerRawValue: PumpManager.RawValue? {
@@ -91,12 +91,22 @@ extension UserDefaults {
9191
}
9292
}
9393

94-
var favoriteFoodsEnabled: Bool {
94+
var favoriteFoods: [StoredFavoriteFood] {
9595
get {
96-
bool(forKey: Key.favoriteFoodsEnabled.rawValue)
96+
let decoder = JSONDecoder()
97+
guard let data = object(forKey: Key.favoriteFoods.rawValue) as? Data else {
98+
return []
99+
}
100+
return (try? decoder.decode([StoredFavoriteFood].self, from: data)) ?? []
97101
}
98102
set {
99-
set(newValue, forKey: Key.favoriteFoodsEnabled.rawValue)
103+
do {
104+
let encoder = JSONEncoder()
105+
let data = try encoder.encode(newValue)
106+
set(data, forKey: Key.favoriteFoods.rawValue)
107+
} catch {
108+
assertionFailure("Unable to encode stored favorite foods")
109+
}
100110
}
101111
}
102112
}

Loop/View Controllers/CarbEntryViewController.swift

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -173,8 +173,6 @@ final class CarbEntryViewController: LoopChartsTableViewController, Identifiable
173173
}()
174174

175175
private var lastContentHeight: CGFloat = 0
176-
177-
public weak var favoriteFoodsDelegate: FavoriteFoodsFeatureUnlockDelegate?
178176

179177
override func createChartsManager() -> ChartsManager {
180178
// Consider including a chart on this screen to demonstrate how absorption time affects prediction
@@ -554,10 +552,6 @@ final class CarbEntryViewController: LoopChartsTableViewController, Identifiable
554552
guard let updatedEntry = updatedCarbEntry else {
555553
return
556554
}
557-
558-
if foodType == "🍞🥜🍫🥛" && quantity?.doubleValue(for: .gram()) == 63 {
559-
favoriteFoodsDelegate?.featureAvailabilityChanged()
560-
}
561555

562556
let viewModel = BolusEntryViewModel(
563557
delegate: deviceManager,
@@ -790,7 +784,3 @@ extension CarbEntryViewController: EmojiInputControllerDelegate {
790784
extension DateAndDurationTableViewCell: NibLoadable {}
791785

792786
extension DateAndDurationSteppableTableViewCell: NibLoadable {}
793-
794-
protocol FavoriteFoodsFeatureUnlockDelegate: AnyObject {
795-
func featureAvailabilityChanged()
796-
}

Loop/View Controllers/StatusTableViewController.swift

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1370,7 +1370,6 @@ final class StatusTableViewController: LoopChartsTableViewController {
13701370
hostingController.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: navigationWrapper, action: #selector(dismissWithAnimation))
13711371
} else {
13721372
let carbEntryViewController = UIStoryboard(name: "Main", bundle: Bundle(for: AppDelegate.self)).instantiateViewController(withIdentifier: "CarbEntryViewController") as! CarbEntryViewController
1373-
carbEntryViewController.favoriteFoodsDelegate = self
13741373
carbEntryViewController.deviceManager = deviceManager
13751374
carbEntryViewController.defaultAbsorptionTimes = deviceManager.carbStore.defaultAbsorptionTimes
13761375
carbEntryViewController.preferredCarbUnit = deviceManager.carbStore.preferredUnit
@@ -2245,10 +2244,3 @@ extension StatusTableViewController: ServicesViewModelDelegate {
22452244
show(settingsViewController, sender: self)
22462245
}
22472246
}
2248-
2249-
extension StatusTableViewController: FavoriteFoodsFeatureUnlockDelegate {
2250-
func featureAvailabilityChanged() {
2251-
UserDefaults.appGroup?.favoriteFoodsEnabled.toggle()
2252-
self.updateToolbarItems()
2253-
}
2254-
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
//
2+
// FavoriteFoodsViewModel.swift
3+
// Loop
4+
//
5+
// Created by Noah Brauner on 7/27/23.
6+
// Copyright © 2023 LoopKit Authors. All rights reserved.
7+
//
8+
9+
import SwiftUI
10+
import LoopKit
11+
import Combine
12+
13+
final class FavoriteFoodsViewModel: ObservableObject {
14+
@Published var favoriteFoods = UserDefaults.standard.favoriteFoods
15+
@Published var selectedFood: StoredFavoriteFood?
16+
17+
@Published var isDetailViewActive = false
18+
@Published var isEditViewActive = false
19+
@Published var isAddViewActive = false
20+
21+
private lazy var cancellables = Set<AnyCancellable>()
22+
23+
init() {
24+
observeFavoriteFoodChange()
25+
}
26+
27+
func onFoodSave(_ newFood: NewFavoriteFood) {
28+
if isAddViewActive {
29+
let newStoredFood = StoredFavoriteFood(name: newFood.name, carbsQuantity: newFood.carbsQuantity, foodType: newFood.foodType, absorptionTime: newFood.absorptionTime)
30+
withAnimation {
31+
favoriteFoods.append(newStoredFood)
32+
}
33+
isAddViewActive = false
34+
}
35+
else if var selectedFood, let selectedFooxIndex = favoriteFoods.firstIndex(of: selectedFood) {
36+
selectedFood.name = newFood.name
37+
selectedFood.carbsQuantity = newFood.carbsQuantity
38+
selectedFood.foodType = newFood.foodType
39+
selectedFood.absorptionTime = newFood.absorptionTime
40+
favoriteFoods[selectedFooxIndex] = selectedFood
41+
isEditViewActive = false
42+
}
43+
}
44+
45+
func onFoodDelete(_ food: StoredFavoriteFood) {
46+
if isDetailViewActive {
47+
isDetailViewActive = false
48+
}
49+
withAnimation {
50+
_ = favoriteFoods.remove(food)
51+
}
52+
}
53+
54+
func onFoodReorder(from: IndexSet, to: Int) {
55+
withAnimation {
56+
favoriteFoods.move(fromOffsets: from, toOffset: to)
57+
}
58+
}
59+
60+
func addFoodTapped() {
61+
isAddViewActive = true
62+
}
63+
64+
private func observeFavoriteFoodChange() {
65+
$favoriteFoods
66+
.dropFirst()
67+
.removeDuplicates()
68+
.sink { newValue in
69+
UserDefaults.standard.favoriteFoods = newValue
70+
}
71+
.store(in: &cancellables)
72+
}
73+
}

0 commit comments

Comments
 (0)