Toasts: non-blocking notifications replace MessageBoxes + log-only failures (TASK 24)

This commit is contained in:
2026-08-23 12:25:04 -07:00
parent 809c066184
commit b400ab59c5
12 changed files with 369 additions and 50 deletions
+38
View File
@@ -1058,6 +1058,44 @@ The tasks below are ordered by dependency and risk. Each task builds on the prev
---
## TASK 24 — Toast notifications (in-app, non-blocking)
### Status: ✅ Done (shipped 2026-08-23, branch `task24-notifications`)
Replace blocking MessageBoxes with in-window toasts; promote actionable log-only failures.
### Requirements:
1. Library: **Notification.Wpf 11.0.0** (Platonenkov fork of Federerer/Notifications.Wpf) — MIT, active, targets net8.0-windows. Notice #10 added to `THIRD-PARTY-NOTICES.txt`.
2. Placement: bottom-right above the footer (`NotificationArea x:Name="ToastArea"`, last child of MainWindow's root grid → topmost z-order, `Grid.RowSpan=4`, `Margin="0,0,12,96"`, outside the preview Viewbox), max 4 stacked.
3. Behavior: Info ~4s / Success ~4s / Warning ~8s auto-dismiss; **Error sticky until dismissed**.
4. Styling: dark card tints matching the health-banner language (slate `#3a3b52` info, green `#1d5c38` success, amber `#b8860b` warning, dark-red `#8f1f1f` error); corner radius 8; keep-visible-on-hover.
5. Threading: all Show calls marshal via a Dispatcher captured at construction (encoder/pump events fire off-thread).
### Migrations (MessageBox → toast):
- Webcam acquire failure ×2 (`AddWebcamToStagedSceneAsync`, `SwapWebcamIdentityAsync`) → Warning
- Image read failure (`PickImageBytes`) → Warning
- Sign-in unsuccessful → Warning; sign-in failed → Error
### Promotions (log-only → toast, each keeps its AppLog line):
- Go-live prep ×3: reusable stream unavailable / broadcast insert failed / prep exception → Error
- Frame-pump death while live (`OnFramePumpFailed`) → Error
- Mic missing at startup (`StartMicCaptureAsync`) → Warning (once)
- Premium lapse at startup re-validation → Warning
- Saved-session refresh failure (signed out notice) → Info
Deliberately left alone: offline license re-validation skip (log-only — would spam every launch); SocialsDialog slot-delete YesNo confirm (stays modal).
### Tests:
- Unit: pure severity→request mapping (`NotificationServiceTests`) — area routing, lifetimes, tints.
- Integration (the ONE): real window-hosted `ToastArea` + real service — Info auto-dismisses, Error sticks (`NotificationAreaIntegrationTests`, RealApp collection).
### Library facts (cost a hunt — see also ai.md):
- Area routing matches the area's XAML `Name` against the request's `AreaName`; unknown name silently drops.
- `NeverExpires()` = `ExpirationTime = TimeSpan.MaxValue` (not null); `NotificationColor.ToHex()` returns `#AARRGGBB`.
- Overlay-window shutdown caveat avoided entirely by using the in-window area mode.
---
## Backlog (future versions)
1. v1.1 — Stream Deck / Loupedeck integration (requires hotkey foundation from TASK 20)