]> Untitled Git - lemmy.git/commitdiff
Adding comment and post vote loading indicators. Fixes #449
authorDessalines <tyhou13@gmx.com>
Mon, 20 Jan 2020 15:11:50 +0000 (10:11 -0500)
committerDessalines <tyhou13@gmx.com>
Mon, 20 Jan 2020 15:11:50 +0000 (10:11 -0500)
ui/src/components/comment-node.tsx
ui/src/components/post-listing.tsx
ui/src/components/post.tsx
ui/src/interfaces.ts

index e6efcf9f1f406ab5e52010f43e739b348091315a..baaf63e90e31928bb773972e266ff494d63ab54e 100644 (file)
@@ -47,8 +47,8 @@ interface CommentNodeState {
   showConfirmAppointAsAdmin: boolean;
   collapsed: boolean;
   viewSource: boolean;
-  my_vote: number;
-  score: number;
+  upvoteLoading: boolean;
+  downvoteLoading: boolean;
 }
 
 interface CommentNodeProps {
@@ -78,8 +78,8 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
     showConfirmTransferCommunity: false,
     showConfirmAppointAsMod: false,
     showConfirmAppointAsAdmin: false,
-    my_vote: this.props.node.comment.my_vote,
-    score: this.props.node.comment.score,
+    upvoteLoading: this.props.node.comment.upvoteLoading,
+    downvoteLoading: this.props.node.comment.downvoteLoading,
   };
 
   constructor(props: any, context: any) {
@@ -87,18 +87,18 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
 
     this.state = this.emptyState;
     this.handleReplyCancel = this.handleReplyCancel.bind(this);
-    this.handleCommentLike = this.handleCommentLike.bind(this);
-    this.handleCommentDisLike = this.handleCommentDisLike.bind(this);
+    this.handleCommentUpvote = this.handleCommentUpvote.bind(this);
+    this.handleCommentDownvote = this.handleCommentDownvote.bind(this);
   }
 
-  componentDidUpdate(prevProps: CommentNodeProps) {
+  componentWillReceiveProps(nextProps: CommentNodeProps) {
     if (
-      prevProps.node.comment.my_vote !== this.props.node.comment.my_vote ||
-      this.state.score !== this.props.node.comment.score
+      nextProps.node.comment.upvoteLoading !== this.state.upvoteLoading ||
+      nextProps.node.comment.downvoteLoading !== this.state.downvoteLoading
     ) {
       this.setState({
-        my_vote: this.props.node.comment.my_vote,
-        score: this.props.node.comment.score,
+        upvoteLoading: false,
+        downvoteLoading: false,
       });
     }
   }
@@ -119,26 +119,40 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
             <button
               disabled={!UserService.Instance.user}
               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">
-                <use xlinkHref="#icon-arrow-up"></use>
-              </svg>
+              {this.state.upvoteLoading ? (
+                <svg class="icon icon-spinner spin upvote">
+                  <use xlinkHref="#icon-spinner"></use>
+                </svg>
+              ) : (
+                <svg class="icon upvote">
+                  <use xlinkHref="#icon-arrow-up"></use>
+                </svg>
+              )}
             </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 && (
               <button
                 disabled={!UserService.Instance.user}
                 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">
-                  <use xlinkHref="#icon-arrow-down"></use>
-                </svg>
+                {this.state.downvoteLoading ? (
+                  <svg class="icon icon-spinner spin downvote">
+                    <use xlinkHref="#icon-spinner"></use>
+                  </svg>
+                ) : (
+                  <svg class="icon downvote">
+                    <use xlinkHref="#icon-arrow-down"></use>
+                  </svg>
+                )}
               </button>
             )}
           </div>
@@ -736,41 +750,26 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
     this.setState(this.state);
   }
 
-  handleCommentLike(i: CommentNodeI) {
-    this.state.my_vote = i.comment.my_vote == 1 ? 0 : 1;
-    let add = 1;
-    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);
-
+  handleCommentUpvote(i: CommentNodeI) {
+    this.setState({
+      upvoteLoading: true,
+    });
     let form: CommentLikeForm = {
       comment_id: i.comment.id,
       post_id: i.comment.post_id,
-      score: this.state.my_vote,
+      score: i.comment.my_vote == 1 ? 0 : 1,
     };
     WebSocketService.Instance.likeComment(form);
   }
 
-  handleCommentDisLike(i: CommentNodeI) {
-    this.state.my_vote = i.comment.my_vote == -1 ? 0 : -1;
-    let add = -1;
-    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);
-
+  handleCommentDownvote(i: CommentNodeI) {
+    this.setState({
+      downvoteLoading: true,
+    });
     let form: CommentLikeForm = {
       comment_id: i.comment.id,
       post_id: i.comment.post_id,
-      score: this.state.my_vote,
+      score: i.comment.my_vote == -1 ? 0 : -1,
     };
     WebSocketService.Instance.likeComment(form);
   }
