parent
66b0ddbbc5
commit
6bc49bdd70
28 changed files with 526 additions and 10 deletions
|
@ -15,7 +15,7 @@ use lemmy_db_schema::{
|
|||
},
|
||||
traits::Crud,
|
||||
};
|
||||
use lemmy_db_views::structs::SiteView;
|
||||
use lemmy_db_views::structs::{CustomEmojiView, SiteView};
|
||||
use lemmy_db_views_actor::structs::PersonView;
|
||||
use lemmy_utils::{error::LemmyError, version, ConnectionId};
|
||||
|
||||
|
@ -64,8 +64,8 @@ impl Perform for LeaveAdmin {
|
|||
|
||||
let all_languages = Language::read_all(context.pool()).await?;
|
||||
let discussion_languages = SiteLanguage::read_local(context.pool()).await?;
|
||||
let taglines_res = Tagline::get_all(context.pool(), site_view.local_site.id).await?;
|
||||
let taglines = taglines_res.is_empty().then_some(taglines_res);
|
||||
let taglines = Tagline::get_all(context.pool(), site_view.local_site.id).await?;
|
||||
let custom_emojis = CustomEmojiView::get_all(context.pool(), site_view.local_site.id).await?;
|
||||
|
||||
Ok(GetSiteResponse {
|
||||
site_view,
|
||||
|
@ -77,6 +77,7 @@ impl Perform for LeaveAdmin {
|
|||
all_languages,
|
||||
discussion_languages,
|
||||
taglines,
|
||||
custom_emojis,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
42
crates/api_common/src/custom_emoji.rs
Normal file
42
crates/api_common/src/custom_emoji.rs
Normal file
|
@ -0,0 +1,42 @@
|
|||
use crate::sensitive::Sensitive;
|
||||
use lemmy_db_schema::newtypes::CustomEmojiId;
|
||||
use lemmy_db_views::structs::CustomEmojiView;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct CreateCustomEmoji {
|
||||
pub category: String,
|
||||
pub shortcode: String,
|
||||
pub image_url: Url,
|
||||
pub alt_text: String,
|
||||
pub keywords: Vec<String>,
|
||||
pub auth: Sensitive<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct EditCustomEmoji {
|
||||
pub id: CustomEmojiId,
|
||||
pub category: String,
|
||||
pub image_url: Url,
|
||||
pub alt_text: String,
|
||||
pub keywords: Vec<String>,
|
||||
pub auth: Sensitive<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
|
||||
pub struct DeleteCustomEmoji {
|
||||
pub id: CustomEmojiId,
|
||||
pub auth: Sensitive<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct DeleteCustomEmojiResponse {
|
||||
pub id: CustomEmojiId,
|
||||
pub success: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct CustomEmojiResponse {
|
||||
pub custom_emoji: CustomEmojiView,
|
||||
}
|
|
@ -2,6 +2,7 @@ pub mod comment;
|
|||
pub mod community;
|
||||
#[cfg(feature = "full")]
|
||||
pub mod context;
|
||||
pub mod custom_emoji;
|
||||
pub mod person;
|
||||
pub mod post;
|
||||
pub mod private_message;
|
||||
|
|
|
@ -14,6 +14,7 @@ use lemmy_db_schema::{
|
|||
};
|
||||
use lemmy_db_views::structs::{
|
||||
CommentView,
|
||||
CustomEmojiView,
|
||||
LocalUserView,
|
||||
PostView,
|
||||
RegistrationApplicationView,
|
||||
|
@ -224,7 +225,8 @@ pub struct GetSiteResponse {
|
|||
pub federated_instances: Option<FederatedInstances>, // Federation may be disabled
|
||||
pub all_languages: Vec<Language>,
|
||||
pub discussion_languages: Vec<LanguageId>,
|
||||
pub taglines: Option<Vec<Tagline>>,
|
||||
pub taglines: Vec<Tagline>,
|
||||
pub custom_emojis: Vec<CustomEmojiView>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
|
|
|
@ -118,6 +118,10 @@ pub enum UserOperationCrud {
|
|||
GetPrivateMessages,
|
||||
EditPrivateMessage,
|
||||
DeletePrivateMessage,
|
||||
//Emojis
|
||||
CreateCustomEmoji,
|
||||
EditCustomEmoji,
|
||||
DeleteCustomEmoji,
|
||||
}
|
||||
|
||||
#[derive(EnumString, Display, Debug, Clone)]
|
||||
|
|
54
crates/api_crud/src/custom_emoji/create.rs
Normal file
54
crates/api_crud/src/custom_emoji/create.rs
Normal file
|
@ -0,0 +1,54 @@
|
|||
use crate::PerformCrud;
|
||||
use actix_web::web::Data;
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
custom_emoji::{CreateCustomEmoji, CustomEmojiResponse},
|
||||
utils::{get_local_user_view_from_jwt, is_admin},
|
||||
};
|
||||
use lemmy_db_schema::source::{
|
||||
custom_emoji::{CustomEmoji, CustomEmojiInsertForm},
|
||||
custom_emoji_keyword::{CustomEmojiKeyword, CustomEmojiKeywordInsertForm},
|
||||
local_site::LocalSite,
|
||||
};
|
||||
use lemmy_db_views::structs::CustomEmojiView;
|
||||
use lemmy_utils::{error::LemmyError, ConnectionId};
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl PerformCrud for CreateCustomEmoji {
|
||||
type Response = CustomEmojiResponse;
|
||||
|
||||
#[tracing::instrument(skip(self, context, _websocket_id))]
|
||||
async fn perform(
|
||||
&self,
|
||||
context: &Data<LemmyContext>,
|
||||
_websocket_id: Option<ConnectionId>,
|
||||
) -> Result<CustomEmojiResponse, LemmyError> {
|
||||
let data: &CreateCustomEmoji = self;
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||
|
||||
let local_site = LocalSite::read(context.pool()).await?;
|
||||
// Make sure user is an admin
|
||||
is_admin(&local_user_view)?;
|
||||
|
||||
let emoji_form = CustomEmojiInsertForm::builder()
|
||||
.local_site_id(local_site.id)
|
||||
.shortcode(data.shortcode.to_lowercase().trim().to_string())
|
||||
.alt_text(data.alt_text.to_string())
|
||||
.category(data.category.to_string())
|
||||
.image_url(data.clone().image_url.into())
|
||||
.build();
|
||||
let emoji = CustomEmoji::create(context.pool(), &emoji_form).await?;
|
||||
let mut keywords = vec![];
|
||||
for keyword in &data.keywords {
|
||||
let keyword_form = CustomEmojiKeywordInsertForm::builder()
|
||||
.custom_emoji_id(emoji.id)
|
||||
.keyword(keyword.to_lowercase().trim().to_string())
|
||||
.build();
|
||||
keywords.push(keyword_form);
|
||||
}
|
||||
CustomEmojiKeyword::create(context.pool(), keywords).await?;
|
||||
let view = CustomEmojiView::get(context.pool(), emoji.id).await?;
|
||||
Ok(CustomEmojiResponse { custom_emoji: view })
|
||||
}
|
||||
}
|
33
crates/api_crud/src/custom_emoji/delete.rs
Normal file
33
crates/api_crud/src/custom_emoji/delete.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
use crate::PerformCrud;
|
||||
use actix_web::web::Data;
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
custom_emoji::{DeleteCustomEmoji, DeleteCustomEmojiResponse},
|
||||
utils::{get_local_user_view_from_jwt, is_admin},
|
||||
};
|
||||
use lemmy_db_schema::source::custom_emoji::CustomEmoji;
|
||||
use lemmy_utils::{error::LemmyError, ConnectionId};
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl PerformCrud for DeleteCustomEmoji {
|
||||
type Response = DeleteCustomEmojiResponse;
|
||||
|
||||
#[tracing::instrument(skip(self, context, _websocket_id))]
|
||||
async fn perform(
|
||||
&self,
|
||||
context: &Data<LemmyContext>,
|
||||
_websocket_id: Option<ConnectionId>,
|
||||
) -> Result<DeleteCustomEmojiResponse, LemmyError> {
|
||||
let data: &DeleteCustomEmoji = self;
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||
|
||||
// Make sure user is an admin
|
||||
is_admin(&local_user_view)?;
|
||||
CustomEmoji::delete(context.pool(), data.id).await?;
|
||||
Ok(DeleteCustomEmojiResponse {
|
||||
id: data.id,
|
||||
success: true,
|
||||
})
|
||||
}
|
||||
}
|
3
crates/api_crud/src/custom_emoji/mod.rs
Normal file
3
crates/api_crud/src/custom_emoji/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
mod create;
|
||||
mod delete;
|
||||
mod update;
|
54
crates/api_crud/src/custom_emoji/update.rs
Normal file
54
crates/api_crud/src/custom_emoji/update.rs
Normal file
|
@ -0,0 +1,54 @@
|
|||
use crate::PerformCrud;
|
||||
use actix_web::web::Data;
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
custom_emoji::{CustomEmojiResponse, EditCustomEmoji},
|
||||
utils::{get_local_user_view_from_jwt, is_admin},
|
||||
};
|
||||
use lemmy_db_schema::source::{
|
||||
custom_emoji::{CustomEmoji, CustomEmojiUpdateForm},
|
||||
custom_emoji_keyword::{CustomEmojiKeyword, CustomEmojiKeywordInsertForm},
|
||||
local_site::LocalSite,
|
||||
};
|
||||
use lemmy_db_views::structs::CustomEmojiView;
|
||||
use lemmy_utils::{error::LemmyError, ConnectionId};
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl PerformCrud for EditCustomEmoji {
|
||||
type Response = CustomEmojiResponse;
|
||||
|
||||
#[tracing::instrument(skip(self, context, _websocket_id))]
|
||||
async fn perform(
|
||||
&self,
|
||||
context: &Data<LemmyContext>,
|
||||
_websocket_id: Option<ConnectionId>,
|
||||
) -> Result<CustomEmojiResponse, LemmyError> {
|
||||
let data: &EditCustomEmoji = self;
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||
|
||||
let local_site = LocalSite::read(context.pool()).await?;
|
||||
// Make sure user is an admin
|
||||
is_admin(&local_user_view)?;
|
||||
|
||||
let emoji_form = CustomEmojiUpdateForm::builder()
|
||||
.local_site_id(local_site.id)
|
||||
.alt_text(data.alt_text.to_string())
|
||||
.category(data.category.to_string())
|
||||
.image_url(data.clone().image_url.into())
|
||||
.build();
|
||||
let emoji = CustomEmoji::update(context.pool(), data.id, &emoji_form).await?;
|
||||
CustomEmojiKeyword::delete(context.pool(), data.id).await?;
|
||||
let mut keywords = vec![];
|
||||
for keyword in &data.keywords {
|
||||
let keyword_form = CustomEmojiKeywordInsertForm::builder()
|
||||
.custom_emoji_id(emoji.id)
|
||||
.keyword(keyword.to_lowercase().trim().to_string())
|
||||
.build();
|
||||
keywords.push(keyword_form);
|
||||
}
|
||||
CustomEmojiKeyword::create(context.pool(), keywords).await?;
|
||||
let view = CustomEmojiView::get(context.pool(), emoji.id).await?;
|
||||
Ok(CustomEmojiResponse { custom_emoji: view })
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ use lemmy_utils::{error::LemmyError, ConnectionId};
|
|||
|
||||
mod comment;
|
||||
mod community;
|
||||
mod custom_emoji;
|
||||
mod post;
|
||||
mod private_message;
|
||||
mod site;
|
||||
|
|
|
@ -10,7 +10,7 @@ use lemmy_db_schema::source::{
|
|||
language::Language,
|
||||
tagline::Tagline,
|
||||
};
|
||||
use lemmy_db_views::structs::SiteView;
|
||||
use lemmy_db_views::structs::{CustomEmojiView, SiteView};
|
||||
use lemmy_db_views_actor::structs::{
|
||||
CommunityBlockView,
|
||||
CommunityFollowerView,
|
||||
|
@ -88,8 +88,8 @@ impl PerformCrud for GetSite {
|
|||
|
||||
let all_languages = Language::read_all(context.pool()).await?;
|
||||
let discussion_languages = SiteLanguage::read_local(context.pool()).await?;
|
||||
let taglines_res = Tagline::get_all(context.pool(), site_view.local_site.id).await?;
|
||||
let taglines = (!taglines_res.is_empty()).then_some(taglines_res);
|
||||
let taglines = Tagline::get_all(context.pool(), site_view.local_site.id).await?;
|
||||
let custom_emojis = CustomEmojiView::get_all(context.pool(), site_view.local_site.id).await?;
|
||||
|
||||
Ok(GetSiteResponse {
|
||||
site_view,
|
||||
|
@ -101,6 +101,7 @@ impl PerformCrud for GetSite {
|
|||
all_languages,
|
||||
discussion_languages,
|
||||
taglines,
|
||||
custom_emojis,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,13 @@ use lemmy_api_common::{
|
|||
ListCommunitiesResponse,
|
||||
TransferCommunity,
|
||||
},
|
||||
custom_emoji::{
|
||||
CreateCustomEmoji,
|
||||
CustomEmojiResponse,
|
||||
DeleteCustomEmoji,
|
||||
DeleteCustomEmojiResponse,
|
||||
EditCustomEmoji,
|
||||
},
|
||||
person::{
|
||||
AddAdmin,
|
||||
AddAdminResponse,
|
||||
|
@ -354,3 +361,15 @@ impl SendActivity for ListCommentReports {
|
|||
impl SendActivity for ResolveCommentReport {
|
||||
type Response = CommentReportResponse;
|
||||
}
|
||||
|
||||
impl SendActivity for CreateCustomEmoji {
|
||||
type Response = CustomEmojiResponse;
|
||||
}
|
||||
|
||||
impl SendActivity for EditCustomEmoji {
|
||||
type Response = CustomEmojiResponse;
|
||||
}
|
||||
|
||||
impl SendActivity for DeleteCustomEmoji {
|
||||
type Response = DeleteCustomEmojiResponse;
|
||||
}
|
||||
|
|
60
crates/db_schema/src/impls/custom_emoji.rs
Normal file
60
crates/db_schema/src/impls/custom_emoji.rs
Normal file
|
@ -0,0 +1,60 @@
|
|||
use crate::{
|
||||
newtypes::CustomEmojiId,
|
||||
schema::{
|
||||
custom_emoji::dsl::custom_emoji,
|
||||
custom_emoji_keyword::dsl::{custom_emoji_id, custom_emoji_keyword},
|
||||
},
|
||||
source::{
|
||||
custom_emoji::{CustomEmoji, CustomEmojiInsertForm, CustomEmojiUpdateForm},
|
||||
custom_emoji_keyword::{CustomEmojiKeyword, CustomEmojiKeywordInsertForm},
|
||||
},
|
||||
utils::{get_conn, DbPool},
|
||||
};
|
||||
use diesel::{dsl::insert_into, result::Error, ExpressionMethods, QueryDsl};
|
||||
use diesel_async::RunQueryDsl;
|
||||
|
||||
impl CustomEmoji {
|
||||
pub async fn create(pool: &DbPool, form: &CustomEmojiInsertForm) -> Result<Self, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
insert_into(custom_emoji)
|
||||
.values(form)
|
||||
.get_result::<Self>(conn)
|
||||
.await
|
||||
}
|
||||
pub async fn update(
|
||||
pool: &DbPool,
|
||||
emoji_id: CustomEmojiId,
|
||||
form: &CustomEmojiUpdateForm,
|
||||
) -> Result<Self, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
diesel::update(custom_emoji.find(emoji_id))
|
||||
.set(form)
|
||||
.get_result::<Self>(conn)
|
||||
.await
|
||||
}
|
||||
pub async fn delete(pool: &DbPool, emoji_id: CustomEmojiId) -> Result<usize, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
diesel::delete(custom_emoji.find(emoji_id))
|
||||
.execute(conn)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl CustomEmojiKeyword {
|
||||
pub async fn create(
|
||||
pool: &DbPool,
|
||||
form: Vec<CustomEmojiKeywordInsertForm>,
|
||||
) -> Result<Vec<Self>, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
insert_into(custom_emoji_keyword)
|
||||
.values(form)
|
||||
.get_results::<Self>(conn)
|
||||
.await
|
||||
}
|
||||
pub async fn delete(pool: &DbPool, emoji_id: CustomEmojiId) -> Result<usize, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
diesel::delete(custom_emoji_keyword.filter(custom_emoji_id.eq(emoji_id)))
|
||||
.execute(conn)
|
||||
.await
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ pub mod comment_reply;
|
|||
pub mod comment_report;
|
||||
pub mod community;
|
||||
pub mod community_block;
|
||||
pub mod custom_emoji;
|
||||
pub mod email_verification;
|
||||
pub mod federation_allowlist;
|
||||
pub mod federation_blocklist;
|
||||
|
|
|
@ -108,6 +108,10 @@ pub struct InstanceId(i32);
|
|||
#[cfg_attr(feature = "full", derive(DieselNewType))]
|
||||
pub struct LocalSiteId(i32);
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)]
|
||||
#[cfg_attr(feature = "full", derive(DieselNewType))]
|
||||
pub struct CustomEmojiId(i32);
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
|
||||
#[cfg_attr(feature = "full", derive(AsExpression, FromSqlRow))]
|
||||
|
|
|
@ -751,6 +751,27 @@ table! {
|
|||
}
|
||||
}
|
||||
|
||||
table! {
|
||||
custom_emoji (id) {
|
||||
id -> Int4,
|
||||
local_site_id -> Int4,
|
||||
shortcode -> Varchar,
|
||||
image_url -> Text,
|
||||
alt_text -> Text,
|
||||
category -> Text,
|
||||
published -> Timestamp,
|
||||
updated -> Nullable<Timestamp>,
|
||||
}
|
||||
}
|
||||
|
||||
table! {
|
||||
custom_emoji_keyword (id) {
|
||||
id -> Int4,
|
||||
custom_emoji_id -> Int4,
|
||||
keyword -> Varchar,
|
||||
}
|
||||
}
|
||||
|
||||
joinable!(person_block -> person (person_id));
|
||||
|
||||
joinable!(comment -> person (creator_id));
|
||||
|
@ -836,6 +857,8 @@ joinable!(federation_blocklist -> instance (instance_id));
|
|||
joinable!(local_site -> site (site_id));
|
||||
joinable!(local_site_rate_limit -> local_site (local_site_id));
|
||||
joinable!(tagline -> local_site (local_site_id));
|
||||
joinable!(custom_emoji -> local_site (local_site_id));
|
||||
joinable!(custom_emoji_keyword -> custom_emoji (custom_emoji_id));
|
||||
|
||||
allow_tables_to_appear_in_same_query!(
|
||||
activity,
|
||||
|
@ -896,5 +919,7 @@ allow_tables_to_appear_in_same_query!(
|
|||
federation_blocklist,
|
||||
local_site,
|
||||
local_site_rate_limit,
|
||||
person_follower
|
||||
person_follower,
|
||||
custom_emoji,
|
||||
custom_emoji_keyword,
|
||||
);
|
||||
|
|
44
crates/db_schema/src/source/custom_emoji.rs
Normal file
44
crates/db_schema/src/source/custom_emoji.rs
Normal file
|
@ -0,0 +1,44 @@
|
|||
use crate::newtypes::{CustomEmojiId, DbUrl, LocalSiteId};
|
||||
#[cfg(feature = "full")]
|
||||
use crate::schema::custom_emoji;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use typed_builder::TypedBuilder;
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = custom_emoji))]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
diesel(belongs_to(crate::source::local_site::LocalSite))
|
||||
)]
|
||||
pub struct CustomEmoji {
|
||||
pub id: CustomEmojiId,
|
||||
pub local_site_id: LocalSiteId,
|
||||
pub shortcode: String,
|
||||
pub image_url: DbUrl,
|
||||
pub alt_text: String,
|
||||
pub category: String,
|
||||
pub published: chrono::NaiveDateTime,
|
||||
pub updated: Option<chrono::NaiveDateTime>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, TypedBuilder)]
|
||||
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = custom_emoji))]
|
||||
pub struct CustomEmojiInsertForm {
|
||||
pub local_site_id: LocalSiteId,
|
||||
pub shortcode: String,
|
||||
pub image_url: DbUrl,
|
||||
pub alt_text: String,
|
||||
pub category: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, TypedBuilder)]
|
||||
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = custom_emoji))]
|
||||
pub struct CustomEmojiUpdateForm {
|
||||
pub local_site_id: LocalSiteId,
|
||||
pub image_url: DbUrl,
|
||||
pub alt_text: String,
|
||||
pub category: String,
|
||||
}
|
26
crates/db_schema/src/source/custom_emoji_keyword.rs
Normal file
26
crates/db_schema/src/source/custom_emoji_keyword.rs
Normal file
|
@ -0,0 +1,26 @@
|
|||
use crate::newtypes::CustomEmojiId;
|
||||
#[cfg(feature = "full")]
|
||||
use crate::schema::custom_emoji_keyword;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use typed_builder::TypedBuilder;
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = custom_emoji_keyword))]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
diesel(belongs_to(crate::source::custom_emoji::CustomEmoji))
|
||||
)]
|
||||
pub struct CustomEmojiKeyword {
|
||||
pub id: i32,
|
||||
pub custom_emoji_id: CustomEmojiId,
|
||||
pub keyword: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, TypedBuilder)]
|
||||
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = custom_emoji_keyword))]
|
||||
pub struct CustomEmojiKeywordInsertForm {
|
||||
pub custom_emoji_id: CustomEmojiId,
|
||||
pub keyword: String,
|
||||
}
|
|
@ -6,6 +6,8 @@ pub mod comment_reply;
|
|||
pub mod comment_report;
|
||||
pub mod community;
|
||||
pub mod community_block;
|
||||
pub mod custom_emoji;
|
||||
pub mod custom_emoji_keyword;
|
||||
pub mod email_verification;
|
||||
pub mod federation_allowlist;
|
||||
pub mod federation_blocklist;
|
||||
|
|
82
crates/db_views/src/custom_emoji_view.rs
Normal file
82
crates/db_views/src/custom_emoji_view.rs
Normal file
|
@ -0,0 +1,82 @@
|
|||
use crate::structs::CustomEmojiView;
|
||||
use diesel::{result::Error, ExpressionMethods, JoinOnDsl, NullableExpressionMethods, QueryDsl};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use lemmy_db_schema::{
|
||||
newtypes::{CustomEmojiId, LocalSiteId},
|
||||
schema::{custom_emoji, custom_emoji_keyword},
|
||||
source::{custom_emoji::CustomEmoji, custom_emoji_keyword::CustomEmojiKeyword},
|
||||
utils::{get_conn, DbPool},
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
|
||||
type CustomEmojiTuple = (CustomEmoji, Option<CustomEmojiKeyword>);
|
||||
|
||||
impl CustomEmojiView {
|
||||
pub async fn get(pool: &DbPool, emoji_id: CustomEmojiId) -> Result<Self, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
let emojis = custom_emoji::table
|
||||
.find(emoji_id)
|
||||
.left_join(
|
||||
custom_emoji_keyword::table.on(custom_emoji_keyword::custom_emoji_id.eq(custom_emoji::id)),
|
||||
)
|
||||
.select((
|
||||
custom_emoji::all_columns,
|
||||
custom_emoji_keyword::all_columns.nullable(), // (or all the columns if you want)
|
||||
))
|
||||
.load::<CustomEmojiTuple>(conn)
|
||||
.await?;
|
||||
if let Some(emoji) = CustomEmojiView::from_tuple_to_vec(emojis)
|
||||
.into_iter()
|
||||
.next()
|
||||
{
|
||||
Ok(emoji)
|
||||
} else {
|
||||
Err(diesel::result::Error::NotFound)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_all(pool: &DbPool, for_local_site_id: LocalSiteId) -> Result<Vec<Self>, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
let emojis = custom_emoji::table
|
||||
.filter(custom_emoji::local_site_id.eq(for_local_site_id))
|
||||
.left_join(
|
||||
custom_emoji_keyword::table.on(custom_emoji_keyword::custom_emoji_id.eq(custom_emoji::id)),
|
||||
)
|
||||
.order(custom_emoji::category)
|
||||
.then_order_by(custom_emoji::id)
|
||||
.select((
|
||||
custom_emoji::all_columns,
|
||||
custom_emoji_keyword::all_columns.nullable(), // (or all the columns if you want)
|
||||
))
|
||||
.load::<CustomEmojiTuple>(conn)
|
||||
.await?;
|
||||
|
||||
Ok(CustomEmojiView::from_tuple_to_vec(emojis))
|
||||
}
|
||||
|
||||
fn from_tuple_to_vec(items: Vec<CustomEmojiTuple>) -> Vec<Self> {
|
||||
let mut result = Vec::new();
|
||||
let mut hash: HashMap<CustomEmojiId, Vec<CustomEmojiKeyword>> = HashMap::new();
|
||||
for item in &items {
|
||||
let emoji_id: CustomEmojiId = item.0.id;
|
||||
if let std::collections::hash_map::Entry::Vacant(e) = hash.entry(emoji_id) {
|
||||
e.insert(Vec::new());
|
||||
result.push(CustomEmojiView {
|
||||
custom_emoji: item.0.clone(),
|
||||
keywords: Vec::new(),
|
||||
})
|
||||
}
|
||||
if let Some(item_keyword) = &item.1 {
|
||||
if let Some(keywords) = hash.get_mut(&emoji_id) {
|
||||
keywords.push(item_keyword.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
for emoji in &mut result {
|
||||
if let Some(keywords) = hash.get_mut(&emoji.custom_emoji.id) {
|
||||
emoji.keywords = keywords.clone();
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
|
@ -6,6 +6,8 @@ pub mod comment_report_view;
|
|||
#[cfg(feature = "full")]
|
||||
pub mod comment_view;
|
||||
#[cfg(feature = "full")]
|
||||
pub mod custom_emoji_view;
|
||||
#[cfg(feature = "full")]
|
||||
pub mod local_user_view;
|
||||
#[cfg(feature = "full")]
|
||||
pub mod post_report_view;
|
||||
|
|
|
@ -4,6 +4,8 @@ use lemmy_db_schema::{
|
|||
comment::Comment,
|
||||
comment_report::CommentReport,
|
||||
community::Community,
|
||||
custom_emoji::CustomEmoji,
|
||||
custom_emoji_keyword::CustomEmojiKeyword,
|
||||
local_site::LocalSite,
|
||||
local_site_rate_limit::LocalSiteRateLimit,
|
||||
local_user::LocalUser,
|
||||
|
@ -113,3 +115,8 @@ pub struct SiteView {
|
|||
pub local_site_rate_limit: LocalSiteRateLimit,
|
||||
pub counts: SiteAggregates,
|
||||
}
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct CustomEmojiView {
|
||||
pub custom_emoji: CustomEmoji,
|
||||
pub keywords: Vec<CustomEmojiKeyword>,
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ services:
|
|||
restart: always
|
||||
|
||||
pictrs:
|
||||
image: asonix/pictrs:0.3.1
|
||||
image: asonix/pictrs:0.4.0-beta.19
|
||||
# this needs to match the pictrs url in lemmy.hjson
|
||||
hostname: pictrs
|
||||
# we can set options to pictrs like this, here we set max. image size and forced format for conversion
|
||||
|
@ -82,6 +82,11 @@ services:
|
|||
- PICTRS__API_KEY=API_KEY
|
||||
- RUST_LOG=debug
|
||||
- RUST_BACKTRACE=full
|
||||
- PICTRS__MEDIA__VIDEO_CODEC=vp9
|
||||
- PICTRS__MEDIA__GIF__MAX_WIDTH=256
|
||||
- PICTRS__MEDIA__GIF__MAX_HEIGHT=256
|
||||
- PICTRS__MEDIA__GIF__MAX_AREA=65536
|
||||
- PICTRS__MEDIA__GIF__MAX_FRAME_COUNT=400
|
||||
user: 991:991
|
||||
volumes:
|
||||
- ./volumes/pictrs:/mnt
|
||||
|
|
|
@ -22,7 +22,7 @@ services:
|
|||
|
||||
pictrs:
|
||||
restart: always
|
||||
image: asonix/pictrs:0.3.1
|
||||
image: asonix/pictrs:0.4.0-beta.19
|
||||
user: 991:991
|
||||
volumes:
|
||||
- ./volumes/pictrs_alpha:/mnt
|
||||
|
|
2
migrations/2023-02-11-173347_custom_emojis/down.sql
Normal file
2
migrations/2023-02-11-173347_custom_emojis/down.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
drop table custom_emoji_keyword;
|
||||
drop table custom_emoji;
|
19
migrations/2023-02-11-173347_custom_emojis/up.sql
Normal file
19
migrations/2023-02-11-173347_custom_emojis/up.sql
Normal file
|
@ -0,0 +1,19 @@
|
|||
create table custom_emoji (
|
||||
id serial primary key,
|
||||
local_site_id int references local_site on update cascade on delete cascade not null,
|
||||
shortcode varchar(128) not null UNIQUE,
|
||||
image_url text not null UNIQUE,
|
||||
alt_text text not null,
|
||||
category text not null,
|
||||
published timestamp without time zone default now() not null,
|
||||
updated timestamp without time zone
|
||||
);
|
||||
|
||||
create table custom_emoji_keyword (
|
||||
id serial primary key,
|
||||
custom_emoji_id int references custom_emoji on update cascade on delete cascade not null,
|
||||
keyword varchar(128) not null,
|
||||
UNIQUE (custom_emoji_id, keyword)
|
||||
);
|
||||
|
||||
create index idx_custom_emoji_category on custom_emoji (id,category);
|
|
@ -31,6 +31,7 @@ use lemmy_api_common::{
|
|||
TransferCommunity,
|
||||
},
|
||||
context::LemmyContext,
|
||||
custom_emoji::{CreateCustomEmoji, DeleteCustomEmoji, EditCustomEmoji},
|
||||
person::{
|
||||
AddAdmin,
|
||||
BanPerson,
|
||||
|
@ -352,6 +353,16 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) {
|
|||
.route("/community", web::post().to(route_post::<PurgeCommunity>))
|
||||
.route("/post", web::post().to(route_post::<PurgePost>))
|
||||
.route("/comment", web::post().to(route_post::<PurgeComment>)),
|
||||
)
|
||||
.service(
|
||||
web::scope("/custom_emoji")
|
||||
.wrap(rate_limit.message())
|
||||
.route("", web::post().to(route_post_crud::<CreateCustomEmoji>))
|
||||
.route("", web::put().to(route_post_crud::<EditCustomEmoji>))
|
||||
.route(
|
||||
"/delete",
|
||||
web::post().to(route_post_crud::<DeleteCustomEmoji>),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ use lemmy_api_common::{
|
|||
TransferCommunity,
|
||||
},
|
||||
context::LemmyContext,
|
||||
custom_emoji::{CreateCustomEmoji, DeleteCustomEmoji, EditCustomEmoji},
|
||||
person::{
|
||||
AddAdmin,
|
||||
BanPerson,
|
||||
|
@ -389,6 +390,16 @@ pub async fn match_websocket_operation_crud(
|
|||
UserOperationCrud::GetComment => {
|
||||
do_websocket_operation_crud::<GetComment>(context, id, op, data).await
|
||||
}
|
||||
// Emojis
|
||||
UserOperationCrud::CreateCustomEmoji => {
|
||||
do_websocket_operation_crud::<CreateCustomEmoji>(context, id, op, data).await
|
||||
}
|
||||
UserOperationCrud::EditCustomEmoji => {
|
||||
do_websocket_operation_crud::<EditCustomEmoji>(context, id, op, data).await
|
||||
}
|
||||
UserOperationCrud::DeleteCustomEmoji => {
|
||||
do_websocket_operation_crud::<DeleteCustomEmoji>(context, id, op, data).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue