]> Untitled Git - lemmy-ui.git/blobdiff - src/shared/components/common/vote-buttons.tsx
Merge remote-tracking branch 'lemmy/main' into fix/wider-max-width-1536
[lemmy-ui.git] / src / shared / components / common / vote-buttons.tsx
index 2bbddad613cd63854e5c67e7c1ca82427b632743..ff12125f21328f33fa5fed7e26e050081802b390 100644 (file)
@@ -1,18 +1,22 @@
-import { showScores } from "@utils/app";
+import { myAuthRequired, newVote, showScores } from "@utils/app";
 import { numToSI } from "@utils/helpers";
+import classNames from "classnames";
 import { Component, linkEvent } from "inferno";
-import { CommentAggregates, PostAggregates } from "lemmy-js-client";
+import {
+  CommentAggregates,
+  CreateCommentLike,
+  CreatePostLike,
+  PostAggregates,
+} from "lemmy-js-client";
+import { VoteContentType, VoteType } from "../../interfaces";
 import { I18NextService } from "../../services";
 import { Icon, Spinner } from "../common/icon";
-import { PostListing } from "../post/post-listing";
 
 interface VoteButtonsProps {
-  postListing: PostListing;
+  voteContentType: VoteContentType;
+  id: number;
+  onVote: (i: CreateCommentLike | CreatePostLike) => void;
   enableDownvotes?: boolean;
-  upvoteLoading?: boolean;
-  downvoteLoading?: boolean;
-  handleUpvote: (i: PostListing) => void;
-  handleDownvote: (i: PostListing) => void;
   counts: CommentAggregates | PostAggregates;
   my_vote?: number;
 }
@@ -22,6 +26,69 @@ interface VoteButtonsState {
   downvoteLoading: boolean;
 }
 
+const tippy = (counts: CommentAggregates | PostAggregates): string => {
+  const points = I18NextService.i18n.t("number_of_points", {
+    count: Number(counts.score),
+    formattedCount: Number(counts.score),
+  });
+
+  const upvotes = I18NextService.i18n.t("number_of_upvotes", {
+    count: Number(counts.upvotes),
+    formattedCount: Number(counts.upvotes),
+  });
+
+  const downvotes = I18NextService.i18n.t("number_of_downvotes", {
+    count: Number(counts.downvotes),
+    formattedCount: Number(counts.downvotes),
+  });
+
+  return `${points} • ${upvotes} • ${downvotes}`;
+};
+
+const handleUpvote = (i: VoteButtons) => {
+  i.setState({ upvoteLoading: true });
+
+  switch (i.props.voteContentType) {
+    case VoteContentType.Comment:
+      i.props.onVote({
+        comment_id: i.props.id,
+        score: newVote(VoteType.Upvote, i.props.my_vote),
+        auth: myAuthRequired(),
+      });
+      break;
+    case VoteContentType.Post:
+    default:
+      i.props.onVote({
+        post_id: i.props.id,
+        score: newVote(VoteType.Upvote, i.props.my_vote),
+        auth: myAuthRequired(),
+      });
+  }
+
+  i.setState({ upvoteLoading: false });
+};
+
+const handleDownvote = (i: VoteButtons) => {
+  i.setState({ downvoteLoading: true });
+  switch (i.props.voteContentType) {
+    case VoteContentType.Comment:
+      i.props.onVote({
+        comment_id: i.props.id,
+        score: newVote(VoteType.Downvote, i.props.my_vote),
+        auth: myAuthRequired(),
+      });
+      break;
+    case VoteContentType.Post:
+    default:
+      i.props.onVote({
+        post_id: i.props.id,
+        score: newVote(VoteType.Downvote, i.props.my_vote),
+        auth: myAuthRequired(),
+      });
+  }
+  i.setState({ downvoteLoading: false });
+};
+
 export class VoteButtonsCompact extends Component<
   VoteButtonsProps,
   VoteButtonsState
@@ -35,93 +102,68 @@ export class VoteButtonsCompact extends Component<
     super(props, context);
   }
 
