Compare commits

...

2 Commits

Author SHA1 Message Date
mixeme 73dae9e64c P4.2: Add Browse button for Command field in job dialog
Add chooseFile helper (dialog.NewFileOpen) in settings_view.go and wrap
the Command entry in job_dialog.go with a Browse button so users can
pick an executable from a file picker instead of typing the path.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 08:19:40 +03:00
mixeme 564609d8fb P4.1: Wire desktop notifications for failed job runs
Add Service.ShouldNotifyOnFailure() (reads config under mu) and call
fyne.CurrentApp().SendNotification in the mainwindow subscriber when a
Manual/Schedule run completes with State == "Failed" and the setting is on.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 08:18:12 +03:00
5 changed files with 38 additions and 3 deletions
+2 -2
View File
@@ -101,8 +101,8 @@ These land together because both edit `domain/job.go` and `storage/store.go`.
- [x] P3.5 — Queue tests - [x] P3.5 — Queue tests
### Phase 4 — Notifications + Command browse ### Phase 4 — Notifications + Command browse
- [ ] P4.1 — Failure notifications - [x] P4.1 — Failure notifications
- [ ] P4.2 — Command Browse button - [x] P4.2 — Command Browse button
### Phase 5 — Icons ### Phase 5 — Icons
- [ ] P5.1 — Embed small icon + `IconSmall()` - [ ] P5.1 — Embed small icon + `IconSmall()`
+9
View File
@@ -170,6 +170,15 @@ func (s *Service) SetGlobalPause(paused bool) error {
return err 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 // UpdateSettings validates and persists a new application configuration. The
// loaded jobs are re-saved because the jobs directory may have changed, and log // loaded jobs are re-saved because the jobs directory may have changed, and log
// cleanup runs so a tightened retention policy takes effect immediately. // cleanup runs so a tightened retention policy takes effect immediately.
+7 -1
View File
@@ -7,7 +7,9 @@ import (
"gitea.mixdep.ru/mix/gosentry/src/domain" "gitea.mixdep.ru/mix/gosentry/src/domain"
"fyne.io/fyne/v2" "fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog" "fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
) )
@@ -27,6 +29,10 @@ func showJobDialog(w fyne.Window, title string, current job, onSave func(job)) {
commandEntry := widget.NewEntry() commandEntry := widget.NewEntry()
commandEntry.SetPlaceHolder(`C:\Program Files\App\App.exe`) commandEntry.SetPlaceHolder(`C:\Program Files\App\App.exe`)
commandEntry.SetText(current.Command) commandEntry.SetText(current.Command)
commandBrowse := widget.NewButtonWithIcon("Browse", theme.FolderOpenIcon(), func() {
chooseFile(w, commandEntry)
})
commandRow := container.NewBorder(nil, nil, nil, commandBrowse, commandEntry)
argumentsEntry := widget.NewMultiLineEntry() argumentsEntry := widget.NewMultiLineEntry()
argumentsEntry.SetPlaceHolder(`D:\Local\Jobs\Auto.ffs_batch`) argumentsEntry.SetPlaceHolder(`D:\Local\Jobs\Auto.ffs_batch`)
argumentsEntry.SetText(current.Arguments) argumentsEntry.SetText(current.Arguments)
@@ -43,7 +49,7 @@ func showJobDialog(w fyne.Window, title string, current job, onSave func(job)) {
widget.NewFormItem("Name", name), widget.NewFormItem("Name", name),
widget.NewFormItem("Folder", folderEntry), widget.NewFormItem("Folder", folderEntry),
widget.NewFormItem("Schedule", scheduleEntry), widget.NewFormItem("Schedule", scheduleEntry),
widget.NewFormItem("Command", commandEntry), widget.NewFormItem("Command", commandRow),
widget.NewFormItem("Arguments", argumentsEntry), widget.NewFormItem("Arguments", argumentsEntry),
widget.NewFormItem("", startOnly), widget.NewFormItem("", startOnly),
widget.NewFormItem("", enabled), widget.NewFormItem("", enabled),
+9
View File
@@ -72,6 +72,15 @@ func newMainView(w fyne.Window) (fyne.CanvasObject, func(time.Duration, bool)) {
fyne.Do(func() { fyne.Do(func() {
if isRecorded { if isRecorded {
events = append(events, recorded.Record) 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 { if isError {
events = append(events, newEvent(0, "Service", "Error", errOccurred.Err.Error())) events = append(events, newEvent(0, "Service", "Error", errOccurred.Err.Error()))
+11
View File
@@ -172,6 +172,17 @@ func mustParseURL(raw string) *url.URL {
return parsed return parsed
} }
func chooseFile(w fyne.Window, target *widget.Entry) {
fileDialog := dialog.NewFileOpen(func(uri fyne.URIReadCloser, err error) {
if err != nil || uri == nil {
return
}
target.SetText(uri.URI().Path())
}, w)
fileDialog.Resize(fyne.NewSize(900, 640))
fileDialog.Show()
}
func chooseFolder(w fyne.Window, target *widget.Entry) { func chooseFolder(w fyne.Window, target *widget.Entry) {
folderDialog := dialog.NewFolderOpen(func(uri fyne.ListableURI, err error) { folderDialog := dialog.NewFolderOpen(func(uri fyne.ListableURI, err error) {
if err != nil || uri == nil { if err != nil || uri == nil {