Adding forum / community page
- Adding the page. Fixes #17. - Adding number of comments for community. - Add sorting to that table.
This commit is contained in:
parent
7b1fb030b3
commit
5e23f00672
9 changed files with 98 additions and 7 deletions
|
@ -3,7 +3,8 @@ select *,
|
|||
(select name from user_ u where c.creator_id = u.id) as creator_name,
|
||||
(select name from category ct where c.category_id = ct.id) as category_name,
|
||||
(select count(*) from community_follower cf where cf.community_id = c.id) as number_of_subscribers,
|
||||
(select count(*) from post p where p.community_id = c.id) as number_of_posts
|
||||
(select count(*) from post p where p.community_id = c.id) as number_of_posts,
|
||||
(select count(*) from comment co, post p where c.id = p.community_id and p.id = co.post_id) as number_of_comments
|
||||
from community c;
|
||||
|
||||
create view community_moderator_view as
|
||||
|
|
|
@ -17,6 +17,7 @@ table! {
|
|||
category_name -> Varchar,
|
||||
number_of_subscribers -> BigInt,
|
||||
number_of_posts -> BigInt,
|
||||
number_of_comments -> BigInt,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,7 +35,8 @@ pub struct CommunityView {
|
|||
pub creator_name: String,
|
||||
pub category_name: String,
|
||||
pub number_of_subscribers: i64,
|
||||
pub number_of_posts: i64
|
||||
pub number_of_posts: i64,
|
||||
pub number_of_comments: i64
|
||||
}
|
||||
|
||||
impl CommunityView {
|
||||
|
|
80
ui/src/components/communities.tsx
Normal file
80
ui/src/components/communities.tsx
Normal file
|
@ -0,0 +1,80 @@
|
|||
import { Component, linkEvent } from 'inferno';
|
||||
import { Link } from 'inferno-router';
|
||||
import { Subscription } from "rxjs";
|
||||
import { retryWhen, delay, take } from 'rxjs/operators';
|
||||
import { UserOperation, Community, Post as PostI, GetPostResponse, PostResponse, Comment, CommentForm as CommentFormI, CommentResponse, CommentLikeForm, CommentSortType, CreatePostLikeResponse, ListCommunitiesResponse } from '../interfaces';
|
||||
import { WebSocketService, UserService } from '../services';
|
||||
import { msgOp, hotRank,mdToHtml } from '../utils';
|
||||
|
||||
interface CommunitiesState {
|
||||
communities: Array<Community>;
|
||||
}
|
||||
|
||||
export class Communities extends Component<any, CommunitiesState> {
|
||||
private subscription: Subscription;
|
||||
private emptyState: CommunitiesState = {
|
||||
communities: []
|
||||
}
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.state = this.emptyState;
|
||||
this.subscription = WebSocketService.Instance.subject
|
||||
.pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
|
||||
.subscribe(
|
||||
(msg) => this.parseMessage(msg),
|
||||
(err) => console.error(err),
|
||||
() => console.log('complete')
|
||||
);
|
||||
WebSocketService.Instance.listCommunities();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div class="container-fluid">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-hover" data-sortable>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Title</th>
|
||||
<th>Category</th>
|
||||
<th class="text-right">Subscribers</th>
|
||||
<th class="text-right">Posts</th>
|
||||
<th class="text-right">Comments</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{this.state.communities.map(community =>
|
||||
<tr>
|
||||
<td><Link to={`/community/${community.id}`}>{community.name}</Link></td>
|
||||
<td>{community.title}</td>
|
||||
<td>{community.category_name}</td>
|
||||
<td class="text-right">{community.number_of_subscribers}</td>
|
||||
<td class="text-right">{community.number_of_posts}</td>
|
||||
<td class="text-right">{community.number_of_comments}</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
parseMessage(msg: any) {
|
||||
console.log(msg);
|
||||
let op: UserOperation = msgOp(msg);
|
||||
if (msg.error) {
|
||||
alert(msg.error);
|
||||
return;
|
||||
} else if (op == UserOperation.ListCommunities) {
|
||||
let res: ListCommunitiesResponse = msg;
|
||||
this.state.communities = res.communities;
|
||||
this.state.communities.sort((a, b) => b.number_of_subscribers - a.number_of_subscribers);
|
||||
this.setState(this.state);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -64,7 +64,7 @@ export class CreateCommunity extends Component<any, State> {
|
|||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">Name</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" value={this.state.communityForm.name} onInput={linkEvent(this, this.handleCommunityNameChange)} required minLength={3} />
|
||||
<input type="text" class="form-control" value={this.state.communityForm.name} onInput={linkEvent(this, this.handleCommunityNameChange)} required minLength={3} pattern="[a-z0-9_]+" title="lowercase, underscores, and no spaces."/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
|
|
|
@ -37,7 +37,7 @@ export class Navbar extends Component<any, any> {
|
|||
<a class="nav-link" href={repoUrl}>About</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href={repoUrl}>Forums</a>
|
||||
<Link class="nav-link" to="/communities">Forums</Link>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<Link class="nav-link" to="/create_post">Create Post</Link>
|
||||
|
|
|
@ -21,10 +21,13 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
|||
return (
|
||||
<div>
|
||||
<h4>{community.title}</h4>
|
||||
<ul class="list-inline">
|
||||
<li className="list-inline-item badge badge-light">{community.category_name}</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_comments} Comments</li>
|
||||
</ul>
|
||||
<div><button type="button" class="btn btn-secondary mb-2">Subscribe</button></div>
|
||||
<div className="badge badge-light">{community.category_name}</div>
|
||||
<div>{community.number_of_subscribers} Subscribers</div>
|
||||
<div>{community.number_of_posts} Posts</div>
|
||||
<hr />
|
||||
{community.description && <div className="md-div" dangerouslySetInnerHTML={mdToHtml(community.description)} />}
|
||||
</div>
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
<link rel="stylesheet" href="https://bootswatch.com/4/darkly/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/balloon-css/0.5.0/balloon.min.css">
|
||||
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,400i,700,800" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/sortable/0.8.0/css/sortable-theme-minimal.min.css" />
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/sortable/0.8.0/js/sortable.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
|
@ -8,6 +8,7 @@ import { CreatePost } from './components/create-post';
|
|||
import { CreateCommunity } from './components/create-community';
|
||||
import { Post } from './components/post';
|
||||
import { Community } from './components/community';
|
||||
import { Communities } from './components/communities';
|
||||
|
||||
import './main.css';
|
||||
|
||||
|
@ -33,6 +34,7 @@ class Index extends Component<any, any> {
|
|||
<Route path={`/login`} component={Login} />
|
||||
<Route path={`/create_post`} component={CreatePost} />
|
||||
<Route path={`/create_community`} component={CreateCommunity} />
|
||||
<Route path={`/communities`} component={Communities} />
|
||||
<Route path={`/post/:id`} component={Post} />
|
||||
<Route path={`/community/:id`} component={Community} />
|
||||
</Switch>
|
||||
|
|
|
@ -19,6 +19,7 @@ export interface Community {
|
|||
category_name: string;
|
||||
number_of_subscribers: number;
|
||||
number_of_posts: number;
|
||||
number_of_comments: number;
|
||||
published: string;
|
||||
updated?: string;
|
||||
}
|
||||
|
|
Reference in a new issue