Originally created by: kristofferR
Windows sibling of [#174] parts 1–2 (Ref [#174]). This issue also establishes the Windows toast foundation that quick reply builds on.
Why a new toast path: notify-rust's Windows backend (tauri-winrt-notification) supports buttons but not text input, toast tag/group, or history removal — everything in scope needs raw WinRT. The windows crate with the needed features (UI_Notifications, Data_Xml_Dom, Foundation_Collections, Win32_Foundation) is already in the tree transitively via tauri-plugin-notification → tauri-winrt-notification, so adding it as a direct cfg(windows) dependency compiles zero new code.
Design
src-tauri/src/windows/ (mod.rs + toast.rs), declared #[cfg(any(test, target_os = "windows"))] like the existing linux module pattern so pure helpers unit-test on Linux CI. show_message_notification becomes a clean three-way dispatch: macOS → deliver_notification_macos, Windows → windows::toast::deliver_notification_windows, Linux → the notify-rust thread path (this also delivers the unix/windows split [#167] assumed; Ref [#167]).CreateToastNotifier(aumid) → attach Activated/Dismissed/Failed handlers capturing the AppHandle → Show(). Keep each ToastNotification in a capped (~256) static map keyed by id so Action Center clicks after the banner times out still fire — a real improvement over today's notify-rust path, which drops them. Avatar temp files stop being deleted per-notification and rely on the existing 10-minute sweep, like macOS.build_toast_xml(ToastSpec) -> String, XML-escaping is load-bearing — title/body are page-controlled): <toast launch="action=open&id=<hex>" activationType="foreground">, avatar as placement="appLogoOverride" hint-crop="circle" (nicer than today's inline image), <audio src="ms-winsoundevent:Notification.Default"/> when sound on, <audio silent="true"/> otherwise. Sync alerts: plain toast, no group.SetTag(hex id) + SetGroup(thread digits) when a validated thread path exists. Tag/group over 16 chars silently break toasts pre-Windows 10 1903 — hence hex-encoded ids and gating on build ≥ 18362 via windows-version (also already in-tree). Unique tag per notification: purely additive stacking under one Action Center group, mirroring [#174].carrier:thread-viewed signal exactly (thread-nav.ts pathname+focus watcher). One shared listen_any in lib.rs cfg-dispatches: macOS clear_delivered_for_thread, Windows clear_thread_group(thread_id) = ToastNotificationHistory.RemoveGroupWithId(group, aumid) + evict from the keep-alive map. Whichever of [#174]/this lands first ships the page half + listener.io.github.kristofferr.carrier) on the Start-menu/Desktop shortcuts, matching what the code passes today. The portable zip has no shortcut — windows::toast::init(app) registers HKCU\SOFTWARE\Classes\AppUserModelId\<aumid> (DisplayName "Carrier" + IconUri) at startup, idempotent, no admin — this makes portable-zip toasts show a name/icon at all. Post-quit toast activation (needs an INotificationActivationCallback COM server + ToastActivatorCLSID) is out of scope; documented limitation: clicks on toasts after app exit do nothing.Edge cases: Focus Assist → toast lands in Action Center, later click still fires (keep-alive map); app notifications disabled → log ToastNotifier.Setting() once, best-effort; pre-1903 → no tag/group, otherwise unchanged.
Testing. Unit (Linux CI): XML building/escaping, parse_activation_args, hex tag/group derivation, keep-alive map eviction. Manual (Win10 + Win11): three messages one thread → one stacked Action Center group; open the thread in Carrier → group clears (including toasts from a previous run — history is OS-side); portable exe shows name/icon; body click after banner timeout opens the thread.
Part of [#187].
Originally posted by: kristofferR
Implemented in PR [#233] and manually verified and finalized in PR [#247]. Native WinRT toasts group by conversation, clear on view and after replies, remain activatable from Action Center after the banner expires, show sender avatars, and handle the notification identity migration safely. Closing as complete.
Related
Tickets:
#233Tickets:
#247Ticket changed by: kristofferR