Skip to content

Commit be8c4a7

Browse files
committed
Improve notification handling and workspace panel behavior
- Add system notification action button that, when clicked, focuses CodeEdit, runs the action, and dismisses the corresponding CodeEdit notification. - Dismissing a CodeEdit notification now also dismisses its corresponding system notification, and vice versa. - The notification panel in a workspace now closes when clicking outside of it, behaving like other menus. - Refactored notification state management: Moved display-related state from `NotificationService` to a dedicated view model to ensure notification panels remain independent across workspaces.
1 parent 0eb5f36 commit be8c4a7

6 files changed

Lines changed: 480 additions & 21 deletions

File tree

CodeEdit/AppDelegate.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,21 @@ final class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
2626

2727
NSApp.closeWindow(.welcome, .about)
2828

29+
// Add test notification
30+
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
31+
NotificationManager.shared.post(
32+
iconText: "👋",
33+
iconTextColor: .white,
34+
iconColor: .indigo,
35+
title: "Welcome to CodeEdit",
36+
description: "This is a test notification to demonstrate the notification system.",
37+
actionButtonTitle: "Learn More...",
38+
action: {
39+
print("Action button clicked!")
40+
}
41+
)
42+
}
43+
2944
DispatchQueue.main.async {
3045
var needToHandleOpen = true
3146

CodeEdit/Features/Documents/WorkspaceDocument/WorkspaceDocument.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ final class WorkspaceDocument: NSDocument, ObservableObject, NSToolbarDelegate {
6161
deinit {
6262
cancellables.forEach { $0.cancel() }
6363
NotificationCenter.default.removeObserver(self)
64+
notificationOverlaySubscription?.cancel()
6465
}
6566

6667
func getFromWorkspaceState(_ key: WorkspaceStateKey) -> Any? {

CodeEdit/Features/InspectorArea/FileInspector/FileInspectorView.swift

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,71 @@ struct FileInspectorView: View {
5959
widthOptions
6060
wrapLinesToggle
6161
}
62+
Section("Test Notifications") {
63+
Button("Add Notification") {
64+
let (iconSymbol, iconColor) = randomSymbolAndColor()
65+
NotificationManager.shared.post(
66+
iconSymbol: iconSymbol,
67+
iconColor: iconColor,
68+
title: "Test Notification",
69+
description: "This is a test notification.",
70+
actionButtonTitle: "Action",
71+
action: {
72+
print("Test notification action triggered")
73+
}
74+
)
75+
}
76+
Button("Add Sticky Notification") {
77+
NotificationManager.shared.post(
78+
iconSymbol: "pin.fill",
79+
iconColor: .orange,
80+
title: "Sticky Notification",
81+
description: "This notification will stay until dismissed.",
82+
actionButtonTitle: "Acknowledge",
83+
action: {
84+
print("Sticky notification acknowledged")
85+
},
86+
isSticky: true
87+
)
88+
}
89+
Button("Add Image Notification") {
90+
NotificationManager.shared.post(
91+
iconImage: randomImage(),
92+
title: "Test Notification with Image",
93+
description: "This is a test notification with a custom image.",
94+
actionButtonTitle: "Action",
95+
action: {
96+
print("Test notification action triggered")
97+
}
98+
)
99+
}
100+
Button("Add Text Notification") {
101+
NotificationManager.shared.post(
102+
iconText: randomLetter(),
103+
iconTextColor: .white,
104+
iconColor: randomColor(),
105+
title: "Text Notification",
106+
description: "This is a test notification with text.",
107+
actionButtonTitle: "Acknowledge",
108+
action: {
109+
print("Test notification action triggered")
110+
}
111+
)
112+
}
113+
Button("Add Emoji Notification") {
114+
NotificationManager.shared.post(
115+
iconText: randomEmoji(),
116+
iconTextColor: .white,
117+
iconColor: randomColor(),
118+
title: "Emoji Notification",
119+
description: "This is a test notification with an emoji.",
120+
actionButtonTitle: "Acknowledge",
121+
action: {
122+
print("Test notification action triggered")
123+
}
124+
)
125+
}
126+
}
62127
}
63128
} else {
64129
NoSelectionInspectorView()
@@ -81,6 +146,62 @@ struct FileInspectorView: View {
81146
}
82147
}
83148

149+
func randomColor() -> Color {
150+
let colors: [Color] = [
151+
.red, .orange, .yellow, .green, .mint, .cyan,
152+
.teal, .blue, .indigo, .purple, .pink, .gray
153+
]
154+
return colors.randomElement() ?? .black
155+
}
156+
157+
func randomSymbolAndColor() -> (String, Color) {
158+
let symbols: [(String, Color)] = [
159+
("bell.fill", .red),
160+
("bell.badge.fill", .red),
161+
("exclamationmark.triangle.fill", .orange),
162+
("info.circle.fill", .blue),
163+
("checkmark.seal.fill", .green),
164+
("xmark.octagon.fill", .red),
165+
("bubble.left.fill", .teal),
166+
("envelope.fill", .blue),
167+
("phone.fill", .green),
168+
("megaphone.fill", .pink),
169+
("clock.fill", .gray),
170+
("calendar", .red),
171+
("flag.fill", .green),
172+
("bookmark.fill", .orange),
173+
("bolt.fill", .indigo),
174+
("shield.lefthalf.fill", .red),
175+
("gift.fill", .purple),
176+
("heart.fill", .pink),
177+
("star.fill", .orange),
178+
("curlybraces", .cyan),
179+
]
180+
return symbols.randomElement() ?? ("bell.fill", .red)
181+
}
182+
183+
func randomEmoji() -> String {
184+
let emoji: [String] = [
185+
"🔔", "🚨", "⚠️", "👋", "😍", "😎", "😘", "😜", "😝", "😀", "😁",
186+
"😂", "🤣", "😃", "😄", "😅", "😆", "😇", "😉", "😊", "😋", "😌"
187+
]
188+
return emoji.randomElement() ?? "🔔"
189+
}
190+
191+
func randomImage() -> Image {
192+
let images: [Image] = [
193+
Image("GitHubIcon"),
194+
Image("BitBucketIcon"),
195+
Image("GitLabIcon")
196+
]
197+
return images.randomElement() ?? Image("GitHubIcon")
198+
}
199+
200+
func randomLetter() -> String {
201+
let letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".map { String($0) }
202+
return letters.randomElement() ?? "A"
203+
}
204+
84205
@ViewBuilder private var fileNameField: some View {
85206
@State var isValid: Bool = true
86207

CodeEdit/Features/Notifications/NotificationManager.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ final class NotificationManager: NSObject, ObservableObject {
5656
notifications.filter { !$0.isRead }.count
5757
}
5858

59+
/// Whether there are currently notifications being displayed in the overlay
60+
var hasActiveNotification: Bool {
61+
!notifications.isEmpty
62+
}
63+
5964
/// Posts a new notification
6065
/// - Parameters:
6166
/// - iconSymbol: SF Symbol or CodeEditSymbol name for the notification icon

0 commit comments

Comments
 (0)