import Foundation import SwiftUI @MainActor class NotificationsViewModel: ObservableObject { @Published var notifications: [NotificationItem] = [] @Published var isLoading: Bool = false @Published var lastRefreshDate: Date? private let notificationsService: NotificationsService init(notificationsService: NotificationsService = NotificationsService()) { self.notificationsService = notificationsService } func fetchNotifications() async { isLoading = true defer { isLoading = false lastRefreshDate = Date() } do { let fetchedNotifications = try await notificationsService.list() notifications = fetchedNotifications.sorted { $0.createdAt > $1.createdAt } } catch { print("Failed to fetch notifications: \(error)") } } func refresh() async { await fetchNotifications() } 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 } catch { print("Failed to mark notification as read: \(error)") } } func markAllAsRead() async { let unreadIds = notifications.filter { !$0.isRead }.map { $0.id } guard !unreadIds.isEmpty else { return } do { try await notificationsService.markAllAsRead() for index in notifications.indices { notifications[index].isRead = true } } catch { print("Failed to mark all as read: \(error)") } } var unreadCount: Int { notifications.filter { !$0.isRead }.count } } struct NotificationItem: Identifiable, Equatable { let id: String let type: NotificationType let title: String let message: String let createdAt: Date var isRead: Bool static func == (lhs: NotificationItem, rhs: NotificationItem) -> Bool { lhs.id == rhs.id && lhs.isRead == rhs.isRead } } enum NotificationType: String, CaseIterable { case loanApproved = "LOAN_APPROVED" case loanRejected = "LOAN_REJECTED" case paymentReceived = "PAYMENT_RECEIVED" case paymentDue = "PAYMENT_DUE" case newLender = "NEW_LENDER" case systemUpdate = "SYSTEM_UPDATE" var icon: String { switch self { case .loanApproved: return "checkmark.circle.fill" case .loanRejected: return "xmark.circle.fill" case .paymentReceived: return "arrow.down.circle.fill" case .paymentDue: return "exclamationmark.circle.fill" case .newLender: return "person.circle.fill" case .systemUpdate: return "info.circle.fill" } } var color: Color { switch self { case .loanApproved: return .green case .loanRejected: return .red case .paymentReceived: return .green case .paymentDue: return .orange case .newLender: return .blue case .systemUpdate: return .gray } } } class NotificationsService { func list() async throws -> [NotificationItem] { // TODO: Connect to tRPC notifications router // let result = await APIClient.shared.query("notifications.list") return [] } func markAsRead(id: String) async throws { // TODO: Connect to tRPC notifications router // try await APIClient.shared.mutation("notifications.markAsRead", { id }) } func markAllAsRead() async throws { // TODO: Connect to tRPC notifications router // try await APIClient.shared.mutation("notifications.markAllAsRead", {}) } }