package main import ( _ "embed" "fmt" "net/http" "strconv" "github.com/kami/maven/internal/ipc" ) //go:embed notifications.html var notificationsHTML string var notificationsTmpl = parsePage("notifications", notificationsHTML, nil) // deliveryRow is one outbox line, with every timestamp already formatted so // the template holds no date logic — same shape as taskRow. type deliveryRow struct { Kind string Target string Channel string Status string Created string Completed string } func deliveryRows(as []ipc.DeliveryAttempt) []deliveryRow { out := make([]deliveryRow, 0, len(as)) for _, a := range as { target := a.Rule if target == "" && a.ReminderID != 0 { target = "reminder #" + strconv.FormatInt(a.ReminderID, 10) } row := deliveryRow{ Kind: a.Kind, Target: target, Channel: a.Channel, Status: a.Status, Created: a.Created.Format("02.01 15:04"), } if a.Completed != nil { row.Completed = a.Completed.Format("15:04") } out = append(out, row) } return out } func handleNotifications(w http.ResponseWriter, r *http.Request, core ipc.CoreAPI) { if !requireCore(w, r, core, "notifications") { return } ctx := r.Context() nudges, err := core.RecentNudges(ctx, 50) if err != nil { writeProblem(w, r, http.StatusBadGateway, problemCoreReadFailed, "notifications unavailable", fmt.Errorf("read recent nudges: %w", err)) return } // The outbox, on the page that already answers "what did she send". // A failed or dropped attempt is why she went quiet, and until now it was // recorded and unreadable (Vikunja #390). Filter with ?status=dropped. status := r.URL.Query().Get("status") attempts, err := core.DeliveryAttempts(ctx, status, 50) if err != nil { // The nudge list is still worth showing, so this is a note on the page // rather than a dead page. logProblem(r, http.StatusOK, problemCoreReadFailed, "delivery attempts unavailable", fmt.Errorf("read delivery attempts: %w", err)) } renderPage(w, notificationsTmpl, map[string]any{ "Nudges": nudges, "Attempts": deliveryRows(attempts), "Status": status, }) }