mirror of
https://github.com/netbirdio/ios-client.git
synced 2026-09-23 09:28:33 -07:00
* 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
76 lines
2.3 KiB
Swift
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
|
|
}
|
|
}
|