Skip to content

feat(alerts): sync read-state from cloud (alert-read / alerts-read-all)#26

Merged
proofofprints merged 1 commit into
mainfrom
feat/cloud-alert-read-sync
Jun 13, 2026
Merged

feat(alerts): sync read-state from cloud (alert-read / alerts-read-all)#26
proofofprints merged 1 commit into
mainfrom
feat/cloud-alert-read-sync

Conversation

@proofofprints

Copy link
Copy Markdown
Collaborator

Makes the desktop react to the new cloud WebSocket broadcasts so an alert acknowledged on the web portal or mobile app shows as read here too.

What

Two new inbound message types on the existing instance WebSocket (same { type, data } shape as command):

  • alert-read { alertId, instanceId, ruleName, minerId, timestamp, acknowledgedAt } → marks the matching local alert acknowledged.
  • alerts-read-all { instanceId, acknowledgedAt } → marks ALL local alerts acknowledged.

How

  • src-tauri/src/cloud/ws.rshandle_message gains both cases, guarded by instanceId (lenient: only rejects when both the payload's and our own instance id are known and differ). On a local change it emits the existing alerts-updated event.
  • src-tauri/src/commands/alerts.rsmark_alert_read / mark_all_alerts_read flip the existing AlertEvent.acknowledged flag in history.json. Read-state already existed (acknowledge_alert), so no new model was needed.
  • src/pages/Alerts.tsx — now listens for alerts-updated to reload the history table (the badge via AlertContext already did).

Matching choice (single alert)

Match on (ruleName, minerId, timestamp), not the cloud alertId. The app does not store the cloud id today — push_alert discards the POST /alert response and alerts are delivered through an offline queue, so the id only exists post-push. Threading it back would mean changing push_alert's contract and writing back into the capped history.json on every successful push. The local AlertEvent already carries the three echoed fields (rule_name, miner_ip == the cloud's minerId for both ASIC IP and mobile device_id, and timestamp), and the cloud includes them in the payload for exactly this purpose. Timestamp comparison tolerates RFC-3339 format differences (Z vs +00:00).

Tests / build

  • New unit tests for the matcher (alert_matches, timestamps_match) — cargo test ✅ (7 passed).
  • npx tsc --noEmit ✅ · npm run build ✅.

Note

Per request, not merging — opening for review. Runtime confirmation (ack on phone → shows read on desktop) is best verified once the cloud broadcast is live against a logged-in instance.

React to two new inbound cloud WebSocket events so an alert acknowledged
on the web portal or mobile app shows as read on the desktop too.

- cloud/ws.rs: handle 'alert-read' and 'alerts-read-all' on the existing
  socket. Guarded by instanceId (lenient when unknown). Emits the existing
  'alerts-updated' event so the badge + Alerts table refresh.
- commands/alerts.rs: mark_alert_read / mark_all_alerts_read mark local
  AlertEvent.acknowledged. Single-alert match is on (ruleName, minerId,
  timestamp) — the app does not store the cloud alertId (push_alert discards
  the response and alerts ship via an offline queue), and the local record
  already carries those three fields (miner_ip == cloud minerId). Timestamp
  comparison tolerates RFC-3339 format differences. Unit-tested.
- Alerts.tsx: listen for 'alerts-updated' to reload the history table.

Matching rationale: storing the cloud id would require changing push_alert's
contract and writing back into the capped history.json on every push;
composite matching reuses fields the cloud echoes specifically for this.
@proofofprints proofofprints merged commit e78bc43 into main Jun 13, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant