From d8a92812d869db644612f271e8dfc26d4378b23d Mon Sep 17 00:00:00 2001 From: Dessalines Date: Thu, 18 Apr 2024 20:20:37 -0400 Subject: [PATCH] Adding vote display modes (#2426) * Adding vote display modes * Only show downvotes setting if site has downvotes enabled. --- .../components/comment/comment-node.tsx | 57 ++---- .../components/comment/comment-nodes.tsx | 3 + .../components/comment/comment-report.tsx | 6 +- src/shared/components/common/vote-buttons.tsx | 145 +++++++++----- src/shared/components/common/vote-display.tsx | 184 ++++++++++++++++++ src/shared/components/community/community.tsx | 33 ++-- src/shared/components/home/home.tsx | 3 + src/shared/components/person/inbox.tsx | 32 +-- .../components/person/person-details.tsx | 6 + src/shared/components/person/profile.tsx | 2 + src/shared/components/person/reports.tsx | 16 ++ src/shared/components/person/settings.tsx | 119 ++++++++++- src/shared/components/post/create-post.tsx | 12 +- src/shared/components/post/post-form.tsx | 4 + src/shared/components/post/post-listing.tsx | 25 ++- src/shared/components/post/post-listings.tsx | 3 + src/shared/components/post/post-report.tsx | 15 +- src/shared/components/post/post.tsx | 37 ++-- src/shared/components/search.tsx | 20 +- src/shared/utils/app/calculate-upvote-pct.ts | 6 + src/shared/utils/app/index.ts | 4 + src/shared/utils/app/show-scores.ts | 4 +- src/shared/utils/app/vote-display-mode.ts | 15 ++ 23 files changed, 580 insertions(+), 171 deletions(-) create mode 100644 src/shared/components/common/vote-display.tsx create mode 100644 src/shared/utils/app/calculate-upvote-pct.ts create mode 100644 src/shared/utils/app/vote-display-mode.ts diff --git a/src/shared/components/comment/comment-node.tsx b/src/shared/components/comment/comment-node.tsx index 056fe14d..52fd300f 100644 --- a/src/shared/components/comment/comment-node.tsx +++ b/src/shared/components/comment/comment-node.tsx @@ -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 { {/* This is an expanding spacer for mobile */}
- {showScores() && ( - <> - - {numToSI(counts.score)} - - - - )} + @@ -354,8 +348,9 @@ export class CommentNode extends Component { id={id} onVote={this.props.onCommentVote} enableDownvotes={this.props.enableDownvotes} + voteDisplayMode={this.props.voteDisplayMode} counts={counts} - my_vote={my_vote} + myVote={my_vote} /> @@ -139,28 +165,32 @@ export class VoteButtonsCompact extends Component< @@ -198,13 +228,16 @@ export class VoteButtons extends Component {
@@ -832,8 +845,8 @@ export class Settings extends Component { { +
+
+ + +
+
+ {enableDownvotes(siteRes) && ( +
+
+ + +
+
+ )} +
+
+ + +
+
{ 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 { } toast(I18NextService.i18n.t("saved")); - snapToTop(); + + // You need to reload the page, to properly update the siteRes everywhere + setTimeout(() => location.reload(), 500); } setThemeOverride(undefined); diff --git a/src/shared/components/post/create-post.tsx b/src/shared/components/post/create-post.tsx index 5e3673bb..4dcaa6e2 100644 --- a/src/shared/components/post/create-post.tsx +++ b/src/shared/components/post/create-post.tsx @@ -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< void; initialCommunities?: CommunityView[]; @@ -453,6 +455,7 @@ export class PostForm extends Component { 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 { showCommunity posts={suggestedPosts} enableDownvotes={this.props.enableDownvotes} + voteDisplayMode={this.props.voteDisplayMode} enableNsfw={this.props.enableNsfw} allLanguages={this.props.allLanguages} siteLanguages={this.props.siteLanguages} diff --git a/src/shared/components/post/post-listing.tsx b/src/shared/components/post/post-listing.tsx index d5815dac..a7c3b7de 100644 --- a/src/shared/components/post/post-listing.tsx +++ b/src/shared/components/post/post-listing.tsx @@ -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>; @@ -168,6 +170,7 @@ export class PostListing extends Component { 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 { } )}{" "} - • + ·
); } @@ -553,12 +556,18 @@ export class PostListing extends Component { } 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 { 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 { 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} />
)} diff --git a/src/shared/components/post/post-listings.tsx b/src/shared/components/post/post-listings.tsx index cf7b4bdb..a0ad05d8 100644 --- a/src/shared/components/post/post-listings.tsx +++ b/src/shared/components/post/post-listings.tsx @@ -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>; @@ -81,6 +83,7 @@ export class PostListings extends Component { 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} diff --git a/src/shared/components/post/post-report.tsx b/src/shared/components/post/post-report.tsx index 7078cd1d..0d4c55ca 100644 --- a/src/shared/components/post/post-report.tsx +++ b/src/shared/components/post/post-report.tsx @@ -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 { { ); case "success": { const res = this.state.postRes.data; + const siteRes = this.state.siteRes; return (
@@ -385,11 +387,12 @@ export class Post extends Component { 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 { { // 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 { 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 { 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 { 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} diff --git a/src/shared/components/search.tsx b/src/shared/components/search.tsx index 30a14e0b..b0de758a 100644 --- a/src/shared/components/search.tsx +++ b/src/shared/components/search.tsx @@ -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 { get all() { const combined = this.buildCombined(); + const siteRes = this.state.siteRes; return (
@@ -718,10 +720,11 @@ export class Search extends Component { 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 { 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 { 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 { post_view={pv} showCommunity enableDownvotes={enableDownvotes(siteRes)} + voteDisplayMode={voteDisplayMode(siteRes)} enableNsfw={enableNsfw(siteRes)} allLanguages={siteRes.all_languages} siteLanguages={siteRes.discussion_languages} diff --git a/src/shared/utils/app/calculate-upvote-pct.ts b/src/shared/utils/app/calculate-upvote-pct.ts new file mode 100644 index 00000000..e3651d07 --- /dev/null +++ b/src/shared/utils/app/calculate-upvote-pct.ts @@ -0,0 +1,6 @@ +export default function calculateUpvotePct( + upvotes: number, + downvotes: number, +): number { + return (upvotes / (upvotes + downvotes)) * 100; +} diff --git a/src/shared/utils/app/index.ts b/src/shared/utils/app/index.ts index 484bae02..30dc2da1 100644 --- a/src/shared/utils/app/index.ts +++ b/src/shared/utils/app/index.ts @@ -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, }; diff --git a/src/shared/utils/app/show-scores.ts b/src/shared/utils/app/show-scores.ts index 03e2cd07..2d6c94ae 100644 --- a/src/shared/utils/app/show-scores.ts +++ b/src/shared/utils/app/show-scores.ts @@ -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; } diff --git a/src/shared/utils/app/vote-display-mode.ts b/src/shared/utils/app/vote-display-mode.ts new file mode 100644 index 00000000..e9688175 --- /dev/null +++ b/src/shared/utils/app/vote-display-mode.ts @@ -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, + } + ); +}