Adding vote display modes (#2426)

* Adding vote display modes

* Only show downvotes setting if site has downvotes enabled.
This commit is contained in:
Dessalines 2024-04-18 20:20:37 -04:00 committed by GitHub
parent 643c1f6f01
commit d8a92812d8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 580 additions and 171 deletions

View file

@ -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")

View file

@ -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}

View file

@ -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={[]}

View file

@ -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,
count: Number(counts.score), counts: CommentAggregates | PostAggregates,
formattedCount: Number(counts.score), ): 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", { const pct = calculateUpvotePct(counts.upvotes, counts.downvotes);
count: Number(counts.upvotes),
formattedCount: Number(counts.upvotes),
});
const downvotes = I18NextService.i18n.t("number_of_downvotes", { const upvotePctStr =
count: Number(counts.downvotes), voteDisplayMode.upvote_percentage &&
formattedCount: Number(counts.downvotes), 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 }); 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,24 +136,28 @@ 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() &&
<span className="ms-2"> this.props.voteContentType === VoteContentType.Post && (
{numToSI(this.props.counts.upvotes)} <span className="ms-2">
</span> {numToSI(this.props.counts.upvotes)}
)} </span>
)}
</> </>
)} )}
</button> </button>
@ -139,28 +165,32 @@ 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() &&
<span this.props.voteContentType === VoteContentType.Post && (
className={classNames("ms-2", { <span
invisible: this.props.counts.downvotes === 0, className={classNames("ms-2", {
})} invisible: this.props.counts.downvotes === 0,
> })}
{numToSI(this.props.counts.downvotes)} >
</span> {numToSI(this.props.counts.downvotes)}
)} </span>
)}
</> </>
)} )}
</button> </button>
@ -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 />

View 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";
}
}

View file

@ -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}

View file

@ -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}

View file

@ -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}

View file

@ -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}

View file

@ -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}

View file

@ -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}
/> />

View file

@ -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);

View file

@ -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={

View file

@ -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}

View file

@ -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>
)} )}

View file

@ -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}

View file

@ -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={[]}

View file

@ -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}

View file

@ -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}

View file

@ -0,0 +1,6 @@
export default function calculateUpvotePct(
upvotes: number,
downvotes: number,
): number {
return (upvotes / (upvotes + downvotes)) * 100;
}

View file

@ -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,
}; };

View file

@ -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;
} }

View 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,
}
);
}