mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-03-21 18:04:39 -05:00
182 lines
4.8 KiB
TypeScript
182 lines
4.8 KiB
TypeScript
import type { Tables, UserWithPlusTier } from "~/db/tables";
|
|
import type * as PlusSuggestionRepository from "~/features/plus-suggestions/PlusSuggestionRepository.server";
|
|
import { isAdmin } from "~/modules/permissions/utils";
|
|
import { allTruthy } from "~/utils/arrays";
|
|
import * as Seasons from "../mmr/core/Seasons";
|
|
import { isVotingActive } from "../plus-voting/core";
|
|
|
|
interface CanAddCommentToSuggestionArgs {
|
|
user?: Pick<UserWithPlusTier, "id" | "plusTier">;
|
|
suggestions: PlusSuggestionRepository.FindAllByMonthItem[];
|
|
suggested: Pick<Tables["User"], "id">;
|
|
targetPlusTier: NonNullable<UserWithPlusTier["plusTier"]>;
|
|
}
|
|
export function canAddCommentToSuggestionFE(
|
|
args: CanAddCommentToSuggestionArgs,
|
|
) {
|
|
return allTruthy([
|
|
!alreadyCommentedByUser(args),
|
|
isPlusServerMember(args.user),
|
|
args.user?.plusTier && args.targetPlusTier >= args.user?.plusTier,
|
|
]);
|
|
}
|
|
|
|
export function canAddCommentToSuggestionBE({
|
|
user,
|
|
suggestions,
|
|
suggested,
|
|
targetPlusTier,
|
|
}: CanAddCommentToSuggestionArgs) {
|
|
return allTruthy([
|
|
canAddCommentToSuggestionFE({
|
|
user,
|
|
suggestions,
|
|
suggested,
|
|
targetPlusTier,
|
|
}),
|
|
playerAlreadySuggested({ suggestions, suggested, targetPlusTier }),
|
|
targetPlusTierIsSmallerOrEqual({ user, targetPlusTier }),
|
|
]);
|
|
}
|
|
|
|
interface CanDeleteCommentArgs {
|
|
suggestionId: Tables["PlusSuggestion"]["id"];
|
|
author: Pick<Tables["User"], "id">;
|
|
user?: Pick<Tables["User"], "id" | "discordId">;
|
|
suggestions: PlusSuggestionRepository.FindAllByMonthItem[];
|
|
}
|
|
export function canDeleteComment(args: CanDeleteCommentArgs) {
|
|
const votingActive =
|
|
process.env.NODE_ENV === "test" ? false : isVotingActive();
|
|
|
|
if (isFirstSuggestion(args)) {
|
|
if (votingActive) return false;
|
|
if (isAdmin(args.user)) return true;
|
|
|
|
return allTruthy([isOwnComment(args), suggestionHasNoOtherComments(args)]);
|
|
}
|
|
|
|
return isOwnComment(args);
|
|
}
|
|
|
|
export function isFirstSuggestion({
|
|
suggestionId,
|
|
suggestions,
|
|
}: Pick<CanDeleteCommentArgs, "suggestionId" | "suggestions">) {
|
|
for (const suggestedUser of Object.values(suggestions).flat()) {
|
|
for (const [i, entry] of suggestedUser.entries.entries()) {
|
|
if (entry.id !== suggestionId) continue;
|
|
|
|
return i === 0;
|
|
}
|
|
}
|
|
|
|
throw new Error(`Invalid suggestion id: ${suggestionId}`);
|
|
}
|
|
|
|
function alreadyCommentedByUser({
|
|
user,
|
|
suggestions,
|
|
suggested,
|
|
targetPlusTier,
|
|
}: CanAddCommentToSuggestionArgs) {
|
|
return suggestions.some(
|
|
(suggestion) =>
|
|
suggestion.tier === targetPlusTier &&
|
|
suggestion.suggested.id === suggested.id &&
|
|
suggestion.entries.some((entry) => entry.author.id === user?.id),
|
|
);
|
|
}
|
|
|
|
function playerAlreadySuggested({
|
|
suggestions,
|
|
suggested,
|
|
targetPlusTier,
|
|
}: Pick<
|
|
CanAddCommentToSuggestionArgs,
|
|
"suggestions" | "suggested" | "targetPlusTier"
|
|
>) {
|
|
return suggestions.some(
|
|
(suggestion) =>
|
|
suggestion.suggested.id === suggested.id &&
|
|
suggestion.tier === targetPlusTier,
|
|
);
|
|
}
|
|
|
|
function targetPlusTierIsSmallerOrEqual({
|
|
user,
|
|
targetPlusTier,
|
|
}: Pick<CanAddCommentToSuggestionArgs, "user" | "targetPlusTier">) {
|
|
return user?.plusTier && user.plusTier <= targetPlusTier;
|
|
}
|
|
|
|
function isOwnComment({ author, user }: CanDeleteCommentArgs) {
|
|
return author.id === user?.id;
|
|
}
|
|
|
|
function suggestionHasNoOtherComments({
|
|
suggestions,
|
|
suggestionId,
|
|
}: Pick<CanDeleteCommentArgs, "suggestionId" | "suggestions">) {
|
|
for (const suggestedUser of Object.values(suggestions).flat()) {
|
|
for (const entry of suggestedUser.entries) {
|
|
if (entry.id !== suggestionId) continue;
|
|
|
|
return suggestedUser.entries.length === 1;
|
|
}
|
|
}
|
|
|
|
throw new Error(`Invalid suggestion id: ${suggestionId}`);
|
|
}
|
|
|
|
interface CanEditSuggestionArgs {
|
|
suggestionId: Tables["PlusSuggestion"]["id"];
|
|
author: Pick<Tables["User"], "id">;
|
|
user?: Pick<Tables["User"], "id">;
|
|
suggestions: PlusSuggestionRepository.FindAllByMonthItem[];
|
|
}
|
|
export function canEditSuggestion(args: CanEditSuggestionArgs) {
|
|
const votingActive =
|
|
process.env.NODE_ENV === "test" ? false : isVotingActive();
|
|
|
|
return allTruthy([
|
|
!votingActive,
|
|
isFirstSuggestion(args),
|
|
args.author.id === args.user?.id,
|
|
]);
|
|
}
|
|
|
|
interface CanSuggestNewUserArgs {
|
|
user?: Pick<UserWithPlusTier, "id" | "plusTier">;
|
|
suggestions: PlusSuggestionRepository.FindAllByMonthItem[];
|
|
}
|
|
export function canSuggestNewUser({
|
|
user,
|
|
suggestions,
|
|
}: CanSuggestNewUserArgs) {
|
|
const votingActive =
|
|
process.env.NODE_ENV === "test" ? false : isVotingActive();
|
|
|
|
const existsSeason = Seasons.current() || Seasons.next();
|
|
|
|
return allTruthy([
|
|
!votingActive,
|
|
!hasUserSuggestedThisMonth({ user, suggestions }),
|
|
isPlusServerMember(user),
|
|
existsSeason,
|
|
]);
|
|
}
|
|
|
|
function isPlusServerMember(user?: Pick<UserWithPlusTier, "plusTier">) {
|
|
return Boolean(user?.plusTier);
|
|
}
|
|
|
|
function hasUserSuggestedThisMonth({
|
|
user,
|
|
suggestions,
|
|
}: Pick<CanSuggestNewUserArgs, "user" | "suggestions">) {
|
|
return suggestions.some(
|
|
(suggestion) => suggestion.entries[0].author.id === user?.id,
|
|
);
|
|
}
|