Polish app theming and toolbar badge behavior

This commit is contained in:
2026-04-18 10:03:18 +02:00
parent b5cf3d9e01
commit 243029c798
3 changed files with 187 additions and 26 deletions
+69 -16
View File
@@ -19,7 +19,7 @@ private extension View {
func cleanTabBarOnIOS() -> some View {
#if os(iOS)
toolbarBackground(.visible, for: .tabBar)
.toolbarBackground(Color.white.opacity(0.98), for: .tabBar)
.toolbarBackground(AppTheme.chromeFill, for: .tabBar)
#else
self
#endif
@@ -28,6 +28,7 @@ private extension View {
struct HomeRootView: View {
@ObservedObject var model: AppViewModel
@State private var notificationBellFrame: CGRect?
var body: some View {
Group {
@@ -37,6 +38,16 @@ struct HomeRootView: View {
RegularHomeContainer(model: model)
}
}
.onPreferenceChange(NotificationBellFrameKey.self) { notificationBellFrame = $0 }
.overlay(alignment: .topLeading) {
if usesCompactNavigation {
NotificationBellBadgeOverlay(
unreadCount: model.unreadNotificationCount,
bellFrame: notificationBellFrame
)
.ignoresSafeArea()
}
}
.sheet(isPresented: $model.isNotificationCenterPresented) {
NotificationCenterSheet(model: model)
}
@@ -90,6 +101,7 @@ private struct RegularHomeContainer: View {
var body: some View {
NavigationSplitView {
Sidebar(model: model)
.navigationSplitViewColumnWidth(min: 240, ideal: 280, max: 320)
} detail: {
HomeSectionScreen(model: model, section: model.selectedSection, compactLayout: false)
.navigationTitle(model.selectedSection.title)
@@ -111,6 +123,39 @@ private struct DashboardToolbar: ToolbarContent {
}
}
private struct NotificationBellFrameKey: PreferenceKey {
static var defaultValue: CGRect? = nil
static func reduce(value: inout CGRect?, nextValue: () -> CGRect?) {
value = nextValue() ?? value
}
}
private struct NotificationBellBadgeOverlay: View {
let unreadCount: Int
let bellFrame: CGRect?
var body: some View {
GeometryReader { proxy in
if unreadCount > 0, let bellFrame {
let rootFrame = proxy.frame(in: .global)
Text("\(min(unreadCount, 9))")
.font(.caption2.weight(.bold))
.foregroundStyle(.white)
.frame(minWidth: 18, minHeight: 18)
.padding(.horizontal, 3)
.background(Color.orange, in: Capsule())
.position(
x: bellFrame.maxX - rootFrame.minX - 2,
y: bellFrame.minY - rootFrame.minY + 2
)
}
}
.allowsHitTesting(false)
}
}
private struct HomeSectionScreen: View {
@ObservedObject var model: AppViewModel
let section: AppSection
@@ -876,25 +921,33 @@ private struct NotificationBellButton: View {
Button {
model.isNotificationCenterPresented = true
} label: {
ZStack(alignment: .topTrailing) {
Image(systemName: model.unreadNotificationCount == 0 ? "bell" : "bell.badge.fill")
.font(.headline)
.foregroundStyle(model.unreadNotificationCount == 0 ? .primary : dashboardAccent)
if model.unreadNotificationCount > 0 {
Text("\(min(model.unreadNotificationCount, 9))")
.font(.caption2.weight(.bold))
.padding(.horizontal, 5)
.padding(.vertical, 2)
.background(Color.orange, in: Capsule())
.foregroundStyle(.white)
.offset(x: 10, y: -10)
Image(systemName: imageName)
.font(.headline)
.foregroundStyle(iconTone)
.frame(width: 28, height: 28, alignment: .center)
.background(alignment: .center) {
#if os(iOS)
GeometryReader { proxy in
Color.clear
.preference(key: NotificationBellFrameKey.self, value: proxy.frame(in: .global))
}
#endif
}
}
.frame(width: 28, height: 28)
}
.accessibilityLabel("Notifications")
}
private var imageName: String {
#if os(iOS)
model.unreadNotificationCount == 0 ? "bell" : "bell.fill"
#else
model.unreadNotificationCount == 0 ? "bell" : "bell.badge.fill"
#endif
}
private var iconTone: some ShapeStyle {
model.unreadNotificationCount == 0 ? Color.primary : dashboardAccent
}
}
private struct NotificationCenterSheet: View {