Fix 3 nicht funktionierende Alert-Dismiss-Buttons (M34)
README-Milestone-Nachcheck (Port-Konflikt-Prüfung/"Fertig"-Button) deckte drei eigenständige Bugs derselben Klasse auf: Cancel/OK-Buttons bei ReviewApplyViews Apply-Fehler-Alert, ConnectViews Zertifikat-Alert und ConnectViews SSH-Hostkey-Alert taten nichts oder zu wenig - der jeweilige Verbindungs-/Fehlerzustand blieb hängen, der Dialog konnte nicht sauber verlassen werden. Neue ConnectionService.cancelPendingTrustConfirmation() und SetupViewModel.dismissApplyError(), alle drei Alerts korrekt verdrahtet (Button-Action + Bindings-Setter fuer Tap-Outside/Esc). Totes dismissPendingSSHTrust() entfernt. 1 neuer Regressionstest, alle 102 Unit-Tests gruen. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -123,6 +123,27 @@ final class ConnectionService: ObservableObject {
|
||||
await connect(with: credentials)
|
||||
}
|
||||
|
||||
/// Backs out of a blocked connect attempt (`.needsCertificateConfirmation`/
|
||||
/// `.needsSSHHostKeyConfirmation`) without trusting anything — back to `.idle` so the
|
||||
/// Verbinden-tab form is usable again for a fresh attempt (e.g. different credentials, wrong
|
||||
/// host typo). Without this, `ConnectView`'s "Abbrechen" buttons on both trust alerts had no
|
||||
/// way to actually change `state`, so the alert's `isPresented` binding (derived from
|
||||
/// `state`/`pendingSSHTrustFingerprint`) stayed true and the dialog could never really be
|
||||
/// dismissed — found during the README milestone re-check, 2026-09-17 (same class of bug as
|
||||
/// `SetupViewModel.dismissApplyError()`, fixed moments earlier this session). Also clears
|
||||
/// `pendingSSHTrustFingerprint`, covering the background-trust-probe trigger path too, so one
|
||||
/// method correctly backs out of either of the two ways `ConnectView`'s SSH-host-key alert
|
||||
/// can be showing (see `pendingSSHTrustFingerprint`'s own doc comment for that distinction).
|
||||
func cancelPendingTrustConfirmation() {
|
||||
switch state {
|
||||
case .needsCertificateConfirmation, .needsSSHHostKeyConfirmation:
|
||||
state = .idle
|
||||
default:
|
||||
break
|
||||
}
|
||||
pendingSSHTrustFingerprint = nil
|
||||
}
|
||||
|
||||
func trustCurrentSSHHostKeyAndRetry(fingerprint: String) async {
|
||||
guard let credentials else { return }
|
||||
sshHostKeyTrust.trust(host: credentials.host, fingerprint: fingerprint)
|
||||
@@ -138,10 +159,6 @@ final class ConnectionService: ObservableObject {
|
||||
pendingSSHTrustFingerprint = nil
|
||||
}
|
||||
|
||||
func dismissPendingSSHTrust() {
|
||||
pendingSSHTrustFingerprint = nil
|
||||
}
|
||||
|
||||
/// Safety net for when the proactive `verifySSHTrust` check (above) didn't already cover a
|
||||
/// dedicated SSH service's host key — confirmed live: a session's first Expert-tab write (its
|
||||
/// pre-apply backup, over its own dedicated SSH connection) can still hit
|
||||
|
||||
@@ -134,7 +134,9 @@ struct ConnectView: View {
|
||||
Button(L10n.t("Vertrauen und verbinden", appLanguage)) {
|
||||
viewModel.trustAndRetry(fingerprint: fingerprint)
|
||||
}
|
||||
Button(L10n.t("Abbrechen", appLanguage), role: .cancel) {}
|
||||
Button(L10n.t("Abbrechen", appLanguage), role: .cancel) {
|
||||
viewModel.cancelCertificatePrompt()
|
||||
}
|
||||
} message: { fingerprint in
|
||||
Text(L10n.t("Der Router hat sich mit einem unbekannten Zertifikat gemeldet.", appLanguage)
|
||||
+ "\n" + L10n.t("Fingerabdruck:", appLanguage) + " \(fingerprint)\n\n"
|
||||
@@ -189,7 +191,7 @@ struct ConnectView: View {
|
||||
private var certificateAlertBinding: Binding<Bool> {
|
||||
Binding(
|
||||
get: { certificateFingerprint != nil },
|
||||
set: { _ in }
|
||||
set: { isPresented in if !isPresented { viewModel.cancelCertificatePrompt() } }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -203,7 +205,7 @@ struct ConnectView: View {
|
||||
private var sshHostKeyAlertBinding: Binding<Bool> {
|
||||
Binding(
|
||||
get: { sshHostKeyFingerprint != nil },
|
||||
set: { _ in }
|
||||
set: { isPresented in if !isPresented { viewModel.dismissSSHHostKeyPrompt() } }
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -145,8 +145,21 @@ final class ConnectViewModel: ObservableObject {
|
||||
}
|
||||
}
|
||||
|
||||
/// Backs out of whichever of the two trust-confirmation states is currently showing the
|
||||
/// SSH-host-key alert (a blocked initial connect, or the background trust probe after a
|
||||
/// successful REST connect — see `ConnectionService.pendingSSHTrustFingerprint`'s doc
|
||||
/// comment). Previously only cleared `pendingSSHTrustFingerprint`, which left the alert stuck
|
||||
/// showing whenever it was triggered by the *other* path (`state ==
|
||||
/// .needsSSHHostKeyConfirmation`) — found during the README milestone re-check, 2026-09-17.
|
||||
func dismissSSHHostKeyPrompt() {
|
||||
connectionService.dismissPendingSSHTrust()
|
||||
connectionService.cancelPendingTrustConfirmation()
|
||||
}
|
||||
|
||||
/// Same "Abbrechen" fix as `dismissSSHHostKeyPrompt()`, for the unknown-TLS-certificate alert
|
||||
/// — its "Abbrechen" button previously had an empty action and could never actually clear
|
||||
/// `connectionService.state` back out of `.needsCertificateConfirmation`.
|
||||
func cancelCertificatePrompt() {
|
||||
connectionService.cancelPendingTrustConfirmation()
|
||||
}
|
||||
|
||||
func checkForUpdates(for credentials: RouterOSCredentials) {
|
||||
|
||||
@@ -79,10 +79,10 @@ struct ReviewApplyView: View {
|
||||
L10n.t("Anwenden fehlgeschlagen", appLanguage),
|
||||
isPresented: Binding(
|
||||
get: { viewModel.applyError != nil },
|
||||
set: { _ in }
|
||||
set: { isPresented in if !isPresented { viewModel.dismissApplyError() } }
|
||||
)
|
||||
) {
|
||||
Button(L10n.t("OK", appLanguage)) {}
|
||||
Button(L10n.t("OK", appLanguage)) { viewModel.dismissApplyError() }
|
||||
} message: {
|
||||
Text(viewModel.applyError ?? "")
|
||||
}
|
||||
|
||||
@@ -264,6 +264,17 @@ final class SetupViewModel: ObservableObject {
|
||||
setFirewallSectionEnabled(true)
|
||||
}
|
||||
|
||||
/// Dismisses the "Anwenden fehlgeschlagen" alert — `ReviewApplyView`'s `.alert(isPresented:)`
|
||||
/// binding derives its presented state from `applyError != nil`, so without this the alert's
|
||||
/// own OK button (whose action previously did nothing) could never actually clear the error:
|
||||
/// SwiftUI's next re-render would immediately re-evaluate the getter as still true and the
|
||||
/// alert would reappear or fail to dismiss. Found during the README milestone re-check,
|
||||
/// 2026-09-17 (bugs.md-style fix — see `SetupViewModel`'s port-conflict entry in bugs.md for
|
||||
/// the same-feature-area race-condition fix earlier this session).
|
||||
func dismissApplyError() {
|
||||
applyError = nil
|
||||
}
|
||||
|
||||
/// Ends the wizard after a successful apply — without this, the completed review screen just
|
||||
/// sits there with a disabled "Jetzt anwenden" and no way forward except "Zurück" (which would
|
||||
/// re-walk now-stale steps against the router state this apply just changed). Resets to a
|
||||
|
||||
Reference in New Issue
Block a user