Allow comment/post upvoting from other pages.

- Fixes #355
- Votes now coming back for posts and comments on search page.
This commit is contained in:
Dessalines 2020-01-20 18:39:45 -05:00
parent 86871d17ac
commit a964b4ce21
6 changed files with 154 additions and 99 deletions

View file

@ -19,6 +19,7 @@ pub struct Search {
sort: String, sort: String,
page: Option<i64>, page: Option<i64>,
limit: Option<i64>, limit: Option<i64>,
auth: Option<String>,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
@ -313,6 +314,17 @@ impl Perform<SearchResponse> for Oper<Search> {
fn perform(&self, conn: &PgConnection) -> Result<SearchResponse, Error> { fn perform(&self, conn: &PgConnection) -> Result<SearchResponse, Error> {
let data: &Search = &self.data; let data: &Search = &self.data;
let user_id: Option<i32> = match &data.auth {
Some(auth) => match Claims::decode(&auth) {
Ok(claims) => {
let user_id = claims.claims.id;
Some(user_id)
}
Err(_e) => None,
},
None => None,
};
let sort = SortType::from_str(&data.sort)?; let sort = SortType::from_str(&data.sort)?;
let type_ = SearchType::from_str(&data.type_)?; let type_ = SearchType::from_str(&data.type_)?;
@ -330,6 +342,7 @@ impl Perform<SearchResponse> for Oper<Search> {
.show_nsfw(true) .show_nsfw(true)
.for_community_id(data.community_id) .for_community_id(data.community_id)
.search_term(data.q.to_owned()) .search_term(data.q.to_owned())
.my_user_id(user_id)
.page(data.page) .page(data.page)
.limit(data.limit) .limit(data.limit)
.list()?; .list()?;
@ -338,6 +351,7 @@ impl Perform<SearchResponse> for Oper<Search> {
comments = CommentQueryBuilder::create(&conn) comments = CommentQueryBuilder::create(&conn)
.sort(&sort) .sort(&sort)
.search_term(data.q.to_owned()) .search_term(data.q.to_owned())
.my_user_id(user_id)
.page(data.page) .page(data.page)
.limit(data.limit) .limit(data.limit)
.list()?; .list()?;
@ -364,6 +378,7 @@ impl Perform<SearchResponse> for Oper<Search> {
.show_nsfw(true) .show_nsfw(true)
.for_community_id(data.community_id) .for_community_id(data.community_id)
.search_term(data.q.to_owned()) .search_term(data.q.to_owned())
.my_user_id(user_id)
.page(data.page) .page(data.page)
.limit(data.limit) .limit(data.limit)
.list()?; .list()?;
@ -371,6 +386,7 @@ impl Perform<SearchResponse> for Oper<Search> {
comments = CommentQueryBuilder::create(&conn) comments = CommentQueryBuilder::create(&conn)
.sort(&sort) .sort(&sort)
.search_term(data.q.to_owned()) .search_term(data.q.to_owned())
.my_user_id(user_id)
.page(data.page) .page(data.page)
.limit(data.limit) .limit(data.limit)
.list()?; .list()?;

View file

@ -52,7 +52,6 @@ interface PostListingProps {
post: Post; post: Post;
showCommunity?: boolean; showCommunity?: boolean;
showBody?: boolean; showBody?: boolean;
viewOnly?: boolean;
moderators?: Array<CommunityUser>; moderators?: Array<CommunityUser>;
admins?: Array<UserView>; admins?: Array<UserView>;
} }
@ -118,10 +117,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
let post = this.props.post; let post = this.props.post;
return ( return (
<div class="listing col-12"> <div class="listing col-12">
<div <div className={`vote-bar mr-2 float-left small text-center`}>
className={`vote-bar mr-2 float-left small text-center ${this.props
.viewOnly && 'no-click'}`}
>
<button <button
disabled={!UserService.Instance.user} disabled={!UserService.Instance.user}
className={`btn p-0 ${ className={`btn p-0 ${

View file

@ -12,6 +12,8 @@ import {
SearchForm, SearchForm,
SearchResponse, SearchResponse,
SearchType, SearchType,
CreatePostLikeResponse,
CommentResponse,
} from '../interfaces'; } from '../interfaces';
import { WebSocketService } from '../services'; import { WebSocketService } from '../services';
import { import {
@ -123,22 +125,18 @@ export class Search extends Component<any, SearchState> {
render() { render() {
return ( return (
<div class="container"> <div class="container">
<div class="row"> <h5>
<div class="col-12"> <T i18nKey="search">#</T>
<h5> </h5>
<T i18nKey="search">#</T> {this.selects()}
</h5> {this.searchForm()}
{this.selects()} {this.state.type_ == SearchType.All && this.all()}
{this.searchForm()} {this.state.type_ == SearchType.Comments && this.comments()}
{this.state.type_ == SearchType.All && this.all()} {this.state.type_ == SearchType.Posts && this.posts()}
{this.state.type_ == SearchType.Comments && this.comments()} {this.state.type_ == SearchType.Communities && this.communities()}
{this.state.type_ == SearchType.Posts && this.posts()} {this.state.type_ == SearchType.Users && this.users()}
{this.state.type_ == SearchType.Communities && this.communities()} {this.noResults()}
{this.state.type_ == SearchType.Users && this.users()} {this.paginator()}
{this.noResults()}
{this.paginator()}
</div>
</div>
</div> </div>
); );
} }
@ -252,54 +250,56 @@ export class Search extends Component<any, SearchState> {
return ( return (
<div> <div>
{combined.map(i => ( {combined.map(i => (
<div> <div class="row">
{i.type_ == 'posts' && ( <div class="col-12">
<PostListing post={i.data as Post} showCommunity viewOnly /> {i.type_ == 'posts' && (
)} <PostListing post={i.data as Post} showCommunity />
{i.type_ == 'comments' && ( )}
<CommentNodes {i.type_ == 'comments' && (
nodes={[{ comment: i.data as Comment }]} <CommentNodes
locked nodes={[{ comment: i.data as Comment }]}
noIndent locked
/> noIndent
)} />
{i.type_ == 'communities' && ( )}
<div> {i.type_ == 'communities' && (
<span> <div>
<Link to={`/c/${(i.data as Community).name}`}>{`/c/${ <span>
(i.data as Community).name <Link to={`/c/${(i.data as Community).name}`}>{`/c/${
}`}</Link> (i.data as Community).name
</span> }`}</Link>
<span>{` - ${(i.data as Community).title} - ${ </span>
(i.data as Community).number_of_subscribers <span>{` - ${(i.data as Community).title} - ${
} subscribers`}</span> (i.data as Community).number_of_subscribers
</div> } subscribers`}</span>
)} </div>
{i.type_ == 'users' && ( )}
<div> {i.type_ == 'users' && (
<span> <div>
<Link <span>
className="text-info" <Link
to={`/u/${(i.data as UserView).name}`} className="text-info"
> to={`/u/${(i.data as UserView).name}`}
{(i.data as UserView).avatar && showAvatars() && ( >
<img {(i.data as UserView).avatar && showAvatars() && (
height="32" <img
width="32" height="32"
src={pictshareAvatarThumbnail( width="32"
(i.data as UserView).avatar src={pictshareAvatarThumbnail(
)} (i.data as UserView).avatar
class="rounded-circle mr-1" )}
/> class="rounded-circle mr-1"
)} />
<span>{`/u/${(i.data as UserView).name}`}</span> )}
</Link> <span>{`/u/${(i.data as UserView).name}`}</span>
</span> </Link>
<span>{` - ${ </span>
(i.data as UserView).comment_score <span>{` - ${
} comment karma`}</span> (i.data as UserView).comment_score
</div> } comment karma`}</span>
)} </div>
)}
</div>
</div> </div>
))} ))}
</div> </div>
@ -308,55 +308,69 @@ export class Search extends Component<any, SearchState> {
comments() { comments() {
return ( return (
<div> <>
{this.state.searchResponse.comments.map(comment => ( {this.state.searchResponse.comments.map(comment => (
<CommentNodes nodes={[{ comment: comment }]} locked noIndent /> <div class="row">
<div class="col-12">
<CommentNodes nodes={[{ comment: comment }]} locked noIndent />
</div>
</div>
))} ))}
</div> </>
); );
} }
posts() { posts() {
return ( return (
<div> <>
{this.state.searchResponse.posts.map(post => ( {this.state.searchResponse.posts.map(post => (
<PostListing post={post} showCommunity viewOnly /> <div class="row">
<div class="col-12">
<PostListing post={post} showCommunity />
</div>
</div>
))} ))}
</div> </>
); );
} }
// Todo possibly create UserListing and CommunityListing // Todo possibly create UserListing and CommunityListing
communities() { communities() {
return ( return (
<div> <>
{this.state.searchResponse.communities.map(community => ( {this.state.searchResponse.communities.map(community => (
<div> <div class="row">
<span> <div class="col-12">
<Link to={`/c/${community.name}`}>{`/c/${community.name}`}</Link> <span>
</span> <Link
<span>{` - ${community.title} - ${community.number_of_subscribers} subscribers`}</span> to={`/c/${community.name}`}
>{`/c/${community.name}`}</Link>
</span>
<span>{` - ${community.title} - ${community.number_of_subscribers} subscribers`}</span>
</div>
</div> </div>
))} ))}
</div> </>
); );
} }
users() { users() {
return ( return (
<div> <>
{this.state.searchResponse.users.map(user => ( {this.state.searchResponse.users.map(user => (
<div> <div class="row">
<span> <div class="col-12">
<Link <span>
className="text-info" <Link
to={`/u/${user.name}`} className="text-info"
>{`/u/${user.name}`}</Link> to={`/u/${user.name}`}
</span> >{`/u/${user.name}`}</Link>
<span>{` - ${user.comment_score} comment karma`}</span> </span>
<span>{` - ${user.comment_score} comment karma`}</span>
</div>
</div> </div>
))} ))}
</div> </>
); );
} }
@ -477,6 +491,30 @@ export class Search extends Component<any, SearchState> {
}`; }`;
window.scrollTo(0, 0); window.scrollTo(0, 0);
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.CreateCommentLike) {
let res: CommentResponse = msg;
let found: Comment = this.state.searchResponse.comments.find(
c => c.id === res.comment.id
);
found.score = res.comment.score;
found.upvotes = res.comment.upvotes;
found.downvotes = res.comment.downvotes;
if (res.comment.my_vote !== null) {
found.my_vote = res.comment.my_vote;
found.upvoteLoading = false;
found.downvoteLoading = false;
}
this.setState(this.state);
} else if (op == UserOperation.CreatePostLike) {
let res: CreatePostLikeResponse = msg;
let found = this.state.searchResponse.posts.find(
c => c.id == res.post.id
);
found.my_vote = res.post.my_vote;
found.score = res.post.score;
found.upvotes = res.post.upvotes;
found.downvotes = res.post.downvotes;
this.setState(this.state);
} }
} }
} }

View file

@ -18,6 +18,7 @@ import {
BanUserResponse, BanUserResponse,
AddAdminResponse, AddAdminResponse,
DeleteAccountForm, DeleteAccountForm,
CreatePostLikeResponse,
} from '../interfaces'; } from '../interfaces';
import { WebSocketService, UserService } from '../services'; import { WebSocketService, UserService } from '../services';
import { import {
@ -307,7 +308,6 @@ export class User extends Component<any, UserState> {
post={i.data as Post} post={i.data as Post}
admins={this.state.admins} admins={this.state.admins}
showCommunity showCommunity
viewOnly
/> />
) : ( ) : (
<CommentNodes <CommentNodes
@ -340,12 +340,7 @@ export class User extends Component<any, UserState> {
return ( return (
<div> <div>
{this.state.posts.map(post => ( {this.state.posts.map(post => (
<PostListing <PostListing post={post} admins={this.state.admins} showCommunity />
post={post}
admins={this.state.admins}
showCommunity
viewOnly
/>
))} ))}
</div> </div>
); );
@ -1043,6 +1038,14 @@ export class User extends Component<any, UserState> {
found.downvotes = res.comment.downvotes; found.downvotes = res.comment.downvotes;
if (res.comment.my_vote !== null) found.my_vote = res.comment.my_vote; if (res.comment.my_vote !== null) found.my_vote = res.comment.my_vote;
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.CreatePostLike) {
let res: CreatePostLikeResponse = msg;
let found = this.state.posts.find(c => c.id == res.post.id);
found.my_vote = res.post.my_vote;
found.score = res.post.score;
found.upvotes = res.post.upvotes;
found.downvotes = res.post.downvotes;
this.setState(this.state);
} else if (op == UserOperation.BanUser) { } else if (op == UserOperation.BanUser) {
let res: BanUserResponse = msg; let res: BanUserResponse = msg;
this.state.comments this.state.comments

View file

@ -700,6 +700,7 @@ export interface SearchForm {
sort: string; sort: string;
page?: number; page?: number;
limit?: number; limit?: number;
auth?: string;
} }
export interface SearchResponse { export interface SearchResponse {

View file

@ -255,6 +255,7 @@ export class WebSocketService {
} }
public search(form: SearchForm) { public search(form: SearchForm) {
this.setAuth(form, false);
this.subject.next(this.wsSendWrapper(UserOperation.Search, form)); this.subject.next(this.wsSendWrapper(UserOperation.Search, form));
} }