Merge remote-tracking branch 'origin/ui-refactor' into ui-refactor

This commit is contained in:
Eduard Gert
2026-06-08 18:09:29 +02:00
12 changed files with 68 additions and 29 deletions

View File

@@ -24,9 +24,13 @@ jobs:
ignore_words_list: erro,clienta,hastable,iif,groupd,testin,groupe,cros,ans,deriver,te,userA,ede,additionals
# Non-English UI translations trip codespell on real foreign words
# (de: "Sie", "oder", "ist"). Only en/common.json is the source of
# truth that should be spell-checked. Add each new locale dir here
# when a language is added under client/ui/i18n/locales/.
skip: go.mod,go.sum,**/proxy/web/**,**/pnpm-lock.yaml,**/package-lock.json,client/ui/i18n/locales/de/**,client/ui/i18n/locales/hu/**
# truth that should be spell-checked. List each translated locale
# dir below and add new ones as languages are added under
# client/ui/i18n/locales/. Single-star globs are matched per path
# segment by codespell and behave the same across versions; the
# recursive "**" form did not take effect with the codespell shipped
# by this action.
skip: go.mod,go.sum,*/proxy/web/*,*pnpm-lock.yaml,*package-lock.json,*/locales/de/*,*/locales/hu/*
golangci:
strategy:
fail-fast: false

View File

@@ -33,6 +33,14 @@ const (
// for the T-10 event, FinalWarningLead for the T-2 event) so the UI
// can show "expires in ~N minutes" without hardcoding either constant.
MetaSessionLeadMinutes = "lead_minutes"
// MetaSessionDeadlineRejected is attached to the ERROR/AUTHENTICATION
// SystemEvent the daemon emits when it discards a deadline from the
// management server (pre-epoch, too far in the future, or past the
// clock-skew tolerance). The value is the rejection reason string.
// userMessage is left empty; the UI detects the event via this key
// and builds a localized notification — same pattern as the session
// warnings above.
MetaSessionDeadlineRejected = "session_deadline_rejected"
)
// expiresAtLayout is the wire format used for MetaSessionExpiresAt.

View File

@@ -9,6 +9,8 @@ import (
log "github.com/sirupsen/logrus"
"google.golang.org/protobuf/types/known/timestamppb"
cProto "github.com/netbirdio/netbird/client/proto"
"github.com/netbirdio/netbird/client/internal/auth/sessionwatch"
"github.com/netbirdio/netbird/client/system"
)
@@ -51,6 +53,13 @@ func (e *Engine) ApplySessionDeadline(ts *timestamppb.Timestamp) {
// of sync with the warning timers.
if err := e.sessionWatcher.Update(deadline); err != nil {
log.Errorf("auth session deadline rejected: %v, clearing", err)
e.statusRecorder.PublishEvent(
cProto.SystemEvent_ERROR,
cProto.SystemEvent_AUTHENTICATION,
"session deadline rejected",
"",
map[string]string{sessionwatch.MetaSessionDeadlineRejected: err.Error()},
)
}
}

View File

@@ -35,5 +35,10 @@ func (w noopSessionWatcher) Update(deadline time.Time) error {
return nil
}
func (noopSessionWatcher) Dismiss() {}
func (noopSessionWatcher) Close() {}
func (noopSessionWatcher) Dismiss() {
// No-op: only suppresses the timer-driven final-warning, which this stub never arms.
}
func (noopSessionWatcher) Close() {
// No-op: no timers to stop and no state to unwind; the recorder is cleared via Update(zero).
}

View File

@@ -959,6 +959,10 @@ func (s *Server) SwitchProfile(callerCtx context.Context, msg *proto.SwitchProfi
s.config = config
if msg != nil && msg.ProfileName != nil {
s.publishProfileListChanged(*msg.ProfileName)
}
return &proto.SwitchProfileResponse{}, nil
}

View File

@@ -19,10 +19,11 @@ import (
// side) so UI-side consumers don't have to import the daemon-internal
// package directly.
const (
MetaWarning = sessionwatch.MetaSessionWarning
MetaFinal = sessionwatch.MetaSessionFinal
MetaExpiresAt = sessionwatch.MetaSessionExpiresAt
MetaLeadMinutes = sessionwatch.MetaSessionLeadMinutes
MetaWarning = sessionwatch.MetaSessionWarning
MetaFinal = sessionwatch.MetaSessionFinal
MetaExpiresAt = sessionwatch.MetaSessionExpiresAt
MetaLeadMinutes = sessionwatch.MetaSessionLeadMinutes
MetaDeadlineRejected = sessionwatch.MetaSessionDeadlineRejected
)
// Warning is the typed payload emitted on the session-warning Wails

View File

@@ -55,6 +55,8 @@
"notify.sessionWarning.failed": "NetBird-Sitzung konnte nicht verlängert werden",
"notify.sessionWarning.successTitle": "NetBird-Sitzung verlängert",
"notify.sessionWarning.successBody": "Ihre Sitzung wurde erneuert.",
"notify.sessionDeadlineRejected.title": "Sitzungsfrist abgelehnt",
"notify.sessionDeadlineRejected.body": "Der Server hat eine ungültige Sitzungsfrist übermittelt. Bitte melden Sie sich erneut an.",
"common.cancel": "Abbrechen",
"common.save": "Speichern",

View File

@@ -55,6 +55,8 @@
"notify.sessionWarning.failed": "Failed to extend NetBird session",
"notify.sessionWarning.successTitle": "NetBird session extended",
"notify.sessionWarning.successBody": "Your session has been refreshed.",
"notify.sessionDeadlineRejected.title": "Session deadline rejected",
"notify.sessionDeadlineRejected.body": "The server sent an invalid session deadline. Please sign in again.",
"common.cancel": "Cancel",
"common.save": "Save",

View File

@@ -55,6 +55,8 @@
"notify.sessionWarning.failed": "A NetBird munkamenet meghosszabbítása sikertelen",
"notify.sessionWarning.successTitle": "NetBird munkamenet meghosszabbítva",
"notify.sessionWarning.successBody": "A munkamenet frissítve.",
"notify.sessionDeadlineRejected.title": "Munkamenet-határidő elutasítva",
"notify.sessionDeadlineRejected.body": "A szerver érvénytelen munkamenet-határidőt küldött. Kérjük, jelentkezzen be újra.",
"common.cancel": "Mégse",
"common.save": "Mentés",

View File

@@ -2,12 +2,7 @@
package main
// bindTrayClick is a no-op on macOS and Linux. On macOS the native
// NSStatusItem auto-shows the menu on left-click; on Linux the
// StatusNotifierItem host paints the menu independently. Binding an
// OnClick→OpenMenu handler is both unnecessary there and actively harmful on
// macOS, where OpenMenu routes through NSStatusItem's blocking [button
// mouseDown:] on the serial main GCD queue and freezes the tray and webview
// until the menu closes (commit c77e5cef8). Windows opts in via the sibling
// tray_click_windows.go file.
func bindTrayClick(*Tray) {}
func bindTrayClick(*Tray) {
// No-op: macOS/Linux native trays open the menu on click themselves.
// Only Windows needs an explicit handler (tray_click_windows.go).
}

View File

@@ -22,11 +22,12 @@ func (t *Tray) onSystemEvent(ev *application.CustomEvent) {
if !ok {
return
}
// Session-warning events carry no UserMessage — the tray builds the
// localised notification body locally from metadata. Every other
// event needs a non-empty UserMessage to show anything meaningful.
// Session-warning and deadline-rejected events carry no UserMessage —
// the tray builds the localised notification body locally from metadata.
// Every other event needs a non-empty UserMessage to show anything meaningful.
isSessionWarning := se.Metadata[authsession.MetaWarning] == "true"
if !isSessionWarning && se.UserMessage == "" {
isDeadlineRejected := se.Metadata[authsession.MetaDeadlineRejected] != ""
if !isSessionWarning && !isDeadlineRejected && se.UserMessage == "" {
return
}
if shouldSkipSystemEvent(se) {
@@ -52,6 +53,15 @@ func (t *Tray) onSystemEvent(ev *application.CustomEvent) {
// - T-FinalWarningLead (MetaSessionFinal=true) → auto-open the
// SessionExpiration dialog. No OS notification here; the
// dialog is the last-chance reminder, doubling up would be noise.
if isDeadlineRejected {
t.notify(
t.loc.T("notify.sessionDeadlineRejected.title"),
t.loc.T("notify.sessionDeadlineRejected.body"),
notifyIDSessionExpired,
)
return
}
if se.Metadata != nil && se.Metadata[authsession.MetaWarning] == "true" {
if se.Metadata[authsession.MetaFinal] == "true" {
t.openSessionExpiration()

View File

@@ -2,10 +2,7 @@
package main
// startTrayTheme is a no-op off Linux: macOS uses template icons (the OS
// recolours them per menubar appearance) and Windows ships colored PNGs, so
// neither needs the freedesktop colour-scheme probe that the Linux build
// uses to choose between the black and white monochrome silhouettes. Left
// callable so NewTray can invoke it unconditionally; panelDark stays nil and
// panelIsDark returns its default.
func (t *Tray) startTrayTheme() {}
func (t *Tray) startTrayTheme() {
// No-op off Linux: macOS template icons and Windows colored PNGs need no
// colour-scheme probe. panelDark stays nil; panelIsDark uses its default.
}