Change comments data model a bit + adjust sorting

This commit is contained in:
Kalle
2022-05-28 10:49:44 +03:00
parent 6cd5225ceb
commit 5d276fcf2c
5 changed files with 152 additions and 132 deletions

View File

@@ -27,6 +27,7 @@ module.exports = {
"@typescript-eslint/no-unsafe-call": 0,
"@typescript-eslint/no-unsafe-argument": 0,
"@typescript-eslint/no-non-null-assertion": 0,
"@typescript-eslint/no-explicit-any": 0,
},
settings: {
react: {

View File

@@ -32,95 +32,110 @@ export function create(
createStm.run(args);
}
const findStm = sql.prepare(`
WITH gs as (
SELECT
"tier",
json_object(
'info',
json_object(
'id',
suggested."id",
'discordId',
suggested."discordId",
'discordName',
suggested."discordName",
'discordDiscriminator',
suggested."discordDiscriminator",
'discordAvatar',
suggested."discordAvatar"
),
'suggestions',
json_group_array(
json_object(
'id',
"PlusSuggestion"."id",
'author',
json_object(
'id',
author."id",
'discordId',
author."discordId",
'discordName',
author."discordName",
'discordDiscriminator',
author."discordDiscriminator"
),
'createdAt',
"createdAt",
'text',
"text"
)
)
) as users
FROM
"PlusSuggestion"
JOIN "User" AS author ON "PlusSuggestion"."authorId" = author."id"
JOIN "User" AS suggested ON "PlusSuggestion"."suggestedId" = suggested."id"
WHERE
"month" = $month
AND "year" = $year
AND "tier" >= $plusTier
GROUP BY
"suggestedId",
"tier"
)
SELECT
tier,
json_group_array(users) as users
from
gs
GROUP BY
"tier"
const findVisibleForUserStm = sql.prepare(`
SELECT
suggestion."id",
suggestion."createdAt",
suggestion."text",
suggestion."tier",
author."id" as "authorId",
author."discordId" as "authorDiscordId",
author."discordName" as "authorDiscordName",
author."discordDiscriminator" as "authorDiscordDiscriminator",
suggested."id" as "suggestedId",
suggested."discordId" as "suggestedDiscordId",
suggested."discordName" as "suggestedDiscordName",
suggested."discordDiscriminator" as "suggestedDiscordDiscriminator",
suggested."discordAvatar" as "suggestedDiscordAvatar"
FROM "PlusSuggestion" as suggestion
JOIN "User" AS author ON suggestion."authorId" = author."id"
JOIN "User" AS suggested ON suggestion."suggestedId" = suggested."id"
WHERE
"month" = $month
AND "year" = $year
AND "tier" >= $plusTier
ORDER BY
"createdAt" ASC
`);
export type FindResult = {
tier: number;
users: {
info: Pick<
export interface FindVisibleForUserSuggestedUserInfo {
info: Pick<
User,
| "id"
| "discordId"
| "discordName"
| "discordDiscriminator"
| "discordAvatar"
>;
suggestions: (Pick<PlusSuggestion, "id" | "createdAt" | "text"> & {
author: Pick<
User,
| "id"
| "discordId"
| "discordName"
| "discordDiscriminator"
| "discordAvatar"
"id" | "discordId" | "discordName" | "discordDiscriminator"
>;
suggestions: (Pick<PlusSuggestion, "id" | "createdAt" | "text"> & {
author: Pick<
User,
"id" | "discordId" | "discordName" | "discordDiscriminator"
>;
})[];
}[];
}[];
})[];
}
export interface FindVisibleForUser {
[tier: string]: FindVisibleForUserSuggestedUserInfo[];
}
export function find(args: MonthYear & Pick<User, "plusTier">) {
if (!args.plusTier) return;
export function findVisibleForUser(
args: MonthYear & Pick<User, "plusTier">
): FindVisibleForUser {
return sortNewestPlayersToBeSuggestedFirst(
mapFindVisibleForUserRowsToResult(findVisibleForUserStm.all(args))
);
}
return findStm.all(args).map((row) => ({
...row,
users: JSON.parse(row.users).map(JSON.parse),
})) as FindResult;
function mapFindVisibleForUserRowsToResult(rows: any[]): FindVisibleForUser {
return rows.reduce((result: FindVisibleForUser, row) => {
if (!result[row.tier]) result[row.tier] = [];
const suggestionInfo = {
id: row.id,
createdAt: row.createdAt,
text: row.text,
author: {
id: row.authorId,
discordId: row.authorDiscordId,
discordName: row.authorDiscordName,
discordDiscriminator: row.authorDiscordDiscriminator,
},
};
const existingSuggestion = result[row.tier].find(
(suggestion) => suggestion.info.id === row.suggestedId
);
if (existingSuggestion) {
existingSuggestion.suggestions.push(suggestionInfo);
} else {
result[row.tier].push({
info: {
id: row.suggestedId,
discordId: row.suggestedDiscordId,
discordName: row.suggestedDiscordName,
discordDiscriminator: row.suggestedDiscriminator,
discordAvatar: row.suggestedDiscordAvatar,
},
suggestions: [suggestionInfo],
});
}
return result;
}, {});
}
function sortNewestPlayersToBeSuggestedFirst(
suggestions: FindVisibleForUser
): FindVisibleForUser {
return Object.fromEntries(
Object.entries(suggestions).map(([tier, suggestions]) => [
tier,
suggestions.sort(
(a, b) => b.suggestions[0].createdAt - a.suggestions[0].createdAt
),
])
);
}
const tiersSuggestedForStm = sql.prepare(`

View File

@@ -4,7 +4,7 @@ import { allTruthy } from "./utils/arrays";
interface CanAddCommentToSuggestionFEArgs {
user?: Pick<User, "id">;
suggestions: plusSuggestions.FindResult;
suggestions: plusSuggestions.FindVisibleForUser;
suggested: { id: User["id"]; plusTier: NonNullable<User["plusTier"]> };
}
export function canAddCommentToSuggestionFE(
@@ -56,9 +56,8 @@ function alreadyCommentedByUser({
suggested,
}: CanAddCommentToSuggestionFEArgs) {
return Boolean(
suggestions
.find(({ tier }) => tier === suggested.plusTier)
?.users.find((u) => u.info.id === suggested.id)
suggestions[suggested.plusTier]
?.find((u) => u.info.id === suggested.id)
?.suggestions.some((s) => s.author.id === user?.id)
);
}
@@ -68,9 +67,7 @@ function playerAlreadySuggested({
suggested,
}: Pick<CanAddCommentToSuggestionBEArgs, "suggestions" | "suggested">) {
return Boolean(
suggestions
.find(({ tier }) => tier === suggested.plusTier)
?.users.find((u) => u.info.id === suggested.id)
suggestions[suggested.plusTier]?.find((u) => u.info.id === suggested.id)
);
}

View File

@@ -28,7 +28,6 @@ import {
validate,
} from "~/utils/remix";
import { discordFullName } from "~/utils/strings";
import type { Unpacked } from "~/utils/types";
import { actualNumber } from "~/utils/zod";
export const links: LinksFunction = () => {
@@ -53,13 +52,13 @@ export const action: ActionFunction = async ({ request }) => {
});
const user = await requireUser(request);
const suggestions = db.plusSuggestions.find({
const suggestions = db.plusSuggestions.findVisibleForUser({
...upcomingVoting(new Date()),
plusTier: user.plusTier,
});
const targetSuggestion = suggestions
?.flatMap((s) => s.users)
const targetSuggestion = Object.values(suggestions)
?.flat()
.flatMap((u) => u.suggestions)
.find((s) => s.id === data.suggestionId);
@@ -72,7 +71,7 @@ export const action: ActionFunction = async ({ request }) => {
};
export interface PlusSuggestionsLoaderData {
suggestions?: plusSuggestions.FindResult;
suggestions?: plusSuggestions.FindVisibleForUser;
suggestedForTiers: number[];
}
@@ -80,7 +79,7 @@ export const loader: LoaderFunction = async ({ request }) => {
const user = await requireUser(request);
return json<PlusSuggestionsLoaderData>({
suggestions: db.plusSuggestions.find({
suggestions: db.plusSuggestions.findVisibleForUser({
...upcomingVoting(new Date()),
plusTier: user.plusTier,
}),
@@ -94,16 +93,15 @@ export const loader: LoaderFunction = async ({ request }) => {
export default function PlusSuggestionsPage() {
const data = useLoaderData<PlusSuggestionsLoaderData>();
const [tierVisible, setTierVisible] = React.useState(
data.suggestions?.[0].tier ?? 0
tierVisibleInitialState(data.suggestions)
);
if (!data.suggestions) {
return <SuggestedForInfo />;
}
const visibleSuggestions = data.suggestions.find(
({ tier }) => tier === tierVisible
);
invariant(tierVisible);
const visibleSuggestions = data.suggestions[tierVisible];
invariant(visibleSuggestions);
return (
@@ -113,28 +111,32 @@ export default function PlusSuggestionsPage() {
<SuggestedForInfo />
<div className="stack md">
<div className="plus__radios">
{data.suggestions.map(({ tier, users }) => {
const id = String(tier);
return (
<div key={id} className="plus__radio-container">
<label htmlFor={id} className="plus__radio-label">
+{tier}{" "}
<span className="plus__users-count">({users.length})</span>
</label>
<input
id={id}
name="tier"
type="radio"
checked={tierVisible === tier}
onChange={() => setTierVisible(tier)}
data-cy={`plus${tier}-radio`}
/>
</div>
);
})}
{Object.entries(data.suggestions)
.sort((a, b) => Number(a[0]) - Number(b[0]))
.map(([tier, suggestions]) => {
const id = String(tier);
return (
<div key={id} className="plus__radio-container">
<label htmlFor={id} className="plus__radio-label">
+{tier}{" "}
<span className="plus__users-count">
({suggestions.length})
</span>
</label>
<input
id={id}
name="tier"
type="radio"
checked={tierVisible === tier}
onChange={() => setTierVisible(tier)}
data-cy={`plus${tier}-radio`}
/>
</div>
);
})}
</div>
<div className="stack lg">
{visibleSuggestions.users.map((u) => (
{visibleSuggestions.map((u) => (
<SuggestedUser
key={`${u.info.id}-${tierVisible}`}
suggested={u}
@@ -148,6 +150,13 @@ export default function PlusSuggestionsPage() {
);
}
function tierVisibleInitialState(
suggestions?: plusSuggestions.FindVisibleForUser
) {
if (!suggestions) return;
return String(Math.min(...Object.keys(suggestions).map(Number)));
}
function SuggestedForInfo() {
const data = useLoaderData<PlusSuggestionsLoaderData>();
const user = useUser();
@@ -178,10 +187,8 @@ function SuggestedUser({
suggested,
tier,
}: {
suggested: Unpacked<
Unpacked<NonNullable<PlusSuggestionsLoaderData["suggestions"]>>["users"]
>;
tier: number;
suggested: plusSuggestions.FindVisibleForUserSuggestedUserInfo;
tier: string;
}) {
const data = useLoaderData<PlusSuggestionsLoaderData>();
const user = useUser();
@@ -200,7 +207,7 @@ function SuggestedUser({
{canAddCommentToSuggestionFE({
user,
suggestions: data.suggestions,
suggested: { id: suggested.info.id, plusTier: tier },
suggested: { id: suggested.info.id, plusTier: Number(tier) },
}) ? (
// TODO: resetScroll={false} https://twitter.com/ryanflorence/status/1527775882797907969
<LinkButton

View File

@@ -34,7 +34,7 @@ export const action: ActionFunction = async ({ request }) => {
});
const user = await requireUser(request);
const suggestions = db.plusSuggestions.find({
const suggestions = db.plusSuggestions.findVisibleForUser({
...upcomingVoting(new Date()),
plusTier: user.plusTier,
});
@@ -65,19 +65,19 @@ export default function PlusCommentModalPage() {
const data = matches.at(-2)!.data as PlusSuggestionsLoaderData;
const targetUserId = Number(params.userId);
const tierSuggestedTo = Number(params.tier);
const userBeingCommented = data.suggestions
?.find(({ tier }) => tier === tierSuggestedTo)
?.users.find((u) => u.info.id === targetUserId);
const tierSuggestedTo = String(params.tier);
invariant(data.suggestions);
const userBeingCommented = data.suggestions[tierSuggestedTo]?.find(
(u) => u.info.id === targetUserId
);
if (
!userBeingCommented ||
!canAddCommentToSuggestionFE({
user,
suggestions: data.suggestions,
suggested: { id: targetUserId, plusTier: tierSuggestedTo },
suggested: { id: targetUserId, plusTier: Number(tierSuggestedTo) },
})
) {
return <Redirect to={PLUS_SUGGESTIONS_PAGE} />;