mirror of
https://github.com/LemmyNet/lemmy-ui.git
synced 2024-11-25 22:01:13 +00:00
Adding vote display modes (#2426)
* Adding vote display modes * Only show downvotes setting if site has downvotes enabled.
This commit is contained in:
parent
643c1f6f01
commit
d8a92812d8
23 changed files with 580 additions and 171 deletions
|
@ -1,4 +1,4 @@
|
||||||
import { colorList, getCommentParentId, showScores } from "@utils/app";
|
import { colorList, getCommentParentId } from "@utils/app";
|
||||||
import { futureDaysToUnixTime, numToSI } from "@utils/helpers";
|
import { futureDaysToUnixTime, numToSI } from "@utils/helpers";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { isBefore, parseISO, subMinutes } from "date-fns";
|
import { isBefore, parseISO, subMinutes } from "date-fns";
|
||||||
|
@ -22,6 +22,7 @@ import {
|
||||||
EditComment,
|
EditComment,
|
||||||
GetComments,
|
GetComments,
|
||||||
Language,
|
Language,
|
||||||
|
LocalUserVoteDisplayMode,
|
||||||
MarkCommentReplyAsRead,
|
MarkCommentReplyAsRead,
|
||||||
MarkPersonMentionAsRead,
|
MarkPersonMentionAsRead,
|
||||||
PersonMentionView,
|
PersonMentionView,
|
||||||
|
@ -54,6 +55,7 @@ import { CommentNodes } from "./comment-nodes";
|
||||||
import { BanUpdateForm } from "../common/mod-action-form-modal";
|
import { BanUpdateForm } from "../common/mod-action-form-modal";
|
||||||
import CommentActionDropdown from "../common/content-actions/comment-action-dropdown";
|
import CommentActionDropdown from "../common/content-actions/comment-action-dropdown";
|
||||||
import { RequestState } from "../../services/HttpService";
|
import { RequestState } from "../../services/HttpService";
|
||||||
|
import { VoteDisplay } from "../common/vote-display";
|
||||||
|
|
||||||
type CommentNodeState = {
|
type CommentNodeState = {
|
||||||
showReply: boolean;
|
showReply: boolean;
|
||||||
|
@ -80,6 +82,7 @@ interface CommentNodeProps {
|
||||||
showContext?: boolean;
|
showContext?: boolean;
|
||||||
showCommunity?: boolean;
|
showCommunity?: boolean;
|
||||||
enableDownvotes?: boolean;
|
enableDownvotes?: boolean;
|
||||||
|
voteDisplayMode: LocalUserVoteDisplayMode;
|
||||||
viewType: CommentViewType;
|
viewType: CommentViewType;
|
||||||
allLanguages: Language[];
|
allLanguages: Language[];
|
||||||
siteLanguages: number[];
|
siteLanguages: number[];
|
||||||
|
@ -264,20 +267,11 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
{/* This is an expanding spacer for mobile */}
|
{/* This is an expanding spacer for mobile */}
|
||||||
<div className="me-lg-5 flex-grow-1 flex-lg-grow-0 unselectable pointer mx-2" />
|
<div className="me-lg-5 flex-grow-1 flex-lg-grow-0 unselectable pointer mx-2" />
|
||||||
|
|
||||||
{showScores() && (
|
<VoteDisplay
|
||||||
<>
|
voteDisplayMode={this.props.voteDisplayMode}
|
||||||
<span
|
myVote={my_vote}
|
||||||
className={`me-1 fw-bold ${this.scoreColor}`}
|
counts={counts}
|
||||||
aria-label={I18NextService.i18n.t("number_of_points", {
|
/>
|
||||||
count: Number(counts.score),
|
|
||||||
formattedCount: numToSI(counts.score),
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{numToSI(counts.score)}
|
|
||||||
</span>
|
|
||||||
<span className="me-1">•</span>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<span>
|
<span>
|
||||||
<MomentTime published={published} updated={updated} />
|
<MomentTime published={published} updated={updated} />
|
||||||
</span>
|
</span>
|
||||||
|
@ -354,8 +348,9 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
id={id}
|
id={id}
|
||||||
onVote={this.props.onCommentVote}
|
onVote={this.props.onCommentVote}
|
||||||
enableDownvotes={this.props.enableDownvotes}
|
enableDownvotes={this.props.enableDownvotes}
|
||||||
|
voteDisplayMode={this.props.voteDisplayMode}
|
||||||
counts={counts}
|
counts={counts}
|
||||||
my_vote={my_vote}
|
myVote={my_vote}
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
@ -445,6 +440,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
moderators={this.props.moderators}
|
moderators={this.props.moderators}
|
||||||
admins={this.props.admins}
|
admins={this.props.admins}
|
||||||
enableDownvotes={this.props.enableDownvotes}
|
enableDownvotes={this.props.enableDownvotes}
|
||||||
|
voteDisplayMode={this.props.voteDisplayMode}
|
||||||
viewType={this.props.viewType}
|
viewType={this.props.viewType}
|
||||||
allLanguages={this.props.allLanguages}
|
allLanguages={this.props.allLanguages}
|
||||||
siteLanguages={this.props.siteLanguages}
|
siteLanguages={this.props.siteLanguages}
|
||||||
|
@ -536,35 +532,6 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
return this.commentView.creator.id === this.commentView.post.creator_id;
|
return this.commentView.creator.id === this.commentView.post.creator_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
get scoreColor() {
|
|
||||||
if (this.commentView.my_vote === 1) {
|
|
||||||
return "text-info";
|
|
||||||
} else if (this.commentView.my_vote === -1) {
|
|
||||||
return "text-danger";
|
|
||||||
} else {
|
|
||||||
return "text-muted";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get pointsTippy(): string {
|
|
||||||
const points = I18NextService.i18n.t("number_of_points", {
|
|
||||||
count: Number(this.commentView.counts.score),
|
|
||||||
formattedCount: numToSI(this.commentView.counts.score),
|
|
||||||
});
|
|
||||||
|
|
||||||
const upvotes = I18NextService.i18n.t("number_of_upvotes", {
|
|
||||||
count: Number(this.commentView.counts.upvotes),
|
|
||||||
formattedCount: numToSI(this.commentView.counts.upvotes),
|
|
||||||
});
|
|
||||||
|
|
||||||
const downvotes = I18NextService.i18n.t("number_of_downvotes", {
|
|
||||||
count: Number(this.commentView.counts.downvotes),
|
|
||||||
formattedCount: numToSI(this.commentView.counts.downvotes),
|
|
||||||
});
|
|
||||||
|
|
||||||
return `${points} • ${upvotes} • ${downvotes}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
get expandText(): string {
|
get expandText(): string {
|
||||||
return this.state.collapsed
|
return this.state.collapsed
|
||||||
? I18NextService.i18n.t("expand")
|
? I18NextService.i18n.t("expand")
|
||||||
|
|
|
@ -18,6 +18,7 @@ import {
|
||||||
EditComment,
|
EditComment,
|
||||||
GetComments,
|
GetComments,
|
||||||
Language,
|
Language,
|
||||||
|
LocalUserVoteDisplayMode,
|
||||||
MarkCommentReplyAsRead,
|
MarkCommentReplyAsRead,
|
||||||
MarkPersonMentionAsRead,
|
MarkPersonMentionAsRead,
|
||||||
PersonView,
|
PersonView,
|
||||||
|
@ -44,6 +45,7 @@ interface CommentNodesProps {
|
||||||
showContext?: boolean;
|
showContext?: boolean;
|
||||||
showCommunity?: boolean;
|
showCommunity?: boolean;
|
||||||
enableDownvotes?: boolean;
|
enableDownvotes?: boolean;
|
||||||
|
voteDisplayMode: LocalUserVoteDisplayMode;
|
||||||
viewType: CommentViewType;
|
viewType: CommentViewType;
|
||||||
allLanguages: Language[];
|
allLanguages: Language[];
|
||||||
siteLanguages: number[];
|
siteLanguages: number[];
|
||||||
|
@ -115,6 +117,7 @@ export class CommentNodes extends Component<CommentNodesProps, any> {
|
||||||
showContext={this.props.showContext}
|
showContext={this.props.showContext}
|
||||||
showCommunity={this.props.showCommunity}
|
showCommunity={this.props.showCommunity}
|
||||||
enableDownvotes={this.props.enableDownvotes}
|
enableDownvotes={this.props.enableDownvotes}
|
||||||
|
voteDisplayMode={this.props.voteDisplayMode}
|
||||||
viewType={this.props.viewType}
|
viewType={this.props.viewType}
|
||||||
allLanguages={this.props.allLanguages}
|
allLanguages={this.props.allLanguages}
|
||||||
siteLanguages={this.props.siteLanguages}
|
siteLanguages={this.props.siteLanguages}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { T } from "inferno-i18next-dess";
|
||||||
import {
|
import {
|
||||||
CommentReportView,
|
CommentReportView,
|
||||||
CommentView,
|
CommentView,
|
||||||
|
LocalUserVoteDisplayMode,
|
||||||
ResolveCommentReport,
|
ResolveCommentReport,
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
import { CommentNodeI, CommentViewType } from "../../interfaces";
|
import { CommentNodeI, CommentViewType } from "../../interfaces";
|
||||||
|
@ -15,6 +16,8 @@ import { tippyMixin } from "../mixins/tippy-mixin";
|
||||||
|
|
||||||
interface CommentReportProps {
|
interface CommentReportProps {
|
||||||
report: CommentReportView;
|
report: CommentReportView;
|
||||||
|
enableDownvotes?: boolean;
|
||||||
|
voteDisplayMode: LocalUserVoteDisplayMode;
|
||||||
onResolveReport(form: ResolveCommentReport): void;
|
onResolveReport(form: ResolveCommentReport): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,7 +82,8 @@ export class CommentReport extends Component<
|
||||||
<CommentNode
|
<CommentNode
|
||||||
node={node}
|
node={node}
|
||||||
viewType={CommentViewType.Flat}
|
viewType={CommentViewType.Flat}
|
||||||
enableDownvotes={true}
|
enableDownvotes={this.props.enableDownvotes}
|
||||||
|
voteDisplayMode={this.props.voteDisplayMode}
|
||||||
viewOnly={true}
|
viewOnly={true}
|
||||||
showCommunity={true}
|
showCommunity={true}
|
||||||
allLanguages={[]}
|
allLanguages={[]}
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
import { newVote, showScores } from "@utils/app";
|
import { calculateUpvotePct, newVote, showScores } from "@utils/app";
|
||||||
import { numToSI } from "@utils/helpers";
|
import { numToSI } from "@utils/helpers";
|
||||||
import classNames from "classnames";
|
|
||||||
import { Component, InfernoNode, linkEvent } from "inferno";
|
import { Component, InfernoNode, linkEvent } from "inferno";
|
||||||
import {
|
import {
|
||||||
CommentAggregates,
|
CommentAggregates,
|
||||||
CreateCommentLike,
|
CreateCommentLike,
|
||||||
CreatePostLike,
|
CreatePostLike,
|
||||||
|
LocalUserVoteDisplayMode,
|
||||||
PostAggregates,
|
PostAggregates,
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
import { VoteContentType, VoteType } from "../../interfaces";
|
import { VoteContentType, VoteType } from "../../interfaces";
|
||||||
import { I18NextService, UserService } from "../../services";
|
import { I18NextService, UserService } from "../../services";
|
||||||
import { Icon, Spinner } from "../common/icon";
|
import { Icon, Spinner } from "../common/icon";
|
||||||
import { tippyMixin } from "../mixins/tippy-mixin";
|
import { tippyMixin } from "../mixins/tippy-mixin";
|
||||||
|
import classNames from "classnames";
|
||||||
|
|
||||||
interface VoteButtonsProps {
|
interface VoteButtonsProps {
|
||||||
voteContentType: VoteContentType;
|
voteContentType: VoteContentType;
|
||||||
|
@ -19,7 +20,8 @@ interface VoteButtonsProps {
|
||||||
onVote: (i: CreateCommentLike | CreatePostLike) => void;
|
onVote: (i: CreateCommentLike | CreatePostLike) => void;
|
||||||
enableDownvotes?: boolean;
|
enableDownvotes?: boolean;
|
||||||
counts: CommentAggregates | PostAggregates;
|
counts: CommentAggregates | PostAggregates;
|
||||||
my_vote?: number;
|
voteDisplayMode: LocalUserVoteDisplayMode;
|
||||||
|
myVote?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface VoteButtonsState {
|
interface VoteButtonsState {
|
||||||
|
@ -27,61 +29,81 @@ interface VoteButtonsState {
|
||||||
downvoteLoading: boolean;
|
downvoteLoading: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tippy = (counts: CommentAggregates | PostAggregates): string => {
|
function tippy(
|
||||||
const points = I18NextService.i18n.t("number_of_points", {
|
voteDisplayMode: LocalUserVoteDisplayMode,
|
||||||
|
counts: CommentAggregates | PostAggregates,
|
||||||
|
): string {
|
||||||
|
const scoreStr =
|
||||||
|
voteDisplayMode.score &&
|
||||||
|
I18NextService.i18n.t("number_of_points", {
|
||||||
count: Number(counts.score),
|
count: Number(counts.score),
|
||||||
formattedCount: Number(counts.score),
|
formattedCount: Number(counts.score),
|
||||||
});
|
});
|
||||||
|
|
||||||
const upvotes = I18NextService.i18n.t("number_of_upvotes", {
|
const pct = calculateUpvotePct(counts.upvotes, counts.downvotes);
|
||||||
|
|
||||||
|
const upvotePctStr =
|
||||||
|
voteDisplayMode.upvote_percentage &&
|
||||||
|
I18NextService.i18n.t("upvote_percentage", {
|
||||||
|
count: Number(pct),
|
||||||
|
formattedCount: Number(pct),
|
||||||
|
});
|
||||||
|
|
||||||
|
const upvoteStr =
|
||||||
|
voteDisplayMode.upvotes &&
|
||||||
|
I18NextService.i18n.t("number_of_upvotes", {
|
||||||
count: Number(counts.upvotes),
|
count: Number(counts.upvotes),
|
||||||
formattedCount: Number(counts.upvotes),
|
formattedCount: Number(counts.upvotes),
|
||||||
});
|
});
|
||||||
|
|
||||||
const downvotes = I18NextService.i18n.t("number_of_downvotes", {
|
const downvoteStr =
|
||||||
|
voteDisplayMode.downvotes &&
|
||||||
|
I18NextService.i18n.t("number_of_downvotes", {
|
||||||
count: Number(counts.downvotes),
|
count: Number(counts.downvotes),
|
||||||
formattedCount: Number(counts.downvotes),
|
formattedCount: Number(counts.downvotes),
|
||||||
});
|
});
|
||||||
|
|
||||||
return `${points} • ${upvotes} • ${downvotes}`;
|
return [scoreStr, upvotePctStr, upvoteStr, downvoteStr]
|
||||||
};
|
.filter(Boolean)
|
||||||
|
.join(" · ");
|
||||||
|
}
|
||||||
|
|
||||||
const handleUpvote = (i: VoteButtons) => {
|
function handleUpvote(i: VoteButtons | VoteButtonsCompact) {
|
||||||
i.setState({ upvoteLoading: true });
|
i.setState({ upvoteLoading: true });
|
||||||
|
|
||||||
switch (i.props.voteContentType) {
|
switch (i.props.voteContentType) {
|
||||||
case VoteContentType.Comment:
|
case VoteContentType.Comment:
|
||||||
i.props.onVote({
|
i.props.onVote({
|
||||||
comment_id: i.props.id,
|
comment_id: i.props.id,
|
||||||
score: newVote(VoteType.Upvote, i.props.my_vote),
|
score: newVote(VoteType.Upvote, i.props.myVote),
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case VoteContentType.Post:
|
case VoteContentType.Post:
|
||||||
default:
|
default:
|
||||||
i.props.onVote({
|
i.props.onVote({
|
||||||
post_id: i.props.id,
|
post_id: i.props.id,
|
||||||
score: newVote(VoteType.Upvote, i.props.my_vote),
|
score: newVote(VoteType.Upvote, i.props.myVote),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleDownvote = (i: VoteButtons) => {
|
function handleDownvote(i: VoteButtons | VoteButtonsCompact) {
|
||||||
i.setState({ downvoteLoading: true });
|
i.setState({ downvoteLoading: true });
|
||||||
switch (i.props.voteContentType) {
|
switch (i.props.voteContentType) {
|
||||||
case VoteContentType.Comment:
|
case VoteContentType.Comment:
|
||||||
i.props.onVote({
|
i.props.onVote({
|
||||||
comment_id: i.props.id,
|
comment_id: i.props.id,
|
||||||
score: newVote(VoteType.Downvote, i.props.my_vote),
|
score: newVote(VoteType.Downvote, i.props.myVote),
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case VoteContentType.Post:
|
case VoteContentType.Post:
|
||||||
default:
|
default:
|
||||||
i.props.onVote({
|
i.props.onVote({
|
||||||
post_id: i.props.id,
|
post_id: i.props.id,
|
||||||
score: newVote(VoteType.Downvote, i.props.my_vote),
|
score: newVote(VoteType.Downvote, i.props.myVote),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
@tippyMixin
|
@tippyMixin
|
||||||
export class VoteButtonsCompact extends Component<
|
export class VoteButtonsCompact extends Component<
|
||||||
|
@ -114,20 +136,24 @@ export class VoteButtonsCompact extends Component<
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className={`btn btn-animate btn-sm btn-link py-0 px-1 ${
|
className={`btn btn-animate btn-sm btn-link py-0 px-1 ${
|
||||||
this.props.my_vote === 1 ? "text-info" : "text-muted"
|
this.props.myVote === 1 ? "text-info" : "text-muted"
|
||||||
}`}
|
}`}
|
||||||
data-tippy-content={tippy(this.props.counts)}
|
data-tippy-content={tippy(
|
||||||
|
this.props.voteDisplayMode,
|
||||||
|
this.props.counts,
|
||||||
|
)}
|
||||||
disabled={!UserService.Instance.myUserInfo}
|
disabled={!UserService.Instance.myUserInfo}
|
||||||
onClick={linkEvent(this, handleUpvote)}
|
onClick={linkEvent(this, handleUpvote)}
|
||||||
aria-label={I18NextService.i18n.t("upvote")}
|
aria-label={I18NextService.i18n.t("upvote")}
|
||||||
aria-pressed={this.props.my_vote === 1}
|
aria-pressed={this.props.myVote === 1}
|
||||||
>
|
>
|
||||||
{this.state.upvoteLoading ? (
|
{this.state.upvoteLoading ? (
|
||||||
<Spinner />
|
<Spinner />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Icon icon="arrow-up1" classes="icon-inline small" />
|
<Icon icon="arrow-up1" classes="icon-inline small" />
|
||||||
{showScores() && (
|
{showScores() &&
|
||||||
|
this.props.voteContentType === VoteContentType.Post && (
|
||||||
<span className="ms-2">
|
<span className="ms-2">
|
||||||
{numToSI(this.props.counts.upvotes)}
|
{numToSI(this.props.counts.upvotes)}
|
||||||
</span>
|
</span>
|
||||||
|
@ -139,20 +165,24 @@ export class VoteButtonsCompact extends Component<
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className={`ms-2 btn btn-sm btn-link btn-animate btn py-0 px-1 ${
|
className={`ms-2 btn btn-sm btn-link btn-animate btn py-0 px-1 ${
|
||||||
this.props.my_vote === -1 ? "text-danger" : "text-muted"
|
this.props.myVote === -1 ? "text-danger" : "text-muted"
|
||||||
}`}
|
}`}
|
||||||
disabled={!UserService.Instance.myUserInfo}
|
disabled={!UserService.Instance.myUserInfo}
|
||||||
onClick={linkEvent(this, handleDownvote)}
|
onClick={linkEvent(this, handleDownvote)}
|
||||||
data-tippy-content={tippy(this.props.counts)}
|
data-tippy-content={tippy(
|
||||||
|
this.props.voteDisplayMode,
|
||||||
|
this.props.counts,
|
||||||
|
)}
|
||||||
aria-label={I18NextService.i18n.t("downvote")}
|
aria-label={I18NextService.i18n.t("downvote")}
|
||||||
aria-pressed={this.props.my_vote === -1}
|
aria-pressed={this.props.myVote === -1}
|
||||||
>
|
>
|
||||||
{this.state.downvoteLoading ? (
|
{this.state.downvoteLoading ? (
|
||||||
<Spinner />
|
<Spinner />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Icon icon="arrow-down1" classes="icon-inline small" />
|
<Icon icon="arrow-down1" classes="icon-inline small" />
|
||||||
{showScores() && (
|
{showScores() &&
|
||||||
|
this.props.voteContentType === VoteContentType.Post && (
|
||||||
<span
|
<span
|
||||||
className={classNames("ms-2", {
|
className={classNames("ms-2", {
|
||||||
invisible: this.props.counts.downvotes === 0,
|
invisible: this.props.counts.downvotes === 0,
|
||||||
|
@ -198,13 +228,16 @@ export class VoteButtons extends Component<VoteButtonsProps, VoteButtonsState> {
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className={`btn-animate btn btn-link p-0 ${
|
className={`btn-animate btn btn-link p-0 ${
|
||||||
this.props.my_vote === 1 ? "text-info" : "text-muted"
|
this.props.myVote === 1 ? "text-info" : "text-muted"
|
||||||
}`}
|
}`}
|
||||||
disabled={!UserService.Instance.myUserInfo}
|
disabled={!UserService.Instance.myUserInfo}
|
||||||
onClick={linkEvent(this, handleUpvote)}
|
onClick={linkEvent(this, handleUpvote)}
|
||||||
data-tippy-content={I18NextService.i18n.t("upvote")}
|
data-tippy-content={tippy(
|
||||||
|
this.props.voteDisplayMode,
|
||||||
|
this.props.counts,
|
||||||
|
)}
|
||||||
aria-label={I18NextService.i18n.t("upvote")}
|
aria-label={I18NextService.i18n.t("upvote")}
|
||||||
aria-pressed={this.props.my_vote === 1}
|
aria-pressed={this.props.myVote === 1}
|
||||||
>
|
>
|
||||||
{this.state.upvoteLoading ? (
|
{this.state.upvoteLoading ? (
|
||||||
<Spinner />
|
<Spinner />
|
||||||
|
@ -215,7 +248,10 @@ export class VoteButtons extends Component<VoteButtonsProps, VoteButtonsState> {
|
||||||
{showScores() ? (
|
{showScores() ? (
|
||||||
<div
|
<div
|
||||||
className="unselectable pointer text-muted post-score"
|
className="unselectable pointer text-muted post-score"
|
||||||
data-tippy-content={tippy(this.props.counts)}
|
data-tippy-content={tippy(
|
||||||
|
this.props.voteDisplayMode,
|
||||||
|
this.props.counts,
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
{numToSI(this.props.counts.score)}
|
{numToSI(this.props.counts.score)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -226,13 +262,16 @@ export class VoteButtons extends Component<VoteButtonsProps, VoteButtonsState> {
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className={`btn-animate btn btn-link p-0 ${
|
className={`btn-animate btn btn-link p-0 ${
|
||||||
this.props.my_vote === -1 ? "text-danger" : "text-muted"
|
this.props.myVote === -1 ? "text-danger" : "text-muted"
|
||||||
}`}
|
}`}
|
||||||
disabled={!UserService.Instance.myUserInfo}
|
disabled={!UserService.Instance.myUserInfo}
|
||||||
onClick={linkEvent(this, handleDownvote)}
|
onClick={linkEvent(this, handleDownvote)}
|
||||||
data-tippy-content={I18NextService.i18n.t("downvote")}
|
data-tippy-content={tippy(
|
||||||
|
this.props.voteDisplayMode,
|
||||||
|
this.props.counts,
|
||||||
|
)}
|
||||||
aria-label={I18NextService.i18n.t("downvote")}
|
aria-label={I18NextService.i18n.t("downvote")}
|
||||||
aria-pressed={this.props.my_vote === -1}
|
aria-pressed={this.props.myVote === -1}
|
||||||
>
|
>
|
||||||
{this.state.downvoteLoading ? (
|
{this.state.downvoteLoading ? (
|
||||||
<Spinner />
|
<Spinner />
|
||||||
|
|
184
src/shared/components/common/vote-display.tsx
Normal file
184
src/shared/components/common/vote-display.tsx
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
import { numToSI } from "@utils/helpers";
|
||||||
|
import { Component } from "inferno";
|
||||||
|
import {
|
||||||
|
CommentAggregates,
|
||||||
|
LocalUserVoteDisplayMode,
|
||||||
|
PostAggregates,
|
||||||
|
} from "lemmy-js-client";
|
||||||
|
import { I18NextService } from "../../services";
|
||||||
|
import { tippyMixin } from "../mixins/tippy-mixin";
|
||||||
|
import { Icon } from "./icon";
|
||||||
|
import classNames from "classnames";
|
||||||
|
import { calculateUpvotePct } from "@utils/app";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
voteDisplayMode: LocalUserVoteDisplayMode;
|
||||||
|
counts: CommentAggregates | PostAggregates;
|
||||||
|
myVote?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BADGE_CLASSES = "unselectable";
|
||||||
|
const UPVOTE_PCT_THRESHOLD = 90;
|
||||||
|
|
||||||
|
@tippyMixin
|
||||||
|
export class VoteDisplay extends Component<Props, any> {
|
||||||
|
constructor(props: any, context: any) {
|
||||||
|
super(props, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
voteDisplayMode,
|
||||||
|
counts: { score, upvotes },
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
// If the score is the same as the upvotes,
|
||||||
|
// and both score and upvotes are enabled,
|
||||||
|
// only show the upvotes.
|
||||||
|
const hideScore =
|
||||||
|
voteDisplayMode.score && voteDisplayMode.upvotes && score === upvotes;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{voteDisplayMode.score && !hideScore && this.score()}
|
||||||
|
{voteDisplayMode.upvote_percentage && this.upvotePct()}
|
||||||
|
{this.upvotesAndDownvotes()}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
score() {
|
||||||
|
const {
|
||||||
|
myVote,
|
||||||
|
counts: { score },
|
||||||
|
} = this.props;
|
||||||
|
const scoreStr = numToSI(score);
|
||||||
|
|
||||||
|
const scoreTippy = I18NextService.i18n.t("number_of_points", {
|
||||||
|
count: Number(score),
|
||||||
|
formattedCount: scoreStr,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
className={`${BADGE_CLASSES} ${scoreColor(myVote)}`}
|
||||||
|
aria-label={scoreTippy}
|
||||||
|
data-tippy-content={scoreTippy}
|
||||||
|
>
|
||||||
|
<Icon icon="heart" classes="me-1 icon-inline small" />
|
||||||
|
{scoreStr}
|
||||||
|
<span className="mx-2">·</span>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
upvotePct() {
|
||||||
|
const { upvotes, downvotes } = this.props.counts;
|
||||||
|
const pct = calculateUpvotePct(upvotes, downvotes);
|
||||||
|
const pctStr = `${pct.toFixed(0)}%`;
|
||||||
|
|
||||||
|
const thresholdCheck = pct < UPVOTE_PCT_THRESHOLD;
|
||||||
|
|
||||||
|
const upvotesPctTippy = I18NextService.i18n.t("upvote_percentage", {
|
||||||
|
count: Number(pct),
|
||||||
|
formattedCount: Number(pct),
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
thresholdCheck && (
|
||||||
|
<span
|
||||||
|
className={BADGE_CLASSES}
|
||||||
|
aria-label={upvotesPctTippy}
|
||||||
|
data-tippy-content={upvotesPctTippy}
|
||||||
|
>
|
||||||
|
<Icon icon="smile" classes="me-1 icon-inline small" />
|
||||||
|
{pctStr}
|
||||||
|
<span className="mx-2">·</span>
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// A special case since they are both wrapped in a badge
|
||||||
|
upvotesAndDownvotes() {
|
||||||
|
const voteDisplayMode = this.props.voteDisplayMode;
|
||||||
|
const votesCheck = voteDisplayMode.upvotes || voteDisplayMode.downvotes;
|
||||||
|
const downvotesCheck = this.props.counts.downvotes > 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
votesCheck && (
|
||||||
|
<span className={BADGE_CLASSES}>
|
||||||
|
{voteDisplayMode.upvotes && (
|
||||||
|
<span className={classNames({ "me-1": downvotesCheck })}>
|
||||||
|
{this.upvotes()}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{voteDisplayMode.downvotes && downvotesCheck && this.downvotes()}
|
||||||
|
<span className="mx-2">·</span>
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
upvotes() {
|
||||||
|
const {
|
||||||
|
myVote,
|
||||||
|
counts: { upvotes },
|
||||||
|
} = this.props;
|
||||||
|
const upvotesStr = numToSI(upvotes);
|
||||||
|
|
||||||
|
const upvotesTippy = I18NextService.i18n.t("number_of_upvotes", {
|
||||||
|
count: Number(upvotes),
|
||||||
|
formattedCount: upvotesStr,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
className={classNames({
|
||||||
|
"text-info": myVote === 1,
|
||||||
|
})}
|
||||||
|
aria-label={upvotesTippy}
|
||||||
|
data-tippy-content={upvotesTippy}
|
||||||
|
>
|
||||||
|
<Icon icon="arrow-up" classes="me-1 icon-inline small" />
|
||||||
|
{upvotesStr}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
downvotes() {
|
||||||
|
const {
|
||||||
|
myVote,
|
||||||
|
counts: { downvotes },
|
||||||
|
} = this.props;
|
||||||
|
const downvotesStr = numToSI(downvotes);
|
||||||
|
|
||||||
|
const downvotesTippy = I18NextService.i18n.t("number_of_downvotes", {
|
||||||
|
count: Number(downvotes),
|
||||||
|
formattedCount: downvotesStr,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
className={classNames({
|
||||||
|
"text-danger": myVote === -1,
|
||||||
|
})}
|
||||||
|
aria-label={downvotesTippy}
|
||||||
|
data-tippy-content={downvotesTippy}
|
||||||
|
>
|
||||||
|
<Icon icon="arrow-down" classes="me-1 icon-inline small" />
|
||||||
|
{downvotesStr}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function scoreColor(myVote?: number): string {
|
||||||
|
if (myVote === 1) {
|
||||||
|
return "text-info";
|
||||||
|
} else if (myVote === -1) {
|
||||||
|
return "text-danger";
|
||||||
|
} else {
|
||||||
|
return "text-muted";
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,6 +13,7 @@ import {
|
||||||
showLocal,
|
showLocal,
|
||||||
updateCommunityBlock,
|
updateCommunityBlock,
|
||||||
updatePersonBlock,
|
updatePersonBlock,
|
||||||
|
voteDisplayMode,
|
||||||
} from "@utils/app";
|
} from "@utils/app";
|
||||||
import {
|
import {
|
||||||
getQueryParams,
|
getQueryParams,
|
||||||
|
@ -422,11 +423,11 @@ export class Community extends Component<CommunityRouteProps, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
sidebar(res: GetCommunityResponse) {
|
sidebar(res: GetCommunityResponse) {
|
||||||
const { site_res } = this.isoData;
|
const siteRes = this.isoData.site_res;
|
||||||
// For some reason, this returns an empty vec if it matches the site langs
|
// For some reason, this returns an empty vec if it matches the site langs
|
||||||
const communityLangs =
|
const communityLangs =
|
||||||
res.discussion_languages.length === 0
|
res.discussion_languages.length === 0
|
||||||
? site_res.all_languages.map(({ id }) => id)
|
? siteRes.all_languages.map(({ id }) => id)
|
||||||
: res.discussion_languages;
|
: res.discussion_languages;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -434,11 +435,11 @@ export class Community extends Component<CommunityRouteProps, State> {
|
||||||
<Sidebar
|
<Sidebar
|
||||||
community_view={res.community_view}
|
community_view={res.community_view}
|
||||||
moderators={res.moderators}
|
moderators={res.moderators}
|
||||||
admins={site_res.admins}
|
admins={siteRes.admins}
|
||||||
enableNsfw={enableNsfw(site_res)}
|
enableNsfw={enableNsfw(siteRes)}
|
||||||
editable
|
editable
|
||||||
allLanguages={site_res.all_languages}
|
allLanguages={siteRes.all_languages}
|
||||||
siteLanguages={site_res.discussion_languages}
|
siteLanguages={siteRes.discussion_languages}
|
||||||
communityLanguages={communityLangs}
|
communityLanguages={communityLangs}
|
||||||
onDeleteCommunity={this.handleDeleteCommunity}
|
onDeleteCommunity={this.handleDeleteCommunity}
|
||||||
onRemoveCommunity={this.handleRemoveCommunity}
|
onRemoveCommunity={this.handleRemoveCommunity}
|
||||||
|
@ -457,7 +458,7 @@ export class Community extends Component<CommunityRouteProps, State> {
|
||||||
|
|
||||||
listings(communityRes: GetCommunityResponse) {
|
listings(communityRes: GetCommunityResponse) {
|
||||||
const { dataType } = this.props;
|
const { dataType } = this.props;
|
||||||
const { site_res } = this.isoData;
|
const siteRes = this.isoData.site_res;
|
||||||
|
|
||||||
if (dataType === DataType.Post) {
|
if (dataType === DataType.Post) {
|
||||||
switch (this.state.postsRes.state) {
|
switch (this.state.postsRes.state) {
|
||||||
|
@ -468,10 +469,11 @@ export class Community extends Component<CommunityRouteProps, State> {
|
||||||
<PostListings
|
<PostListings
|
||||||
posts={this.state.postsRes.data.posts}
|
posts={this.state.postsRes.data.posts}
|
||||||
removeDuplicates
|
removeDuplicates
|
||||||
enableDownvotes={enableDownvotes(site_res)}
|
enableDownvotes={enableDownvotes(siteRes)}
|
||||||
enableNsfw={enableNsfw(site_res)}
|
voteDisplayMode={voteDisplayMode(siteRes)}
|
||||||
allLanguages={site_res.all_languages}
|
enableNsfw={enableNsfw(siteRes)}
|
||||||
siteLanguages={site_res.discussion_languages}
|
allLanguages={siteRes.all_languages}
|
||||||
|
siteLanguages={siteRes.discussion_languages}
|
||||||
onBlockPerson={this.handleBlockPerson}
|
onBlockPerson={this.handleBlockPerson}
|
||||||
onPostEdit={this.handlePostEdit}
|
onPostEdit={this.handlePostEdit}
|
||||||
onPostVote={this.handlePostVote}
|
onPostVote={this.handlePostVote}
|
||||||
|
@ -505,11 +507,12 @@ export class Community extends Component<CommunityRouteProps, State> {
|
||||||
finished={this.state.finished}
|
finished={this.state.finished}
|
||||||
isTopLevel
|
isTopLevel
|
||||||
showContext
|
showContext
|
||||||
enableDownvotes={enableDownvotes(site_res)}
|
enableDownvotes={enableDownvotes(siteRes)}
|
||||||
|
voteDisplayMode={voteDisplayMode(siteRes)}
|
||||||
moderators={communityRes.moderators}
|
moderators={communityRes.moderators}
|
||||||
admins={site_res.admins}
|
admins={siteRes.admins}
|
||||||
allLanguages={site_res.all_languages}
|
allLanguages={siteRes.all_languages}
|
||||||
siteLanguages={site_res.discussion_languages}
|
siteLanguages={siteRes.discussion_languages}
|
||||||
onSaveComment={this.handleSaveComment}
|
onSaveComment={this.handleSaveComment}
|
||||||
onBlockPerson={this.handleBlockPerson}
|
onBlockPerson={this.handleBlockPerson}
|
||||||
onDeleteComment={this.handleDeleteComment}
|
onDeleteComment={this.handleDeleteComment}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import {
|
||||||
setIsoData,
|
setIsoData,
|
||||||
showLocal,
|
showLocal,
|
||||||
updatePersonBlock,
|
updatePersonBlock,
|
||||||
|
voteDisplayMode,
|
||||||
} from "@utils/app";
|
} from "@utils/app";
|
||||||
import {
|
import {
|
||||||
getQueryParams,
|
getQueryParams,
|
||||||
|
@ -729,6 +730,7 @@ export class Home extends Component<HomeRouteProps, HomeState> {
|
||||||
showCommunity
|
showCommunity
|
||||||
removeDuplicates
|
removeDuplicates
|
||||||
enableDownvotes={enableDownvotes(siteRes)}
|
enableDownvotes={enableDownvotes(siteRes)}
|
||||||
|
voteDisplayMode={voteDisplayMode(siteRes)}
|
||||||
enableNsfw={enableNsfw(siteRes)}
|
enableNsfw={enableNsfw(siteRes)}
|
||||||
allLanguages={siteRes.all_languages}
|
allLanguages={siteRes.all_languages}
|
||||||
siteLanguages={siteRes.discussion_languages}
|
siteLanguages={siteRes.discussion_languages}
|
||||||
|
@ -769,6 +771,7 @@ export class Home extends Component<HomeRouteProps, HomeState> {
|
||||||
showCommunity
|
showCommunity
|
||||||
showContext
|
showContext
|
||||||
enableDownvotes={enableDownvotes(siteRes)}
|
enableDownvotes={enableDownvotes(siteRes)}
|
||||||
|
voteDisplayMode={voteDisplayMode(siteRes)}
|
||||||
allLanguages={siteRes.all_languages}
|
allLanguages={siteRes.all_languages}
|
||||||
siteLanguages={siteRes.discussion_languages}
|
siteLanguages={siteRes.discussion_languages}
|
||||||
onSaveComment={this.handleSaveComment}
|
onSaveComment={this.handleSaveComment}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {
|
||||||
myAuth,
|
myAuth,
|
||||||
setIsoData,
|
setIsoData,
|
||||||
updatePersonBlock,
|
updatePersonBlock,
|
||||||
|
voteDisplayMode,
|
||||||
} from "@utils/app";
|
} from "@utils/app";
|
||||||
import {
|
import {
|
||||||
capitalizeFirstLetter,
|
capitalizeFirstLetter,
|
||||||
|
@ -500,6 +501,7 @@ export class Inbox extends Component<InboxRouteProps, InboxState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
renderReplyType(i: ReplyType) {
|
renderReplyType(i: ReplyType) {
|
||||||
|
const siteRes = this.state.siteRes;
|
||||||
switch (i.type_) {
|
switch (i.type_) {
|
||||||
case ReplyEnum.Reply:
|
case ReplyEnum.Reply:
|
||||||
return (
|
return (
|
||||||
|
@ -513,9 +515,10 @@ export class Inbox extends Component<InboxRouteProps, InboxState> {
|
||||||
markable
|
markable
|
||||||
showCommunity
|
showCommunity
|
||||||
showContext
|
showContext
|
||||||
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
enableDownvotes={enableDownvotes(siteRes)}
|
||||||
allLanguages={this.state.siteRes.all_languages}
|
voteDisplayMode={voteDisplayMode(siteRes)}
|
||||||
siteLanguages={this.state.siteRes.discussion_languages}
|
allLanguages={siteRes.all_languages}
|
||||||
|
siteLanguages={siteRes.discussion_languages}
|
||||||
onSaveComment={this.handleSaveComment}
|
onSaveComment={this.handleSaveComment}
|
||||||
onBlockPerson={this.handleBlockPerson}
|
onBlockPerson={this.handleBlockPerson}
|
||||||
onDeleteComment={this.handleDeleteComment}
|
onDeleteComment={this.handleDeleteComment}
|
||||||
|
@ -552,9 +555,10 @@ export class Inbox extends Component<InboxRouteProps, InboxState> {
|
||||||
markable
|
markable
|
||||||
showCommunity
|
showCommunity
|
||||||
showContext
|
showContext
|
||||||
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
enableDownvotes={enableDownvotes(siteRes)}
|
||||||
allLanguages={this.state.siteRes.all_languages}
|
voteDisplayMode={voteDisplayMode(siteRes)}
|
||||||
siteLanguages={this.state.siteRes.discussion_languages}
|
allLanguages={siteRes.all_languages}
|
||||||
|
siteLanguages={siteRes.discussion_languages}
|
||||||
onSaveComment={this.handleSaveComment}
|
onSaveComment={this.handleSaveComment}
|
||||||
onBlockPerson={this.handleBlockPerson}
|
onBlockPerson={this.handleBlockPerson}
|
||||||
onDeleteComment={this.handleDeleteComment}
|
onDeleteComment={this.handleDeleteComment}
|
||||||
|
@ -607,6 +611,7 @@ export class Inbox extends Component<InboxRouteProps, InboxState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
replies() {
|
replies() {
|
||||||
|
const siteRes = this.state.siteRes;
|
||||||
switch (this.state.repliesRes.state) {
|
switch (this.state.repliesRes.state) {
|
||||||
case "loading":
|
case "loading":
|
||||||
return <CommentsLoadingSkeleton />;
|
return <CommentsLoadingSkeleton />;
|
||||||
|
@ -621,9 +626,10 @@ export class Inbox extends Component<InboxRouteProps, InboxState> {
|
||||||
markable
|
markable
|
||||||
showCommunity
|
showCommunity
|
||||||
showContext
|
showContext
|
||||||
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
enableDownvotes={enableDownvotes(siteRes)}
|
||||||
allLanguages={this.state.siteRes.all_languages}
|
voteDisplayMode={voteDisplayMode(siteRes)}
|
||||||
siteLanguages={this.state.siteRes.discussion_languages}
|
allLanguages={siteRes.all_languages}
|
||||||
|
siteLanguages={siteRes.discussion_languages}
|
||||||
onSaveComment={this.handleSaveComment}
|
onSaveComment={this.handleSaveComment}
|
||||||
onBlockPerson={this.handleBlockPerson}
|
onBlockPerson={this.handleBlockPerson}
|
||||||
onDeleteComment={this.handleDeleteComment}
|
onDeleteComment={this.handleDeleteComment}
|
||||||
|
@ -650,6 +656,7 @@ export class Inbox extends Component<InboxRouteProps, InboxState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
mentions() {
|
mentions() {
|
||||||
|
const siteRes = this.state.siteRes;
|
||||||
switch (this.state.mentionsRes.state) {
|
switch (this.state.mentionsRes.state) {
|
||||||
case "loading":
|
case "loading":
|
||||||
return <CommentsLoadingSkeleton />;
|
return <CommentsLoadingSkeleton />;
|
||||||
|
@ -666,9 +673,10 @@ export class Inbox extends Component<InboxRouteProps, InboxState> {
|
||||||
markable
|
markable
|
||||||
showCommunity
|
showCommunity
|
||||||
showContext
|
showContext
|
||||||
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
enableDownvotes={enableDownvotes(siteRes)}
|
||||||
allLanguages={this.state.siteRes.all_languages}
|
voteDisplayMode={voteDisplayMode(siteRes)}
|
||||||
siteLanguages={this.state.siteRes.discussion_languages}
|
allLanguages={siteRes.all_languages}
|
||||||
|
siteLanguages={siteRes.discussion_languages}
|
||||||
onSaveComment={this.handleSaveComment}
|
onSaveComment={this.handleSaveComment}
|
||||||
onBlockPerson={this.handleBlockPerson}
|
onBlockPerson={this.handleBlockPerson}
|
||||||
onDeleteComment={this.handleDeleteComment}
|
onDeleteComment={this.handleDeleteComment}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import {
|
||||||
GetComments,
|
GetComments,
|
||||||
GetPersonDetailsResponse,
|
GetPersonDetailsResponse,
|
||||||
Language,
|
Language,
|
||||||
|
LocalUserVoteDisplayMode,
|
||||||
LockPost,
|
LockPost,
|
||||||
MarkCommentReplyAsRead,
|
MarkCommentReplyAsRead,
|
||||||
MarkPersonMentionAsRead,
|
MarkPersonMentionAsRead,
|
||||||
|
@ -56,6 +57,7 @@ interface PersonDetailsProps {
|
||||||
limit: number;
|
limit: number;
|
||||||
sort: SortType;
|
sort: SortType;
|
||||||
enableDownvotes: boolean;
|
enableDownvotes: boolean;
|
||||||
|
voteDisplayMode: LocalUserVoteDisplayMode;
|
||||||
enableNsfw: boolean;
|
enableNsfw: boolean;
|
||||||
view: PersonDetailsView;
|
view: PersonDetailsView;
|
||||||
onPageChange(page: number): number | any;
|
onPageChange(page: number): number | any;
|
||||||
|
@ -157,6 +159,7 @@ export class PersonDetails extends Component<PersonDetailsProps, any> {
|
||||||
showCommunity
|
showCommunity
|
||||||
showContext
|
showContext
|
||||||
enableDownvotes={this.props.enableDownvotes}
|
enableDownvotes={this.props.enableDownvotes}
|
||||||
|
voteDisplayMode={this.props.voteDisplayMode}
|
||||||
allLanguages={this.props.allLanguages}
|
allLanguages={this.props.allLanguages}
|
||||||
siteLanguages={this.props.siteLanguages}
|
siteLanguages={this.props.siteLanguages}
|
||||||
onCommentReplyRead={this.props.onCommentReplyRead}
|
onCommentReplyRead={this.props.onCommentReplyRead}
|
||||||
|
@ -190,6 +193,7 @@ export class PersonDetails extends Component<PersonDetailsProps, any> {
|
||||||
admins={this.props.admins}
|
admins={this.props.admins}
|
||||||
showCommunity
|
showCommunity
|
||||||
enableDownvotes={this.props.enableDownvotes}
|
enableDownvotes={this.props.enableDownvotes}
|
||||||
|
voteDisplayMode={this.props.voteDisplayMode}
|
||||||
enableNsfw={this.props.enableNsfw}
|
enableNsfw={this.props.enableNsfw}
|
||||||
allLanguages={this.props.allLanguages}
|
allLanguages={this.props.allLanguages}
|
||||||
siteLanguages={this.props.siteLanguages}
|
siteLanguages={this.props.siteLanguages}
|
||||||
|
@ -267,6 +271,7 @@ export class PersonDetails extends Component<PersonDetailsProps, any> {
|
||||||
showCommunity
|
showCommunity
|
||||||
showContext
|
showContext
|
||||||
enableDownvotes={this.props.enableDownvotes}
|
enableDownvotes={this.props.enableDownvotes}
|
||||||
|
voteDisplayMode={this.props.voteDisplayMode}
|
||||||
allLanguages={this.props.allLanguages}
|
allLanguages={this.props.allLanguages}
|
||||||
siteLanguages={this.props.siteLanguages}
|
siteLanguages={this.props.siteLanguages}
|
||||||
onCommentReplyRead={this.props.onCommentReplyRead}
|
onCommentReplyRead={this.props.onCommentReplyRead}
|
||||||
|
@ -303,6 +308,7 @@ export class PersonDetails extends Component<PersonDetailsProps, any> {
|
||||||
admins={this.props.admins}
|
admins={this.props.admins}
|
||||||
showCommunity
|
showCommunity
|
||||||
enableDownvotes={this.props.enableDownvotes}
|
enableDownvotes={this.props.enableDownvotes}
|
||||||
|
voteDisplayMode={this.props.voteDisplayMode}
|
||||||
enableNsfw={this.props.enableNsfw}
|
enableNsfw={this.props.enableNsfw}
|
||||||
allLanguages={this.props.allLanguages}
|
allLanguages={this.props.allLanguages}
|
||||||
siteLanguages={this.props.siteLanguages}
|
siteLanguages={this.props.siteLanguages}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {
|
||||||
getCommentParentId,
|
getCommentParentId,
|
||||||
setIsoData,
|
setIsoData,
|
||||||
updatePersonBlock,
|
updatePersonBlock,
|
||||||
|
voteDisplayMode,
|
||||||
} from "@utils/app";
|
} from "@utils/app";
|
||||||
import { scrollMixin } from "../mixins/scroll-mixin";
|
import { scrollMixin } from "../mixins/scroll-mixin";
|
||||||
import {
|
import {
|
||||||
|
@ -409,6 +410,7 @@ export class Profile extends Component<ProfileRouteProps, ProfileState> {
|
||||||
limit={fetchLimit}
|
limit={fetchLimit}
|
||||||
finished={this.state.finished}
|
finished={this.state.finished}
|
||||||
enableDownvotes={enableDownvotes(siteRes)}
|
enableDownvotes={enableDownvotes(siteRes)}
|
||||||
|
voteDisplayMode={voteDisplayMode(siteRes)}
|
||||||
enableNsfw={enableNsfw(siteRes)}
|
enableNsfw={enableNsfw(siteRes)}
|
||||||
view={view}
|
view={view}
|
||||||
onPageChange={this.handlePageChange}
|
onPageChange={this.handlePageChange}
|
||||||
|
|
|
@ -2,7 +2,10 @@ import {
|
||||||
editCommentReport,
|
editCommentReport,
|
||||||
editPostReport,
|
editPostReport,
|
||||||
editPrivateMessageReport,
|
editPrivateMessageReport,
|
||||||
|
enableDownvotes,
|
||||||
|
enableNsfw,
|
||||||
setIsoData,
|
setIsoData,
|
||||||
|
voteDisplayMode,
|
||||||
} from "@utils/app";
|
} from "@utils/app";
|
||||||
import { randomStr, resourcesSettled } from "@utils/helpers";
|
import { randomStr, resourcesSettled } from "@utils/helpers";
|
||||||
import { scrollMixin } from "../mixins/scroll-mixin";
|
import { scrollMixin } from "../mixins/scroll-mixin";
|
||||||
|
@ -412,12 +415,15 @@ export class Reports extends Component<ReportsRouteProps, ReportsState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
renderItemType(i: ItemType) {
|
renderItemType(i: ItemType) {
|
||||||
|
const siteRes = this.state.siteRes;
|
||||||
switch (i.type_) {
|
switch (i.type_) {
|
||||||
case MessageEnum.CommentReport:
|
case MessageEnum.CommentReport:
|
||||||
return (
|
return (
|
||||||
<CommentReport
|
<CommentReport
|
||||||
key={i.id}
|
key={i.id}
|
||||||
report={i.view as CommentReportView}
|
report={i.view as CommentReportView}
|
||||||
|
enableDownvotes={enableDownvotes(siteRes)}
|
||||||
|
voteDisplayMode={voteDisplayMode(siteRes)}
|
||||||
onResolveReport={this.handleResolveCommentReport}
|
onResolveReport={this.handleResolveCommentReport}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -426,6 +432,9 @@ export class Reports extends Component<ReportsRouteProps, ReportsState> {
|
||||||
<PostReport
|
<PostReport
|
||||||
key={i.id}
|
key={i.id}
|
||||||
report={i.view as PostReportView}
|
report={i.view as PostReportView}
|
||||||
|
enableDownvotes={enableDownvotes(siteRes)}
|
||||||
|
voteDisplayMode={voteDisplayMode(siteRes)}
|
||||||
|
enableNsfw={enableNsfw(siteRes)}
|
||||||
onResolveReport={this.handleResolvePostReport}
|
onResolveReport={this.handleResolvePostReport}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -457,6 +466,7 @@ export class Reports extends Component<ReportsRouteProps, ReportsState> {
|
||||||
|
|
||||||
commentReports() {
|
commentReports() {
|
||||||
const res = this.state.commentReportsRes;
|
const res = this.state.commentReportsRes;
|
||||||
|
const siteRes = this.state.siteRes;
|
||||||
switch (res.state) {
|
switch (res.state) {
|
||||||
case "loading":
|
case "loading":
|
||||||
return (
|
return (
|
||||||
|
@ -474,6 +484,8 @@ export class Reports extends Component<ReportsRouteProps, ReportsState> {
|
||||||
<CommentReport
|
<CommentReport
|
||||||
key={cr.comment_report.id}
|
key={cr.comment_report.id}
|
||||||
report={cr}
|
report={cr}
|
||||||
|
enableDownvotes={enableDownvotes(siteRes)}
|
||||||
|
voteDisplayMode={voteDisplayMode(siteRes)}
|
||||||
onResolveReport={this.handleResolveCommentReport}
|
onResolveReport={this.handleResolveCommentReport}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
@ -486,6 +498,7 @@ export class Reports extends Component<ReportsRouteProps, ReportsState> {
|
||||||
|
|
||||||
postReports() {
|
postReports() {
|
||||||
const res = this.state.postReportsRes;
|
const res = this.state.postReportsRes;
|
||||||
|
const siteRes = this.state.siteRes;
|
||||||
switch (res.state) {
|
switch (res.state) {
|
||||||
case "loading":
|
case "loading":
|
||||||
return (
|
return (
|
||||||
|
@ -502,6 +515,9 @@ export class Reports extends Component<ReportsRouteProps, ReportsState> {
|
||||||
<hr />
|
<hr />
|
||||||
<PostReport
|
<PostReport
|
||||||
key={pr.post_report.id}
|
key={pr.post_report.id}
|
||||||
|
enableDownvotes={enableDownvotes(siteRes)}
|
||||||
|
voteDisplayMode={voteDisplayMode(siteRes)}
|
||||||
|
enableNsfw={enableNsfw(siteRes)}
|
||||||
report={pr}
|
report={pr}
|
||||||
onResolveReport={this.handleResolvePostReport}
|
onResolveReport={this.handleResolvePostReport}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import {
|
import {
|
||||||
communityToChoice,
|
communityToChoice,
|
||||||
|
enableDownvotes,
|
||||||
fetchCommunities,
|
fetchCommunities,
|
||||||
fetchThemeList,
|
fetchThemeList,
|
||||||
fetchUsers,
|
fetchUsers,
|
||||||
|
@ -100,6 +101,9 @@ interface SettingsState {
|
||||||
matrix_user_id?: string;
|
matrix_user_id?: string;
|
||||||
show_avatars?: boolean;
|
show_avatars?: boolean;
|
||||||
show_scores?: boolean;
|
show_scores?: boolean;
|
||||||
|
show_upvotes?: boolean;
|
||||||
|
show_downvotes?: boolean;
|
||||||
|
show_upvote_percentage?: boolean;
|
||||||
send_notifications_to_email?: boolean;
|
send_notifications_to_email?: boolean;
|
||||||
bot_account?: boolean;
|
bot_account?: boolean;
|
||||||
show_bot_accounts?: boolean;
|
show_bot_accounts?: boolean;
|
||||||
|
@ -276,7 +280,6 @@ export class Settings extends Component<SettingsRouteProps, SettingsState> {
|
||||||
interface_language,
|
interface_language,
|
||||||
show_avatars,
|
show_avatars,
|
||||||
show_bot_accounts,
|
show_bot_accounts,
|
||||||
show_scores,
|
|
||||||
show_read_posts,
|
show_read_posts,
|
||||||
send_notifications_to_email,
|
send_notifications_to_email,
|
||||||
email,
|
email,
|
||||||
|
@ -290,6 +293,12 @@ export class Settings extends Component<SettingsRouteProps, SettingsState> {
|
||||||
bio,
|
bio,
|
||||||
matrix_user_id,
|
matrix_user_id,
|
||||||
},
|
},
|
||||||
|
local_user_vote_display_mode: {
|
||||||
|
score: show_scores,
|
||||||
|
upvotes: show_upvotes,
|
||||||
|
downvotes: show_downvotes,
|
||||||
|
upvote_percentage: show_upvote_percentage,
|
||||||
|
},
|
||||||
} = mui.local_user_view;
|
} = mui.local_user_view;
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
|
@ -314,6 +323,9 @@ export class Settings extends Component<SettingsRouteProps, SettingsState> {
|
||||||
bot_account,
|
bot_account,
|
||||||
show_bot_accounts,
|
show_bot_accounts,
|
||||||
show_scores,
|
show_scores,
|
||||||
|
show_upvotes,
|
||||||
|
show_downvotes,
|
||||||
|
show_upvote_percentage,
|
||||||
show_read_posts,
|
show_read_posts,
|
||||||
email,
|
email,
|
||||||
bio,
|
bio,
|
||||||
|
@ -703,6 +715,7 @@ export class Settings extends Component<SettingsRouteProps, SettingsState> {
|
||||||
|
|
||||||
saveUserSettingsHtmlForm() {
|
saveUserSettingsHtmlForm() {
|
||||||
const selectedLangs = this.state.saveUserSettingsForm.discussion_languages;
|
const selectedLangs = this.state.saveUserSettingsForm.discussion_languages;
|
||||||
|
const siteRes = this.state.siteRes;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -735,8 +748,8 @@ export class Settings extends Component<SettingsRouteProps, SettingsState> {
|
||||||
onContentChange={this.handleBioChange}
|
onContentChange={this.handleBioChange}
|
||||||
maxLength={300}
|
maxLength={300}
|
||||||
hideNavigationWarnings
|
hideNavigationWarnings
|
||||||
allLanguages={this.state.siteRes.all_languages}
|
allLanguages={siteRes.all_languages}
|
||||||
siteLanguages={this.state.siteRes.discussion_languages}
|
siteLanguages={siteRes.discussion_languages}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -832,8 +845,8 @@ export class Settings extends Component<SettingsRouteProps, SettingsState> {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<LanguageSelect
|
<LanguageSelect
|
||||||
allLanguages={this.state.siteRes.all_languages}
|
allLanguages={siteRes.all_languages}
|
||||||
siteLanguages={this.state.siteRes.discussion_languages}
|
siteLanguages={siteRes.discussion_languages}
|
||||||
selectedLanguageIds={selectedLangs}
|
selectedLanguageIds={selectedLangs}
|
||||||
multiple={true}
|
multiple={true}
|
||||||
showLanguageWarning={true}
|
showLanguageWarning={true}
|
||||||
|
@ -958,6 +971,59 @@ export class Settings extends Component<SettingsRouteProps, SettingsState> {
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="input-group mb-3">
|
||||||
|
<div className="form-check">
|
||||||
|
<input
|
||||||
|
className="form-check-input"
|
||||||
|
id="user-show-upvotes"
|
||||||
|
type="checkbox"
|
||||||
|
checked={this.state.saveUserSettingsForm.show_upvotes}
|
||||||
|
onChange={linkEvent(this, this.handleShowUpvotesChange)}
|
||||||
|
/>
|
||||||
|
<label className="form-check-label" htmlFor="user-show-upvotes">
|
||||||
|
{I18NextService.i18n.t("show_upvotes")}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{enableDownvotes(siteRes) && (
|
||||||
|
<div className="input-group mb-3">
|
||||||
|
<div className="form-check">
|
||||||
|
<input
|
||||||
|
className="form-check-input"
|
||||||
|
id="user-show-downvotes"
|
||||||
|
type="checkbox"
|
||||||
|
checked={this.state.saveUserSettingsForm.show_downvotes}
|
||||||
|
onChange={linkEvent(this, this.handleShowDownvotesChange)}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
className="form-check-label"
|
||||||
|
htmlFor="user-show-downvotes"
|
||||||
|
>
|
||||||
|
{I18NextService.i18n.t("show_downvotes")}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="input-group mb-3">
|
||||||
|
<div className="form-check">
|
||||||
|
<input
|
||||||
|
className="form-check-input"
|
||||||
|
id="user-show-upvote-percentage"
|
||||||
|
type="checkbox"
|
||||||
|
checked={this.state.saveUserSettingsForm.show_upvote_percentage}
|
||||||
|
onChange={linkEvent(
|
||||||
|
this,
|
||||||
|
this.handleShowUpvotePercentageChange,
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
className="form-check-label"
|
||||||
|
htmlFor="user-show-upvote-percentage"
|
||||||
|
>
|
||||||
|
{I18NextService.i18n.t("show_upvote_percentage")}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div className="input-group mb-3">
|
<div className="input-group mb-3">
|
||||||
<div className="form-check">
|
<div className="form-check">
|
||||||
<input
|
<input
|
||||||
|
@ -1442,13 +1508,50 @@ export class Settings extends Component<SettingsRouteProps, SettingsState> {
|
||||||
handleShowScoresChange(i: Settings, event: any) {
|
handleShowScoresChange(i: Settings, event: any) {
|
||||||
const mui = UserService.Instance.myUserInfo;
|
const mui = UserService.Instance.myUserInfo;
|
||||||
if (mui) {
|
if (mui) {
|
||||||
mui.local_user_view.local_user.show_scores = event.target.checked;
|
mui.local_user_view.local_user_vote_display_mode.score =
|
||||||
|
event.target.checked;
|
||||||
}
|
}
|
||||||
i.setState(
|
i.setState(
|
||||||
s => ((s.saveUserSettingsForm.show_scores = event.target.checked), s),
|
s => ((s.saveUserSettingsForm.show_scores = event.target.checked), s),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleShowUpvotesChange(i: Settings, event: any) {
|
||||||
|
const mui = UserService.Instance.myUserInfo;
|
||||||
|
if (mui) {
|
||||||
|
mui.local_user_view.local_user_vote_display_mode.upvotes =
|
||||||
|
event.target.checked;
|
||||||
|
}
|
||||||
|
i.setState(
|
||||||
|
s => ((s.saveUserSettingsForm.show_upvotes = event.target.checked), s),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleShowDownvotesChange(i: Settings, event: any) {
|
||||||
|
const mui = UserService.Instance.myUserInfo;
|
||||||
|
if (mui) {
|
||||||
|
mui.local_user_view.local_user_vote_display_mode.downvotes =
|
||||||
|
event.target.checked;
|
||||||
|
}
|
||||||
|
i.setState(
|
||||||
|
s => ((s.saveUserSettingsForm.show_downvotes = event.target.checked), s),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleShowUpvotePercentageChange(i: Settings, event: any) {
|
||||||
|
const mui = UserService.Instance.myUserInfo;
|
||||||
|
if (mui) {
|
||||||
|
mui.local_user_view.local_user_vote_display_mode.upvote_percentage =
|
||||||
|
event.target.checked;
|
||||||
|
}
|
||||||
|
i.setState(
|
||||||
|
s => (
|
||||||
|
(s.saveUserSettingsForm.show_upvote_percentage = event.target.checked),
|
||||||
|
s
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
async handleGenerateTotp(i: Settings) {
|
async handleGenerateTotp(i: Settings) {
|
||||||
i.setState({ generateTotpRes: LOADING_REQUEST });
|
i.setState({ generateTotpRes: LOADING_REQUEST });
|
||||||
|
|
||||||
|
@ -1584,7 +1687,9 @@ export class Settings extends Component<SettingsRouteProps, SettingsState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
toast(I18NextService.i18n.t("saved"));
|
toast(I18NextService.i18n.t("saved"));
|
||||||
snapToTop();
|
|
||||||
|
// You need to reload the page, to properly update the siteRes everywhere
|
||||||
|
setTimeout(() => location.reload(), 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
setThemeOverride(undefined);
|
setThemeOverride(undefined);
|
||||||
|
|
|
@ -3,6 +3,7 @@ import {
|
||||||
enableDownvotes,
|
enableDownvotes,
|
||||||
enableNsfw,
|
enableNsfw,
|
||||||
setIsoData,
|
setIsoData,
|
||||||
|
voteDisplayMode,
|
||||||
} from "@utils/app";
|
} from "@utils/app";
|
||||||
import { getIdFromString, getQueryParams } from "@utils/helpers";
|
import { getIdFromString, getQueryParams } from "@utils/helpers";
|
||||||
import { Choice, RouteDataResponse } from "@utils/types";
|
import { Choice, RouteDataResponse } from "@utils/types";
|
||||||
|
@ -163,7 +164,7 @@ export class CreatePost extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { selectedCommunityChoice } = this.state;
|
const { selectedCommunityChoice, siteRes } = this.state;
|
||||||
|
|
||||||
const locationState = this.props.history.location.state as
|
const locationState = this.props.history.location.state as
|
||||||
| PostFormParams
|
| PostFormParams
|
||||||
|
@ -191,10 +192,11 @@ export class CreatePost extends Component<
|
||||||
<PostForm
|
<PostForm
|
||||||
onCreate={this.handlePostCreate}
|
onCreate={this.handlePostCreate}
|
||||||
params={locationState}
|
params={locationState}
|
||||||
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
enableDownvotes={enableDownvotes(siteRes)}
|
||||||
enableNsfw={enableNsfw(this.state.siteRes)}
|
voteDisplayMode={voteDisplayMode(siteRes)}
|
||||||
allLanguages={this.state.siteRes.all_languages}
|
enableNsfw={enableNsfw(siteRes)}
|
||||||
siteLanguages={this.state.siteRes.discussion_languages}
|
allLanguages={siteRes.all_languages}
|
||||||
|
siteLanguages={siteRes.discussion_languages}
|
||||||
selectedCommunityChoice={selectedCommunityChoice}
|
selectedCommunityChoice={selectedCommunityChoice}
|
||||||
onSelectCommunity={this.handleSelectedCommunityChange}
|
onSelectCommunity={this.handleSelectedCommunityChange}
|
||||||
initialCommunities={
|
initialCommunities={
|
||||||
|
|
|
@ -18,6 +18,7 @@ import {
|
||||||
EditPost,
|
EditPost,
|
||||||
GetSiteMetadataResponse,
|
GetSiteMetadataResponse,
|
||||||
Language,
|
Language,
|
||||||
|
LocalUserVoteDisplayMode,
|
||||||
PostView,
|
PostView,
|
||||||
SearchResponse,
|
SearchResponse,
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
|
@ -57,6 +58,7 @@ interface PostFormProps {
|
||||||
onEdit?(form: EditPost): void;
|
onEdit?(form: EditPost): void;
|
||||||
enableNsfw?: boolean;
|
enableNsfw?: boolean;
|
||||||
enableDownvotes?: boolean;
|
enableDownvotes?: boolean;
|
||||||
|
voteDisplayMode: LocalUserVoteDisplayMode;
|
||||||
selectedCommunityChoice?: Choice;
|
selectedCommunityChoice?: Choice;
|
||||||
onSelectCommunity?: (choice: Choice) => void;
|
onSelectCommunity?: (choice: Choice) => void;
|
||||||
initialCommunities?: CommunityView[];
|
initialCommunities?: CommunityView[];
|
||||||
|
@ -453,6 +455,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
showCommunity
|
showCommunity
|
||||||
posts={this.props.crossPosts}
|
posts={this.props.crossPosts}
|
||||||
enableDownvotes={this.props.enableDownvotes}
|
enableDownvotes={this.props.enableDownvotes}
|
||||||
|
voteDisplayMode={this.props.voteDisplayMode}
|
||||||
enableNsfw={this.props.enableNsfw}
|
enableNsfw={this.props.enableNsfw}
|
||||||
allLanguages={this.props.allLanguages}
|
allLanguages={this.props.allLanguages}
|
||||||
siteLanguages={this.props.siteLanguages}
|
siteLanguages={this.props.siteLanguages}
|
||||||
|
@ -667,6 +670,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
showCommunity
|
showCommunity
|
||||||
posts={suggestedPosts}
|
posts={suggestedPosts}
|
||||||
enableDownvotes={this.props.enableDownvotes}
|
enableDownvotes={this.props.enableDownvotes}
|
||||||
|
voteDisplayMode={this.props.voteDisplayMode}
|
||||||
enableNsfw={this.props.enableNsfw}
|
enableNsfw={this.props.enableNsfw}
|
||||||
allLanguages={this.props.allLanguages}
|
allLanguages={this.props.allLanguages}
|
||||||
siteLanguages={this.props.siteLanguages}
|
siteLanguages={this.props.siteLanguages}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import {
|
||||||
FeaturePost,
|
FeaturePost,
|
||||||
HidePost,
|
HidePost,
|
||||||
Language,
|
Language,
|
||||||
|
LocalUserVoteDisplayMode,
|
||||||
LockPost,
|
LockPost,
|
||||||
MarkPostAsRead,
|
MarkPostAsRead,
|
||||||
PersonView,
|
PersonView,
|
||||||
|
@ -73,6 +74,7 @@ interface PostListingProps {
|
||||||
showBody?: boolean;
|
showBody?: boolean;
|
||||||
hideImage?: boolean;
|
hideImage?: boolean;
|
||||||
enableDownvotes?: boolean;
|
enableDownvotes?: boolean;
|
||||||
|
voteDisplayMode: LocalUserVoteDisplayMode;
|
||||||
enableNsfw?: boolean;
|
enableNsfw?: boolean;
|
||||||
viewOnly?: boolean;
|
viewOnly?: boolean;
|
||||||
onPostEdit(form: EditPost): Promise<RequestState<PostResponse>>;
|
onPostEdit(form: EditPost): Promise<RequestState<PostResponse>>;
|
||||||
|
@ -168,6 +170,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
onCancel={this.handleEditCancel}
|
onCancel={this.handleEditCancel}
|
||||||
enableNsfw={this.props.enableNsfw}
|
enableNsfw={this.props.enableNsfw}
|
||||||
enableDownvotes={this.props.enableDownvotes}
|
enableDownvotes={this.props.enableDownvotes}
|
||||||
|
voteDisplayMode={this.props.voteDisplayMode}
|
||||||
allLanguages={this.props.allLanguages}
|
allLanguages={this.props.allLanguages}
|
||||||
siteLanguages={this.props.siteLanguages}
|
siteLanguages={this.props.siteLanguages}
|
||||||
/>
|
/>
|
||||||
|
@ -392,7 +395,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
}
|
}
|
||||||
</span>
|
</span>
|
||||||
)}{" "}
|
)}{" "}
|
||||||
• <MomentTime published={pv.post.published} updated={pv.post.updated} />
|
· <MomentTime published={pv.post.published} updated={pv.post.updated} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -553,12 +556,18 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
commentsLine(mobile = false) {
|
commentsLine(mobile = false) {
|
||||||
const { admins, moderators, showBody, onPostVote, enableDownvotes } =
|
const {
|
||||||
this.props;
|
admins,
|
||||||
|
moderators,
|
||||||
|
showBody,
|
||||||
|
onPostVote,
|
||||||
|
enableDownvotes,
|
||||||
|
voteDisplayMode,
|
||||||
|
} = this.props;
|
||||||
const {
|
const {
|
||||||
post: { ap_id, id, body },
|
post: { ap_id, id, body },
|
||||||
counts,
|
|
||||||
my_vote,
|
my_vote,
|
||||||
|
counts,
|
||||||
} = this.postView;
|
} = this.postView;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -585,9 +594,10 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
voteContentType={VoteContentType.Post}
|
voteContentType={VoteContentType.Post}
|
||||||
id={id}
|
id={id}
|
||||||
onVote={onPostVote}
|
onVote={onPostVote}
|
||||||
enableDownvotes={enableDownvotes}
|
|
||||||
counts={counts}
|
counts={counts}
|
||||||
my_vote={my_vote}
|
enableDownvotes={enableDownvotes}
|
||||||
|
voteDisplayMode={voteDisplayMode}
|
||||||
|
myVote={my_vote}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@ -743,8 +753,9 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
id={this.postView.post.id}
|
id={this.postView.post.id}
|
||||||
onVote={this.props.onPostVote}
|
onVote={this.props.onPostVote}
|
||||||
enableDownvotes={this.props.enableDownvotes}
|
enableDownvotes={this.props.enableDownvotes}
|
||||||
|
voteDisplayMode={this.props.voteDisplayMode}
|
||||||
counts={this.postView.counts}
|
counts={this.postView.counts}
|
||||||
my_vote={this.postView.my_vote}
|
myVote={this.postView.my_vote}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import {
|
||||||
FeaturePost,
|
FeaturePost,
|
||||||
HidePost,
|
HidePost,
|
||||||
Language,
|
Language,
|
||||||
|
LocalUserVoteDisplayMode,
|
||||||
LockPost,
|
LockPost,
|
||||||
MarkPostAsRead,
|
MarkPostAsRead,
|
||||||
PostResponse,
|
PostResponse,
|
||||||
|
@ -35,6 +36,7 @@ interface PostListingsProps {
|
||||||
showCommunity?: boolean;
|
showCommunity?: boolean;
|
||||||
removeDuplicates?: boolean;
|
removeDuplicates?: boolean;
|
||||||
enableDownvotes?: boolean;
|
enableDownvotes?: boolean;
|
||||||
|
voteDisplayMode: LocalUserVoteDisplayMode;
|
||||||
enableNsfw?: boolean;
|
enableNsfw?: boolean;
|
||||||
viewOnly?: boolean;
|
viewOnly?: boolean;
|
||||||
onPostEdit(form: EditPost): Promise<RequestState<PostResponse>>;
|
onPostEdit(form: EditPost): Promise<RequestState<PostResponse>>;
|
||||||
|
@ -81,6 +83,7 @@ export class PostListings extends Component<PostListingsProps, any> {
|
||||||
crossPosts={this.duplicatesMap.get(post_view.post.id)}
|
crossPosts={this.duplicatesMap.get(post_view.post.id)}
|
||||||
showCommunity={this.props.showCommunity}
|
showCommunity={this.props.showCommunity}
|
||||||
enableDownvotes={this.props.enableDownvotes}
|
enableDownvotes={this.props.enableDownvotes}
|
||||||
|
voteDisplayMode={this.props.voteDisplayMode}
|
||||||
enableNsfw={this.props.enableNsfw}
|
enableNsfw={this.props.enableNsfw}
|
||||||
viewOnly={this.props.viewOnly}
|
viewOnly={this.props.viewOnly}
|
||||||
allLanguages={this.props.allLanguages}
|
allLanguages={this.props.allLanguages}
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
import { Component, InfernoNode, linkEvent } from "inferno";
|
import { Component, InfernoNode, linkEvent } from "inferno";
|
||||||
import { T } from "inferno-i18next-dess";
|
import { T } from "inferno-i18next-dess";
|
||||||
import { PostReportView, PostView, ResolvePostReport } from "lemmy-js-client";
|
import {
|
||||||
|
LocalUserVoteDisplayMode,
|
||||||
|
PostReportView,
|
||||||
|
PostView,
|
||||||
|
ResolvePostReport,
|
||||||
|
} from "lemmy-js-client";
|
||||||
import { I18NextService } from "../../services";
|
import { I18NextService } from "../../services";
|
||||||
import { Icon, Spinner } from "../common/icon";
|
import { Icon, Spinner } from "../common/icon";
|
||||||
import { PersonListing } from "../person/person-listing";
|
import { PersonListing } from "../person/person-listing";
|
||||||
|
@ -10,6 +15,9 @@ import { tippyMixin } from "../mixins/tippy-mixin";
|
||||||
|
|
||||||
interface PostReportProps {
|
interface PostReportProps {
|
||||||
report: PostReportView;
|
report: PostReportView;
|
||||||
|
enableDownvotes?: boolean;
|
||||||
|
voteDisplayMode: LocalUserVoteDisplayMode;
|
||||||
|
enableNsfw?: boolean;
|
||||||
onResolveReport(form: ResolvePostReport): void;
|
onResolveReport(form: ResolvePostReport): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,8 +78,9 @@ export class PostReport extends Component<PostReportProps, PostReportState> {
|
||||||
<PostListing
|
<PostListing
|
||||||
post_view={pv}
|
post_view={pv}
|
||||||
showCommunity={true}
|
showCommunity={true}
|
||||||
enableDownvotes={true}
|
enableDownvotes={this.props.enableDownvotes}
|
||||||
enableNsfw={true}
|
voteDisplayMode={this.props.voteDisplayMode}
|
||||||
|
enableNsfw={this.props.enableNsfw}
|
||||||
viewOnly={true}
|
viewOnly={true}
|
||||||
allLanguages={[]}
|
allLanguages={[]}
|
||||||
siteLanguages={[]}
|
siteLanguages={[]}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import {
|
||||||
setIsoData,
|
setIsoData,
|
||||||
updateCommunityBlock,
|
updateCommunityBlock,
|
||||||
updatePersonBlock,
|
updatePersonBlock,
|
||||||
|
voteDisplayMode,
|
||||||
} from "@utils/app";
|
} from "@utils/app";
|
||||||
import { isBrowser } from "@utils/browser";
|
import { isBrowser } from "@utils/browser";
|
||||||
import {
|
import {
|
||||||
|
@ -369,6 +370,7 @@ export class Post extends Component<PostRouteProps, PostState> {
|
||||||
);
|
);
|
||||||
case "success": {
|
case "success": {
|
||||||
const res = this.state.postRes.data;
|
const res = this.state.postRes.data;
|
||||||
|
const siteRes = this.state.siteRes;
|
||||||
return (
|
return (
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<main className="col-12 col-md-8 col-lg-9 mb-3">
|
<main className="col-12 col-md-8 col-lg-9 mb-3">
|
||||||
|
@ -385,11 +387,12 @@ export class Post extends Component<PostRouteProps, PostState> {
|
||||||
showBody
|
showBody
|
||||||
showCommunity
|
showCommunity
|
||||||
moderators={res.moderators}
|
moderators={res.moderators}
|
||||||
admins={this.state.siteRes.admins}
|
admins={siteRes.admins}
|
||||||
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
enableDownvotes={enableDownvotes(siteRes)}
|
||||||
enableNsfw={enableNsfw(this.state.siteRes)}
|
voteDisplayMode={voteDisplayMode(siteRes)}
|
||||||
allLanguages={this.state.siteRes.all_languages}
|
enableNsfw={enableNsfw(siteRes)}
|
||||||
siteLanguages={this.state.siteRes.discussion_languages}
|
allLanguages={siteRes.all_languages}
|
||||||
|
siteLanguages={siteRes.discussion_languages}
|
||||||
onBlockPerson={this.handleBlockPerson}
|
onBlockPerson={this.handleBlockPerson}
|
||||||
onPostEdit={this.handlePostEdit}
|
onPostEdit={this.handlePostEdit}
|
||||||
onPostVote={this.handlePostVote}
|
onPostVote={this.handlePostVote}
|
||||||
|
@ -419,8 +422,8 @@ export class Post extends Component<PostRouteProps, PostState> {
|
||||||
<CommentForm
|
<CommentForm
|
||||||
node={res.post_view.post.id}
|
node={res.post_view.post.id}
|
||||||
disabled={res.post_view.post.locked}
|
disabled={res.post_view.post.locked}
|
||||||
allLanguages={this.state.siteRes.all_languages}
|
allLanguages={siteRes.all_languages}
|
||||||
siteLanguages={this.state.siteRes.discussion_languages}
|
siteLanguages={siteRes.discussion_languages}
|
||||||
containerClass="post-comment-container"
|
containerClass="post-comment-container"
|
||||||
onUpsertComment={this.handleCreateComment}
|
onUpsertComment={this.handleCreateComment}
|
||||||
finished={this.state.finished.get(0)}
|
finished={this.state.finished.get(0)}
|
||||||
|
@ -581,6 +584,7 @@ export class Post extends Component<PostRouteProps, PostState> {
|
||||||
// These are already sorted by new
|
// These are already sorted by new
|
||||||
const commentsRes = this.state.commentsRes;
|
const commentsRes = this.state.commentsRes;
|
||||||
const postRes = this.state.postRes;
|
const postRes = this.state.postRes;
|
||||||
|
const siteRes = this.state.siteRes;
|
||||||
|
|
||||||
if (commentsRes.state === "success" && postRes.state === "success") {
|
if (commentsRes.state === "success" && postRes.state === "success") {
|
||||||
return (
|
return (
|
||||||
|
@ -592,12 +596,13 @@ export class Post extends Component<PostRouteProps, PostState> {
|
||||||
isTopLevel
|
isTopLevel
|
||||||
locked={postRes.data.post_view.post.locked}
|
locked={postRes.data.post_view.post.locked}
|
||||||
moderators={postRes.data.moderators}
|
moderators={postRes.data.moderators}
|
||||||
admins={this.state.siteRes.admins}
|
admins={siteRes.admins}
|
||||||
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
enableDownvotes={enableDownvotes(siteRes)}
|
||||||
|
voteDisplayMode={voteDisplayMode(siteRes)}
|
||||||
showContext
|
showContext
|
||||||
finished={this.state.finished}
|
finished={this.state.finished}
|
||||||
allLanguages={this.state.siteRes.all_languages}
|
allLanguages={siteRes.all_languages}
|
||||||
siteLanguages={this.state.siteRes.discussion_languages}
|
siteLanguages={siteRes.discussion_languages}
|
||||||
onSaveComment={this.handleSaveComment}
|
onSaveComment={this.handleSaveComment}
|
||||||
onBlockPerson={this.handleBlockPerson}
|
onBlockPerson={this.handleBlockPerson}
|
||||||
onDeleteComment={this.handleDeleteComment}
|
onDeleteComment={this.handleDeleteComment}
|
||||||
|
@ -652,6 +657,7 @@ export class Post extends Component<PostRouteProps, PostState> {
|
||||||
const firstComment = this.commentTree().at(0)?.comment_view.comment;
|
const firstComment = this.commentTree().at(0)?.comment_view.comment;
|
||||||
const depth = getDepthFromComment(firstComment);
|
const depth = getDepthFromComment(firstComment);
|
||||||
const showContextButton = depth ? depth > 0 : false;
|
const showContextButton = depth ? depth > 0 : false;
|
||||||
|
const siteRes = this.state.siteRes;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
res.state === "success" && (
|
res.state === "success" && (
|
||||||
|
@ -680,11 +686,12 @@ export class Post extends Component<PostRouteProps, PostState> {
|
||||||
maxCommentsShown={this.state.maxCommentsShown}
|
maxCommentsShown={this.state.maxCommentsShown}
|
||||||
locked={res.data.post_view.post.locked}
|
locked={res.data.post_view.post.locked}
|
||||||
moderators={res.data.moderators}
|
moderators={res.data.moderators}
|
||||||
admins={this.state.siteRes.admins}
|
admins={siteRes.admins}
|
||||||
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
enableDownvotes={enableDownvotes(siteRes)}
|
||||||
|
voteDisplayMode={voteDisplayMode(siteRes)}
|
||||||
finished={this.state.finished}
|
finished={this.state.finished}
|
||||||
allLanguages={this.state.siteRes.all_languages}
|
allLanguages={siteRes.all_languages}
|
||||||
siteLanguages={this.state.siteRes.discussion_languages}
|
siteLanguages={siteRes.discussion_languages}
|
||||||
onSaveComment={this.handleSaveComment}
|
onSaveComment={this.handleSaveComment}
|
||||||
onBlockPerson={this.handleBlockPerson}
|
onBlockPerson={this.handleBlockPerson}
|
||||||
onDeleteComment={this.handleDeleteComment}
|
onDeleteComment={this.handleDeleteComment}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import {
|
||||||
personToChoice,
|
personToChoice,
|
||||||
setIsoData,
|
setIsoData,
|
||||||
showLocal,
|
showLocal,
|
||||||
|
voteDisplayMode,
|
||||||
} from "@utils/app";
|
} from "@utils/app";
|
||||||
import { scrollMixin } from "./mixins/scroll-mixin";
|
import { scrollMixin } from "./mixins/scroll-mixin";
|
||||||
import {
|
import {
|
||||||
|
@ -707,6 +708,7 @@ export class Search extends Component<SearchRouteProps, SearchState> {
|
||||||
|
|
||||||
get all() {
|
get all() {
|
||||||
const combined = this.buildCombined();
|
const combined = this.buildCombined();
|
||||||
|
const siteRes = this.state.siteRes;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
@ -718,10 +720,11 @@ export class Search extends Component<SearchRouteProps, SearchState> {
|
||||||
key={(i.data as PostView).post.id}
|
key={(i.data as PostView).post.id}
|
||||||
post_view={i.data as PostView}
|
post_view={i.data as PostView}
|
||||||
showCommunity
|
showCommunity
|
||||||
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
enableDownvotes={enableDownvotes(siteRes)}
|
||||||
enableNsfw={enableNsfw(this.state.siteRes)}
|
voteDisplayMode={voteDisplayMode(siteRes)}
|
||||||
allLanguages={this.state.siteRes.all_languages}
|
enableNsfw={enableNsfw(siteRes)}
|
||||||
siteLanguages={this.state.siteRes.discussion_languages}
|
allLanguages={siteRes.all_languages}
|
||||||
|
siteLanguages={siteRes.discussion_languages}
|
||||||
viewOnly
|
viewOnly
|
||||||
// All of these are unused, since its view only
|
// All of these are unused, since its view only
|
||||||
onPostEdit={async () => EMPTY_REQUEST}
|
onPostEdit={async () => EMPTY_REQUEST}
|
||||||
|
@ -758,9 +761,10 @@ export class Search extends Component<SearchRouteProps, SearchState> {
|
||||||
viewOnly
|
viewOnly
|
||||||
locked
|
locked
|
||||||
isTopLevel
|
isTopLevel
|
||||||
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
enableDownvotes={enableDownvotes(siteRes)}
|
||||||
allLanguages={this.state.siteRes.all_languages}
|
voteDisplayMode={voteDisplayMode(siteRes)}
|
||||||
siteLanguages={this.state.siteRes.discussion_languages}
|
allLanguages={siteRes.all_languages}
|
||||||
|
siteLanguages={siteRes.discussion_languages}
|
||||||
// All of these are unused, since its viewonly
|
// All of these are unused, since its viewonly
|
||||||
finished={new Map()}
|
finished={new Map()}
|
||||||
onSaveComment={async () => {}}
|
onSaveComment={async () => {}}
|
||||||
|
@ -820,6 +824,7 @@ export class Search extends Component<SearchRouteProps, SearchState> {
|
||||||
locked
|
locked
|
||||||
isTopLevel
|
isTopLevel
|
||||||
enableDownvotes={enableDownvotes(siteRes)}
|
enableDownvotes={enableDownvotes(siteRes)}
|
||||||
|
voteDisplayMode={voteDisplayMode(siteRes)}
|
||||||
allLanguages={siteRes.all_languages}
|
allLanguages={siteRes.all_languages}
|
||||||
siteLanguages={siteRes.discussion_languages}
|
siteLanguages={siteRes.discussion_languages}
|
||||||
// All of these are unused, since its viewonly
|
// All of these are unused, since its viewonly
|
||||||
|
@ -871,6 +876,7 @@ export class Search extends Component<SearchRouteProps, SearchState> {
|
||||||
post_view={pv}
|
post_view={pv}
|
||||||
showCommunity
|
showCommunity
|
||||||
enableDownvotes={enableDownvotes(siteRes)}
|
enableDownvotes={enableDownvotes(siteRes)}
|
||||||
|
voteDisplayMode={voteDisplayMode(siteRes)}
|
||||||
enableNsfw={enableNsfw(siteRes)}
|
enableNsfw={enableNsfw(siteRes)}
|
||||||
allLanguages={siteRes.all_languages}
|
allLanguages={siteRes.all_languages}
|
||||||
siteLanguages={siteRes.discussion_languages}
|
siteLanguages={siteRes.discussion_languages}
|
||||||
|
|
6
src/shared/utils/app/calculate-upvote-pct.ts
Normal file
6
src/shared/utils/app/calculate-upvote-pct.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
export default function calculateUpvotePct(
|
||||||
|
upvotes: number,
|
||||||
|
downvotes: number,
|
||||||
|
): number {
|
||||||
|
return (upvotes / (upvotes + downvotes)) * 100;
|
||||||
|
}
|
|
@ -18,6 +18,8 @@ import editPrivateMessageReport from "./edit-private-message-report";
|
||||||
import editRegistrationApplication from "./edit-registration-application";
|
import editRegistrationApplication from "./edit-registration-application";
|
||||||
import editWith from "./edit-with";
|
import editWith from "./edit-with";
|
||||||
import enableDownvotes from "./enable-downvotes";
|
import enableDownvotes from "./enable-downvotes";
|
||||||
|
import voteDisplayMode from "./vote-display-mode";
|
||||||
|
import calculateUpvotePct from "./calculate-upvote-pct";
|
||||||
import enableNsfw from "./enable-nsfw";
|
import enableNsfw from "./enable-nsfw";
|
||||||
import fetchCommunities from "./fetch-communities";
|
import fetchCommunities from "./fetch-communities";
|
||||||
import fetchSearchResults from "./fetch-search-results";
|
import fetchSearchResults from "./fetch-search-results";
|
||||||
|
@ -112,4 +114,6 @@ export {
|
||||||
instanceToChoice,
|
instanceToChoice,
|
||||||
updateInstanceBlock,
|
updateInstanceBlock,
|
||||||
isAnonymousPath,
|
isAnonymousPath,
|
||||||
|
voteDisplayMode,
|
||||||
|
calculateUpvotePct,
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,5 +3,7 @@ import { UserService } from "../../services";
|
||||||
export default function showScores(
|
export default function showScores(
|
||||||
myUserInfo = UserService.Instance.myUserInfo,
|
myUserInfo = UserService.Instance.myUserInfo,
|
||||||
): boolean {
|
): boolean {
|
||||||
return myUserInfo?.local_user_view.local_user.show_scores ?? true;
|
const voteDisplayMode =
|
||||||
|
myUserInfo?.local_user_view.local_user_vote_display_mode;
|
||||||
|
return (voteDisplayMode?.score || voteDisplayMode?.upvotes) ?? true;
|
||||||
}
|
}
|
||||||
|
|
15
src/shared/utils/app/vote-display-mode.ts
Normal file
15
src/shared/utils/app/vote-display-mode.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import { GetSiteResponse, LocalUserVoteDisplayMode } from "lemmy-js-client";
|
||||||
|
|
||||||
|
export default function voteDisplayMode(
|
||||||
|
siteRes: GetSiteResponse,
|
||||||
|
): LocalUserVoteDisplayMode {
|
||||||
|
return (
|
||||||
|
siteRes?.my_user?.local_user_view.local_user_vote_display_mode ?? {
|
||||||
|
local_user_id: -1,
|
||||||
|
upvotes: true,
|
||||||
|
downvotes: true,
|
||||||
|
score: false,
|
||||||
|
upvote_percentage: false,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
Loading…
Reference in a new issue