Skip to content

Commit d56855a

Browse files
authored
post-trial activity nudge toggle (#156)
1 parent 9b99f57 commit d56855a

7 files changed

Lines changed: 154 additions & 61 deletions

File tree

MyHeartCounts.xcodeproj/project.pbxproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1450,8 +1450,8 @@
14501450
isa = XCRemoteSwiftPackageReference;
14511451
repositoryURL = "https://github.com/apple/FHIRModels.git";
14521452
requirement = {
1453-
kind = upToNextMajorVersion;
1454-
minimumVersion = 0.7.0;
1453+
kind = upToNextMinorVersion;
1454+
minimumVersion = 0.8.0;
14551455
};
14561456
};
14571457
80BE064E2DCF3A2D00171114 /* XCRemoteSwiftPackageReference "swift-package-list" */ = {

MyHeartCounts.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

Lines changed: 5 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

MyHeartCounts/Account/AccountSheet.swift

Lines changed: 91 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -79,48 +79,8 @@ struct AccountSheet: View {
7979
}
8080
}
8181
if let enrollment = enrollments.first {
82-
Section("Study Participation") { // swiftlint:disable:this closure_body_length
83-
Button {
84-
openUrl(MyHeartCounts.website())
85-
} label: {
86-
HStack {
87-
makeEnrolledStudyRow(for: enrollment)
88-
Spacer()
89-
DisclosureIndicator()
90-
}
91-
.contentShape(Rectangle())
92-
.foregroundStyle(colorScheme.textLabelForegroundStyle)
93-
}
94-
NavigationLink("Review Consent Forms") {
95-
SignedConsentForms()
96-
}
97-
if let text = { () -> LocalizedStringResource? in
98-
switch (isProcessingHealthData, isProcessingSensorKitData) {
99-
case (true, true):
100-
"Processing Health and SensorKit Data…"
101-
case (true, false):
102-
"Processing Health Data…"
103-
case (false, true):
104-
"Processing SensorKit Data…"
105-
case (false, false):
106-
nil
107-
}
108-
}() {
109-
let label = HStack {
110-
Text(text)
111-
Spacer()
112-
ProgressView()
113-
}
114-
if debugModeEnabled {
115-
NavigationLink {
116-
DataProcessingDebugView()
117-
} label: {
118-
label
119-
}
120-
} else {
121-
label
122-
}
123-
}
82+
Section("Study Participation") {
83+
studyParticipationSection(enrollment)
12484
}
12585
}
12686
Section {
@@ -170,6 +130,52 @@ struct AccountSheet: View {
170130
}
171131

172132

133+
@ViewBuilder
134+
private func studyParticipationSection(_ enrollment: StudyEnrollment) -> some View {
135+
Button {
136+
openUrl(MyHeartCounts.website())
137+
} label: {
138+
HStack {
139+
makeEnrolledStudyRow(for: enrollment)
140+
Spacer()
141+
DisclosureIndicator()
142+
}
143+
.contentShape(Rectangle())
144+
.foregroundStyle(colorScheme.textLabelForegroundStyle)
145+
}
146+
PostTrialNudgesToggle()
147+
NavigationLink("Review Consent Forms") {
148+
SignedConsentForms()
149+
}
150+
if let text = { () -> LocalizedStringResource? in
151+
switch (isProcessingHealthData, isProcessingSensorKitData) {
152+
case (true, true):
153+
"Processing Health and SensorKit Data…"
154+
case (true, false):
155+
"Processing Health Data…"
156+
case (false, true):
157+
"Processing SensorKit Data…"
158+
case (false, false):
159+
nil
160+
}
161+
}() {
162+
let label = HStack {
163+
Text(text)
164+
Spacer()
165+
ProgressView()
166+
}
167+
if debugModeEnabled {
168+
NavigationLink {
169+
DataProcessingDebugView()
170+
} label: {
171+
label
172+
}
173+
} else {
174+
label
175+
}
176+
}
177+
}
178+
173179
@ViewBuilder
174180
private func makeEnrolledStudyRow(for enrollment: StudyEnrollment) -> some View {
175181
if let studyInfo = enrollment.studyBundle?.studyDefinition.metadata {
@@ -198,6 +204,49 @@ struct AccountSheet: View {
198204
}
199205

200206

207+
extension AccountSheet {
208+
private struct PostTrialNudgesToggle: View {
209+
@Environment(Account.self)
210+
private var account
211+
212+
@State private var value = false
213+
@State private var updateTask: Task<Void, Never>?
214+
@State private var shouldHandleUpdates = true
215+
216+
var body: some View {
217+
Toggle(isOn: $value) {
218+
VStack(alignment: .leading) {
219+
Text("POST_TRIAL_ACTIVITY_NUDGES_TOGGLE_TITLE")
220+
Text("POST_TRIAL_ACTIVITY_NUDGES_TOGGLE_SUBTITLE")
221+
.font(.footnote)
222+
.foregroundStyle(.secondary)
223+
}
224+
}
225+
.onChange(of: account.details?.postTrialNudgesOptIn ?? false, initial: true) { _, newValue in
226+
shouldHandleUpdates = false
227+
value = newValue
228+
shouldHandleUpdates = true
229+
}
230+
.onChange(of: value) { _, newValue in
231+
updateTask?.cancel()
232+
guard shouldHandleUpdates else {
233+
return
234+
}
235+
updateTask = Task {
236+
do {
237+
try await Task.sleep(for: .seconds(0.25))
238+
var details = AccountDetails()
239+
details.postTrialNudgesOptIn = newValue
240+
try await account.accountService.updateAccountDetails(AccountModifications(modifiedDetails: details))
241+
} catch {
242+
// we silently ignore the error here
243+
}
244+
}
245+
}
246+
}
247+
}
248+
}
249+
201250
extension AccountSheet {
202251
private struct AboutRow: View {
203252
// swiftlint:disable attributes

MyHeartCounts/Account/MHCAccountKeys.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,9 @@ extension AccountDetails {
132132
@AccountKey(id: "preferredMeasurementSystem", name: "Preferred Measurement System", as: String.self)
133133
var preferredMeasurementSystem: String?
134134

135+
@AccountKey(id: "extendedActivityNudgesOptIn", name: "Post-Trial Nudges Opt-In", as: Bool.self)
136+
var postTrialNudgesOptIn: Bool?
137+
135138
@AccountKey(
136139
id: "mostRecentOnboardingStep",
137140
name: "",
@@ -148,7 +151,7 @@ extension AccountDetails {
148151
\.hasWithdrawnFromStudy,
149152
\.dateOfEnrollment, \.lastSignedConsentVersion, \.lastSignedConsentDate, \.didOptInToTrial,
150153
\.fcmToken, \.enableDebugMode, \.timeZone, \.language, \.preferredMeasurementSystem, \.lastActiveDate,
151-
\.mostRecentOnboardingStep, \.preferredWorkoutTypes, \.preferredNudgeNotificationTime
154+
\.mostRecentOnboardingStep, \.preferredWorkoutTypes, \.preferredNudgeNotificationTime, \.postTrialNudgesOptIn
152155
)
153156
extension AccountKeys {}
154157

MyHeartCounts/Modules/DeferredConfigLoading.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ enum DeferredConfigLoading {
239239
.manual(\.preferredWorkoutTypes),
240240
.manual(\.preferredNudgeNotificationTime),
241241
.manual(\.stageOfChange),
242+
.manual(\.postTrialNudgesOptIn),
242243
// demographics
243244
.manual(\.mhcGenderIdentity),
244245
.manual(\.usRegion),

MyHeartCounts/Resources/Localizable.xcstrings

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6289,6 +6289,53 @@
62896289
}
62906290
}
62916291
}
6292+
},
6293+
"POST_TRIAL_ACTIVITY_NUDGES_TOGGLE_SUBTITLE" : {
6294+
"localizations" : {
6295+
"en" : {
6296+
"stringUnit" : {
6297+
"state" : "translated",
6298+
"value" : "Enable to continue receiving periodic activity nudges after the initial trial period."
6299+
}
6300+
},
6301+
"en-GB" : {
6302+
"stringUnit" : {
6303+
"state" : "translated",
6304+
"value" : "Enable to continue receiving periodic activity nudges after the initial trial period."
6305+
}
6306+
},
6307+
"es" : {
6308+
"stringUnit" : {
6309+
"state" : "translated",
6310+
"value" : "Habilitar la opción para seguir recibiendo recordatorios periódicos de actividad después del período de prueba inicial."
6311+
}
6312+
}
6313+
}
6314+
},
6315+
"POST_TRIAL_ACTIVITY_NUDGES_TOGGLE_TITLE" : {
6316+
"localizations" : {
6317+
"en" : {
6318+
"stringUnit" : {
6319+
"state" : "translated",
6320+
"value" : "Post-Trial Activity Nudges"
6321+
}
6322+
},
6323+
"en-GB" : {
6324+
"stringUnit" : {
6325+
"state" : "translated",
6326+
"value" : "Post-Trial Activity Nudges"
6327+
}
6328+
},
6329+
"es" : {
6330+
"stringUnit" : {
6331+
"state" : "translated",
6332+
"value" : "Incentivos para la actividad posterior al ensayo"
6333+
}
6334+
}
6335+
}
6336+
},
6337+
"Post-Trial Nudges Opt-In" : {
6338+
62926339
},
62936340
"Prefer not to state" : {
62946341
"localizations" : {

MyHeartCountsShared/Package.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@ var packageDeps: [Package.Dependency] = [
2121
#if !os(Linux)
2222
packageDeps += [
2323
.package(url: "https://github.com/StanfordSpezi/SpeziStudy.git", from: "0.1.20"),
24-
.package(url: "https://github.com/StanfordSpezi/SpeziStorage.git", from: "2.1.4"), // not actually used but we need to force the version until we update SpeziStudy
25-
.package(url: "https://github.com/SFSafeSymbols/SFSafeSymbols.git", from: "7.0.0")
24+
.package(url: "https://github.com/SFSafeSymbols/SFSafeSymbols.git", from: "7.0.0"),
25+
// not actually used but we need to force the version until we update SpeziStudy
26+
.package(url: "https://github.com/StanfordSpezi/SpeziStorage.git", from: "2.1.4"),
27+
.package(url: "https://github.com/StanfordSpezi/SpeziScheduler.git", from: "1.2.20")
2628
]
2729
#endif
2830

0 commit comments

Comments
 (0)