Fix high CPU usage from notifications and add regression tests
- Fix auto-updating Text(date, style: .time) causing continuous SwiftUI updates by using static formatting with .formatted(date:time:) - Fix notification popover keeping SwiftUI observers active when closed by clearing contentViewController on popover close and recreating on open - Fix focus loss when notifications arrive while typing by only setting focus in NotificationsPage when the page is visible - Make Update Pill and Update Logs debug-only features - Add CPU regression tests: test_cpu_usage.py, test_cpu_notifications.py - Add lint test for auto-updating Text patterns: test_lint_swiftui_patterns.py
This commit is contained in:
parent
5e6aad94c4
commit
eb7c06ceb1
7 changed files with 628 additions and 14 deletions
|
|
@ -20,6 +20,7 @@ final class UpdateLogStore {
|
|||
}
|
||||
|
||||
func append(_ message: String) {
|
||||
#if DEBUG
|
||||
let timestamp = formatter.string(from: Date())
|
||||
let line = "[\(timestamp)] \(message)"
|
||||
queue.async { [weak self] in
|
||||
|
|
@ -30,6 +31,7 @@ final class UpdateLogStore {
|
|||
}
|
||||
appendToFile(line: line)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
func snapshot() -> String {
|
||||
|
|
|
|||
|
|
@ -79,8 +79,12 @@ private struct TitlebarAccessoryView: View {
|
|||
@ObservedObject var model: UpdateViewModel
|
||||
|
||||
var body: some View {
|
||||
#if DEBUG
|
||||
UpdatePill(model: model)
|
||||
.padding(.trailing, 8)
|
||||
.padding(.trailing, 8)
|
||||
#else
|
||||
EmptyView()
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -208,9 +212,7 @@ private struct NotificationsAnchorView: NSViewRepresentable {
|
|||
}
|
||||
|
||||
func updateNSView(_ nsView: NSView, context: Context) {
|
||||
DispatchQueue.main.async {
|
||||
onResolve(nsView)
|
||||
}
|
||||
// Only need to resolve once in makeNSView - the view reference doesn't change
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -417,6 +419,15 @@ final class TitlebarControlsAccessoryViewController: NSTitlebarAccessoryViewCont
|
|||
notificationsPopover.performClose(nil)
|
||||
return
|
||||
}
|
||||
// Recreate content view each time to avoid stale observers when popover is hidden
|
||||
notificationsPopover.contentViewController = NSHostingController(
|
||||
rootView: NotificationsPopoverView(
|
||||
notificationStore: notificationStore,
|
||||
onDismiss: { [weak notificationsPopover] in
|
||||
notificationsPopover?.performClose(nil)
|
||||
}
|
||||
)
|
||||
)
|
||||
let anchorView = viewModel.notificationsAnchorView ?? hostingView
|
||||
notificationsPopover.animates = animated
|
||||
notificationsPopover.show(relativeTo: anchorView.bounds, of: anchorView, preferredEdge: .maxY)
|
||||
|
|
@ -427,16 +438,16 @@ final class TitlebarControlsAccessoryViewController: NSTitlebarAccessoryViewCont
|
|||
popover.behavior = .semitransient
|
||||
popover.animates = true
|
||||
popover.delegate = self
|
||||
popover.contentViewController = NSHostingController(
|
||||
rootView: NotificationsPopoverView(
|
||||
notificationStore: notificationStore,
|
||||
onDismiss: { [weak popover] in
|
||||
popover?.performClose(nil)
|
||||
}
|
||||
)
|
||||
)
|
||||
// Content view controller is set dynamically in toggleNotificationsPopover
|
||||
return popover
|
||||
}
|
||||
|
||||
// MARK: - NSPopoverDelegate
|
||||
|
||||
func popoverDidClose(_ notification: Notification) {
|
||||
// Clear the content view controller to stop SwiftUI observers when popover is hidden
|
||||
notificationsPopover.contentViewController = nil
|
||||
}
|
||||
}
|
||||
|
||||
private struct NotificationsPopoverView: View {
|
||||
|
|
@ -557,7 +568,7 @@ private struct NotificationPopoverRow: View {
|
|||
.font(.headline)
|
||||
.foregroundColor(.primary)
|
||||
Spacer()
|
||||
Text(notification.createdAt, style: .time)
|
||||
Text(notification.createdAt.formatted(date: .omitted, time: .shortened))
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue