implemented progressive message fetching on scroll

This commit is contained in:
2026-01-05 19:34:29 +01:00
parent 79ce9f7db2
commit 7732fbec32
3 changed files with 48 additions and 28 deletions

View File

@@ -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,

View File

@@ -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

View File

@@ -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<i32>,
before: Option<Uuid>,
}
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<Uuid>,
Query(query): Query<MessageFetchQuery>,
headers: HeaderMap,
Extension(db): Extension<PgPool>,
) -> Result<Json<Vec<Message>>, (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<Message> = messages
let mut messages: Vec<Message> = 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,