Skip to content

Commit d6a029e

Browse files
committed
Improve onboarding views and fix bugs
1 parent f5df3eb commit d6a029e

9 files changed

Lines changed: 111 additions & 66 deletions

File tree

InfiniLink/BLE/BLEManager.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
129129
}
130130

131131
func scanForNewDevices() {
132+
guard !isScanning else { return }
133+
132134
central.scanForPeripherals(withServices: nil, options: nil)
133135
newPeripherals = []
134136
isScanning = true

InfiniLink/Core/DeviceView.swift

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ struct DeviceView: View {
2121

2222
@AppStorage("sleepGoal") var sleepGoal = 28800
2323
@AppStorage("enableDeveloperMode") var enableDeveloperMode = false
24+
@AppStorage("enableReminders") var enableReminders = true
25+
@AppStorage("enableCalendarNotifications") var enableCalendarNotifications = true
2426

2527
@Environment(\.colorScheme) var colorScheme
2628

@@ -280,8 +282,16 @@ struct DeviceView: View {
280282
bleManager.pairedDevice = deviceManager.fetchDevice()
281283

282284
notificationManager.setWaterRemindersPerDay()
283-
remindersManager.requestAccess()
284-
remindersManager.fetchAllItems()
285+
286+
if !personalizationController.showSetupSheet {
287+
// We've already gone through the inital setup and the user has enabled reminder/calendar notifications so set state and fetch the events
288+
if enableReminders {
289+
remindersManager.requestReminderAccess()
290+
}
291+
if enableCalendarNotifications {
292+
remindersManager.requestCalendarAccess()
293+
}
294+
}
285295
}
286296
.onChange(of: bleManager.weatherCharacteristic) { _ in
287297
WeatherController.shared.fetchWeatherData()

InfiniLink/Core/Exercise/Views/AllExercisesView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ struct AllExercisesView: View {
5050
var body: some View {
5151
List {
5252
Section {
53-
if filteredExercises.isEmpty {
53+
if filteredExercises.isEmpty && !searchText.trimmingCharacters(in: .whitespaces).isEmpty {
5454
Text("Nothing matched your search. Ensure your spelling is correct and try again.")
5555
} else {
5656
ForEach(filteredExercises.sorted(by: { $0.startDate ?? Date() > $1.startDate ?? Date() })) { userExercise in

InfiniLink/Core/Settings/BatterySettingsView.swift

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,18 @@ struct BatterySettingsView: View {
4949
}
5050
.listRowBackground(Color.clear)
5151
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
52-
Section(footer: Text("Send a notification to your devices when your watch is on low battery.")) {
53-
Toggle("Notify on Low Battery", isOn: $sendLowBatteryNotification)
54-
}
55-
if sendLowBatteryNotification {
56-
Section(footer: watchNotifications ? nil : Text("Watch notifications are currently disabled.")) {
57-
Toggle("Send to iPhone", isOn: $sendLowBatteryNotificationToiPhone)
58-
Toggle("Send to Watch", isOn: $sendLowBatteryNotificationToWatch)
59-
.disabled(!watchNotifications)
52+
Group {
53+
Section(footer: watchNotifications ? Text("Send a notification to your devices when your watch is on low battery.") : Text("Watch notifications are currently disabled.")) {
54+
Toggle("Notify on Low Battery", isOn: watchNotifications ? $sendLowBatteryNotification : .constant(false))
55+
}
56+
if sendLowBatteryNotification && watchNotifications {
57+
Section {
58+
Toggle("Send to iPhone", isOn: $sendLowBatteryNotificationToiPhone)
59+
Toggle("Send to Watch", isOn: $sendLowBatteryNotificationToWatch)
60+
}
6061
}
6162
}
63+
.disabled(!watchNotifications)
6264
}
6365
}
6466
.navigationTitle("Battery")

InfiniLink/Core/Welcome/SetUpDetailsView.swift

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,16 @@ struct SetUpDetailsView: View {
1919

2020
let list: Bool
2121

22+
func resetFields() {
23+
personalizationController.weight = nil
24+
personalizationController.height = nil
25+
weight = ""
26+
height = ""
27+
}
28+
func filteredUnit(_ unit: Double) -> String {
29+
String(unit).replacingOccurrences(of: ".0", with: "")
30+
}
31+
2232
init(list: Bool = false) {
2333
self.list = list
2434
}
@@ -30,7 +40,6 @@ struct SetUpDetailsView: View {
3040
NavigationStack {
3141
content
3242
}
33-
.navigationViewStyle(.stack)
3443
}
3544
}
3645

@@ -57,11 +66,7 @@ struct SetUpDetailsView: View {
5766
Text("Imperial").tag(PersonalizationController.Unit.imperial)
5867
}
5968
.onChange(of: personalizationController.units) { _ in
60-
personalizationController.weight = nil
61-
personalizationController.height = nil
62-
63-
weight = ""
64-
height = ""
69+
resetFields()
6570
}
6671
}
6772
Section {
@@ -102,20 +107,17 @@ struct SetUpDetailsView: View {
102107
.keyboardType(.decimalPad)
103108
if !list {
104109
Button {
105-
// Go to next view
106110
nextViewActive = true
107111

108-
// Is this handled by the onDisappear?
109-
personalizationController.height = Double(height)
110-
personalizationController.weight = Double(weight)
112+
// We don't need to assign any vars here because it's handled by the onDisappear
111113
} label: {
112114
Text("Next")
113115
.padding()
114-
.font(.body.weight(.semibold))
116+
.fontWeight(.semibold)
115117
.frame(maxWidth: .infinity)
116118
.background(Color.blue)
117119
.foregroundStyle(.white)
118-
.clipShape(RoundedRectangle(cornerRadius: 20))
120+
.clipShape(.rect(cornerRadius: 20))
119121
}
120122
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
121123
.listRowBackground(Color.clear)
@@ -127,18 +129,19 @@ struct SetUpDetailsView: View {
127129
}
128130
.interactiveDismissDisabled()
129131
.toolbar {
130-
if !list {
132+
// Don't show the button if the user has edited anything
133+
if !list && weight == filteredUnit(personalizationController.calculatedWeight) && height == filteredUnit(personalizationController.calculatedHeight) {
131134
Button("Skip") {
132135
nextViewActive = true
133136
}
134137
}
135138
}
136139
.onAppear {
137140
if let weight = personalizationController.weight, weight > 0 {
138-
self.weight = String(personalizationController.calculatedWeight)
141+
self.weight = filteredUnit(personalizationController.calculatedWeight)
139142
}
140143
if let height = personalizationController.height, height > 0 {
141-
self.height = String(personalizationController.calculatedHeight)
144+
self.height = filteredUnit(personalizationController.calculatedHeight)
142145
}
143146
}
144147
.onDisappear {
@@ -162,6 +165,8 @@ struct NotificationsSetupView: View {
162165
@AppStorage("enableReminders") var enableReminders = true
163166
@AppStorage("enableCalendarNotifications") var enableCalendarNotifications = true
164167
@AppStorage("remindOnStepGoalCompletion") var remindOnStepGoalCompletion = true
168+
@AppStorage("heartRangeReminder") var heartRangeReminder = false
169+
@AppStorage("sendLowBatteryNotification") var sendLowBatteryNotification = true
165170

166171
var body: some View {
167172
Form {
@@ -177,19 +182,31 @@ struct NotificationsSetupView: View {
177182
.multilineTextAlignment(.center)
178183
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
179184
.listRowBackground(Color.clear)
180-
Section("Health") {
185+
Section(header: Text("Health"), footer: Text("Receive a reminder to drink water for the set amount of times a day.") + Text(" You can customize this in notification settings.")) {
181186
Toggle("Water Reminder", isOn: $waterReminder)
182187
}
188+
Section(footer: Text("Get a notification when your heart rate goes above or below the specified range.") + Text(" You can customize this in notification settings.")) {
189+
Toggle("Heart Range Notifications", isOn: $heartRangeReminder)
190+
}
183191
Section(header: Text("Daily Goals"), footer: Text("Get notified when you reach your daily fitness goals.")) {
184192
Toggle("Steps", isOn: $remindOnStepGoalCompletion)
185193
}
194+
Section(header: Text("Battery"), footer: Text("Get notified when your watch's battery is low.")) {
195+
Toggle("Notify on Low Battery", isOn: $sendLowBatteryNotification)
196+
}
186197
Section(header: Text("Other"), footer: Text("Receive notifications on your watch when reminders and calendar events are due.")) {
187198
Toggle("Reminder Notifications", isOn: $enableReminders)
188199
Toggle("Calendar Notifications", isOn: $enableCalendarNotifications)
189200
}
190201
Button {
191202
notificationManager.requestNotificationAuthorization()
192-
remindersManager.requestAccess()
203+
204+
if enableReminders {
205+
remindersManager.requestReminderAccess()
206+
}
207+
if enableCalendarNotifications {
208+
remindersManager.requestCalendarAccess()
209+
}
193210

194211
personalizationController.showSetupSheet = false
195212
} label: {

InfiniLink/Localizable.xcstrings

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@
3232
},
3333
" step goal streak!" : {
3434

35+
},
36+
" You can customize this in notification settings." : {
37+
3538
},
3639
"-" : {
3740
"localizations" : {
@@ -702,6 +705,9 @@
702705
},
703706
"Get notified when you reach your daily fitness goals." : {
704707

708+
},
709+
"Get notified when your watch's battery is low." : {
710+
705711
},
706712
"GitHub Actions" : {
707713

InfiniLink/Utils/MusicController.swift

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,10 @@ import SwiftUI
1313
class MusicController {
1414
static let shared = MusicController()
1515

16-
let bleManager = BLEManager.shared
17-
18-
var musicPlayer = MPMusicPlayerController.systemMusicPlayer
19-
var musicPlaying = 0
20-
21-
let volumeSlots: Float = 15.0
16+
private let bleManager = BLEManager.shared
17+
private let volumeNotch: Float = 0.1
18+
private var musicPlayer = MPMusicPlayerController.systemMusicPlayer
19+
private var musicPlaying = 0
2220

2321
struct SongInfo {
2422
var trackName: String = ""
@@ -32,7 +30,7 @@ class MusicController {
3230
@AppStorage("allowMusicControl") var allowMusicControl = true
3331
@AppStorage("allowVolumeControl") var allowVolumeControl = true
3432

35-
private init() {
33+
init() {
3634
initialize()
3735
}
3836

@@ -73,15 +71,9 @@ class MusicController {
7371
case 4:
7472
musicPlayer.skipToPreviousItem()
7573
case 5:
76-
if allowVolumeControl {
77-
let newVolume = min(session.outputVolume + (1 / volumeSlots), 1.0)
78-
MPVolumeView.setVolume(newVolume)
79-
}
74+
changeVolume(up: true)
8075
case 6:
81-
if allowVolumeControl {
82-
let newVolume = max(session.outputVolume - (1 / volumeSlots), 0.0)
83-
MPVolumeView.setVolume(newVolume)
84-
}
76+
changeVolume(up: false)
8577
default:
8678
break
8779
}
@@ -90,6 +82,17 @@ class MusicController {
9082
}
9183
}
9284

85+
func changeVolume(up: Bool) {
86+
guard allowVolumeControl else { return }
87+
88+
let session = AVAudioSession.sharedInstance()
89+
let sessionVolume = session.outputVolume // FIXME: session.outputVolume is always 1
90+
91+
print(session.outputVolume)
92+
93+
let volume = up ? min(sessionVolume + volumeNotch, 1.0) : max(sessionVolume - volumeNotch, 0.0)
94+
MPVolumeView.setVolume(volume)
95+
}
9396

9497
func getCurrentSongInfo() -> SongInfo {
9598
let currentTrack = self.musicPlayer.nowPlayingItem
@@ -140,7 +143,7 @@ extension MPVolumeView {
140143
let volumeView = MPVolumeView()
141144
let slider = volumeView.subviews.first(where: { $0 is UISlider }) as? UISlider
142145

143-
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.01) {
146+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
144147
slider?.value = volume
145148
}
146149
}

InfiniLink/Utils/PersonalizationController.swift

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,19 @@ class PersonalizationController: ObservableObject {
2626

2727
@AppStorage("showSetupSheet") var showSetupSheet = true
2828

29+
private let avgMaleWeight: Double = 68.039
30+
private let avgFemaleWeight: Double = 54.43
31+
private let avgMaleHeight: Double = 175.26
32+
private let avgFemaleHeight: Double = 162.56
33+
2934
var isPersonalizationAvailable: Bool {
3035
!showSetupSheet && (weight != nil || height != nil)
3136
}
3237

3338
var calculatedWeight: Double {
34-
guard let weight = self.weight, weight > 0 else { return gender == .male ? 68.039 : 54.43 }
39+
guard let weight = self.weight, weight > 0 else {
40+
return gender == .male ? avgMaleWeight : avgFemaleWeight
41+
}
3542

3643
if units == .imperial {
3744
// Convert from kg to lbs
@@ -42,7 +49,9 @@ class PersonalizationController: ObservableObject {
4249
}
4350

4451
var calculatedHeight: Double {
45-
guard let height = self.height, height > 0 else { return gender == .male ? 175.26 : 162.56 }
52+
guard let height = self.height, height > 0 else {
53+
return gender == .male ? avgMaleHeight : avgFemaleHeight
54+
}
4655

4756
if units == .imperial {
4857
// Convert from cm to in

InfiniLink/Utils/RemindersManager.swift

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -91,33 +91,29 @@ class RemindersManager: ObservableObject {
9191
}
9292

9393
func requestReminderAccess() {
94-
eventStore.requestAccess(to: .reminder) { granted, error in
95-
if let error = error {
96-
log("Unknown error while requesting reminder access: \(error.localizedDescription)", caller: "RemindersManager")
97-
} else if granted {
98-
DispatchQueue.main.async {
99-
self.areRemindersAuthorized = true
100-
self.fetchAllItems()
101-
}
102-
}
94+
eventStore.requestAccess(to: .reminder) {
95+
self.onAccessRequest($0, $1, reminder: true)
10396
}
10497
}
10598

10699
func requestCalendarAccess() {
107-
eventStore.requestAccess(to: .event) { granted, error in
108-
if let error = error {
109-
log("Unknown error while requesting calendar access: \(error.localizedDescription)", caller: "RemindersManager")
110-
} else if granted {
111-
DispatchQueue.main.async {
100+
eventStore.requestAccess(to: .event) {
101+
self.onAccessRequest($0, $1, reminder: false)
102+
}
103+
}
104+
105+
func onAccessRequest(_ granted: Bool, _ error: Error?, reminder: Bool) {
106+
if let error = error {
107+
log("Unknown error while requesting \(reminder ? "reminder" : "calendar") access: \(error.localizedDescription)", caller: "RemindersManager")
108+
} else if granted {
109+
DispatchQueue.main.async {
110+
if reminder {
111+
self.areRemindersAuthorized = true
112+
} else {
112113
self.areEventsAuthorized = true
113-
self.fetchAllItems()
114114
}
115+
self.fetchAllItems()
115116
}
116117
}
117118
}
118-
119-
func requestAccess() {
120-
requestReminderAccess()
121-
requestCalendarAccess()
122-
}
123119
}

0 commit comments

Comments
 (0)