forked from nutomic/lemmy
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:
parent
dc35c7b126
commit
fc86b83e36
6 changed files with 154 additions and 99 deletions
|
@ -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()?;
|
||||||
|
|
6
ui/src/components/post-listing.tsx
vendored
6
ui/src/components/post-listing.tsx
vendored
|
@ -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 ${
|
||||||
|
|
212
ui/src/components/search.tsx
vendored
212
ui/src/components/search.tsx
vendored
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
17
ui/src/components/user.tsx
vendored
17
ui/src/components/user.tsx
vendored
|
@ -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
|
||||||
|
|
1
ui/src/interfaces.ts
vendored
1
ui/src/interfaces.ts
vendored
|
@ -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 {
|
||||||
|
|
1
ui/src/services/WebSocketService.ts
vendored
1
ui/src/services/WebSocketService.ts
vendored
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue