From 5787dee01839815a1a96ef7daf19d0103b14be91 Mon Sep 17 00:00:00 2001 From: Rune Olsen Date: Thu, 20 Aug 2026 08:00:03 +0200 Subject: [PATCH] Fix Test Connection buttons needing a tab switch to activate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause: on macOS, SecureField's bound value only commits when the field loses focus (Return, click-away, tab switch) — not on every keystroke like TextField. Every "Test Connection" button (Sync, Email, Paperless, Anytype, Jarvis) gated its .disabled(...) on a *Configured value fed by a SecureField-backed API key/token/password, so typing a key straight into the field left the button looking permanently disabled until something else forced a focus change. Moved the "configured" check from the disabled condition into each test function's action handler instead — by the time a click fires, the click itself has already moved focus away and committed the field's value, so the check now sees it correctly. Buttons stay clickable at all times (gated only by their own isTesting spinner state) and show a clear "Enter X first" message if config is actually incomplete. --- oAI/Views/Screens/SettingsView.swift | 37 ++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/oAI/Views/Screens/SettingsView.swift b/oAI/Views/Screens/SettingsView.swift index ea3c3a0..d17efec 100644 --- a/oAI/Views/Screens/SettingsView.swift +++ b/oAI/Views/Screens/SettingsView.swift @@ -2131,7 +2131,7 @@ It's better to admit "I need more information" or "I cannot do that" than to fak Text("Test Connection") } } - .disabled(isTestingSync || !settingsService.syncConfigured) + .disabled(isTestingSync) if let result = syncTestResult { Text(result) .font(.system(size: 13)) @@ -2431,7 +2431,7 @@ It's better to admit "I need more information" or "I cannot do that" than to fak Text("Test Connection") } } - .disabled(isTestingEmailConnection || !settingsService.emailServerConfigured) + .disabled(isTestingEmailConnection) if let result = emailConnectionTestResult { Text(result) .font(.system(size: settingsService.guiTextSize - 1)) @@ -2714,7 +2714,7 @@ It's better to admit "I need more information" or "I cannot do that" than to fak Text("Test Connection") } } - .disabled(isTestingPaperless || !settingsService.paperlessConfigured) + .disabled(isTestingPaperless) if let result = paperlessTestResult { Text(result) .font(.system(size: 13)) @@ -2748,6 +2748,11 @@ It's better to admit "I need more information" or "I cannot do that" than to fak private func testPaperlessConnection() async { isTestingPaperless = true paperlessTestResult = nil + guard settingsService.paperlessConfigured else { + paperlessTestResult = "✗ Enter a base URL and API token first." + isTestingPaperless = false + return + } let result = await PaperlessService.shared.testConnection() await MainActor.run { switch result { @@ -2825,7 +2830,7 @@ It's better to admit "I need more information" or "I cannot do that" than to fak Text("Test Connection") } } - .disabled(isTestingAnytype || !settingsService.anytypeMcpConfigured) + .disabled(isTestingAnytype) if let result = anytypeTestResult { Text(result) .font(.system(size: 13)) @@ -2920,7 +2925,7 @@ It's better to admit "I need more information" or "I cannot do that" than to fak Text("Test Connection") } } - .disabled(isTestingJarvis || !settingsService.jarvisConfigured) + .disabled(isTestingJarvis) if let result = jarvisTestResult { Text(result) .font(.system(size: 13)) @@ -2951,6 +2956,11 @@ It's better to admit "I need more information" or "I cannot do that" than to fak private func testJarvisConnection() async { isTestingJarvis = true jarvisTestResult = nil + guard settingsService.jarvisConfigured else { + jarvisTestResult = "✗ Enter a URL and API key first." + isTestingJarvis = false + return + } let ok = await JarvisService.shared.testConnection() await MainActor.run { jarvisTestResult = ok ? "✓ Connected" : "✗ Connection failed" @@ -2961,6 +2971,11 @@ It's better to admit "I need more information" or "I cannot do that" than to fak private func testAnytypeConnection() async { isTestingAnytype = true anytypeTestResult = nil + guard settingsService.anytypeMcpConfigured else { + anytypeTestResult = "✗ Enter an API key first." + isTestingAnytype = false + return + } let result = await AnytypeMCPService.shared.testConnection() await MainActor.run { switch result { @@ -3389,6 +3404,12 @@ It's better to admit "I need more information" or "I cannot do that" than to fak isTestingEmailConnection = true emailConnectionTestResult = nil + guard settingsService.emailServerConfigured else { + emailConnectionTestResult = "✗ Enter your IMAP/SMTP host, username, and password first." + isTestingEmailConnection = false + return + } + do { let result = try await EmailService.shared.testConnection() emailConnectionTestResult = "✓ \(result)" @@ -3405,6 +3426,12 @@ It's better to admit "I need more information" or "I cannot do that" than to fak isTestingSync = true syncTestResult = nil + guard settingsService.syncConfigured else { + syncTestResult = "✗ Enter a repository URL and credentials first." + isTestingSync = false + return + } + do { let result = try await gitSync.testConnection() syncTestResult = "✓ \(result)"