Added a lot of functionality. Bugfixes and changes
This commit is contained in:
169
oAI/Views/Main/SyncStatusIndicator.swift
Normal file
169
oAI/Views/Main/SyncStatusIndicator.swift
Normal file
@@ -0,0 +1,169 @@
|
||||
//
|
||||
// SyncStatusIndicator.swift
|
||||
// oAI
|
||||
//
|
||||
// Git sync status indicator (bottom-right corner)
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
enum SyncState {
|
||||
case disabled // Gray - sync not configured or disabled
|
||||
case synced // Green - successfully synced
|
||||
case syncing // Yellow - sync in progress
|
||||
case error(String) // Red - sync failed with error message
|
||||
|
||||
var color: Color {
|
||||
switch self {
|
||||
case .disabled: return .secondary
|
||||
case .synced: return .green
|
||||
case .syncing: return .orange
|
||||
case .error: return .red
|
||||
}
|
||||
}
|
||||
|
||||
var icon: String {
|
||||
switch self {
|
||||
case .disabled: return "arrow.triangle.2.circlepath"
|
||||
case .synced: return "checkmark.circle.fill"
|
||||
case .syncing: return "arrow.triangle.2.circlepath"
|
||||
case .error: return "exclamationmark.triangle.fill"
|
||||
}
|
||||
}
|
||||
|
||||
var tooltipText: String {
|
||||
switch self {
|
||||
case .disabled:
|
||||
return "Auto-sync disabled"
|
||||
case .synced:
|
||||
if let lastSync = GitSyncService.shared.syncStatus.lastSyncTime {
|
||||
return "Last synced: \(timeAgo(lastSync))"
|
||||
} else {
|
||||
return "Synced"
|
||||
}
|
||||
case .syncing:
|
||||
return "Syncing..."
|
||||
case .error(let message):
|
||||
return "Sync failed: \(message)"
|
||||
}
|
||||
}
|
||||
|
||||
private func timeAgo(_ date: Date) -> String {
|
||||
let seconds = Int(Date().timeIntervalSince(date))
|
||||
if seconds < 60 {
|
||||
return "just now"
|
||||
} else if seconds < 3600 {
|
||||
let minutes = seconds / 60
|
||||
return "\(minutes)m ago"
|
||||
} else if seconds < 86400 {
|
||||
let hours = seconds / 3600
|
||||
return "\(hours)h ago"
|
||||
} else {
|
||||
let days = seconds / 86400
|
||||
return "\(days)d ago"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SyncStatusIndicator: View {
|
||||
@State private var isHovering = false
|
||||
@State private var syncState: SyncState = .disabled
|
||||
@State private var showSettings = false
|
||||
|
||||
private let settings = SettingsService.shared
|
||||
private let gitSync = GitSyncService.shared
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
Spacer()
|
||||
HStack {
|
||||
Spacer()
|
||||
|
||||
// Floating indicator
|
||||
ZStack {
|
||||
// Background circle
|
||||
Circle()
|
||||
.fill(Color(nsColor: .windowBackgroundColor))
|
||||
.shadow(color: .black.opacity(0.2), radius: 4, x: 0, y: 2)
|
||||
.frame(width: 40, height: 40)
|
||||
|
||||
// Icon
|
||||
statusIcon
|
||||
}
|
||||
.scaleEffect(isHovering ? 1.1 : 1.0)
|
||||
.animation(.spring(response: 0.3), value: isHovering)
|
||||
.onHover { hovering in
|
||||
isHovering = hovering
|
||||
}
|
||||
.help(syncState.tooltipText)
|
||||
.onTapGesture {
|
||||
if case .error = syncState {
|
||||
// Open settings on error
|
||||
showSettings = true
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showSettings) {
|
||||
SettingsView()
|
||||
}
|
||||
.padding(.trailing, 16)
|
||||
.padding(.bottom, 16)
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
updateState()
|
||||
}
|
||||
.onChange(of: gitSync.syncStatus) {
|
||||
updateState()
|
||||
}
|
||||
.onChange(of: gitSync.isSyncing) {
|
||||
updateState()
|
||||
}
|
||||
.onChange(of: gitSync.lastSyncError) {
|
||||
updateState()
|
||||
}
|
||||
.onChange(of: settings.syncEnabled) {
|
||||
updateState()
|
||||
}
|
||||
.onChange(of: settings.syncAutoSave) {
|
||||
updateState()
|
||||
}
|
||||
}
|
||||
|
||||
private var statusIcon: some View {
|
||||
Image(systemName: syncState.icon)
|
||||
.font(.system(size: 18))
|
||||
.foregroundStyle(syncState.color)
|
||||
}
|
||||
|
||||
private func updateState() {
|
||||
// Determine current sync state
|
||||
guard settings.syncEnabled && settings.syncConfigured else {
|
||||
syncState = .disabled
|
||||
return
|
||||
}
|
||||
|
||||
guard gitSync.syncStatus.isCloned else {
|
||||
syncState = .disabled
|
||||
return
|
||||
}
|
||||
|
||||
// Check for error
|
||||
if let error = gitSync.lastSyncError {
|
||||
syncState = .error(error)
|
||||
return
|
||||
}
|
||||
|
||||
// Check if currently syncing
|
||||
if gitSync.isSyncing {
|
||||
syncState = .syncing
|
||||
return
|
||||
}
|
||||
|
||||
// All good
|
||||
syncState = .synced
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
SyncStatusIndicator()
|
||||
}
|
||||
Reference in New Issue
Block a user