From a6bf547fdc44ad183a68e8cfcecec5f3b6073a04 Mon Sep 17 00:00:00 2001 From: trisua Date: Thu, 14 Aug 2025 23:04:23 -0400 Subject: [PATCH] add: letter reactions --- crates/app/src/public/html/components.lisp | 5 +++ .../database/drivers/sql/create_letters.sql | 4 ++- .../drivers/sql/version_migrations.sql | 8 +++++ crates/core/src/database/letters.rs | 13 ++++++-- crates/core/src/database/reactions.rs | 33 ++++++++++++++++++- crates/core/src/model/mail.rs | 4 +++ crates/core/src/model/reactions.rs | 2 ++ 7 files changed, 65 insertions(+), 4 deletions(-) diff --git a/crates/app/src/public/html/components.lisp b/crates/app/src/public/html/components.lisp index 6b2832a..883e35c 100644 --- a/crates/app/src/public/html/components.lisp +++ b/crates/app/src/public/html/components.lisp @@ -2640,6 +2640,11 @@ ("href" "/mail/compose?receivers={{ owner.username }}{% for receiver in letter.receivers %},id%3A{{ receiver }}{% endfor %}&subject=Re%3A%20{{ letter.subject }}&replying_to={{ letter.id }}") ("title" "Reply all") (icon (text "reply-all"))) + (div + ("class" "flex gap_2 reactions_box") + ("hook" "check_reactions") + ("hook-arg:id" "{{ letter.id }}") + (text "{{ self::likes(id=letter.id, asset_type=\"Letter\", likes=letter.likes, dislikes=letter.dislikes, disable_dislikes=owner.settings.hide_dislikes, secondary=true) }}")) (text "{% if user and letter.owner == user.id -%}") (button ("class" "small lowered red") diff --git a/crates/core/src/database/drivers/sql/create_letters.sql b/crates/core/src/database/drivers/sql/create_letters.sql index 4668b64..49ff222 100644 --- a/crates/core/src/database/drivers/sql/create_letters.sql +++ b/crates/core/src/database/drivers/sql/create_letters.sql @@ -6,5 +6,7 @@ CREATE TABLE IF NOT EXISTS letters ( subject TEXT NOT NULL, content TEXT NOT NULL, read_by TEXT NOT NULL, - replying_to BIGINT NOT NULL + replying_to BIGINT NOT NULL, + likes INT NOT NULL, + dislikes INT NOT NULL ) diff --git a/crates/core/src/database/drivers/sql/version_migrations.sql b/crates/core/src/database/drivers/sql/version_migrations.sql index 4598560..38320ee 100644 --- a/crates/core/src/database/drivers/sql/version_migrations.sql +++ b/crates/core/src/database/drivers/sql/version_migrations.sql @@ -69,3 +69,11 @@ ADD COLUMN IF NOT EXISTS uploads TEXT DEFAULT '{}'; -- users last_policy_consent ALTER TABLE users ADD COLUMN IF NOT EXISTS last_policy_consent BIGINT DEFAULT 0; + +-- letters likes +ALTER TABLE letters +ADD COLUMN IF NOT EXISTS likes INT DEFAULT 0; + +-- letters dislikes +ALTER TABLE letters +ADD COLUMN IF NOT EXISTS dislikes INT DEFAULT 0; diff --git a/crates/core/src/database/letters.rs b/crates/core/src/database/letters.rs index 4375ebb..ed298d3 100644 --- a/crates/core/src/database/letters.rs +++ b/crates/core/src/database/letters.rs @@ -17,6 +17,8 @@ impl DataManager { content: get!(x->5(String)), read_by: serde_json::from_str(&get!(x->6(String))).unwrap(), replying_to: get!(x->7(i64)) as usize, + likes: get!(x->8(i32)) as isize, + dislikes: get!(x->9(i32)) as isize, } } @@ -172,7 +174,7 @@ impl DataManager { let res = execute!( &conn, - "INSERT INTO letters VALUES ($1, $2, $3, $4, $5, $6, $7, $8)", + "INSERT INTO letters VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)", params![ &(data.id as i64), &(data.created as i64), @@ -181,7 +183,9 @@ impl DataManager { &data.subject, &data.content, &serde_json::to_string(&data.read_by).unwrap(), - &(data.replying_to as i64) + &(data.replying_to as i64), + &(data.likes as i32), + &(data.dislikes as i32), ] ); @@ -236,4 +240,9 @@ impl DataManager { } auto_method!(update_letter_read_by(Vec) -> "UPDATE letters SET read_by = $1 WHERE id = $2" --serde --cache-key-tmpl="atto.letter:{}"); + + auto_method!(incr_letter_likes() -> "UPDATE letters SET likes = likes + 1 WHERE id = $1" --cache-key-tmpl="atto.letter:{}" --incr); + auto_method!(incr_letter_dislikes() -> "UPDATE letters SET dislikes = dislikes + 1 WHERE id = $1" --cache-key-tmpl="atto.letter:{}" --incr); + auto_method!(decr_letter_likes()@get_letter_by_id -> "UPDATE letters SET likes = likes - 1 WHERE id = $1" --cache-key-tmpl="atto.letter:{}" --decr=likes); + auto_method!(decr_letter_dislikes()@get_letter_by_id -> "UPDATE letters SET dislikes = dislikes - 1 WHERE id = $1" --cache-key-tmpl="atto.letter:{}" --decr=dislikes); } diff --git a/crates/core/src/database/reactions.rs b/crates/core/src/database/reactions.rs index c26c3dc..cd531b1 100644 --- a/crates/core/src/database/reactions.rs +++ b/crates/core/src/database/reactions.rs @@ -7,7 +7,6 @@ use crate::model::{ Error, Result, }; use crate::{auto_method, DataManager}; - use oiseau::{PostgresRow, execute, get, query_row, query_rows, params}; impl DataManager { @@ -302,6 +301,31 @@ impl DataManager { AssetType::User => { return Err(Error::NotAllowed); } + AssetType::Letter => { + if let Err(e) = { + if data.is_like { + self.incr_letter_likes(data.asset).await + } else { + self.incr_letter_dislikes(data.asset).await + } + } { + return Err(e); + } else if data.is_like { + let letter = self.get_letter_by_id(data.asset).await.unwrap(); + + if letter.owner != user.id { + self.create_notification(Notification::new( + "Your letter has received a like!".to_string(), + format!( + "[@{}](/api/v1/auth/user/find/{}) has liked your [letter](/mail/letter/{})!", + user.username, user.id, data.asset + ), + letter.owner, + )) + .await? + } + } + } }; // return @@ -358,6 +382,13 @@ impl DataManager { AssetType::User => { return Err(Error::NotAllowed); } + AssetType::Letter => { + if reaction.is_like { + self.decr_letter_likes(reaction.asset).await + } else { + self.decr_letter_dislikes(reaction.asset).await + } + }?, }; // return diff --git a/crates/core/src/model/mail.rs b/crates/core/src/model/mail.rs index f0771fa..9d64cf8 100644 --- a/crates/core/src/model/mail.rs +++ b/crates/core/src/model/mail.rs @@ -20,6 +20,8 @@ pub struct Letter { pub read_by: Vec, /// The ID of the letter this letter is replying to. pub replying_to: usize, + pub likes: isize, + pub dislikes: isize, } impl Letter { @@ -40,6 +42,8 @@ impl Letter { content, read_by: Vec::new(), replying_to, + likes: 0, + dislikes: 0, } } diff --git a/crates/core/src/model/reactions.rs b/crates/core/src/model/reactions.rs index 827c74e..262956e 100644 --- a/crates/core/src/model/reactions.rs +++ b/crates/core/src/model/reactions.rs @@ -12,6 +12,8 @@ pub enum AssetType { Question, #[serde(alias = "user")] User, + #[serde(alias = "letter")] + Letter, } #[derive(Clone, Serialize, Deserialize)]