From 81a76288613276cb10f11ade4f19a1f2985b4636 Mon Sep 17 00:00:00 2001 From: trisua Date: Wed, 6 Aug 2025 23:13:31 -0400 Subject: [PATCH] add: better pinned posts ui --- crates/app/src/public/css/style.css | 2 +- .../app/src/public/html/communities/feed.lisp | 14 +-- crates/app/src/public/html/components.lisp | 6 +- crates/app/src/public/html/profile/base.lisp | 10 -- crates/app/src/public/html/profile/posts.lisp | 21 ++-- .../src/public/html/profile/responses.lisp | 21 ++-- .../app/src/public/html/profile/settings.lisp | 5 - crates/app/src/routes/api/v1/auth/profile.rs | 30 ------ crates/app/src/routes/api/v1/mod.rs | 4 - crates/app/src/routes/pages/profile.rs | 6 -- crates/core/src/database/posts.rs | 95 +------------------ crates/core/src/model/auth.rs | 3 - 12 files changed, 25 insertions(+), 192 deletions(-) diff --git a/crates/app/src/public/css/style.css b/crates/app/src/public/css/style.css index d02e7b0..1333485 100644 --- a/crates/app/src/public/css/style.css +++ b/crates/app/src/public/css/style.css @@ -502,7 +502,7 @@ select:focus { } input[type="checkbox"] { - --color: #c9b1bc; + --color: #93c5fd; appearance: none; border-radius: var(--radius); transition: diff --git a/crates/app/src/public/html/communities/feed.lisp b/crates/app/src/public/html/communities/feed.lisp index 9c73512..ccf868e 100644 --- a/crates/app/src/public/html/communities/feed.lisp +++ b/crates/app/src/public/html/communities/feed.lisp @@ -1,18 +1,7 @@ (text "{% import \"components.html\" as components %} {% extends \"communities/base.html\" %} {% block content %}") (div ("class" "flex flex_col gap_4 w_full") - (text "{{ macros::community_nav(community=community, selected=\"posts\") }} {% if pinned|length != 0 %}") - (div - ("class" "card_nest") - (div - ("class" "card small flex gap_2 items_center") - (text "{{ icon \"pin\" }}") - (span - (text "{{ text \"communities:label.pinned\" }}"))) - (div - ("class" "card flex flex_col gap_4") - (text "{% for post in pinned %} {% if post[0].context.repost and post[0].context.repost.reposting -%} {{ components::repost(repost=post[2], post=post[0], owner=post[1], secondary=true, show_community=false, can_manage_post=can_manage_posts) }} {% else %} {{ components::post(post=post[0], owner=post[1], question=post[3], secondary=true, show_community=false, can_manage_post=can_manage_posts, poll=post[4]) }} {%- endif %} {% endfor %}"))) - (text "{%- endif %}") + (text "{{ macros::community_nav(community=community, selected=\"posts\") }}") (div ("class" "card_nest") (div @@ -22,5 +11,6 @@ (text "{{ text \"communities:label.posts\" }}"))) (div ("class" "card flex flex_col gap_4") + (text "{% for post in pinned %} {% if post[0].context.repost and post[0].context.repost.reposting -%} {{ components::repost(repost=post[2], post=post[0], owner=post[1], secondary=true, show_community=false, can_manage_post=can_manage_posts) }} {% else %} {{ components::post(post=post[0], owner=post[1], question=post[3], secondary=true, show_community=false, can_manage_post=can_manage_posts, poll=post[4]) }} {%- endif %} {% endfor %}") (text "{% for post in feed %} {% if post[0].context.repost and post[0].context.repost.reposting -%} {{ components::repost(repost=post[2], post=post[0], owner=post[1], secondary=true, show_community=false, can_manage_post=can_manage_posts) }} {% else %} {{ components::post(post=post[0], owner=post[1], question=post[3], secondary=true, show_community=false, can_manage_post=can_manage_posts, poll=post[4]) }} {%- endif %} {% endfor %} {{ components::pagination(page=page, items=feed|length) }}")))) (text "{% endblock %}") diff --git a/crates/app/src/public/html/components.lisp b/crates/app/src/public/html/components.lisp index 3870cc4..0a03300 100644 --- a/crates/app/src/public/html/components.lisp +++ b/crates/app/src/public/html/components.lisp @@ -358,8 +358,7 @@ ("class" "flush flex gap_1 items_center") (text "{{ self::community_avatar(id=post.community, community=community) }}") (b - (text "{% if community.context.display_name -%} {{ community.context.display_name }} {% else %} {{ community.title }} {%- endif %}")) - (text "{% if post.context.is_pinned or post.context.is_profile_pinned -%} {{ icon \"pin\" }} {%- endif %}"))) + (text "{% if community.context.display_name -%} {{ community.context.display_name }} {% else %} {{ community.title }} {%- endif %}")))) (text "{%- endif %} {%- endif %}") (div ("class" "card flex flex_col post gap_2 post:{{ post.id }} {% if secondary -%}secondary{%- endif %}") @@ -386,7 +385,8 @@ (span ("class" "name") (text "{{ self::full_username(user=owner) }}")) - (text "{{ self::post_info(post=post, community=community) }}")) + (text "{{ self::post_info(post=post, community=community) }}") + (text "{% if post.context.is_pinned or post.context.is_profile_pinned -%} {{ icon \"pin\" }} {%- endif %}")) (text "{% if not dont_show_title and post.title and community and community.context.enable_titles -%}") ; post has a title AND whatever is rendering this component wants to see it (a diff --git a/crates/app/src/public/html/profile/base.lisp b/crates/app/src/public/html/profile/base.lisp index a7e1cd3..e730aa6 100644 --- a/crates/app/src/public/html/profile/base.lisp +++ b/crates/app/src/public/html/profile/base.lisp @@ -170,16 +170,6 @@ (text "Posts")) (span (text "{{ profile.post_count }}"))) - (text "{% if gpa and gpa > 0 and (not user.settings.disable_gpa_fun or is_helper) -%}") - (div - ("class" "w_full flex justify_between items_center") - ("title" "great post average (limited time fun)") - (span - ("class" "notification chip") - (text "GPA")) - (span - (text "{{ gpa|round(method=\"floor\", precision=2) }}"))) - (text "{%- endif %}") (text "{% if not profile.settings.private_last_seen or is_self or is_helper %}") (div ("class" "w_full flex justify_between items_center") diff --git a/crates/app/src/public/html/profile/posts.lisp b/crates/app/src/public/html/profile/posts.lisp index ab33a08..7f40dbb 100644 --- a/crates/app/src/public/html/profile/posts.lisp +++ b/crates/app/src/public/html/profile/posts.lisp @@ -3,19 +3,8 @@ ("style" "display: contents") (text "{{ components::create_question_form(receiver=profile.id, header=profile.settings.motivational_header, drawing_enabled=profile.settings.enable_drawings, allow_anonymous=profile.settings.allow_anonymous_questions) }}")) -(text "{%- endif %} {% if not tag and pinned|length != 0 -%}") -(div - ("class" "card_nest") - (div - ("class" "card small flex gap_2 items_center") - (text "{{ icon \"pin\" }}") - (span - (text "{{ text \"communities:label.pinned\" }}"))) - (div - ("class" "card flex flex_col gap_4") - (text "{% for post in pinned %} {% if post[2].read_access == \"Everybody\" -%} {% if post[0].context.repost and post[0].context.repost.reposting -%} {{ components::repost(repost=post[3], post=post[0], owner=post[1], secondary=true, community=post[2], show_community=true, can_manage_post=is_self) }} {% else %} {{ components::post(post=post[0], owner=post[1], question=post[4], secondary=true, community=post[2], can_manage_post=is_self, poll=post[5]) }} {%- endif %} {%- endif %} {% endfor %}"))) - -(text "{%- endif %} {{ macros::profile_nav(selected=\"posts\") }}") +(text "{%- endif %}") +(text "{{ macros::profile_nav(selected=\"posts\") }}") (div ("class" "card_nest") (div @@ -42,6 +31,12 @@ (div ("class" "card w_full flex flex_col gap_2") ("ui_ident" "io_data_load") + ; pinned + (text "{% for post in pinned %} {% if post[2].read_access == \"Everybody\" -%} {% if post[0].context.repost and post[0].context.repost.reposting -%} {{ components::repost(repost=post[3], post=post[0], owner=post[1], secondary=true, community=post[2], show_community=true, can_manage_post=is_self) }} {% else %} {{ components::post(post=post[0], owner=post[1], question=post[4], secondary=true, community=post[2], can_manage_post=is_self, poll=post[5]) }} {%- endif %} {%- endif %} {% endfor %}") + (text "{% if pinned|length > 0 -%}") + (div ("class" "squig")) + (text "{%- endif %}") + ; ... (div ("ui_ident" "io_data_marker")))) (text "{% set paged = user and user.settings.paged_timelines %}") diff --git a/crates/app/src/public/html/profile/responses.lisp b/crates/app/src/public/html/profile/responses.lisp index 93c605e..37e2e9e 100644 --- a/crates/app/src/public/html/profile/responses.lisp +++ b/crates/app/src/public/html/profile/responses.lisp @@ -3,19 +3,8 @@ ("style" "display: contents") (text "{{ components::create_question_form(receiver=profile.id, header=profile.settings.motivational_header, drawing_enabled=profile.settings.enable_drawings, allow_anonymous=profile.settings.allow_anonymous_questions) }}")) -(text "{%- endif %} {% if not tag and pinned|length != 0 -%}") -(div - ("class" "card_nest") - (div - ("class" "card small flex gap_2 items_center") - (text "{{ icon \"pin\" }}") - (span - (text "{{ text \"communities:label.pinned\" }}"))) - (div - ("class" "card flex flex_col gap_4") - (text "{% for post in pinned %} {% if post[2].read_access == \"Everybody\" -%} {% if post[0].context.repost and post[0].context.repost.reposting -%} {{ components::repost(repost=post[3], post=post[0], owner=post[1], secondary=true, community=post[2], show_community=true, can_manage_post=is_self) }} {% else %} {{ components::post(post=post[0], owner=post[1], question=post[4], secondary=true, community=post[2], can_manage_post=is_self, poll=post[5]) }} {%- endif %} {%- endif %} {% endfor %}"))) - -(text "{%- endif %} {{ macros::profile_nav(selected=\"responses\") }}") +(text "{%- endif %}") +(text "{{ macros::profile_nav(selected=\"responses\") }}") (div ("class" "card_nest") (div @@ -42,6 +31,12 @@ (div ("class" "card w_full flex flex_col gap_2") ("ui_ident" "io_data_load") + ; pinned + (text "{% for post in pinned %} {% if post[2].read_access == \"Everybody\" -%} {% if post[0].context.repost and post[0].context.repost.reposting -%} {{ components::repost(repost=post[3], post=post[0], owner=post[1], secondary=true, community=post[2], show_community=true, can_manage_post=is_self) }} {% else %} {{ components::post(post=post[0], owner=post[1], question=post[4], secondary=true, community=post[2], can_manage_post=is_self, poll=post[5]) }} {%- endif %} {%- endif %} {% endfor %}") + (text "{% if pinned|length > 0 -%}") + (div ("class" "squig")) + (text "{%- endif %}") + ; ... (div ("ui_ident" "io_data_marker")))) (text "{% set paged = user and user.settings.paged_timelines %}") diff --git a/crates/app/src/public/html/profile/settings.lisp b/crates/app/src/public/html/profile/settings.lisp index 0b7abad..3f5901a 100644 --- a/crates/app/src/public/html/profile/settings.lisp +++ b/crates/app/src/public/html/profile/settings.lisp @@ -1970,11 +1970,6 @@ \"text\", ], [[], \"Fun\", \"title\"], - [ - [\"disable_gpa_fun\", \"Disable GPA\"], - \"{{ profile.settings.disable_gpa_fun }}\", - \"checkbox\", - ], [ [\"disable_achievements\", \"Disable achievements\"], \"{{ profile.settings.disable_achievements }}\", diff --git a/crates/app/src/routes/api/v1/auth/profile.rs b/crates/app/src/routes/api/v1/auth/profile.rs index 969cbce..ba3c17a 100644 --- a/crates/app/src/routes/api/v1/auth/profile.rs +++ b/crates/app/src/routes/api/v1/auth/profile.rs @@ -869,36 +869,6 @@ pub async fn post_to_socket_request( }) } -/// Calculate the user's great post average. -pub async fn get_user_gpa_request( - jar: CookieJar, - Path(id): Path, - Extension(data): Extension, -) -> impl IntoResponse { - let data = &(data.read().await).0; - let user = match get_user_from_token!(jar, data, oauth::AppScope::UserReadProfile) { - Some(ua) => ua, - None => return Json(Error::NotAllowed.into()), - }; - - if !user.permissions.check(FinePermission::MANAGE_USERS) { - return Json(Error::NotAllowed.into()); - } - - let gpa = data.calculate_user_gpa(id).await; - return Json(ApiReturn { - ok: true, - message: if gpa >= 3.0 { - "cool".to_string() - } else if gpa >= 4.0 { - "extraordinary".to_string() - } else { - "ok".to_string() - }, - payload: Some(gpa), - }); -} - /// Remove a grant token. pub async fn remove_grant_request( jar: CookieJar, diff --git a/crates/app/src/routes/api/v1/mod.rs b/crates/app/src/routes/api/v1/mod.rs index 5539bf4..5e5ddb4 100644 --- a/crates/app/src/routes/api/v1/mod.rs +++ b/crates/app/src/routes/api/v1/mod.rs @@ -405,10 +405,6 @@ pub fn routes() -> Router { get(auth::profile::redirect_from_stripe_id), ) .route("/auth/ip/{ip}/block", post(auth::social::ip_block_request)) - .route( - "/auth/user/{id}/gpa", - get(auth::profile::get_user_gpa_request), - ) .route( "/auth/user/{id}/_connect/{stream}", any(auth::profile::subscription_handler), diff --git a/crates/app/src/routes/pages/profile.rs b/crates/app/src/routes/pages/profile.rs index 3f6274b..9f5b16d 100644 --- a/crates/app/src/routes/pages/profile.rs +++ b/crates/app/src/routes/pages/profile.rs @@ -371,7 +371,6 @@ pub async fn posts_request( context.insert("pinned", &pinned); context.insert("page", &props.page); context.insert("tag", &props.tag); - context.insert("gpa", &data.0.calculate_user_gpa(other_user.id).await); profile_context( &mut context, &user, @@ -488,7 +487,6 @@ pub async fn replies_request( context.insert("posts", &posts); context.insert("page", &props.page); - context.insert("gpa", &data.0.calculate_user_gpa(other_user.id).await); profile_context( &mut context, &user, @@ -601,7 +599,6 @@ pub async fn media_request( context.insert("posts", &posts); context.insert("page", &props.page); - context.insert("gpa", &data.0.calculate_user_gpa(other_user.id).await); profile_context( &mut context, &user, @@ -696,7 +693,6 @@ pub async fn outbox_request( context.insert("questions", &questions); context.insert("page", &props.page); - context.insert("gpa", &data.0.calculate_user_gpa(other_user.id).await); profile_context( &mut context, &Some(user), @@ -806,7 +802,6 @@ pub async fn following_request( context.insert("list", &list); context.insert("page", &props.page); - context.insert("gpa", &data.0.calculate_user_gpa(other_user.id).await); profile_context( &mut context, &user, @@ -916,7 +911,6 @@ pub async fn followers_request( context.insert("list", &list); context.insert("page", &props.page); - context.insert("gpa", &data.0.calculate_user_gpa(other_user.id).await); profile_context( &mut context, &user, diff --git a/crates/core/src/database/posts.rs b/crates/core/src/database/posts.rs index a98250f..7a7a2a7 100644 --- a/crates/core/src/database/posts.rs +++ b/crates/core/src/database/posts.rs @@ -13,9 +13,7 @@ use crate::model::{ }; use tetratto_shared::unix_epoch_timestamp; use crate::{auto_method, DataManager}; - -use oiseau::{PostgresRow, cache::redis::Commands}; -use oiseau::{execute, get, query_row, query_rows, params, cache::Cache}; +use oiseau::{PostgresRow, execute, get, query_row, query_rows, params, cache::Cache}; pub type FullPost = ( Post, @@ -816,95 +814,6 @@ impl DataManager { Ok(res.unwrap()) } - /// Calculate the GPA (great post average) of a given user. - /// - /// To be considered a "great post", a post must have a score ((likes - dislikes) / (likes + dislikes)) - /// of at least 0.6. - /// - /// GPA is calculated based on the user's last 48 posts. - pub async fn calculate_user_gpa(&self, id: usize) -> f32 { - // just for note, this is SUPER bad for performance... which is why we - // only calculate this when it expires in the cache (every day) - if let Some(cached) = self.0.1.get(format!("atto.user.gpa:{}", id)).await { - if let Ok(c) = cached.parse() { - return c; - } - } - - // ... - let conn = match self.0.connect().await { - Ok(c) => c, - Err(_) => return 0.0, - }; - - let res = query_rows!( - &conn, - &format!("SELECT * FROM posts WHERE owner = $1 ORDER BY created DESC LIMIT 48"), - &[&(id as i64)], - |x| { Self::get_post_from_row(x) } - ); - - if res.is_err() { - return 0.0; - } - - // ... - let mut real_posts_count: usize = 0; // posts which can be scored - let mut good_posts: usize = 0; - // let mut bad_posts: usize = 0; - - let posts = res.unwrap(); - - for post in posts { - if post.likes == 0 && post.dislikes == 0 { - // post has no likes or dislikes... doesn't count - if good_posts > 8 { - good_posts -= 1; // we're going to say this is a bad post because it isn't liked enough - } - - continue; - } - - real_posts_count += 1; - - // likes percentage / total likes - let score: f32 = (post.likes as f32 - post.dislikes as f32) - / (post.likes as f32 + post.dislikes as f32); - - if score.is_sign_negative() { - // bad_posts += 1; - continue; - } - - if score > 0.6 { - good_posts += 1; - } - // } else { - // bad_posts += 1; - // } - } - - let gpa = (good_posts as f32 / real_posts_count as f32) * 4.0; - let gpa_rounded = format!("{gpa:.2}").parse::().unwrap(); - - let mut redis_con = self.0.1.get_con().await; - - // expires in one day - if redis_con - .set_ex::( - format!("atto.user.gpa:{}", id), - gpa_rounded.to_string(), - 86400, - ) - .is_err() - { - return 0.0; - }; - - // ... - gpa_rounded - } - /// Get all replies from the given user (from most recent). /// /// # Arguments @@ -1924,6 +1833,8 @@ impl DataManager { } else { return Err(Error::GeneralNotFound("topic".to_string())); } + } else if data.topic != 0 { + return Err(Error::DoesNotSupportField("Community".to_string())); } // ... diff --git a/crates/core/src/model/auth.rs b/crates/core/src/model/auth.rs index ef19586..d730e13 100644 --- a/crates/core/src/model/auth.rs +++ b/crates/core/src/model/auth.rs @@ -294,9 +294,6 @@ pub struct UserSettings { /// If extra post tabs are hidden (replies, media). #[serde(default)] pub hide_extra_post_tabs: bool, - /// If the GPA experiment is disabled. - #[serde(default)] - pub disable_gpa_fun: bool, /// A list of strings the user has muted. #[serde(default)] pub muted: Vec,