feat(toast): wire ToastVariant::Warning for daily-challenge expiry

Adds the first in-engine consumer of `ToastVariant::Warning` — a 4s
amber-bordered toast that fires once per daily-challenge date when the
player is within 30 minutes of UTC midnight reset and hasn't yet
completed today's challenge.

Mirrors the v0.21.2 `ToastVariant::Error` wiring: a domain-event
message (`WarningToastEvent(String)`) crosses the plugin boundary;
`animation_plugin::handle_warning_toast` reads it and spawns the
fire-and-forget toast. Suppression is decided by a pure helper
(`compute_expiry_warning_minutes`) that's exhaustively covered by 7
unit tests + 1 in-Bevy idempotence test.

After this lands, every `ToastVariant` (Info, Warning, Error,
Celebration) has at least one real driver — closing the "is this enum
scaffolding or load-bearing?" ambiguity that's been latent since the
variant was introduced.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
funman300
2026-05-08 14:22:58 -07:00
parent 12fba2157a
commit 279e23d0af
3 changed files with 259 additions and 4 deletions
+15
View File
@@ -212,6 +212,21 @@ pub struct SyncCompleteEvent(pub Result<SyncResponse, String>);
#[derive(Message, Debug, Clone)]
pub struct InfoToastEvent(pub String);
/// Generic warning toast message. Spawns a fire-and-forget
/// [`ToastVariant::Warning`](crate::animation_plugin::ToastVariant) toast.
///
/// Distinct from [`InfoToastEvent`] in two ways:
/// 1. **Variant.** Warning carries the design-system warning border accent,
/// not the neutral info accent — so the player can distinguish "you might
/// want to act" from "here's some neutral information".
/// 2. **No queue.** Warnings are alerts, not a stream. Each event spawns its
/// own toast immediately rather than waiting for the info queue to drain.
///
/// First in-engine driver: daily-challenge expiry warning fired by
/// `daily_challenge_plugin` when < 30 min from UTC midnight reset.
#[derive(Message, Debug, Clone)]
pub struct WarningToastEvent(pub String);
/// Fired by `ProgressPlugin` immediately after awarding XP for a win so the
/// animation layer can display a "+N XP" toast alongside the win cascade.
#[derive(Message, Debug, Clone, Copy)]