index 0c6d44b491f5adb0fc3cda258ff8cf7143814e2b..96825753bba693a50c792449697c39f31bc0ba4a 100644 (file)
@@ -44,8 +44,8 @@ interface PostListingState {
   showConfirmTransferCommunity: boolean;
   imageExpanded: boolean;
   viewSource: boolean;
-  my_vote: number;
-  score: number;
+  upvoteLoading: boolean;
+  downvoteLoading: boolean;
 }
 
 interface PostListingProps {
@@ -70,8 +70,8 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
     showConfirmTransferCommunity: false,
     imageExpanded: false,
     viewSource: false,
-    my_vote: this.props.post.my_vote,
-    score: this.props.post.score,
+    upvoteLoading: this.props.post.upvoteLoading,
+    downvoteLoading: this.props.post.downvoteLoading,
   };
 
   constructor(props: any, context: any) {
@@ -84,11 +84,14 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
     this.handleEditCancel = this.handleEditCancel.bind(this);
   }
 
-  componentDidUpdate(prevProps: PostListingProps) {
-    if (prevProps.post.my_vote !== this.props.post.my_vote) {
+  componentWillReceiveProps(nextProps: PostListingProps) {
+    if (
+      nextProps.post.upvoteLoading !== this.state.upvoteLoading ||
+      nextProps.post.downvoteLoading !== this.state.downvoteLoading
+    ) {
       this.setState({
-        my_vote: this.props.post.my_vote,
-        score: this.props.post.score,
+        upvoteLoading: false,
+        downvoteLoading: false,
       });
     }
   }
@@ -122,26 +125,38 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
           <button
             disabled={!UserService.Instance.user}
             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)}
           >
-            <svg class="icon upvote">
-              <use xlinkHref="#icon-arrow-up"></use>
-            </svg>
+            {this.state.upvoteLoading ? (
+              <svg class="icon icon-spinner spin upvote">
+                <use xlinkHref="#icon-spinner"></use>
+              </svg>
+            ) : (
+              <svg class="icon upvote">
+                <use xlinkHref="#icon-arrow-up"></use>
+              </svg>
+            )}
           </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 && (
             <button
               disabled={!UserService.Instance.user}
               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)}
             >
-              <svg class="icon downvote">
-                <use xlinkHref="#icon-arrow-down"></use>
-              </svg>
+              {this.state.downvoteLoading ? (
+                <svg class="icon icon-spinner spin downvote">
+                  <use xlinkHref="#icon-spinner"></use>
+                </svg>
+              ) : (
+                <svg class="icon downvote">
+                  <use xlinkHref="#icon-arrow-down"></use>
+                </svg>
+              )}
             </button>
           )}
         </div>
@@ -731,38 +746,21 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
   }
 
   handlePostLike(i: PostListing) {
-    this.state.my_vote = i.props.post.my_vote == 1 ? 0 : 1;
-    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);
+    i.setState({ upvoteLoading: true });
 
     let form: CreatePostLikeForm = {
       post_id: i.props.post.id,
-      score: this.state.my_vote,
+      score: i.props.post.my_vote == 1 ? 0 : 1,
     };
     WebSocketService.Instance.likePost(form);
   }
 
   handlePostDisLike(i: PostListing) {
-    this.state.my_vote = i.props.post.my_vote == -1 ? 0 : -1;
-    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);
+    i.setState({ downvoteLoading: true });
 
     let form: CreatePostLikeForm = {
       post_id: i.props.post.id,
-      score: this.state.my_vote,
+      score: i.props.post.my_vote == -1 ? 0 : -1,
     };
     WebSocketService.Instance.likePost(form);
   }
index c4bfccee184de8ea0fe76b4a6c99015d3b92a546..2005cc173048396b05ceb59f25be9df80ee86626 100644 (file)
@@ -400,7 +400,11 @@ export class Post extends Component<any, PostState> {
       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;
+      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;
@@ -408,6 +412,8 @@ export class Post extends Component<any, PostState> {
       this.state.post.score = res.post.score;
       this.state.post.upvotes = res.post.upvotes;
       this.state.post.downvotes = res.post.downvotes;
+      this.state.post.upvoteLoading = false;
+      this.state.post.downvoteLoading = false;
       this.setState(this.state);
     } else if (op == UserOperation.EditPost) {
       let res: PostResponse = msg;
index 7fc7a252901ac26bd73fe3b4269944366e091610..232d1388dd66a8be72fa2125c76715d22f894841 100644 (file)
@@ -164,6 +164,8 @@ export interface Post {
   subscribed?: boolean;
   read?: boolean;
   saved?: boolean;
+  upvoteLoading?: boolean;
+  downvoteLoading?: boolean;
 }
 
 export interface Comment {
@@ -190,6 +192,8 @@ export interface Comment {
   saved?: boolean;
   user_mention_id?: number; // For mention type
   recipient_id?: number;
+  upvoteLoading?: boolean;
+  downvoteLoading?: boolean;
 }
 
 export interface Category {