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
88 lines
2.0 KiB
Rust
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 },
|
|
}
|