Skip to content

Commit 65c063c

Browse files
committed
Migrate data store to group container, add low battery notification time limit, add empty chart view, filter exercises by device
1 parent aad52cc commit 65c063c

19 files changed

Lines changed: 421 additions & 167 deletions

.DS_Store

2 KB
Binary file not shown.

InfiniLink.xcodeproj/project.pbxproj

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@
4747
E0599A0D2CB4DF0800D64E0B /* FileSystemDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0599A0C2CB4DF0800D64E0B /* FileSystemDetailView.swift */; };
4848
E0599A0F2CB4E62800D64E0B /* SetUpDetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0599A0E2CB4E62800D64E0B /* SetUpDetailsView.swift */; };
4949
E0599A112CB5553B00D64E0B /* FSFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0599A102CB5553B00D64E0B /* FSFile.swift */; };
50+
E05D2E722D7E8801004A6CE0 /* EmptyChartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E05D2E712D7E8801004A6CE0 /* EmptyChartView.swift */; };
51+
E05D2E752D7EA95B004A6CE0 /* ChartType.swift in Sources */ = {isa = PBXBuildFile; fileRef = E05D2E742D7EA95B004A6CE0 /* ChartType.swift */; };
5052
E05DA4C12D13752C00A49203 /* DebugLogManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E05DA4C02D13752C00A49203 /* DebugLogManager.swift */; };
5153
E064D40A2D77F32F008500AE /* CalendarDay.swift in Sources */ = {isa = PBXBuildFile; fileRef = E064D4092D77F32F008500AE /* CalendarDay.swift */; };
5254
E078ECFB2D4E6D9800560364 /* Zip in Frameworks */ = {isa = PBXBuildFile; productRef = E078ECFA2D4E6D9800560364 /* Zip */; };
@@ -173,6 +175,8 @@
173175
E0599A0C2CB4DF0800D64E0B /* FileSystemDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileSystemDetailView.swift; sourceTree = "<group>"; };
174176
E0599A0E2CB4E62800D64E0B /* SetUpDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetUpDetailsView.swift; sourceTree = "<group>"; };
175177
E0599A102CB5553B00D64E0B /* FSFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FSFile.swift; sourceTree = "<group>"; };
178+
E05D2E712D7E8801004A6CE0 /* EmptyChartView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyChartView.swift; sourceTree = "<group>"; };
179+
E05D2E742D7EA95B004A6CE0 /* ChartType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChartType.swift; sourceTree = "<group>"; };
176180
E05DA4C02D13752C00A49203 /* DebugLogManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugLogManager.swift; sourceTree = "<group>"; };
177181
E064D4092D77F32F008500AE /* CalendarDay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarDay.swift; sourceTree = "<group>"; };
178182
E06973AA2B35E6F4009C2F7D /* InfiniLink.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = InfiniLink.entitlements; sourceTree = "<group>"; };
@@ -422,6 +426,14 @@
422426
path = Views;
423427
sourceTree = "<group>";
424428
};
429+
E05D2E732D7EA249004A6CE0 /* Heart */ = {
430+
isa = PBXGroup;
431+
children = (
432+
E09696E22D2F807700CCCBF8 /* HeartChartView.swift */,
433+
);
434+
path = Heart;
435+
sourceTree = "<group>";
436+
};
425437
E08F2F312CC899D00094217A /* Weather */ = {
426438
isa = PBXGroup;
427439
children = (
@@ -434,8 +446,9 @@
434446
E09696E12D2F806A00CCCBF8 /* Charts */ = {
435447
isa = PBXGroup;
436448
children = (
449+
E05D2E732D7EA249004A6CE0 /* Heart */,
437450
E09990962D77684800B0FCAF /* Steps */,
438-
E09696E22D2F807700CCCBF8 /* HeartChartView.swift */,
451+
E05D2E712D7E8801004A6CE0 /* EmptyChartView.swift */,
439452
);
440453
path = Charts;
441454
sourceTree = "<group>";
@@ -457,6 +470,7 @@
457470
E0B85D202CB625FE00D85122 /* Exercise.swift */,
458471
E08C4A482CC4048900013D15 /* SleepData.swift */,
459472
E064D4092D77F32F008500AE /* CalendarDay.swift */,
473+
E05D2E742D7EA95B004A6CE0 /* ChartType.swift */,
460474
);
461475
path = Model;
462476
sourceTree = "<group>";
@@ -564,12 +578,12 @@
564578
isa = PBXGroup;
565579
children = (
566580
E0A7C09F2CB245F70042A12D /* Components */,
567-
E05046872D136F0600FE4BCD /* Developer */,
568581
E059999A2CB2E55500D64E0B /* Apps */,
569582
E0A7C0742CB0ECD10042A12D /* Settings */,
570583
E0B85D172CB6241000D85122 /* Exercise */,
571584
E0CD94CD2CC4A20F00DA53A2 /* Connect */,
572585
E0C7C8372CAEF70B0043DA2D /* Welcome */,
586+
E05046872D136F0600FE4BCD /* Developer */,
573587
E0A7C0972CB227060042A12D /* HeartView.swift */,
574588
E05999982CB25C5600D64E0B /* SleepView.swift */,
575589
E03C30B62CC7417C00DD8363 /* WeatherView.swift */,
@@ -797,8 +811,10 @@
797811
E0A7C09E2CB2454B0042A12D /* ExerciseView.swift in Sources */,
798812
E0C7C8592CAF14870043DA2D /* BLECharacteristicHandler.swift in Sources */,
799813
E03A7DF82D0D4E95007A9209 /* DeveloperView.swift in Sources */,
814+
E05D2E752D7EA95B004A6CE0 /* ChartType.swift in Sources */,
800815
E0A7C0A32CB246D10042A12D /* DetailHeaderView.swift in Sources */,
801816
E0599A0D2CB4DF0800D64E0B /* FileSystemDetailView.swift in Sources */,
817+
E05D2E722D7E8801004A6CE0 /* EmptyChartView.swift in Sources */,
802818
E0599A0F2CB4E62800D64E0B /* SetUpDetailsView.swift in Sources */,
803819
E03A7DF62D0D4DDB007A9209 /* DeveloperModeSettingsView.swift in Sources */,
804820
E0A7C0762CB17E520042A12D /* MusicSettingsView.swift in Sources */,
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
//
2+
// EmptyChartView.swift
3+
// InfiniLink
4+
//
5+
// Created by Liam Willey on 3/9/25.
6+
//
7+
8+
import SwiftUI
9+
10+
struct Line: Shape {
11+
func path(in rect: CGRect) -> Path {
12+
return Path { path in
13+
let top = CGPoint(x: rect.maxX, y: rect.minY)
14+
let bottom = CGPoint(x: rect.maxX, y: rect.maxY)
15+
16+
path.move(to: top)
17+
path.addLine(to: bottom)
18+
}
19+
}
20+
}
21+
22+
struct EmptyChartView: View {
23+
@AppStorage("heartRateChartDataSelection") private var dataSelection = 0
24+
25+
let chartType: ChartType
26+
27+
private let backgroundColor = Color.primary.opacity(0.35)
28+
private var detailString: String {
29+
var base = "There isn't any \(chartType.rawValue) data to show "
30+
31+
switch dataSelection {
32+
case 1:
33+
base += "for today"
34+
case 2:
35+
base += "for the week"
36+
case 3:
37+
base += "for the month"
38+
default:
39+
base += "for the current hour"
40+
}
41+
42+
return base
43+
}
44+
45+
init(_ chartType: ChartType) {
46+
self.chartType = chartType
47+
}
48+
49+
var body: some View {
50+
ZStack {
51+
Rectangle()
52+
.stroke(backgroundColor, lineWidth: 1)
53+
HStack(spacing: 0) {
54+
ForEach(1...4, id: \.self) { _ in
55+
Line()
56+
.stroke(backgroundColor, style: StrokeStyle(lineWidth: 1, dash: [4]))
57+
.frame(maxWidth: .infinity)
58+
}
59+
}
60+
VStack(spacing: 5) {
61+
Image(systemName: chartType.icon)
62+
.font(.system(size: 40))
63+
.foregroundStyle(Color(.lightGray))
64+
VStack {
65+
Text("Nothing to see here")
66+
.font(.title2.weight(.bold))
67+
Text(detailString)
68+
.foregroundStyle(.gray)
69+
}
70+
}
71+
.multilineTextAlignment(.center)
72+
.padding()
73+
}
74+
.frame(height: 280)
75+
.listRowBackground(Color.clear)
76+
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
77+
}
78+
}
79+
80+
#Preview {
81+
EmptyChartView(.steps)
82+
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
//
2+
// HeartChartView.swift
3+
// InfiniLink
4+
//
5+
// Created by Liam Willey on 1/8/25.
6+
//
7+
8+
import SwiftUI
9+
import Charts
10+
11+
struct HeartChartDataPoint: Identifiable {
12+
var id = UUID()
13+
let date: Date
14+
let value: Double
15+
}
16+
17+
struct HeartChartView: View {
18+
@ObservedObject var chartManager = ChartManager.shared
19+
20+
@AppStorage("heartRateChartDataSelection") private var dataSelection = 0
21+
22+
func heartPoints() -> [HeartChartDataPoint] {
23+
let now = Date()
24+
let calendar = Calendar.current
25+
26+
let points = chartManager.heartPoints().map { point in
27+
let timestamp = point.timestamp ?? Date() // Should we catch this date?
28+
let dataPoint = HeartChartDataPoint(date: timestamp, value: point.value)
29+
30+
return dataPoint
31+
}
32+
return points.filter { point in
33+
switch dataSelection {
34+
case 1:
35+
if calendar.isDate(point.date, equalTo: now, toGranularity: .day) {
36+
return true
37+
}
38+
case 2:
39+
if calendar.isDate(point.date, equalTo: now, toGranularity: .weekOfYear) {
40+
return true
41+
}
42+
case 3:
43+
if calendar.isDate(point.date, equalTo: now, toGranularity: .month) {
44+
return true
45+
}
46+
default:
47+
if calendar.isDate(point.date, equalTo: now, toGranularity: .hour) {
48+
return true
49+
}
50+
}
51+
52+
return false
53+
}
54+
}
55+
var earliestDate: Date {
56+
return heartPoints().compactMap({ $0.date }).min() ?? Date()
57+
}
58+
var latestDate: Date {
59+
return heartPoints().compactMap({ $0.date }).max() ?? Date()
60+
}
61+
var max: Int {
62+
return Int(heartPoints().compactMap({ $0.value }).max() ?? 0)
63+
}
64+
var min: Int {
65+
return Int(heartPoints().compactMap({ $0.value }).min() ?? 0)
66+
}
67+
68+
var body: some View {
69+
Group {
70+
Group {
71+
Section {
72+
Picker("Range", selection: $dataSelection) {
73+
ForEach(0...3, id: \.self) { index in
74+
Text({
75+
switch index {
76+
case 0: return "H"
77+
case 1: return "D"
78+
case 2: return "W"
79+
case 3: return "M"
80+
default: return "-"
81+
}
82+
}())
83+
.tag(index)
84+
}
85+
}
86+
.pickerStyle(.segmented)
87+
}
88+
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
89+
if heartPoints().count <= 1 {
90+
EmptyChartView(.heart)
91+
} else {
92+
Section {
93+
Chart(heartPoints()) { point in
94+
BarMark(
95+
x: .value("Day", point.date),
96+
y: .value("BPM Min", point.value)
97+
)
98+
.clipShape(Capsule())
99+
.foregroundStyle(Color.red)
100+
}
101+
.frame(height: 280)
102+
} header: {
103+
VStack(alignment: .leading) {
104+
Text(heartPoints().count > 1 ? "Range" : "No Data")
105+
Text({
106+
if max == 0 || min == 0 {
107+
return "0 "
108+
} else {
109+
return "\(min)-\(max) "
110+
}
111+
}())
112+
.font(.system(.title, design: .rounded))
113+
.foregroundColor(.primary)
114+
+ Text("BPM")
115+
Text("\(earliestDate.formatted(.dateTime.month(.abbreviated).day()))-\(latestDate.formatted(.dateTime.day()))")
116+
}
117+
.fontWeight(.semibold)
118+
}
119+
.listRowInsets(EdgeInsets(top: 18, leading: 0, bottom: 0, trailing: 0))
120+
}
121+
}
122+
.listRowBackground(Color.clear)
123+
Section {
124+
Text("Today your heart rate reached a high of \(max), and dropped to a low of \(min) BPM.")
125+
// Text("Is a heart point in an exercise in the last day: \(ExerciseViewModel.shared.isDateDuringExercise(Date()))")
126+
}
127+
}
128+
}
129+
}

InfiniLink/Core/Components/Charts/HeartChartView.swift

Lines changed: 0 additions & 105 deletions
This file was deleted.

0 commit comments

Comments
 (0)