diff --git a/crates/app/src/public/html/timelines/popular_questions.html b/crates/app/src/public/html/timelines/popular_questions.html
new file mode 100644
index 0000000..9c015d3
--- /dev/null
+++ b/crates/app/src/public/html/timelines/popular_questions.html
@@ -0,0 +1,18 @@
+{% extends "root.html" %} {% block head %}
+
Popular (questions) - {{ config.name }}
+{% endblock %} {% block body %} {{ macros::nav() }}
+
+ {{ macros::timelines_nav(selected="popular") }} {{
+ macros::timelines_secondary_nav(posts="/popular",
+ questions="/popular/questions", selected="popular") }}
+
+
+
+ {% for question in list %}
+ {{ components::global_question(question=question, can_manage_questions=false, secondary=true) }}
+ {% endfor %}
+
+ {{ components::pagination(page=page, items=list|length) }}
+
+
+{% endblock %}
diff --git a/crates/app/src/routes/pages/misc.rs b/crates/app/src/routes/pages/misc.rs
index 8ea30f3..992cc58 100644
--- a/crates/app/src/routes/pages/misc.rs
+++ b/crates/app/src/routes/pages/misc.rs
@@ -196,6 +196,44 @@ pub async fn index_questions_request(
)
}
+/// `/popular/questions`
+pub async fn popular_questions_request(
+ jar: CookieJar,
+ Extension(data): Extension
,
+ Query(req): Query,
+) -> impl IntoResponse {
+ let data = data.read().await;
+ let user = match get_user_from_token!(jar, data.0) {
+ Some(ua) => ua,
+ None => {
+ return Html(render_error(Error::NotAllowed, &jar, &data, &None).await);
+ }
+ };
+
+ let list = match data
+ .0
+ .get_popular_global_questions(12, req.page, 604_800_000)
+ .await
+ {
+ Ok(l) => match data.0.fill_questions(l).await {
+ Ok(l) => l,
+ Err(e) => return Html(render_error(e, &jar, &data, &Some(user)).await),
+ },
+ Err(e) => return Html(render_error(e, &jar, &data, &Some(user)).await),
+ };
+
+ let lang = get_lang!(jar, data.0);
+ let mut context = initial_context(&data.0.0, lang, &Some(user)).await;
+
+ context.insert("list", &list);
+ context.insert("page", &req.page);
+ Html(
+ data.1
+ .render("timelines/popular_questions.html", &context)
+ .unwrap(),
+ )
+}
+
/// `/following/questions`
pub async fn following_questions_request(
jar: CookieJar,
diff --git a/crates/app/src/routes/pages/mod.rs b/crates/app/src/routes/pages/mod.rs
index 66eb71a..b3c2292 100644
--- a/crates/app/src/routes/pages/mod.rs
+++ b/crates/app/src/routes/pages/mod.rs
@@ -23,6 +23,7 @@ pub fn routes() -> Router {
.route("/all", get(misc::all_request))
// question timelines
.route("/questions", get(misc::index_questions_request))
+ .route("/popular/questions", get(misc::popular_questions_request))
.route(
"/following/questions",
get(misc::following_questions_request),
diff --git a/crates/core/src/database/questions.rs b/crates/core/src/database/questions.rs
index 5c221bc..dcabc13 100644
--- a/crates/core/src/database/questions.rs
+++ b/crates/core/src/database/questions.rs
@@ -15,6 +15,7 @@ use crate::{auto_method, execute, get, query_row, query_rows, params};
#[cfg(feature = "sqlite")]
use rusqlite::Row;
+use tetratto_shared::unix_epoch_timestamp;
#[cfg(feature = "postgres")]
use tokio_postgres::Row;
@@ -246,6 +247,42 @@ impl DataManager {
Ok(res.unwrap())
}
+ /// Get global questions from all communities, sorted by likes.
+ ///
+ /// # Arguments
+ /// * `batch` - the limit of questions in each page
+ /// * `page` - the page number
+ /// * `cutoff` - the maximum number of milliseconds ago the question could have been created
+ pub async fn get_popular_global_questions(
+ &self,
+ batch: usize,
+ page: usize,
+ cutoff: usize,
+ ) -> Result> {
+ 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 questions WHERE is_global = 1 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_question_from_row(x) }
+ );
+
+ if res.is_err() {
+ return Err(Error::GeneralNotFound("question".to_string()));
+ }
+
+ Ok(res.unwrap())
+ }
+
/// Create a new question in the database.
///
/// # Arguments