mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-23 05:49:07 +02:00
Keep the UI running when the notification service fails to start
This commit is contained in:
+3
-4
@@ -14,7 +14,6 @@ import (
|
|||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/wailsapp/wails/v3/pkg/application"
|
"github.com/wailsapp/wails/v3/pkg/application"
|
||||||
"github.com/wailsapp/wails/v3/pkg/events"
|
"github.com/wailsapp/wails/v3/pkg/events"
|
||||||
"github.com/wailsapp/wails/v3/pkg/services/notifications"
|
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/client/ui/authsession"
|
"github.com/netbirdio/netbird/client/ui/authsession"
|
||||||
"github.com/netbirdio/netbird/client/ui/i18n"
|
"github.com/netbirdio/netbird/client/ui/i18n"
|
||||||
@@ -63,7 +62,7 @@ type registeredServices struct {
|
|||||||
profiles *services.Profiles
|
profiles *services.Profiles
|
||||||
update *services.Update
|
update *services.Update
|
||||||
daemonFeed *services.DaemonFeed
|
daemonFeed *services.DaemonFeed
|
||||||
notifier *notifications.NotificationService
|
notifier *Notifier
|
||||||
compat *services.Compat
|
compat *services.Compat
|
||||||
profileSwitcher *services.ProfileSwitcher
|
profileSwitcher *services.ProfileSwitcher
|
||||||
bundle *i18n.Bundle
|
bundle *i18n.Bundle
|
||||||
@@ -103,7 +102,7 @@ func main() {
|
|||||||
updaterHolder := updater.NewHolder(app.Event)
|
updaterHolder := updater.NewHolder(app.Event)
|
||||||
update := services.NewUpdate(conn, updaterHolder)
|
update := services.NewUpdate(conn, updaterHolder)
|
||||||
daemonFeed := services.NewDaemonFeed(conn, app.Event, updaterHolder, debugLog)
|
daemonFeed := services.NewDaemonFeed(conn, app.Event, updaterHolder, debugLog)
|
||||||
notifier := notifications.New()
|
notifier := newNotifier()
|
||||||
compat := services.NewCompat(conn)
|
compat := services.NewCompat(conn)
|
||||||
// macOS shows no toast until permission is requested. Run it after
|
// macOS shows no toast until permission is requested. Run it after
|
||||||
// ApplicationStarted so the notifier's Startup has initialised the
|
// ApplicationStarted so the notifier's Startup has initialised the
|
||||||
@@ -210,7 +209,7 @@ func main() {
|
|||||||
// requestNotificationAuthorization prompts for macOS notification permission.
|
// requestNotificationAuthorization prompts for macOS notification permission.
|
||||||
// The request blocks until the user responds (up to 3 minutes), so callers run
|
// The request blocks until the user responds (up to 3 minutes), so callers run
|
||||||
// it in a goroutine. No-op on Linux/Windows.
|
// it in a goroutine. No-op on Linux/Windows.
|
||||||
func requestNotificationAuthorization(notifier *notifications.NotificationService) {
|
func requestNotificationAuthorization(notifier *Notifier) {
|
||||||
authorized, err := notifier.CheckNotificationAuthorization()
|
authorized, err := notifier.CheckNotificationAuthorization()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Debugf("check notification authorization: %v", err)
|
logrus.Debugf("check notification authorization: %v", err)
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
//go:build !android && !ios && !freebsd && !js
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/wailsapp/wails/v3/pkg/application"
|
||||||
|
"github.com/wailsapp/wails/v3/pkg/services/notifications"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errNotificationsUnavailable = errors.New("notifications unavailable")
|
||||||
|
|
||||||
|
// Notifier wraps the Wails notification service so an unavailable backend
|
||||||
|
// disables notifications instead of aborting the app. Startup fails for
|
||||||
|
// environment reasons (a bare unbundled binary on macOS has no bundle
|
||||||
|
// identifier, a headless Linux session has no D-Bus session bus), and Wails
|
||||||
|
// treats a service startup error as fatal. After a failed startup every call
|
||||||
|
// is a no-op: on macOS, touching UNUserNotificationCenter without a bundle
|
||||||
|
// identifier raises an Objective-C exception that recover() cannot catch.
|
||||||
|
type Notifier struct {
|
||||||
|
inner *notifications.NotificationService
|
||||||
|
available atomic.Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newNotifier() *Notifier {
|
||||||
|
return &Notifier{inner: notifications.New()}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceName implements the Wails service-name hook for startup logs.
|
||||||
|
func (n *Notifier) ServiceName() string {
|
||||||
|
return n.inner.ServiceName()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceStartup starts the platform notifier, downgrading failure to a
|
||||||
|
// warning so the app keeps running without notifications.
|
||||||
|
func (n *Notifier) ServiceStartup(ctx context.Context, options application.ServiceOptions) error {
|
||||||
|
if err := n.inner.ServiceStartup(ctx, options); err != nil {
|
||||||
|
log.Warnf("notifications disabled: %v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
n.available.Store(true)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Notifier) ServiceShutdown() error {
|
||||||
|
if !n.available.Load() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return n.inner.ServiceShutdown()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Notifier) CheckNotificationAuthorization() (bool, error) {
|
||||||
|
if !n.available.Load() {
|
||||||
|
return false, errNotificationsUnavailable
|
||||||
|
}
|
||||||
|
return n.inner.CheckNotificationAuthorization()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Notifier) RequestNotificationAuthorization() (bool, error) {
|
||||||
|
if !n.available.Load() {
|
||||||
|
return false, errNotificationsUnavailable
|
||||||
|
}
|
||||||
|
return n.inner.RequestNotificationAuthorization()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendNotification delivers a notification, silently dropping it when the
|
||||||
|
// backend never started (notifications are best-effort everywhere).
|
||||||
|
func (n *Notifier) SendNotification(options notifications.NotificationOptions) error {
|
||||||
|
if !n.available.Load() {
|
||||||
|
log.Debugf("notifications disabled, dropping %q", options.ID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return n.inner.SendNotification(options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Notifier) SendNotificationWithActions(options notifications.NotificationOptions) error {
|
||||||
|
if !n.available.Load() {
|
||||||
|
log.Debugf("notifications disabled, dropping %q", options.ID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return n.inner.SendNotificationWithActions(options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Notifier) RegisterNotificationCategory(category notifications.NotificationCategory) error {
|
||||||
|
if !n.available.Load() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return n.inner.RegisterNotificationCategory(category)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnNotificationResponse registers the response callback. Pure Go state, so
|
||||||
|
// it is safe (and simply inert) when the backend never started.
|
||||||
|
func (n *Notifier) OnNotificationResponse(callback func(result notifications.NotificationResult)) {
|
||||||
|
n.inner.OnNotificationResponse(callback)
|
||||||
|
}
|
||||||
+1
-1
@@ -44,7 +44,7 @@ type TrayServices struct {
|
|||||||
Profiles *services.Profiles
|
Profiles *services.Profiles
|
||||||
Networks *services.Networks
|
Networks *services.Networks
|
||||||
DaemonFeed *services.DaemonFeed
|
DaemonFeed *services.DaemonFeed
|
||||||
Notifier *notifications.NotificationService
|
Notifier *Notifier
|
||||||
Update *services.Update
|
Update *services.Update
|
||||||
ProfileSwitcher *services.ProfileSwitcher
|
ProfileSwitcher *services.ProfileSwitcher
|
||||||
WindowManager *services.WindowManager
|
WindowManager *services.WindowManager
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ func safeSendNotification(send sendFn, what string, opts notifications.Notificat
|
|||||||
// notifyIfDaemonOutdated probes the daemon once and fires an OS toast when it
|
// notifyIfDaemonOutdated probes the daemon once and fires an OS toast when it
|
||||||
// is reachable but too old for this UI. A probe error means the daemon isn't
|
// is reachable but too old for this UI. A probe error means the daemon isn't
|
||||||
// reachable (not outdated), so it is left to the normal connection flow.
|
// reachable (not outdated), so it is left to the normal connection flow.
|
||||||
func notifyIfDaemonOutdated(compat *services.Compat, notifier *notifications.NotificationService, loc *Localizer) {
|
func notifyIfDaemonOutdated(compat *services.Compat, notifier *Notifier, loc *Localizer) {
|
||||||
ready, err := compat.DaemonReady(context.Background())
|
ready, err := compat.DaemonReady(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugf("daemon compatibility probe: %v", err)
|
log.Debugf("daemon compatibility probe: %v", err)
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ type trayUpdater struct {
|
|||||||
app *application.App
|
app *application.App
|
||||||
window *application.WebviewWindow
|
window *application.WebviewWindow
|
||||||
update *services.Update
|
update *services.Update
|
||||||
notifier *notifications.NotificationService
|
notifier *Notifier
|
||||||
loc *Localizer
|
loc *Localizer
|
||||||
onIconChange func()
|
onIconChange func()
|
||||||
// onMenuChange drives a full tray relayout: the update row lives in the
|
// onMenuChange drives a full tray relayout: the update row lives in the
|
||||||
@@ -36,7 +36,7 @@ type trayUpdater struct {
|
|||||||
progressWindowOpen bool
|
progressWindowOpen bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTrayUpdater(app *application.App, window *application.WebviewWindow, update *services.Update, notifier *notifications.NotificationService, loc *Localizer, onIconChange func(), onMenuChange func()) *trayUpdater {
|
func newTrayUpdater(app *application.App, window *application.WebviewWindow, update *services.Update, notifier *Notifier, loc *Localizer, onIconChange func(), onMenuChange func()) *trayUpdater {
|
||||||
u := &trayUpdater{
|
u := &trayUpdater{
|
||||||
app: app,
|
app: app,
|
||||||
window: window,
|
window: window,
|
||||||
|
|||||||
Reference in New Issue
Block a user