Adding comment and post vote loading indicators. Fixes #449
This commit is contained in:
parent
2394993dd4
commit
6312ff333b
4 changed files with 93 additions and 86 deletions
93
ui/src/components/comment-node.tsx
vendored
93
ui/src/components/comment-node.tsx
vendored
|
@ -47,8 +47,8 @@ interface CommentNodeState {
|
||||||
showConfirmAppointAsAdmin: boolean;
|
showConfirmAppointAsAdmin: boolean;
|
||||||
collapsed: boolean;
|
collapsed: boolean;
|
||||||
viewSource: boolean;
|
viewSource: boolean;
|
||||||
my_vote: number;
|
upvoteLoading: boolean;
|
||||||
score: number;
|
downvoteLoading: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CommentNodeProps {
|
interface CommentNodeProps {
|
||||||
|
@ -78,8 +78,8 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
showConfirmTransferCommunity: false,
|
showConfirmTransferCommunity: false,
|
||||||
showConfirmAppointAsMod: false,
|
showConfirmAppointAsMod: false,
|
||||||
showConfirmAppointAsAdmin: false,
|
showConfirmAppointAsAdmin: false,
|
||||||
my_vote: this.props.node.comment.my_vote,
|
upvoteLoading: this.props.node.comment.upvoteLoading,
|
||||||
score: this.props.node.comment.score,
|
downvoteLoading: this.props.node.comment.downvoteLoading,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
|
@ -87,18 +87,18 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
|
|
||||||
this.state = this.emptyState;
|
this.state = this.emptyState;
|
||||||
this.handleReplyCancel = this.handleReplyCancel.bind(this);
|
this.handleReplyCancel = this.handleReplyCancel.bind(this);
|
||||||
this.handleCommentLike = this.handleCommentLike.bind(this);
|
this.handleCommentUpvote = this.handleCommentUpvote.bind(this);
|
||||||
this.handleCommentDisLike = this.handleCommentDisLike.bind(this);
|
this.handleCommentDownvote = this.handleCommentDownvote.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps: CommentNodeProps) {
|
componentWillReceiveProps(nextProps: CommentNodeProps) {
|
||||||
if (
|
if (
|
||||||
prevProps.node.comment.my_vote !== this.props.node.comment.my_vote ||
|
nextProps.node.comment.upvoteLoading !== this.state.upvoteLoading ||
|
||||||
this.state.score !== this.props.node.comment.score
|
nextProps.node.comment.downvoteLoading !== this.state.downvoteLoading
|
||||||
) {
|
) {
|
||||||
this.setState({
|
this.setState({
|
||||||
my_vote: this.props.node.comment.my_vote,
|
upvoteLoading: false,
|
||||||
score: this.props.node.comment.score,
|
downvoteLoading: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,26 +119,40 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
<button
|
<button
|
||||||
disabled={!UserService.Instance.user}
|
disabled={!UserService.Instance.user}
|
||||||
className={`btn p-0 ${
|
className={`btn p-0 ${
|
||||||
this.state.my_vote == 1 ? 'text-info' : 'text-muted'
|
node.comment.my_vote == 1 ? 'text-info' : 'text-muted'
|
||||||
}`}
|
}`}
|
||||||
onClick={linkEvent(node, this.handleCommentLike)}
|
onClick={linkEvent(node, this.handleCommentUpvote)}
|
||||||
>
|
>
|
||||||
<svg class="icon upvote">
|
{this.state.upvoteLoading ? (
|
||||||
<use xlinkHref="#icon-arrow-up"></use>
|
<svg class="icon icon-spinner spin upvote">
|
||||||
</svg>
|
<use xlinkHref="#icon-spinner"></use>
|
||||||
|
</svg>
|
||||||
|
) : (
|
||||||
|
<svg class="icon upvote">
|
||||||
|
<use xlinkHref="#icon-arrow-up"></use>
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
</button>
|
</button>
|
||||||
<div class={`font-weight-bold text-muted`}>{this.state.score}</div>
|
<div class={`font-weight-bold text-muted`}>
|
||||||
|
{node.comment.score}
|
||||||
|
</div>
|
||||||
{WebSocketService.Instance.site.enable_downvotes && (
|
{WebSocketService.Instance.site.enable_downvotes && (
|
||||||
<button
|
<button
|
||||||
disabled={!UserService.Instance.user}
|
disabled={!UserService.Instance.user}
|
||||||
className={`btn p-0 ${
|
className={`btn p-0 ${
|
||||||
this.state.my_vote == -1 ? 'text-danger' : 'text-muted'
|
node.comment.my_vote == -1 ? 'text-danger' : 'text-muted'
|
||||||
}`}
|
}`}
|
||||||
onClick={linkEvent(node, this.handleCommentDisLike)}
|
onClick={linkEvent(node, this.handleCommentDownvote)}
|
||||||
>
|
>
|
||||||
<svg class="icon downvote">
|
{this.state.downvoteLoading ? (
|
||||||
<use xlinkHref="#icon-arrow-down"></use>
|
<svg class="icon icon-spinner spin downvote">
|
||||||
</svg>
|
<use xlinkHref="#icon-spinner"></use>
|
||||||
|
</svg>
|
||||||
|
) : (
|
||||||
|
<svg class="icon downvote">
|
||||||
|
<use xlinkHref="#icon-arrow-down"></use>
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -736,41 +750,26 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCommentLike(i: CommentNodeI) {
|
handleCommentUpvote(i: CommentNodeI) {
|
||||||
this.state.my_vote = i.comment.my_vote == 1 ? 0 : 1;
|
this.setState({
|
||||||
let add = 1;
|
upvoteLoading: true,
|
||||||
if (i.comment.my_vote == 1) {
|
});
|
||||||
add = -1;
|
|
||||||
} else if (i.comment.my_vote == -1) {
|
|
||||||
add = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.state.score = i.comment.score + add;
|
|
||||||
this.setState(this.state);
|
|
||||||
|
|
||||||
let form: CommentLikeForm = {
|
let form: CommentLikeForm = {
|
||||||
comment_id: i.comment.id,
|
comment_id: i.comment.id,
|
||||||
post_id: i.comment.post_id,
|
post_id: i.comment.post_id,
|
||||||
score: this.state.my_vote,
|
score: i.comment.my_vote == 1 ? 0 : 1,
|
||||||
};
|
};
|
||||||
WebSocketService.Instance.likeComment(form);
|
WebSocketService.Instance.likeComment(form);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCommentDisLike(i: CommentNodeI) {
|
handleCommentDownvote(i: CommentNodeI) {
|
||||||
this.state.my_vote = i.comment.my_vote == -1 ? 0 : -1;
|
this.setState({
|
||||||
let add = -1;
|
downvoteLoading: true,
|
||||||
if (i.comment.my_vote == 1) {
|
});
|
||||||
add = -2;
|
|
||||||
} else if (i.comment.my_vote == -1) {
|
|
||||||
add = 1;
|
|
||||||
}
|
|
||||||
this.state.score = i.comment.score + add;
|
|
||||||
this.setState(this.state);
|
|
||||||
|
|
||||||
let form: CommentLikeForm = {
|
let form: CommentLikeForm = {
|
||||||
comment_id: i.comment.id,
|
comment_id: i.comment.id,
|
||||||
post_id: i.comment.post_id,
|
post_id: i.comment.post_id,
|
||||||
score: this.state.my_vote,
|
score: i.comment.my_vote == -1 ? 0 : -1,
|
||||||
};
|
};
|
||||||
WebSocketService.Instance.likeComment(form);
|
WebSocketService.Instance.likeComment(form);
|
||||||
}
|
}
|
||||||
|
|
74
ui/src/components/post-listing.tsx
vendored
74
ui/src/components/post-listing.tsx
vendored
|
@ -44,8 +44,8 @@ interface PostListingState {
|
||||||
showConfirmTransferCommunity: boolean;
|
showConfirmTransferCommunity: boolean;
|
||||||
imageExpanded: boolean;
|
imageExpanded: boolean;
|
||||||
viewSource: boolean;
|
viewSource: boolean;
|
||||||
my_vote: number;
|
upvoteLoading: boolean;
|
||||||
score: number;
|
downvoteLoading: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PostListingProps {
|
interface PostListingProps {
|
||||||
|
@ -70,8 +70,8 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
showConfirmTransferCommunity: false,
|
showConfirmTransferCommunity: false,
|
||||||
imageExpanded: false,
|
imageExpanded: false,
|
||||||
viewSource: false,
|
viewSource: false,
|
||||||
my_vote: this.props.post.my_vote,
|
upvoteLoading: this.props.post.upvoteLoading,
|
||||||
score: this.props.post.score,
|
downvoteLoading: this.props.post.downvoteLoading,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
|
@ -84,11 +84,14 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
this.handleEditCancel = this.handleEditCancel.bind(this);
|
this.handleEditCancel = this.handleEditCancel.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps: PostListingProps) {
|
componentWillReceiveProps(nextProps: PostListingProps) {
|
||||||
if (prevProps.post.my_vote !== this.props.post.my_vote) {
|
if (
|
||||||
|
nextProps.post.upvoteLoading !== this.state.upvoteLoading ||
|
||||||
|
nextProps.post.downvoteLoading !== this.state.downvoteLoading
|
||||||
|
) {
|
||||||
this.setState({
|
this.setState({
|
||||||
my_vote: this.props.post.my_vote,
|
upvoteLoading: false,
|
||||||
score: this.props.post.score,
|
downvoteLoading: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,26 +125,38 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
<button
|
<button
|
||||||
disabled={!UserService.Instance.user}
|
disabled={!UserService.Instance.user}
|
||||||
className={`btn p-0 ${
|
className={`btn p-0 ${
|
||||||
this.state.my_vote == 1 ? 'text-info' : 'text-muted'
|
post.my_vote == 1 ? 'text-info' : 'text-muted'
|
||||||
}`}
|
}`}
|
||||||
onClick={linkEvent(this, this.handlePostLike)}
|
onClick={linkEvent(this, this.handlePostLike)}
|
||||||
>
|
>
|
||||||
<svg class="icon upvote">
|
{this.state.upvoteLoading ? (
|
||||||
<use xlinkHref="#icon-arrow-up"></use>
|
<svg class="icon icon-spinner spin upvote">
|
||||||
</svg>
|
<use xlinkHref="#icon-spinner"></use>
|
||||||
|
</svg>
|
||||||
|
) : (
|
||||||
|
<svg class="icon upvote">
|
||||||
|
<use xlinkHref="#icon-arrow-up"></use>
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
</button>
|
</button>
|
||||||
<div class={`font-weight-bold text-muted`}>{this.state.score}</div>
|
<div class={`font-weight-bold text-muted`}>{post.score}</div>
|
||||||
{WebSocketService.Instance.site.enable_downvotes && (
|
{WebSocketService.Instance.site.enable_downvotes && (
|
||||||
<button
|
<button
|
||||||
disabled={!UserService.Instance.user}
|
disabled={!UserService.Instance.user}
|
||||||
className={`btn p-0 ${
|
className={`btn p-0 ${
|
||||||
this.state.my_vote == -1 ? 'text-danger' : 'text-muted'
|
post.my_vote == -1 ? 'text-danger' : 'text-muted'
|
||||||
}`}
|
}`}
|
||||||
onClick={linkEvent(this, this.handlePostDisLike)}
|
onClick={linkEvent(this, this.handlePostDisLike)}
|
||||||
>
|
>
|
||||||
<svg class="icon downvote">
|
{this.state.downvoteLoading ? (
|
||||||
<use xlinkHref="#icon-arrow-down"></use>
|
<svg class="icon icon-spinner spin downvote">
|
||||||
</svg>
|
<use xlinkHref="#icon-spinner"></use>
|
||||||
|
</svg>
|
||||||
|
) : (
|
||||||
|
<svg class="icon downvote">
|
||||||
|
<use xlinkHref="#icon-arrow-down"></use>
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -731,38 +746,21 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePostLike(i: PostListing) {
|
handlePostLike(i: PostListing) {
|
||||||
this.state.my_vote = i.props.post.my_vote == 1 ? 0 : 1;
|
i.setState({ upvoteLoading: true });
|
||||||
let add = 1;
|
|
||||||
if (i.props.post.my_vote == 1) {
|
|
||||||
add = -1;
|
|
||||||
} else if (i.props.post.my_vote == -1) {
|
|
||||||
add = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.state.score = i.props.post.score + add;
|
|
||||||
this.setState(this.state);
|
|
||||||
|
|
||||||
let form: CreatePostLikeForm = {
|
let form: CreatePostLikeForm = {
|
||||||
post_id: i.props.post.id,
|
post_id: i.props.post.id,
|
||||||
score: this.state.my_vote,
|
score: i.props.post.my_vote == 1 ? 0 : 1,
|
||||||
};
|
};
|
||||||
WebSocketService.Instance.likePost(form);
|
WebSocketService.Instance.likePost(form);
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePostDisLike(i: PostListing) {
|
handlePostDisLike(i: PostListing) {
|
||||||
this.state.my_vote = i.props.post.my_vote == -1 ? 0 : -1;
|
i.setState({ downvoteLoading: true });
|
||||||
let add = -1;
|
|
||||||
if (i.props.post.my_vote == 1) {
|
|
||||||
add = -2;
|
|
||||||
} else if (i.props.post.my_vote == -1) {
|
|
||||||
add = 1;
|
|
||||||
}
|
|
||||||
this.state.score = i.props.post.score + add;
|
|
||||||
this.setState(this.state);
|
|
||||||
|
|
||||||
let form: CreatePostLikeForm = {
|
let form: CreatePostLikeForm = {
|
||||||
post_id: i.props.post.id,
|
post_id: i.props.post.id,
|
||||||
score: this.state.my_vote,
|
score: i.props.post.my_vote == -1 ? 0 : -1,
|
||||||
};
|
};
|
||||||
WebSocketService.Instance.likePost(form);
|
WebSocketService.Instance.likePost(form);
|
||||||
}
|
}
|
||||||
|
|
8
ui/src/components/post.tsx
vendored
8
ui/src/components/post.tsx
vendored
|
@ -400,7 +400,11 @@ export class Post extends Component<any, PostState> {
|
||||||
found.score = res.comment.score;
|
found.score = res.comment.score;
|
||||||
found.upvotes = res.comment.upvotes;
|
found.upvotes = res.comment.upvotes;
|
||||||
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;
|
||||||
|
found.upvoteLoading = false;
|
||||||
|
found.downvoteLoading = false;
|
||||||
|
}
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.CreatePostLike) {
|
} else if (op == UserOperation.CreatePostLike) {
|
||||||
let res: CreatePostLikeResponse = msg;
|
let res: CreatePostLikeResponse = msg;
|
||||||
|
@ -408,6 +412,8 @@ export class Post extends Component<any, PostState> {
|
||||||
this.state.post.score = res.post.score;
|
this.state.post.score = res.post.score;
|
||||||
this.state.post.upvotes = res.post.upvotes;
|
this.state.post.upvotes = res.post.upvotes;
|
||||||
this.state.post.downvotes = res.post.downvotes;
|
this.state.post.downvotes = res.post.downvotes;
|
||||||
|
this.state.post.upvoteLoading = false;
|
||||||
|
this.state.post.downvoteLoading = false;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.EditPost) {
|
} else if (op == UserOperation.EditPost) {
|
||||||
let res: PostResponse = msg;
|
let res: PostResponse = msg;
|
||||||
|
|
4
ui/src/interfaces.ts
vendored
4
ui/src/interfaces.ts
vendored
|
@ -164,6 +164,8 @@ export interface Post {
|
||||||
subscribed?: boolean;
|
subscribed?: boolean;
|
||||||
read?: boolean;
|
read?: boolean;
|
||||||
saved?: boolean;
|
saved?: boolean;
|
||||||
|
upvoteLoading?: boolean;
|
||||||
|
downvoteLoading?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Comment {
|
export interface Comment {
|
||||||
|
@ -190,6 +192,8 @@ export interface Comment {
|
||||||
saved?: boolean;
|
saved?: boolean;
|
||||||
user_mention_id?: number; // For mention type
|
user_mention_id?: number; // For mention type
|
||||||
recipient_id?: number;
|
recipient_id?: number;
|
||||||
|
upvoteLoading?: boolean;
|
||||||
|
downvoteLoading?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Category {
|
export interface Category {
|
||||||
|
|
Reference in a new issue