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