feat: unread badge for automations#2656
Conversation
Greptile SummaryThis PR adds an unread badge to the Automations sidebar item that counts finished automation runs since the user last viewed automations, and a right-click context menu to mark all as read. The
Confidence Score: 4/5Safe to merge after adding The unread badge logic correctly excludes apps/emdash-desktop/src/shared/core/automations/automation-run.ts — the AUTOMATION_SKIP_CODES_EXCLUDED_FROM_NOTIFICATIONS constant needs trigger_changed added.
|
| Filename | Overview |
|---|---|
| apps/emdash-desktop/src/shared/core/automations/automation-run.ts | Adds terminal status set, type guard, and skip-code exclusion list; trigger_changed is missing from the exclusion list causing spurious badge increments. |
| apps/emdash-desktop/src/main/core/automations/automations-service.ts | Adds countUnreadFinishedRuns, getNotificationsBaselineTimestamp, and properly wires terminal-state emission; isExcludedFromUnreadNotifications name is inverted and uses sql.raw with manual quoting. |
| apps/emdash-desktop/src/renderer/features/automations/use-automation-unread-count.ts | New hook that wires unread count query with baseline initialization and terminal-state invalidation; logic is sound. |
| apps/emdash-desktop/src/renderer/features/sidebar/automations-sidebar-item.tsx | New sidebar item with unread badge, context menu, and error-handling for mark-as-read; implementation looks correct. |
| apps/emdash-desktop/src/main/core/settings/schema.ts | Adds automationsLastReadAt: z.number() field to interface settings schema; clean addition. |
| apps/emdash-desktop/src/main/core/settings/settings-registry.ts | Adds default value of 0 for the new automationsLastReadAt field; correct. |
| apps/emdash-desktop/src/main/core/automations/controller.ts | Exposes the two new RPC methods; straightforward delegation. |
| apps/emdash-desktop/src/renderer/features/sidebar/left-sidebar.tsx | Replaces inline automations button with the new AutomationsSidebarItem component; clean refactor. |
Sequence Diagram
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Scheduler as AutomationScheduler
participant Service as AutomationsService
participant Events as IPC Events
participant Hook as useAutomationUnreadCount
participant DB as SQLite (automationRuns)
participant Settings as AppSettings
Note over Service,DB: New user first mount
Hook->>Service: getNotificationsBaselineTimestamp()
Service->>DB: SELECT MAX(finishedAt) WHERE terminal
DB-->>Service: baseline ts
Service-->>Hook: baseline ts
Hook->>Settings: updateAsync automationsLastReadAt baseline
Note over Scheduler,Events: Run reaches terminal state
Scheduler->>Service: onRunStep(run)
Service->>Events: emit automationRunChangedChannel run
Events-->>Hook: run
Hook->>Hook: isTerminalStatus invalidateQueries
Hook->>Service: countUnreadFinishedRuns(lastReadAt)
Service->>DB: "SELECT count WHERE terminal AND not-excluded AND finishedAt > lastReadAt"
DB-->>Service: n
Service-->>Hook: n
Hook-->>AutomationsSidebarItem: unreadCount n
Note over Hook,Settings: User clicks Mark all as read
AutomationsSidebarItem->>Hook: markAsRead()
Hook->>Settings: updateAsync automationsLastReadAt Date.now
Hook->>Hook: invalidateQueries refetch count 0
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant Scheduler as AutomationScheduler
participant Service as AutomationsService
participant Events as IPC Events
participant Hook as useAutomationUnreadCount
participant DB as SQLite (automationRuns)
participant Settings as AppSettings
Note over Service,DB: New user first mount
Hook->>Service: getNotificationsBaselineTimestamp()
Service->>DB: SELECT MAX(finishedAt) WHERE terminal
DB-->>Service: baseline ts
Service-->>Hook: baseline ts
Hook->>Settings: updateAsync automationsLastReadAt baseline
Note over Scheduler,Events: Run reaches terminal state
Scheduler->>Service: onRunStep(run)
Service->>Events: emit automationRunChangedChannel run
Events-->>Hook: run
Hook->>Hook: isTerminalStatus invalidateQueries
Hook->>Service: countUnreadFinishedRuns(lastReadAt)
Service->>DB: "SELECT count WHERE terminal AND not-excluded AND finishedAt > lastReadAt"
DB-->>Service: n
Service-->>Hook: n
Hook-->>AutomationsSidebarItem: unreadCount n
Note over Hook,Settings: User clicks Mark all as read
AutomationsSidebarItem->>Hook: markAsRead()
Hook->>Settings: updateAsync automationsLastReadAt Date.now
Hook->>Hook: invalidateQueries refetch count 0
Reviews (2): Last reviewed commit: "fix(automations): refine unread run badg..." | Re-trigger Greptile
Description
Screenshot/Recording (if applicable)
https://streamable.com/y1btel
Checklist
messages and, when possible, the PR title