Files
quicproquo/crates/quicprochat-sdk/src/events.rs
Christian Nennemann e4c5868b31 feat: add client auto-reconnect, heartbeat, and connection status UI
RPC layer (quicprochat-rpc):
- RpcClient now uses tokio::sync::Mutex<Connection> for safe reconnection
- Auto-reconnect with exponential backoff + jitter on retriable errors
- QUIC-level keepalive via quinn TransportConfig
- subscribe_push() returns Option<PushFrame> with None sentinel on break
- RpcError::is_retriable() classifies transient vs permanent errors
- ConnectionState enum (Connected/Reconnecting/Disconnected) with Display
- Configurable max_retries, base_delay, max_backoff, keepalive_secs

SDK layer (quicprochat-sdk):
- QpqClient wraps RpcClient in Arc for safe heartbeat task sharing
- start_heartbeat() spawns background task checking connection every 30s
- connection_state() exposes RPC-layer state to UI
- Reconnecting event added to ClientEvent enum
- disconnect() aborts heartbeat before closing connection

Client UI (quicprochat-client):
- TUI status bar shows Connected/Reconnecting.../Offline with color
- TUI handles Reconnecting event with attempt count display
- REPL event listener prints connection state changes
- REPL /status shows connection state instead of bool
- Both TUI and REPL call start_heartbeat() on startup
2026-03-21 19:14:06 +01:00

88 lines
2.0 KiB
Rust

//! Client event system — real-time notifications from the SDK.
/// Events emitted by the SDK to the UI layer.
#[derive(Debug, Clone)]
pub enum ClientEvent {
/// Successfully connected to the server.
Connected,
/// Disconnected from the server.
Disconnected { reason: String },
/// Connection lost, attempting to reconnect.
Reconnecting { attempt: u32 },
/// Registration succeeded.
Registered { username: String },
/// Login succeeded.
LoggedIn { username: String },
/// Logged out.
LoggedOut { username: String },
/// Authentication succeeded.
Authenticated { username: String },
/// A new message was received in a conversation.
MessageReceived {
conversation_id: [u8; 16],
sender_key: Vec<u8>,
sender_name: Option<String>,
body: String,
timestamp_ms: u64,
},
/// A message was sent successfully.
MessageSent {
conversation_id: [u8; 16],
seq: u64,
},
/// A new conversation was created or discovered.
ConversationCreated {
conversation_id: [u8; 16],
display_name: String,
},
/// A member was added to a group conversation.
MemberAdded {
conversation_id: [u8; 16],
member_key: Vec<u8>,
},
/// A member was removed from a group conversation.
MemberRemoved {
conversation_id: [u8; 16],
member_key: Vec<u8>,
},
/// Server-push event received.
PushEvent {
event_type: u16,
payload: Vec<u8>,
},
/// A message was queued in the offline outbox (send failed or disconnected).
MessageQueued {
outbox_id: i64,
conversation_id: [u8; 16],
},
/// Outbox flush completed after reconnect.
OutboxFlushed {
sent: usize,
failed: usize,
},
/// Gap detected in message sequence numbers.
MessageGap {
conversation_id: [u8; 16],
expected_seq: u64,
received_seq: u64,
},
/// An error occurred in the background.
Error { message: String },
}