Repository navigation
Expand file tree
/
Copy pathAppModel.swift
More file actions
137 lines (124 loc) · 4.92 KB
/
Copy pathAppModel.swift
File metadata and controls
137 lines (124 loc) · 4.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
import Combine
import Foundation
import SwiftUI
@MainActor
final class AppModel: ObservableObject {
static let shared = AppModel()
@Published var states: [ProviderID: ProviderLoadState] = [:]
@Published var selected: ProviderID?
@Published var lastSelected: ProviderID?
@Published var panelVisible = true
@Published var lastRefresh: Date?
@Published var refreshing = false
let settings = SettingsStore.shared
private var timer: Timer?
var visibleProviders: [ProviderID] {
ProviderID.allCases.filter { id in
guard settings.isEnabled(id) else { return false }
if settings.showUnsigned { return true }
if lastRefresh == nil {
switch states[id] {
case .signedOut, .missingKey, .error: return false
default: return true
}
}
return states[id]?.snapshot != nil
}
}
var worstReady: QuotaSnapshot? {
visibleProviders
.compactMap { states[$0]?.snapshot }
.max(by: { $0.primaryPercent < $1.primaryPercent })
}
func start() {
for id in ProviderID.allCases {
if states[id] == nil { states[id] = .idle }
}
Task { await refresh() }
restartTimer()
}
func restartTimer() {
timer?.invalidate()
timer = Timer.scheduledTimer(withTimeInterval: settings.refreshSeconds, repeats: true) { [weak self] _ in
Task { @MainActor in
await self?.refresh()
}
}
RunLoop.main.add(timer!, forMode: .common)
}
func refresh() async {
refreshing = true
settings.persistKeys()
var service = UsageService()
service.openRouterKey = settings.openRouterKey
service.deepSeekKey = settings.deepSeekKey
service.zaiKey = settings.zaiKey
let snapshot = service
let enabledIDs = ProviderID.allCases.filter { settings.isEnabled($0) }
await withTaskGroup(of: (ProviderID, ProviderLoadState).self) { group in
for id in enabledIDs {
states[id] = .loading
group.addTask {
let result = await snapshot.fetch(id)
return (id, result)
}
}
for await (id, state) in group {
states[id] = state
}
}
lastRefresh = Date()
refreshing = false
for state in states.values {
if let snapshot = state.snapshot {
settings.noteUsage(snapshot)
}
}
notifyThresholds()
NotificationCenter.default.post(name: .checkUsageDidRefresh, object: nil)
}
private func notifyThresholds() {
guard settings.usageAlerts else { return }
for id in visibleProviders {
guard let snapshot = states[id]?.snapshot else { continue }
let percent = snapshot.primaryPercent
let window = snapshot.primary
let cycleEnd = window?.resetsAt ?? snapshot.cycleEnd
settings.clearAlerts(ifPercentBelow: percent, provider: id, cycleEnd: cycleEnd)
if percent >= 90, !settings.wasAlerted(id, level: 90, cycleEnd: cycleEnd) {
settings.markAlerted(id, level: 90, cycleEnd: cycleEnd)
UsageAlerts.post(provider: id, window: window?.title, percent: percent, reset: cycleEnd)
} else if percent >= 70, !settings.wasAlerted(id, level: 70, cycleEnd: cycleEnd) {
settings.markAlerted(id, level: 70, cycleEnd: cycleEnd)
UsageAlerts.post(provider: id, window: window?.title, percent: percent, reset: cycleEnd)
}
}
}
func select(_ id: ProviderID?) {
selected = id
if let id { lastSelected = id }
NotificationCenter.default.post(name: .checkUsageSelectionChanged, object: id)
}
func togglePanel() {
if !settings.widgetEnabled {
settings.widgetEnabled = true
panelVisible = true
} else {
panelVisible.toggle()
}
NotificationCenter.default.post(name: .checkUsagePanelVisibility, object: panelVisible && settings.widgetEnabled)
}
func showPanel() {
settings.widgetEnabled = true
panelVisible = true
NotificationCenter.default.post(name: .checkUsagePanelVisibility, object: true)
}
}
extension Notification.Name {
static let checkUsageDidRefresh = Notification.Name("checkUsageDidRefresh")
static let checkUsageSelectionChanged = Notification.Name("checkUsageSelectionChanged")
static let checkUsagePanelVisibility = Notification.Name("checkUsagePanelVisibility")
static let checkUsageOpenSettings = Notification.Name("checkUsageOpenSettings")
static let checkUsageLayoutChanged = Notification.Name("checkUsageLayoutChanged")
static let checkUsageHotkeysChanged = Notification.Name("checkUsageHotkeysChanged")
}