mirror of
https://github.com/LemmyNet/lemmy.git
synced 2025-01-08 11:11:37 +00:00
Merge branch 'combined_profile' into combined_modlog
This commit is contained in:
commit
b46fdab768
14 changed files with 115 additions and 15 deletions
|
@ -28,8 +28,10 @@ pub async fn list_person_saved(
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
let page_back = data.page_back;
|
let page_back = data.page_back;
|
||||||
|
let type_ = data.type_;
|
||||||
|
|
||||||
let saved = PersonSavedCombinedQuery {
|
let saved = PersonSavedCombinedQuery {
|
||||||
|
type_,
|
||||||
page_after,
|
page_after,
|
||||||
page_back,
|
page_back,
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,11 @@ pub async fn verify_email(
|
||||||
let site_view = SiteView::read_local(&mut context.pool()).await?;
|
let site_view = SiteView::read_local(&mut context.pool()).await?;
|
||||||
let token = data.token.clone();
|
let token = data.token.clone();
|
||||||
let verification = EmailVerification::read_for_token(&mut context.pool(), &token).await?;
|
let verification = EmailVerification::read_for_token(&mut context.pool(), &token).await?;
|
||||||
|
let local_user_id = verification.local_user_id;
|
||||||
|
let local_user_view = LocalUserView::read(&mut context.pool(), local_user_id).await?;
|
||||||
|
|
||||||
|
// Check if their email has already been verified once, before this
|
||||||
|
let email_already_verified = local_user_view.local_user.email_verified;
|
||||||
|
|
||||||
let form = LocalUserUpdateForm {
|
let form = LocalUserUpdateForm {
|
||||||
// necessary in case this is a new signup
|
// necessary in case this is a new signup
|
||||||
|
@ -27,18 +32,16 @@ pub async fn verify_email(
|
||||||
email: Some(Some(verification.email)),
|
email: Some(Some(verification.email)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let local_user_id = verification.local_user_id;
|
|
||||||
|
|
||||||
LocalUser::update(&mut context.pool(), local_user_id, &form).await?;
|
LocalUser::update(&mut context.pool(), local_user_id, &form).await?;
|
||||||
|
|
||||||
EmailVerification::delete_old_tokens_for_local_user(&mut context.pool(), local_user_id).await?;
|
EmailVerification::delete_old_tokens_for_local_user(&mut context.pool(), local_user_id).await?;
|
||||||
|
|
||||||
// send out notification about registration application to admins if enabled
|
// Send out notification about registration application to admins if enabled, and the user hasn't
|
||||||
if site_view.local_site.application_email_admins {
|
// already been verified.
|
||||||
let local_user = LocalUserView::read(&mut context.pool(), local_user_id).await?;
|
if site_view.local_site.application_email_admins && !email_already_verified {
|
||||||
|
|
||||||
send_new_applicant_email_to_admins(
|
send_new_applicant_email_to_admins(
|
||||||
&local_user.person.name,
|
&local_user_view.person.name,
|
||||||
&mut context.pool(),
|
&mut context.pool(),
|
||||||
context.settings(),
|
context.settings(),
|
||||||
)
|
)
|
||||||
|
|
|
@ -4,6 +4,7 @@ use lemmy_db_schema::{
|
||||||
source::{login_token::LoginToken, site::Site},
|
source::{login_token::LoginToken, site::Site},
|
||||||
CommentSortType,
|
CommentSortType,
|
||||||
ListingType,
|
ListingType,
|
||||||
|
PersonContentType,
|
||||||
PostListingMode,
|
PostListingMode,
|
||||||
PostSortType,
|
PostSortType,
|
||||||
};
|
};
|
||||||
|
@ -249,6 +250,8 @@ pub struct GetPersonDetailsResponse {
|
||||||
///
|
///
|
||||||
/// Either person_id, or username are required.
|
/// Either person_id, or username are required.
|
||||||
pub struct ListPersonContent {
|
pub struct ListPersonContent {
|
||||||
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
|
pub type_: Option<PersonContentType>,
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub person_id: Option<PersonId>,
|
pub person_id: Option<PersonId>,
|
||||||
/// Example: dessalines , or dessalines@xyz.tld
|
/// Example: dessalines , or dessalines@xyz.tld
|
||||||
|
@ -275,6 +278,8 @@ pub struct ListPersonContentResponse {
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
#[cfg_attr(feature = "full", ts(export))]
|
||||||
/// Gets your saved posts and comments
|
/// Gets your saved posts and comments
|
||||||
pub struct ListPersonSaved {
|
pub struct ListPersonSaved {
|
||||||
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
|
pub type_: Option<PersonContentType>,
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub page_cursor: Option<PersonSavedCombinedPaginationCursor>,
|
pub page_cursor: Option<PersonSavedCombinedPaginationCursor>,
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
|
|
|
@ -97,6 +97,10 @@ pub struct GetPosts {
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub community_name: Option<String>,
|
pub community_name: Option<String>,
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
|
pub saved_only: Option<bool>,
|
||||||
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
|
pub read_only: Option<bool>,
|
||||||
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub liked_only: Option<bool>,
|
pub liked_only: Option<bool>,
|
||||||
#[cfg_attr(feature = "full", ts(optional))]
|
#[cfg_attr(feature = "full", ts(optional))]
|
||||||
pub disliked_only: Option<bool>,
|
pub disliked_only: Option<bool>,
|
||||||
|
|
|
@ -32,7 +32,7 @@ use reqwest::{
|
||||||
};
|
};
|
||||||
use reqwest_middleware::ClientWithMiddleware;
|
use reqwest_middleware::ClientWithMiddleware;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tracing::info;
|
use tracing::{info, warn};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use urlencoding::encode;
|
use urlencoding::encode;
|
||||||
use webpage::HTML;
|
use webpage::HTML;
|
||||||
|
@ -173,15 +173,23 @@ pub async fn generate_post_link_metadata(
|
||||||
metadata.opengraph_data.image.clone()
|
metadata.opengraph_data.image.clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Attempt to generate a thumbnail depending on the instance settings. Either by proxying,
|
||||||
|
// storing image persistently in pict-rs or returning the remote url directly as thumbnail.
|
||||||
let thumbnail_url = if let (false, Some(url)) = (is_image_post, custom_thumbnail) {
|
let thumbnail_url = if let (false, Some(url)) = (is_image_post, custom_thumbnail) {
|
||||||
proxy_image_link(url, &context).await.ok()
|
proxy_image_link(url.clone(), &context)
|
||||||
} else if let (true, Some(url)) = (allow_generate_thumbnail, image_url) {
|
.await
|
||||||
|
.map_err(|e| warn!("Failed to proxy thumbnail: {e}"))
|
||||||
|
.ok()
|
||||||
|
.or(Some(url.into()))
|
||||||
|
} else if let (true, Some(url)) = (allow_generate_thumbnail, image_url.clone()) {
|
||||||
generate_pictrs_thumbnail(&url, &context)
|
generate_pictrs_thumbnail(&url, &context)
|
||||||
.await
|
.await
|
||||||
|
.map_err(|e| warn!("Failed to generate thumbnail: {e}"))
|
||||||
.ok()
|
.ok()
|
||||||
.map(Into::into)
|
.map(Into::into)
|
||||||
|
.or(image_url)
|
||||||
} else {
|
} else {
|
||||||
metadata.opengraph_data.image.clone()
|
image_url.clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
let form = PostUpdateForm {
|
let form = PostUpdateForm {
|
||||||
|
|
|
@ -9,7 +9,7 @@ use actix_web::web::{Json, Query};
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
comment::{GetComments, GetCommentsResponse},
|
comment::{GetComments, GetCommentsResponse},
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
utils::check_private_instance,
|
utils::{check_conflicting_like_filters, check_private_instance},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{comment::Comment, community::Community},
|
source::{comment::Comment, community::Community},
|
||||||
|
@ -19,7 +19,7 @@ use lemmy_db_views::{
|
||||||
comment_view::CommentQuery,
|
comment_view::CommentQuery,
|
||||||
structs::{LocalUserView, SiteView},
|
structs::{LocalUserView, SiteView},
|
||||||
};
|
};
|
||||||
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult};
|
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||||
|
|
||||||
#[tracing::instrument(skip(context))]
|
#[tracing::instrument(skip(context))]
|
||||||
pub async fn list_comments(
|
pub async fn list_comments(
|
||||||
|
@ -49,9 +49,7 @@ pub async fn list_comments(
|
||||||
|
|
||||||
let liked_only = data.liked_only;
|
let liked_only = data.liked_only;
|
||||||
let disliked_only = data.disliked_only;
|
let disliked_only = data.disliked_only;
|
||||||
if liked_only.unwrap_or_default() && disliked_only.unwrap_or_default() {
|
check_conflicting_like_filters(liked_only, disliked_only)?;
|
||||||
return Err(LemmyError::from(LemmyErrorType::ContradictingFilters));
|
|
||||||
}
|
|
||||||
|
|
||||||
let page = data.page;
|
let page = data.page;
|
||||||
let limit = data.limit;
|
let limit = data.limit;
|
||||||
|
|
|
@ -37,9 +37,11 @@ pub async fn list_person_content(
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
let page_back = data.page_back;
|
let page_back = data.page_back;
|
||||||
|
let type_ = data.type_;
|
||||||
|
|
||||||
let content = PersonContentCombinedQuery {
|
let content = PersonContentCombinedQuery {
|
||||||
creator_id: person_details_id,
|
creator_id: person_details_id,
|
||||||
|
type_,
|
||||||
page_after,
|
page_after,
|
||||||
page_back,
|
page_back,
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,7 @@ pub async fn list_posts(
|
||||||
} else {
|
} else {
|
||||||
data.community_id
|
data.community_id
|
||||||
};
|
};
|
||||||
|
let read_only = data.read_only;
|
||||||
let show_hidden = data.show_hidden;
|
let show_hidden = data.show_hidden;
|
||||||
let show_read = data.show_read;
|
let show_read = data.show_read;
|
||||||
let show_nsfw = data.show_nsfw;
|
let show_nsfw = data.show_nsfw;
|
||||||
|
@ -76,6 +77,7 @@ pub async fn list_posts(
|
||||||
listing_type,
|
listing_type,
|
||||||
sort,
|
sort,
|
||||||
community_id,
|
community_id,
|
||||||
|
read_only,
|
||||||
liked_only,
|
liked_only,
|
||||||
disliked_only,
|
disliked_only,
|
||||||
page,
|
page,
|
||||||
|
|
|
@ -219,6 +219,16 @@ pub enum ModlogActionType {
|
||||||
AdminAllowInstance,
|
AdminAllowInstance,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(EnumString, Display, Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
#[cfg_attr(feature = "full", derive(TS))]
|
||||||
|
#[cfg_attr(feature = "full", ts(export))]
|
||||||
|
/// A list of possible types for the various modlog actions.
|
||||||
|
pub enum PersonContentType {
|
||||||
|
All,
|
||||||
|
Comments,
|
||||||
|
Posts,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
EnumString, Display, Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq, Hash,
|
EnumString, Display, Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq, Hash,
|
||||||
)]
|
)]
|
||||||
|
|
|
@ -43,6 +43,7 @@ use lemmy_db_schema::{
|
||||||
},
|
},
|
||||||
utils::{actions, actions_alias, functions::coalesce, get_conn, DbPool},
|
utils::{actions, actions_alias, functions::coalesce, get_conn, DbPool},
|
||||||
InternalToCombinedView,
|
InternalToCombinedView,
|
||||||
|
PersonContentType,
|
||||||
};
|
};
|
||||||
use lemmy_utils::error::LemmyResult;
|
use lemmy_utils::error::LemmyResult;
|
||||||
|
|
||||||
|
@ -82,6 +83,8 @@ pub struct PaginationCursorData(PersonContentCombined);
|
||||||
pub struct PersonContentCombinedQuery {
|
pub struct PersonContentCombinedQuery {
|
||||||
pub creator_id: PersonId,
|
pub creator_id: PersonId,
|
||||||
#[new(default)]
|
#[new(default)]
|
||||||
|
pub type_: Option<PersonContentType>,
|
||||||
|
#[new(default)]
|
||||||
pub page_after: Option<PaginationCursorData>,
|
pub page_after: Option<PaginationCursorData>,
|
||||||
#[new(default)]
|
#[new(default)]
|
||||||
pub page_back: Option<bool>,
|
pub page_back: Option<bool>,
|
||||||
|
@ -208,6 +211,16 @@ impl PersonContentCombinedQuery {
|
||||||
|
|
||||||
let mut query = PaginatedQueryBuilder::new(query);
|
let mut query = PaginatedQueryBuilder::new(query);
|
||||||
|
|
||||||
|
if let Some(type_) = self.type_ {
|
||||||
|
query = match type_ {
|
||||||
|
PersonContentType::All => query,
|
||||||
|
PersonContentType::Comments => {
|
||||||
|
query.filter(person_content_combined::comment_id.is_not_null())
|
||||||
|
}
|
||||||
|
PersonContentType::Posts => query.filter(person_content_combined::post_id.is_not_null()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let page_after = self.page_after.map(|c| c.0);
|
let page_after = self.page_after.map(|c| c.0);
|
||||||
|
|
||||||
if self.page_back.unwrap_or_default() {
|
if self.page_back.unwrap_or_default() {
|
||||||
|
|
|
@ -40,6 +40,7 @@ use lemmy_db_schema::{
|
||||||
},
|
},
|
||||||
utils::{actions, actions_alias, functions::coalesce, get_conn, DbPool},
|
utils::{actions, actions_alias, functions::coalesce, get_conn, DbPool},
|
||||||
InternalToCombinedView,
|
InternalToCombinedView,
|
||||||
|
PersonContentType,
|
||||||
};
|
};
|
||||||
use lemmy_utils::error::LemmyResult;
|
use lemmy_utils::error::LemmyResult;
|
||||||
|
|
||||||
|
@ -77,6 +78,7 @@ pub struct PaginationCursorData(PersonSavedCombined);
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct PersonSavedCombinedQuery {
|
pub struct PersonSavedCombinedQuery {
|
||||||
|
pub type_: Option<PersonContentType>,
|
||||||
pub page_after: Option<PaginationCursorData>,
|
pub page_after: Option<PaginationCursorData>,
|
||||||
pub page_back: Option<bool>,
|
pub page_back: Option<bool>,
|
||||||
}
|
}
|
||||||
|
@ -210,6 +212,16 @@ impl PersonSavedCombinedQuery {
|
||||||
|
|
||||||
let mut query = PaginatedQueryBuilder::new(query);
|
let mut query = PaginatedQueryBuilder::new(query);
|
||||||
|
|
||||||
|
if let Some(type_) = self.type_ {
|
||||||
|
query = match type_ {
|
||||||
|
PersonContentType::All => query,
|
||||||
|
PersonContentType::Comments => {
|
||||||
|
query.filter(person_saved_combined::comment_id.is_not_null())
|
||||||
|
}
|
||||||
|
PersonContentType::Posts => query.filter(person_saved_combined::post_id.is_not_null()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let page_after = self.page_after.map(|c| c.0);
|
let page_after = self.page_after.map(|c| c.0);
|
||||||
|
|
||||||
if self.page_back.unwrap_or_default() {
|
if self.page_back.unwrap_or_default() {
|
||||||
|
|
|
@ -306,6 +306,12 @@ fn queries<'a>() -> Queries<
|
||||||
query = query.filter(post_aggregates::comments.eq(0));
|
query = query.filter(post_aggregates::comments.eq(0));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if o.read_only.unwrap_or_default() {
|
||||||
|
query = query
|
||||||
|
.filter(post_actions::read.is_not_null())
|
||||||
|
.then_order_by(post_actions::read.desc())
|
||||||
|
}
|
||||||
|
|
||||||
if !o.show_read.unwrap_or(o.local_user.show_read_posts()) {
|
if !o.show_read.unwrap_or(o.local_user.show_read_posts()) {
|
||||||
// Do not hide read posts when it is a user profile view
|
// Do not hide read posts when it is a user profile view
|
||||||
// Or, only hide read posts on non-profile views
|
// Or, only hide read posts on non-profile views
|
||||||
|
@ -496,6 +502,7 @@ pub struct PostQuery<'a> {
|
||||||
pub local_user: Option<&'a LocalUser>,
|
pub local_user: Option<&'a LocalUser>,
|
||||||
pub search_term: Option<String>,
|
pub search_term: Option<String>,
|
||||||
pub url_only: Option<bool>,
|
pub url_only: Option<bool>,
|
||||||
|
pub read_only: Option<bool>,
|
||||||
pub liked_only: Option<bool>,
|
pub liked_only: Option<bool>,
|
||||||
pub disliked_only: Option<bool>,
|
pub disliked_only: Option<bool>,
|
||||||
pub title_only: Option<bool>,
|
pub title_only: Option<bool>,
|
||||||
|
@ -1192,6 +1199,34 @@ mod tests {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test_context(Data)]
|
||||||
|
#[tokio::test]
|
||||||
|
#[serial]
|
||||||
|
async fn post_listing_read_only(data: &mut Data) -> LemmyResult<()> {
|
||||||
|
let pool = &data.pool();
|
||||||
|
let pool = &mut pool.into();
|
||||||
|
|
||||||
|
// Only mark the bot post as read
|
||||||
|
// The read_only should only show the bot post
|
||||||
|
let post_read_form =
|
||||||
|
PostReadForm::new(data.inserted_bot_post.id, data.local_user_view.person.id);
|
||||||
|
PostRead::mark_as_read(pool, &post_read_form).await?;
|
||||||
|
|
||||||
|
// Only read the post marked as read
|
||||||
|
let read_read_post_listing = PostQuery {
|
||||||
|
community_id: Some(data.inserted_community.id),
|
||||||
|
read_only: Some(true),
|
||||||
|
..data.default_post_query()
|
||||||
|
}
|
||||||
|
.list(&data.site, pool)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// This should only include the bot post, not the one you created
|
||||||
|
assert_eq!(vec![POST_BY_BOT], names(&read_read_post_listing));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test_context(Data)]
|
#[test_context(Data)]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
DROP INDEX idx_post_actions_on_read_read_not_null;
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
CREATE INDEX idx_post_actions_on_read_read_not_null ON post_actions (person_id, read, post_id)
|
||||||
|
WHERE
|
||||||
|
read IS NOT NULL;
|
||||||
|
|
Loading…
Reference in a new issue