fix(native): toast coalescing, real reply target, Focus Assist mount query
Build Lotus Chat Desktop / prepare (push) Canceled after 0s
Build Lotus Chat Desktop / build-windows (push) Canceled after 0s
Build Lotus Chat Desktop / build-linux (push) Canceled after 0s
Build Lotus Chat Desktop / build-arch (push) Canceled after 0s
Build Lotus Chat Desktop / update-manifest (push) Canceled after 0s

- Rich toasts set a WinRT Tag (hash of the web notification tag, since
  room:thread tags exceed the 64-char limit) in a fixed Group, so a newer
  toast for the same room/thread replaces the older one instead of
  stacking; the replaced toast's keep-alive entry is dropped (#16).
- The bridge passes tag, roomId and threadId separately. The reply target
  is the real room id (+ thread), never the coalescing tag, which was
  `room:thread` for threads and `lotus-invites` for invites, so those
  replies failed silently. Toasts with no room id (invites) get no reply
  box (#17).
- New `get_focus_assist` command serves the latest Focus Assist poll so
  the web hook can hydrate on mount; the setup-time first emit was lost
  before the page listened (#15).
- Bump cinny to 3b6de2fd (the matching web-side hooks).

Closes #15, #16, #17

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
Lotus CI
2026-09-23 19:33:30 -04:00
co-authored by Claude Opus 5.5
parent 7716307e54
commit c1ae01bf41
4 changed files with 107 additions and 26 deletions
+22 -2
View File
@@ -11,14 +11,33 @@
//! ~5 seconds. We prefer a robust poll over hooking shell events — the poll is
//! trivial to reason about and a 5s cadence is more than responsive enough for a
//! notification-suppression hint. We emit **only on a boolean transition**, so the
//! web side gets one event per change rather than a steady heartbeat; the first
//! read always emits so the frontend learns the initial state.
//! web side gets one event per change rather than a steady heartbeat. The latest
//! reading is also kept in [`LAST_STATE`] and served by [`get_focus_assist`]: the
//! first read happens during app setup, before the page has loaded, so that
//! event is lost, and the web atom resets on every reload anyway. The web hook
//! queries it on mount (Gitea cinny-desktop #15).
//!
//! Other platforms are a no-op: there's no equivalent cross-platform signal, and
//! the web hook stays unconditional so nothing there needs guarding.
use std::sync::atomic::{AtomicU8, Ordering};
use tauri::AppHandle;
/// Latest poll result: 0 = not read yet (or not Windows), 1 = inactive, 2 = active.
static LAST_STATE: AtomicU8 = AtomicU8::new(0);
/// Return the latest Focus Assist reading so the web side can hydrate
/// `focusAssistActiveAtom` on mount. `None` until the first successful poll, and
/// always `None` off Windows.
#[tauri::command]
pub fn get_focus_assist() -> Option<bool> {
match LAST_STATE.load(Ordering::Relaxed) {
1 => Some(false),
2 => Some(true),
_ => None,
}
}
/// Payload for the `focus-assist-changed` DOM event (`{ active: bool }`).
#[cfg(target_os = "windows")]
#[derive(serde::Serialize)]
@@ -81,6 +100,7 @@ fn watch_focus_assist(app: AppHandle) {
|| state == QUNS_PRESENTATION_MODE
|| state == QUNS_RUNNING_D3D_FULL_SCREEN
|| state == QUNS_BUSY;
LAST_STATE.store(if active { 2 } else { 1 }, Ordering::Relaxed);
if last != Some(active) {
last = Some(active);
super::emit_to_web(