mirror of
https://github.com/netbirdio/netbird.git
synced 2026-07-20 15:31:30 +02:00
[client/ui] Refresh profile list in tray + UI on CLI profile add/remove
The daemon emits no dedicated profile-changed RPC event, and a profile add/remove doesn't move the connection status, so the UI's SubscribeStatus path never fired for CLI-driven `netbird profile add|remove` (and the tray's iconChanged guard would swallow it anyway). The tray menu and the React profile list stayed stale until the next status-string transition. AddProfile/RemoveProfile now publish a marked INFO/SYSTEM event over SubscribeEvents (metadata kind=profile-list-changed, empty userMessage so it stays silent). The UI's dispatchSystemEvent recognises the marker and re-emits the existing EventProfileChanged, which the tray's loadProfiles and React's ProfileContext.refresh already subscribe to — so both surfaces refresh from a single signal that originates in the shared daemon handler (covering both CLI and UI-initiated removals). No proto change. Also drop a stray, build-breaking `app.Updater` line in main.go.
This commit is contained in:
@@ -1864,6 +1864,8 @@ func (s *Server) AddProfile(ctx context.Context, msg *proto.AddProfileRequest) (
|
||||
return nil, fmt.Errorf("failed to create profile: %w", err)
|
||||
}
|
||||
|
||||
s.publishProfileListChanged(msg.ProfileName)
|
||||
|
||||
return &proto.AddProfileResponse{}, nil
|
||||
}
|
||||
|
||||
@@ -1885,9 +1887,32 @@ func (s *Server) RemoveProfile(ctx context.Context, msg *proto.RemoveProfileRequ
|
||||
return nil, fmt.Errorf("failed to remove profile: %w", err)
|
||||
}
|
||||
|
||||
s.publishProfileListChanged(msg.ProfileName)
|
||||
|
||||
return &proto.RemoveProfileResponse{}, nil
|
||||
}
|
||||
|
||||
// publishProfileListChanged nudges the desktop UI to refresh its profile list
|
||||
// after a CLI-driven add/remove. The daemon exposes no dedicated
|
||||
// profile-changed RPC event, and a profile add/remove doesn't move the
|
||||
// connection status, so the UI's SubscribeStatus path never fires for it (and
|
||||
// the tray's status-string guard would swallow it anyway). Instead we publish
|
||||
// a marked INFO/SYSTEM event over SubscribeEvents: the UI's dispatchSystemEvent
|
||||
// recognises the metadata "kind" marker and translates it into its internal
|
||||
// profile-changed signal that both the tray menu and the React profile views
|
||||
// already subscribe to (see client/ui/services/daemon_feed.go,
|
||||
// MetadataKindProfileListChanged). userMessage is intentionally empty so this
|
||||
// stays a silent refresh signal rather than a user-facing notification.
|
||||
func (s *Server) publishProfileListChanged(profileName string) {
|
||||
s.statusRecorder.PublishEvent(
|
||||
proto.SystemEvent_INFO,
|
||||
proto.SystemEvent_SYSTEM,
|
||||
"Profile list changed",
|
||||
"",
|
||||
map[string]string{"kind": "profile-list-changed", "profile": profileName},
|
||||
)
|
||||
}
|
||||
|
||||
// ListProfiles lists all profiles in the daemon.
|
||||
func (s *Server) ListProfiles(ctx context.Context, msg *proto.ListProfilesRequest) (*proto.ListProfilesResponse, error) {
|
||||
s.mutex.Lock()
|
||||
|
||||
@@ -46,6 +46,21 @@ const (
|
||||
// tray (Go side) so the frontend stays passive on this flow.
|
||||
EventSessionWarning = "netbird:session:warning"
|
||||
|
||||
// MetadataKindProfileListChanged is the SystemEvent.metadata["kind"]
|
||||
// marker the daemon stamps on the INFO/SYSTEM event it publishes after a
|
||||
// CLI-driven AddProfile / RemoveProfile (the daemon emits no dedicated
|
||||
// profile RPC event). dispatchSystemEvent recognises it and re-emits the
|
||||
// existing EventProfileChanged so the tray and React profile views refresh
|
||||
// — closing the gap the SubscribeStatus path can't, since a profile
|
||||
// add/remove doesn't change the daemon's status string (the tray's
|
||||
// iconChanged guard would swallow it). The daemon side hard-codes the same
|
||||
// string literal in client/server/server.go (client/server cannot import
|
||||
// this UI package).
|
||||
MetadataKindProfileListChanged = "profile-list-changed"
|
||||
// metadataKindKey is the SystemEvent.metadata key the "kind" marker lives
|
||||
// under. Kept in sync with the daemon-side literal in client/server.
|
||||
metadataKindKey = "kind"
|
||||
|
||||
// StatusDaemonUnavailable is the synthetic Status the UI emits when the
|
||||
// daemon's gRPC socket is unreachable (daemon not running, socket
|
||||
// permission, etc.). Real daemon statuses come straight from
|
||||
@@ -526,6 +541,16 @@ func (s *DaemonFeed) subscribeAndStreamEvents(ctx context.Context) error {
|
||||
func (s *DaemonFeed) dispatchSystemEvent(ev *proto.SystemEvent) {
|
||||
se := systemEventFromProto(ev)
|
||||
log.Infof("backend event: system severity=%s category=%s msg=%q", se.Severity, se.Category, se.UserMessage)
|
||||
// A CLI-driven profile add/remove publishes a marked SYSTEM event purely
|
||||
// to nudge the UI's profile views. Translate it into the existing
|
||||
// EventProfileChanged (which the tray's loadProfiles and React's
|
||||
// ProfileContext.refresh already subscribe to) and stop — it's an internal
|
||||
// refresh signal, not a user-facing notification, so it must not reach the
|
||||
// Recent Events list or fire an OS toast.
|
||||
if se.Metadata[metadataKindKey] == MetadataKindProfileListChanged {
|
||||
s.emitter.Emit(EventProfileChanged, ProfileRef{})
|
||||
return
|
||||
}
|
||||
s.emitter.Emit(EventDaemonNotification, se)
|
||||
if warn, ok := authsession.WarningFromMetadata(se.Metadata); ok {
|
||||
s.emitter.Emit(EventSessionWarning, warn)
|
||||
|
||||
Reference in New Issue
Block a user