diff --git a/crates/app/src/public/html/post/quotes.html b/crates/app/src/public/html/post/quotes.html
new file mode 100644
index 0000000..0462e06
--- /dev/null
+++ b/crates/app/src/public/html/post/quotes.html
@@ -0,0 +1,67 @@
+{% extends "root.html" %} {% block head %}
+
Post quotes - {{ config.name }}
+{% endblock %} {% block body %} {{ macros::nav() }}
+
+ {% if post.replying_to %}
+
+ {{ icon "arrow-up" }}
+ {{ text "communities:action.continue_thread" }}
+
+ {% endif %}
+
+
+
+ {% if post.context.repost and post.context.repost.reposting %}
+ {{ components::repost(repost=reposting, post=post, owner=owner, community=community, show_community=true, can_manage_post=can_manage_posts) }}
+ {% else %}
+ {{ components::post(post=post, owner=owner, question=question, community=community, show_community=true, can_manage_post=can_manage_posts) }}
+ {% endif %}
+
+
+
+
+
+
+ {{ icon "quote" }}
+ {{ text "communities:label.quotes" }}
+
+
+
+
+ {% for post in list %}
+ {{ components::post(post=post[0], owner=post[1], question=post[3], secondary=true, show_community=false) }}
+ {% endfor %}
+
+ {{ components::pagination(page=page, items=list|length, key="quotes", value="true") }}
+
+
+
+{% endblock %}
diff --git a/crates/app/src/public/html/post/reposts.html b/crates/app/src/public/html/post/reposts.html
new file mode 100644
index 0000000..37595f6
--- /dev/null
+++ b/crates/app/src/public/html/post/reposts.html
@@ -0,0 +1,86 @@
+{% extends "root.html" %} {% block head %}
+
Post reposts - {{ config.name }}
+{% endblock %} {% block body %} {{ macros::nav() }}
+
+ {% if post.replying_to %}
+
+ {{ icon "arrow-up" }}
+ {{ text "communities:action.continue_thread" }}
+
+ {% endif %}
+
+
+
+ {% if post.context.repost and post.context.repost.reposting %}
+ {{ components::repost(repost=reposting, post=post, owner=owner, community=community, show_community=true, can_manage_post=can_manage_posts) }}
+ {% else %}
+ {{ components::post(post=post, owner=owner, question=question, community=community, show_community=true, can_manage_post=can_manage_posts) }}
+ {% endif %}
+
+
+
+
+
+
+ {{ icon "repeat-2" }}
+ {{ text "communities:label.reposts" }}
+
+
+
+ {% for post in list %}
+
+ {% endfor %} {{ components::pagination(page=page, items=list|length,
+ key="quotes", value="false") }}
+
+
+
+{% endblock %}
diff --git a/crates/app/src/public/html/profile/settings.html b/crates/app/src/public/html/profile/settings.html
index 6daa467..eb57ff3 100644
--- a/crates/app/src/public/html/profile/settings.html
+++ b/crates/app/src/public/html/profile/settings.html
@@ -320,7 +320,9 @@
ui_ident="awful_contrast"
>
- {{ icon "contrast" }}
+ {{ icon "contrast" }}
Some of your custom colors fail contrast checks:
@@ -830,6 +832,17 @@
settings.anonymous_avatar_url,
"input",
],
+ [[], "Misc", "title"],
+ [
+ ["hide_dislikes", "Hide post dislikes"],
+ "{{ profile.settings.hide_dislikes }}",
+ "checkbox",
+ ],
+ [
+ [],
+ "Hides dislikes on all posts. Users can still dislike your posts, you just won't be able to see it.",
+ "text",
+ ],
],
settings,
);
@@ -852,7 +865,6 @@
"Allow the site to build the theme for you given a base hue, saturation, and lightness. Scroll down to the next section to manually build the theme.",
"text",
],
-
[
["theme_hue", "Theme hue (integer 0-255)"],
"{{ profile.settings.theme_hue }}",
diff --git a/crates/app/src/routes/pages/communities.rs b/crates/app/src/routes/pages/communities.rs
index d5679c6..0b29bf7 100644
--- a/crates/app/src/routes/pages/communities.rs
+++ b/crates/app/src/routes/pages/communities.rs
@@ -1,4 +1,4 @@
-use super::{render_error, PaginatedQuery, SearchedQuery};
+use super::{render_error, PaginatedQuery, RepostsQuery, SearchedQuery};
use crate::{assets::initial_context, get_lang, get_user_from_token, sanitize::clean_context, State};
use axum::{
Extension,
@@ -327,12 +327,18 @@ pub async fn feed_request(
let (can_read, _) = check_permissions!(community, jar, data, user);
// ...
+ let ignore_users = if let Some(ref ua) = user {
+ data.0.get_userblocks_receivers(ua.id).await
+ } else {
+ Vec::new()
+ };
+
let feed = match data
.0
.get_posts_by_community(community.id, 12, props.page)
.await
{
- Ok(p) => match data.0.fill_posts(p).await {
+ Ok(p) => match data.0.fill_posts(p, &ignore_users).await {
Ok(p) => p,
Err(e) => return Err(Html(render_error(e, &jar, &data, &user).await)),
},
@@ -340,7 +346,7 @@ pub async fn feed_request(
};
let pinned = match data.0.get_pinned_posts_by_community(community.id).await {
- Ok(p) => match data.0.fill_posts(p).await {
+ Ok(p) => match data.0.fill_posts(p, &ignore_users).await {
Ok(p) => p,
Err(e) => return Err(Html(render_error(e, &jar, &data, &user).await)),
},
@@ -423,12 +429,18 @@ pub async fn questions_request(
let (can_read, _) = check_permissions!(community, jar, data, user);
// ...
+ let ignore_users = if let Some(ref ua) = user {
+ data.0.get_userblocks_receivers(ua.id).await
+ } else {
+ Vec::new()
+ };
+
let feed = match data
.0
.get_questions_by_community(community.id, 12, props.page)
.await
{
- Ok(p) => match data.0.fill_questions(p).await {
+ Ok(p) => match data.0.fill_questions(p, &ignore_users).await {
Ok(p) => p,
Err(e) => return Err(Html(render_error(e, &jar, &data, &user).await)),
},
@@ -570,8 +582,14 @@ pub async fn post_request(
}
// ...
+ let ignore_users = if let Some(ref ua) = user {
+ data.0.get_userblocks_receivers(ua.id).await
+ } else {
+ Vec::new()
+ };
+
let feed = match data.0.get_post_comments(post.id, 12, props.page).await {
- Ok(p) => match data.0.fill_posts(p).await {
+ Ok(p) => match data.0.fill_posts(p, &ignore_users).await {
Ok(p) => p,
Err(e) => return Err(Html(render_error(e, &jar, &data, &user).await)),
},
@@ -628,9 +646,132 @@ pub async fn post_request(
can_manage_questions,
);
+ // return
+ Ok(Html(data.1.render("post/post.html", &context).unwrap()))
+}
+
+/// `/post/{id}/reposts`
+pub async fn reposts_request(
+ jar: CookieJar,
+ Path(id): Path
,
+ Query(props): Query,
+ Extension(data): Extension,
+) -> impl IntoResponse {
+ let data = data.read().await;
+ let user = get_user_from_token!(jar, data.0);
+
+ let post = match data.0.get_post_by_id(id).await {
+ Ok(p) => p,
+ Err(e) => return Err(Html(render_error(e, &jar, &data, &user).await)),
+ };
+
+ let community = match data.0.get_community_by_id(post.community).await {
+ Ok(c) => c,
+ Err(e) => return Err(Html(render_error(e, &jar, &data, &user).await)),
+ };
+
+ // check repost
+ let reposting = data.0.get_post_reposting(&post).await;
+
+ // check question
+ let question = match data.0.get_post_question(&post).await {
+ Ok(q) => q,
+ Err(e) => return Err(Html(render_error(e, &jar, &data, &user).await)),
+ };
+
+ // check permissions
+ let (can_read, _) = check_permissions!(community, jar, data, user);
+
+ if !can_read {
+ return Err(Html(
+ render_error(Error::NotAllowed, &jar, &data, &user).await,
+ ));
+ }
+
+ // ...
+ let ignore_users = if let Some(ref ua) = user {
+ data.0.get_userblocks_receivers(ua.id).await
+ } else {
+ Vec::new()
+ };
+
+ let list = if props.quotes {
+ match data
+ .0
+ .get_quoting_posts_by_quoting(post.id, 12, props.page)
+ .await
+ {
+ Ok(p) => match data.0.fill_posts(p, &ignore_users).await {
+ Ok(p) => p,
+ Err(e) => return Err(Html(render_error(e, &jar, &data, &user).await)),
+ },
+ Err(e) => return Err(Html(render_error(e, &jar, &data, &user).await)),
+ }
+ } else {
+ match data.0.get_reposts_by_quoting(post.id, 12, props.page).await {
+ Ok(p) => match data.0.fill_posts(p, &ignore_users).await {
+ Ok(p) => p,
+ Err(e) => return Err(Html(render_error(e, &jar, &data, &user).await)),
+ },
+ Err(e) => return Err(Html(render_error(e, &jar, &data, &user).await)),
+ }
+ };
+
+ // init context
+ let lang = get_lang!(jar, data.0);
+ let mut context = initial_context(&data.0.0, lang, &user).await;
+
+ let (
+ is_owner,
+ is_joined,
+ is_pending,
+ can_post,
+ can_manage_posts,
+ can_manage_community,
+ can_manage_roles,
+ can_manage_questions,
+ ) = community_context_bools!(data, user, community);
+
+ context.insert("post", &post);
+ context.insert("reposting", &reposting);
+ context.insert("question", &question);
+ context.insert("list", &list);
+ context.insert("page", &props.page);
+ context.insert(
+ "owner",
+ &data
+ .0
+ .get_user_by_id(post.owner)
+ .await
+ .unwrap_or(User::deleted()),
+ );
+
+ community_context(
+ &mut context,
+ &community,
+ is_owner,
+ is_joined,
+ is_pending,
+ can_post,
+ can_read,
+ can_manage_posts,
+ can_manage_community,
+ can_manage_roles,
+ can_manage_questions,
+ );
+
// return
Ok(Html(
- data.1.render("communities/post.html", &context).unwrap(),
+ data.1
+ .render(
+ if props.quotes {
+ "post/quotes.html"
+ } else {
+ "post/reposts.html"
+ },
+ &context,
+ )
+ .unwrap(),
))
}
@@ -761,12 +902,18 @@ pub async fn question_request(
}
// ...
+ let ignore_users = if let Some(ref ua) = user {
+ data.0.get_userblocks_receivers(ua.id).await
+ } else {
+ Vec::new()
+ };
+
let feed = match data
.0
.get_posts_by_question(question.id, 12, props.page)
.await
{
- Ok(p) => match data.0.fill_posts(p).await {
+ Ok(p) => match data.0.fill_posts(p, &ignore_users).await {
Ok(p) => p,
Err(e) => return Err(Html(render_error(e, &jar, &data, &user).await)),
},
diff --git a/crates/app/src/routes/pages/misc.rs b/crates/app/src/routes/pages/misc.rs
index 992cc58..0ddef29 100644
--- a/crates/app/src/routes/pages/misc.rs
+++ b/crates/app/src/routes/pages/misc.rs
@@ -42,12 +42,18 @@ pub async fn index_request(
}
};
+ let ignore_users = data.0.get_userblocks_receivers(user.id).await;
+
let list = match data
.0
.get_posts_from_user_communities(user.id, 12, req.page)
.await
{
- Ok(l) => match data.0.fill_posts_with_community(l, user.id).await {
+ Ok(l) => match data
+ .0
+ .fill_posts_with_community(l, user.id, &ignore_users)
+ .await
+ {
Ok(l) => l,
Err(e) => return Html(render_error(e, &jar, &data, &Some(user)).await),
},
@@ -71,10 +77,20 @@ pub async fn popular_request(
let data = data.read().await;
let user = get_user_from_token!(jar, data.0);
+ let ignore_users = if let Some(ref ua) = user {
+ data.0.get_userblocks_receivers(ua.id).await
+ } else {
+ Vec::new()
+ };
+
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 })
+ .fill_posts_with_community(
+ l,
+ if let Some(ref ua) = user { ua.id } else { 0 },
+ &ignore_users,
+ )
.await
{
Ok(l) => l,
@@ -107,12 +123,18 @@ pub async fn following_request(
}
};
+ let ignore_users = data.0.get_userblocks_receivers(user.id).await;
+
let list = match data
.0
.get_posts_from_user_following(user.id, 12, req.page)
.await
{
- Ok(l) => match data.0.fill_posts_with_community(l, user.id).await {
+ Ok(l) => match data
+ .0
+ .fill_posts_with_community(l, user.id, &ignore_users)
+ .await
+ {
Ok(l) => l,
Err(e) => return Err(Html(render_error(e, &jar, &data, &Some(user)).await)),
},
@@ -138,10 +160,20 @@ pub async fn all_request(
let data = data.read().await;
let user = get_user_from_token!(jar, data.0);
+ let ignore_users = if let Some(ref ua) = user {
+ data.0.get_userblocks_receivers(ua.id).await
+ } else {
+ Vec::new()
+ };
+
let list = match data.0.get_latest_posts(12, req.page).await {
Ok(l) => match data
.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 },
+ &ignore_users,
+ )
.await
{
Ok(l) => l,
@@ -172,12 +204,14 @@ pub async fn index_questions_request(
}
};
+ let ignore_users = data.0.get_userblocks_receivers(user.id).await;
+
let list = match data
.0
.get_questions_from_user_communities(user.id, 12, req.page)
.await
{
- Ok(l) => match data.0.fill_questions(l).await {
+ Ok(l) => match data.0.fill_questions(l, &ignore_users).await {
Ok(l) => l,
Err(e) => return Html(render_error(e, &jar, &data, &Some(user)).await),
},
@@ -210,12 +244,14 @@ pub async fn popular_questions_request(
}
};
+ let ignore_users = data.0.get_userblocks_receivers(user.id).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) => match data.0.fill_questions(l, &ignore_users).await {
Ok(l) => l,
Err(e) => return Html(render_error(e, &jar, &data, &Some(user)).await),
},
@@ -250,12 +286,14 @@ pub async fn following_questions_request(
}
};
+ let ignore_users = data.0.get_userblocks_receivers(user.id).await;
+
let list = match data
.0
.get_questions_from_user_following(user.id, 12, req.page)
.await
{
- Ok(l) => match data.0.fill_questions(l).await {
+ Ok(l) => match data.0.fill_questions(l, &ignore_users).await {
Ok(l) => l,
Err(e) => return Err(Html(render_error(e, &jar, &data, &Some(user)).await)),
},
@@ -283,8 +321,14 @@ pub async fn all_questions_request(
let data = data.read().await;
let user = get_user_from_token!(jar, data.0);
+ let ignore_users = if let Some(ref ua) = user {
+ data.0.get_userblocks_receivers(ua.id).await
+ } else {
+ Vec::new()
+ };
+
let list = match data.0.get_latest_global_questions(12, req.page).await {
- Ok(l) => match data.0.fill_questions(l).await {
+ Ok(l) => match data.0.fill_questions(l, &ignore_users).await {
Ok(l) => l,
Err(e) => return Html(render_error(e, &jar, &data, &user).await),
},
@@ -353,24 +397,31 @@ pub async fn requests_request(
Err(e) => return Err(Html(render_error(e, &jar, &data, &Some(user)).await)),
};
+ let ignore_users = data.0.get_userblocks_receivers(user.id).await;
+
let questions = match data
.0
- .fill_questions({
- let mut q = Vec::new();
+ .fill_questions(
+ {
+ let mut q = Vec::new();
- for req in &requests {
- if req.action_type != ActionType::Answer {
- continue;
+ for req in &requests {
+ if req.action_type != ActionType::Answer {
+ continue;
+ }
+
+ q.push(match data.0.get_question_by_id(req.linked_asset).await {
+ Ok(p) => p,
+ Err(e) => {
+ return Err(Html(render_error(e, &jar, &data, &Some(user)).await));
+ }
+ });
}
- q.push(match data.0.get_question_by_id(req.linked_asset).await {
- Ok(p) => p,
- Err(e) => return Err(Html(render_error(e, &jar, &data, &Some(user)).await)),
- });
- }
-
- q
- })
+ q
+ },
+ &ignore_users,
+ )
.await
{
Ok(q) => q,
diff --git a/crates/app/src/routes/pages/mod.rs b/crates/app/src/routes/pages/mod.rs
index b3c2292..dc736d1 100644
--- a/crates/app/src/routes/pages/mod.rs
+++ b/crates/app/src/routes/pages/mod.rs
@@ -76,6 +76,7 @@ pub fn routes() -> Router {
get(communities::members_request),
)
.route("/post/{id}", get(communities::post_request))
+ .route("/post/{id}/reposts", get(communities::reposts_request))
.route("/question/{id}", get(communities::question_request))
}
@@ -112,3 +113,11 @@ pub struct SearchedQuery {
#[serde(default)]
pub page: usize,
}
+
+#[derive(Deserialize)]
+pub struct RepostsQuery {
+ #[serde(default)]
+ pub quotes: bool,
+ #[serde(default)]
+ pub page: usize,
+}
diff --git a/crates/app/src/routes/pages/profile.rs b/crates/app/src/routes/pages/profile.rs
index d24669c..65eee16 100644
--- a/crates/app/src/routes/pages/profile.rs
+++ b/crates/app/src/routes/pages/profile.rs
@@ -251,6 +251,12 @@ pub async fn posts_request(
}
// fetch data
+ let ignore_users = if let Some(ref ua) = user {
+ data.0.get_userblocks_receivers(ua.id).await
+ } else {
+ Vec::new()
+ };
+
let posts = match data
.0
.get_posts_by_user(other_user.id, 12, props.page)
@@ -258,7 +264,11 @@ pub async fn posts_request(
{
Ok(p) => match data
.0
- .fill_posts_with_community(p, if let Some(ref ua) = user { ua.id } else { 0 })
+ .fill_posts_with_community(
+ p,
+ if let Some(ref ua) = user { ua.id } else { 0 },
+ &ignore_users,
+ )
.await
{
Ok(p) => p,
@@ -270,7 +280,11 @@ pub async fn posts_request(
let pinned = match data.0.get_pinned_posts_by_user(other_user.id).await {
Ok(p) => match data
.0
- .fill_posts_with_community(p, if let Some(ref ua) = user { ua.id } else { 0 })
+ .fill_posts_with_community(
+ p,
+ if let Some(ref ua) = user { ua.id } else { 0 },
+ &ignore_users,
+ )
.await
{
Ok(p) => p,
diff --git a/crates/core/src/database/posts.rs b/crates/core/src/database/posts.rs
index 856a12b..a6bd53b 100644
--- a/crates/core/src/database/posts.rs
+++ b/crates/core/src/database/posts.rs
@@ -121,6 +121,7 @@ impl DataManager {
pub async fn fill_posts(
&self,
posts: Vec,
+ ignore_users: &Vec,
) -> Result, Option<(Question, User)>)>> {
let mut out: Vec<(Post, User, Option<(User, Post)>, Option<(Question, User)>)> = Vec::new();
@@ -128,6 +129,10 @@ impl DataManager {
for post in posts {
let owner = post.owner;
+ if ignore_users.contains(&owner) {
+ continue;
+ }
+
if let Some(user) = users.get(&owner) {
out.push((
post.clone(),
@@ -155,6 +160,7 @@ impl DataManager {
&self,
posts: Vec,
user_id: usize,
+ ignore_users: &Vec,
) -> Result<
Vec<(
Post,
@@ -177,6 +183,11 @@ impl DataManager {
for post in posts {
let owner = post.owner;
+
+ if ignore_users.contains(&owner) {
+ continue;
+ }
+
let community = post.community;
if let Some((user, community)) = seen_before.get(&(owner, community)) {
@@ -402,6 +413,82 @@ impl DataManager {
Ok(res.unwrap())
}
+ /// Get all quoting posts by the post their quoting.
+ ///
+ /// Requires that the post has content. See [`Self::get_reposts_by_quoting`]
+ /// for the no-content version.
+ ///
+ /// # Arguments
+ /// * `id` - the ID of the post that is being quoted
+ /// * `batch` - the limit of posts in each page
+ /// * `page` - the page number
+ pub async fn get_quoting_posts_by_quoting(
+ &self,
+ id: usize,
+ batch: usize,
+ page: 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 posts WHERE NOT content = '' AND context LIKE $1 ORDER BY created DESC LIMIT $2 OFFSET $3",
+ params![
+ &format!("%\"reposting\":{id}%"),
+ &(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 all quoting posts by the post their quoting.
+ ///
+ /// Requires that the post has no content. See [`Self::get_quoting_posts_by_quoting`]
+ /// for the content-required version.
+ ///
+ /// # Arguments
+ /// * `id` - the ID of the post that is being quoted
+ /// * `batch` - the limit of posts in each page
+ /// * `page` - the page number
+ pub async fn get_reposts_by_quoting(
+ &self,
+ id: usize,
+ batch: usize,
+ page: 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 posts WHERE content = '' AND context LIKE $1 ORDER BY created DESC LIMIT $2 OFFSET $3",
+ params![
+ &format!("%\"reposting\":{id}%"),
+ &(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 likes.
///
/// # Arguments
@@ -694,6 +781,25 @@ impl DataManager {
{
return Err(Error::NotAllowed);
}
+
+ // send notification
+ // this would look better if rustfmt didn't give up on this line
+ if owner.id != rt.owner {
+ self.create_notification(
+ Notification::new(
+ format!(
+ "[@{}](/api/v1/auth/user/find/{}) has [quoted](/post/{}) your [post](/post/{})",
+ owner.username,
+ owner.id,
+ data.id,
+ rt.id
+ ),
+ format!("\"{}\"", data.content),
+ rt.owner
+ )
+ )
+ .await?;
+ }
}
// check if the post we're replying to allows commments
@@ -729,8 +835,8 @@ impl DataManager {
self.create_notification(Notification::new(
"You've been mentioned in a post!".to_string(),
format!(
- "[Somebody](/api/v1/auth/user/find/{}) mentioned you in their [post](/post/{}).",
- data.owner, data.id
+ "[@{}](/api/v1/auth/user/find/{}) has mentioned you in their [post](/post/{}).",
+ owner.username, owner.id, data.id
),
user.id,
))
diff --git a/crates/core/src/database/questions.rs b/crates/core/src/database/questions.rs
index 85f18b7..97ecd0b 100644
--- a/crates/core/src/database/questions.rs
+++ b/crates/core/src/database/questions.rs
@@ -46,11 +46,19 @@ impl DataManager {
auto_method!(get_question_by_id()@get_question_from_row -> "SELECT * FROM questions WHERE id = $1" --name="question" --returns=Question --cache-key-tmpl="atto.question:{}");
/// Fill the given vector of questions with their owner as well.
- pub async fn fill_questions(&self, questions: Vec) -> Result> {
+ pub async fn fill_questions(
+ &self,
+ questions: Vec,
+ ignore_users: &Vec,
+ ) -> Result> {
let mut out: Vec<(Question, User)> = Vec::new();
let mut seen_users: HashMap = HashMap::new();
for question in questions {
+ if ignore_users.contains(&question.owner) {
+ continue;
+ }
+
if let Some(ua) = seen_users.get(&question.owner) {
out.push((question, ua.to_owned()));
} else {
diff --git a/crates/core/src/database/requests.rs b/crates/core/src/database/requests.rs
index 87bde05..3786cdc 100644
--- a/crates/core/src/database/requests.rs
+++ b/crates/core/src/database/requests.rs
@@ -130,7 +130,8 @@ impl DataManager {
.get_request_by_id_linked_asset(id, linked_asset)
.await?;
- if !force && user.id != y.owner && !user.permissions.check(FinePermission::MANAGE_REQUESTS) {
+ if !force && user.id != y.owner && !user.permissions.check(FinePermission::MANAGE_REQUESTS)
+ {
return Err(Error::NotAllowed);
}
diff --git a/crates/core/src/database/userblocks.rs b/crates/core/src/database/userblocks.rs
index 77edf25..77fe8b4 100644
--- a/crates/core/src/database/userblocks.rs
+++ b/crates/core/src/database/userblocks.rs
@@ -1,7 +1,7 @@
use super::*;
use crate::cache::Cache;
use crate::model::{Error, Result, auth::User, auth::UserBlock, permissions::FinePermission};
-use crate::{auto_method, execute, get, query_row, params};
+use crate::{auto_method, execute, get, params, query_row, query_rows};
#[cfg(feature = "sqlite")]
use rusqlite::Row;
@@ -75,6 +75,35 @@ impl DataManager {
Ok(res.unwrap())
}
+ /// Get the receiver of all user blocks for the given `initiator`.
+ pub async fn get_userblocks_receivers(&self, initiator: usize) -> Vec {
+ let conn = match self.connect().await {
+ Ok(c) => c,
+ Err(_) => return Vec::new(),
+ };
+
+ let res = query_rows!(
+ &conn,
+ "SELECT * FROM userblocks WHERE receiver = $1",
+ &[&(initiator as i64)],
+ |x| { Self::get_userblock_from_row(x) }
+ );
+
+ if res.is_err() {
+ return Vec::new();
+ }
+
+ // get receivers
+ let mut out: Vec = Vec::new();
+
+ for b in res.unwrap() {
+ out.push(b.receiver);
+ }
+
+ // return
+ out
+ }
+
/// Create a new user block in the database.
///
/// # Arguments
diff --git a/crates/core/src/model/auth.rs b/crates/core/src/model/auth.rs
index 2e6fe96..7b7b52a 100644
--- a/crates/core/src/model/auth.rs
+++ b/crates/core/src/model/auth.rs
@@ -145,6 +145,9 @@ pub struct UserSettings {
/// The URL of the avatar used for anonymous users.
#[serde(default)]
pub anonymous_avatar_url: String,
+ /// If dislikes are hidden for the user.
+ #[serde(default)]
+ pub hide_dislikes: bool,
}
impl Default for User {
diff --git a/crates/core/src/model/communities.rs b/crates/core/src/model/communities.rs
index 827ebc8..b333cc4 100644
--- a/crates/core/src/model/communities.rs
+++ b/crates/core/src/model/communities.rs
@@ -344,10 +344,8 @@ impl Question {
}
}
-#[derive(Debug, Clone, Serialize, Deserialize)]
-#[derive(Default)]
+#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct QuestionContext {
#[serde(default)]
pub is_nsfw: bool,
}
-