mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-07-18 16:43:56 -05:00
Show Plus Voting % to patrons
This commit is contained in:
parent
dd3377e7f3
commit
7bce94d97e
|
|
@ -61,14 +61,14 @@ export interface PlusVotingResultByMonthYear {
|
|||
passed: PlusVotingResultUser[];
|
||||
failed: PlusVotingResultUser[];
|
||||
}[];
|
||||
ownScores?: Pick<
|
||||
scores: (Pick<
|
||||
PlusVotingResult,
|
||||
"score" | "tier" | "wasSuggested" | "passedVoting"
|
||||
>[];
|
||||
> & { userId: User["id"] })[];
|
||||
}
|
||||
|
||||
export function resultsByMontYear(
|
||||
args: MonthYear & { userId?: User["id"] }
|
||||
args: MonthYear
|
||||
): PlusVotingResultByMonthYear {
|
||||
const results = resultsByMonthYearStm.all(
|
||||
args
|
||||
|
|
@ -76,7 +76,7 @@ export function resultsByMontYear(
|
|||
|
||||
return {
|
||||
results: groupPlusVotingResults(results),
|
||||
ownScores: ownScoresFromPlusVotingResults(results, args.userId),
|
||||
scores: scoresFromPlusVotingResults(results),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -114,26 +114,18 @@ function groupPlusVotingResults(
|
|||
.sort((a, b) => a.tier - b.tier);
|
||||
}
|
||||
|
||||
function ownScoresFromPlusVotingResults(
|
||||
rows: PlusVotingResultsByMonthYearDatabaseResult,
|
||||
userId?: User["id"]
|
||||
function scoresFromPlusVotingResults(
|
||||
rows: PlusVotingResultsByMonthYearDatabaseResult
|
||||
) {
|
||||
if (!userId) return;
|
||||
|
||||
const result: PlusVotingResultByMonthYear["ownScores"] = [];
|
||||
|
||||
for (const row of rows) {
|
||||
if (row.id === userId) {
|
||||
result.push({
|
||||
tier: row.tier,
|
||||
score: row.score,
|
||||
wasSuggested: row.wasSuggested,
|
||||
passedVoting: row.passedVoting,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return result.sort((a, b) => a.tier - b.tier);
|
||||
return rows
|
||||
.map((row) => ({
|
||||
userId: row.id,
|
||||
tier: row.tier,
|
||||
score: row.score,
|
||||
wasSuggested: row.wasSuggested,
|
||||
passedVoting: row.passedVoting,
|
||||
}))
|
||||
.sort((a, b) => a.tier - b.tier);
|
||||
}
|
||||
|
||||
const plusServerMembersStm = sql.prepare(plusServerMembersSql);
|
||||
|
|
|
|||
|
|
@ -15,4 +15,4 @@ where
|
|||
"PlusVotingResult"."month" = @month
|
||||
and "PlusVotingResult"."year" = @year
|
||||
order by
|
||||
"User"."discordName" collate nocase asc
|
||||
"User"."discordName" collate nocase asc
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import { databaseTimestampToDate } from "./utils/dates";
|
|||
// TODO: 1) move "root checkers" to one file and utils to one file 2) make utils const for more terseness
|
||||
|
||||
type IsAdminUser = Pick<User, "discordId">;
|
||||
function isAdmin(user?: IsAdminUser) {
|
||||
export function isAdmin(user?: IsAdminUser) {
|
||||
return user?.discordId === ADMIN_DISCORD_ID;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,15 +8,15 @@ import { Link, useLoaderData } from "@remix-run/react";
|
|||
import { lastCompletedVoting } from "~/modules/plus-server";
|
||||
import { db } from "~/db";
|
||||
import type { PlusVotingResultByMonthYear } from "~/db/models/plusVotes/queries.server";
|
||||
import type { PlusVotingResult } from "~/db/types";
|
||||
import type { PlusVotingResult, UserWithPlusTier } from "~/db/types";
|
||||
import { roundToNDecimalPlaces } from "~/utils/number";
|
||||
import { makeTitle } from "~/utils/strings";
|
||||
import type { Unpacked } from "~/utils/types";
|
||||
import styles from "~/styles/plus-history.css";
|
||||
import { discordFullName } from "~/utils/strings";
|
||||
import { PLUS_SERVER_DISCORD_URL, userPage } from "~/utils/urls";
|
||||
import clsx from "clsx";
|
||||
import { getUser } from "~/modules/auth";
|
||||
import { isAtLeastFiveDollarTierPatreon } from "~/utils/users";
|
||||
|
||||
export const links: LinksFunction = () => {
|
||||
return [{ rel: "stylesheet", href: styles }];
|
||||
|
|
@ -37,22 +37,76 @@ interface PlusVotingResultsLoaderData {
|
|||
score?: PlusVotingResult["score"];
|
||||
tier: PlusVotingResult["tier"];
|
||||
passedVoting: PlusVotingResult["passedVoting"];
|
||||
betterThan?: number;
|
||||
}[];
|
||||
}
|
||||
|
||||
export const loader: LoaderFunction = async ({ request }) => {
|
||||
const user = await getUser(request);
|
||||
const { results, ownScores } = db.plusVotes.resultsByMontYear({
|
||||
...lastCompletedVoting(new Date()),
|
||||
userId: user?.id,
|
||||
});
|
||||
const { results, scores } = db.plusVotes.resultsByMontYear(
|
||||
lastCompletedVoting(new Date())
|
||||
);
|
||||
|
||||
return json<PlusVotingResultsLoaderData>({
|
||||
results,
|
||||
ownScores: ownScores?.map(scoreForDisplaying),
|
||||
ownScores: ownScores({ scores, user }),
|
||||
});
|
||||
};
|
||||
|
||||
function databaseAvgToPercentage(score: number) {
|
||||
const scoreNormalized = score + 1;
|
||||
|
||||
return roundToNDecimalPlaces((scoreNormalized / 2) * 100);
|
||||
}
|
||||
|
||||
function ownScores({
|
||||
scores,
|
||||
user,
|
||||
}: {
|
||||
scores: PlusVotingResultByMonthYear["scores"];
|
||||
user?: UserWithPlusTier;
|
||||
}) {
|
||||
return scores
|
||||
.filter((score) => {
|
||||
return score.userId === user?.id;
|
||||
})
|
||||
.map((score) => {
|
||||
const showScore =
|
||||
(score.wasSuggested && !score.passedVoting) ||
|
||||
isAtLeastFiveDollarTierPatreon(user);
|
||||
|
||||
const sameTierButNotOwn = (
|
||||
filteredScore: Pick<PlusVotingResult, "tier"> & { userId: number }
|
||||
) =>
|
||||
filteredScore.tier === score.tier && filteredScore.userId !== user?.id;
|
||||
|
||||
const result: {
|
||||
tier: number;
|
||||
score?: number;
|
||||
passedVoting: number;
|
||||
betterThan?: number;
|
||||
} = {
|
||||
tier: score.tier,
|
||||
score: databaseAvgToPercentage(score.score),
|
||||
passedVoting: score.passedVoting,
|
||||
betterThan: roundToNDecimalPlaces(
|
||||
(scores
|
||||
.filter(sameTierButNotOwn)
|
||||
.filter((otherScore) => otherScore.score <= score.score).length /
|
||||
scores.filter(sameTierButNotOwn).length) *
|
||||
100
|
||||
),
|
||||
};
|
||||
|
||||
if (!showScore) result.score = undefined;
|
||||
if (!isAtLeastFiveDollarTierPatreon(user) || !result.passedVoting) {
|
||||
result.betterThan = undefined;
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
export default function PlusVotingResultsPage() {
|
||||
const data = useLoaderData<PlusVotingResultsLoaderData>();
|
||||
|
||||
|
|
@ -76,7 +130,11 @@ export default function PlusVotingResultsPage() {
|
|||
)}{" "}
|
||||
the +{result.tier} voting
|
||||
{typeof result.score === "number"
|
||||
? `, your score was ${result.score}% (at least 50% required to pass)`
|
||||
? `, your score was ${result.score}% ${
|
||||
result.betterThan
|
||||
? `(better than ${result.betterThan}% others)`
|
||||
: "(at least 50% required to pass)"
|
||||
}`
|
||||
: ""}
|
||||
</li>
|
||||
))}
|
||||
|
|
@ -143,21 +201,3 @@ function Results({
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function databaseAvgToPercentage(score: number) {
|
||||
const scoreNormalized = score + 1;
|
||||
|
||||
return roundToNDecimalPlaces((scoreNormalized / 2) * 100);
|
||||
}
|
||||
|
||||
function scoreForDisplaying(
|
||||
score: Unpacked<NonNullable<PlusVotingResultByMonthYear["ownScores"]>>
|
||||
) {
|
||||
const showScore = score.wasSuggested && !score.passedVoting;
|
||||
|
||||
return {
|
||||
tier: score.tier,
|
||||
score: showScore ? databaseAvgToPercentage(score.score) : undefined,
|
||||
passedVoting: score.passedVoting,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
.plus-history__own-scores {
|
||||
width: max-content;
|
||||
padding: var(--s-2);
|
||||
border-radius: var(--rounded);
|
||||
margin: 0 auto;
|
||||
|
|
|
|||
10
app/utils/users.ts
Normal file
10
app/utils/users.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import type { User } from "~/db/types";
|
||||
import { isAdmin } from "~/permissions";
|
||||
|
||||
export function isAtLeastFiveDollarTierPatreon(
|
||||
user?: Pick<User, "patronTier" | "discordId">
|
||||
) {
|
||||
if (!user) return false;
|
||||
|
||||
return isAdmin(user) || (user.patronTier && user.patronTier >= 2);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user