diff --git a/app/permissions.ts b/app/permissions.ts index fe5c8f049..994608100 100644 --- a/app/permissions.ts +++ b/app/permissions.ts @@ -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?: Pick; + suggestions: plusSuggestions.FindVisibleForUser; } export function canDeleteComment(args: CanDeleteCommentArgs) { + if (isFirstSuggestion(args)) { + return allTruthy([!isVotingActive(), isOwnComment(args)]); + } + return isOwnComment(args); } +function isFirstSuggestion({ + suggestionId, + suggestions, +}: Pick) { + 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(), diff --git a/app/routes/plus/suggestions.tsx b/app/routes/plus/suggestions.tsx index 50c1e8d12..048d55b15 100644 --- a/app/routes/plus/suggestions.tsx +++ b/app/routes/plus/suggestions.tsx @@ -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 */}

{suggested.info.discordName}

{canAddCommentToSuggestionFE({ user, @@ -251,38 +261,67 @@ function SuggestedUser({ Comments ({suggested.suggestions.length})
- {suggested.suggestions.map((s) => ( -
- {discordFullName(s.author)} - {s.text} -
- - - - {canDeleteComment({ author: s.author, user }) ? ( - - {/* xxx: what when this is the first suggestion... it would trigger different behavior so confusing maybe? */} -
+
+ ); + })}
); } +function CommentDeleteButton({ + suggestionId, + tier, + suggestedDiscordName, +}: { + suggestionId: PlusSuggestion["id"]; + tier: string; + suggestedDiscordName: string; +}) { + return ( + + {/* xxx: what when this is the first suggestion... it would trigger different behavior so confusing maybe? */} +