add: forums ui

This commit is contained in:
trisua 2025-08-04 12:12:04 -04:00
parent 2be87c397d
commit 9ec52abfe4
24 changed files with 770 additions and 64 deletions

View file

@ -532,6 +532,25 @@ impl DataManager {
Ok(())
}
pub async fn delete_topic_posts(&self, id: usize, topic: usize) -> Result<()> {
let conn = match self.0.connect().await {
Ok(c) => c,
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
};
let res = execute!(
&conn,
"DELETE FROM posts WHERE community = $1 AND topic = $2",
params![&(id as i64), &(topic as i64)]
);
if let Err(e) = res {
return Err(Error::DatabaseError(e.to_string()));
}
Ok(())
}
auto_method!(update_community_context(CommunityContext)@get_community_by_id_no_void:FinePermission::MANAGE_COMMUNITIES; -> "UPDATE communities SET context = $1 WHERE id = $2" --serde --cache-key-tmpl=cache_clear_community);
auto_method!(update_community_read_access(CommunityReadAccess)@get_community_by_id_no_void:FinePermission::MANAGE_COMMUNITIES; -> "UPDATE communities SET read_access = $1 WHERE id = $2" --serde --cache-key-tmpl=cache_clear_community);
auto_method!(update_community_write_access(CommunityWriteAccess)@get_community_by_id_no_void:FinePermission::MANAGE_COMMUNITIES; -> "UPDATE communities SET write_access = $1 WHERE id = $2" --serde --cache-key-tmpl=cache_clear_community);

View file

@ -18,5 +18,6 @@ CREATE TABLE IF NOT EXISTS posts (
poll_id BIGINT NOT NULL,
title TEXT NOT NULL,
is_open INT NOT NULL DEFAULT 1,
circle BIGINT NOT NULL
stack BIGINT NOT NULL,
topic BIGINT NOT NULL
)

View file

@ -21,3 +21,7 @@ ADD COLUMN IF NOT EXISTS is_forum INT DEFAULT 0;
-- communities topics
ALTER TABLE communities
ADD COLUMN IF NOT EXISTS topics TEXT DEFAULT '{}';
-- posts topic
ALTER TABLE posts
ADD COLUMN IF NOT EXISTS topic BIGINT DEFAULT 0;

View file

@ -116,6 +116,7 @@ impl DataManager {
title: get!(x->14(String)),
is_open: get!(x->15(i32)) as i8 == 1,
stack: get!(x->16(i64)) as usize,
topic: get!(x->17(i64)) as usize,
}
}
@ -1209,6 +1210,60 @@ impl DataManager {
Ok(res.unwrap())
}
/// Get all posts from the given community and topic (from most recent).
///
/// # Arguments
/// * `id` - the ID of the community the requested posts belong to
/// * `topic` - the ID of the topic the requested posts belong to
/// * `batch` - the limit of posts in each page
/// * `page` - the page number
pub async fn get_posts_by_community_topic(
&self,
id: usize,
topic: usize,
batch: usize,
page: usize,
user: &Option<User>,
) -> Result<Vec<Post>> {
let conn = match self.0.connect().await {
Ok(c) => c,
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
};
// check if we should hide nsfw posts
let mut hide_nsfw: bool = true;
if let Some(ua) = user {
hide_nsfw = !ua.settings.show_nsfw;
}
// ...
let res = query_rows!(
&conn,
&format!(
"SELECT * FROM posts WHERE community = $1 AND topic = $2 AND replying_to = 0 AND NOT context LIKE '%\"is_pinned\":true%' AND is_deleted = 0 {} ORDER BY created DESC LIMIT $3 OFFSET $4",
if hide_nsfw {
"AND NOT (context::json->>'is_nsfw')::boolean"
} else {
""
}
),
&[
&(id as i64),
&(topic 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 all posts from the given stack (from most recent).
///
/// # Arguments
@ -1264,6 +1319,35 @@ impl DataManager {
Ok(res.unwrap())
}
/// Get all pinned posts from the given community (from most recent).
///
/// # Arguments
/// * `id` - the ID of the community the requested posts belong to
/// * `topic` - the ID of the topic the requested posts belong to
pub async fn get_pinned_posts_by_community_topic(
&self,
id: usize,
topic: usize,
) -> Result<Vec<Post>> {
let conn = match self.0.connect().await {
Ok(c) => c,
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
};
let res = query_rows!(
&conn,
"SELECT * FROM posts WHERE community = $1 AND topic = $2 AND context LIKE '%\"is_pinned\":true%' ORDER BY created DESC",
&[&(id as i64), &(topic 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 pinned posts from the given user (from most recent).
///
/// # Arguments
@ -1494,7 +1578,7 @@ impl DataManager {
let res = query_rows!(
&conn,
&format!(
"SELECT * FROM posts WHERE replying_to = 0{}{}{} AND NOT context LIKE '%\"full_unlist\":true%' ORDER BY created DESC LIMIT $1 OFFSET $2",
"SELECT * FROM posts WHERE replying_to = 0{}{}{} AND NOT context LIKE '%\"full_unlist\":true%' AND topic = 0 ORDER BY created DESC LIMIT $1 OFFSET $2",
if before_time > 0 {
format!(" AND created < {before_time}")
} else {
@ -1748,6 +1832,20 @@ impl DataManager {
self.get_community_by_id(data.community).await?
};
// check is_forum
if community.is_forum {
if data.topic == 0 {
return Err(Error::MiscError(
"Topic is required for this community".to_string(),
));
}
if community.topics.get(&data.topic).is_none() {
return Err(Error::GeneralNotFound("topic".to_string()));
}
}
// ...
let mut owner = self.get_user_by_id(data.owner).await?;
// check values (if this isn't reposting something else)
@ -2019,7 +2117,7 @@ impl DataManager {
let res = execute!(
&conn,
"INSERT INTO posts VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, DEFAULT, $13, $14, $15, $16)",
"INSERT INTO posts VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, DEFAULT, $13, $14, $15, $16, $17)",
params![
&(data.id as i64),
&(data.created as i64),
@ -2041,6 +2139,7 @@ impl DataManager {
&data.title,
&{ if data.is_open { 1 } else { 0 } },
&(data.stack as i64),
&(data.topic as i64),
]
);

View file

@ -280,6 +280,11 @@ pub struct Post {
///
/// If stack is not 0, community should be 0 (and vice versa).
pub stack: usize,
/// The ID of the topic this post belongs to. 0 means no topic is connected.
///
/// This can only be set if the post is created in a community with `is_forum: true`,
/// where this is also a required field.
pub topic: usize,
}
impl Post {
@ -308,6 +313,7 @@ impl Post {
title: String::new(),
is_open: true,
stack: 0,
topic: 0,
}
}
@ -534,6 +540,7 @@ pub struct ForumTopic {
pub title: String,
pub description: String,
pub color: String,
pub position: i32,
}
impl ForumTopic {
@ -542,13 +549,14 @@ impl ForumTopic {
/// # Returns
/// * ID for [`Community`] hashmap
/// * [`ForumTopic`]
pub fn new(title: String, description: String, color: String) -> (usize, Self) {
pub fn new(title: String, description: String, color: String, position: i32) -> (usize, Self) {
(
Snowflake::new().to_string().parse::<usize>().unwrap(),
Self {
title,
description,
color,
position,
},
)
}