You've already forked ios-client
mirror of
https://github.com/netbirdio/ios-client.git
synced 2026-05-22 17:10:12 -07:00
315283822c
* Add Home Screen widget with VPN toggle and refactor app activation logic - Add NetBirdWidgetExtension target with small/medium widget sizes - Support direct connect/disconnect from widget via interactive buttons (iOS 17+) - Detect missing VPN config or login-required state and open app via deep link - Poll for stable VPN state after toggle to prevent loader getting stuck - Add widget shared state keys to GlobalConstants and sync status from MainViewModel - Fix false "authentication required" alert on app resume after widget disconnect - Deduplicate app activation logic into shared startActivation/stopActivation - Extract polling helpers: updateDetailsIfChanged, updatePeersIfChanged, applyExtensionStatus
68 lines
1.9 KiB
Swift
68 lines
1.9 KiB
Swift
import SwiftUI
|
|
import NetworkExtension
|
|
import WidgetKit
|
|
|
|
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 .orange
|
|
case .disconnected: return .gray
|
|
}
|
|
}
|
|
|
|
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
|
|
|
|
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
|
|
}
|
|
}
|