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