Skip to content

Commit

Permalink
Add consent document viewer to profile screen (#26)
Browse files Browse the repository at this point in the history
* Add consent document viewer to profile screen

* Update strings, improve organization, download consent if not present

* Add privacy policy link
  • Loading branch information
vishnuravi authored Jun 30, 2024
1 parent 22e67a6 commit dbbdb1f
Show file tree
Hide file tree
Showing 7 changed files with 207 additions and 29 deletions.
16 changes: 10 additions & 6 deletions LifeSpace.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
63497B732BBF855E001F8419 /* OptionsPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63497B722BBF855E001F8419 /* OptionsPanel.swift */; };
634FFF672C169F40005E8217 /* LifeSpaceConsent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 634FFF662C169F40005E8217 /* LifeSpaceConsent.swift */; };
634FFF6D2C16B81A005E8217 /* HIPAAAuthorization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 634FFF6C2C16B81A005E8217 /* HIPAAAuthorization.swift */; };
639B69DB2C2BCD6A00C0FF4A /* ConsentPDFVIewer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 639B69DA2C2BCD6A00C0FF4A /* ConsentPDFVIewer.swift */; };
63A28D312C0580310025A1E0 /* RefreshIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63A28D302C0580310025A1E0 /* RefreshIcon.swift */; };
63A28D332C062E2E0025A1E0 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 63A28D322C062E2E0025A1E0 /* GoogleService-Info.plist */; };
63A8DB292C1FE81200939757 /* AppIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = 63A8DB282C1FE81200939757 /* AppIcon.png */; };
Expand Down Expand Up @@ -139,6 +140,7 @@
63497B722BBF855E001F8419 /* OptionsPanel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptionsPanel.swift; sourceTree = "<group>"; };
634FFF662C169F40005E8217 /* LifeSpaceConsent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LifeSpaceConsent.swift; sourceTree = "<group>"; };
634FFF6C2C16B81A005E8217 /* HIPAAAuthorization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HIPAAAuthorization.swift; sourceTree = "<group>"; };
639B69DA2C2BCD6A00C0FF4A /* ConsentPDFVIewer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsentPDFVIewer.swift; sourceTree = "<group>"; };
63A28D302C0580310025A1E0 /* RefreshIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefreshIcon.swift; sourceTree = "<group>"; };
63A28D322C062E2E0025A1E0 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
63A8DB282C1FE81200939757 /* AppIcon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = AppIcon.png; sourceTree = "<group>"; };
Expand Down Expand Up @@ -404,6 +406,7 @@
A9FE7ACF2AA39BAB0077B045 /* AccountSheet.swift */,
A9720E422ABB68CC00872D23 /* AccountSetupHeader.swift */,
A9DFE8A82ABE551400428242 /* AccountButton.swift */,
639B69DA2C2BCD6A00C0FF4A /* ConsentPDFVIewer.swift */,
);
path = Account;
sourceTree = "<group>";
Expand Down Expand Up @@ -622,6 +625,7 @@
2FE5DC3829EDD7CA004B9AB4 /* InterestingModules.swift in Sources */,
2FE5DC3529EDD7CA004B9AB4 /* Consent.swift in Sources */,
63EA5F8C2BC78F8400A48590 /* DailySurveyResponse.swift in Sources */,
639B69DB2C2BCD6A00C0FF4A /* ConsentPDFVIewer.swift in Sources */,
2FE5DC4529EDD7F2004B9AB4 /* Binding+Negate.swift in Sources */,
63497B732BBF855E001F8419 /* OptionsPanel.swift in Sources */,
2FC975A82978F11A00BA99FE /* Home.swift in Sources */,
Expand Down Expand Up @@ -768,7 +772,7 @@
CODE_SIGN_ENTITLEMENTS = "LifeSpace/Supporting Files/LifeSpace.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 4;
CURRENT_PROJECT_VERSION = 5;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = "";
ENABLE_PREVIEWS = YES;
Expand Down Expand Up @@ -832,7 +836,7 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 637867499T;
DEVELOPMENT_TEAM = "";
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.odden.strokecoguitests;
Expand Down Expand Up @@ -972,7 +976,7 @@
CODE_SIGN_ENTITLEMENTS = "LifeSpace/Supporting Files/LifeSpace.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 4;
CURRENT_PROJECT_VERSION = 5;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = "";
ENABLE_PREVIEWS = YES;
Expand Down Expand Up @@ -1019,7 +1023,7 @@
CODE_SIGN_ENTITLEMENTS = "LifeSpace/Supporting Files/LifeSpace.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 4;
CURRENT_PROJECT_VERSION = 5;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = "";
ENABLE_PREVIEWS = YES;
Expand Down Expand Up @@ -1102,7 +1106,7 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 637867499T;
DEVELOPMENT_TEAM = "";
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.odden.strokecoguitests;
Expand All @@ -1120,7 +1124,7 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 637867499T;
DEVELOPMENT_TEAM = "";
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.odden.strokecoguitests;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/app-check.git",
"state" : {
"revision" : "076b241a625e25eac22f8849be256dfb960fcdfe",
"version" : "10.19.1"
"revision" : "3b62f154d00019ae29a71e9738800bb6f18b236d",
"version" : "10.19.2"
}
},
{
Expand All @@ -42,17 +42,17 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/firebase-ios-sdk.git",
"state" : {
"revision" : "8bcaf973b1d84e119b7c7c119abad72ed460979f",
"version" : "10.27.0"
"revision" : "e57841b296d04370ea23580f908881b0ccab17b9",
"version" : "10.28.1"
}
},
{
"identity" : "googleappmeasurement",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleAppMeasurement.git",
"state" : {
"revision" : "70df02431e216bed98dd461e0c4665889245ba70",
"version" : "10.27.0"
"revision" : "fe727587518729046fc1465625b9afd80b5ab361",
"version" : "10.28.0"
}
},
{
Expand Down Expand Up @@ -123,17 +123,17 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/mapbox/mapbox-common-ios.git",
"state" : {
"revision" : "9c04997ed32c5b2506eb704f9f7a16367b5dcc64",
"version" : "24.4.0"
"revision" : "8e3259433704add95894a3e7823f5658a17bd43b",
"version" : "24.5.0-rc.1"
}
},
{
"identity" : "mapbox-core-maps-ios",
"kind" : "remoteSourceControl",
"location" : "https://github.com/mapbox/mapbox-core-maps-ios.git",
"state" : {
"revision" : "c7897628028afb2c008d50c5d5d4054768c99340",
"version" : "11.4.0"
"revision" : "20d05d13d66371c46d486ce6d79d0e463df20fb9",
"version" : "11.5.0-rc.1"
}
},
{
Expand All @@ -142,7 +142,7 @@
"location" : "https://github.com/mapbox/mapbox-maps-ios.git",
"state" : {
"branch" : "main",
"revision" : "88a52b28b59e2bf89b149b6cfa221b7db039b977"
"revision" : "a4fb127d3a48ce84fbff0aa922218766d27000e0"
}
},
{
Expand Down Expand Up @@ -213,17 +213,17 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/StanfordSpezi/SpeziFirebase.git",
"state" : {
"revision" : "f05c859f75d317dca9b378b7f8a7cfa8e135df04",
"version" : "1.1.1"
"revision" : "00ff0db12bf72ba39354e263d8f916ae8392368b",
"version" : "1.1.2"
}
},
{
"identity" : "spezifoundation",
"kind" : "remoteSourceControl",
"location" : "https://github.com/StanfordSpezi/SpeziFoundation",
"state" : {
"revision" : "01af5b91a54f30ddd121258e81aff2ddc2a99ff9",
"version" : "1.0.4"
"revision" : "656f7659895dd4f182306963c2def75c16608472",
"version" : "1.1.1"
}
},
{
Expand Down Expand Up @@ -285,8 +285,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/StanfordSpezi/SpeziViews.git",
"state" : {
"revision" : "4d2a724d97c8f19ac7de7aa2c046b1cb3ef7b279",
"version" : "1.3.1"
"revision" : "0899c34c4b05e85b19e2dd2395783686aef81652",
"version" : "1.4.0"
}
},
{
Expand Down
51 changes: 51 additions & 0 deletions LifeSpace/Account/AccountSheet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
// SPDX-License-Identifier: MIT
//

import PDFKit
import SpeziAccount
import SwiftUI

Expand All @@ -19,12 +20,14 @@ struct AccountSheet: View {
@State var isInSetup = false
@State var overviewIsEditing = false

@AppStorage(StorageKeys.studyID) var studyID = "unknownStudyID"

var body: some View {
NavigationStack {
ZStack {
if account.signedIn && !isInSetup {
AccountOverview(isEditing: $overviewIsEditing) {
optionsList
}
.onDisappear {
overviewIsEditing = false
Expand Down Expand Up @@ -53,6 +56,44 @@ struct AccountSheet: View {
}
}
}

var optionsList: some View {
List {
Section(header: Text("STUDYID_SECTION")) {
Text(studyID)
}
Section(header: Text("DOCUMENTS_SECTION")) {
NavigationLink(destination: {
if let url = getDocumentURL(for: "consent") {
ConsentPDFViewer(url: url)
} else {
Text("DOCUMENT_NOT_FOUND_MESSAGE")
}
}) {
Text("VIEW_CONSENT_DOCUMENT")
}
NavigationLink(destination: {
if let url = getDocumentURL(for: "hipaaAuthorization") {
ConsentPDFViewer(url: url)
} else {
Text("DOCUMENT_NOT_FOUND_MESSAGE")
}
}) {
Text("VIEW_HIPAA_AUTHORIZATION")
}
NavigationLink(destination: EmptyView()) {
Button(action: {
if let url = URL(string: "https://michelleodden.com/cardinal-lifespace-privacy-policy/") {
UIApplication.shared.open(url)
}
}) {
Text("VIEW_PRIVACY_POLICY")
}
.buttonStyle(PlainButtonStyle())
}
}
}
}

var closeButton: some ToolbarContent {
ToolbarItem(placement: .cancellationAction) {
Expand All @@ -61,6 +102,16 @@ struct AccountSheet: View {
}
}
}


func getDocumentURL(for fileName: String) -> URL? {
guard let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
return nil
}

let filenameWithStudyID = "\(studyID)_\(fileName).pdf"
return documentsURL.appendingPathComponent(filenameWithStudyID)
}
}


Expand Down
22 changes: 22 additions & 0 deletions LifeSpace/Account/ConsentPDFVIewer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//
// ConsentPDFViewer.swift
// LifeSpace
//
// Created by Vishnu Ravi on 6/26/24.
//

import PDFKit
import SwiftUI

struct ConsentPDFViewer: UIViewRepresentable {
let url: URL

func makeUIView(context: Context) -> PDFView {
let pdfView = PDFView()
pdfView.document = PDFDocument(url: url)
pdfView.autoScales = true
return pdfView
}

func updateUIView(_ uiView: PDFView, context: Context) {}
}
18 changes: 13 additions & 5 deletions LifeSpace/LifeSpaceStandard.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ actor LifeSpaceStandard: Standard, EnvironmentAccessible, HealthKitConstraint, O
}
}

var studyID: String {
UserDefaults.standard.string(forKey: StorageKeys.studyID) ?? "unknownStudyID"
}


init() {
if !FeatureFlags.disableFirebase {
Expand Down Expand Up @@ -214,8 +218,6 @@ actor LifeSpaceStandard: Standard, EnvironmentAccessible, HealthKitConstraint, O
///
/// - Parameter consent: The consent form's data to be stored as a `PDFDocument`.
func store(consent: PDFDocument) async {
let studyID = UserDefaults.standard.string(forKey: StorageKeys.studyID) ?? "unknownStudyID"

guard !FeatureFlags.disableFirebase else {
guard let basePath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
logger.error("Could not create path for writing consent form to user document directory.")
Expand Down Expand Up @@ -248,7 +250,6 @@ actor LifeSpaceStandard: Standard, EnvironmentAccessible, HealthKitConstraint, O
/// - Parameter name: The name of the consent document.
func store(consentData: Data, name: String) async {
/// Adds the study ID to the file name
let studyID = UserDefaults.standard.string(forKey: StorageKeys.studyID) ?? "unknownStudyID"
let filename = "\(studyID)_\(name).pdf"

guard let docURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
Expand All @@ -271,8 +272,15 @@ actor LifeSpaceStandard: Standard, EnvironmentAccessible, HealthKitConstraint, O

func isConsentFormUploaded(name: String) async -> Bool {
do {
let studyID = UserDefaults.standard.string(forKey: StorageKeys.studyID) ?? "unknownStudyID"
_ = try await userBucketReference.child("ls_consent/\(studyID)_\(name).pdf").getMetadata()
let maxSize: Int64 = 10 * 1024 * 1024
let data = try await userBucketReference.child("ls_consent/\(studyID)_\(name).pdf").data(maxSize: maxSize)

if let docURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
let filename = "\(studyID)_\(name).pdf"
let url = docURL.appendingPathComponent(filename)
try? data.write(to: url)
}

return true
} catch {
return false
Expand Down
Loading

0 comments on commit dbbdb1f

Please sign in to comment.