use crate::{community_moderator_view::CommunityModeratorView, person_view::PersonViewSafe};
use diesel::{result::Error, *};
-use lemmy_db_queries::{
+use lemmy_db_schema::{
aggregates::community_aggregates::CommunityAggregates,
functions::hot_rank,
fuzzy_search,
limit_and_offset,
+ newtypes::{CommunityId, PersonId},
+ schema::{community, community_aggregates, community_block, community_follower, local_user},
+ source::{
+ community::{Community, CommunityFollower, CommunitySafe},
+ community_block::CommunityBlock,
+ },
+ traits::{MaybeOptional, ToSafe, ViewToVec},
ListingType,
- MaybeOptional,
SortType,
- ToSafe,
- ViewToVec,
-};
-use lemmy_db_schema::{
- schema::{community, community_aggregates, community_follower},
- source::community::{Community, CommunityFollower, CommunitySafe},
- CommunityId,
- PersonId,
};
-use serde::Serialize;
+use serde::{Deserialize, Serialize};
-#[derive(Debug, Serialize, Clone)]
+#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct CommunityView {
pub community: CommunitySafe,
pub subscribed: bool,
+ pub blocked: bool,
pub counts: CommunityAggregates,
}
CommunitySafe,
CommunityAggregates,
Option<CommunityFollower>,
+ Option<CommunityBlock>,
);
impl CommunityView {
// The left join below will return None in this case
let person_id_join = my_person_id.unwrap_or(PersonId(-1));
- let (community, counts, follower) = community::table
+ let (community, counts, follower, blocked) = community::table
.find(community_id)
.inner_join(community_aggregates::table)
.left_join(
.and(community_follower::person_id.eq(person_id_join)),
),
)
+ .left_join(
+ community_block::table.on(
+ community::id
+ .eq(community_block::community_id)
+ .and(community_block::person_id.eq(person_id_join)),
+ ),
+ )
.select((
Community::safe_columns_tuple(),
community_aggregates::all_columns,
community_follower::all_columns.nullable(),
+ community_block::all_columns.nullable(),
))
.first::<CommunityViewTuple>(conn)?;
Ok(CommunityView {
community,
subscribed: follower.is_some(),
+ blocked: blocked.is_some(),
counts,
})
}
- // TODO: this function is only used by is_mod_or_admin() below, can probably be merged
- fn community_mods_and_admins(
- conn: &PgConnection,
- community_id: CommunityId,
- ) -> Result<Vec<PersonId>, Error> {
- let mut mods_and_admins: Vec<PersonId> = Vec::new();
- mods_and_admins.append(
- &mut CommunityModeratorView::for_community(conn, community_id)
- .map(|v| v.into_iter().map(|m| m.moderator.id).collect())?,
- );
- mods_and_admins.append(
- &mut PersonViewSafe::admins(conn).map(|v| v.into_iter().map(|a| a.person.id).collect())?,
- );
- Ok(mods_and_admins)
- }
-
pub fn is_mod_or_admin(
conn: &PgConnection,
person_id: PersonId,
community_id: CommunityId,
) -> bool {
- Self::community_mods_and_admins(conn, community_id)
+ let is_mod = CommunityModeratorView::for_community(conn, community_id)
+ .map(|v| {
+ v.into_iter()
+ .map(|m| m.moderator.id)
+ .collect::<Vec<PersonId>>()
+ })
+ .unwrap_or_default()
+ .contains(&person_id);
+ if is_mod {
+ return true;
+ }
+
+ PersonViewSafe::admins(conn)
+ .map(|v| {
+ v.into_iter()
+ .map(|a| a.person.id)
+ .collect::<Vec<PersonId>>()
+ })
.unwrap_or_default()
.contains(&person_id)
}
pub struct CommunityQueryBuilder<'a> {
conn: &'a PgConnection,
- listing_type: &'a ListingType,
- sort: &'a SortType,
+ listing_type: Option<ListingType>,
+ sort: Option<SortType>,
my_person_id: Option<PersonId>,
- show_nsfw: bool,
+ show_nsfw: Option<bool>,
search_term: Option<String>,
page: Option<i64>,
limit: Option<i64>,
CommunityQueryBuilder {
conn,
my_person_id: None,
- listing_type: &ListingType::All,
- sort: &SortType::Hot,
- show_nsfw: true,
+ listing_type: None,
+ sort: None,
+ show_nsfw: None,
search_term: None,
page: None,
limit: None,
}
}
- pub fn listing_type(mut self, listing_type: &'a ListingType) -> Self {
- self.listing_type = listing_type;
+ pub fn listing_type<T: MaybeOptional<ListingType>>(mut self, listing_type: T) -> Self {
+ self.listing_type = listing_type.get_optional();
self
}
- pub fn sort(mut self, sort: &'a SortType) -> Self {
- self.sort = sort;
+ pub fn sort<T: MaybeOptional<SortType>>(mut self, sort: T) -> Self {
+ self.sort = sort.get_optional();
self
}
- pub fn show_nsfw(mut self, show_nsfw: bool) -> Self {
- self.show_nsfw = show_nsfw;
+ pub fn show_nsfw<T: MaybeOptional<bool>>(mut self, show_nsfw: T) -> Self {
+ self.show_nsfw = show_nsfw.get_optional();
self
}
let mut query = community::table
.inner_join(community_aggregates::table)
+ .left_join(local_user::table.on(local_user::person_id.eq(person_id_join)))
.left_join(
community_follower::table.on(
community::id
.and(community_follower::person_id.eq(person_id_join)),
),
)
+ .left_join(
+ community_block::table.on(
+ community::id
+ .eq(community_block::community_id)
+ .and(community_block::person_id.eq(person_id_join)),
+ ),
+ )
.select((
Community::safe_columns_tuple(),
community_aggregates::all_columns,
community_follower::all_columns.nullable(),
+ community_block::all_columns.nullable(),
))
.into_boxed();
.or_filter(community::description.ilike(searcher));
};
- match self.sort {
+ match self.sort.unwrap_or(SortType::Hot) {
SortType::New => query = query.order_by(community::published.desc()),
SortType::TopAll => query = query.order_by(community_aggregates::subscribers.desc()),
SortType::TopMonth => query = query.order_by(community_aggregates::users_active_month.desc()),
- // Covers all other sorts, including hot
+ SortType::Hot => {
+ query = query
+ .order_by(
+ hot_rank(
+ community_aggregates::subscribers,
+ community_aggregates::published,
+ )
+ .desc(),
+ )
+ .then_order_by(community_aggregates::published.desc());
+ // Don't show hidden communities in Hot (trending)
+ query = query.filter(
+ community::hidden
+ .eq(false)
+ .or(community_follower::person_id.eq(person_id_join)),
+ );
+ }
+ // Covers all other sorts
_ => {
query = query
.order_by(
}
};
- if !self.show_nsfw {
- query = query.filter(community::nsfw.eq(false));
- };
+ if let Some(listing_type) = self.listing_type {
+ query = match listing_type {
+ ListingType::Subscribed => query.filter(community_follower::person_id.is_not_null()), // TODO could be this: and(community_follower::person_id.eq(person_id_join)),
+ ListingType::Local => query.filter(community::local.eq(true)),
+ _ => query,
+ };
+ }
- query = match self.listing_type {
- ListingType::Subscribed => query.filter(community_follower::person_id.is_not_null()), // TODO could be this: and(community_follower::person_id.eq(person_id_join)),
- ListingType::Local => query.filter(community::local.eq(true)),
- _ => query,
- };
+ // Don't show blocked communities or nsfw communities if not enabled in profile
+ if self.my_person_id.is_some() {
+ query = query.filter(community_block::person_id.is_null());
+ query = query.filter(community::nsfw.eq(false).or(local_user::show_nsfw.eq(true)));
+ } else {
+ // No person in request, only show nsfw communities if show_nsfw passed into request
+ if !self.show_nsfw.unwrap_or(false) {
+ query = query.filter(community::nsfw.eq(false));
+ }
+ }
let (limit, offset) = limit_and_offset(self.page, self.limit);
let res = query
community: a.0.to_owned(),
counts: a.1.to_owned(),
subscribed: a.2.is_some(),
+ blocked: a.3.is_some(),
})
.collect::<Vec<Self>>()
}