add: don't allow poll creator to vote

add: unfollow user when you block them
add: force other user to unfollow you by blocking them
add: leave receiver communities when you block them
This commit is contained in:
trisua 2025-06-05 16:23:57 -04:00
parent 5a330b7a18
commit 460e87e90e
11 changed files with 165 additions and 17 deletions

View file

@ -56,6 +56,14 @@ fn check_banned(value: &Value, _: &HashMap<String, Value>) -> tera::Result<Value
.into())
}
fn remove_script_tags(value: &Value, _: &HashMap<String, Value>) -> tera::Result<Value> {
Ok(value
.as_str()
.unwrap()
.replace("</script>", "&lt;/script&gt;")
.into())
}
#[tokio::main(flavor = "multi_thread")]
async fn main() {
tracing_subscriber::fmt()
@ -86,6 +94,7 @@ async fn main() {
tera.register_filter("has_supporter", check_supporter);
tera.register_filter("has_staff_badge", check_staff_badge);
tera.register_filter("has_banned", check_banned);
tera.register_filter("remove_script_tags", remove_script_tags);
let client = Client::new();

View file

@ -1343,6 +1343,7 @@
window.POLL_OPTION_B = \"\";
window.POLL_OPTION_C = \"\";
window.POLL_OPTION_D = \"\";
window.POLL_EXPIRES = null;
window.get_poll_data = () => {
if (!POLL_OPTION_A && !POLL_OPTION_B) {
@ -1353,11 +1354,16 @@
return [false, \"At least 2 options are required for a poll\"];
}
if (POLL_EXPIRES < 0) {
return [false, \"Polls cannot time travel\"];
}
return [true, {
option_a: POLL_OPTION_A,
option_b: POLL_OPTION_B,
option_c: POLL_OPTION_C,
option_d: POLL_OPTION_D
option_d: POLL_OPTION_D,
expires: POLL_EXPIRES,
}];
}"))
@ -1395,7 +1401,12 @@
(div
("class" "card flex flex-col gap-2")
(b (text "Option D"))
(input ("type" "text") ("placeholder" "option D") ("onchange" "window.POLL_OPTION_D = event.target.value"))))
(input ("type" "text") ("placeholder" "option D") ("onchange" "window.POLL_OPTION_D = event.target.value")))
(div
("class" "card flex flex-col gap-2")
(b (text "Expires"))
(input ("type" "date") ("onchange" "window.POLL_EXPIRES = event.target.valueAsDate.getTime() - new Date().getTime()"))))
(hr)
(div
("class" "flex justify-between")
@ -1414,9 +1425,13 @@
("class" "card tertiary w-full flex flex-col gap-2")
(text "{% set total = poll[0].votes_a + poll[0].votes_b + poll[0].votes_c + poll[0].votes_d %}")
(text "{% if poll[1] -%}")
(text "{% if poll[1] or poll[2] or user and user.id == poll[0].owner -%}")
; already voted, show results
(text "{% if poll[1] %}")
(span ("class" "fade") (text "You've already voted!"))
(text "{% elif poll[2] %}")
(span ("class" "fade") (text "Poll ended!"))
(text "{% endif %}")
; option a
(div
@ -1484,5 +1499,12 @@
; show expiration date + totals
(div
("class" "flex w-full flex-wrap gap-2")
(span ("class" "notification chip") (text "{{ total }} votes"))))
(span ("class" "notification chip") (text "{{ total }} votes"))
(span
("class" "notification chip")
(text "Expires in ")
(span
("class" "poll_date")
("data-created" "{{ poll[0].created }}")
("data-expires" "{{ poll[0].expires }}")))))
(text "{%- endmacro %}")

View file

@ -855,7 +855,7 @@
(script
("type" "application/json")
("id" "settings_json")
(text "{{ profile.settings|json_encode()|safe }}"))
(text "{{ profile.settings|json_encode()|remove_script_tags|safe }}"))
(script
(text "setTimeout(() => {
const ui = ns(\"ui\");

View file

@ -91,6 +91,7 @@
atto.disconnect_observers();
atto.remove_false_options();
atto.clean_date_codes();
atto.clean_poll_date_codes();
atto.link_filter();
atto[\"hooks::scroll\"](document.body, document.documentElement);

View file

@ -91,10 +91,11 @@ media_theme_pref();
self.define("rel_date", (_, date) => {
// stolen and slightly modified because js dates suck
const diff = (new Date().getTime() - date.getTime()) / 1000;
const diff = Math.abs((new Date().getTime() - date.getTime()) / 1000);
const day_diff = Math.floor(diff / 86400);
if (Number.isNaN(day_diff) || day_diff < 0 || day_diff >= 31) {
console.log(diff);
return;
}
@ -162,6 +163,48 @@ media_theme_pref();
}
});
self.define("clean_poll_date_codes", ({ $ }) => {
for (const element of Array.from(
document.querySelectorAll(".poll_date"),
)) {
const created = Number.parseInt(
element.getAttribute("data-created"),
);
const expires = Number.parseInt(
element.getAttribute("data-expires"),
);
const then = new Date(created + expires);
if (Number.isNaN(element.innerText)) {
continue;
}
element.setAttribute("title", then.toLocaleString());
console.log($.rel_date(then));
const pretty =
$.rel_date(then)
.replaceAll(" minutes ago", "m")
.replaceAll(" minute ago", "m")
.replaceAll(" hours ago", "h")
.replaceAll(" hour ago", "h")
.replaceAll(" days ago", "d")
.replaceAll(" day ago", "d")
.replaceAll(" weeks ago", "w")
.replaceAll(" week ago", "w")
.replaceAll(" months ago", "m")
.replaceAll(" month ago", "m")
.replaceAll(" years ago", "y")
.replaceAll(" year ago", "y") || "";
element.innerText =
pretty === undefined ? then.toLocaleDateString() : pretty;
element.style.display = "inline-block";
}
});
self.define("copy_text", ({ $ }, text) => {
navigator.clipboard.writeText(text);
$.toast("success", "Copied!");

View file

@ -70,7 +70,16 @@ pub async fn create_request(
let poll_id = if let Some(p) = req.poll {
match data
.create_poll(Poll::new(
user.id, 86400000, p.option_a, p.option_b, p.option_c, p.option_d,
user.id,
if let Some(expires) = p.expires {
expires
} else {
86400000
},
p.option_a,
p.option_b,
p.option_c,
p.option_d,
))
.await
{

View file

@ -429,6 +429,8 @@ pub struct CreatePostPoll {
pub option_b: String,
pub option_c: String,
pub option_d: String,
#[serde(default)]
pub expires: Option<usize>,
}
#[derive(Deserialize)]