diff --git a/app/public/messages.js b/app/public/messages.js index 01d3a4d..db9fc1c 100644 --- a/app/public/messages.js +++ b/app/public/messages.js @@ -4,7 +4,8 @@ const STATE = { observer: null, is_loading: false, stream_element: null, - first_message_time: 0, + last_message_time: 0, + last_read_receipt_load: 0, }; function create_streamer(chat_id, hook_element) { @@ -24,6 +25,7 @@ function create_streamer(chat_id, hook_element) { ); STATE.observer.observe(hook_element); + read_receipt(); } function load_messages() { @@ -35,7 +37,7 @@ function load_messages() { STATE.id += 1; fetch( - `/chats/_templates/chat/${STATE.chat_id}/messages/before/${STATE.first_message_time}?use_id=${STATE.id}`, + `/chats/_templates/chat/${STATE.chat_id}/messages/before/${STATE.last_message_time}?use_id=${STATE.id}`, ) .then((res) => res.text()) .then((res) => { @@ -44,7 +46,7 @@ function load_messages() { }, 2000); STATE.stream_element.innerHTML += res; - STATE.first_message_time = Number.parseInt( + STATE.last_message_time = Number.parseInt( document .getElementById(`msgs_data_${STATE.id}`) .getAttribute("data-first-message-time"), @@ -78,7 +80,6 @@ function render_message(id) { STATE.is_loading = false; STATE.stream_element.innerHTML = `${res}${STATE.stream_element.innerHTML}`; mark_message_read(); - read_receipt(); STATE.stream_element.scrollTo(0, STATE.stream_element.scrollHeight); }); } @@ -101,6 +102,10 @@ function sock_con() { if (document.getElementById(`message_${msg.body}`)) { document.getElementById(`message_${msg.body}`).remove(); } + } else if (msg.method === "ReadReceipt") { + setTimeout(() => { + read_receipt(); + }, 1500); } }); } @@ -141,6 +146,13 @@ function mark_message_read() { } function read_receipt() { + if (new Date().getTime() - STATE.last_read_receipt_load < 150) { + console.log("too soon"); + return; + } + + STATE.last_read_receipt_load = new Date().getTime(); + if (document.getElementById("delivered_read_status")) { document.getElementById("delivered_read_status").remove(); } @@ -151,3 +163,19 @@ function read_receipt() { STATE.stream_element.innerHTML = `${res}${STATE.stream_element.innerHTML}`; }); } + +function delete_message(id) { + if (!confirm("Are you sure you want to do this?")) { + return; + } + + fetch(`/api/v1/messages/${id}`, { + method: "DELETE", + }) + .then((res) => res.json()) + .then((res) => { + if (!res.ok) { + show_message(res.message, res.ok); + } + }); +} diff --git a/app/public/style.css b/app/public/style.css index 1fa3b85..89ea5db 100644 --- a/app/public/style.css +++ b/app/public/style.css @@ -323,6 +323,10 @@ video { max-width: 15rem; } +.dropdown .inner.surface { + background: var(--color-surface); +} + .dropdown .inner.left { right: unset; left: 0; @@ -757,13 +761,13 @@ menu.col { justify-content: flex-end; } -.message .inner { +.message .body { padding: var(--pad-2) var(--pad-3); background: var(--color-surface); color: var(--color-text); } -.message.mine .inner { +.message.mine .body { background: var(--color-primary); color: var(--color-text-primary); } diff --git a/app/templates_src/components.lisp b/app/templates_src/components.lisp index 048e06e..ade1d4d 100644 --- a/app/templates_src/components.lisp +++ b/app/templates_src/components.lisp @@ -45,8 +45,24 @@ (div ("class" "flex w_full gap_ch message {%- if user.id == message.owner %} justify_right mine {%- endif %}") ("id" "message_{{ message.id }}") + (text "{% if message.owner == user.id -%}") (div - ("class" "inner no_p_margin") + ("class" "dropdown") + (button + ("onclick" "open_dropdown(event)") + ("exclude" "dropdown") + ("class" "button") + (text "{{ icon \"ellipsis\" }}")) + (div + ("class" "inner surface") + (button + ("class" "button surface red") + ("onclick" "delete_message('{{ message.id }}')") + (text "delete")))) + (text "{%- endif %}") + + (div + ("class" "body no_p_margin") (text "{{ message.content|markdown|safe }}")) (text "{{ self::avatar(id=message.owner) }}")) (text "{%- endmacro %}") diff --git a/app/templates_src/messages.lisp b/app/templates_src/messages.lisp index 23159e2..e90786c 100644 --- a/app/templates_src/messages.lisp +++ b/app/templates_src/messages.lisp @@ -6,7 +6,7 @@ (div ("class" "hidden") ("id" "msgs_data_{{ id }}") - ("data-first-message-time" "{{ first_message_time }}")) + ("data-first-message-time" "{{ last_message_time }}")) (text "{% if messages|length == 0 -%}") (div diff --git a/src/model.rs b/src/model.rs index f77e64e..2a35f1f 100644 --- a/src/model.rs +++ b/src/model.rs @@ -86,6 +86,8 @@ pub enum SocketMethod { MessageUpdate, /// A chat update event. ChatUpdate, + /// A read receipt update event. + ReadReceipt, /// Simple ping. Ping, } diff --git a/src/routes/api/chats.rs b/src/routes/api/chats.rs index fa9c911..eac1074 100644 --- a/src/routes/api/chats.rs +++ b/src/routes/api/chats.rs @@ -185,6 +185,20 @@ pub async fn read_message_request( { return Json(e.into()); } + + // send event + let mut con = data.0.1.get_con().await; + + if let Err(e) = con.publish::( + id, + SocketMessage { + method: SocketMethod::ReadReceipt, + body: String::new(), + } + .to_string(), + ) { + return Json(Error::MiscError(e.to_string()).into()); + } } Json(ApiReturn { diff --git a/src/routes/pages/chats.rs b/src/routes/pages/chats.rs index a5ea0c8..c2fa5f5 100644 --- a/src/routes/pages/chats.rs +++ b/src/routes/pages/chats.rs @@ -71,7 +71,7 @@ pub async fn chat_request( } }; - let messages = match data.get_messages_by_chat(id, 12, props.page).await { + let messages = match data.get_messages_by_chat(id, 24, props.page).await { Ok(x) => x, Err(e) => { return Err(render_error(e, tera, data.0.0.clone(), Some(user)).await); @@ -132,9 +132,9 @@ pub async fn messages_request( }; let messages = match if before > 0 { - data.get_messages_by_chat_before(id, before, 12, 0).await + data.get_messages_by_chat_before(id, before, 24, 0).await } else { - data.get_messages_by_chat(id, 12, 0).await + data.get_messages_by_chat(id, 24, 0).await } { Ok(x) => x, Err(e) => { @@ -145,8 +145,8 @@ pub async fn messages_request( let mut ctx = default_context(&data.0.0, &build_code, &Some(user)); ctx.insert( - "first_message_time", - &match messages.first() { + "last_message_time", + &match messages.last() { Some(x) => x.created, None => 0, },