Adding trending communities.

- Fixes #29
- Communities fetching now has sort and limit.
This commit is contained in:
Dessalines 2019-04-09 23:19:12 -07:00
parent 42939df040
commit 3ff85b1fdb
8 changed files with 89 additions and 31 deletions

View file

@ -2,6 +2,7 @@ extern crate diesel;
use diesel::*; use diesel::*;
use diesel::result::Error; use diesel::result::Error;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use {SortType};
table! { table! {
community_view (id) { community_view (id) {
@ -83,17 +84,27 @@ impl CommunityView {
query.first::<Self>(conn) query.first::<Self>(conn)
} }
pub fn list_all(conn: &PgConnection, from_user_id: Option<i32>) -> Result<Vec<Self>, Error> { pub fn list(conn: &PgConnection, from_user_id: Option<i32>, sort: SortType, limit: Option<i64>) -> Result<Vec<Self>, Error> {
use actions::community_view::community_view::dsl::*; use actions::community_view::community_view::dsl::*;
let mut query = community_view.into_boxed(); let mut query = community_view.into_boxed();
// The view lets you pass a null user_id, if you're not logged in // The view lets you pass a null user_id, if you're not logged in
if let Some(from_user_id) = from_user_id {
query = query.filter(user_id.eq(from_user_id)) match sort {
.order_by((subscribed.desc(), number_of_subscribers.desc())); SortType::New => query = query.order_by(published.desc()).filter(user_id.is_null()),
} else { SortType::TopAll => {
query = query.filter(user_id.is_null()) match from_user_id {
.order_by(number_of_subscribers.desc()); Some(from_user_id) => query = query.filter(user_id.eq(from_user_id)).order_by((subscribed.desc(), number_of_subscribers.desc())),
None => query = query.order_by(number_of_subscribers.desc()).filter(user_id.is_null())
}
}
_ => ()
};
if let Some(limit) = limit {
query = query.limit(limit);
}; };
query.load::<Self>(conn) query.load::<Self>(conn)

View file

@ -111,6 +111,8 @@ pub struct CommunityResponse {
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct ListCommunities { pub struct ListCommunities {
sort: String,
limit: Option<i64>,
auth: Option<String> auth: Option<String>
} }
@ -675,7 +677,9 @@ impl Perform for ListCommunities {
None => None None => None
}; };
let communities: Vec<CommunityView> = CommunityView::list_all(&conn, user_id).unwrap(); let sort = SortType::from_str(&self.sort).expect("listing sort");
let communities: Vec<CommunityView> = CommunityView::list(&conn, user_id, sort, self.limit).unwrap();
// Return the jwt // Return the jwt
serde_json::to_string( serde_json::to_string(

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, ListCommunitiesResponse, CommunityResponse, FollowCommunityForm } from '../interfaces'; import { UserOperation, Community, ListCommunitiesResponse, CommunityResponse, FollowCommunityForm, ListCommunitiesForm, SortType } from '../interfaces';
import { WebSocketService } from '../services'; import { WebSocketService } from '../services';
import { msgOp } from '../utils'; import { msgOp } from '../utils';
@ -30,7 +30,12 @@ export class Communities extends Component<any, CommunitiesState> {
(err) => console.error(err), (err) => console.error(err),
() => console.log('complete') () => console.log('complete')
); );
WebSocketService.Instance.listCommunities();
let listCommunitiesForm: ListCommunitiesForm = {
sort: SortType[SortType.TopAll]
}
WebSocketService.Instance.listCommunities(listCommunitiesForm);
} }

View file

@ -103,7 +103,7 @@ export class Login extends Component<any, State> {
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-2 col-form-label">Email</label> <label class="col-sm-2 col-form-label">Email</label>
<div class="col-sm-10"> <div class="col-sm-10">
<input type="email" class="form-control" value={this.state.registerForm.email} onInput={linkEvent(this, this.handleRegisterEmailChange)} minLength={3} /> <input type="email" class="form-control" placeholder="Optional" value={this.state.registerForm.email} onInput={linkEvent(this, this.handleRegisterEmailChange)} minLength={3} />
</div> </div>
</div> </div>
<div class="form-group row"> <div class="form-group row">

View file

@ -2,13 +2,14 @@ import { Component } 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, CommunityUser, GetFollowedCommunitiesResponse } from '../interfaces'; import { UserOperation, CommunityUser, GetFollowedCommunitiesResponse, ListCommunitiesForm, ListCommunitiesResponse, Community, SortType } from '../interfaces';
import { WebSocketService, UserService } from '../services'; import { WebSocketService, UserService } from '../services';
import { PostListings } from './post-listings'; import { PostListings } from './post-listings';
import { msgOp, repoUrl } from '../utils'; import { msgOp, repoUrl } from '../utils';
interface State { interface State {
subscribedCommunities: Array<CommunityUser>; subscribedCommunities: Array<CommunityUser>;
trendingCommunities: Array<Community>;
loading: boolean; loading: boolean;
} }
@ -17,6 +18,7 @@ export class Main extends Component<any, State> {
private subscription: Subscription; private subscription: Subscription;
private emptyState: State = { private emptyState: State = {
subscribedCommunities: [], subscribedCommunities: [],
trendingCommunities: [],
loading: true loading: true
} }
@ -36,6 +38,13 @@ export class Main extends Component<any, State> {
if (UserService.Instance.loggedIn) { if (UserService.Instance.loggedIn) {
WebSocketService.Instance.getFollowedCommunities(); WebSocketService.Instance.getFollowedCommunities();
} }
let listCommunitiesForm: ListCommunitiesForm = {
sort: SortType[SortType.New],
limit: 8
}
WebSocketService.Instance.listCommunities(listCommunitiesForm);
} }
componentWillUnmount() { componentWillUnmount() {
@ -50,25 +59,39 @@ export class Main extends Component<any, State> {
<PostListings /> <PostListings />
</div> </div>
<div class="col-12 col-md-4"> <div class="col-12 col-md-4">
{UserService.Instance.loggedIn ?
<div>
{this.state.loading ? {this.state.loading ?
<h4><svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg></h4> : <h4><svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg></h4> :
<div>
{this.trendingCommunities()}
{UserService.Instance.loggedIn ?
<div> <div>
<h4>Subscribed forums</h4> <h4>Subscribed forums</h4>
<ul class="list-unstyled"> <ul class="list-inline">
{this.state.subscribedCommunities.map(community => {this.state.subscribedCommunities.map(community =>
<li><Link to={`/community/${community.community_id}`}>{community.community_name}</Link></li> <li class="list-inline-item"><Link to={`/community/${community.community_id}`}>{community.community_name}</Link></li>
)} )}
</ul> </ul>
</div>
}
</div> : </div> :
this.landing() this.landing()
} }
</div> </div>
}
</div> </div>
</div> </div>
</div>
)
}
trendingCommunities() {
return (
<div>
<h4>Trending forums</h4>
<ul class="list-inline">
{this.state.trendingCommunities.map(community =>
<li class="list-inline-item"><Link to={`/community/${community.id}`}>{community.name}</Link></li>
)}
</ul>
</div>
) )
} }
@ -99,6 +122,11 @@ export class Main extends Component<any, State> {
this.state.subscribedCommunities = res.communities; this.state.subscribedCommunities = res.communities;
this.state.loading = false; this.state.loading = false;
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.ListCommunities) {
let res: ListCommunitiesResponse = msg;
this.state.trendingCommunities = res.communities;
this.state.loading = false;
this.setState(this.state);
} }
} }
} }

