191 lines · 6.6 KB · Swift
RawDownload
import AppKitimport CodexBarCoreimport Foundation@preconcurrency import UserNotifications enum SessionQuotaTransition: Equatable {    case none    case depleted    case restored} struct QuotaWarningEvent: Equatable {    let window: QuotaWarningWindow    let threshold: Int    let currentRemaining: Double    let accountDisplayName: String?     init(        window: QuotaWarningWindow,        threshold: Int,        currentRemaining: Double,        accountDisplayName: String? = nil)    {        self.window = window        self.threshold = threshold        self.currentRemaining = currentRemaining        self.accountDisplayName = accountDisplayName    }} enum SessionQuotaNotificationLogic {    static let depletedThreshold: Double = 0.0001     static func isDepleted(_ remaining: Double?) -> Bool {        guard let remaining else { return false }        return remaining <= Self.depletedThreshold    }     static func transition(previousRemaining: Double?, currentRemaining: Double?) -> SessionQuotaTransition {        guard let currentRemaining else { return .none }        guard let previousRemaining else { return .none }         let wasDepleted = previousRemaining <= Self.depletedThreshold        let isDepleted = currentRemaining <= Self.depletedThreshold         if !wasDepleted, isDepleted { return .depleted }        if wasDepleted, !isDepleted { return .restored }        return .none    }     static func notificationCopy(        transition: SessionQuotaTransition,        providerName: String) -> (title: String, body: String)    {        switch transition {        case .none:            ("", "")        case .depleted:            (                L("session_depleted_notification_title", providerName),                L("session_depleted_notification_body"))        case .restored:            (                L("session_restored_notification_title", providerName),                L("session_restored_notification_body"))        }    }} enum QuotaWarningNotificationLogic {    static func notificationCopy(        providerName: String,        window: QuotaWarningWindow,        threshold: Int,        currentRemaining: Double,        accountDisplayName: String? = nil) -> (title: String, body: String)    {        let windowLabel = window.localizedNotificationDisplayName        let remainingText = Self.percentText(currentRemaining)        let title = L("quota_warning_notification_title", providerName, windowLabel)        let body = if let accountDisplayName {            L(                "quota_warning_notification_body_with_account",                accountDisplayName,                remainingText,                threshold,                windowLabel)        } else {            L(                "quota_warning_notification_body",                remainingText,                threshold,                windowLabel)        }        return (title, body)    }     static func crossedThreshold(        previousRemaining: Double?,        currentRemaining: Double,        thresholds: [Int],        alreadyFired: Set<Int>) -> Int?    {        let sanitized = QuotaWarningThresholds.active(thresholds)        let eligible = sanitized.filter { threshold in            currentRemaining <= Double(threshold) && !alreadyFired.contains(threshold)        }        guard !eligible.isEmpty else { return nil }         if let previousRemaining {            let crossed = eligible.filter { previousRemaining > Double($0) }            return crossed.min()        }         return eligible.min()    }     static func firedThresholdsAfterWarning(threshold: Int, thresholds: [Int]) -> Set<Int> {        Set(QuotaWarningThresholds.active(thresholds).filter { $0 >= threshold })    }     static func thresholdsToClear(currentRemaining: Double, alreadyFired: Set<Int>) -> Set<Int> {        Set(alreadyFired.filter { currentRemaining > Double($0) })    }     private static func percentText(_ value: Double) -> String {        "\(Int(min(100, max(0, value)).rounded()))%"    }} @MainActorprotocol SessionQuotaNotifying: AnyObject {    func post(transition: SessionQuotaTransition, provider: UsageProvider, badge: NSNumber?)    func postQuotaWarning(event: QuotaWarningEvent, provider: UsageProvider, soundEnabled: Bool)} @MainActorfinal class SessionQuotaNotifier: SessionQuotaNotifying {    private let logger = CodexBarLog.logger(LogCategories.sessionQuotaNotifications)     init() {}     func post(transition: SessionQuotaTransition, provider: UsageProvider, badge: NSNumber? = nil) {        guard transition != .none else { return }         let providerName = ProviderDescriptorRegistry.descriptor(for: provider).metadata.displayName         let (title, body) = SessionQuotaNotificationLogic.notificationCopy(            transition: transition,            providerName: providerName)         let providerText = provider.rawValue        let transitionText = String(describing: transition)        let idPrefix = "session-\(providerText)-\(transitionText)"        self.logger.info("enqueuing", metadata: ["prefix": idPrefix])        AppNotifications.shared.post(idPrefix: idPrefix, title: title, body: body, badge: badge)    }     func postQuotaWarning(event: QuotaWarningEvent, provider: UsageProvider, soundEnabled: Bool = true) {        let providerName = ProviderDescriptorRegistry.descriptor(for: provider).metadata.displayName        let threshold = event.threshold        let copy = QuotaWarningNotificationLogic.notificationCopy(            providerName: providerName,            window: event.window,            threshold: threshold,            currentRemaining: event.currentRemaining,            accountDisplayName: event.accountDisplayName)        let idPrefix = "quota-warning-\(provider.rawValue)-\(event.window.rawValue)-\(threshold)"        self.logger.info("enqueuing", metadata: ["prefix": idPrefix])        if soundEnabled {            (NSSound(named: "Glass") ?? NSSound(named: "Ping"))?.play()        }        NotificationCenter.default.post(            name: .codexbarQuotaWarningDidPost,            object: QuotaWarningPostedEvent(                provider: provider,                window: event.window,                threshold: threshold,                postedAt: Date()))        AppNotifications.shared.post(idPrefix: idPrefix, title: copy.title, body: copy.body, soundEnabled: false)    }} extension QuotaWarningWindow {    fileprivate var localizedNotificationDisplayName: String {        switch self {        case .session: L("quota_warning_session")        case .weekly: L("quota_warning_weekly")        }    }}