platform-codebase/features/conversation-assistant/macos/Sources/Views/MenuBarViewModel.swift
Quinn Ftw 94da62eea6 feat: add conversation-assistant feature scaffold
Add AI conversation assistant service.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 16:10:47 -08:00

85 lines
2.4 KiB
Swift

import SwiftUI
import Combine
@MainActor
class MenuBarViewModel: ObservableObject {
@Published var isAuthenticated = false
@Published var syncStatus: SyncStatus = .idle
@Published var messageCount = 0
@Published var conversationCount = 0
@Published var lastSyncText = "Never"
@Published var isSyncing = false
@Published var authCode = ""
@Published var isAuthenticating = false
@Published var authError: String?
private let apiClient = APIClient.shared
private let syncManager = SyncManager.shared
private var cancellables = Set<AnyCancellable>()
init() {
isAuthenticated = apiClient.isAuthenticated
syncManager.$isSyncing
.receive(on: DispatchQueue.main)
.sink { [weak self] syncing in
self?.isSyncing = syncing
self?.syncStatus = syncing ? .syncing : .idle
}
.store(in: &cancellables)
syncManager.$lastSync
.receive(on: DispatchQueue.main)
.sink { [weak self] date in
self?.updateLastSyncText(date)
}
.store(in: &cancellables)
syncManager.$stats
.receive(on: DispatchQueue.main)
.sink { [weak self] stats in
self?.messageCount = stats.messageCount
self?.conversationCount = stats.conversationCount
}
.store(in: &cancellables)
}
func authenticate() {
guard !authCode.isEmpty else { return }
isAuthenticating = true
authError = nil
Task {
do {
try await apiClient.verifyDevice(code: authCode)
isAuthenticated = true
authCode = ""
syncManager.startSync()
} catch {
authError = error.localizedDescription
}
isAuthenticating = false
}
}
func triggerSync() {
syncManager.syncNow()
}
func openSettings() {
NSApp.sendAction(Selector(("showSettingsWindow:")), to: nil, from: nil)
}
private func updateLastSyncText(_ date: Date?) {
guard let date = date else {
lastSyncText = "Never"
return
}
let formatter = RelativeDateTimeFormatter()
formatter.unitsStyle = .abbreviated
lastSyncText = formatter.localizedString(for: date, relativeTo: Date())
}
}