View file

@ -1,7 +1,7 @@
import { Component, linkEvent } from 'inferno'; import { Component, linkEvent } from 'inferno';
import { Subscription } from "rxjs"; import { Subscription } from "rxjs";
import { retryWhen, delay, take } from 'rxjs/operators'; import { retryWhen, delay, take } from 'rxjs/operators';
import { PostForm as PostFormI, Post, PostResponse, UserOperation, Community, ListCommunitiesResponse } from '../interfaces'; import { PostForm as PostFormI, Post, PostResponse, UserOperation, Community, ListCommunitiesResponse, ListCommunitiesForm, SortType } from '../interfaces';
import { WebSocketService } from '../services'; import { WebSocketService } from '../services';
import { msgOp } from '../utils'; import { msgOp } from '../utils';
import * as autosize from 'autosize'; import * as autosize from 'autosize';
@ -56,7 +56,11 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
() => console.log('complete') () => console.log('complete')
); );
WebSocketService.Instance.listCommunities(); let listCommunitiesForm: ListCommunitiesForm = {
sort: SortType[SortType.TopAll]
}
WebSocketService.Instance.listCommunities(listCommunitiesForm);
} }
componentDidMount() { componentDidMount() {

View file

@ -67,6 +67,12 @@ export interface CommunityResponse {
community: Community; community: Community;
} }
export interface ListCommunitiesForm {
sort: string;
limit?: number;
auth?: string;
}
export interface ListCommunitiesResponse { export interface ListCommunitiesResponse {
op: string; op: string;
communities: Array<Community>; communities: Array<Community>;

View file

@ -1,5 +1,5 @@
import { wsUri } from '../env'; import { wsUri } from '../env';
import { LoginForm, RegisterForm, UserOperation, CommunityForm, PostForm, CommentForm, CommentLikeForm, GetPostsForm, CreatePostLikeForm, FollowCommunityForm, GetUserDetailsForm } from '../interfaces'; import { LoginForm, RegisterForm, UserOperation, CommunityForm, PostForm, CommentForm, CommentLikeForm, GetPostsForm, CreatePostLikeForm, FollowCommunityForm, GetUserDetailsForm, ListCommunitiesForm } from '../interfaces';
import { webSocket } from 'rxjs/webSocket'; import { webSocket } from 'rxjs/webSocket';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { retryWhen, delay, take } from 'rxjs/operators'; import { retryWhen, delay, take } from 'rxjs/operators';
@ -47,9 +47,9 @@ export class WebSocketService {
this.subject.next(this.wsSendWrapper(UserOperation.FollowCommunity, followCommunityForm)); this.subject.next(this.wsSendWrapper(UserOperation.FollowCommunity, followCommunityForm));
} }
public listCommunities() { public listCommunities(form: ListCommunitiesForm) {
let data = {auth: UserService.Instance.auth }; this.setAuth(form, false);
this.subject.next(this.wsSendWrapper(UserOperation.ListCommunities, data)); this.subject.next(this.wsSendWrapper(UserOperation.ListCommunities, form));
} }
public getFollowedCommunities() { public getFollowedCommunities() {