diff --git a/docs/PRE-RELEASE-TASKS.md b/docs/PRE-RELEASE-TASKS.md index 43c9136..c0c6882 100644 --- a/docs/PRE-RELEASE-TASKS.md +++ b/docs/PRE-RELEASE-TASKS.md @@ -101,7 +101,7 @@ These land together because both edit `domain/job.go` and `storage/store.go`. - [x] P3.5 — Queue tests ### Phase 4 — Notifications + Command browse -- [ ] P4.1 — Failure notifications +- [x] P4.1 — Failure notifications - [ ] P4.2 — Command Browse button ### Phase 5 — Icons diff --git a/src/app/operations.go b/src/app/operations.go index a51a383..e853f72 100644 --- a/src/app/operations.go +++ b/src/app/operations.go @@ -170,6 +170,15 @@ func (s *Service) SetGlobalPause(paused bool) error { return err } +// ShouldNotifyOnFailure reports whether the user has enabled desktop +// notifications for failed job runs. It reads the config under mu so it is +// safe to call from any goroutine. +func (s *Service) ShouldNotifyOnFailure() bool { + s.mu.Lock() + defer s.mu.Unlock() + return s.store.Config.NotifyOnFailure +} + // UpdateSettings validates and persists a new application configuration. The // loaded jobs are re-saved because the jobs directory may have changed, and log // cleanup runs so a tightened retention policy takes effect immediately. diff --git a/src/ui/mainwindow.go b/src/ui/mainwindow.go index 37a4878..d4c5c4d 100644 --- a/src/ui/mainwindow.go +++ b/src/ui/mainwindow.go @@ -72,6 +72,15 @@ func newMainView(w fyne.Window) (fyne.CanvasObject, func(time.Duration, bool)) { fyne.Do(func() { if isRecorded { events = append(events, recorded.Record) + r := recorded.Record + if r.State == "Failed" && + (r.Trigger == "Manual" || r.Trigger == "Schedule") && + svc.ShouldNotifyOnFailure() { + fyne.CurrentApp().SendNotification(&fyne.Notification{ + Title: "GoSentry: Job Failed", + Content: r.JobName + ": " + r.Detail, + }) + } } if isError { events = append(events, newEvent(0, "Service", "Error", errOccurred.Err.Error()))