Fix the black screen

This commit is contained in:
Stephen B 2025-11-10 00:30:45 -05:00
parent bbae4ac2b0
commit 525e5f57b0
3 changed files with 85 additions and 13 deletions

View file

@ -227,6 +227,9 @@ class TunnelManager: ObservableObject {
self.tunnelStatus = .error
}
VPNLogger.shared.log("VPN status updated: \(self.tunnelStatus.rawValue)")
if connectionStatus == .connected && heartbeatStartPending {
startHeartbeatInBackground()
}
}
}
@ -295,8 +298,8 @@ class TunnelManager: ObservableObject {
}
private func startExistingVPN(manager: NETunnelProviderManager) {
guard tunnelStatus != .connected else {
VPNLogger.shared.log("Network tunnel is already connected")
guard tunnelStatus == .disconnected || tunnelStatus == .error else {
VPNLogger.shared.log("Ignoring VPN start; current status: \(tunnelStatus.rawValue)")
return
}
tunnelStatus = .connecting
@ -483,12 +486,15 @@ class DNSChecker: ObservableObject {
// Global state variable for the heartbeat response.
var pubHeartBeat = false
private var heartbeatStartPending = false
private var heartbeatStartInProgress = false
@main
struct HeartbeatApp: App {
@AppStorage("hasLaunchedBefore") var hasLaunchedBefore: Bool = false
@AppStorage("customAccentColor") private var customAccentColorHex: String = ""
@AppStorage("appTheme") private var appThemeRaw: String = AppTheme.system.rawValue
@AppStorage("autoStartVPN") private var autoStartVPN = true
@State private var showWelcomeSheet: Bool = false
@State private var show_alert = false
@State private var alert_string = ""
@ -551,6 +557,14 @@ struct HeartbeatApp: App {
}
}
private func triggerAutoVPNStartIfNeeded() {
guard autoStartVPN else { return }
let manager = TunnelManager.shared
if manager.tunnelStatus == .disconnected || manager.tunnelStatus == .error {
manager.startVPN()
}
}
private var globalAccent: Color {
themeExpansionManager.resolvedAccentColor(from: customAccentColorHex)
}
@ -599,7 +613,7 @@ struct HeartbeatApp: App {
if !hasLaunchedBefore {
showWelcomeSheet = true
} else {
TunnelManager.shared.startVPN()
triggerAutoVPNStartIfNeeded()
}
HeartbeatApp.updateUIKitTint(customHex: customAccentColorHex,
hasAccess: themeExpansionManager.hasThemeExpansion)
@ -613,10 +627,10 @@ struct HeartbeatApp: App {
}
.sheet(isPresented: $showWelcomeSheet) {
WelcomeSheetView {
// When the user taps "Continue", mark the app as launched and start the VPN.
// When the user taps "Continue", mark the app as launched and start the VPN if allowed.
hasLaunchedBefore = true
showWelcomeSheet = false
TunnelManager.shared.startVPN()
triggerAutoVPNStartIfNeeded()
}
}
}
@ -653,8 +667,11 @@ class MountingProgress: ObservableObject {
@Published var coolisMounted: Bool = false
func checkforMounted() {
DispatchQueue.main.async {
self.coolisMounted = isMounted()
DispatchQueue.global(qos: .utility).async {
let mounted = isMounted()
DispatchQueue.main.async {
self.coolisMounted = mounted
}
}
}
@ -671,10 +688,21 @@ class MountingProgress: ObservableObject {
}
private func mount() {
self.coolisMounted = isMounted()
guard TunnelManager.shared.tunnelStatus == .connected else {
DispatchQueue.main.async {
self.coolisMounted = false
self.mountingThread = nil
}
return
}
let currentlyMounted = isMounted()
DispatchQueue.main.async {
self.coolisMounted = currentlyMounted
}
let pairingpath = URL.documentsDirectory.appendingPathComponent("pairingFile.plist").path
if isPairing(), !isMounted() {
if isPairing(), !currentlyMounted {
if let mountingThread = mountingThread {
mountingThread.cancel()
self.mountingThread = nil
@ -697,7 +725,8 @@ class MountingProgress: ObservableObject {
}
}
} else {
self.coolisMounted = isMounted()
self.coolisMounted = true
self.checkforMounted()
}
self.mountingThread = nil
}
@ -724,8 +753,37 @@ func isPairing() -> Bool {
return true
}
func startHeartbeatInBackground() {
func startHeartbeatInBackground(requireVPNConnection: Bool = true) {
assert(Thread.isMainThread, "startHeartbeatInBackground must be called on the main thread")
let pairingFileURL = URL.documentsDirectory.appendingPathComponent("pairingFile.plist")
guard FileManager.default.fileExists(atPath: pairingFileURL.path) else {
heartbeatStartPending = false
return
}
let vpnConnected = TunnelManager.shared.tunnelStatus == .connected
if requireVPNConnection && !vpnConnected {
if !heartbeatStartPending {
print("Heartbeat start deferred until VPN connects")
}
heartbeatStartPending = true
return
}
guard !heartbeatStartInProgress else {
return
}
heartbeatStartPending = false
heartbeatStartInProgress = true
let heartBeatThread = Thread {
defer {
DispatchQueue.main.async {
heartbeatStartInProgress = false
}
}
let completionHandler: @convention(block) (Int32, String?) -> Void = { result, message in
if result == 0 {
print("Heartbeat started successfully: \(message ?? "")")
@ -762,7 +820,9 @@ func startHeartbeatInBackground() {
showTryAgain: true
) { shouldTryAgain in
if shouldTryAgain {
startHeartbeatInBackground()
DispatchQueue.main.async {
startHeartbeatInBackground()
}
}
}
}

View file

@ -47,6 +47,10 @@ func htons(_ value: UInt16) -> UInt16 {
}
func isMounted() -> Bool {
guard TunnelManager.shared.tunnelStatus == .connected else {
return false
}
var addr = sockaddr_in()
memset(&addr, 0, MemoryLayout<sockaddr_in>.size)
addr.sin_family = sa_family_t(AF_INET)

View file

@ -86,7 +86,7 @@ struct HomeView: View {
themeExpansion?.resolvedAccentColor(from: customAccentColorHex) ?? .blue
}
private var ddiMounted: Bool { isMounted() }
private var ddiMounted: Bool { mounting.coolisMounted }
private var canConnectByApp: Bool { pairingFileExists && ddiMounted }
private var pairingFileLikelyInvalid: Bool {
(pairingFileExists || pairingFilePresentOnDisk) &&
@ -119,6 +119,7 @@ struct HomeView: View {
.padding(.horizontal, 20)
.padding(.vertical, 30)
}
.scrollIndicators(.hidden)
if isImportingFile {
Color.black.opacity(0.35).ignoresSafeArea()
@ -154,6 +155,9 @@ struct HomeView: View {
refreshBackground()
loadAppListIfNeeded()
startWiFiMonitoring()
if tunnel.tunnelStatus == .connected {
MountingProgress.shared.checkforMounted()
}
if autoStartVPN && tunnel.tunnelStatus == .disconnected {
TunnelManager.shared.startVPN()
}
@ -191,7 +195,9 @@ struct HomeView: View {
}
.onChange(of: tunnel.tunnelStatus) { _, newStatus in
if newStatus == .connected {
loadAppListIfNeeded(force: cachedAppNames.isEmpty)
runConnectionDiagnostics()
MountingProgress.shared.checkforMounted()
}
}
.onChange(of: favoriteApps) { _, _ in
@ -1111,6 +1117,8 @@ struct HomeView: View {
return
}
guard tunnel.tunnelStatus == .connected else { return }
if !force && !cachedAppNames.isEmpty { return }
DispatchQueue.global(qos: .userInitiated).async {