diff --git a/Cargo.lock b/Cargo.lock index 3a62069..6795fef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2998,7 +2998,7 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tawny" -version = "1.0.3" +version = "1.0.4" dependencies = [ "ammonia", "axum", @@ -3015,7 +3015,7 @@ dependencies = [ "serde", "serde_json", "tera", - "tetratto-core 16.0.2", + "tetratto-core 16.0.3", "tetratto-shared", "tokio", "toml 0.9.5", @@ -3098,9 +3098,9 @@ dependencies = [ [[package]] name = "tetratto-core" -version = "16.0.2" +version = "16.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380eed8dec18b0dcda3440d47375a1bacf94e42fdcd93d464e27682d005bf356" +checksum = "9e3e81378d7f02a6f7d18bf9ca58e3885c6cb8611ca4d0536c76b320e6e4017a" dependencies = [ "async-recursion", "base16ct", diff --git a/Cargo.toml b/Cargo.toml index 08e1f57..dca71de 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tawny" -version = "1.0.3" +version = "1.0.4" edition = "2024" authors = ["trisuaso"] repository = "https://trisua.com/t/tawny" @@ -8,7 +8,7 @@ license = "AGPL-3.0-or-later" homepage = "https://tawny.cc" [dependencies] -tetratto-core = "16.0.2" +tetratto-core = "16.0.3" tetratto-shared = "12.0.6" tokio = { version = "1.47.1", features = ["macros", "rt-multi-thread"] } pathbufd = "0.1.4" diff --git a/app/public/messages.js b/app/public/messages.js index e1e57ae..023e38c 100644 --- a/app/public/messages.js +++ b/app/public/messages.js @@ -12,6 +12,7 @@ const STATE = { function create_streamer(chat_id, hook_element) { STATE.chat_id = chat_id; STATE.stream_element = hook_element.parentElement; + clear_notifications(); STATE.observer = new IntersectionObserver( (entries) => { @@ -112,6 +113,10 @@ function sock_con() { if (msg.method === "MessageCreate") { render_message(msg.body); + + setTimeout(() => { + clear_notifications(); + }, 150); } else if (msg.method === "MessageDelete") { if (document.getElementById(`message_${msg.body}`)) { document.getElementById(`message_${msg.body}`).remove(); @@ -420,3 +425,15 @@ function clear_replying_to() { STATE.replying_to = undefined; document.getElementById("replying_to_zone").classList.add("hidden"); } + +function clear_notifications() { + fetch(`/api/v1/chats/${STATE.chat_id}/notifications`, { + 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 5a585be..74806ac 100644 --- a/app/public/style.css +++ b/app/public/style.css @@ -837,3 +837,22 @@ menu.col { .message .body p:last-of-type { margin: 0 !important; } + +.message_reply_wrapper .message { + opacity: 75%; + padding: 0 4px; + + & .body { + min-height: unset; + height: 26px !important; + overflow: hidden; + } + + & p { + font-size: 10px; + } + + & .avatar { + --size: 18px !important; + } +} diff --git a/app/templates_src/components.lisp b/app/templates_src/components.lisp index 293ad86..59ebd0e 100644 --- a/app/templates_src/components.lisp +++ b/app/templates_src/components.lisp @@ -111,7 +111,7 @@ (text "{% if replying_to -%}") (div - ("style" "transform: scale(0.8); opacity: 75%; width: 110%") + ("class" "message_reply_wrapper") (text "{{ self::message(message=replying_to, hide_actions=true) }}")) (text "{%- endif %}") (text "{%- endmacro %}") diff --git a/app/templates_src/profile.lisp b/app/templates_src/profile.lisp index 8fa811a..f26bd28 100644 --- a/app/templates_src/profile.lisp +++ b/app/templates_src/profile.lisp @@ -58,7 +58,9 @@ ("class" "card_nest w_full") ("style" "max-width: 25rem") (div - ("class" "card banner")) + ("class" "card banner") + (img + ("src" "{{ config.service_hosts.buckets }}/banners/{{ profile.id }}"))) (div ("class" "card flex flex_col gap_ch") (text "{{ components::avatar(id=profile.id, size=\"160px\") }}") @@ -134,12 +136,17 @@ } .profile .banner { - background-image: url(\"{{ config.service_hosts.buckets }}/banners/{{ profile.id }}\") !important; - background-repeat: no-repeat !important; - background-position: center !important; - background-size: cover !important; border-radius: var(--radius) var(--radius) 0 0; height: 225px; + overflow: hidden; + padding: 0 !important; + } + + .profile .banner img { + width: 100%; + height: 100%; + object-fit: cover; + object-position: center; } .card_nest .card:nth-child(2) { diff --git a/src/routes/api/chats.rs b/src/routes/api/chats.rs index 8a7d69c..29e8932 100644 --- a/src/routes/api/chats.rs +++ b/src/routes/api/chats.rs @@ -540,3 +540,29 @@ pub async fn remove_pin_request( Err(e) => Json(e.into()), } } + +pub async fn clear_chat_notifications( + jar: CookieJar, + Extension(data): Extension, + Path(id): Path, +) -> impl IntoResponse { + let data = &(data.read().await).0; + let user = match get_user_from_token!(jar, data.2) { + Some(x) => x, + None => return Json(Error::NotAllowed.into()), + }; + + if let Err(e) = data + .2 + .delete_all_notifications_by_tag(&user, &id.to_string()) + .await + { + return Json(e.into()); + } + + Json(ApiReturn { + ok: true, + message: "Success".to_string(), + payload: (), + }) +} diff --git a/src/routes/api/messages.rs b/src/routes/api/messages.rs index 8bfd877..5bebb8a 100644 --- a/src/routes/api/messages.rs +++ b/src/routes/api/messages.rs @@ -6,7 +6,7 @@ use axum_extra::extract::CookieJar; use axum_image::{encode::save_webp_buffer, extract::JsonMultipart}; use buckets_core::model::{MediaType, MediaUpload}; use serde::Deserialize; -use tetratto_core::model::{ApiReturn, Error, permissions::FinePermission}; +use tetratto_core::model::{ApiReturn, Error, auth::Notification, permissions::FinePermission}; #[derive(Deserialize)] pub struct CreateMessage { @@ -121,6 +121,20 @@ pub async fn create_request( if let Err(e) = data.2.incr_user_missed_messages(member).await { return Json(e.into()); } + + let mut notif = Notification::new( + "You've received a new message".to_string(), + format!( + "[@{}](/api/v1/auth/user/find/{}) has sent you a message in a [chat]({}/chats/{})", + user.username, user.id, data.0.0.host, chat.id + ), + member, + ); + + notif.tag = chat.id.to_string(); + if let Err(e) = data.2.create_notification(notif).await { + return Json(e.into()); + } } // ... diff --git a/src/routes/api/mod.rs b/src/routes/api/mod.rs index 35329eb..813b4c3 100644 --- a/src/routes/api/mod.rs +++ b/src/routes/api/mod.rs @@ -38,6 +38,10 @@ pub fn routes() -> Router { "/chats/{id}/pins/{message}", delete(chats::remove_pin_request), ) + .route( + "/chats/{id}/notifications", + delete(chats::clear_chat_notifications), + ) // messages .route("/messages/{id}", post(messages::create_request)) .route("/messages/{id}", delete(messages::delete_request))