Restructure and add notes

This commit is contained in:
Kalle
2022-06-02 17:34:46 +03:00
parent 03c7404720
commit 5f66dddb08
4 changed files with 97 additions and 35 deletions

View File

@@ -1,6 +1,6 @@
import type * as plusSuggestions from "~/db/models/plusSuggestions.server";
import { monthsVotingRange } from "./core/plus";
import type { User } from "./db/types";
import type { PlusSuggestion, User } from "./db/types";
import { allTruthy } from "./utils/arrays";
// TODO: 1) move "root checkers" to one file and utils to one file 2) make utils const for more terseness
@@ -39,13 +39,34 @@ export function canAddCommentToSuggestionBE({
}
interface CanDeleteCommentArgs {
suggestionId: PlusSuggestion["id"];
author: Pick<User, "id">;
user?: Pick<User, "id">;
suggestions: plusSuggestions.FindVisibleForUser;
}
export function canDeleteComment(args: CanDeleteCommentArgs) {
if (isFirstSuggestion(args)) {
return allTruthy([!isVotingActive(), isOwnComment(args)]);
}
return isOwnComment(args);
}
function isFirstSuggestion({
suggestionId,
suggestions,
}: Pick<CanDeleteCommentArgs, "suggestionId" | "suggestions">) {
for (const suggestedUser of Object.values(suggestions).flat()) {
for (const [i, suggestion] of suggestedUser.suggestions.entries()) {
if (suggestion.id !== suggestionId) continue;
return i === 0;
}
}
throw new Error(`Invalid suggestion id: ${suggestionId}`);
}
function alreadyCommentedByUser({
user,
suggestions,
@@ -92,7 +113,7 @@ export function canSuggestNewUserFE({
suggestions,
}: CanSuggestNewUserFEArgs) {
return allTruthy([
!votingIsActive(),
!isVotingActive(),
!hasUserSuggestedThisMonth({ user, suggestions }),
isPlusServerMember(user),
]);
@@ -116,7 +137,7 @@ export function canSuggestNewUserBE({
]);
}
function votingIsActive() {
function isVotingActive() {
const now = new Date();
const { endDate, startDate } = monthsVotingRange({
month: now.getMonth(),

View File

@@ -18,6 +18,7 @@ import { TrashIcon } from "~/components/icons/Trash";
import { upcomingVoting } from "~/core/plus";
import { db } from "~/db";
import type * as plusSuggestions from "~/db/models/plusSuggestions.server";
import type { PlusSuggestion } from "~/db/types";
import { useUser } from "~/hooks/useUser";
import {
canAddCommentToSuggestionFE,
@@ -69,8 +70,16 @@ export const action: ActionFunction = async ({ request }) => {
.find((s) => s.id === data.suggestionId)
: undefined;
validate(suggestions);
validate(targetSuggestion);
validate(canDeleteComment({ user, author: targetSuggestion.author }));
validate(
canDeleteComment({
user,
author: targetSuggestion.author,
suggestionId: data.suggestionId,
suggestions,
})
);
db.plusSuggestions.del(data.suggestionId);
@@ -224,6 +233,7 @@ function SuggestedUser({
discordId={suggested.info.discordId}
size="md"
/>
{/* xxx: can cause page to overflow when long e.g. Buckinghamshire */}
<h2>{suggested.info.discordName}</h2>
{canAddCommentToSuggestionFE({
user,
@@ -251,38 +261,67 @@ function SuggestedUser({
Comments ({suggested.suggestions.length})
</summary>
<div className="stack sm mt-2">
{suggested.suggestions.map((s) => (
<fieldset key={s.id} className="plus__comment">
<legend>{discordFullName(s.author)}</legend>
{s.text}
<div className="stack vertical xs items-center">
<span className="plus__comment-time">
<time>
{databaseTimestampToDate(s.createdAt).toLocaleString()}
</time>
</span>
{canDeleteComment({ author: s.author, user }) ? (
<FormWithConfirm
fields={[["suggestionId", s.id]]}
dialogHeading={`Delete your comment to ${suggested.info.discordName}'s +${tier} suggestion?`}
>
{/* xxx: what when this is the first suggestion... it would trigger different behavior so confusing maybe? */}
<Button
className="plus__delete-button"
icon={<TrashIcon />}
variant="minimal-destructive"
aria-label="Delete comment"
data-cy="delete-comment-button"
{suggested.suggestions.map((suggestion) => {
invariant(data.suggestions);
return (
<fieldset key={suggestion.id} className="plus__comment">
<legend>{discordFullName(suggestion.author)}</legend>
{suggestion.text}
<div className="stack vertical xs items-center">
<span className="plus__comment-time">
<time>
{databaseTimestampToDate(
suggestion.createdAt
).toLocaleString()}
</time>
</span>
{canDeleteComment({
author: suggestion.author,
user,
suggestionId: suggestion.id,
suggestions: data.suggestions,
}) ? (
<CommentDeleteButton
suggestionId={suggestion.id}
tier={tier}
suggestedDiscordName={suggested.info.discordName}
/>
</FormWithConfirm>
) : null}
</div>
</fieldset>
))}
) : null}
</div>
</fieldset>
);
})}
</div>
</details>
</div>
);
}
function CommentDeleteButton({
suggestionId,
tier,
suggestedDiscordName,
}: {
suggestionId: PlusSuggestion["id"];
tier: string;
suggestedDiscordName: string;
}) {
return (
<FormWithConfirm
fields={[["suggestionId", suggestionId]]}
// TODO: Delete your suggestion of suggestedDiscordName and 3 comments to it? + different behavior of the delete event
dialogHeading={`Delete your comment to ${suggestedDiscordName}'s +${tier} suggestion?`}
>
{/* xxx: what when this is the first suggestion... it would trigger different behavior so confusing maybe? */}
<Button
className="plus__delete-button"
icon={<TrashIcon />}
variant="minimal-destructive"
aria-label="Delete comment"
data-cy="delete-comment-button"
/>
</FormWithConfirm>
);
}
export const CatchBoundary = Catcher;

View File

@@ -1,6 +1,6 @@
import type { ActionFunction } from "@remix-run/node";
import { redirect } from "@remix-run/node";
import { Form, useMatches, useNavigate, useParams } from "@remix-run/react";
import { Form, useMatches, useParams } from "@remix-run/react";
import { z } from "zod";
import { Button, LinkButton } from "~/components/Button";
import { Dialog } from "~/components/Dialog";
@@ -42,7 +42,8 @@ export const action: ActionFunction = async ({ request }) => {
canAddCommentToSuggestionBE({
suggestions,
user,
suggested: { id: data.suggestedId, plusTier: data.tier },
suggested: { id: data.suggestedId },
targetPlusTier: data.tier,
})
);
@@ -58,7 +59,6 @@ export const action: ActionFunction = async ({ request }) => {
export default function PlusCommentModalPage() {
const user = useUser();
const matches = useMatches();
const navigate = useNavigate();
const params = useParams();
const data = matches.at(-2)!.data as PlusSuggestionsLoaderData;
@@ -75,7 +75,8 @@ export default function PlusCommentModalPage() {
!canAddCommentToSuggestionFE({
user,
suggestions: data.suggestions,
suggested: { id: targetUserId, plusTier: Number(tierSuggestedTo) },
suggested: { id: targetUserId },
targetPlusTier: Number(tierSuggestedTo),
})
) {
return <Redirect to={PLUS_SUGGESTIONS_PAGE} />;

View File

@@ -26,6 +26,7 @@ export async function parseRequestFormData<T extends z.ZodTypeAny>({
}: {
request: Request;
schema: T;
// xxx: get rid of this
useBody?: boolean;
}): Promise<z.infer<T>> {
try {