add: more mod panel stats add: show user invite in mod panel add:

ability to share to twitter/bluesky
This commit is contained in:
trisua 2025-06-24 13:18:52 -04:00
parent 66beef6b1d
commit 2676340fba
8 changed files with 201 additions and 22 deletions

View file

@ -182,6 +182,7 @@ version = "1.0.0"
"mod_panel:label.warnings" = "Warnings" "mod_panel:label.warnings" = "Warnings"
"mod_panel:label.create_warning" = "Create warning" "mod_panel:label.create_warning" = "Create warning"
"mod_panel:label.associations" = "Associations" "mod_panel:label.associations" = "Associations"
"mod_panel:label.invited_by" = "Invited by"
"requests:label.requests" = "Requests" "requests:label.requests" = "Requests"
"requests:label.community_join_request" = "Community join request" "requests:label.community_join_request" = "Community join request"

View file

@ -132,6 +132,7 @@
("class" "card flex flex-col post gap-2 post:{{ post.id }} {% if secondary -%}secondary{%- endif %}") ("class" "card flex flex-col post gap-2 post:{{ post.id }} {% if secondary -%}secondary{%- endif %}")
("data-community" "{{ post.community }}") ("data-community" "{{ post.community }}")
("data-ownsup" "{{ owner.permissions|has_supporter }}") ("data-ownsup" "{{ owner.permissions|has_supporter }}")
("data-id" "{{ post.id }}")
("hook" "verify_emojis") ("hook" "verify_emojis")
(div (div
("class" "w-full flex gap-2") ("class" "w-full flex gap-2")
@ -214,7 +215,7 @@
("class" "flush") ("class" "flush")
("href" "/post/{{ post.id }}") ("href" "/post/{{ post.id }}")
(h2 (h2
("id" "post-content:{{ post.id }}") ("id" "post_content:{{ post.id }}")
("class" "no_p_margin post_content") ("class" "no_p_margin post_content")
("hook" "long") ("hook" "long")
(text "{{ post.title }}")) (text "{{ post.title }}"))
@ -223,7 +224,6 @@
(text "{% else %}") (text "{% else %}")
(text "{% if not post.context.content_warning -%}") (text "{% if not post.context.content_warning -%}")
(span (span
("id" "post-content:{{ post.id }}")
("class" "no_p_margin post_content") ("class" "no_p_margin post_content")
("hook" "long") ("hook" "long")
@ -234,7 +234,8 @@
(text "{%- endif %}") (text "{%- endif %}")
; content ; content
(text "{{ post.content|markdown|safe }} {% if expect_repost -%} {% if repost -%} {{ self::post(post=repost[1], owner=repost[0], secondary=not secondary, community=false, show_community=false, can_manage_post=false) }} {% else %}") (span ("id" "post_content:{{ post.id }}") (text "{{ post.content|markdown|safe }}"))
(text "{% if expect_repost -%} {% if repost -%} {{ self::post(post=repost[1], owner=repost[0], secondary=not secondary, community=false, show_community=false, can_manage_post=false) }} {% else %}")
(div (div
("class" "card lowered red flex items-center gap-2") ("class" "card lowered red flex items-center gap-2")
(text "{{ icon \"frown\" }}") (text "{{ icon \"frown\" }}")
@ -251,7 +252,6 @@
(div (div
("class" "flex flex-col gap-2") ("class" "flex flex-col gap-2")
(span (span
("id" "post-content:{{ post.id }}")
("class" "no_p_margin post_content") ("class" "no_p_margin post_content")
("hook" "long") ("hook" "long")
@ -261,7 +261,8 @@
(text "{% endif %}") (text "{% endif %}")
; content ; content
(text "{{ post.content|markdown|safe }} {% if expect_repost -%} {% if repost -%} {{ self::post(post=repost[1], owner=repost[0], secondary=not secondary, community=false, show_community=false, can_manage_post=false) }} {% else %}") (span ("id" "post_content:{{ post.id }}") (text "{{ post.content|markdown|safe }}"))
(text "{% if expect_repost -%} {% if repost -%} {{ self::post(post=repost[1], owner=repost[0], secondary=not secondary, community=false, show_community=false, can_manage_post=false) }} {% else %}")
(div (div
("class" "card lowered red flex items-center gap-2") ("class" "card lowered red flex items-center gap-2")
(text "{{ icon \"frown\" }}") (text "{{ icon \"frown\" }}")
@ -338,7 +339,32 @@
(text "{{ icon \"quote\" }}") (text "{{ icon \"quote\" }}")
(span (span
(text "{{ text \"communities:label.quote_post\" }}"))) (text "{{ text \"communities:label.quote_post\" }}")))
(button
("onclick" "trigger('me::intent_twitter', [trigger('me::gen_share', [{ q: '{{ post.context.answering }}', p: '{{ post.id }}' }, 280, true])])")
(icon (text "bird"))
(span
(text "Twitter")))
(button
("onclick" "trigger('me::intent_bluesky', [trigger('me::gen_share', [{ q: '{{ post.context.answering }}', p: '{{ post.id }}' }, 280, true])])")
(icon (text "cloud"))
(span
(text "BlueSky")))
(text "{%- endif %}") (text "{%- endif %}")
(text "{% if user.id != post.owner -%}")
(b
("class" "title")
(text "{{ text \"general:label.safety\" }}"))
(button
("class" "red")
("onclick" "trigger('me::report', ['{{ post.id }}', 'post'])")
(text "{{ icon \"flag\" }}")
(span
(text "{{ text \"general:action.report\" }}")))
(text "{%- endif %} {% if (user.id == post.owner) or is_helper or can_manage_post %}")
(b
("class" "title")
(text "{{ text \"general:action.manage\" }}"))
; forge stuff
(text "{% if community and community.is_forge -%} {% if post.is_open -%}") (text "{% if community and community.is_forge -%} {% if post.is_open -%}")
(button (button
("class" "green") ("class" "green")
@ -354,20 +380,7 @@
(span (span
(text "{{ text \"forge:action.reopen\" }}"))) (text "{{ text \"forge:action.reopen\" }}")))
(text "{%- endif %} {%- endif %}") (text "{%- endif %} {%- endif %}")
(text "{% if user.id != post.owner -%}") ; owner stuff
(b
("class" "title")
(text "{{ text \"general:label.safety\" }}"))
(button
("class" "red")
("onclick" "trigger('me::report', ['{{ post.id }}', 'post'])")
(text "{{ icon \"flag\" }}")
(span
(text "{{ text \"general:action.report\" }}")))
(text "{%- endif %} {% if (user.id == post.owner) or is_helper or can_manage_post %}")
(b
("class" "title")
(text "{{ text \"general:action.manage\" }}"))
(text "{% if user.id == post.owner -%}") (text "{% if user.id == post.owner -%}")
(a (a
("href" "/post/{{ post.id }}#/edit") ("href" "/post/{{ post.id }}#/edit")
@ -675,6 +688,7 @@
(span (span
("class" "no_p_margin") ("class" "no_p_margin")
("style" "font-weight: 500") ("style" "font-weight: 500")
("id" "question_content:{{ question.id }}")
(text "{{ question.content|markdown|safe }}")) (text "{{ question.content|markdown|safe }}"))
; question drawings ; question drawings
(text "{{ self::post_media(upload_ids=question.drawings) }}") (text "{{ self::post_media(upload_ids=question.drawings) }}")

View file

@ -202,6 +202,20 @@
(text "{% for user in associations -%}") (text "{% for user in associations -%}")
(text "{{ components::user_plate(user=user, show_menu=false) }}") (text "{{ components::user_plate(user=user, show_menu=false) }}")
(text "{%- endfor %}"))) (text "{%- endfor %}")))
(text "{% if invite -%}")
(div
("class" "card-nest w-full")
(div
("class" "card small flex items-center justify-between gap-2")
(div
("class" "flex items-center gap-2")
(text "{{ icon \"ticket\" }}")
(span
(text "{{ text \"mod_panel:label.invited_by\" }}"))))
(div
("class" "card lowered flex flex-wrap gap-2")
(text "{{ components::user_plate(user=invite[0], show_menu=false) }}")))
(text "{%- endif %}")
(div (div
("class" "card-nest w-full") ("class" "card-nest w-full")
(div (div

View file

@ -29,6 +29,15 @@
(b (b
(text "Socket tasks: ")) (text "Socket tasks: "))
(span (span
(text "{{ (active_users_chats + active_users) * 3 }}"))))))) (text "{{ (active_users_chats + active_users) * 3 }}"))))
(hr)
(ul
(li (b (text "Users: ")) (span (text "{{ table_users }}")))
(li (b (text "IP bans: ")) (span (text "{{ table_ipbans }}")))
(li (b (text "Invite codes: ")) (span (text "{{ table_invite_codes }}")))
(li (b (text "Posts: ")) (span (text "{{ table_posts }}")))
(li (b (text "Uploads: ")) (span (text "{{ table_uploads }}")))
(li (b (text "Communities: ")) (span (text "{{ table_communities }}")))))))
(text "{% endblock %}") (text "{% endblock %}")

View file

@ -1227,7 +1227,14 @@ ${option.input_element_type === "textarea" ? `${option.value}</textarea>` : ""}
).text(); ).text();
self.IO_DATA_WAITING = false; self.IO_DATA_WAITING = false;
self.IO_DATA_ELEMENT.querySelector("[ui_ident=loading_skel]").remove();
const loading_skel = self.IO_DATA_ELEMENT.querySelector(
"[ui_ident=loading_skel]",
);
if (loading_skel) {
loading_skel.remove();
}
if ( if (
text.includes(`!<!-- observer_disconnect_${window.BUILD_CODE} -->`) text.includes(`!<!-- observer_disconnect_${window.BUILD_CODE} -->`)

View file

@ -531,6 +531,78 @@
return out; return out;
}); });
// share intents
self.define(
"gen_share",
(
_,
ids = { q: "0", p: "0" },
target_length = 280,
include_link = true,
) => {
const part_1 = (
document.getElementById(`question_content:${ids.q}`) || {
innerText: "",
}
).innerText;
const part_2 = document.getElementById(
`post_content:${ids.p}`,
).innerText;
// ...
const link =
include_link !== false
? `${window.location.origin}/post/${ids.p}`
: "";
const link_size = link.length;
target_length -= link_size;
let out = "";
const separator = " — ";
const part_2_size = target_length / 2 - 1;
const sep_size = separator.length;
const part_1_size = target_length / 2 - sep_size;
if (part_1 !== "") {
out +=
part_1_size > part_1.length
? part_1
: part_1.substring(0, part_1_size);
out += separator;
}
if (part_2 !== "") {
out +=
part_2_size > part_2.length
? part_2
: part_2.substring(0, part_2_size);
}
out += ` ${link}`;
return out;
},
);
self.define("intent_twitter", (_, text) => {
window.open(
`https://twitter.com/intent/tweet?text=${encodeURIComponent(text)}`,
);
trigger("atto::toast", ["success", "Opened intent!"]);
});
self.define("intent_bluesky", (_, text) => {
window.open(
`https://bsky.app/intent/compose?text=${encodeURIComponent(text)}`,
);
trigger("atto::toast", ["success", "Opened intent!"]);
});
// token switcher // token switcher
self.define("append_associations", (_, tokens) => { self.define("append_associations", (_, tokens) => {
fetch("/api/v1/auth/user/me/append_associations", { fetch("/api/v1/auth/user/me/append_associations", {

View file

@ -194,10 +194,23 @@ pub async fn manage_profile_request(
out out
}; };
let invite_code = if profile.invite_code != 0 {
match data.0.get_invite_code_by_id(profile.invite_code).await {
Ok(i) => match data.0.get_user_by_id(i.owner).await {
Ok(u) => Some((u, i)),
Err(e) => return Err(Html(render_error(e, &jar, &data, &Some(user)).await)),
},
Err(e) => return Err(Html(render_error(e, &jar, &data, &Some(user)).await)),
}
} else {
None
};
let lang = get_lang!(jar, data.0); let lang = get_lang!(jar, data.0);
let mut context = initial_context(&data.0.0.0, lang, &Some(user)).await; let mut context = initial_context(&data.0.0.0, lang, &Some(user)).await;
context.insert("profile", &profile); context.insert("profile", &profile);
context.insert("invite", &invite_code);
context.insert("associations", &associations); context.insert("associations", &associations);
// return // return
@ -298,6 +311,35 @@ pub async fn stats_request(jar: CookieJar, Extension(data): Extension<State>) ->
.unwrap(), .unwrap(),
); );
context.insert(
"table_users",
&data.0.get_table_row_count("users").await.unwrap_or(0),
);
context.insert(
"table_posts",
&data.0.get_table_row_count("posts").await.unwrap_or(0),
);
context.insert(
"table_invite_codes",
&data
.0
.get_table_row_count("invite_codes")
.await
.unwrap_or(0),
);
context.insert(
"table_uploads",
&data.0.get_table_row_count("uploads").await.unwrap_or(0),
);
context.insert(
"table_communities",
&data.0.get_table_row_count("communities").await.unwrap_or(0),
);
context.insert(
"table_ipbans",
&data.0.get_table_row_count("ipbans").await.unwrap_or(0),
);
// return // return
Ok(Html(data.1.render("mod/stats.html", &context).unwrap())) Ok(Html(data.1.render("mod/stats.html", &context).unwrap()))
} }

View file

@ -1,6 +1,6 @@
use crate::model::{Error, Result}; use crate::model::{Error, Result};
use super::{DataManager, drivers::common}; use super::{DataManager, drivers::common};
use oiseau::{cache::Cache, execute}; use oiseau::{cache::Cache, execute, query_row, params};
pub const NAME_REGEX: &str = r"[^\w_\-\.,!]+"; pub const NAME_REGEX: &str = r"[^\w_\-\.,!]+";
@ -52,6 +52,26 @@ impl DataManager {
Ok(()) Ok(())
} }
pub async fn get_table_row_count(&self, table: &str) -> Result<i32> {
let conn = match self.0.connect().await {
Ok(c) => c,
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
};
let res = query_row!(
&conn,
&format!("SELECT COUNT(*)::int FROM {}", table),
params![],
|x| Ok(x.get::<usize, i32>(0))
);
if let Err(e) = res {
return Err(Error::DatabaseError(e.to_string()));
}
Ok(res.unwrap())
}
} }
#[macro_export] #[macro_export]