-  get pointsTippy(): string {
-    const points = I18NextService.i18n.t("number_of_points", {
-      count: Number(this.props.counts.score),
-      formattedCount: Number(this.props.counts.score),
-    });
-
-    const upvotes = I18NextService.i18n.t("number_of_upvotes", {
-      count: Number(this.props.counts.upvotes),
-      formattedCount: Number(this.props.counts.upvotes),
-    });
-
-    const downvotes = I18NextService.i18n.t("number_of_downvotes", {
-      count: Number(this.props.counts.downvotes),
-      formattedCount: Number(this.props.counts.downvotes),
-    });
-
-    return `${points} • ${upvotes} • ${downvotes}`;
-  }
-
-  get tippy() {
-    return showScores() ? { "data-tippy-content": this.pointsTippy } : {};
-  }
-
   render() {
     return (
-      <>
-        <div className="input-group input-group-sm w-auto">
+      <div>
+        <button
+          type="button"
+          className={`btn-animate btn py-0 px-1 ${
+            this.props.my_vote === 1 ? "text-info" : "text-muted"
+          }`}
+          data-tippy-content={tippy(this.props.counts)}
+          onClick={linkEvent(this, handleUpvote)}
+          aria-label={I18NextService.i18n.t("upvote")}
+          aria-pressed={this.props.my_vote === 1}
+        >
+          {this.state.upvoteLoading ? (
+            <Spinner />
+          ) : (
+            <>
+              <Icon icon="arrow-up1" classes="icon-inline small" />
+              {showScores() && (
+                <span className="ms-2">
+                  {numToSI(this.props.counts.upvotes)}
+                </span>
+              )}
+            </>
+          )}
+        </button>
+        {this.props.enableDownvotes && (
           <button
-            className={`btn btn-sm btn-animate btn-outline-primary rounded-start py-0 ${
-              this.props.my_vote === 1 ? "text-info" : "text-muted"
+            type="button"
+            className={`ms-2 btn-animate btn py-0 px-1 ${
+              this.props.my_vote === -1 ? "text-danger" : "text-muted"
             }`}
-            {...this.tippy}
-            onClick={linkEvent(this.props.postListing, this.props.handleUpvote)}
-            aria-label={I18NextService.i18n.t("upvote")}
-            aria-pressed={this.props.my_vote === 1}
+            onClick={linkEvent(this, handleDownvote)}
+            data-tippy-content={tippy(this.props.counts)}
+            aria-label={I18NextService.i18n.t("downvote")}
+            aria-pressed={this.props.my_vote === -1}
           >
-            {this.state.upvoteLoading ? (
+            {this.state.downvoteLoading ? (
               <Spinner />
             ) : (
               <>
-                <Icon icon="arrow-up1" classes="icon-inline small" />
+                <Icon icon="arrow-down1" classes="icon-inline small" />
                 {showScores() && (
-                  <span className="ms-2">
-                    {numToSI(this.props.counts.upvotes)}
+                  <span
+                    className={classNames("ms-2", {
+                      invisible: this.props.counts.downvotes === 0,
+                    })}
+                  >
+                    {numToSI(this.props.counts.downvotes)}
                   </span>
                 )}
               </>
             )}
           </button>
-          <span className="input-group-text small py-0">
-            {numToSI(this.props.counts.score)}
-          </span>
-          {this.props.enableDownvotes && (
-            <button
-              className={`btn btn-sm btn-animate btn-outline-primary rounded-end py-0 ${
-                this.props.my_vote === -1 ? "text-danger" : "text-muted"
-              }`}
-              onClick={linkEvent(
-                this.props.postListing,
-                this.props.handleDownvote
-              )}
-              {...this.tippy}
-              aria-label={I18NextService.i18n.t("downvote")}
-              aria-pressed={this.props.my_vote === -1}
-            >
-              {this.state.downvoteLoading ? (
-                <Spinner />
-              ) : (
-                <>
-                  <Icon icon="arrow-down1" classes="icon-inline small" />
-                  {showScores() && (
-                    <span className="ms-2">
-                      {numToSI(this.props.counts.downvotes)}
-                    </span>
-                  )}
-                </>
-              )}
-            </button>
-          )}
-        </div>
-      </>
+        )}
+      </div>
     );
   }
 }
 
-export class VoteButtons extends Component<VotesProps, VotesState> {
-  state: VotesState = {
+export class VoteButtons extends Component<VoteButtonsProps, VoteButtonsState> {
+  state: VoteButtonsState = {
     upvoteLoading: false,
     downvoteLoading: false,
   };
@@ -130,37 +172,15 @@ export class VoteButtons extends Component<VotesProps, VotesState> {
     super(props, context);
   }
 
-  get pointsTippy(): string {
-    const points = I18NextService.i18n.t("number_of_points", {
-      count: Number(this.props.counts.score),
-      formattedCount: Number(this.props.counts.score),
-    });
-
-    const upvotes = I18NextService.i18n.t("number_of_upvotes", {
-      count: Number(this.props.counts.upvotes),
-      formattedCount: Number(this.props.counts.upvotes),
-    });
-
-    const downvotes = I18NextService.i18n.t("number_of_downvotes", {
-      count: Number(this.props.counts.downvotes),
-      formattedCount: Number(this.props.counts.downvotes),
-    });
-
-    return `${points} • ${upvotes} • ${downvotes}`;
-  }
-
-  get tippy() {
-    return showScores() ? { "data-tippy-content": this.pointsTippy } : {};
-  }
-
   render() {
     return (
-      <div className={`vote-bar col-1 pe-0 small text-center`}>
+      <div className="vote-bar pe-0 small text-center">
         <button
+          type="button"
           className={`btn-animate btn btn-link p-0 ${
             this.props.my_vote == 1 ? "text-info" : "text-muted"
           }`}
-          onClick={linkEvent(this.props.postListing, this.props.handleUpvote)}
+          onClick={linkEvent(thishandleUpvote)}
           data-tippy-content={I18NextService.i18n.t("upvote")}
           aria-label={I18NextService.i18n.t("upvote")}
           aria-pressed={this.props.my_vote === 1}
@@ -173,8 +193,8 @@ export class VoteButtons extends Component<VotesProps, VotesState> {
         </button>
         {showScores() ? (
           <div
-            className={`unselectable pointer text-muted px-1 post-score`}
-            data-tippy-content={this.pointsTippy}
+            className="unselectable pointer text-muted px-1 post-score"
+            data-tippy-content={tippy(this.props.counts)}
           >
             {numToSI(this.props.counts.score)}
           </div>
@@ -183,13 +203,11 @@ export class VoteButtons extends Component<VotesProps, VotesState> {
         )}
         {this.props.enableDownvotes && (
           <button
+            type="button"
             className={`btn-animate btn btn-link p-0 ${
               this.props.my_vote == -1 ? "text-danger" : "text-muted"
             }`}
-            onClick={linkEvent(
-              this.props.postListing,
-              this.props.handleDownvote
-            )}
+            onClick={linkEvent(this, handleDownvote)}
             data-tippy-content={I18NextService.i18n.t("downvote")}
             aria-label={I18NextService.i18n.t("downvote")}
             aria-pressed={this.props.my_vote === -1}