mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-11-22 20:31:19 +00:00
partial post tags implementation
This commit is contained in:
parent
f98b15511a
commit
003613b736
13 changed files with 106 additions and 18 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -2996,6 +2996,7 @@ dependencies = [
|
||||||
"lemmy_utils",
|
"lemmy_utils",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"serde_with",
|
"serde_with",
|
||||||
"serial_test",
|
"serial_test",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
|
|
@ -30,6 +30,7 @@ pub struct CreatePost {
|
||||||
pub language_id: Option<LanguageId>,
|
pub language_id: Option<LanguageId>,
|
||||||
/// Instead of fetching a thumbnail, use a custom one.
|
/// Instead of fetching a thumbnail, use a custom one.
|
||||||
pub custom_thumbnail: Option<String>,
|
pub custom_thumbnail: Option<String>,
|
||||||
|
pub community_post_tags: Option<Vec<CommunityPostTagId>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
@ -124,6 +125,7 @@ pub struct EditPost {
|
||||||
pub language_id: Option<LanguageId>,
|
pub language_id: Option<LanguageId>,
|
||||||
/// Instead of fetching a thumbnail, use a custom one.
|
/// Instead of fetching a thumbnail, use a custom one.
|
||||||
pub custom_thumbnail: Option<String>,
|
pub custom_thumbnail: Option<String>,
|
||||||
|
pub community_post_tags: Option<Vec<CommunityPostTagId>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq, Hash)]
|
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq, Hash)]
|
||||||
|
|
|
@ -283,3 +283,10 @@ impl InstanceId {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Default, Serialize, Deserialize)]
|
||||||
|
#[cfg_attr(feature = "full", derive(DieselNewType, TS))]
|
||||||
|
#[cfg_attr(feature = "full", ts(export))]
|
||||||
|
/// The post id.
|
||||||
|
pub struct CommunityPostTagId(pub i32);
|
|
@ -251,6 +251,18 @@ diesel::table! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
diesel::table! {
|
||||||
|
community_post_tag (id) {
|
||||||
|
id -> Int4,
|
||||||
|
ap_id -> Text,
|
||||||
|
community_id -> Int4,
|
||||||
|
name -> Text,
|
||||||
|
published -> Timestamptz,
|
||||||
|
updated -> Nullable<Timestamptz>,
|
||||||
|
deleted -> Nullable<Timestamptz>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
diesel::table! {
|
diesel::table! {
|
||||||
custom_emoji (id) {
|
custom_emoji (id) {
|
||||||
id -> Int4,
|
id -> Int4,
|
||||||
|
@ -759,6 +771,13 @@ diesel::table! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
diesel::table! {
|
||||||
|
post_community_post_tag (post_id, community_post_tag_id) {
|
||||||
|
post_id -> Int4,
|
||||||
|
community_post_tag_id -> Int4,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
diesel::table! {
|
diesel::table! {
|
||||||
post_hide (person_id, post_id) {
|
post_hide (person_id, post_id) {
|
||||||
post_id -> Int4,
|
post_id -> Int4,
|
||||||
|
@ -974,6 +993,7 @@ diesel::joinable!(community_moderator -> community (community_id));
|
||||||
diesel::joinable!(community_moderator -> person (person_id));
|
diesel::joinable!(community_moderator -> person (person_id));
|
||||||
diesel::joinable!(community_person_ban -> community (community_id));
|
diesel::joinable!(community_person_ban -> community (community_id));
|
||||||
diesel::joinable!(community_person_ban -> person (person_id));
|
diesel::joinable!(community_person_ban -> person (person_id));
|
||||||
|
diesel::joinable!(community_post_tag -> community (community_id));
|
||||||
diesel::joinable!(custom_emoji -> local_site (local_site_id));
|
diesel::joinable!(custom_emoji -> local_site (local_site_id));
|
||||||
diesel::joinable!(custom_emoji_keyword -> custom_emoji (custom_emoji_id));
|
diesel::joinable!(custom_emoji_keyword -> custom_emoji (custom_emoji_id));
|
||||||
diesel::joinable!(email_verification -> local_user (local_user_id));
|
diesel::joinable!(email_verification -> local_user (local_user_id));
|
||||||
|
@ -1020,6 +1040,8 @@ diesel::joinable!(post_aggregates -> community (community_id));
|
||||||
diesel::joinable!(post_aggregates -> instance (instance_id));
|
diesel::joinable!(post_aggregates -> instance (instance_id));
|
||||||
diesel::joinable!(post_aggregates -> person (creator_id));
|
diesel::joinable!(post_aggregates -> person (creator_id));
|
||||||
diesel::joinable!(post_aggregates -> post (post_id));
|
diesel::joinable!(post_aggregates -> post (post_id));
|
||||||
|
diesel::joinable!(post_community_post_tag -> community_post_tag (community_post_tag_id));
|
||||||
|
diesel::joinable!(post_community_post_tag -> post (post_id));
|
||||||
diesel::joinable!(post_hide -> person (person_id));
|
diesel::joinable!(post_hide -> person (person_id));
|
||||||
diesel::joinable!(post_hide -> post (post_id));
|
diesel::joinable!(post_hide -> post (post_id));
|
||||||
diesel::joinable!(post_like -> person (person_id));
|
diesel::joinable!(post_like -> person (person_id));
|
||||||
|
@ -1057,6 +1079,7 @@ diesel::allow_tables_to_appear_in_same_query!(
|
||||||
community_language,
|
community_language,
|
||||||
community_moderator,
|
community_moderator,
|
||||||
community_person_ban,
|
community_person_ban,
|
||||||
|
community_post_tag,
|
||||||
custom_emoji,
|
custom_emoji,
|
||||||
custom_emoji_keyword,
|
custom_emoji_keyword,
|
||||||
email_verification,
|
email_verification,
|
||||||
|
@ -1096,6 +1119,7 @@ diesel::allow_tables_to_appear_in_same_query!(
|
||||||
person_post_aggregates,
|
person_post_aggregates,
|
||||||
post,
|
post,
|
||||||
post_aggregates,
|
post_aggregates,
|
||||||
|
post_community_post_tag,
|
||||||
post_hide,
|
post_hide,
|
||||||
post_like,
|
post_like,
|
||||||
post_read,
|
post_read,
|
||||||
|
|
26
crates/db_schema/src/source/community_post_tag.rs
Normal file
26
crates/db_schema/src/source/community_post_tag.rs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_with::skip_serializing_none;
|
||||||
|
use ts_rs::TS;
|
||||||
|
|
||||||
|
use crate::newtypes::{CommunityId, CommunityPostTagId};
|
||||||
|
|
||||||
|
/// A tag that can be assigned to a post within a community.
|
||||||
|
/// The tag object is created by the community moderators.
|
||||||
|
/// The assignment happens by the post creator and can be updated by the community moderators.
|
||||||
|
#[skip_serializing_none]
|
||||||
|
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
|
||||||
|
#[cfg_attr(feature = "full", derive(TS, Queryable))]
|
||||||
|
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||||
|
#[cfg_attr(feature = "full", ts(export))]
|
||||||
|
pub struct CommunityPostTag {
|
||||||
|
pub id: CommunityPostTagId,
|
||||||
|
pub ap_id: String,
|
||||||
|
pub community_id: CommunityId,
|
||||||
|
pub name: String,
|
||||||
|
pub published: DateTime<Utc>,
|
||||||
|
pub updated: Option<DateTime<Utc>>,
|
||||||
|
pub deleted: Option<DateTime<Utc>>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@ pub mod registration_application;
|
||||||
pub mod secret;
|
pub mod secret;
|
||||||
pub mod site;
|
pub mod site;
|
||||||
pub mod tagline;
|
pub mod tagline;
|
||||||
|
pub mod community_post_tag;
|
||||||
|
|
||||||
/// Default value for columns like [community::Community.inbox_url] which are marked as serde(skip).
|
/// Default value for columns like [community::Community.inbox_url] which are marked as serde(skip).
|
||||||
///
|
///
|
||||||
|
|
|
@ -508,6 +508,11 @@ pub mod functions {
|
||||||
sql_function!(fn coalesce<T: diesel::sql_types::SqlType + diesel::sql_types::SingleValue>(x: diesel::sql_types::Nullable<T>, y: T) -> T);
|
sql_function!(fn coalesce<T: diesel::sql_types::SqlType + diesel::sql_types::SingleValue>(x: diesel::sql_types::Nullable<T>, y: T) -> T);
|
||||||
|
|
||||||
sql_function!(fn set_config(setting_name: Text, new_value: Text, is_local: Bool) -> Text);
|
sql_function!(fn set_config(setting_name: Text, new_value: Text, is_local: Bool) -> Text);
|
||||||
|
|
||||||
|
sql_function! {
|
||||||
|
#[aggregate]
|
||||||
|
fn json_agg<T: diesel::sql_types::SqlType + diesel::sql_types::SingleValue>(obj: T) -> Json
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const DELETED_REPLACEMENT_TEXT: &str = "*Permanently Deleted*";
|
pub const DELETED_REPLACEMENT_TEXT: &str = "*Permanently Deleted*";
|
||||||
|
|
|
@ -35,6 +35,7 @@ diesel-async = { workspace = true, optional = true }
|
||||||
diesel_ltree = { workspace = true, optional = true }
|
diesel_ltree = { workspace = true, optional = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_with = { workspace = true }
|
serde_with = { workspace = true }
|
||||||
|
serde_json = { workspace = true }
|
||||||
tracing = { workspace = true, optional = true }
|
tracing = { workspace = true, optional = true }
|
||||||
ts-rs = { workspace = true, optional = true }
|
ts-rs = { workspace = true, optional = true }
|
||||||
actix-web = { workspace = true, optional = true }
|
actix-web = { workspace = true, optional = true }
|
||||||
|
|
|
@ -41,10 +41,13 @@ use lemmy_db_schema::{
|
||||||
post_like,
|
post_like,
|
||||||
post_read,
|
post_read,
|
||||||
post_saved,
|
post_saved,
|
||||||
|
post_community_post_tag,
|
||||||
|
community_post_tag
|
||||||
},
|
},
|
||||||
source::{local_user::LocalUser, site::Site},
|
source::{local_user::LocalUser, site::Site},
|
||||||
utils::{
|
utils::{
|
||||||
functions::coalesce,
|
functions::coalesce,
|
||||||
|
functions::json_agg,
|
||||||
fuzzy_search,
|
fuzzy_search,
|
||||||
get_conn,
|
get_conn,
|
||||||
limit_and_offset,
|
limit_and_offset,
|
||||||
|
@ -214,6 +217,14 @@ fn queries<'a>() -> Queries<
|
||||||
} else {
|
} else {
|
||||||
Box::new(None::<i64>.into_sql::<sql_types::Nullable<sql_types::BigInt>>())
|
Box::new(None::<i64>.into_sql::<sql_types::Nullable<sql_types::BigInt>>())
|
||||||
};
|
};
|
||||||
|
let community_post_tags: Box<dyn BoxableExpression<_, Pg, SqlType = sql_types::Nullable<sql_types::Json>>> =
|
||||||
|
Box::new(
|
||||||
|
post_community_post_tag::table
|
||||||
|
.inner_join(community_post_tag::table)
|
||||||
|
.select(diesel::dsl::sql::<diesel::sql_types::Json>("json_agg(community_post_tag.*)"))
|
||||||
|
.filter(post_community_post_tag::post_id.eq(post_aggregates::post_id))
|
||||||
|
.single_value(),
|
||||||
|
);
|
||||||
|
|
||||||
query
|
query
|
||||||
.inner_join(person::table)
|
.inner_join(person::table)
|
||||||
|
@ -247,6 +258,7 @@ fn queries<'a>() -> Queries<
|
||||||
post_aggregates::comments.nullable() - read_comments,
|
post_aggregates::comments.nullable() - read_comments,
|
||||||
post_aggregates::comments,
|
post_aggregates::comments,
|
||||||
),
|
),
|
||||||
|
community_post_tags
|
||||||
))
|
))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1764,6 +1776,7 @@ mod tests {
|
||||||
hidden: false,
|
hidden: false,
|
||||||
saved: false,
|
saved: false,
|
||||||
creator_blocked: false,
|
creator_blocked: false,
|
||||||
|
community_post_tags: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,23 +3,7 @@ use diesel::Queryable;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
aggregates::structs::{CommentAggregates, PersonAggregates, PostAggregates, SiteAggregates},
|
aggregates::structs::{CommentAggregates, PersonAggregates, PostAggregates, SiteAggregates},
|
||||||
source::{
|
source::{
|
||||||
comment::Comment,
|
comment::Comment, comment_report::CommentReport, community::Community, community_post_tag::CommunityPostTag, custom_emoji::CustomEmoji, custom_emoji_keyword::CustomEmojiKeyword, images::{ImageDetails, LocalImage}, local_site::LocalSite, local_site_rate_limit::LocalSiteRateLimit, local_user::LocalUser, local_user_vote_display_mode::LocalUserVoteDisplayMode, person::Person, post::Post, post_report::PostReport, private_message::PrivateMessage, private_message_report::PrivateMessageReport, registration_application::RegistrationApplication, site::Site
|
||||||
comment_report::CommentReport,
|
|
||||||
community::Community,
|
|
||||||
custom_emoji::CustomEmoji,
|
|
||||||
custom_emoji_keyword::CustomEmojiKeyword,
|
|
||||||
images::{ImageDetails, LocalImage},
|
|
||||||
local_site::LocalSite,
|
|
||||||
local_site_rate_limit::LocalSiteRateLimit,
|
|
||||||
local_user::LocalUser,
|
|
||||||
local_user_vote_display_mode::LocalUserVoteDisplayMode,
|
|
||||||
person::Person,
|
|
||||||
post::Post,
|
|
||||||
post_report::PostReport,
|
|
||||||
private_message::PrivateMessage,
|
|
||||||
private_message_report::PrivateMessageReport,
|
|
||||||
registration_application::RegistrationApplication,
|
|
||||||
site::Site,
|
|
||||||
},
|
},
|
||||||
SubscribedType,
|
SubscribedType,
|
||||||
};
|
};
|
||||||
|
@ -144,6 +128,7 @@ pub struct PostView {
|
||||||
pub creator_blocked: bool,
|
pub creator_blocked: bool,
|
||||||
pub my_vote: Option<i16>,
|
pub my_vote: Option<i16>,
|
||||||
pub unread_comments: i64,
|
pub unread_comments: i64,
|
||||||
|
pub community_post_tags: Option<serde_json::Value>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)]
|
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)]
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
-- This file should undo anything in `up.sql`
|
||||||
|
drop table post_community_post_tag;
|
||||||
|
drop table community_post_tag;
|
17
migrations/2024-08-17-144959_community-post-tags/up.sql
Normal file
17
migrations/2024-08-17-144959_community-post-tags/up.sql
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
-- a tag for a post, valid in a community. created by mods of a community
|
||||||
|
CREATE TABLE community_post_tag (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
ap_id TEXT NOT NULL UNIQUE,
|
||||||
|
community_id INT NOT NULL REFERENCES community(id),
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
published TIMESTAMPTZ NOT NULL,
|
||||||
|
updated TIMESTAMPTZ,
|
||||||
|
deleted TIMESTAMPTZ
|
||||||
|
);
|
||||||
|
|
||||||
|
-- an association between a post and a community post tag. created/updated by the post author or mods of a community
|
||||||
|
CREATE TABLE post_community_post_tag (
|
||||||
|
post_id INT NOT NULL REFERENCES post(id),
|
||||||
|
community_post_tag_id INT NOT NULL REFERENCES community_post_tag(id),
|
||||||
|
PRIMARY KEY (post_id, community_post_tag_id)
|
||||||
|
);
|
|
@ -183,11 +183,14 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) {
|
||||||
.route("/follow", web::post().to(follow_community))
|
.route("/follow", web::post().to(follow_community))
|
||||||
.route("/block", web::post().to(block_community))
|
.route("/block", web::post().to(block_community))
|
||||||
.route("/delete", web::post().to(delete_community))
|
.route("/delete", web::post().to(delete_community))
|
||||||
|
.route("/post_tags", web::get().to(get_community_post_tags))
|
||||||
// Mod Actions
|
// Mod Actions
|
||||||
.route("/remove", web::post().to(remove_community))
|
.route("/remove", web::post().to(remove_community))
|
||||||
.route("/transfer", web::post().to(transfer_community))
|
.route("/transfer", web::post().to(transfer_community))
|
||||||
.route("/ban_user", web::post().to(ban_from_community))
|
.route("/ban_user", web::post().to(ban_from_community))
|
||||||
.route("/mod", web::post().to(add_mod_to_community)),
|
.route("/mod", web::post().to(add_mod_to_community))
|
||||||
|
.route("/post_tags", web::post().to(create_update_community_post_tag))
|
||||||
|
.route("/post_tags/delete", web::post().to(delete_community_post_tag)),
|
||||||
)
|
)
|
||||||
.service(
|
.service(
|
||||||
web::scope("/federated_instances")
|
web::scope("/federated_instances")
|
||||||
|
|
Loading…
Reference in a new issue