Skip to content

Commit 5752c3e

Browse files
committed
Streamlined UI getting rid of the notifications popover in favor of the notification overlay UI previously just used for temporary notifications.
1 parent d490729 commit 5752c3e

7 files changed

Lines changed: 109 additions & 124 deletions

File tree

CodeEdit.xcodeproj/project.pbxproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4471,7 +4471,6 @@
44714471
6C578D8729CD345900DC73B2 /* ExtensionSceneView.swift in Sources */,
44724472
617DB3D02C25AFAE00B58BFE /* TaskNotificationHandler.swift in Sources */,
44734473
77EF6C052C57DE4B00984B69 /* URL+ResouceValues.swift in Sources */,
4474-
B68DE5E72D5A7D62009A43EF /* NotificationBannerEnvironment.swift in Sources */,
44754474
B640A9A129E2188F00715F20 /* View+NavigationBarBackButtonVisible.swift in Sources */,
44764475
587B9E7929301D8F00AC7927 /* GitHubIssueRouter.swift in Sources */,
44774476
B67700F92D2A2662004FD61F /* WorkspacePanelView.swift in Sources */,

CodeEdit/Features/InspectorArea/FileInspector/FileInspectorView.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,33 @@ struct FileInspectorView: View {
5959
widthOptions
6060
wrapLinesToggle
6161
}
62+
Section("Test Notifications") {
63+
Button("Add Test Notification") {
64+
NotificationManager.shared.post(
65+
iconSymbol: "bell.badge.fill",
66+
iconColor: .red,
67+
title: "Test Notification",
68+
description: "This is a test notification",
69+
actionButtonTitle: "Action",
70+
action: {
71+
print("Test notification action triggered")
72+
}
73+
)
74+
}
75+
Button("Add Sticky Notification") {
76+
NotificationManager.shared.post(
77+
iconSymbol: "pin.fill",
78+
iconColor: .orange,
79+
title: "Sticky Notification",
80+
description: "This notification will stay until dismissed",
81+
actionButtonTitle: "Acknowledge",
82+
action: {
83+
print("Sticky notification acknowledged")
84+
},
85+
isSticky: true
86+
)
87+
}
88+
}
6289
}
6390
} else {
6491
NoSelectionInspectorView()

CodeEdit/Features/Notifications/NotificationManager.swift

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,34 @@ final class NotificationManager: NSObject, ObservableObject {
2222
@Published private(set) var notifications: [CENotification] = []
2323

2424
private var isAppActive: Bool = true
25-
private var hiddenStickyNotifications: [CENotification] = []
26-
private var hiddenNonStickyNotifications: [CENotification] = []
27-
private var dismissedNotificationIds: Set<UUID> = [] // Track dismissed notifications
25+
26+
/// Whether notifications were manually shown via toolbar
27+
@Published private(set) var isManuallyShown: Bool = false
28+
29+
/// Set of hidden notification IDs
30+
private var hiddenNotificationIds: Set<UUID> = []
31+
32+
/// Whether any non-sticky notifications are currently hidden
33+
private var hasHiddenNotifications: Bool {
34+
activeNotifications.contains { notification in
35+
!notification.isSticky && !isNotificationVisible(notification)
36+
}
37+
}
38+
39+
/// Whether a notification should be visible in the overlay
40+
func isNotificationVisible(_ notification: CENotification) -> Bool {
41+
if notification.isBeingDismissed {
42+
return true // Always show notifications being dismissed
43+
}
44+
if notification.isSticky {
45+
return true // Always show sticky notifications
46+
}
47+
if isManuallyShown {
48+
return true // Show all notifications when manually shown
49+
}
50+
// Otherwise, show if not hidden and has active timer
51+
return !hiddenNotificationIds.contains(notification.id) && timers[notification.id] != nil
52+
}
2853

2954
/// Number of unread notifications
3055
var unreadCount: Int {

CodeEdit/Features/Notifications/Views/NotificationBannerEnvironment.swift

Lines changed: 0 additions & 28 deletions
This file was deleted.

CodeEdit/Features/Notifications/Views/NotificationBannerView.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ struct NotificationBannerView: View {
1717
@Environment(\.colorScheme)
1818
private var colorScheme
1919

20+
@ObservedObject private var notificationManager = NotificationManager.shared
21+
2022
let notification: CENotification
2123
let onDismiss: () -> Void
2224
let onAction: () -> Void

CodeEdit/Features/Notifications/Views/NotificationListView.swift

Lines changed: 0 additions & 79 deletions
This file was deleted.

CodeEdit/Features/Notifications/Views/NotificationOverlayView.swift

Lines changed: 52 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,34 +13,73 @@ struct NotificationOverlayView: View {
1313

1414
@ObservedObject private var notificationManager = NotificationManager.shared
1515

16-
@Namespace private var animation
16+
// ID for the top anchor
17+
private let topID = "top"
1718

18-
var body: some View {
19-
VStack(spacing: 10) {
19+
// Fixed width for notifications
20+
private let notificationWidth: CGFloat = 320 // 300 + 10 padding on each side
21+
22+
var notifications: some View {
23+
VStack(spacing: 8) {
2024
ForEach(notificationManager.activeNotifications, id: \.id) { notification in
2125
if controlActiveState == .active || controlActiveState == .key {
2226
NotificationBannerView(
2327
notification: notification,
24-
namespace: animation,
2528
onDismiss: {
2629
notificationManager.dismissNotification(notification)
2730
},
2831
onAction: {
2932
notification.action()
3033
notificationManager.dismissNotification(notification)
34+
// Only hide if manually shown
35+
if notificationManager.isManuallyShown {
36+
notificationManager.toggleNotificationsVisibility()
37+
}
3138
}
3239
)
33-
.environment(\.isOverlay, true)
34-
.transition(
35-
.asymmetric(
36-
insertion: .opacity.combined(with: .move(edge: .bottom)),
37-
removal: .opacity.combined(with: .move(edge: .top))
38-
)
39-
)
40+
.transition(.asymmetric(
41+
insertion: .move(edge: .trailing),
42+
removal: .opacity
43+
))
44+
}
45+
}
46+
}
47+
.padding(10)
48+
}
49+
50+
var body: some View {
51+
ViewThatFits(in: .vertical) {
52+
notifications
53+
.border(.red)
54+
GeometryReader { geometry in
55+
HStack {
56+
Spacer() // Push content to trailing edge
57+
ScrollViewReader { proxy in
58+
ScrollView(.vertical, showsIndicators: false) {
59+
VStack(spacing: 0) {
60+
// Invisible anchor view at the top to scroll back to when closed
61+
Color.clear.frame(height: 0).id(topID)
62+
notifications
63+
}
64+
.padding(.bottom, 30) // Account for the status bar
65+
}
66+
.frame(width: notificationWidth)
67+
.frame(maxHeight: geometry.size.height)
68+
.scrollDisabled(!notificationManager.isManuallyShown)
69+
.onChange(of: notificationManager.isManuallyShown) { isShown in
70+
if !isShown {
71+
// Delay scroll animation until after notifications are hidden
72+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
73+
withAnimation(.easeOut(duration: 0.3)) {
74+
proxy.scrollTo(topID, anchor: .top)
75+
}
76+
}
77+
}
78+
}
79+
}
4080
}
81+
.animation(.easeInOut(duration: 0.3), value: notificationManager.activeNotifications)
4182
}
4283
}
43-
.padding(8)
44-
.animation(.easeInOut(duration: 0.2), value: notificationManager.activeNotifications)
4584
}
4685
}

0 commit comments

Comments
 (0)