use diesel::result::Error;
use diesel::dsl::*;
use serde::{Deserialize, Serialize};
-use { SortType };
+use { SortType, limit_and_offset };
// The faked schema since diesel doesn't do views
table! {
for_post_id: Option<i32>,
for_creator_id: Option<i32>,
my_user_id: Option<i32>,
- limit: i64) -> Result<Vec<Self>, Error> {
+ page: Option<i64>,
+ limit: Option<i64>,
+ ) -> Result<Vec<Self>, Error> {
use actions::comment_view::comment_view::dsl::*;
- let mut query = comment_view.limit(limit).into_boxed();
+ let (limit, offset) = limit_and_offset(page, limit);
+
+ // TODO no limits here?
+ let mut query = comment_view.into_boxed();
// The view lets you pass a null user_id, if you're not logged in
if let Some(my_user_id) = my_user_id {
_ => query.order_by(published.desc())
};
- query.load::<Self>(conn)
+ query
+ .limit(limit)
+ .offset(offset)
+ .load::<Self>(conn)
}
pub fn read(conn: &PgConnection, from_comment_id: i32, my_user_id: Option<i32>) -> Result<Self, Error> {
am_mod: None,
};
- let read_comment_views_no_user = CommentView::list(&conn, &SortType::New, Some(inserted_post.id), None, None, 999).unwrap();
- let read_comment_views_with_user = CommentView::list(&conn, &SortType::New, Some(inserted_post.id), None, Some(inserted_user.id), 999).unwrap();
+ let read_comment_views_no_user = CommentView::list(&conn, &SortType::New, Some(inserted_post.id), None, None, None, None).unwrap();
+ let read_comment_views_with_user = CommentView::list(&conn, &SortType::New, Some(inserted_post.id), None, Some(inserted_user.id), None, None).unwrap();
let like_removed = CommentLike::remove(&conn, &comment_like_form).unwrap();
let num_deleted = Comment::delete(&conn, inserted_comment.id).unwrap();
Post::delete(&conn, inserted_post.id).unwrap();
use diesel::*;
use diesel::result::Error;
use serde::{Deserialize, Serialize};
-use {SortType};
+use {SortType, limit_and_offset};
table! {
community_view (id) {
query.first::<Self>(conn)
}
- pub fn list(conn: &PgConnection, from_user_id: Option<i32>, sort: SortType, limit: Option<i64>) -> Result<Vec<Self>, Error> {
+ pub fn list(conn: &PgConnection,
+ from_user_id: Option<i32>,
+ sort: SortType,
+ page: Option<i64>,
+ limit: Option<i64>,
+ ) -> Result<Vec<Self>, Error> {
use actions::community_view::community_view::dsl::*;
let mut query = community_view.into_boxed();
-
+ let (limit, offset) = limit_and_offset(page, limit);
// The view lets you pass a null user_id, if you're not logged in
-
match sort {
SortType::New => query = query.order_by(published.desc()).filter(user_id.is_null()),
SortType::TopAll => {
_ => ()
};
- if let Some(limit) = limit {
- query = query.limit(limit);
- };
-
- query.filter(removed.eq(false)).load::<Self>(conn)
+ query
+ .limit(limit)
+ .offset(offset)
+ .filter(removed.eq(false))
+ .load::<Self>(conn)
}
}
use diesel::*;
use diesel::result::Error;
use serde::{Deserialize, Serialize};
+use {limit_and_offset};
table! {
mod_remove_post_view (id) {
pub fn list(conn: &PgConnection,
from_community_id: Option<i32>,
from_mod_user_id: Option<i32>,
+ page: Option<i64>,
limit: Option<i64>,
- page: Option<i64>) -> Result<Vec<Self>, Error> {
+ ) -> Result<Vec<Self>, Error> {
use actions::moderator_views::mod_remove_post_view::dsl::*;
let mut query = mod_remove_post_view.into_boxed();
- let page = page.unwrap_or(1);
- let limit = limit.unwrap_or(10);
- let offset = limit * (page - 1);
+ let (limit, offset) = limit_and_offset(page, limit);
if let Some(from_community_id) = from_community_id {
query = query.filter(community_id.eq(from_community_id));
pub fn list(conn: &PgConnection,
from_community_id: Option<i32>,
from_mod_user_id: Option<i32>,
+ page: Option<i64>,
limit: Option<i64>,
- page: Option<i64>) -> Result<Vec<Self>, Error> {
+ ) -> Result<Vec<Self>, Error> {
use actions::moderator_views::mod_lock_post_view::dsl::*;
let mut query = mod_lock_post_view.into_boxed();
- let page = page.unwrap_or(1);
- let limit = limit.unwrap_or(10);
- let offset = limit * (page - 1);
+ let (limit, offset) = limit_and_offset(page, limit);
if let Some(from_community_id) = from_community_id {
query = query.filter(community_id.eq(from_community_id));
pub fn list(conn: &PgConnection,
from_community_id: Option<i32>,
from_mod_user_id: Option<i32>,
+ page: Option<i64>,
limit: Option<i64>,
- page: Option<i64>) -> Result<Vec<Self>, Error> {
+ ) -> Result<Vec<Self>, Error> {
use actions::moderator_views::mod_remove_comment_view::dsl::*;
let mut query = mod_remove_comment_view.into_boxed();
- let page = page.unwrap_or(1);
- let limit = limit.unwrap_or(10);
- let offset = limit * (page - 1);
+ let (limit, offset) = limit_and_offset(page, limit);
if let Some(from_community_id) = from_community_id {
query = query.filter(community_id.eq(from_community_id));
impl ModRemoveCommunityView {
pub fn list(conn: &PgConnection,
from_mod_user_id: Option<i32>,
+ page: Option<i64>,
limit: Option<i64>,
- page: Option<i64>) -> Result<Vec<Self>, Error> {
+ ) -> Result<Vec<Self>, Error> {
use actions::moderator_views::mod_remove_community_view::dsl::*;
let mut query = mod_remove_community_view.into_boxed();
- let page = page.unwrap_or(1);
- let limit = limit.unwrap_or(10);
- let offset = limit * (page - 1);
+ let (limit, offset) = limit_and_offset(page, limit);
if let Some(from_mod_user_id) = from_mod_user_id {
query = query.filter(mod_user_id.eq(from_mod_user_id));
pub fn list(conn: &PgConnection,
from_community_id: Option<i32>,
from_mod_user_id: Option<i32>,
+ page: Option<i64>,
limit: Option<i64>,
- page: Option<i64>) -> Result<Vec<Self>, Error> {
+ ) -> Result<Vec<Self>, Error> {
use actions::moderator_views::mod_ban_from_community_view::dsl::*;
let mut query = mod_ban_from_community_view.into_boxed();
- let page = page.unwrap_or(1);
- let limit = limit.unwrap_or(10);
- let offset = limit * (page - 1);
+ let (limit, offset) = limit_and_offset(page, limit);
if let Some(from_community_id) = from_community_id {
query = query.filter(community_id.eq(from_community_id));
impl ModBanView {
pub fn list(conn: &PgConnection,
from_mod_user_id: Option<i32>,
+ page: Option<i64>,
limit: Option<i64>,
- page: Option<i64>) -> Result<Vec<Self>, Error> {
+ ) -> Result<Vec<Self>, Error> {
use actions::moderator_views::mod_ban_view::dsl::*;
let mut query = mod_ban_view.into_boxed();
- let page = page.unwrap_or(1);
- let limit = limit.unwrap_or(10);
- let offset = limit * (page - 1);
+ let (limit, offset) = limit_and_offset(page, limit);
if let Some(from_mod_user_id) = from_mod_user_id {
query = query.filter(mod_user_id.eq(from_mod_user_id));
pub fn list(conn: &PgConnection,
from_community_id: Option<i32>,
from_mod_user_id: Option<i32>,
+ page: Option<i64>,
limit: Option<i64>,
- page: Option<i64>) -> Result<Vec<Self>, Error> {
+ ) -> Result<Vec<Self>, Error> {
use actions::moderator_views::mod_add_community_view::dsl::*;
let mut query = mod_add_community_view.into_boxed();
- let page = page.unwrap_or(1);
- let limit = limit.unwrap_or(10);
- let offset = limit * (page - 1);
+ let (limit, offset) = limit_and_offset(page, limit);
if let Some(from_community_id) = from_community_id {
query = query.filter(community_id.eq(from_community_id));
impl ModAddView {
pub fn list(conn: &PgConnection,
from_mod_user_id: Option<i32>,
+ page: Option<i64>,
limit: Option<i64>,
- page: Option<i64>) -> Result<Vec<Self>, Error> {
+ ) -> Result<Vec<Self>, Error> {
use actions::moderator_views::mod_add_view::dsl::*;
let mut query = mod_add_view.into_boxed();
- let page = page.unwrap_or(1);
- let limit = limit.unwrap_or(10);
- let offset = limit * (page - 1);
+ let (limit, offset) = limit_and_offset(page, limit);
if let Some(from_mod_user_id) = from_mod_user_id {
query = query.filter(mod_user_id.eq(from_mod_user_id));
use diesel::result::Error;
use diesel::dsl::*;
use serde::{Deserialize, Serialize};
-use { SortType };
+use { SortType, limit_and_offset };
#[derive(EnumString,ToString,Debug, Serialize, Deserialize)]
pub enum PostListingType {
for_community_id: Option<i32>,
for_creator_id: Option<i32>,
my_user_id: Option<i32>,
- limit: i64) -> Result<Vec<Self>, Error> {
+ page: Option<i64>,
+ limit: Option<i64>,
+ ) -> Result<Vec<Self>, Error> {
use actions::post_view::post_view::dsl::*;
- let mut query = post_view.limit(limit).into_boxed();
+ let (limit, offset) = limit_and_offset(page, limit);
+
+ let mut query = post_view.into_boxed();
if let Some(for_community_id) = for_community_id {
query = query.filter(community_id.eq(for_community_id));
// TODO make sure community removed isn't fetched either
- query = query.filter(removed.eq(false));
+ query = query
+ .limit(limit)
+ .offset(offset)
+ .filter(removed.eq(false));
query.load::<Self>(conn)
}
};
- let read_post_listings_with_user = PostView::list(&conn, PostListingType::Community, &SortType::New, Some(inserted_community.id), None, Some(inserted_user.id), 10).unwrap();
- let read_post_listings_no_user = PostView::list(&conn, PostListingType::Community, &SortType::New, Some(inserted_community.id), None, None, 10).unwrap();
+ let read_post_listings_with_user = PostView::list(&conn, PostListingType::Community, &SortType::New, Some(inserted_community.id), None, Some(inserted_user.id), None, None).unwrap();
+ let read_post_listings_no_user = PostView::list(&conn, PostListingType::Community, &SortType::New, Some(inserted_community.id), None, None, None, None).unwrap();
let read_post_listing_no_user = PostView::read(&conn, inserted_post.id, None).unwrap();
let read_post_listing_with_user = PostView::read(&conn, inserted_post.id, Some(inserted_user.id)).unwrap();
SLUR_REGEX.is_match(test)
}
+pub fn limit_and_offset(page: Option<i64>, limit: Option<i64>) -> (i64, i64) {
+ let page = page.unwrap_or(1);
+ let limit = limit.unwrap_or(10);
+ let offset = limit * (page - 1);
+ (limit, offset)
+}
+
#[cfg(test)]
mod tests {
use {Settings, is_email_regex, remove_slurs, has_slurs};
#[derive(Serialize, Deserialize)]
pub struct ListCommunities {
sort: String,
+ page: Option<i64>,
limit: Option<i64>,
auth: Option<String>
}
pub struct GetPosts {
type_: String,
sort: String,
- limit: i64,
+ page: Option<i64>,
+ limit: Option<i64>,
community_id: Option<i32>,
auth: Option<String>
}
pub struct GetUserDetails {
user_id: i32,
sort: String,
- limit: i64,
+ page: Option<i64>,
+ limit: Option<i64>,
community_id: Option<i32>,
auth: Option<String>
}
pub struct GetModlog {
mod_user_id: Option<i32>,
community_id: Option<i32>,
- limit: Option<i64>,
page: Option<i64>,
+ limit: Option<i64>,
}
#[derive(Serialize, Deserialize)]
Some(community_id),
None,
None,
- 999).unwrap();
+ None,
+ Some(9999))
+ .unwrap();
for post in posts {
self.send_room_message(post.id, message, skip_id);
}
let sort = SortType::from_str(&self.sort).expect("listing sort");
- let communities: Vec<CommunityView> = CommunityView::list(&conn, user_id, sort, self.limit).unwrap();
+ let communities: Vec<CommunityView> = CommunityView::list(&conn, user_id, sort, self.page, self.limit).unwrap();
// Return the jwt
serde_json::to_string(
chat.rooms.get_mut(&self.id).unwrap().insert(addr);
- let comments = CommentView::list(&conn, &SortType::New, Some(self.id), None, user_id, 999).unwrap();
+ let comments = CommentView::list(&conn, &SortType::New, Some(self.id), None, user_id, None, Some(9999)).unwrap();
let community = CommunityView::read(&conn, post_view.community_id, user_id).unwrap();
let type_ = PostListingType::from_str(&self.type_).expect("listing type");
let sort = SortType::from_str(&self.sort).expect("listing sort");
- let posts = match PostView::list(&conn, type_, &sort, self.community_id, None, user_id, self.limit) {
+ let posts = match PostView::list(&conn, type_, &sort, self.community_id, None, user_id, self.page, self.limit) {
Ok(posts) => posts,
Err(_e) => {
return self.error("Couldn't get posts");
let sort = SortType::from_str(&self.sort).expect("listing sort");
let user_view = UserView::read(&conn, self.user_id).unwrap();
- let posts = PostView::list(&conn, PostListingType::All, &sort, self.community_id, Some(self.user_id), user_id, self.limit).unwrap();
- let comments = CommentView::list(&conn, &sort, None, Some(self.user_id), user_id, self.limit).unwrap();
+ let posts = PostView::list(&conn, PostListingType::All, &sort, self.community_id, Some(self.user_id), user_id, self.page, self.limit).unwrap();
+ let comments = CommentView::list(&conn, &sort, None, Some(self.user_id), user_id, self.page, self.limit).unwrap();
let follows = CommunityFollowerView::for_user(&conn, self.user_id).unwrap();
let moderates = CommunityModeratorView::for_user(&conn, self.user_id).unwrap();
let conn = establish_connection();
- let removed_posts = ModRemovePostView::list(&conn, self.community_id, self.mod_user_id, self.limit, self.page).unwrap();
- let locked_posts = ModLockPostView::list(&conn, self.community_id, self.mod_user_id, self.limit, self.page).unwrap();
- let removed_comments = ModRemoveCommentView::list(&conn, self.community_id, self.mod_user_id, self.limit, self.page).unwrap();
- let banned_from_community = ModBanFromCommunityView::list(&conn, self.community_id, self.mod_user_id, self.limit, self.page).unwrap();
- let added_to_community = ModAddCommunityView::list(&conn, self.community_id, self.mod_user_id, self.limit, self.page).unwrap();
+ let removed_posts = ModRemovePostView::list(&conn, self.community_id, self.mod_user_id, self.page, self.limit).unwrap();
+ let locked_posts = ModLockPostView::list(&conn, self.community_id, self.mod_user_id, self.page, self.limit).unwrap();
+ let removed_comments = ModRemoveCommentView::list(&conn, self.community_id, self.mod_user_id, self.page, self.limit).unwrap();
+ let banned_from_community = ModBanFromCommunityView::list(&conn, self.community_id, self.mod_user_id, self.page, self.limit).unwrap();
+ let added_to_community = ModAddCommunityView::list(&conn, self.community_id, self.mod_user_id, self.page, self.limit).unwrap();
// These arrays are only for the full modlog, when a community isn't given
let mut removed_communities = Vec::new();
import { UserOperation, Community as CommunityI, Post, GetPostsForm, SortType, ListingType, GetPostsResponse, CreatePostLikeResponse, CommunityUser} from '../interfaces';
import { WebSocketService, UserService } from '../services';
import { PostListing } from './post-listing';
-import { msgOp } from '../utils';
-
+import { msgOp, fetchLimit } from '../utils';
interface PostListingsProps {
communityId?: number;
posts: Array<Post>;
sortType: SortType;
type_: ListingType;
+ page: number;
loading: boolean;
}
: UserService.Instance.user
? ListingType.Subscribed
: ListingType.All,
+ page: 1,
loading: true
}
() => console.log('complete')
);
- let getPostsForm: GetPostsForm = {
- type_: ListingType[this.state.type_],
- community_id: this.props.communityId,
- limit: 10,
- sort: SortType[SortType.Hot],
- }
- WebSocketService.Instance.getPosts(getPostsForm);
+ this.refetch();
}
componentWillUnmount() {
{this.state.loading ?
<h4><svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg></h4> :
<div>
- <div>{this.selects()}</div>
+ {this.selects()}
{this.state.posts.length > 0
? this.state.posts.map(post =>
<PostListing post={post} showCommunity={!this.props.communityId}/>)
: <div>No Listings. {!this.props.communityId && <span>Subscribe to some <Link to="/communities">forums</Link>.</span>}</div>
}
+ {this.paginator()}
</div>
}
</div>
}
</div>
)
+ }
+ paginator() {
+ return (
+ <div class="mt-2">
+ {this.state.page > 1 &&
+ <button class="btn btn-sm btn-secondary mr-1" onClick={linkEvent(this, this.prevPage)}>Prev</button>
+ }
+ <button class="btn btn-sm btn-secondary" onClick={linkEvent(this, this.nextPage)}>Next</button>
+ </div>
+ );
+ }
+
+ nextPage(i: PostListings) {
+ i.state.page++;
+ i.setState(i.state);
+ i.refetch();
+ }
+
+ prevPage(i: PostListings) {
+ i.state.page--;
+ i.setState(i.state);
+ i.refetch();
}
handleSortChange(i: PostListings, event: any) {
i.state.sortType = Number(event.target.value);
+ i.state.page = 1;
i.setState(i.state);
+ i.refetch();
+ }
+ refetch() {
let getPostsForm: GetPostsForm = {
- community_id: i.state.community.id,
- limit: 10,
- sort: SortType[i.state.sortType],
+ community_id: this.state.community.id,
+ page: this.state.page,
+ limit: fetchLimit,
+ sort: SortType[this.state.sortType],
type_: ListingType[ListingType.Community]
}
WebSocketService.Instance.getPosts(getPostsForm);
handleTypeChange(i: PostListings, event: any) {
i.state.type_ = Number(event.target.value);
+ i.state.page = 1;
i.setState(i.state);
-
- let getPostsForm: GetPostsForm = {
- limit: 10,
- sort: SortType[i.state.sortType],
- type_: ListingType[i.state.type_]
- }
- WebSocketService.Instance.getPosts(getPostsForm);
+ i.refetch();
}
parseMessage(msg: any) {
import { retryWhen, delay, take } from 'rxjs/operators';
import { UserOperation, Post, Comment, CommunityUser, GetUserDetailsForm, SortType, UserDetailsResponse, UserView } from '../interfaces';
import { WebSocketService } from '../services';
-import { msgOp } from '../utils';
+import { msgOp, fetchLimit } from '../utils';
import { PostListing } from './post-listing';
import { CommentNodes } from './comment-nodes';
import { MomentTime } from './moment-time';
interface UserState {
user: UserView;
+ user_id: number;
follows: Array<CommunityUser>;
moderates: Array<CommunityUser>;
comments: Array<Comment>;
saved?: Array<Post>;
view: View;
sort: SortType;
+ page: number;
}
export class User extends Component<any, UserState> {
number_of_comments: null,
comment_score: null,
},
+ user_id: null,
follows: [],
moderates: [],
comments: [],
posts: [],
view: View.Overview,
- sort: SortType.New
+ sort: SortType.New,
+ page: 1,
}
constructor(props: any, context: any) {
this.state = this.emptyState;
- let userId = Number(this.props.match.params.id);
+ this.state.user_id = Number(this.props.match.params.id);
this.subscription = WebSocketService.Instance.subject
.pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
() => console.log('complete')
);
- let form: GetUserDetailsForm = {
- user_id: userId,
- sort: SortType[this.state.sort],
- limit: 999
- };
- WebSocketService.Instance.getUserDetails(form);
+ this.refetch();
}
componentWillUnmount() {
{this.state.view == View.Posts &&
this.posts()
}
+ {this.paginator()}
</div>
<div class="col-12 col-md-3">
{this.userInfo()}
)
}
- handleSortChange(i: User, event: any) {
- i.state.sort = Number(event.target.value);
+ paginator() {
+ return (
+ <div class="mt-2">
+ {this.state.page > 1 &&
+ <button class="btn btn-sm btn-secondary mr-1" onClick={linkEvent(this, this.prevPage)}>Prev</button>
+ }
+ <button class="btn btn-sm btn-secondary" onClick={linkEvent(this, this.nextPage)}>Next</button>
+ </div>
+ );
+ }
+
+ nextPage(i: User) {
+ i.state.page++;
+ i.setState(i.state);
+ i.refetch();
+ }
+
+ prevPage(i: User) {
+ i.state.page--;
i.setState(i.state);
+ i.refetch();
+ }
+ refetch() {
let form: GetUserDetailsForm = {
- user_id: i.state.user.id,
- sort: SortType[i.state.sort],
- limit: 999
+ user_id: this.state.user_id,
+ sort: SortType[this.state.sort],
+ page: this.state.page,
+ limit: fetchLimit,
};
WebSocketService.Instance.getUserDetails(form);
}
+ handleSortChange(i: User, event: any) {
+ i.state.sort = Number(event.target.value);
+ i.state.page = 1;
+ i.setState(i.state);
+ i.refetch();
+ }
+
handleViewChange(i: User, event: any) {
i.state.view = Number(event.target.value);
+ i.state.page = 1;
i.setState(i.state);
+ i.refetch();
}
parseMessage(msg: any) {
export interface GetUserDetailsForm {
user_id: number;
sort: string; // TODO figure this one out
- limit: number;
+ page?: number;
+ limit?: number;
community_id?: number;
auth?: string;
}
export interface GetModlogForm {
mod_user_id?: number;
community_id?: number;
- limit?: number;
page?: number;
+ limit?: number;
}
export interface GetModlogResponse {
export interface ListCommunitiesForm {
sort: string;
+ page?: number;
limit?: number;
auth?: string;
}
op: string;
categories: Array<Category>;
}
+
export interface PostForm {
name: string;
url?: string;
export interface GetPostsForm {
type_: string;
sort: string;
- limit: number;
+ page?: number;
+ limit?: number;
community_id?: number;
auth?: string;
}
export function addTypeInfo<T>(arr: Array<T>, name: string): Array<{type_: string, data: T}> {
return arr.map(e => {return {type_: name, data: e}});
}
+
+export let fetchLimit: number = 20;