add: "all" timeline
add: only show most popular posts from the past week
This commit is contained in:
parent
fc9c0f294e
commit
24f67221ca
13 changed files with 143 additions and 27 deletions
|
@ -58,8 +58,9 @@ pub const COMMUNITIES_CREATE_POST: &str =
|
||||||
include_str!("./public/html/communities/create_post.html");
|
include_str!("./public/html/communities/create_post.html");
|
||||||
|
|
||||||
pub const TIMELINES_HOME: &str = include_str!("./public/html/timelines/home.html");
|
pub const TIMELINES_HOME: &str = include_str!("./public/html/timelines/home.html");
|
||||||
pub const TIMELINES_FOLLOWING: &str = include_str!("./public/html/timelines/following.html");
|
|
||||||
pub const TIMELINES_POPULAR: &str = include_str!("./public/html/timelines/popular.html");
|
pub const TIMELINES_POPULAR: &str = include_str!("./public/html/timelines/popular.html");
|
||||||
|
pub const TIMELINES_FOLLOWING: &str = include_str!("./public/html/timelines/following.html");
|
||||||
|
pub const TIMELINES_ALL: &str = include_str!("./public/html/timelines/all.html");
|
||||||
|
|
||||||
pub const MOD_AUDIT_LOG: &str = include_str!("./public/html/mod/audit_log.html");
|
pub const MOD_AUDIT_LOG: &str = include_str!("./public/html/mod/audit_log.html");
|
||||||
pub const MOD_REPORTS: &str = include_str!("./public/html/mod/reports.html");
|
pub const MOD_REPORTS: &str = include_str!("./public/html/mod/reports.html");
|
||||||
|
@ -190,8 +191,9 @@ pub(crate) async fn write_assets(config: &Config) -> PathBufD {
|
||||||
write_template!(html_path->"communities/create_post.html"(crate::assets::COMMUNITIES_CREATE_POST) --config=config);
|
write_template!(html_path->"communities/create_post.html"(crate::assets::COMMUNITIES_CREATE_POST) --config=config);
|
||||||
|
|
||||||
write_template!(html_path->"timelines/home.html"(crate::assets::TIMELINES_HOME) -d "timelines" --config=config);
|
write_template!(html_path->"timelines/home.html"(crate::assets::TIMELINES_HOME) -d "timelines" --config=config);
|
||||||
write_template!(html_path->"timelines/following.html"(crate::assets::TIMELINES_FOLLOWING) --config=config);
|
|
||||||
write_template!(html_path->"timelines/popular.html"(crate::assets::TIMELINES_POPULAR) --config=config);
|
write_template!(html_path->"timelines/popular.html"(crate::assets::TIMELINES_POPULAR) --config=config);
|
||||||
|
write_template!(html_path->"timelines/following.html"(crate::assets::TIMELINES_FOLLOWING) --config=config);
|
||||||
|
write_template!(html_path->"timelines/all.html"(crate::assets::TIMELINES_ALL) --config=config);
|
||||||
|
|
||||||
write_template!(html_path->"mod/audit_log.html"(crate::assets::MOD_AUDIT_LOG) -d "mod" --config=config);
|
write_template!(html_path->"mod/audit_log.html"(crate::assets::MOD_AUDIT_LOG) -d "mod" --config=config);
|
||||||
write_template!(html_path->"mod/reports.html"(crate::assets::MOD_REPORTS) --config=config);
|
write_template!(html_path->"mod/reports.html"(crate::assets::MOD_REPORTS) --config=config);
|
||||||
|
|
|
@ -3,8 +3,9 @@ version = "1.0.0"
|
||||||
|
|
||||||
[data]
|
[data]
|
||||||
"general:link.home" = "Home"
|
"general:link.home" = "Home"
|
||||||
"general:link.following" = "Following"
|
|
||||||
"general:link.popular" = "Popular"
|
"general:link.popular" = "Popular"
|
||||||
|
"general:link.following" = "Following"
|
||||||
|
"general:link.all" = "All"
|
||||||
"general:link.communities" = "Communities"
|
"general:link.communities" = "Communities"
|
||||||
"general:link.next" = "Next"
|
"general:link.next" = "Next"
|
||||||
"general:link.previous" = "Previous"
|
"general:link.previous" = "Previous"
|
||||||
|
@ -96,6 +97,7 @@ version = "1.0.0"
|
||||||
"settings:tab.profile" = "Profile"
|
"settings:tab.profile" = "Profile"
|
||||||
"settings:tab.theme" = "Theme"
|
"settings:tab.theme" = "Theme"
|
||||||
"settings:tab.sessions" = "Sessions"
|
"settings:tab.sessions" = "Sessions"
|
||||||
|
"settings:tab.images" = "Images"
|
||||||
"settings:label.change_password" = "Change password"
|
"settings:label.change_password" = "Change password"
|
||||||
"settings:label.current_password" = "Current password"
|
"settings:label.current_password" = "Current password"
|
||||||
"settings:label.delete_account" = "Delete account"
|
"settings:label.delete_account" = "Delete account"
|
||||||
|
|
|
@ -5,6 +5,7 @@ mod routes;
|
||||||
mod sanitize;
|
mod sanitize;
|
||||||
|
|
||||||
use assets::{init_dirs, write_assets};
|
use assets::{init_dirs, write_assets};
|
||||||
|
use tetratto_core::model::permissions::FinePermission;
|
||||||
pub use tetratto_core::*;
|
pub use tetratto_core::*;
|
||||||
|
|
||||||
use axum::{Extension, Router};
|
use axum::{Extension, Router};
|
||||||
|
@ -26,6 +27,13 @@ fn color_escape(value: &Value, _: &HashMap<String, Value>) -> tera::Result<Value
|
||||||
Ok(sanitize::color_escape(value.as_str().unwrap()).into())
|
Ok(sanitize::color_escape(value.as_str().unwrap()).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_supporter(value: &Value, _: &HashMap<String, Value>) -> tera::Result<Value> {
|
||||||
|
Ok(FinePermission::from_bits(value.as_u64().unwrap() as u32)
|
||||||
|
.unwrap()
|
||||||
|
.check(FinePermission::SUPPORTER)
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
tracing_subscriber::fmt()
|
tracing_subscriber::fmt()
|
||||||
|
@ -46,6 +54,7 @@ async fn main() {
|
||||||
let mut tera = Tera::new(&format!("{html_path}/**/*")).unwrap();
|
let mut tera = Tera::new(&format!("{html_path}/**/*")).unwrap();
|
||||||
tera.register_filter("markdown", render_markdown);
|
tera.register_filter("markdown", render_markdown);
|
||||||
tera.register_filter("color", color_escape);
|
tera.register_filter("color", color_escape);
|
||||||
|
tera.register_filter("has_supporter", check_supporter);
|
||||||
|
|
||||||
let client = Client::new();
|
let client = Client::new();
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{% extends "root.html" %} {% block head %}
|
{% extends "root.html" %} {% block head %}
|
||||||
<title>Create post - {{ config.name }}</title>
|
<title>Create post - {{ config.name }}</title>
|
||||||
{% endblock %} {% block body %} {{ macros::nav(selected="") }}
|
{% endblock %} {% block body %} {{ macros::nav() }}
|
||||||
<main class="flex flex-col gap-2">
|
<main class="flex flex-col gap-2">
|
||||||
<div class="card-nest">
|
<div class="card-nest">
|
||||||
<div class="card small flex items-center justify-between gap-2">
|
<div class="card small flex items-center justify-between gap-2">
|
||||||
|
|
|
@ -3,17 +3,20 @@
|
||||||
{% endblock %} {% block body %} {{ macros::nav() }}
|
{% endblock %} {% block body %} {{ macros::nav() }}
|
||||||
<main class="flex flex-col gap-2">
|
<main class="flex flex-col gap-2">
|
||||||
<div class="pillmenu">
|
<div class="pillmenu">
|
||||||
<a href="#/general" data-tab-button="general" class="active"
|
<a href="#/general" data-tab-button="general" class="active">
|
||||||
>{{ text "settings:tab.general" }}</a
|
{{ icon "settings" }}
|
||||||
>
|
<span>{{ text "settings:tab.general" }}</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
<a href="#/profile" data-tab-button="profile"
|
<a href="#/images" data-tab-button="images">
|
||||||
>{{ text "settings:tab.profile" }}</a
|
{{ icon "image" }}
|
||||||
>
|
<span>{{ text "settings:tab.images" }}</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
<a href="#/members" data-tab-button="members"
|
<a href="#/members" data-tab-button="members">
|
||||||
>{{ text "communities:tab.members" }}</a
|
{{ icon "users-round" }}
|
||||||
>
|
<span>{{ text "communities:tab.members" }}</span>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="w-full flex flex-col gap-2" data-tab="general">
|
<div class="w-full flex flex-col gap-2" data-tab="general">
|
||||||
|
@ -159,7 +162,7 @@
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="card tertiary w-full hidden flex flex-col gap-2"
|
class="card tertiary w-full hidden flex flex-col gap-2"
|
||||||
data-tab="profile"
|
data-tab="images"
|
||||||
>
|
>
|
||||||
<div class="card-nest" ui_ident="change_avatar">
|
<div class="card-nest" ui_ident="change_avatar">
|
||||||
<div class="card small">
|
<div class="card small">
|
||||||
|
|
|
@ -534,6 +534,10 @@ user %} {% if user.settings.theme_hue %}
|
||||||
{{ components::theme_color(color=user.settings.theme_color_secondary, css="color-secondary") }}
|
{{ components::theme_color(color=user.settings.theme_color_secondary, css="color-secondary") }}
|
||||||
{{ components::theme_color(color=user.settings.theme_color_text_secondary, css="color-text-secondary") }}
|
{{ components::theme_color(color=user.settings.theme_color_text_secondary, css="color-text-secondary") }}
|
||||||
{{ components::theme_color(color=user.settings.theme_color_secondary_lowered, css="color-secondary-lowered") }}
|
{{ components::theme_color(color=user.settings.theme_color_secondary_lowered, css="color-secondary-lowered") }}
|
||||||
|
|
||||||
|
{% if user.permissions|has_supporter %}
|
||||||
|
<style>{{ user.settings.theme_custom_css }}</style>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %} {%- endmacro %} {% macro theme_color(color, css) -%} {% if color %}
|
{% endif %} {%- endmacro %} {% macro theme_color(color, css) -%} {% if color %}
|
||||||
<!-- prettier-ignore -->
|
<!-- prettier-ignore -->
|
||||||
|
|
|
@ -163,19 +163,24 @@
|
||||||
<span>{{ text "general:link.home" }}</span>
|
<span>{{ text "general:link.home" }}</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
<a href="/popular" class="{% if selected == 'popular' %}active{% endif %}">
|
||||||
|
{{ icon "trending-up" }}
|
||||||
|
<span>{{ text "general:link.popular" }}</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
{% if user %}
|
{% if user %}
|
||||||
<a
|
<a
|
||||||
href="/following"
|
href="/following"
|
||||||
class="{% if selected == 'following' %}active{% endif %}"
|
class="{% if selected == 'following' %}active{% endif %}"
|
||||||
>
|
>
|
||||||
{{ icon "earth" }}
|
{{ icon "rss" }}
|
||||||
<span>{{ text "general:link.following" }}</span>
|
<span>{{ text "general:link.following" }}</span>
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<a href="/popular" class="{% if selected == 'popular' %}active{% endif %}">
|
<a href="/all" class="{% if selected == 'all' %}active{% endif %}">
|
||||||
{{ icon "trending-up" }}
|
{{ icon "earth" }}
|
||||||
<span>{{ text "general:link.popular" }}</span>
|
<span>{{ text "general:link.all" }}</span>
|
||||||
</a>
|
</a>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{% extends "root.html" %} {% block head %}
|
{% extends "root.html" %} {% block head %}
|
||||||
<title>{{ profile.username }} (warning) - {{ config.name }}</title>
|
<title>{{ profile.username }} (warning) - {{ config.name }}</title>
|
||||||
{% endblock %} {% block body %} {{ macros::nav(selected="") }}
|
{% endblock %} {% block body %} {{ macros::nav() }}
|
||||||
<main class="flex flex-col gap-2">
|
<main class="flex flex-col gap-2">
|
||||||
<div class="card-nest">
|
<div class="card-nest">
|
||||||
<div class="card small flex items-center justify-between gap-2">
|
<div class="card small flex items-center justify-between gap-2">
|
||||||
|
|
20
crates/app/src/public/html/timelines/all.html
Normal file
20
crates/app/src/public/html/timelines/all.html
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
{% extends "root.html" %} {% block head %}
|
||||||
|
<title>Latest posts - {{ config.name }}</title>
|
||||||
|
{% endblock %} {% block body %} {{ macros::nav() }}
|
||||||
|
<main class="flex flex-col gap-2">
|
||||||
|
{{ macros::timelines_nav(selected="all") }}
|
||||||
|
|
||||||
|
<!-- prettier-ignore -->
|
||||||
|
<div class="card w-full flex flex-col gap-2">
|
||||||
|
{% for post in list %}
|
||||||
|
{% 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) }}
|
||||||
|
{% else %}
|
||||||
|
{{ components::post(post=post[0], owner=post[1], secondary=true, community=post[2]) }}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{{ components::pagination(page=page, items=list|length) }}
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
{% endblock %}
|
|
@ -62,6 +62,35 @@ pub async fn index_request(
|
||||||
Html(data.1.render("timelines/home.html", &context).unwrap())
|
Html(data.1.render("timelines/home.html", &context).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `/popular`
|
||||||
|
pub async fn popular_request(
|
||||||
|
jar: CookieJar,
|
||||||
|
Extension(data): Extension<State>,
|
||||||
|
Query(req): Query<PaginatedQuery>,
|
||||||
|
) -> impl IntoResponse {
|
||||||
|
let data = data.read().await;
|
||||||
|
let user = get_user_from_token!(jar, data.0);
|
||||||
|
|
||||||
|
let list = match data.0.get_popular_posts(12, req.page, 604_800_000).await {
|
||||||
|
Ok(l) => match data
|
||||||
|
.0
|
||||||
|
.fill_posts_with_community(l, if let Some(ref ua) = user { ua.id } else { 0 })
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(l) => l,
|
||||||
|
Err(e) => return Html(render_error(e, &jar, &data, &user).await),
|
||||||
|
},
|
||||||
|
Err(e) => return Html(render_error(e, &jar, &data, &user).await),
|
||||||
|
};
|
||||||
|
|
||||||
|
let lang = get_lang!(jar, data.0);
|
||||||
|
let mut context = initial_context(&data.0.0, lang, &user).await;
|
||||||
|
|
||||||
|
context.insert("list", &list);
|
||||||
|
context.insert("page", &req.page);
|
||||||
|
Html(data.1.render("timelines/popular.html", &context).unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
/// `/following`
|
/// `/following`
|
||||||
pub async fn following_request(
|
pub async fn following_request(
|
||||||
jar: CookieJar,
|
jar: CookieJar,
|
||||||
|
@ -100,8 +129,8 @@ pub async fn following_request(
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `/popular`
|
/// `/all`
|
||||||
pub async fn popular_request(
|
pub async fn all_request(
|
||||||
jar: CookieJar,
|
jar: CookieJar,
|
||||||
Extension(data): Extension<State>,
|
Extension(data): Extension<State>,
|
||||||
Query(req): Query<PaginatedQuery>,
|
Query(req): Query<PaginatedQuery>,
|
||||||
|
@ -109,7 +138,7 @@ pub async fn popular_request(
|
||||||
let data = data.read().await;
|
let data = data.read().await;
|
||||||
let user = get_user_from_token!(jar, data.0);
|
let user = get_user_from_token!(jar, data.0);
|
||||||
|
|
||||||
let list = match data.0.get_popular_posts(12, req.page).await {
|
let list = match data.0.get_latest_posts(12, req.page).await {
|
||||||
Ok(l) => match data
|
Ok(l) => match data
|
||||||
.0
|
.0
|
||||||
.fill_posts_with_community(l, if let Some(ref ua) = user { ua.id } else { 0 })
|
.fill_posts_with_community(l, if let Some(ref ua) = user { ua.id } else { 0 })
|
||||||
|
@ -126,7 +155,7 @@ pub async fn popular_request(
|
||||||
|
|
||||||
context.insert("list", &list);
|
context.insert("list", &list);
|
||||||
context.insert("page", &req.page);
|
context.insert("page", &req.page);
|
||||||
Html(data.1.render("timelines/popular.html", &context).unwrap())
|
Html(data.1.render("timelines/all.html", &context).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `/notifs`
|
/// `/notifs`
|
||||||
|
|
|
@ -18,8 +18,9 @@ pub fn routes() -> Router {
|
||||||
Router::new()
|
Router::new()
|
||||||
// misc
|
// misc
|
||||||
.route("/", get(misc::index_request))
|
.route("/", get(misc::index_request))
|
||||||
.route("/following", get(misc::following_request))
|
|
||||||
.route("/popular", get(misc::popular_request))
|
.route("/popular", get(misc::popular_request))
|
||||||
|
.route("/following", get(misc::following_request))
|
||||||
|
.route("/all", get(misc::all_request))
|
||||||
.route("/notifs", get(misc::notifications_request))
|
.route("/notifs", get(misc::notifications_request))
|
||||||
.route("/doc/{*file_name}", get(misc::markdown_document_request))
|
.route("/doc/{*file_name}", get(misc::markdown_document_request))
|
||||||
.fallback_service(get(misc::not_found))
|
.fallback_service(get(misc::not_found))
|
||||||
|
|
|
@ -308,7 +308,13 @@ impl DataManager {
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * `batch` - the limit of posts in each page
|
/// * `batch` - the limit of posts in each page
|
||||||
/// * `page` - the page number
|
/// * `page` - the page number
|
||||||
pub async fn get_popular_posts(&self, batch: usize, page: usize) -> Result<Vec<Post>> {
|
/// * `cutoff` - the maximum number of milliseconds ago the post could have been created
|
||||||
|
pub async fn get_popular_posts(
|
||||||
|
&self,
|
||||||
|
batch: usize,
|
||||||
|
page: usize,
|
||||||
|
cutoff: usize,
|
||||||
|
) -> Result<Vec<Post>> {
|
||||||
let conn = match self.connect().await {
|
let conn = match self.connect().await {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
|
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
|
||||||
|
@ -316,7 +322,37 @@ impl DataManager {
|
||||||
|
|
||||||
let res = query_rows!(
|
let res = query_rows!(
|
||||||
&conn,
|
&conn,
|
||||||
"SELECT * FROM posts WHERE replying_to = 0 AND NOT context LIKE '%\"is_nsfw\":true%' ORDER BY likes DESC, created ASC LIMIT $1 OFFSET $2",
|
"SELECT * FROM posts WHERE replying_to = 0 AND NOT context LIKE '%\"is_nsfw\":true%' AND ($1 - created) < $2 ORDER BY likes DESC, created ASC LIMIT $3 OFFSET $4",
|
||||||
|
&[
|
||||||
|
&(unix_epoch_timestamp() as i64),
|
||||||
|
&(cutoff as i64),
|
||||||
|
&(batch as i64),
|
||||||
|
&((page * batch) as i64)
|
||||||
|
],
|
||||||
|
|x| { Self::get_post_from_row(x) }
|
||||||
|
);
|
||||||
|
|
||||||
|
if res.is_err() {
|
||||||
|
return Err(Error::GeneralNotFound("post".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(res.unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get posts from all communities, sorted by creation.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `batch` - the limit of posts in each page
|
||||||
|
/// * `page` - the page number
|
||||||
|
pub async fn get_latest_posts(&self, batch: usize, page: usize) -> Result<Vec<Post>> {
|
||||||
|
let conn = match self.connect().await {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
|
||||||
|
};
|
||||||
|
|
||||||
|
let res = query_rows!(
|
||||||
|
&conn,
|
||||||
|
"SELECT * FROM posts WHERE replying_to = 0 AND NOT context LIKE '%\"is_nsfw\":true%' ORDER BY created DESC LIMIT $1 OFFSET $2",
|
||||||
&[&(batch as i64), &((page * batch) as i64)],
|
&[&(batch as i64), &((page * batch) as i64)],
|
||||||
|x| { Self::get_post_from_row(x) }
|
|x| { Self::get_post_from_row(x) }
|
||||||
);
|
);
|
||||||
|
|
|
@ -120,8 +120,13 @@ pub struct UserSettings {
|
||||||
/// Hover state for secondary buttons.
|
/// Hover state for secondary buttons.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub theme_color_secondary_lowered: String,
|
pub theme_color_secondary_lowered: String,
|
||||||
|
/// Custom CSS input.
|
||||||
|
#[serde(default)]
|
||||||
|
pub theme_custom_css: String,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub disable_other_themes: bool,
|
pub disable_other_themes: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub disable_other_theme_css: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for User {
|
impl Default for User {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue