report daemon-down as DaemonUnavailable on initial Peers.Get and gate UI

- Peers.Get returns Status{Status: DaemonUnavailable} on Unavailable
  instead of an error so the React useStatus initial refresh picks up
  the same string the live event stream emits — the overlay no longer
  depends on receiving the synthetic event during boot.
- ProfileContext.refresh swallows Unavailable so the redundant
  "Load Profiles Failed" popup does not overlap the overlay.
- Tray Profiles submenu is disabled while the daemon is unavailable,
  matching the existing settings/debug/connect gating.
- gRPC client uses a 5s ConnectParams MaxDelay; the default 120s cap
  was keeping the SubChannel in backoff for tens of seconds after the
  daemon came back, masking the recovery.
This commit is contained in:
Zoltan Papp
2026-05-18 12:33:46 +02:00
parent f84b1df857
commit 6b44d65cac
4 changed files with 42 additions and 2 deletions

View File

@@ -253,14 +253,25 @@ func (s *Peers) ServiceShutdown() error {
return nil
}
// Get returns the current daemon status snapshot.
// Get returns the current daemon status snapshot. When the daemon socket
// is unreachable (process down, socket missing, permission denied) it
// returns Status{Status: StatusDaemonUnavailable} instead of an error so
// the frontend's initial useStatus().refresh() picks up the same string
// the live event stream emits — the React overlay and per-screen gating
// then key off a single status enum without a parallel "error" path.
func (s *Peers) Get(ctx context.Context) (Status, error) {
cli, err := s.conn.Client()
if err != nil {
if isDaemonUnreachable(err) {
return Status{Status: StatusDaemonUnavailable}, nil
}
return Status{}, err
}
resp, err := cli.Status(ctx, &proto.StatusRequest{GetFullPeerStatus: true})
if err != nil {
if isDaemonUnreachable(err) {
return Status{Status: StatusDaemonUnavailable}, nil
}
return Status{}, err
}
return statusFromProto(resp), nil