add: ability to mute phrases
add: ability to disable gpa experiment
This commit is contained in:
parent
9d839a1a63
commit
f034cc4f27
7 changed files with 212 additions and 63 deletions
|
@ -160,13 +160,13 @@
|
|||
(text "Posts"))
|
||||
(span
|
||||
(text "{{ profile.post_count }}")))
|
||||
(text "{% if gpa and gpa > 0 -%}")
|
||||
(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)")
|
||||
("title" "great post average (limited time fun)")
|
||||
(span
|
||||
("class" "notification chip")
|
||||
(text "GPA 🐇"))
|
||||
(text "GPA"))
|
||||
(span
|
||||
(text "{{ gpa|round(method=\"floor\", precision=2) }}")))
|
||||
(text "{%- endif %}")
|
||||
|
|
|
@ -1204,8 +1204,19 @@
|
|||
settings.warning,
|
||||
\"textarea\",
|
||||
],
|
||||
[[\"muted\", \"Muted phrases\"], settings.muted.join(\"\\n\"), \"textarea\", {
|
||||
embed_html:
|
||||
'<span class=\"fade\">Muted phrases should all be on new lines.</span>',
|
||||
}],
|
||||
],
|
||||
settings,
|
||||
{
|
||||
muted: (new_muted) => {
|
||||
settings.muted = new_muted
|
||||
.split(\"\\n\")
|
||||
.map((t) => t.trim());
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
ui.generate_settings_ui(
|
||||
|
@ -1303,6 +1314,12 @@
|
|||
\"Hides dislikes on all posts. Users can still dislike your posts, you just won't be able to see it.\",
|
||||
\"text\",
|
||||
],
|
||||
[[], \"Fun\", \"title\"],
|
||||
[
|
||||
[\"disable_gpa_fun\", \"Disable GPA\"],
|
||||
\"{{ profile.settings.disable_gpa_fun }}\",
|
||||
\"checkbox\",
|
||||
],
|
||||
],
|
||||
settings,
|
||||
);
|
||||
|
|
|
@ -856,27 +856,29 @@ media_theme_pref();
|
|||
}
|
||||
});
|
||||
|
||||
self.define("render_settings_ui_field", (_, into_element, option) => {
|
||||
if (option.input_element_type === "divider") {
|
||||
into_element.innerHTML += `<hr class="margin" />`;
|
||||
return;
|
||||
}
|
||||
self.define(
|
||||
"render_settings_ui_field",
|
||||
(_, into_element, option, id_key) => {
|
||||
if (option.input_element_type === "divider") {
|
||||
into_element.innerHTML += `<hr class="margin" />`;
|
||||
return;
|
||||
}
|
||||
|
||||
if (option.input_element_type === "title") {
|
||||
into_element.innerHTML += `<hr class="margin" /><b>${option.value}</b>`;
|
||||
return;
|
||||
}
|
||||
if (option.input_element_type === "title") {
|
||||
into_element.innerHTML += `<hr class="margin" /><b>${option.value}</b>`;
|
||||
return;
|
||||
}
|
||||
|
||||
if (option.input_element_type === "text") {
|
||||
into_element.innerHTML += `<p>${option.value}</p>`;
|
||||
return;
|
||||
}
|
||||
if (option.input_element_type === "text") {
|
||||
into_element.innerHTML += `<p>${option.value}</p>`;
|
||||
return;
|
||||
}
|
||||
|
||||
if (option.input_element_type === "checkbox") {
|
||||
into_element.innerHTML += `<div class="card flex gap-2">
|
||||
if (option.input_element_type === "checkbox") {
|
||||
into_element.innerHTML += `<div class="card flex gap-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
onchange="window.set_setting_field('${option.key}', event.target.checked)"
|
||||
onchange="window.set_setting_field${id_key}('${option.key}', event.target.checked)"
|
||||
placeholder="${option.key}"
|
||||
name="${option.key}"
|
||||
id="${option.key}"
|
||||
|
@ -887,11 +889,11 @@ media_theme_pref();
|
|||
<label for="${option.key}"><b>${option.label.replaceAll("_", " ")}</b></label>
|
||||
</div>`;
|
||||
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (option.input_element_type === "color") {
|
||||
into_element.innerHTML += `<div class="flex flex-col gap-2">
|
||||
if (option.input_element_type === "color") {
|
||||
into_element.innerHTML += `<div class="flex flex-col gap-2">
|
||||
<label for="${option.key}"><b>${option.label.replaceAll("_", " ")}</b></label>
|
||||
|
||||
<div class="card flex flex-row gap-2">
|
||||
|
@ -905,7 +907,7 @@ media_theme_pref();
|
|||
|
||||
<input
|
||||
type="text"
|
||||
onchange="window.set_setting_field('${option.key}', event.target.value); window.update_color_field('${option.key}', event.target.value)"
|
||||
onchange="window.set_setting_field${id_key}('${option.key}', event.target.value); window.update_color_field('${option.key}', event.target.value)"
|
||||
placeholder="${option.key}"
|
||||
name="${option.key}"
|
||||
id="${option.key}"
|
||||
|
@ -918,10 +920,10 @@ media_theme_pref();
|
|||
<span class="fade">${(option.attributes || { description: "" }).description}</span>
|
||||
</div>`;
|
||||
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
into_element.innerHTML += `<div class="card-nest">
|
||||
into_element.innerHTML += `<div class="card-nest">
|
||||
<div class="card small">
|
||||
<label for="${option.key}"><b>${option.label.replaceAll("_", " ")}</b></label>
|
||||
</div>
|
||||
|
@ -929,7 +931,7 @@ media_theme_pref();
|
|||
<div class="card flex flex-col gap-2">
|
||||
<${option.input_element_type || "input"}
|
||||
type="text"
|
||||
onchange="window.set_setting_field('${option.key}', event.target.value)"
|
||||
onchange="window.set_setting_field${id_key}('${option.key}', event.target.value)"
|
||||
placeholder="${option.key}"
|
||||
name="${option.key}"
|
||||
id="${option.key}"
|
||||
|
@ -939,26 +941,37 @@ ${option.input_element_type === "textarea" ? `${option.value}</textarea>` : ""}
|
|||
${(option.attributes || { embed_html: "" }).embed_html}
|
||||
</div>
|
||||
</div>`;
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
self.define(
|
||||
"generate_settings_ui",
|
||||
({ $ }, into_element, options, settings_ref, key_map = {}) => {
|
||||
const id_key = `a${crypto.randomUUID().replaceAll("-", "")}`;
|
||||
for (const option of options) {
|
||||
$.render_settings_ui_field(into_element, {
|
||||
key: Array.isArray(option[0]) ? option[0][0] : option[0],
|
||||
label: Array.isArray(option[0]) ? option[0][1] : option[0],
|
||||
value: option[1],
|
||||
input_element_type: option[2],
|
||||
attributes: option[3],
|
||||
});
|
||||
$.render_settings_ui_field(
|
||||
into_element,
|
||||
{
|
||||
key: Array.isArray(option[0])
|
||||
? option[0][0]
|
||||
: option[0],
|
||||
label: Array.isArray(option[0])
|
||||
? option[0][1]
|
||||
: option[0],
|
||||
value: option[1],
|
||||
input_element_type: option[2],
|
||||
attributes: option[3],
|
||||
},
|
||||
id_key,
|
||||
);
|
||||
}
|
||||
|
||||
window.set_setting_field = (key, value) => {
|
||||
window[`set_setting_field${id_key}`] = (key, value) => {
|
||||
if (settings_ref && !key_map[key]) {
|
||||
settings_ref[key] = value;
|
||||
} else {
|
||||
key_map[key](value);
|
||||
console.log("custom_update", key);
|
||||
}
|
||||
|
||||
console.log("update", key);
|
||||
|
|
|
@ -71,7 +71,13 @@ pub async fn index_request(
|
|||
{
|
||||
Ok(l) => match data
|
||||
.0
|
||||
.fill_posts_with_community(l, user.id, &ignore_users, &Some(user.clone()))
|
||||
.fill_posts_with_community(
|
||||
data.0
|
||||
.posts_muted_phrase_filter(&l, Some(&user.settings.muted)),
|
||||
user.id,
|
||||
&ignore_users,
|
||||
&Some(user.clone()),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(l) => l,
|
||||
|
@ -103,7 +109,14 @@ pub async fn popular_request(
|
|||
Ok(l) => match data
|
||||
.0
|
||||
.fill_posts_with_community(
|
||||
l,
|
||||
data.0.posts_muted_phrase_filter(
|
||||
&l,
|
||||
if let Some(ref ua) = user {
|
||||
Some(&ua.settings.muted)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
),
|
||||
if let Some(ref ua) = user { ua.id } else { 0 },
|
||||
&ignore_users,
|
||||
&user,
|
||||
|
@ -149,7 +162,13 @@ pub async fn following_request(
|
|||
{
|
||||
Ok(l) => match data
|
||||
.0
|
||||
.fill_posts_with_community(l, user.id, &ignore_users, &Some(user.clone()))
|
||||
.fill_posts_with_community(
|
||||
data.0
|
||||
.posts_muted_phrase_filter(&l, Some(&user.settings.muted)),
|
||||
user.id,
|
||||
&ignore_users,
|
||||
&Some(user.clone()),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(l) => l,
|
||||
|
@ -183,7 +202,14 @@ pub async fn all_request(
|
|||
Ok(l) => match data
|
||||
.0
|
||||
.fill_posts_with_community(
|
||||
l,
|
||||
data.0.posts_muted_phrase_filter(
|
||||
&l,
|
||||
if let Some(ref ua) = user {
|
||||
Some(&ua.settings.muted)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
),
|
||||
if let Some(ref ua) = user { ua.id } else { 0 },
|
||||
&ignore_users,
|
||||
&user,
|
||||
|
@ -580,7 +606,13 @@ pub async fn search_request(
|
|||
{
|
||||
Ok(l) => match data
|
||||
.0
|
||||
.fill_posts_with_community(l, user.id, &ignore_users, &Some(user.clone()))
|
||||
.fill_posts_with_community(
|
||||
data.0
|
||||
.posts_muted_phrase_filter(&l, Some(&user.settings.muted)),
|
||||
user.id,
|
||||
&ignore_users,
|
||||
&Some(user.clone()),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(l) => l,
|
||||
|
@ -592,7 +624,13 @@ pub async fn search_request(
|
|||
match data.0.get_posts_searched(12, req.page, &req.query).await {
|
||||
Ok(l) => match data
|
||||
.0
|
||||
.fill_posts_with_community(l, user.id, &ignore_users, &Some(user.clone()))
|
||||
.fill_posts_with_community(
|
||||
data.0
|
||||
.posts_muted_phrase_filter(&l, Some(&user.settings.muted)),
|
||||
user.id,
|
||||
&ignore_users,
|
||||
&Some(user.clone()),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(l) => l,
|
||||
|
|
|
@ -245,7 +245,14 @@ pub async fn posts_request(
|
|||
Ok(p) => match data
|
||||
.0
|
||||
.fill_posts_with_community(
|
||||
p,
|
||||
data.0.posts_muted_phrase_filter(
|
||||
&p,
|
||||
if let Some(ref ua) = user {
|
||||
Some(&ua.settings.muted)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
),
|
||||
if let Some(ref ua) = user { ua.id } else { 0 },
|
||||
&ignore_users,
|
||||
&user,
|
||||
|
@ -266,7 +273,14 @@ pub async fn posts_request(
|
|||
Ok(p) => match data
|
||||
.0
|
||||
.fill_posts_with_community(
|
||||
p,
|
||||
data.0.posts_muted_phrase_filter(
|
||||
&p,
|
||||
if let Some(ref ua) = user {
|
||||
Some(&ua.settings.muted)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
),
|
||||
if let Some(ref ua) = user { ua.id } else { 0 },
|
||||
&ignore_users,
|
||||
&user,
|
||||
|
@ -285,7 +299,14 @@ pub async fn posts_request(
|
|||
Ok(p) => match data
|
||||
.0
|
||||
.fill_posts_with_community(
|
||||
p,
|
||||
data.0.posts_muted_phrase_filter(
|
||||
&p,
|
||||
if let Some(ref ua) = user {
|
||||
Some(&ua.settings.muted)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
),
|
||||
if let Some(ref ua) = user { ua.id } else { 0 },
|
||||
&ignore_users,
|
||||
&user,
|
||||
|
@ -394,7 +415,14 @@ pub async fn replies_request(
|
|||
Ok(p) => match data
|
||||
.0
|
||||
.fill_posts_with_community(
|
||||
p,
|
||||
data.0.posts_muted_phrase_filter(
|
||||
&p,
|
||||
if let Some(ref ua) = user {
|
||||
Some(&ua.settings.muted)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
),
|
||||
if let Some(ref ua) = user { ua.id } else { 0 },
|
||||
&ignore_users,
|
||||
&user,
|
||||
|
@ -500,7 +528,14 @@ pub async fn media_request(
|
|||
Ok(p) => match data
|
||||
.0
|
||||
.fill_posts_with_community(
|
||||
p,
|
||||
data.0.posts_muted_phrase_filter(
|
||||
&p,
|
||||
if let Some(ref ua) = user {
|
||||
Some(&ua.settings.muted)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
),
|
||||
if let Some(ref ua) = user { ua.id } else { 0 },
|
||||
&ignore_users,
|
||||
&user,
|
||||
|
|
|
@ -21,8 +21,20 @@ use oiseau::SqliteRow;
|
|||
#[cfg(feature = "postgres")]
|
||||
use oiseau::PostgresRow;
|
||||
|
||||
#[cfg(feature = "redis")]
|
||||
use oiseau::cache::redis::Commands;
|
||||
|
||||
use oiseau::{execute, get, query_row, query_rows, params};
|
||||
|
||||
pub type FullPost = (
|
||||
Post,
|
||||
User,
|
||||
Community,
|
||||
Option<(User, Post)>,
|
||||
Option<(Question, User)>,
|
||||
Option<(Poll, bool, bool)>,
|
||||
);
|
||||
|
||||
macro_rules! private_post_replying {
|
||||
($post:ident, $replying_posts:ident, $ua1:ident, $data:ident) => {
|
||||
// post owner is not following us
|
||||
|
@ -376,16 +388,7 @@ impl DataManager {
|
|||
user_id: usize,
|
||||
ignore_users: &[usize],
|
||||
user: &Option<User>,
|
||||
) -> Result<
|
||||
Vec<(
|
||||
Post,
|
||||
User,
|
||||
Community,
|
||||
Option<(User, Post)>,
|
||||
Option<(Question, User)>,
|
||||
Option<(Poll, bool, bool)>,
|
||||
)>,
|
||||
> {
|
||||
) -> Result<Vec<FullPost>> {
|
||||
let mut out = Vec::new();
|
||||
|
||||
let mut seen_before: HashMap<(usize, usize), (User, Community)> = HashMap::new();
|
||||
|
@ -463,6 +466,33 @@ impl DataManager {
|
|||
Ok(out)
|
||||
}
|
||||
|
||||
/// Update posts which contain a muted phrase.
|
||||
pub fn posts_muted_phrase_filter(
|
||||
&self,
|
||||
posts: &Vec<Post>,
|
||||
muted: Option<&Vec<String>>,
|
||||
) -> Vec<Post> {
|
||||
let muted = match muted {
|
||||
Some(m) => m,
|
||||
None => return posts.to_owned(),
|
||||
};
|
||||
|
||||
let mut out: Vec<Post> = Vec::new();
|
||||
|
||||
for mut post in posts.clone() {
|
||||
for phrase in muted {
|
||||
if post.content.contains(phrase) {
|
||||
post.context.content_warning = "Contains muted phrase".to_string();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
out.push(post);
|
||||
}
|
||||
|
||||
out
|
||||
}
|
||||
|
||||
/// Get all posts from the given user (from most recent).
|
||||
///
|
||||
/// # Arguments
|
||||
|
@ -533,7 +563,7 @@ impl DataManager {
|
|||
|
||||
let res = query_rows!(
|
||||
&conn,
|
||||
&format!("SELECT * FROM posts WHERE owner = $1 ORDER BY created DESC LIMIT 50"),
|
||||
&format!("SELECT * FROM posts WHERE owner = $1 ORDER BY created DESC LIMIT 12"),
|
||||
&[&(id as i64)],
|
||||
|x| { Self::get_post_from_row(x) }
|
||||
);
|
||||
|
@ -581,11 +611,21 @@ impl DataManager {
|
|||
let gpa = (good_posts as f32 / real_posts_count as f32) * 4.0;
|
||||
let gpa_rounded = format!("{gpa:.2}").parse::<f32>().unwrap();
|
||||
|
||||
self.0
|
||||
.1
|
||||
.set(format!("atto.user.gpa:{}", id), gpa_rounded.to_string())
|
||||
.await;
|
||||
let mut redis_con = self.0.1.get_con().await;
|
||||
|
||||
// expires in one day
|
||||
if redis_con
|
||||
.set_ex::<String, String, usize>(
|
||||
format!("atto.user.gpa:{}", id),
|
||||
gpa_rounded.to_string(),
|
||||
86400,
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
return 0.0;
|
||||
};
|
||||
|
||||
// ...
|
||||
gpa_rounded
|
||||
}
|
||||
|
||||
|
|
|
@ -225,6 +225,12 @@ 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<String>,
|
||||
}
|
||||
|
||||
fn mime_avif() -> String {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue