Files
rustification-client/src-tauri/src/models.rs
MDietermann 90c2c1e3e0 feat(realtime): replace polling with a Rust-owned WebSocket connection
MessageList polled every three seconds and ChatPage bumped a refreshKey after
each send. Both are gone; messages now arrive over a socket.

The connection lives in the Tauri Rust layer rather than in the WebView. Two
reasons: the handshake can carry a real Authorization header, which is the only
channel that nginx, Apache and Caddy do not write to their access logs by
default, and tungstenite surfaces the full HTTP response on a rejected
handshake. The browser API is required by spec to collapse every failure into
close code 1006, so "token expired" and "server down" would be
indistinguishable — which matters once the client holds connections to several
servers and has to show a meaningful state for each.

The socket only carries {conversation, seq}. Rust does the backfill over REST
and hands resolved entities to the frontend, so the UI keeps no cursor logic:
it replaces by id and sorts by seq.

The client now supplies the message id. Without it an optimistically rendered
message cannot be matched against the copy that arrives over the realtime
channel and shows up twice; it also makes a retry after an unclear network
error a no-op instead of a duplicate.

icons/icon.ico is generated from the existing icon.png — tauri-build requires
it on Windows, and without it the crate does not build there at all.

Not yet done, marked as TODO in the code: token refresh (the connection ends
at expiry with 4001), the server list replacing BASE_URL, optimistic rendering,
and the DM-versus-channel rule for background fetching.
2026-08-14 18:26:28 +02:00

48 lines
1.1 KiB
Rust

use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone)]
pub struct User {
pub id: String,
pub email: String,
pub username: String,
pub display_name: String,
pub avatar_url: Option<String>,
pub bio: Option<String>,
}
#[derive(Serialize, Deserialize)]
pub struct Workspace {
pub id: String,
pub name: String,
pub slug: String,
pub owner_id: String,
}
#[derive(Serialize, Deserialize)]
pub struct Conversation {
pub id: String,
pub workspace_id: Option<String>,
pub kind: String,
pub name: Option<String>,
pub is_public: bool,
pub created_by: String,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Message {
pub id: String,
pub conversation_id: String,
pub user_id: String,
pub content: String,
/// Sort key within the conversation. Display order follows this, never
/// `created_at` — that is transaction start time on the server, so it is
/// neither unique nor commit-ordered.
pub seq: i64,
pub created_at: String,
}
#[derive(Deserialize)]
pub struct AuthTokenResponse {
pub token: String,
}