Skip to content
Merged
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
4 changes: 2 additions & 2 deletions MyHeartCounts.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1306,8 +1306,8 @@
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/SchmiedmayerLab/Spezi.git";
requirement = {
kind = upToNextMinorVersion;
minimumVersion = 0.1.6;
kind = exactVersion;
version = 0.1.6;
};
};
/* End XCRemoteSwiftPackageReference section */
Expand Down
1 change: 1 addition & 0 deletions MyHeartCounts/Account/AccountSheet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ struct AccountSheet: View {
.foregroundStyle(.textLabel)
}
PostTrialNudgesToggle()
UpdateComorbiditiesButton()
NavigationLink("Review Consent Forms") {
SignedConsentForms()
}
Expand Down
8 changes: 8 additions & 0 deletions MyHeartCounts/Account/Demographics/DemographicsData.swift
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,14 @@ final class DemographicsData {
populate(from: account.details ?? AccountDetails())
}

/// Populates the instance, unless it already has been populated before.
func populateIfEmpty(from account: Account) {
guard self.account == nil else {
return
}
populate(from: account)
}

private func populate(from details: AccountDetails) {
shouldHandleUpdates = false
defer {
Expand Down
71 changes: 71 additions & 0 deletions MyHeartCounts/Account/UpdateComorbiditiesButton.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//
// This source file is part of the My Heart Counts iOS application based on the Stanford Spezi Template Application project
//
// SPDX-FileCopyrightText: 2026 Stanford University
//
// SPDX-License-Identifier: MIT
//

import SFSafeSymbols
import SpeziAccount
import SpeziViews
import SwiftUI


struct UpdateComorbiditiesButton: View {
@Environment(Account.self)
private var account

@State private var showSheet = false
@State private var demographicsData = DemographicsData()
@State private var viewState: ViewState = .idle

var body: some View {
Button {
showSheet = true
} label: {
HStack {
Text("UPDATE_COMORBIDITIES_BUTTON_TITLE")
.foregroundStyle(.textLabel)
Spacer()
DisclosureIndicator()
}
}
.disabled(account.details == nil)
.sheet(isPresented: $showSheet) {
@Bindable var data = demographicsData
NavigationStack {
ComorbiditiesPicker(selection: $data[\.comorbidities].withDefault(Comorbidities()))
.navigationBarTitleDisplayMode(.inline)
.interactiveDismissDisabled()
.toolbar {
ToolbarItem(placement: .confirmationAction) {
closeSheetButton
}
}
}
.onAppear {
demographicsData.populateIfEmpty(from: account)
}
}
}

@ViewBuilder private var closeSheetButton: some View {
if #available(iOS 26, *) {
AsyncButton(role: .confirm, state: $viewState) {
try await closeSheet()
} label: {
Label("Done", systemSymbol: .checkmark)
}
} else {
AsyncButton("Done", state: $viewState) {
try await closeSheet()
}
}
}
Comment thread
lukaskollmer marked this conversation as resolved.

private func closeSheet() async throws {
showSheet = false
try await demographicsData.flush()
}
}
13 changes: 8 additions & 5 deletions MyHeartCounts/Heart Health Dashboard/CVHScore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -400,12 +400,15 @@ extension ScoreDefinition {
])
}()

/// LDL Cholesterol
static let cvhBloodLipids = ScoreDefinition(default: 0, scoringBands: [
.inRange(..<130, score: 1, explainer: "< 130"),
.inRange(130..<160, score: 0.6, explainer: "130 – 159"),
.inRange(160..<190, score: 0.4, explainer: "160 – 189"),
.inRange(190..<220, score: 0.2, explainer: "190 – 219"),
.inRange(220..., score: 0, explainer: "220+")
.inRange(..<56, score: 1, explainer: "< 55"),
.inRange(56..<70, score: 0.9, explainer: "56 – 69"),
Comment thread
lukaskollmer marked this conversation as resolved.
.inRange(70..<100, score: 0.8, explainer: "70 – 99"),
.inRange(100..<130, score: 0.6, explainer: "100 – 129"),
.inRange(130..<160, score: 0.4, explainer: "130 – 159"),
.inRange(160..<190, score: 0.2, explainer: "160 – 189"),
.inRange(190..., score: 0, explainer: "190+")
])

static let cvhBloodGlucose = ScoreDefinition(default: 0, scoringBands: [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//
// This source file is part of the My Heart Counts iOS application based on the Stanford Spezi Template Application project
//
// SPDX-FileCopyrightText: 2026 Stanford University
//
// SPDX-License-Identifier: MIT
//

import Foundation
import SpeziStudyDefinition
import SwiftUI


struct TimedWalkRunTestResultInfo: View {
let result: TimedWalkingTestResult

var body: some View {
LabeledContent("Date", value: result.startDate, format: .dateTime)
LabeledContent("Steps", value: result.numberOfSteps, format: .number)
LabeledContent(
"Distance",
value: Measurement<UnitLength>(value: result.distanceCovered, unit: .meters),
format: .measurement(width: .abbreviated)
)
switch result.test.kind {
case .walking:
let _ = () // swiftlint:disable:this redundant_discardable_let
case .running:
LabeledContent(
"VO2MAX_ESTIMATE_LABEL",
value: Measurement<UnitOxygenConsumption>(
value: (result.distanceCovered - 504.9) / 44.73,
unit: .mlPerKgPerMin
),
format: .measurement(width: .abbreviated, usage: .asProvided, numberFormatStyle: .number.precision(.fractionLength(...1)))
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,7 @@ struct PastTimedWalkTestResults: View {
@ViewBuilder
private func makeDetailsView(for result: TimedWalkingTestResult) -> some View {
Form {
LabeledContent("Start", value: result.startDate, format: .dateTime)
LabeledContent("End", value: result.endDate, format: .dateTime)
LabeledContent("Number of Steps", value: result.numberOfSteps, format: .number)
LabeledContent(
"Distance",
value: Measurement<UnitLength>(value: result.distanceCovered, unit: .meters),
format: .measurement(width: .abbreviated)
)
TimedWalkRunTestResultInfo(result: result)
}
.navigationTitle(result.test.displayTitle.localizedString())
.navigationBarTitleDisplayMode(.inline)
Expand Down
13 changes: 11 additions & 2 deletions MyHeartCounts/Modules/SetupTestEnvironment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ final class SetupTestEnvironment: Module, EnvironmentAccessible, Sendable {
}


private func loginAndEnroll( // swiftlint:disable:this function_body_length
private func loginAndEnroll( // swiftlint:disable:this function_body_length cyclomatic_complexity
_ credentials: SetupTestEnvironmentConfig.Credentials
) async throws {
logger.notice("Logging in and enrolling into Study using credentials \(String(describing: credentials))")
Expand Down Expand Up @@ -245,7 +245,16 @@ final class SetupTestEnvironment: Module, EnvironmentAccessible, Sendable {
newDetails.weightInKG = 67
newDetails.raceEthnicity = .white
newDetails.latinoStatus = LatinoStatusOption.options[0]
newDetails.comorbidities = Comorbidities()
newDetails.comorbidities = { () -> Comorbidities in
var value = Comorbidities()
guard let heartFailureOption = Comorbidities.Comorbidity.primaryComorbidities.first(where: {
$0.title.localizedString(for: .enUS).contains("Heart Failure")
}) else {
return value
}
value[heartFailureOption] = .selected(startDate: DateComponents())
return value
}()
newDetails.usRegion = .dc
newDetails.householdIncomeUS = HouseholdIncomeUS.options[0]
newDetails.educationUS = EducationStatusUS.options[0]
Expand Down
Loading
Loading