Skip to content

Commit

Permalink
Add Export Button (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
PSchmiedmayer authored Sep 26, 2023
1 parent 6ab3df7 commit 040daef
Show file tree
Hide file tree
Showing 13 changed files with 261 additions and 37 deletions.
8 changes: 8 additions & 0 deletions OwnYourData.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
2F0191F92A9E4CF100E9EB0E /* String+LocalizedError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F0191F82A9E4CF100E9EB0E /* String+LocalizedError.swift */; };
2F0191FB2A9E579E00E9EB0E /* FHIRMultipleResourceInterpreter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F0191FA2A9E579E00E9EB0E /* FHIRMultipleResourceInterpreter.swift */; };
2F0191FD2A9E57BD00E9EB0E /* Prompt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F0191FC2A9E57BD00E9EB0E /* Prompt.swift */; };
2F0608CF2AC3884100836556 /* ExportPackage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F0608CE2AC3884100836556 /* ExportPackage.swift */; };
2F2146F42A82AF7F007CB929 /* SpeziOpenAI in Frameworks */ = {isa = PBXBuildFile; productRef = 2F2146F32A82AF7F007CB929 /* SpeziOpenAI */; };
2F2146F72A82AF9B007CB929 /* SpeziOnboarding in Frameworks */ = {isa = PBXBuildFile; productRef = 2F2146F62A82AF9B007CB929 /* SpeziOnboarding */; };
2F2146FE2A82B236007CB929 /* SpeziFirebaseAccount in Frameworks */ = {isa = PBXBuildFile; productRef = 2F2146FD2A82B236007CB929 /* SpeziFirebaseAccount */; };
Expand All @@ -36,6 +37,7 @@
2F8537712A9DB6C8006994BB /* ModelsR4 in Frameworks */ = {isa = PBXBuildFile; productRef = 2F8537702A9DB6C8006994BB /* ModelsR4 */; };
2F8537742A9DB6E5006994BB /* HealthKitOnFHIR in Frameworks */ = {isa = PBXBuildFile; productRef = 2F8537732A9DB6E5006994BB /* HealthKitOnFHIR */; };
2F8537762A9DB781006994BB /* SpeziAccount in Frameworks */ = {isa = PBXBuildFile; productRef = 2F8537752A9DB781006994BB /* SpeziAccount */; };
2F9652FE2AC388F300977083 /* URL+Zip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F9652FD2AC388F300977083 /* URL+Zip.swift */; };
2FA2023329CBCC0C0039C21A /* DocumentScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FA2023229CBCC0C0039C21A /* DocumentScanner.swift */; };
2FB2943929CBA29900EE91A0 /* ProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FB2943329CBA29900EE91A0 /* ProfileView.swift */; };
2FB2943D29CBA29900EE91A0 /* ClinicalTrialsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FB2943729CBA29900EE91A0 /* ClinicalTrialsView.swift */; };
Expand Down Expand Up @@ -103,6 +105,7 @@
2F0191F82A9E4CF100E9EB0E /* String+LocalizedError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+LocalizedError.swift"; sourceTree = "<group>"; };
2F0191FA2A9E579E00E9EB0E /* FHIRMultipleResourceInterpreter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FHIRMultipleResourceInterpreter.swift; sourceTree = "<group>"; };
2F0191FC2A9E57BD00E9EB0E /* Prompt.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Prompt.swift; sourceTree = "<group>"; };
2F0608CE2AC3884100836556 /* ExportPackage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExportPackage.swift; sourceTree = "<group>"; };
2F4E237D2989A2FE0013F3D9 /* OnboardingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingTests.swift; sourceTree = "<group>"; };
2F4E23822989D51F0013F3D9 /* TemplateAppTestingSetup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateAppTestingSetup.swift; sourceTree = "<group>"; };
2F5E32BC297E05EA003432F8 /* TemplateAppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateAppDelegate.swift; sourceTree = "<group>"; };
Expand All @@ -112,6 +115,7 @@
2F8537672A9DB67E006994BB /* FHIRResource+Search.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "FHIRResource+Search.swift"; sourceTree = "<group>"; };
2F8537682A9DB67E006994BB /* FHIR.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FHIR.swift; sourceTree = "<group>"; };
2F8537692A9DB67E006994BB /* FHIRResource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FHIRResource.swift; sourceTree = "<group>"; };
2F9652FD2AC388F300977083 /* URL+Zip.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+Zip.swift"; sourceTree = "<group>"; };
2FA2023229CBCC0C0039C21A /* DocumentScanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentScanner.swift; sourceTree = "<group>"; };
2FAEC07F297F583900C11C42 /* TemplateApplication.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = TemplateApplication.entitlements; sourceTree = "<group>"; };
2FB2943329CBA29900EE91A0 /* ProfileView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProfileView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -294,6 +298,8 @@
isa = PBXGroup;
children = (
2FB2943329CBA29900EE91A0 /* ProfileView.swift */,
2F0608CE2AC3884100836556 /* ExportPackage.swift */,
2F9652FD2AC388F300977083 /* URL+Zip.swift */,
);
path = Profile;
sourceTree = "<group>";
Expand Down Expand Up @@ -601,12 +607,14 @@
2FA2023329CBCC0C0039C21A /* DocumentScanner.swift in Sources */,
FCD3FFB729CD0C31004D1E0E /* LogoView.swift in Sources */,
2FB2FCD029CBDDC00027D85A /* TemplateSignUp.swift in Sources */,
2F9652FE2AC388F300977083 /* URL+Zip.swift in Sources */,
2FB2FCD729CBDDC00027D85A /* Consent.swift in Sources */,
2FB2FCD329CBDDC00027D85A /* TemplateLogin.swift in Sources */,
2F8537652A9D1279006994BB /* HealthKitPermissions.swift in Sources */,
2FE573A729CD4672008EBBD4 /* PDFView.swift in Sources */,
2F0191F62A9E4CBD00E9EB0E /* OpenAIChatView.swift in Sources */,
2FE573A329CD4617008EBBD4 /* PDFDocument+Transferable.swift in Sources */,
2F0608CF2AC3884100836556 /* ExportPackage.swift in Sources */,
2F4E23832989D51F0013F3D9 /* TemplateAppTestingSetup.swift in Sources */,
2F5E32BD297E05EA003432F8 /* TemplateAppDelegate.swift in Sources */,
2FB2FCD129CBDDC00027D85A /* UserView.swift in Sources */,
Expand Down
1 change: 1 addition & 0 deletions TemplateApplication/Documents/DocumentGallery.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ struct DocumentGallery: View {
},
label: {
Image(systemName: "plus")
.accessibilityLabel("Add Document")
}
)
}
Expand Down
4 changes: 4 additions & 0 deletions TemplateApplication/FHIR Standard/FHIR.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ actor FHIR: Standard, ObservableObject, ObservableObjectProvider, HealthKitConst
Array(_resources.values)
}

