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";
10 } from "lemmy-js-client";
11 import { VoteContentType, VoteType } from "../../interfaces";
12 import { I18NextService } from "../../services";
13 import { Icon, Spinner } from "../common/icon";
15 interface VoteButtonsProps {
16 voteContentType: VoteContentType;
18 onVote: (i: CreateCommentLike | CreatePostLike) => void;
19 enableDownvotes?: boolean;
20 counts: CommentAggregates | PostAggregates;
24 interface VoteButtonsState {
25 upvoteLoading: boolean;
26 downvoteLoading: boolean;
29 const tippy = (counts: CommentAggregates | PostAggregates): string => {
30 const points = I18NextService.i18n.t("number_of_points", {
31 count: Number(counts.score),
32 formattedCount: Number(counts.score),
35 const upvotes = I18NextService.i18n.t("number_of_upvotes", {
36 count: Number(counts.upvotes),
37 formattedCount: Number(counts.upvotes),
40 const downvotes = I18NextService.i18n.t("number_of_downvotes", {
41 count: Number(counts.downvotes),
42 formattedCount: Number(counts.downvotes),
45 return `${points} • ${upvotes} • ${downvotes}`;
48 const handleUpvote = (i: VoteButtons) => {
49 i.setState({ upvoteLoading: true });
51 switch (i.props.voteContentType) {
52 case VoteContentType.Comment:
54 comment_id: i.props.id,
55 score: newVote(VoteType.Upvote, i.props.my_vote),
56 auth: myAuthRequired(),
59 case VoteContentType.Post:
63 score: newVote(VoteType.Upvote, i.props.my_vote),
64 auth: myAuthRequired(),
69 const handleDownvote = (i: VoteButtons) => {
70 i.setState({ downvoteLoading: true });
71 switch (i.props.voteContentType) {
72 case VoteContentType.Comment:
74 comment_id: i.props.id,
75 score: newVote(VoteType.Downvote, i.props.my_vote),
76 auth: myAuthRequired(),
79 case VoteContentType.Post:
83 score: newVote(VoteType.Downvote, i.props.my_vote),
84 auth: myAuthRequired(),
89 export class VoteButtonsCompact extends Component<
93 state: VoteButtonsState = {
95 downvoteLoading: false,
98 constructor(props: any, context: any) {
99 super(props, context);
102 componentWillReceiveProps(nextProps: VoteButtonsProps) {
103 if (this.props !== nextProps) {
105 upvoteLoading: false,
106 downvoteLoading: false,
116 className={`btn btn-animate btn-sm btn-link py-0 px-1 ${
117 this.props.my_vote === 1 ? "text-info" : "text-muted"
119 data-tippy-content={tippy(this.props.counts)}
120 onClick={linkEvent(this, handleUpvote)}
121 aria-label={I18NextService.i18n.t("upvote")}
122 aria-pressed={this.props.my_vote === 1}
124 {this.state.upvoteLoading ? (
128 <Icon icon="arrow-up1" classes="icon-inline small" />
130 <span className="ms-2">
131 {numToSI(this.props.counts.upvotes)}
137 {this.props.enableDownvotes && (
140 className={`ms-2 btn btn-sm btn-link btn-animate btn py-0 px-1 ${
141 this.props.my_vote === -1 ? "text-danger" : "text-muted"
143 onClick={linkEvent(this, handleDownvote)}
144 data-tippy-content={tippy(this.props.counts)}
145 aria-label={I18NextService.i18n.t("downvote")}
146 aria-pressed={this.props.my_vote === -1}
148 {this.state.downvoteLoading ? (
152 <Icon icon="arrow-down1" classes="icon-inline small" />
155 className={classNames("ms-2", {
156 invisible: this.props.counts.downvotes === 0,
159 {numToSI(this.props.counts.downvotes)}
171 export class VoteButtons extends Component<VoteButtonsProps, VoteButtonsState> {
172 state: VoteButtonsState = {
173 upvoteLoading: false,
174 downvoteLoading: false,
177 constructor(props: any, context: any) {
178 super(props, context);
181 componentWillReceiveProps(nextProps: VoteButtonsProps) {
182 if (this.props !== nextProps) {
184 upvoteLoading: false,
185 downvoteLoading: false,
192 <div className="vote-bar small text-center">
195 className={`btn-animate btn btn-link p-0 ${
196 this.props.my_vote === 1 ? "text-info" : "text-muted"
198 onClick={linkEvent(this, handleUpvote)}
199 data-tippy-content={I18NextService.i18n.t("upvote")}
200 aria-label={I18NextService.i18n.t("upvote")}
201 aria-pressed={this.props.my_vote === 1}
203 {this.state.upvoteLoading ? (
206 <Icon icon="arrow-up1" classes="upvote" />
211 className="unselectable pointer text-muted post-score"
212 data-tippy-content={tippy(this.props.counts)}
214 {numToSI(this.props.counts.score)}
217 <div className="p-1"></div>
219 {this.props.enableDownvotes && (
222 className={`btn-animate btn btn-link p-0 ${
223 this.props.my_vote === -1 ? "text-danger" : "text-muted"
225 onClick={linkEvent(this, handleDownvote)}
226 data-tippy-content={I18NextService.i18n.t("downvote")}
227 aria-label={I18NextService.i18n.t("downvote")}
228 aria-pressed={this.props.my_vote === -1}
230 {this.state.downvoteLoading ? (
233 <Icon icon="arrow-down1" classes="downvote" />