2022-05-03 17:44:13 +00:00
use crate ::structs ::{ CommunityModeratorView , CommunityView , PersonViewSafe } ;
2020-12-21 16:30:34 +00:00
use diesel ::{ result ::Error , * } ;
2021-10-16 13:33:38 +00:00
use lemmy_db_schema ::{
2022-05-03 17:44:13 +00:00
aggregates ::structs ::CommunityAggregates ,
2021-10-16 13:33:38 +00:00
newtypes ::{ CommunityId , PersonId } ,
2022-03-02 17:39:27 +00:00
schema ::{ community , community_aggregates , community_block , community_follower , local_user } ,
2021-08-19 20:54:15 +00:00
source ::{
community ::{ Community , CommunityFollower , CommunitySafe } ,
community_block ::CommunityBlock ,
2022-08-19 14:27:39 +00:00
local_user ::LocalUser ,
2021-08-19 20:54:15 +00:00
} ,
2022-08-04 19:30:17 +00:00
traits ::{ ToSafe , ViewToVec } ,
2022-05-06 20:55:07 +00:00
utils ::{ functions ::hot_rank , fuzzy_search , limit_and_offset } ,
ListingType ,
SortType ,
2020-12-18 16:17:21 +00:00
} ;
2022-08-04 19:30:17 +00:00
use typed_builder ::TypedBuilder ;
2020-12-04 16:29:44 +00:00
2020-12-11 01:39:42 +00:00
type CommunityViewTuple = (
CommunitySafe ,
CommunityAggregates ,
Option < CommunityFollower > ,
2021-08-19 20:54:15 +00:00
Option < CommunityBlock > ,
2020-12-11 01:39:42 +00:00
) ;
2020-12-04 16:29:44 +00:00
impl CommunityView {
pub fn read (
2022-09-26 14:09:32 +00:00
conn : & mut PgConnection ,
2021-03-18 20:25:21 +00:00
community_id : CommunityId ,
my_person_id : Option < PersonId > ,
2020-12-04 16:29:44 +00:00
) -> Result < Self , Error > {
2020-12-06 03:49:15 +00:00
// The left join below will return None in this case
2021-03-18 20:25:21 +00:00
let person_id_join = my_person_id . unwrap_or ( PersonId ( - 1 ) ) ;
2020-12-04 16:29:44 +00:00
2021-08-19 20:54:15 +00:00
let ( community , counts , follower , blocked ) = community ::table
2020-12-04 16:29:44 +00:00
. find ( community_id )
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 )
2021-03-10 22:33:55 +00:00
. and ( community_follower ::person_id . eq ( person_id_join ) ) ,
2020-12-06 03:49:15 +00:00
) ,
)
2021-08-19 20:54:15 +00:00
. left_join (
community_block ::table . on (
community ::id
. eq ( community_block ::community_id )
. and ( community_block ::person_id . eq ( person_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
community_aggregates ::all_columns ,
2020-12-06 03:49:15 +00:00
community_follower ::all_columns . nullable ( ) ,
2021-08-19 20:54:15 +00:00
community_block ::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 ,
2022-06-22 12:05:41 +00:00
subscribed : CommunityFollower ::to_subscribed_type ( & follower ) ,
2021-08-19 20:54:15 +00:00
blocked : blocked . 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
2021-03-18 20:25:21 +00:00
pub fn is_mod_or_admin (
2022-09-26 14:09:32 +00:00
conn : & mut PgConnection ,
2021-03-18 20:25:21 +00:00
person_id : PersonId ,
community_id : CommunityId ,
) -> bool {
2022-04-28 20:32:32 +00:00
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 > > ( )
} )
2020-12-21 16:30:34 +00:00
. unwrap_or_default ( )
2021-03-10 22:33:55 +00:00
. contains ( & person_id )
2020-12-21 16:30:34 +00:00
}
2020-12-04 16:29:44 +00:00
}
2020-12-06 03:49:15 +00:00
2022-08-04 19:30:17 +00:00
#[ derive(TypedBuilder) ]
#[ builder(field_defaults(default)) ]
pub struct CommunityQuery < ' a > {
#[ builder(!default) ]
2022-09-26 14:09:32 +00:00
conn : & ' a mut PgConnection ,
2021-04-15 03:37:51 +00:00
listing_type : Option < ListingType > ,
sort : Option < SortType > ,
2022-08-19 14:27:39 +00:00
local_user : Option < & ' a LocalUser > ,
2020-12-06 03:49:15 +00:00
search_term : Option < String > ,
page : Option < i64 > ,
limit : Option < i64 > ,
}
2022-08-04 19:30:17 +00:00
impl < ' a > CommunityQuery < ' a > {
2020-12-06 03:49:15 +00:00
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
2022-08-19 14:27:39 +00:00
let person_id_join = self . local_user . map ( | l | l . person_id ) . unwrap_or ( PersonId ( - 1 ) ) ;
2020-12-16 18:59:43 +00:00
let mut query = community ::table
. inner_join ( community_aggregates ::table )
2022-03-02 17:39:27 +00:00
. left_join ( local_user ::table . on ( local_user ::person_id . eq ( person_id_join ) ) )
2020-12-16 18:59:43 +00:00
. left_join (
community_follower ::table . on (
community ::id
. eq ( community_follower ::community_id )
2021-03-10 22:33:55 +00:00
. and ( community_follower ::person_id . eq ( person_id_join ) ) ,
2020-12-16 18:59:43 +00:00
) ,
)
2021-08-19 20:54:15 +00:00
. left_join (
community_block ::table . on (
community ::id
. eq ( community_block ::community_id )
. and ( community_block ::person_id . eq ( person_id_join ) ) ,
) ,
)
2020-12-16 18:59:43 +00:00
. select ( (
Community ::safe_columns_tuple ( ) ,
community_aggregates ::all_columns ,
community_follower ::all_columns . nullable ( ) ,
2021-08-19 20:54:15 +00:00
community_block ::all_columns . nullable ( ) ,
2020-12-16 18:59:43 +00:00
) )
. 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 ( ) ) )
2022-09-07 08:58:41 +00:00
. or_filter ( community ::title . ilike ( searcher ) ) ;
2020-12-06 03:49:15 +00:00
} ;
2021-04-15 03:37:51 +00:00
match self . sort . unwrap_or ( SortType ::Hot ) {
2020-12-06 03:49:15 +00:00
SortType ::New = > query = query . order_by ( community ::published . desc ( ) ) ,
SortType ::TopAll = > query = query . order_by ( community_aggregates ::subscribers . desc ( ) ) ,
2021-04-01 18:34:27 +00:00
SortType ::TopMonth = > query = query . order_by ( community_aggregates ::users_active_month . desc ( ) ) ,
2022-02-18 02:30:47 +00:00
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
2020-12-06 03:49:15 +00:00
_ = > {
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
}
} ;
2021-04-15 03:37:51 +00:00
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 ,
} ;
}
2021-01-26 17:18:01 +00:00
2022-03-02 17:39:27 +00:00
// Don't show blocked communities or nsfw communities if not enabled in profile
2022-08-19 14:27:39 +00:00
if self . local_user . is_some ( ) {
2021-08-19 20:54:15 +00:00
query = query . filter ( community_block ::person_id . is_null ( ) ) ;
2022-03-02 17:39:27 +00:00
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
2022-08-19 14:27:39 +00:00
if ! self . local_user . map ( | l | l . show_nsfw ) . unwrap_or ( false ) {
2022-03-02 17:39:27 +00:00
query = query . filter ( community ::nsfw . eq ( false ) ) ;
}
2021-08-19 20:54:15 +00:00
}
2022-07-08 10:21:33 +00:00
let ( limit , offset ) = limit_and_offset ( self . page , self . limit ) ? ;
2020-12-06 03:49:15 +00:00
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
2022-08-04 19:30:17 +00:00
. into_iter ( )
2020-12-11 01:39:42 +00:00
. map ( | a | Self {
2022-08-04 19:30:17 +00:00
community : a . 0 ,
counts : a . 1 ,
2022-06-22 12:05:41 +00:00
subscribed : CommunityFollower ::to_subscribed_type ( & a . 2 ) ,
2021-08-19 20:54:15 +00:00
blocked : a . 3. is_some ( ) ,
2020-12-11 01:39:42 +00:00
} )
. collect ::< Vec < Self > > ( )
}
2020-12-06 03:49:15 +00:00
}