sendou.ink/app/features/plus-suggestions/actions/plus.suggestions.server.ts

130 lines
3.3 KiB
TypeScript

import { type ActionFunction, redirect } from "react-router";
import { requireUser } from "~/features/auth/core/user.server";
import * as PlusSuggestionRepository from "~/features/plus-suggestions/PlusSuggestionRepository.server";
import {
isVotingActive,
nextNonCompletedVoting,
rangeToMonthYear,
} from "~/features/plus-voting/core";
import { parseFormData } from "~/form/parse.server";
import invariant from "~/utils/invariant";
import { badRequestIfFalsy, errorToastIfFalsy } from "~/utils/remix.server";
import { assertUnreachable } from "~/utils/types";
import { plusSuggestionPage } from "~/utils/urls";
import { suggestionActionSchema } from "../plus-suggestions-schemas";
import {
canDeleteComment,
canEditSuggestion,
isFirstSuggestion,
} from "../plus-suggestions-utils";
export const action: ActionFunction = async ({ request }) => {
const user = requireUser();
const votingMonthYear = rangeToMonthYear(
badRequestIfFalsy(nextNonCompletedVoting(new Date())),
);
const result = await parseFormData({
request,
schema: suggestionActionSchema,
});
if (!result.success) {
return { fieldErrors: result.fieldErrors };
}
const data = result.data;
switch (data._action) {
case "EDIT_SUGGESTION": {
const suggestions =
await PlusSuggestionRepository.findAllByMonth(votingMonthYear);
const suggestion = suggestions.find((s) =>
s.entries.some((entry) => entry.id === data.suggestionId),
);
invariant(suggestion);
const entry = suggestion.entries.find((e) => e.id === data.suggestionId);
invariant(entry);
errorToastIfFalsy(
canEditSuggestion({
user,
author: entry.author,
suggestionId: data.suggestionId,
suggestions,
}),
"No permissions to edit this suggestion",
);
await PlusSuggestionRepository.updateTextById(
data.suggestionId,
data.comment,
);
throw redirect(plusSuggestionPage({ tier: suggestion.tier }));
}
case "DELETE_COMMENT": {
const suggestions =
await PlusSuggestionRepository.findAllByMonth(votingMonthYear);
const suggestionToDelete = suggestions.find((suggestion) =>
suggestion.entries.some((entry) => entry.id === data.suggestionId),
);
invariant(suggestionToDelete);
const entryToDelete = suggestionToDelete.entries.find(
(entry) => entry.id === data.suggestionId,
);
invariant(entryToDelete);
errorToastIfFalsy(
canDeleteComment({
user,
author: entryToDelete.author,
suggestionId: data.suggestionId,
suggestions,
}),
"No permissions to delete this comment",
);
const suggestionHasComments = suggestionToDelete.entries.length > 1;
if (
suggestionHasComments &&
isFirstSuggestion({
suggestionId: data.suggestionId,
suggestions,
})
) {
// admin only action
await PlusSuggestionRepository.deleteWithCommentsBySuggestedUserId({
tier: suggestionToDelete.tier,
userId: suggestionToDelete.suggested.id,
...votingMonthYear,
});
} else {
await PlusSuggestionRepository.deleteById(data.suggestionId);
}
break;
}
case "DELETE_SUGGESTION_OF_THEMSELVES": {
invariant(!isVotingActive(), "Voting is active");
await PlusSuggestionRepository.deleteWithCommentsBySuggestedUserId({
tier: data.tier,
userId: user.id,
...votingMonthYear,
});
break;
}
default: {
assertUnreachable(data);
}
}
return null;
};