implemented progressive message fetching on scroll
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,9 +67,12 @@ async fn list_messages(
|
||||
));
|
||||
}
|
||||
|
||||
let limit: i32 = query.limit.unwrap_or(30).abs().min(80);
|
||||
|
||||
let messages = sqlx::query_as::<_, MessageRow>(
|
||||
r#"
|
||||
SELECT
|
||||
m.uuid,
|
||||
u.username AS sender,
|
||||
r.uuid AS room,
|
||||
m.message_type,
|
||||
@@ -71,23 +82,27 @@ async fn list_messages(
|
||||
JOIN user_ u ON u.id = m.sender
|
||||
JOIN room_ r ON r.id = m.room
|
||||
WHERE m.room = $1
|
||||
ORDER BY m.id
|
||||
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,
|
||||
|
||||
Reference in New Issue
Block a user