Skip to content

Commit

Permalink
Code Cleanup & Restructure Code (#49)
Browse files Browse the repository at this point in the history
# Code Cleanup & Restructure Code

## ⚙️ Release Notes 
- Restructures the code into smaller views & improves testability and
SwiftUI previews.


## 📝 Code of Conduct & Contributing Guidelines 

By submitting creating this pull request, you agree to follow our [Code
of
Conduct](https://github.com/CS342/.github/blob/main/CODE_OF_CONDUCT.md)
and [Contributing
Guidelines](https://github.com/CS342/.github/blob/main/CONTRIBUTING.md):
- [x] I agree to follow the [Code of
Conduct](https://github.com/CS342/.github/blob/main/CODE_OF_CONDUCT.md)
and [Contributing
Guidelines](https://github.com/CS342/.github/blob/main/CONTRIBUTING.md).
  • Loading branch information
PSchmiedmayer authored Mar 18, 2024
1 parent 87a986f commit 18e0bc8
Show file tree
Hide file tree
Showing 51 changed files with 1,304 additions and 1,220 deletions.
19 changes: 13 additions & 6 deletions CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,17 @@ authors:
- family-names: "Schmiedmayer"
given-names: "Paul"
orcid: "https://orcid.org/0000-0002-8607-9148"
- family-names: "Ravi"
given-names: "Vishnu"
orcid: "https://orcid.org/0000-0003-0359-1275"
- family-names: "Aalami"
given-names: "Oliver"
orcid: "https://orcid.org/0000-0002-7799-2429"
- family-names: "Jörke"
given-names: "Matthew"
orcid: "https://orcid.org/0000-0003-2972-462X"
- family-names: "Jimenez"
given-names: "Bryant"
- family-names: "Hur"
given-names: "Evelyn"
- family-names: "Tran"
given-names: "Caroline"
- family-names: "Song"
given-names: "Evelyn"
- family-names: "Naik"
given-names: "Dhruv"
title: "Prisma"
6 changes: 6 additions & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ Prisma Contributors
=================================

* [Paul Schmiedmayer](https://github.com/PSchmiedmayer)
* [Matthew Jörke](https://github.com/mjoerke)
* [Bryant Jimenez](https://github.com/bryant-jimenez)
* [Evelyn Hur](https://github.com/evelyn-hur)
* [Caroline Tran](https://github.com/carolinentran)
* [Evelyn Song](https://github.com/EvelynBunnyDev)
* [Dhruv Naik](https://github.com/dhruvna1k)
* [Andreas Bauer](https://github.com/Supereg)
* [Philipp Zagar](https://github.com/philippzagar)
* [Nikolai Madlener](https://github.com/NikolaiMadlener)
113 changes: 64 additions & 49 deletions Prisma.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"originHash" : "2744d3b6cb9385cb089a3b7f85f47cd50be0044a6d049ff1f93f530ab4329df2",
"originHash" : "0234cf36595d6a5fc4bf3c54942b170bdbefd26ef609a4d8a651f17037d3face",
"pins" : [
{
"identity" : "abseil-cpp-binary",
Expand Down Expand Up @@ -132,8 +132,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/StanfordBDHG/ResearchKit",
"state" : {
"revision" : "64512d0a0a5cc3e9d5b3fc5217c54f11d0dc044c",
"version" : "2.2.28"
"revision" : "6b28cdf0d06c3d6e96b5585369968b85deac96e0",
"version" : "2.2.29"
}
},
{
Expand Down Expand Up @@ -199,15 +199,6 @@
"version" : "0.5.3"
}
},
{
"identity" : "spezimockwebservice",
"kind" : "remoteSourceControl",
"location" : "https://github.com/StanfordSpezi/SpeziMockWebService.git",
"state" : {
"revision" : "b18067d3499e630bbd995ef05a296ef8fdd42528",
"version" : "1.0.0"
}
},
{
"identity" : "spezionboarding",
"kind" : "remoteSourceControl",
Expand Down Expand Up @@ -258,8 +249,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-argument-parser",
"state" : {
"revision" : "c8ed701b513cf5177118a175d85fbbbcd707ab41",
"version" : "1.3.0"
"revision" : "46989693916f56d1186bd59ac15124caef896560",
"version" : "1.3.1"
}
},
{
Expand Down
11 changes: 5 additions & 6 deletions Prisma/Account/AccountSheet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,12 @@ import SwiftUI


struct AccountSheet: View {
@Environment(\.dismiss) var dismiss

@Environment(\.dismiss) private var dismiss
@Environment(Account.self) private var account
@Environment(\.accountRequired) var accountRequired
@Environment(\.accountRequired) private var accountRequired

@State var isInSetup = false
@State var overviewIsEditing = false
@State private var isInSetup = false
@State private var overviewIsEditing = false


var body: some View {
Expand Down Expand Up @@ -57,7 +56,7 @@ struct AccountSheet: View {
}
}
}

var closeButton: some ToolbarContent {
ToolbarItem(placement: .cancellationAction) {
Button("CLOSE") {
Expand Down
89 changes: 48 additions & 41 deletions Prisma/Chat/ChatView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,72 +7,79 @@
//

import Firebase
import Foundation
import SpeziAccount
import SwiftUI
import WebKit


struct ChatView: View {
@Binding var presentingAccount: Bool
@State private var token: String?


private var url: URL? {
guard let token else {
return nil
}

return Constants.hostname.appending(queryItems: [URLQueryItem(name: token, value: token)])
}


var body: some View {
NavigationStack {
GeometryReader { geometry in
// Fetch JWT token asynchronously
if let token = token {
if let url = URL(string: "http://localhost:3000?token=\(token)") { // this needs to be sent to the frontend
Group {
if let url {
WebView(url: url)
.navigationTitle("Chat")
.frame(
width: geometry.size.width,
height: geometry.size.height
)
} else {
Text("Invalid URL")
VStack(spacing: 16) {
ProgressView()
Text("Loading Chat View")
.foregroundStyle(.secondary)
.font(.caption)
}
}
} else {
ProgressView()
}
.frame(
width: geometry.size.width,
height: geometry.size.height
)
}
/*
.onChange(of: account.signedIn) {
guard account.signedIn else {
return
}
Task {
try await self.signInWithFirebase()
}
}
*/
.task {
do {
try await self.getFirebaseIDToken()
} catch {
print("Firebase Auth failed \(error)")
.navigationTitle("Chat")
.task {
await self.getFirebaseIDToken()
}
.toolbar {
if AccountButton.shouldDisplay {
AccountButton(isPresented: $presentingAccount)
}
}
}
}
}


init(presentingAccount: Binding<Bool>) {
self._presentingAccount = presentingAccount
}
}

extension ChatView {
func getFirebaseIDToken() async throws {
token = try await Auth.auth().currentUser?.getIDToken()
print("token is:", token ?? "")


private func getFirebaseIDToken() async {
guard !ProcessInfo.processInfo.isPreviewSimulator else {
try? await Task.sleep(for: .seconds(1.0))
token = "TOKEN"
return
}

do {
token = try await Auth.auth().currentUser?.getIDToken()
} catch {
print("Firebase Auth failed \(error)")
}
}
}


#if DEBUG
struct ChatView_Previews: PreviewProvider {
static var previews: some View {
ChatView(presentingAccount: .constant(false))
}
#Preview {
ChatView(presentingAccount: .constant(false))
}
#endif
15 changes: 13 additions & 2 deletions Prisma/Chat/WebView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,28 @@
import SwiftUI
import WebKit


struct WebView: UIViewRepresentable {
var url: URL



func makeUIView(context: Context) -> WKWebView {
let webView = WKWebView()
webView.scrollView.isScrollEnabled = true
return webView
}

func updateUIView(_ uiView: WKWebView, context: Context) {
let request = URLRequest(url: url)
uiView.load(request)
}
}


#Preview {
guard let stanfordURL = URL(string: "https://stanford.edu") else {
fatalError("Could not construct URL.")
}

return WebView(url: stanfordURL)
}
131 changes: 131 additions & 0 deletions Prisma/Helper/Date+ConstructTimeIndex.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
//
// This source file is part of the Stanford Prisma Application based on the Stanford Spezi Template Application project
//
// SPDX-FileCopyrightText: 2023 Stanford University
//
// SPDX-License-Identifier: MIT
//

import Foundation


extension Date {
static func constructTimeIndex(startDate: Date, endDate: Date) -> [String: Any?] {
let calendar = Calendar.current
// extract the calendar components from the startDate and the endDate
let startComponents = calendar.dateComponents([.year, .month, .day, .hour, .minute, .second, .timeZone], from: startDate)
let endComponents = calendar.dateComponents([.year, .month, .day, .hour, .minute, .second, .timeZone], from: endDate)
let isRange = startDate != endDate

// initialize a dictionary for timeIndex and populate with info extracted above
var timeIndex: [String: Any?] = [
"range": isRange,
"timezone": startComponents.timeZone?.identifier,
"datetime.start": startDate.toISOFormat(),
"datetime.end": endDate.toISOFormat()
]

// passing the timeIndex dictionary by reference so the changes persist
addTimeIndexComponents(&timeIndex, dateComponents: startComponents, suffix: ".start")
addTimeIndexComponents(&timeIndex, dateComponents: endComponents, suffix: ".end")
addTimeIndexRangeComponents(&timeIndex, startComponents: startComponents, endComponents: endComponents)

return timeIndex
}


// populate timeIndex dict with individual components from DateComponents (startComponents for this case)
// "inout" parameter means the argument is passed by reference (dict is modified inside the funct and changes persist)
private static func addTimeIndexComponents(_ timeIndex: inout [String: Any?], dateComponents: DateComponents, suffix: String) {
timeIndex["year" + suffix] = dateComponents.year
timeIndex["month" + suffix] = dateComponents.month
timeIndex["day" + suffix] = dateComponents.day
timeIndex["hour" + suffix] = dateComponents.hour
timeIndex["minute" + suffix] = dateComponents.minute
timeIndex["second" + suffix] = dateComponents.second
timeIndex["dayMinute" + suffix] = calculateDayMinute(hour: dateComponents.hour, minute: dateComponents.minute)
timeIndex["fifteenMinBucket" + suffix] = calculate15MinBucket(hour: dateComponents.hour, minute: dateComponents.minute)
}

// if the start/end time shows that we have a time RANGE and not a time STAMP
// then add the range-related components to the timeIndex
private static func addTimeIndexRangeComponents(
_ timeIndex: inout [String: Any?],
startComponents: DateComponents,
endComponents: DateComponents
) {
timeIndex["year.range"] = getRange(
start: startComponents.year,
end: endComponents.year,
maxValue: Int.max
)
timeIndex["month.range"] = getRange(
start: startComponents.month,
end: endComponents.month,
maxValue: 12,
startValue: 1 // months are 1-indexed
)
timeIndex["day.range"] = getRange(
start: startComponents.day,
end: endComponents.day,
maxValue: daysInMonth(month: startComponents.month, year: startComponents.year),
startValue: 1 // days are 1-indexed
)
timeIndex["hour.range"] = getRange(
start: startComponents.hour,
end: endComponents.hour,
maxValue: 23
)
timeIndex["dayMinute.range"] = getRange(
start: calculateDayMinute(hour: startComponents.hour, minute: startComponents.minute),
end: calculateDayMinute(hour: endComponents.hour, minute: endComponents.minute),
maxValue: 1439
)
timeIndex["fifteenMinBucket.range"] = getRange(
start: calculate15MinBucket(hour: startComponents.hour, minute: startComponents.minute),
end: calculate15MinBucket(hour: endComponents.hour, minute: endComponents.minute),
maxValue: 95
)

// Minute and second ranges are not likely to be accurate since they often will fill the whole range.
// We will also never query on individual minutes or seconds worth of data.
}

// swiftlint:disable discouraged_optional_collection
// passed the start and end bounds, returns the range in whichever unit passed in
private static func getRange(start: Int?, end: Int?, maxValue: Int?, startValue: Int = 0) -> [Int]? {
guard let startInt = start, let endInt = end, let maxValueInt = maxValue else {
return nil
}

if startInt <= endInt {
return Array(startInt...endInt)
} else {
return Array(startInt...maxValueInt) + Array(startValue...endInt)
}
}

private static func daysInMonth(month: Int?, year: Int?) -> Int? {
let dateComponents = DateComponents(year: year, month: month)
let calendar = Calendar.current
guard let date = calendar.date(from: dateComponents),
let range = calendar.range(of: .day, in: .month, for: date) else {
return nil // Provide a default value in case of nil
}
return range.count
}

private static func calculateDayMinute(hour: Int?, minute: Int?) -> Int? {
guard let hour = hour, let minute = minute else {
return nil
}
return hour * 60 + minute
}

private static func calculate15MinBucket(hour: Int?, minute: Int?) -> Int? {
guard let hour = hour, let minute = minute else {
return nil
}
return hour * 4 + minute / 15
}
}
Loading

0 comments on commit 18e0bc8

Please sign in to comment.