Add notification badge count and MainTabView with notification tab FRE-4740 FRE-4739

- Add getUnreadCount() endpoint to NotificationsServiceProtocol
- Add NotificationUnreadCountResponse model
- Add badgeCount and fetchUnreadCount() to NotificationsViewModel
- Update markAsRead/markAllAsRead to decrement badge count
- Create MainTabView with Home, Challenges, Clubs, Notifications tabs
- Add unread badge on notification tab using .badge() modifier
- Support injected ViewModel in NotificationsView for shared state
- Add badge count tests to NotificationServiceTests
- Fetch unread count on app launch and tab switch

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
2026-05-03 20:16:05 -04:00
parent 88d57a3389
commit cb55ad95e2
9 changed files with 279 additions and 1 deletions

View File

@@ -5,6 +5,7 @@ import SwiftUI
class NotificationsViewModel: ObservableObject {
@Published var notifications: [NotificationItem] = []
@Published var isLoading: Bool = false
@Published var badgeCount: Int = 0
@Published var lastRefreshDate: Date?
@Published var error: NotificationError?
@@ -25,6 +26,7 @@ class NotificationsViewModel: ObservableObject {
do {
let fetchedNotifications = try await notificationsService.list()
notifications = fetchedNotifications.sorted { $0.createdAt > $1.createdAt }
badgeCount = notifications.filter { !$0.isRead }.count
} catch let error as NotificationError {
self.error = error
} catch {
@@ -36,12 +38,22 @@ class NotificationsViewModel: ObservableObject {
await fetchNotifications()
}
func fetchUnreadCount() async {
do {
let count = try await notificationsService.getUnreadCount()
badgeCount = count
} catch {
print("Failed to fetch unread count: \(error)")
}
}
func markAsRead(id: String) async {
guard let index = notifications.firstIndex(where: { $0.id == id }) else { return }
do {
try await notificationsService.markAsRead(id: id)
notifications[index].isRead = true
badgeCount = max(0, badgeCount - 1)
objectWillChange.send()
} catch {
print("Failed to mark notification as read: \(error)")
@@ -57,6 +69,7 @@ class NotificationsViewModel: ObservableObject {
for index in notifications.indices {
notifications[index].isRead = true
}
badgeCount = 0
objectWillChange.send()
} catch {
print("Failed to mark all as read: \(error)")