diff --git a/src/OpenClaw.Tray.WinUI/App.xaml.cs b/src/OpenClaw.Tray.WinUI/App.xaml.cs index 4c2f57162..8199f1838 100644 --- a/src/OpenClaw.Tray.WinUI/App.xaml.cs +++ b/src/OpenClaw.Tray.WinUI/App.xaml.cs @@ -200,7 +200,6 @@ public IntPtr GetHubWindowHandle() private ToastService? _toastService; private AppNotificationService? _appNotificationService; internal AppNotificationService? AppNotifications => _appNotificationService; - private string? _lastAuthFailureNotificationMessage; private string? _lastConnectionIssueNotificationKey; private readonly Dictionary _reportedChannelIssueSignatures = new(StringComparer.OrdinalIgnoreCase); private string? _lastSandboxRiskNotificationKey; @@ -2332,6 +2331,37 @@ private void ShowPairingRejectedAppNotification(string deviceId, string? detail) id: BuildPairingRejectedNotificationId(deviceId)); } + /// + /// Publishes an immediate connection-error banner using the single + /// connection-issue notification identity. Used for transient, page-driven + /// failures (e.g. a manual gateway switch that throws) where the snapshot + /// may be briefly silent. Because it reuses the connection-issue id/dedupe + /// key it occupies the same banner slot — it cannot produce a second bar — + /// and the snapshot-driven path will replace or dismiss it on the next tick. + /// + internal void ShowTransientConnectionError(string message) + { + var body = string.IsNullOrWhiteSpace(message) + ? LocalizationHelper.GetString("AppNotification_GatewayConnectionFailed_DefaultMessage") + : message; + + // Keep the snapshot-driven publisher from immediately re-emitting a + // duplicate for the same underlying error. + _lastConnectionIssueNotificationKey = $"operator-error:{message}"; + + AppNotificationPublisher.Show( + _appNotificationService, + LocalizationHelper.GetString("AppNotification_GatewayConnectionFailed_Title"), + body, + "connection", + "lifecycle", + AppNotificationSeverity.Error, + ConnectionIssueNotificationDedupeKey, + "connection", + LocalizationHelper.GetString("AppNotification_ActionOpenConnection"), + id: ConnectionIssueNotificationId); + } + private void UpdateConnectionIssueNotification(GatewayConnectionSnapshot snapshot) { if (!TryBuildConnectionIssueNotification(snapshot, out var title, out var message, out var severity, out var category, out var key)) @@ -2375,9 +2405,12 @@ private static bool TryBuildConnectionIssueNotification( if (snapshot.OverallState == OverallConnectionState.Error) { title = LocalizationHelper.GetString("AppNotification_GatewayConnectionFailed_Title"); - message = snapshot.OperatorError ?? LocalizationHelper.GetString("AppNotification_GatewayConnectionFailed_DefaultMessage"); + var rawError = snapshot.OperatorError; + message = string.IsNullOrWhiteSpace(rawError) + ? LocalizationHelper.GetString("AppNotification_GatewayConnectionFailed_DefaultMessage") + : rawError; severity = AppNotificationSeverity.Error; - key = $"operator-error:{message}"; + key = $"operator-error:{rawError ?? "default"}"; return true; } @@ -2663,8 +2696,6 @@ private void OnGatewayConnectionStatusChanged(object? sender, ConnectionStatus s if (status == ConnectionStatus.Connected && _appState != null) { _appState.AuthFailureMessage = null; - _lastAuthFailureNotificationMessage = null; - _appNotificationService?.Dismiss("connection:authentication-failed"); } UpdateTrayIcon(); @@ -2714,27 +2745,18 @@ private void OnGatewayAuthenticationFailed(object? sender, string message) { UpdateTrayIcon(); - // Store auth failure in AppState — HubWindow observes it via PropertyChanged + // Store auth failure in AppState — observed for tray tooltip / status. if (_appState != null) { _appState.AuthFailureMessage = message; } - if (!string.Equals(_lastAuthFailureNotificationMessage, message, StringComparison.Ordinal)) - { - _lastAuthFailureNotificationMessage = message; - AppNotificationPublisher.Show( - _appNotificationService, - LocalizationHelper.GetString("AppNotification_GatewayAuthenticationFailed_Title"), - message, - "connection", - "authentication", - AppNotificationSeverity.Error, - "connection:authentication-failed", - "connection", - LocalizationHelper.GetString("AppNotification_ActionOpenConnection"), - id: "connection:authentication-failed"); - } + // The user-facing banner is published by the single connection-issue + // notification (UpdateConnectionIssueNotification), driven off the + // snapshot's Error state + OperatorError (same string surfaced here). + // Publishing a second "authentication failed" banner here produced a + // duplicate top bar and forced the action button to degrade to + // "Show more", so it is intentionally not raised from this handler. } private void OnGatewaySessionCommandCompleted(object? sender, SessionCommandResult result) diff --git a/src/OpenClaw.Tray.WinUI/Pages/ConnectionPage.xaml b/src/OpenClaw.Tray.WinUI/Pages/ConnectionPage.xaml index 7c9b1d03f..a5581d83a 100644 --- a/src/OpenClaw.Tray.WinUI/Pages/ConnectionPage.xaml +++ b/src/OpenClaw.Tray.WinUI/Pages/ConnectionPage.xaml @@ -59,12 +59,6 @@ PINNED MODIFIERS — render across every mode ═════════════════════════════════════════════════════════ --> - - - _hiddenNotificationIds = new(StringComparer.Ordinal); public AppNotification? SelectVisibleNotification(AppNotificationSnapshot snapshot, bool revealHiddenIfNeeded = false) { PruneRemovedNotifications(snapshot); - var visible = snapshot.ActiveNotifications.FirstOrDefault(notification => - !_hiddenNotificationIds.Contains(notification.Id)); + + var visibleCandidates = snapshot.ActiveNotifications + .Where(notification => !_hiddenNotificationIds.Contains(notification.Id)) + .ToList(); + + // Connection issues are the most actionable banner (they route the user + // to the Connection page), so surface them ahead of any earlier, + // unrelated notification that happens to be current. Without this, a + // connection failure arriving while another notification is showing + // would be queued behind it and the user couldn't reach Connection. + // Among connection-source notifications, prefer one that actually has an + // action: an action-less connection notification (e.g. a transient + // gateway-host failure) must not mask a real connection error and + // degrade the banner to "Show more". + var visible = visibleCandidates.FirstOrDefault(IsActionableConnectionPriority) + ?? visibleCandidates.FirstOrDefault(IsConnectionPriority) + ?? visibleCandidates.FirstOrDefault(); if (visible is not null || !revealHiddenIfNeeded) return visible; - var fallback = snapshot.ActiveNotifications.FirstOrDefault(); + var fallback = snapshot.ActiveNotifications.FirstOrDefault(IsActionableConnectionPriority) + ?? snapshot.ActiveNotifications.FirstOrDefault(IsConnectionPriority) + ?? snapshot.ActiveNotifications.FirstOrDefault(); if (fallback is not null) _hiddenNotificationIds.Remove(fallback.Id); return fallback; } + private static bool IsConnectionPriority(AppNotification notification) => + string.Equals(notification.Source, ConnectionSource, StringComparison.OrdinalIgnoreCase); + + private static bool IsActionableConnectionPriority(AppNotification notification) => + IsConnectionPriority(notification) + && !string.IsNullOrWhiteSpace(notification.ActionLabel) + && !string.IsNullOrWhiteSpace(notification.ActionRoute); + public void HideActiveNotifications(AppNotificationSnapshot snapshot) { PruneRemovedNotifications(snapshot); diff --git a/src/OpenClaw.Tray.WinUI/Strings/en-us/Resources.resw b/src/OpenClaw.Tray.WinUI/Strings/en-us/Resources.resw index 02652edf6..6ce7d3a6d 100644 --- a/src/OpenClaw.Tray.WinUI/Strings/en-us/Resources.resw +++ b/src/OpenClaw.Tray.WinUI/Strings/en-us/Resources.resw @@ -1815,9 +1815,6 @@ On your gateway host (Mac/Linux), run: Gateway endpoint used by both the operator client and node service. - - Connection Error - Disconnected @@ -3276,9 +3273,6 @@ On your gateway host (Mac/Linux), run: Open Cron - - Gateway authentication failed - Gateway connection failed @@ -5599,9 +5593,6 @@ Make sure the gateway is running. Cancel - - Connect failed - Gateway connection failed. @@ -5632,31 +5623,6 @@ Make sure the gateway is running. Deny pairing request - - {0} - -Paste a new setup code from the Add Gateway flow. - - - {0} - -Your device needs approval on the gateway host. - - - {0} - -This gateway requires password authentication. - - - {0} - -The gateway may require a different auth protocol version. - - - {0} - -Check your connection settings and try again. - bootstrap diff --git a/src/OpenClaw.Tray.WinUI/Strings/fr-fr/Resources.resw b/src/OpenClaw.Tray.WinUI/Strings/fr-fr/Resources.resw index 96ea77906..45705128b 100644 --- a/src/OpenClaw.Tray.WinUI/Strings/fr-fr/Resources.resw +++ b/src/OpenClaw.Tray.WinUI/Strings/fr-fr/Resources.resw @@ -1767,9 +1767,6 @@ Sur votre hôte passerelle (Mac/Linux), exécutez : Point de terminaison de passerelle utilisé par le client opérateur et le service de nœud. - - Erreur de connexion - Déconnecté @@ -3204,9 +3201,6 @@ Sur votre hôte passerelle (Mac/Linux), exécutez : Ouvrir Cron - - Échec de l'authentification de la passerelle - Échec de la connexion à la passerelle @@ -5551,9 +5545,6 @@ Assurez-vous que la passerelle est en cours d'exécution. Annuler - - Échec de la connexion - Échec de la connexion à la passerelle. @@ -5584,31 +5575,6 @@ Assurez-vous que la passerelle est en cours d'exécution. Refuser la demande d’appairage - - {0} - -Collez un nouveau code de configuration depuis le flux Ajouter une passerelle. - - - {0} - -Votre appareil nécessite une approbation sur l’hôte de la passerelle. - - - {0} - -Cette passerelle nécessite une authentification par mot de passe. - - - {0} - -La passerelle pourrait nécessiter une version différente du protocole d’authentification. - - - {0} - -Vérifiez vos paramètres de connexion et réessayez. - amorçage diff --git a/src/OpenClaw.Tray.WinUI/Strings/nl-nl/Resources.resw b/src/OpenClaw.Tray.WinUI/Strings/nl-nl/Resources.resw index 01dde0c68..5a2b26bc8 100644 --- a/src/OpenClaw.Tray.WinUI/Strings/nl-nl/Resources.resw +++ b/src/OpenClaw.Tray.WinUI/Strings/nl-nl/Resources.resw @@ -1768,9 +1768,6 @@ Voer op uw gateway-host (Mac/Linux) uit: Gateway-eindpunt dat door zowel de operatorclient als de knooppuntservice wordt gebruikt. - - Verbindingsfout - Verbinding verbroken @@ -3205,9 +3202,6 @@ Voer op uw gateway-host (Mac/Linux) uit: Cron openen - - Gatewayverificatie mislukt - Gatewayverbinding mislukt @@ -5552,9 +5546,6 @@ Controleer of de gateway actief is. Annuleren - - Verbinding mislukt - Gatewayverbinding mislukt. @@ -5585,31 +5576,6 @@ Controleer of de gateway actief is. Koppelingverzoek weigeren - - {0} - -Plak een nieuwe installatiecode vanuit het Gateway toevoegen-proces. - - - {0} - -Uw apparaat moet worden goedgekeurd op de gatewayhost. - - - {0} - -Deze gateway vereist wachtwoordverificatie. - - - {0} - -De gateway vereist mogelijk een andere versie van het verificatieprotocol. - - - {0} - -Controleer uw verbindingsinstellingen en probeer het opnieuw. - bootstrap-modus diff --git a/src/OpenClaw.Tray.WinUI/Strings/zh-cn/Resources.resw b/src/OpenClaw.Tray.WinUI/Strings/zh-cn/Resources.resw index 38e5ecbad..698ce6c04 100644 --- a/src/OpenClaw.Tray.WinUI/Strings/zh-cn/Resources.resw +++ b/src/OpenClaw.Tray.WinUI/Strings/zh-cn/Resources.resw @@ -1767,9 +1767,6 @@ 操作员客户端和节点服务共同使用的网关终结点。 - - 连接错误 - 已断开连接 @@ -3204,9 +3201,6 @@ 打开 Cron - - 网关身份验证失败 - 网关连接失败 @@ -5552,9 +5546,6 @@ 取消 - - 连接失败 - 网关连接失败。 @@ -5585,31 +5576,6 @@ 拒绝配对请求 - - {0} - -从添加网关流程中粘贴新的设置代码。 - - - {0} - -您的设备需要在网关主机上获得批准。 - - - {0} - -此网关需要密码身份验证。 - - - {0} - -网关可能需要不同版本的认证协议。 - - - {0} - -请检查您的连接设置并重试。 - 引导 diff --git a/src/OpenClaw.Tray.WinUI/Strings/zh-tw/Resources.resw b/src/OpenClaw.Tray.WinUI/Strings/zh-tw/Resources.resw index 08258bfa4..43dbc909e 100644 --- a/src/OpenClaw.Tray.WinUI/Strings/zh-tw/Resources.resw +++ b/src/OpenClaw.Tray.WinUI/Strings/zh-tw/Resources.resw @@ -1767,9 +1767,6 @@ 操作員用戶端和節點服務共同使用的閘道端點。 - - 連線錯誤 - 已中斷連線 @@ -3204,9 +3201,6 @@ 開啟 Cron - - 閘道驗證失敗 - 閘道連線失敗 @@ -5552,9 +5546,6 @@ 取消 - - 連線失敗 - 閘道連線失敗。 @@ -5585,31 +5576,6 @@ 拒絕配對請求 - - {0} - -從新增閘道流程中貼上新的設定碼。 - - - {0} - -您的裝置需要在閘道主機上獲得核准。 - - - {0} - -此閘道需要密碼驗證。 - - - {0} - -閘道可能需要不同版本的認證協定。 - - - {0} - -請檢查您的連線設定並重試。 - 引導 diff --git a/src/OpenClaw.Tray.WinUI/Windows/HubWindow.xaml b/src/OpenClaw.Tray.WinUI/Windows/HubWindow.xaml index 1402ad533..7a97be51a 100644 --- a/src/OpenClaw.Tray.WinUI/Windows/HubWindow.xaml +++ b/src/OpenClaw.Tray.WinUI/Windows/HubWindow.xaml @@ -121,19 +121,25 @@ IsClosable="True" Closed="OnAppNotificationInfoBarClosed"> - + + -