Files
ios-client/NetBirdWidgetExtension/WidgetVPNStatus.swift
evgeniyChepelev ca8cf59d55 Feature/widget toggle switch UI (#138)
* feat(widget): replace action button with toggle switch control

All connection states now render as a unified toggle-style control:
green (connected), red (disconnected), orange with spinner (transitioning).

* fix(widget+auth): toggle UI, VPN state sync, and login flow fixes

* fix(widget+sdk): update Go SDK to fix/ios-debug-bundle, fix Control Center transitioning state and app disconnect flash

* fix(widget): remove transitioning states, update toggle colors and UI

* Change color indicator

* fix(widget+auth): toggle UI, VPN state sync, and login flow fixes

* fix framework version
2026-06-15 13:32:57 +02:00

76 lines
2.3 KiB
Swift

import SwiftUI
import NetworkExtension
import WidgetKit
extension Color {
// NetBird brand orange (#F68330) — matches the app's AccentColor asset.
static let netbirdOrange = Color(red: 0.965, green: 0.514, blue: 0.188)
}
enum WidgetVPNStatus: String {
case connected
case connecting
case disconnecting
case disconnected
var displayText: String {
switch self {
case .connected: return "Connected"
case .connecting: return "Connecting..."
case .disconnecting: return "Disconnecting..."
case .disconnected: return "Disconnected"
}
}
var isTransitioning: Bool {
self == .connecting || self == .disconnecting
}
var isStable: Bool {
!isTransitioning
}
var statusColor: Color {
switch self {
case .connected: return .green
case .connecting, .disconnecting: return .netbirdOrange
case .disconnected: return Color(.systemGray3)
}
}
init(neStatus: NEVPNStatus) {
switch neStatus {
case .connected: self = .connected
case .connecting, .reasserting: self = .connecting
case .disconnecting: self = .disconnecting
case .disconnected, .invalid: self = .disconnected
@unknown default: self = .disconnected
}
}
}
struct VPNStatusEntry: TimelineEntry {
let date: Date
let status: WidgetVPNStatus
let ip: String
let fqdn: String
let needsAppSetup: Bool
let loginRequired: Bool
/// True when NE was mid-transition at load time (shown status is stale).
/// Used by the timeline provider to schedule a short-interval refresh.
var neWasTransitioning: Bool = false
var isConnected: Bool { status == .connected }
/// Deep-link URL for the pre-iOS 17 `Link` fallback.
/// Mirrors the routing logic in `WidgetActionButton` so both paths stay in sync.
/// Returns `nil` when a transitioning state makes any tap meaningless.
var fallbackDeepLink: URL? {
guard !status.isTransitioning else { return nil }
if needsAppSetup && !isConnected {
return loginRequired ? WidgetConstants.deepLinkLogin : WidgetConstants.deepLinkConnect
}
return isConnected ? WidgetConstants.deepLinkDisconnect : WidgetConstants.deepLinkConnect
}
}