2020-12-21 23:27:42 +00:00
use crate ::{ community_moderator_view ::CommunityModeratorView , user_view ::UserViewSafe } ;
2020-12-21 16:30:34 +00:00
use diesel ::{ result ::Error , * } ;
2020-12-21 23:27:42 +00:00
use lemmy_db_queries ::{
2020-12-04 21:35:46 +00:00
aggregates ::community_aggregates ::CommunityAggregates ,
2020-12-06 21:44:36 +00:00
functions ::hot_rank ,
2020-12-06 03:49:15 +00:00
fuzzy_search ,
limit_and_offset ,
2021-01-26 17:18:01 +00:00
ListingType ,
2020-12-06 03:49:15 +00:00
MaybeOptional ,
SortType ,
2020-12-05 04:18:30 +00:00
ToSafe ,
2020-12-21 23:27:42 +00:00
ViewToVec ,
2020-12-04 16:29:44 +00:00
} ;
2020-12-18 18:38:32 +00:00
use lemmy_db_schema ::{
schema ::{ category , community , community_aggregates , community_follower , user_ } ,
2020-12-21 12:28:12 +00:00
source ::{
2020-12-21 13:38:34 +00:00
category ::Category ,
2020-12-21 12:28:12 +00:00
community ::{ Community , CommunityFollower , CommunitySafe } ,
user ::{ UserSafe , User_ } ,
} ,
2020-12-18 16:17:21 +00:00
} ;
2020-12-04 16:29:44 +00:00
use serde ::Serialize ;
#[ derive(Debug, Serialize, Clone) ]
pub struct CommunityView {
2020-12-06 03:49:15 +00:00
pub community : CommunitySafe ,
2020-12-04 16:29:44 +00:00
pub creator : UserSafe ,
pub category : Category ,
pub subscribed : bool ,
2020-12-04 21:35:46 +00:00
pub counts : CommunityAggregates ,
2020-12-04 16:29:44 +00:00
}
2020-12-11 01:39:42 +00:00
type CommunityViewTuple = (
CommunitySafe ,
UserSafe ,
Category ,
CommunityAggregates ,
Option < CommunityFollower > ,
) ;
2020-12-04 16:29:44 +00:00
impl CommunityView {
pub fn read (
conn : & PgConnection ,
community_id : i32 ,
my_user_id : Option < i32 > ,
) -> Result < Self , Error > {
2020-12-06 03:49:15 +00:00
// The left join below will return None in this case
let user_id_join = my_user_id . unwrap_or ( - 1 ) ;
2020-12-04 16:29:44 +00:00
2020-12-10 20:53:49 +00:00
let ( community , creator , category , counts , follower ) = community ::table
2020-12-04 16:29:44 +00:00
. find ( community_id )
. inner_join ( user_ ::table )
. inner_join ( category ::table )
2020-12-04 21:35:46 +00:00
. inner_join ( community_aggregates ::table )
2020-12-06 03:49:15 +00:00
. left_join (
community_follower ::table . on (
community ::id
. eq ( community_follower ::community_id )
. and ( community_follower ::user_id . eq ( user_id_join ) ) ,
) ,
)
2020-12-05 04:18:30 +00:00
. select ( (
2020-12-06 03:49:15 +00:00
Community ::safe_columns_tuple ( ) ,
2020-12-05 04:18:30 +00:00
User_ ::safe_columns_tuple ( ) ,
category ::all_columns ,
community_aggregates ::all_columns ,
2020-12-06 03:49:15 +00:00
community_follower ::all_columns . nullable ( ) ,
2020-12-05 04:18:30 +00:00
) )
2020-12-11 01:39:42 +00:00
. first ::< CommunityViewTuple > ( conn ) ? ;
2020-12-04 16:29:44 +00:00
Ok ( CommunityView {
community ,
2020-12-05 04:18:30 +00:00
creator ,
2020-12-04 16:29:44 +00:00
category ,
2020-12-10 20:53:49 +00:00
subscribed : follower . is_some ( ) ,
2020-12-04 21:35:46 +00:00
counts ,
2020-12-04 16:29:44 +00:00
} )
}
2020-12-21 16:30:34 +00:00
// 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 : i32 ) -> Result < Vec < i32 > , Error > {
let mut mods_and_admins : Vec < i32 > = 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 UserViewSafe ::admins ( conn ) . map ( | v | v . into_iter ( ) . map ( | a | a . user . id ) . collect ( ) ) ? ) ;
Ok ( mods_and_admins )
}
pub fn is_mod_or_admin ( conn : & PgConnection , user_id : i32 , community_id : i32 ) -> bool {
Self ::community_mods_and_admins ( conn , community_id )
. unwrap_or_default ( )
. contains ( & user_id )
}
2020-12-04 16:29:44 +00:00
}
2020-12-06 03:49:15 +00:00
pub struct CommunityQueryBuilder < ' a > {
conn : & ' a PgConnection ,
2021-01-26 17:18:01 +00:00
listing_type : & ' a ListingType ,
2020-12-06 03:49:15 +00:00
sort : & ' a SortType ,
2020-12-16 18:59:43 +00:00
my_user_id : Option < i32 > ,
2020-12-06 03:49:15 +00:00
show_nsfw : bool ,
search_term : Option < String > ,
page : Option < i64 > ,
limit : Option < i64 > ,
}
impl < ' a > CommunityQueryBuilder < ' a > {
2020-12-16 18:59:43 +00:00
pub fn create ( conn : & ' a PgConnection ) -> Self {
2020-12-06 03:49:15 +00:00
CommunityQueryBuilder {
conn ,
2020-12-16 18:59:43 +00:00
my_user_id : None ,
2021-01-26 17:18:01 +00:00
listing_type : & ListingType ::All ,
2020-12-06 03:49:15 +00:00
sort : & SortType ::Hot ,
show_nsfw : true ,
search_term : None ,
page : None ,
limit : None ,
}
}
2021-01-26 17:18:01 +00:00
pub fn listing_type ( mut self , listing_type : & ' a ListingType ) -> Self {
self . listing_type = listing_type ;
self
}
2020-12-06 03:49:15 +00:00
pub fn sort ( mut self , sort : & ' a SortType ) -> Self {
self . sort = sort ;
self
}
pub fn show_nsfw ( mut self , show_nsfw : bool ) -> Self {
self . show_nsfw = show_nsfw ;
self
}
pub fn search_term < T : MaybeOptional < String > > ( mut self , search_term : T ) -> Self {
self . search_term = search_term . get_optional ( ) ;
self
}
2020-12-16 18:59:43 +00:00
pub fn my_user_id < T : MaybeOptional < i32 > > ( mut self , my_user_id : T ) -> Self {
self . my_user_id = my_user_id . get_optional ( ) ;
self
}
2020-12-06 03:49:15 +00:00
pub fn page < T : MaybeOptional < i64 > > ( mut self , page : T ) -> Self {
self . page = page . get_optional ( ) ;
self
}
pub fn limit < T : MaybeOptional < i64 > > ( mut self , limit : T ) -> Self {
self . limit = limit . get_optional ( ) ;
self
}
pub fn list ( self ) -> Result < Vec < CommunityView > , Error > {
2020-12-16 18:59:43 +00:00
// The left join below will return None in this case
let user_id_join = self . my_user_id . unwrap_or ( - 1 ) ;
let mut query = community ::table
. inner_join ( user_ ::table )
. inner_join ( category ::table )
. inner_join ( community_aggregates ::table )
. left_join (
community_follower ::table . on (
community ::id
. eq ( community_follower ::community_id )
. and ( community_follower ::user_id . eq ( user_id_join ) ) ,
) ,
)
. select ( (
Community ::safe_columns_tuple ( ) ,
User_ ::safe_columns_tuple ( ) ,
category ::all_columns ,
community_aggregates ::all_columns ,
community_follower ::all_columns . nullable ( ) ,
) )
. into_boxed ( ) ;
2020-12-06 03:49:15 +00:00
if let Some ( search_term ) = self . search_term {
let searcher = fuzzy_search ( & search_term ) ;
query = query
. filter ( community ::name . ilike ( searcher . to_owned ( ) ) )
. or_filter ( community ::title . ilike ( searcher . to_owned ( ) ) )
. or_filter ( community ::description . ilike ( searcher ) ) ;
} ;
match self . sort {
SortType ::New = > query = query . order_by ( community ::published . desc ( ) ) ,
SortType ::TopAll = > query = query . order_by ( community_aggregates ::subscribers . desc ( ) ) ,
// Covers all other sorts, including hot
_ = > {
query = query
2021-01-06 04:42:48 +00:00
. order_by (
hot_rank (
community_aggregates ::subscribers ,
community_aggregates ::published ,
)
. desc ( ) ,
)
. then_order_by ( community_aggregates ::published . desc ( ) )
2020-12-06 03:49:15 +00:00
}
} ;
if ! self . show_nsfw {
query = query . filter ( community ::nsfw . eq ( false ) ) ;
} ;
2021-01-26 17:18:01 +00:00
query = match self . listing_type {
ListingType ::Subscribed = > query . filter ( community_follower ::user_id . is_not_null ( ) ) , // TODO could be this: and(community_follower::user_id.eq(user_id_join)),
ListingType ::Local = > query . filter ( community ::local . eq ( true ) ) ,
_ = > query ,
} ;
2020-12-06 03:49:15 +00:00
let ( limit , offset ) = limit_and_offset ( self . page , self . limit ) ;
let res = query
. limit ( limit )
. offset ( offset )
. filter ( community ::removed . eq ( false ) )
. filter ( community ::deleted . eq ( false ) )
2020-12-11 01:39:42 +00:00
. load ::< CommunityViewTuple > ( self . conn ) ? ;
2020-12-06 03:49:15 +00:00
2020-12-23 21:56:20 +00:00
Ok ( CommunityView ::from_tuple_to_vec ( res ) )
2020-12-06 03:49:15 +00:00
}
}
2020-12-11 01:39:42 +00:00
impl ViewToVec for CommunityView {
type DbTuple = CommunityViewTuple ;
2020-12-23 21:56:20 +00:00
fn from_tuple_to_vec ( items : Vec < Self ::DbTuple > ) -> Vec < Self > {
items
2020-12-11 01:39:42 +00:00
. iter ( )
. map ( | a | Self {
community : a . 0. to_owned ( ) ,
creator : a . 1. to_owned ( ) ,
category : a . 2. to_owned ( ) ,
counts : a . 3. to_owned ( ) ,
subscribed : a . 4. is_some ( ) ,
} )
. collect ::< Vec < Self > > ( )
}
2020-12-06 03:49:15 +00:00
}