Skip to content

Commit

Permalink
Finish UI for notitifications
Browse files Browse the repository at this point in the history
  • Loading branch information
nriedman committed May 31, 2024
1 parent 8bb5d71 commit a62e8fe
Show file tree
Hide file tree
Showing 11 changed files with 336 additions and 73 deletions.
16 changes: 16 additions & 0 deletions ENGAGEHF.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@
2FE5DCB129EE6107004B9AB4 /* AccountOnboarding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FE5DCAC29EE6107004B9AB4 /* AccountOnboarding.swift */; };
2FF53D8D2A8729D600042B76 /* ENGAGEHFStandard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FF53D8C2A8729D600042B76 /* ENGAGEHFStandard.swift */; };
4D052DB82BE07892006A784E /* MeasurementManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D052DB72BE07892006A784E /* MeasurementManager.swift */; };
4D065BE02C09401700EBB3AE /* StudyApplicationListCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D065BDF2C09401700EBB3AE /* StudyApplicationListCard.swift */; };
4D0F389E2C09779100DA12F7 /* LearnMoreButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D0F389D2C09779000DA12F7 /* LearnMoreButton.swift */; };
4D19ED012BE5CAFC00CDBAA8 /* MeasurementRecordedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D19ED002BE5CAFC00CDBAA8 /* MeasurementRecordedView.swift */; };
4D49AAFE2BC9D50400C77310 /* BluetoothServices in Frameworks */ = {isa = PBXBuildFile; productRef = 4D49AAFD2BC9D50400C77310 /* BluetoothServices */; };
4D49AB002BC9D50400C77310 /* BluetoothViews in Frameworks */ = {isa = PBXBuildFile; productRef = 4D49AAFF2BC9D50400C77310 /* BluetoothViews */; };
Expand Down Expand Up @@ -136,6 +138,8 @@
2FE5DCAC29EE6107004B9AB4 /* AccountOnboarding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountOnboarding.swift; sourceTree = "<group>"; };
2FF53D8C2A8729D600042B76 /* ENGAGEHFStandard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ENGAGEHFStandard.swift; sourceTree = "<group>"; };
4D052DB72BE07892006A784E /* MeasurementManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeasurementManager.swift; sourceTree = "<group>"; };
4D065BDF2C09401700EBB3AE /* StudyApplicationListCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StudyApplicationListCard.swift; sourceTree = "<group>"; };
4D0F389D2C09779000DA12F7 /* LearnMoreButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LearnMoreButton.swift; sourceTree = "<group>"; };
4D19ED002BE5CAFC00CDBAA8 /* MeasurementRecordedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeasurementRecordedView.swift; sourceTree = "<group>"; };
4D49AB052BC9D56900C77310 /* WeightScaleDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeightScaleDevice.swift; sourceTree = "<group>"; };
4D49AB0C2BC9DF9100C77310 /* WeightScaleFeature.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeightScaleFeature.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -292,6 +296,15 @@
path = Helper;
sourceTree = "<group>";
};
4D065BDE2C093FF600EBB3AE /* ReusableElements */ = {
isa = PBXGroup;
children = (
4D065BDF2C09401700EBB3AE /* StudyApplicationListCard.swift */,
4D0F389D2C09779000DA12F7 /* LearnMoreButton.swift */,
);
path = ReusableElements;
sourceTree = "<group>";
};
4D19ECFC2BE5B9F200CDBAA8 /* Views */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -469,6 +482,7 @@
2FF53D8C2A8729D600042B76 /* ENGAGEHFStandard.swift */,
2F4E23822989D51F0013F3D9 /* ENGAGEHFTestingSetup.swift */,
2FC975A72978F11A00BA99FE /* Home.swift */,
4D065BDE2C093FF600EBB3AE /* ReusableElements */,
4DDFC7632BF34FC1002B07A1 /* Education */,
4DDFC7622BF34FB4002B07A1 /* Medications */,
4DDFC7612BF34FAC002B07A1 /* HeartHealth */,
Expand Down Expand Up @@ -709,6 +723,7 @@
buildActionMask = 2147483647;
files = (
2FE5DC4129EDD7EE004B9AB4 /* StorageKeys.swift in Sources */,
4D0F389E2C09779100DA12F7 /* LearnMoreButton.swift in Sources */,
4D19ED012BE5CAFC00CDBAA8 /* MeasurementRecordedView.swift in Sources */,
4DDFC7652BF350C2002B07A1 /* Education.swift in Sources */,
4DDFC7692BF35113002B07A1 /* HeartHealth.swift in Sources */,
Expand Down Expand Up @@ -746,6 +761,7 @@
4DDFC7492BF32CD6002B07A1 /* ToDoSection.swift in Sources */,
2F4E23832989D51F0013F3D9 /* ENGAGEHFTestingSetup.swift in Sources */,
2F5E32BD297E05EA003432F8 /* ENGAGEHFDelegate.swift in Sources */,
4D065BE02C09401700EBB3AE /* StudyApplicationListCard.swift in Sources */,
4DDFC7702BFAEAD7002B07A1 /* ConfirmMeasurementButton.swift in Sources */,
A9FE7AD02AA39BAB0077B045 /* AccountSheet.swift in Sources */,
653A2551283387FE005D4D48 /* ENGAGEHF.swift in Sources */,
Expand Down
15 changes: 9 additions & 6 deletions ENGAGEHF/Bluetooth/Views/ViewElements.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,15 @@ struct CloseButtonLayer: View {

var body: some View {
HStack {
Button(action: {
dismiss()
}) {
Text(NSLocalizedString("Close", comment: "For closing sheets."))
.foregroundStyle(Color.accentColor)
}
Button(
action: {
dismiss()
},
label: {
Text(NSLocalizedString("Close", comment: "For closing sheets."))
.foregroundStyle(Color.accentColor)
}
)
.buttonStyle(PlainButtonStyle())
.disabled(viewState != .idle)

Expand Down
6 changes: 5 additions & 1 deletion ENGAGEHF/Dashboard/Dashboard.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ struct Dashboard: View {
// Survey, if available
SurveySection(showFullSurvey: $showSurvey)
}
.listSectionSpacing(0)

.studyApplicationList()

.sheet(isPresented: $showSurvey, content: {
FullSurveyView()
Expand All @@ -48,4 +49,7 @@ struct Dashboard: View {

#Preview {
Dashboard(presentingAccount: .constant(false))
.previewWith(standard: ENGAGEHFStandard()) {
NotificationManager()
}
}
4 changes: 3 additions & 1 deletion ENGAGEHF/Dashboard/Notifications/Notification.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ import Foundation
struct Notification: Identifiable, Equatable {
var id: String

var type: String
var title: String
var description: String

init(title: String, description: String, id: String) {
init(type: String, title: String, description: String, id: String) {
self.id = id
self.type = type
self.title = title
self.description = description
}
Expand Down
14 changes: 8 additions & 6 deletions ENGAGEHF/Dashboard/Notifications/NotificationManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import SpeziFirebaseConfiguration


//
// A notification manager
// Notification manager
//
// Maintains a list of Notifications associated with the current user in firebase
//
Expand All @@ -35,15 +35,15 @@ class NotificationManager: Module, EnvironmentAccessible {
private let expirationDate = 10

var notifications: [Notification] = []
var isDeletingLastNotification = false


func configure() {
if ProcessInfo.processInfo.isPreviewSimulator {
let dummyNotification = Notification(
type: "Mock Notification",
title: "Weight Recorded",
description: "A weight measurement has been recorded.",
id: "test"
id: String(describing: UUID())
)
notifications.append(dummyNotification)
return
Expand Down Expand Up @@ -90,6 +90,7 @@ class NotificationManager: Module, EnvironmentAccessible {
self.notifications = documents.compactMap {
if $0.get("completed") == nil {
return Notification(
type: String(describing: $0["type"] ?? "Unknown"),
title: String(describing: $0["title"] ?? "Unknown"),
description: String(describing: $0["description"] ?? "Unknown"),
id: $0.documentID
Expand Down Expand Up @@ -147,9 +148,10 @@ extension NotificationManager {
// Function for adding a mock notification for the preview simulator
func addMock() {
let dummyNotification = Notification(
title: "Mock Notification",
description: "This is a mock notification.",
id: "mock"
type: "Medication Change",
title: "Your dose of XXX was changed.",
description: "Your dose of XXX was changed. You can review medication information in the Education Page.",
id: String(describing: UUID())
)
notifications.append(dummyNotification)
}
Expand Down
101 changes: 88 additions & 13 deletions ENGAGEHF/Dashboard/Notifications/Views/NotificationRow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,106 @@
// SPDX-License-Identifier: MIT
//

import SpeziViews
import SwiftUI


struct NotificationRow: View {
@Binding var notification: Notification
@State private var isExpanded = false


var body: some View {
DisclosureGroup(
content: {
VStack(alignment: .leading) {
HStack(alignment: .center) {
Text(notification.type)
.font(.subheadline)
.foregroundStyle(.secondary)
Spacer()
XButton(notification: $notification)
}
Divider()
Text(notification.title)
.multilineTextAlignment(.leading)
.padding(.bottom, 10)
if isExpanded {
Text(notification.description)
},
label: {
Text(notification.title)
.bold()
.multilineTextAlignment(.leading)
.font(.footnote)
} else {
LearnMoreButton(isExpanded: $isExpanded)
}
}
.onDisappear {
isExpanded = false
}
)
}


private struct XButton: View {
@Environment(NotificationManager.self) private var notificationManager
@Binding var notification: Notification


var body: some View {
AsyncButton(
action: {
let indx = notificationManager.notifications.firstIndex {$0.id == notification.id}

if let indx: Int {
await notificationManager.markComplete(at: IndexSet(integer: indx))
}
},
label: {
Image(systemName: "xmark")
.foregroundStyle(.accent)
.imageScale(.small)
}
)
}
}
}


#Preview {
let dummyNotification = Notification(
title: "Weight Recorded",
description: "A weight measurement has been recorded.",
id: "test"
)
return NotificationRow(notification: .constant(dummyNotification))
struct NotificationRowPreviewWrapper: View {
@Environment(NotificationManager.self) private var notificationManager


var body: some View {
@Bindable var notificationManager = notificationManager

List {
Section(
content: {
ForEach($notificationManager.notifications, id: \.id) { notification in
StudyApplicationListCard {
NotificationRow(notification: notification)
}
}
},
header: {
Text("Notifications")
.studyApplicationHeaderStyle()
}
)
.buttonStyle(.borderless)
Button(
action: {
notificationManager.addMock()
},
label: {
Text("Add Mock")
}
)
}
.studyApplicationList()
}
}


return NotificationRowPreviewWrapper()
.previewWith(standard: ENGAGEHFStandard()) {
NotificationManager()
}
}
88 changes: 42 additions & 46 deletions ENGAGEHF/Dashboard/Notifications/Views/NotificationSection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,69 +15,65 @@ import Spezi
import SpeziViews


struct NotificationSectionPreviewWrapper: View {
@Environment(NotificationManager.self) private var notificationManager

var body: some View {
List {
NotificationSection()
Button(
action: {
notificationManager.addMock()
},
label: {
Text("Add mock notification")
}
)

}
}
}


struct NotificationSection: View {
@Environment(NotificationManager.self) private var notificationManager


var body: some View {
@Bindable var notificationManager = notificationManager

Section("Notifications") {
if !notificationManager.notifications.isEmpty && !notificationManager.isDeletingLastNotification {
ForEach($notificationManager.notifications, id: \.id) { notification in
NotificationRow(notification: notification)
.transition(.opacity)
}
.onDelete { index in
Task {
if notificationManager.notifications.count == 1 {
withAnimation {
notificationManager.isDeletingLastNotification = true
}
Section(
content: {
if !notificationManager.notifications.isEmpty {
ForEach($notificationManager.notifications, id: \.id) { notification in
StudyApplicationListCard {
NotificationRow(notification: notification)
}
await notificationManager.markComplete(at: index)
}
}
} else {
Text("No new notifications")
.transition(.opacity)
.onChange(of: notificationManager.notifications) { oldNotifs, newNotifs in
if oldNotifs.isEmpty && !newNotifs.isEmpty {
withAnimation {
notificationManager.isDeletingLastNotification = false
}
.listRowSeparator(.hidden)
.buttonStyle(.borderless)
} else {
StudyApplicationListCard {
HStack {
Text("No new notifications")
Spacer()
}
}
}
},
header: {
Text("Notifications")
.studyApplicationHeaderStyle()
}
}
.headerProminence(.increased)
.animation(.easeInOut, value: notificationManager.notifications)
)
}
}


#Preview {
NotificationSectionPreviewWrapper()
struct NotificationSectionPreviewWrapper: View {
@Environment(NotificationManager.self) private var notificationManager

var body: some View {
List {
NotificationSection()
StudyApplicationListCard {
Button(
action: {
notificationManager.addMock()
},
label: {
Text("Add mock notification")
}
)
}
}
.studyApplicationList()
}
}


return NotificationSectionPreviewWrapper()
.previewWith(standard: ENGAGEHFStandard()) {
NotificationManager()
}
Expand Down
Loading

0 comments on commit a62e8fe

Please sign in to comment.