Getting community moderators

- Getting back mods with a fetch. Fixes #28
This commit is contained in:
Dessalines 2019-04-04 13:53:32 -07:00
parent d07a1b4563
commit f3cbe9e6ce
8 changed files with 105 additions and 13 deletions

View file

@ -9,10 +9,12 @@ from community c;
create view community_moderator_view as create view community_moderator_view as
select *, select *,
(select name from user_ u where cm.user_id = u.id) as user_name (select name from user_ u where cm.user_id = u.id) as user_name,
(select name from community c where cm.community_id = c.id) as community_name
from community_moderator cm; from community_moderator cm;
create view community_follower_view as create view community_follower_view as
select *, select *,
(select name from user_ u where cf.user_id = u.id) as user_name (select name from user_ u where cf.user_id = u.id) as user_name,
(select name from community c where cf.community_id = c.id) as community_name
from community_follower cf; from community_follower cf;

View file

@ -21,6 +21,28 @@ table! {
} }
} }
table! {
community_moderator_view (id) {
id -> Int4,
community_id -> Int4,
user_id -> Int4,
published -> Timestamp,
user_name -> Varchar,
community_name -> Varchar,
}
}
table! {
community_follower_view (id) {
id -> Int4,
community_id -> Int4,
user_id -> Int4,
published -> Timestamp,
user_name -> Varchar,
community_name -> Varchar,
}
}
#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize,QueryableByName,Clone)] #[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize,QueryableByName,Clone)]
#[table_name="community_view"] #[table_name="community_view"]
pub struct CommunityView { pub struct CommunityView {
@ -51,3 +73,27 @@ impl CommunityView {
} }
} }
#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize,QueryableByName,Clone)]
#[table_name="community_moderator_view"]
pub struct CommunityModeratorView {
pub id: i32,
pub community_id: i32,
pub user_id: i32,
pub published: chrono::NaiveDateTime,
pub user_name : String,
pub community_name: String,
}
impl CommunityModeratorView {
pub fn for_community(conn: &PgConnection, from_community_id: i32) -> Result<Vec<Self>, Error> {
use actions::community_view::community_moderator_view::dsl::*;
community_moderator_view.filter(community_id.eq(from_community_id)).load::<Self>(conn)
}
pub fn for_user(conn: &PgConnection, from_user_id: i32) -> Result<Vec<Self>, Error> {
use actions::community_view::community_moderator_view::dsl::*;
community_moderator_view.filter(user_id.eq(from_user_id)).load::<Self>(conn)
}
}

View file

