add: questions, requests

This commit is contained in:
trisua 2025-04-12 22:25:54 -04:00
parent 24f67221ca
commit 7960484bf9
52 changed files with 1698 additions and 100 deletions

View file

@ -3,6 +3,7 @@ use std::collections::HashMap;
use super::*;
use crate::cache::Cache;
use crate::model::auth::Notification;
use crate::model::communities::Question;
use crate::model::communities_permissions::CommunityPermission;
use crate::model::moderation::AuditLogEntry;
use crate::model::{
@ -100,12 +101,23 @@ impl DataManager {
}
}
/// Get the question of a given post.
pub async fn get_post_question(&self, post: &Post) -> Result<Option<(Question, User)>> {
if post.context.answering != 0 {
let question = self.get_question_by_id(post.context.answering).await?;
let user = self.get_user_by_id_with_void(question.owner).await?;
Ok(Some((question, user)))
} else {
Ok(None)
}
}
/// Complete a vector of just posts with their owner as well.
pub async fn fill_posts(
&self,
posts: Vec<Post>,
) -> Result<Vec<(Post, User, Option<(User, Post)>)>> {
let mut out: Vec<(Post, User, Option<(User, Post)>)> = Vec::new();
) -> Result<Vec<(Post, User, Option<(User, Post)>, Option<(Question, User)>)>> {
let mut out: Vec<(Post, User, Option<(User, Post)>, Option<(Question, User)>)> = Vec::new();
let mut users: HashMap<usize, User> = HashMap::new();
for post in posts {
@ -116,11 +128,17 @@ impl DataManager {
post.clone(),
user.clone(),
self.get_post_reposting(&post).await,
self.get_post_question(&post).await?,
));
} else {
let user = self.get_user_by_id(owner).await?;
users.insert(owner, user.clone());
out.push((post.clone(), user, self.get_post_reposting(&post).await));
out.push((
post.clone(),
user,
self.get_post_reposting(&post).await,
self.get_post_question(&post).await?,
));
}
}
@ -132,8 +150,22 @@ impl DataManager {
&self,
posts: Vec<Post>,
user_id: usize,
) -> Result<Vec<(Post, User, Community, Option<(User, Post)>)>> {
let mut out: Vec<(Post, User, Community, Option<(User, Post)>)> = Vec::new();
) -> Result<
Vec<(
Post,
User,
Community,
Option<(User, Post)>,
Option<(Question, User)>,
)>,
> {
let mut out: Vec<(
Post,
User,
Community,
Option<(User, Post)>,
Option<(Question, User)>,
)> = Vec::new();
let mut seen_before: HashMap<(usize, usize), (User, Community)> = HashMap::new();
let mut seen_user_follow_statuses: HashMap<(usize, usize), bool> = HashMap::new();
@ -148,6 +180,7 @@ impl DataManager {
user.clone(),
community.to_owned(),
self.get_post_reposting(&post).await,
self.get_post_question(&post).await?,
));
} else {
let user = self.get_user_by_id(owner).await?;
@ -186,6 +219,7 @@ impl DataManager {
user,
community,
self.get_post_reposting(&post).await,
self.get_post_question(&post).await?,
));
}
}
@ -303,6 +337,66 @@ impl DataManager {
Ok(res.unwrap())
}
/// Get all posts answering the given question (from most recent).
///
/// # Arguments
/// * `id` - the ID of the question the requested posts belong to
/// * `batch` - the limit of posts in each page
/// * `page` - the page number
pub async fn get_posts_by_question(
&self,
id: usize,
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 context LIKE $1 ORDER BY created DESC LIMIT $2 OFFSET $3",
params![
&format!("%\"answering\":{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 a post given its owner and question ID.
///
/// # Arguments
/// * `owner` - the ID of the post owner
/// * `question` - the ID of the post question
pub async fn get_post_by_owner_question(&self, owner: usize, question: usize) -> Result<Post> {
let conn = match self.connect().await {
Ok(c) => c,
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
};
let res = query_row!(
&conn,
"SELECT * FROM posts WHERE context LIKE $1 AND owner = $2 LIMIT 1",
params![&format!("%\"answering\":{question}%"), &(owner as i64),],
|x| { Ok(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
@ -508,6 +602,42 @@ impl DataManager {
// mirror nsfw state
data.context.is_nsfw = community.context.is_nsfw;
// remove request if we were answering a question
let owner = self.get_user_by_id(data.owner).await?;
if data.context.answering != 0 {
let question = self.get_question_by_id(data.context.answering).await?;
// check if we've already answered this
if self
.get_post_by_owner_question(owner.id, question.id)
.await
.is_ok()
{
return Err(Error::MiscError(
"You've already answered this question".to_string(),
));
}
if !question.is_global {
self.delete_request(question.owner, question.id, &owner)
.await?;
} else {
self.incr_question_answer_count(data.context.answering)
.await?;
}
// create notification for question owner
self.create_notification(Notification::new(
"Your question has received a new answer!".to_string(),
format!(
"[@{}](/api/v1/auth/user/find/{}) has answered your [question](/question/{}).",
owner.username, owner.id, question.id
),
question.owner,
))
.await?;
}
// check if we're reposting a post
let reposting = if let Some(ref repost) = data.context.repost {
if let Some(id) = repost.reposting {
@ -650,6 +780,9 @@ impl DataManager {
}
}
// increase user post count
self.incr_user_post_count(data.owner).await?;
// return
Ok(data.id)
}
@ -695,6 +828,22 @@ impl DataManager {
self.decr_post_comments(replying_to).await.unwrap();
}
// decr user post count
let owner = self.get_user_by_id(y.owner).await?;
if owner.post_count > 0 {
self.decr_user_post_count(y.owner).await?;
}
// decr question answer count
if y.context.answering != 0 {
let question = self.get_question_by_id(y.context.answering).await?;
if question.is_global {
self.incr_question_answer_count(y.context.answering).await?;
}
}
// return
Ok(())
}
@ -707,6 +856,7 @@ impl DataManager {
) -> Result<()> {
let y = self.get_post_by_id(id).await?;
x.repost = y.context.repost; // cannot change repost settings at all
x.answering = y.context.answering; // cannot change answering settings at all
let user_membership = self
.get_membership_by_owner_community(user.id, y.community)