@MainActor var exportPackage: ExportPackage {
ExportPackage(resources: resources)
}


init() {
guard HKHealthStore.isHealthDataAvailable() else {
Expand Down
1 change: 1 addition & 0 deletions TemplateApplication/Home.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ struct Home: View {
},
label: {
Image(systemName: "person.crop.circle")
.accessibilityLabel("Profile View")
}
)
}
Expand Down
1 change: 1 addition & 0 deletions TemplateApplication/Instructions/Instructions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ struct Instructions: View {
ForEach(steps, id: \.offset) { step in
HStack {
Image(systemName: "\(step.offset + 1).circle.fill")
.accessibilityHidden(true)
.foregroundColor(Color("ButtonColor_light"))
.font(.system(size: 45))
.frame(minHeight: 90)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,10 @@ struct AccountSetup: View {
Group {
if account.signedIn {
Image(systemName: "person.badge.shield.checkmark.fill")
.accessibilityLabel("You are signed in")
} else {
Image(systemName: "person.fill.badge.plus")
.accessibilityLabel("You can sign up or sign in")
}
}
.font(.system(size: 150))
Expand Down
1 change: 1 addition & 0 deletions TemplateApplication/Onboarding/HealthKitPermissions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ struct HealthKitPermissions: View {
)
Spacer()
Image(systemName: "heart.text.square.fill")
.accessibilityHidden(true)
.font(.system(size: 150))
.foregroundColor(.accentColor)
Text("HEALTHKIT_PERMISSIONS_DESCRIPTION")
Expand Down
6 changes: 3 additions & 3 deletions TemplateApplication/Onboarding/Welcome.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,17 @@ struct Welcome: View {
subtitle: "WELCOME_SUBTITLE",
areas: [
.init(
icon: Image(systemName: "folder.badge.plus"),
icon: Image(systemName: "folder.badge.plus"), // swiftlint:disable:this accessibility_label_for_image
title: "WELCOME_AREA1_TITLE",
description: "WELCOME_AREA1_DESCRIPTION"
),
.init(
icon: Image(systemName: "magnifyingglass"),
icon: Image(systemName: "magnifyingglass"), // swiftlint:disable:this accessibility_label_for_image
title: "WELCOME_AREA2_TITLE",
description: "WELCOME_AREA2_DESCRIPTION"
),
.init(
icon: Image(systemName: "square.and.arrow.up"),
icon: Image(systemName: "square.and.arrow.up"), // swiftlint:disable:this accessibility_label_for_image
title: "WELCOME_AREA3_TITLE",
description: "WELCOME_AREA3_DESCRIPTION"
)
Expand Down
1 change: 1 addition & 0 deletions TemplateApplication/Overview/InstructionsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ struct InstructionsView: View {
var body: some View {
VStack(alignment: .center) {
Image(systemName: "doc.text.magnifyingglass")
.accessibilityHidden(true)
.font(.system(size: 90))
.foregroundColor(.accentColor)
.padding(.vertical, 8)
Expand Down
1 change: 1 addition & 0 deletions TemplateApplication/Overview/LLMSummaryView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ struct LLMSummaryView: View {
var body: some View {
VStack(alignment: .center) {
Image(systemName: "magnifyingglass")
.accessibilityHidden(true)
.font(.system(size: 90))
.foregroundColor(.accentColor)
.padding(.vertical, 8)
Expand Down
61 changes: 61 additions & 0 deletions TemplateApplication/Profile/ExportPackage.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//
// This source file is part of the Stanford OwnYourData Application project
//
// SPDX-FileCopyrightText: 2023 Stanford University
//
// SPDX-License-Identifier: MIT
//

import CoreTransferable
import OSLog


struct ExportPackage: Transferable {
static var transferRepresentation: some TransferRepresentation {
FileRepresentation(
exportedContentType: .zip,
exporting: { document in
try await SentTransferredFile(document.zipRepresentation)
}
)
}


let resources: [FHIRResource]

private var directory: URL {
FileManager.default.temporaryDirectory.appendingPathComponent(
"edu.stanford.ownyourdate.export",
isDirectory: true
)
}

var zipRepresentation: URL {
get async throws {
if directory.exists {
try FileManager.default.removeItem(at: directory)
}
try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true, attributes: nil)

for resource in resources {
guard let id = resource.id else {
os_log("Can not export resource named \(resource.displayName)")
continue
}

let resourceJSONData = Data(resource.jsonDescription.utf8)
try resourceJSONData.write(to: directory.appending(path: "\(id).json"))
}

let zipURL = try directory.zip()
try FileManager.default.removeItem(at: directory)

return zipURL
}
}


func deleteZipRepresentation() throws {
try FileManager.default.removeItem(at: directory.appendingPathExtension(".zip"))
}
}
103 changes: 69 additions & 34 deletions TemplateApplication/Profile/ProfileView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,51 +19,86 @@ struct ProfileView: View {
@AppStorage(StorageKeys.onboardingFlowComplete) var completedOnboardingFlow = false

@EnvironmentObject var documentManager: DocumentManager
@EnvironmentObject var fhirStandard: FHIR


var body: some View {
VStack {
Image(systemName: "person.circle.fill")
.resizable()
.scaledToFit()
.accessibility(label: Text("profile image"))
.foregroundColor(Color("ButtonColor_dark"))
.frame(width: 120, height: 120)
.padding(.top, 80)
VStack(spacing: 10) {
Text("\(firstName) \(lastName)")
.font(.title2)
Text("Email: \(email)")
.font(.subheadline)
}
Spacer()
OwnYourDataButton(
title: "Log Out",
action: {
do {
try Auth.auth().signOut()

firstName = ""
lastName = ""
email = ""

completedOnboardingFlow = false

documentManager.removeAllDocuments()

print("Logged out.")
} catch {
print("Error signing out: \(error)")
GeometryReader { proxy in
ScrollView(.vertical) {
VStack {
Image(systemName: "person.circle.fill")
.resizable()
.scaledToFit()
.accessibility(label: Text("profile image"))
.foregroundColor(Color("ButtonColor_dark"))
.frame(width: 120, height: 120)
.padding(.top, 40)
VStack(spacing: 10) {
Text("\(firstName) \(lastName)")
.font(.title2)
Text("Email: \(email)")
.font(.subheadline)
}
sharebutton
Spacer(minLength: 64)
logoutButton
}
)
.padding(.bottom, 30)
.frame(minHeight: proxy.size.height)
}
.frame(width: proxy.size.width)
}
.padding(.bottom, 30)
.task {
fetchUserData()
}
}

@ViewBuilder private var sharebutton: some View {
VStack(alignment: .center, spacing: 8) {
ShareLink(
item: fhirStandard.exportPackage,
preview: SharePreview(
Text("FHIR JSON Export Package")
)
) {
HStack {
Image(systemName: "square.and.arrow.up")
.accessibilityHidden(true)
Text("Export")
}
}
.buttonStyle(.borderedProminent)
Text("Export your health records to share them with the OwnYourData team.")
.multilineTextAlignment(.center)
.foregroundStyle(Color.accentColor)
.font(.caption)
}
.padding()
}

@ViewBuilder private var logoutButton: some View {
OwnYourDataButton(
title: "Log Out",
action: {
do {
try Auth.auth().signOut()

firstName = ""
lastName = ""
email = ""

completedOnboardingFlow = false

documentManager.removeAllDocuments()

print("Logged out.")
} catch {
print("Error signing out: \(error)")
}
}
)
}


private func fetchUserData() {
if let currentUser = Auth.auth().currentUser {
Expand Down
Loading

0 comments on commit 040daef

Please sign in to comment.