]> Untitled Git - lemmy-ui.git/blob - src/shared/components/common/vote-buttons.tsx
fix!: Try to get Vote Buttons component working in Comments
[lemmy-ui.git] / src / shared / components / common / vote-buttons.tsx
1 import { myAuthRequired, newVote, showScores } from "@utils/app";
2 import { numToSI } from "@utils/helpers";
3 import classNames from "classnames";
4 import { Component, linkEvent } from "inferno";
5 import {
6   CommentAggregates,
7   CreateCommentLike,
8   CreatePostLike,
9   PostAggregates,
10 } from "lemmy-js-client";
11 import { VoteType } from "../../interfaces";
12 import { I18NextService } from "../../services";
13 import { Icon, Spinner } from "../common/icon";
14
15 interface VoteButtonsProps {
16   id: number;
17   onVote: (i: CreateCommentLike | CreatePostLike) => void;
18   enableDownvotes?: boolean;
19   counts: CommentAggregates | PostAggregates;
20   my_vote?: number;
21 }
22
23 interface VoteButtonsState {
24   upvoteLoading: boolean;
25   downvoteLoading: boolean;
26 }
27
28 const tippy = (counts: CommentAggregates | PostAggregates): string => {
29   const points = I18NextService.i18n.t("number_of_points", {
30     count: Number(counts.score),
31     formattedCount: Number(counts.score),
32   });
33
34   const upvotes = I18NextService.i18n.t("number_of_upvotes", {
35     count: Number(counts.upvotes),
36     formattedCount: Number(counts.upvotes),
37   });
38
39   const downvotes = I18NextService.i18n.t("number_of_downvotes", {
40     count: Number(counts.downvotes),
41     formattedCount: Number(counts.downvotes),
42   });
43
44   return `${points} • ${upvotes} • ${downvotes}`;
45 };
46
47 const handleUpvote = (i: VoteButtons) => {
48   i.setState({ upvoteLoading: true });
49   i.props.onVote({
50     id: i.props.id,
51     score: newVote(VoteType.Upvote, i.props.my_vote),
52     auth: myAuthRequired(),
53   });
54   i.setState({ upvoteLoading: false });
55 };
56
57 const handleDownvote = (i: VoteButtons) => {
58   i.setState({ downvoteLoading: true });
59   i.props.onVote({
60     id: i.props.id,
61     score: newVote(VoteType.Downvote, i.props.my_vote),
62     auth: myAuthRequired(),
63   });
64   i.setState({ downvoteLoading: false });
65 };
66
67 export class VoteButtonsCompact extends Component<
68   VoteButtonsProps,
69   VoteButtonsState
70 > {
71   state: VoteButtonsState = {
72     upvoteLoading: false,
73     downvoteLoading: false,
74   };
75
76   constructor(props: any, context: any) {
77     super(props, context);
78   }
79
80   render() {
81     return (
82       <div>
83         <button
84           className={`btn-animate btn py-0 px-1 ${
85             this.props.my_vote === 1 ? "text-info" : "text-muted"
86           }`}
87           data-tippy-content={tippy(this.props.counts)}
88           onClick={linkEvent(this, handleUpvote)}
89           aria-label={I18NextService.i18n.t("upvote")}
90           aria-pressed={this.props.my_vote === 1}
91         >
92           {this.state.upvoteLoading ? (
93             <Spinner />
94           ) : (
95             <>
96               <Icon icon="arrow-up1" classes="icon-inline small" />
97               {showScores() && (
98                 <span className="ms-2">
99                   {numToSI(this.props.counts.upvotes)}
100                 </span>
101               )}
102             </>
103           )}
104         </button>
105         {this.props.enableDownvotes && (
106           <button
107             className={`ms-2 btn-animate btn py-0 px-1 ${
108               this.props.my_vote === -1 ? "text-danger" : "text-muted"
109             }`}
110             onClick={linkEvent(this, handleDownvote)}
111             data-tippy-content={tippy(this.props.counts)}
112             aria-label={I18NextService.i18n.t("downvote")}
113             aria-pressed={this.props.my_vote === -1}
114           >
115             {this.state.downvoteLoading ? (
116               <Spinner />
117             ) : (
118               <>
119                 <Icon icon="arrow-down1" classes="icon-inline small" />
120                 {showScores() && (
121                   <span
122                     className={classNames("ms-2", {
123                       invisible: this.props.counts.downvotes === 0,
124                     })}
125                   >
126                     {numToSI(this.props.counts.downvotes)}
127                   </span>
128                 )}
129               </>
130             )}
131           </button>
132         )}
133       </div>
134     );
135   }
136 }
137
138 export class VoteButtons extends Component<VoteButtonsProps, VoteButtonsState> {
139   state: VoteButtonsState = {
140     upvoteLoading: false,
141     downvoteLoading: false,
142   };
143
144   constructor(props: any, context: any) {
145     super(props, context);
146   }
147
148   render() {
149     return (
150       <div className={`vote-bar col-1 pe-0 small text-center`}>
151         <button
152           className={`btn-animate btn btn-link p-0 ${
153             this.props.my_vote == 1 ? "text-info" : "text-muted"
154           }`}
155           onClick={linkEvent(this, handleUpvote)}
156           data-tippy-content={I18NextService.i18n.t("upvote")}
157           aria-label={I18NextService.i18n.t("upvote")}
158           aria-pressed={this.props.my_vote === 1}
159         >
160           {this.state.upvoteLoading ? (
161             <Spinner />
162           ) : (
163             <Icon icon="arrow-up1" classes="upvote" />
164           )}
165         </button>
166         {showScores() ? (
167           <div
168             className={`unselectable pointer text-muted px-1 post-score`}
169             data-tippy-content={tippy(this.props.counts)}
170           >
171             {numToSI(this.props.counts.score)}
172           </div>
173         ) : (
174           <div className="p-1"></div>
175         )}
176         {this.props.enableDownvotes && (
177           <button
178             className={`btn-animate btn btn-link p-0 ${
179               this.props.my_vote == -1 ? "text-danger" : "text-muted"
180             }`}
181             onClick={linkEvent(this, handleDownvote)}
182             data-tippy-content={I18NextService.i18n.t("downvote")}
183             aria-label={I18NextService.i18n.t("downvote")}
184             aria-pressed={this.props.my_vote === -1}
185           >
186             {this.state.downvoteLoading ? (
187               <Spinner />
188             ) : (
189               <Icon icon="arrow-down1" classes="downvote" />
190             )}
191           </button>
192         )}
193       </div>
194     );
195   }
196 }