@ -153,7 +153,8 @@ pub struct GetPostResponse {
op: String, op: String,
post: PostView, post: PostView,
comments: Vec<CommentView>, comments: Vec<CommentView>,
community: CommunityView community: CommunityView,
moderators: Vec<CommunityModeratorView>
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
@ -179,7 +180,8 @@ pub struct GetCommunity {
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct GetCommunityResponse { pub struct GetCommunityResponse {
op: String, op: String,
community: CommunityView community: CommunityView,
moderators: Vec<CommunityModeratorView>
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
@ -762,13 +764,16 @@ impl Perform for GetPost {
let community = CommunityView::read(&conn, post_view.community_id).unwrap(); let community = CommunityView::read(&conn, post_view.community_id).unwrap();
let moderators = CommunityModeratorView::for_community(&conn, post_view.community_id).unwrap();
// Return the jwt // Return the jwt
serde_json::to_string( serde_json::to_string(
&GetPostResponse { &GetPostResponse {
op: self.op_type().to_string(), op: self.op_type().to_string(),
post: post_view, post: post_view,
comments: comments, comments: comments,
community: community community: community,
moderators: moderators
} }
) )
.unwrap() .unwrap()
@ -791,11 +796,20 @@ impl Perform for GetCommunity {
} }
}; };
let moderators = match CommunityModeratorView::for_community(&conn, self.id) {
Ok(moderators) => moderators,
Err(_e) => {
return self.error("Couldn't find Community");
}
};
// Return the jwt // Return the jwt
serde_json::to_string( serde_json::to_string(
&GetCommunityResponse { &GetCommunityResponse {
op: self.op_type().to_string(), op: self.op_type().to_string(),
community: community_view community: community_view,
moderators: moderators
} }
) )
.unwrap() .unwrap()

View file

@ -32,6 +32,7 @@ export class Communities extends Component<any, CommunitiesState> {
render() { render() {
return ( return (
<div class="container-fluid"> <div class="container-fluid">
<h4>Communities</h4>
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-sm table-hover" data-sortable> <table class="table table-sm table-hover" data-sortable>
<thead> <thead>

View file

@ -2,7 +2,7 @@ import { Component, linkEvent } from 'inferno';
import { Link } from 'inferno-router'; import { Link } from 'inferno-router';
import { Subscription } from "rxjs"; import { Subscription } from "rxjs";
import { retryWhen, delay, take } from 'rxjs/operators'; import { retryWhen, delay, take } from 'rxjs/operators';
import { UserOperation, Community as CommunityI, CommunityResponse, Post, GetPostsForm, ListingSortType, ListingType, GetPostsResponse, CreatePostLikeForm, CreatePostLikeResponse} from '../interfaces'; import { UserOperation, Community as CommunityI, CommunityResponse, Post, GetPostsForm, ListingSortType, ListingType, GetPostsResponse, CreatePostLikeForm, CreatePostLikeResponse, CommunityUser} from '../interfaces';
import { WebSocketService, UserService } from '../services'; import { WebSocketService, UserService } from '../services';
import { MomentTime } from './moment-time'; import { MomentTime } from './moment-time';
import { PostListing } from './post-listing'; import { PostListing } from './post-listing';
@ -11,6 +11,7 @@ import { msgOp, mdToHtml } from '../utils';
interface State { interface State {
community: CommunityI; community: CommunityI;
moderators: Array<CommunityUser>;
posts: Array<Post>; posts: Array<Post>;
sortType: ListingSortType; sortType: ListingSortType;
} }
@ -29,8 +30,10 @@ export class Community extends Component<any, State> {
creator_name: null, creator_name: null,
number_of_subscribers: null, number_of_subscribers: null,
number_of_posts: null, number_of_posts: null,
number_of_comments: null,
published: null published: null
}, },
moderators: [],
posts: [], posts: [],
sortType: ListingSortType.Hot, sortType: ListingSortType.Hot,
} }
@ -78,7 +81,7 @@ export class Community extends Component<any, State> {
} }
</div> </div>
<div class="col-12 col-sm-2 col-lg-3"> <div class="col-12 col-sm-2 col-lg-3">
<Sidebar community={this.state.community} /> <Sidebar community={this.state.community} moderators={this.state.moderators} />
</div> </div>
</div> </div>
</div> </div>
@ -126,6 +129,7 @@ export class Community extends Component<any, State> {
} else if (op == UserOperation.GetCommunity) { } else if (op == UserOperation.GetCommunity) {
let res: CommunityResponse = msg; let res: CommunityResponse = msg;
this.state.community = res.community; this.state.community = res.community;
this.state.moderators = res.moderators;
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.GetPosts) { } else if (op == UserOperation.GetPosts) {
let res: GetPostsResponse = msg; let res: GetPostsResponse = msg;

View file

@ -2,7 +2,7 @@ import { Component, linkEvent } from 'inferno';
import { Link } from 'inferno-router'; import { Link } from 'inferno-router';
import { Subscription } from "rxjs"; import { Subscription } from "rxjs";
import { retryWhen, delay, take } from 'rxjs/operators'; import { retryWhen, delay, take } from 'rxjs/operators';
import { UserOperation, Community, Post as PostI, GetPostResponse, PostResponse, Comment, CommentForm as CommentFormI, CommentResponse, CommentLikeForm, CommentSortType, CreatePostLikeResponse } from '../interfaces'; import { UserOperation, Community, Post as PostI, GetPostResponse, PostResponse, Comment, CommentForm as CommentFormI, CommentResponse, CommentLikeForm, CommentSortType, CreatePostLikeResponse, CommunityUser } from '../interfaces';
import { WebSocketService, UserService } from '../services'; import { WebSocketService, UserService } from '../services';
import { msgOp, hotRank,mdToHtml } from '../utils'; import { msgOp, hotRank,mdToHtml } from '../utils';
import { MomentTime } from './moment-time'; import { MomentTime } from './moment-time';
@ -20,6 +20,7 @@ interface PostState {
comments: Array<Comment>; comments: Array<Comment>;
commentSort: CommentSortType; commentSort: CommentSortType;
community: Community; community: Community;
moderators: Array<CommunityUser>;
} }
export class Post extends Component<any, PostState> { export class Post extends Component<any, PostState> {
@ -30,6 +31,7 @@ export class Post extends Component<any, PostState> {
comments: [], comments: [],
commentSort: CommentSortType.Hot, commentSort: CommentSortType.Hot,
community: null, community: null,
moderators: []
} }
constructor(props, context) { constructor(props, context) {
@ -118,7 +120,7 @@ export class Post extends Component<any, PostState> {
sidebar() { sidebar() {
return ( return (
<div class="sticky-top"> <div class="sticky-top">
<Sidebar community={this.state.community} /> <Sidebar community={this.state.community} moderators={this.state.moderators} />
</div> </div>
); );
} }
@ -188,6 +190,7 @@ export class Post extends Component<any, PostState> {
this.state.post = res.post; this.state.post = res.post;
this.state.comments = res.comments; this.state.comments = res.comments;
this.state.community = res.community; this.state.community = res.community;
this.state.moderators = res.moderators;
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.CreateComment) { } else if (op == UserOperation.CreateComment) {
let res: CommentResponse = msg; let res: CommentResponse = msg;

View file

@ -1,9 +1,11 @@
import { Component, linkEvent } from 'inferno'; import { Component, linkEvent } from 'inferno';
import { Community } from '../interfaces'; import { Link } from 'inferno-router';
import { Community, CommunityUser } from '../interfaces';
import { mdToHtml } from '../utils'; import { mdToHtml } from '../utils';
interface SidebarProps { interface SidebarProps {
community: Community; community: Community;
moderators: Array<CommunityUser>;
} }
interface SidebarState { interface SidebarState {
@ -22,14 +24,23 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
<div> <div>
<h4>{community.title}</h4> <h4>{community.title}</h4>
<ul class="list-inline"> <ul class="list-inline">
<li className="list-inline-item badge badge-light">{community.category_name}</li> <li className="list-inline-item"><Link className="badge badge-light" to="/communities">{community.category_name}</Link></li>
<li className="list-inline-item badge badge-light">{community.number_of_subscribers} Subscribers</li> <li className="list-inline-item badge badge-light">{community.number_of_subscribers} Subscribers</li>
<li className="list-inline-item badge badge-light">{community.number_of_posts} Posts</li> <li className="list-inline-item badge badge-light">{community.number_of_posts} Posts</li>
<li className="list-inline-item badge badge-light">{community.number_of_comments} Comments</li> <li className="list-inline-item badge badge-light">{community.number_of_comments} Comments</li>
</ul> </ul>
<div><button type="button" class="btn btn-secondary mb-2">Subscribe</button></div> <div><button type="button" class="btn btn-secondary mb-2">Subscribe</button></div>
{community.description &&
<div>
<hr /> <hr />
{community.description && <div className="md-div" dangerouslySetInnerHTML={mdToHtml(community.description)} />} <div className="md-div" dangerouslySetInnerHTML={mdToHtml(community.description)} />
</div>
}
<hr />
<h5>Moderators</h5>
{this.props.moderators.map(mod =>
<Link to={`/user/${mod.user_id}`}>{mod.user_name}</Link>
)}
</div> </div>
); );
} }

View file

@ -8,6 +8,15 @@ export interface User {
username: string; username: string;
} }
export interface CommunityUser {
id: number;
user_id: number;
user_name: string;
community_id: number;
community_name: string;
published: string;
}
export interface Community { export interface Community {
id: number; id: number;
name: string; name: string;
@ -35,6 +44,7 @@ export interface CommunityForm {
export interface CommunityResponse { export interface CommunityResponse {
op: string; op: string;
community: Community; community: Community;
moderators: Array<CommunityUser>;
} }
export interface ListCommunitiesResponse { export interface ListCommunitiesResponse {
@ -82,6 +92,7 @@ export interface GetPostResponse {
post: Post; post: Post;
comments: Array<Comment>; comments: Array<Comment>;
community: Community; community: Community;
moderators: Array<CommunityUser>;
} }
export interface PostResponse { export interface PostResponse {