From 7732fbec329ddb38d5c361d19f4a3bfa91e2cd57 Mon Sep 17 00:00:00 2001 From: eiiko6 Date: Mon, 5 Jan 2026 19:34:29 +0100 Subject: [PATCH] implemented progressive message fetching on scroll --- db/init.sql | 2 +- db/mock_data.sql | 14 +++++----- src/routes/messages.rs | 60 ++++++++++++++++++++++++++++-------------- 3 files changed, 48 insertions(+), 28 deletions(-) diff --git a/db/init.sql b/db/init.sql index 12a475d..e8bacf5 100644 --- a/db/init.sql +++ b/db/init.sql @@ -46,7 +46,7 @@ CREATE TABLE IF NOT EXISTS room_invite_ ( CREATE TABLE IF NOT EXISTS message_ ( id BIGSERIAL PRIMARY KEY, - -- uuid UUID NOT NULL, + uuid UUID NOT NULL, sender INT REFERENCES user_(id) NOT NULL, room INT REFERENCES room_(id) NOT NULL, message_type VARCHAR(32) NOT NULL, diff --git a/db/mock_data.sql b/db/mock_data.sql index 927aa33..c5f738c 100644 --- a/db/mock_data.sql +++ b/db/mock_data.sql @@ -14,13 +14,13 @@ INSERT INTO membership_ (user_id, room) VALUES (3, 1), -- Carol in General Discussion (1, 3); -- Alice in Random Memes -INSERT INTO message_ (sender, room, message_type, content) VALUES -(1, 1, 'text', 'Hey everyone, hows it going?'), -(2, 1, 'text', 'All good! Just trying to get through some work.'), -(3, 1, 'text', 'Hello! How are you guys?'), -(2, 2, 'text', 'Anyone seen the new tech updates?'), -(1, 3, 'image', 'Heres a funny meme I found!'), -(3, 1, 'text', 'I love how active this room is!'); +INSERT INTO message_ (sender, room, message_type, content, uuid) VALUES +(1, 1, 'text', 'Hey everyone, hows it going?', '3ae85002-8a82-479f-b1c9-6faa3dceb2f3'), +(2, 1, 'text', 'All good! Just trying to get through some work.', '8e60aa27-9eef-4f1c-a913-47ac6ea1229b'), +(3, 1, 'text', 'Hello! How are you guys?', 'f2b688f8-6678-465c-8092-9636a9ae2f16'), +(2, 2, 'text', 'Anyone seen the new tech updates?', '20c6b5d4-c8b1-4afe-844c-339e128fc344'), +(1, 3, 'image', 'Heres a funny meme I found!', '7dd79706-9187-47a5-b4f0-86e07cbb4564'), +(3, 1, 'text', 'I love how active this room is!', '9024823f-1b0c-436b-b81d-08dc06ac34df'); INSERT INTO friendship_ (user_first, user_second) VALUES (1, 3), -- Alice and Carol diff --git a/src/routes/messages.rs b/src/routes/messages.rs index d098ba2..58bfd00 100644 --- a/src/routes/messages.rs +++ b/src/routes/messages.rs @@ -1,6 +1,6 @@ use axum::{ Extension, Json, Router, - extract::Path, + extract::{Path, Query}, http::{HeaderMap, StatusCode}, routing::{get, post}, }; @@ -15,6 +15,7 @@ use crate::{ #[derive(sqlx::FromRow, serde::Serialize, Debug)] pub struct MessageRow { + pub uuid: Uuid, pub sender: String, pub message_type: String, pub content: String, @@ -36,6 +37,12 @@ pub struct NewMessagePayload { pub content: String, } +#[derive(serde::Deserialize)] +struct MessageFetchQuery { + limit: Option, + before: Option, +} + pub fn routes() -> Router { Router::new() .route("/messages/{room_uuid}", get(list_messages)) @@ -44,6 +51,7 @@ pub fn routes() -> Router { async fn list_messages( Path(room_uuid): Path, + Query(query): Query, headers: HeaderMap, Extension(db): Extension, ) -> Result>, (StatusCode, String)> { @@ -59,35 +67,42 @@ async fn list_messages( )); } + let limit: i32 = query.limit.unwrap_or(30).abs().min(80); + let messages = sqlx::query_as::<_, MessageRow>( r#" - SELECT - u.username AS sender, - r.uuid AS room, - m.message_type, - m.content, - m.sent_at - FROM message_ m - JOIN user_ u ON u.id = m.sender - JOIN room_ r ON r.id = m.room - WHERE m.room = $1 - ORDER BY m.id - "#, + SELECT + m.uuid, + u.username AS sender, + r.uuid AS room, + m.message_type, + m.content, + m.sent_at + FROM message_ m + JOIN user_ u ON u.id = m.sender + JOIN room_ r ON r.id = m.room + WHERE m.room = $1 + AND ($2::uuid IS NULL OR m.id < (SELECT id FROM message_ WHERE uuid = $2)) + ORDER BY m.id DESC + LIMIT $3 + "#, ) .bind(room_id) + .bind(query.before) + .bind(limit) .fetch_all(&db) .await - .map_err(|_| { + .map_err(|e| { ( StatusCode::INTERNAL_SERVER_ERROR, - "Failed to list messages".into(), + format!("Failed to list messages: {e}"), ) })?; - let messages: Vec = messages + let mut messages: Vec = messages .into_iter() .map(|m| Message { - uuid: uuid::Uuid::now_v7(), + uuid: m.uuid, sender: m.sender, message_type: m.message_type, content: m.content, @@ -95,6 +110,8 @@ async fn list_messages( }) .collect(); + messages.reverse(); + Ok(Json(messages)) } @@ -117,14 +134,17 @@ async fn create_message( )); } + let uuid = Uuid::now_v7(); + let sent_at: chrono::NaiveDateTime = sqlx::query_scalar( - "INSERT INTO message_ (sender, room, message_type, content) - VALUES ($1, $2, $3, $4) RETURNING sent_at", + "INSERT INTO message_ (sender, room, message_type, content, uuid) + VALUES ($1, $2, $3, $4, $5) RETURNING sent_at", ) .bind(user_id) .bind(room_id) .bind(&payload.message_type) .bind(&payload.content) + .bind(&uuid) .fetch_one(&db) .await .map_err(|_| (StatusCode::BAD_REQUEST, "Could not create message".into()))?; @@ -132,7 +152,7 @@ async fn create_message( let sender_name = username_from_uuid(&db, claims.sub).await?; let message = Message { - uuid: uuid::Uuid::now_v7(), + uuid: uuid, sender: sender_name, message_type: payload.message_type, content: payload.content,