Files
FrenoCorp/Lendair/Models/Notification.swift
Michael Freno cb55ad95e2 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>
2026-05-03 20:16:05 -04:00

97 lines
2.4 KiB
Swift

import Foundation
import SwiftUI
// MARK: - Notification Item
struct NotificationItem: Identifiable, Equatable, Codable {
let id: String
let type: NotificationType
let title: String
let message: String
let createdAt: Date
var isRead: Bool
enum CodingKeys: String, CodingKey {
case id, type, title, message, createdAt, isRead
}
init(id: String, type: NotificationType, title: String, message: String, createdAt: Date, isRead: Bool) {
self.id = id
self.type = type
self.title = title
self.message = message
self.createdAt = createdAt
self.isRead = isRead
}
static func == (lhs: NotificationItem, rhs: NotificationItem) -> Bool {
lhs.id == rhs.id && lhs.isRead == rhs.isRead
}
}
// MARK: - Notification Type
enum NotificationType: String, CaseIterable, Codable {
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
}
}
}
// MARK: - List Parameters
struct NotificationListParams: Encodable {
var limit: Int
var offset: Int
init(limit: Int = 20, offset: Int = 0) {
self.limit = limit
self.offset = offset
}
}
// MARK: - API Response Types
struct NotificationListResponse: Decodable {
let notifications: [NotificationItem]
let hasMore: Bool
}
struct NotificationMarkAsReadResponse: Decodable {
let success: Bool
let notificationId: String
}
struct NotificationMarkAllReadResponse: Decodable {
let success: Bool
let markedCount: Int
}
struct NotificationUnreadCountResponse: Decodable {
let count: Int
}