mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-05-05 20:56:13 -05:00
can submit votes
This commit is contained in:
parent
d0ed62e5a5
commit
4486f317b3
|
|
@ -22,6 +22,12 @@ const plusApi = createRouter()
|
|||
return service.getUsersForVoting(user.id);
|
||||
},
|
||||
})
|
||||
.query("hasVoted", {
|
||||
resolve({ ctx }) {
|
||||
const user = throwIfNotLoggedIn(ctx.user);
|
||||
return service.hasVoted(user.id);
|
||||
},
|
||||
})
|
||||
.mutation("suggestion", {
|
||||
input: suggestionFullSchema,
|
||||
resolve({ input, ctx }) {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { Alert, AlertIcon } from "@chakra-ui/alert";
|
||||
import { Button } from "@chakra-ui/button";
|
||||
import { Box, HStack } from "@chakra-ui/layout";
|
||||
import { Progress } from "@chakra-ui/progress";
|
||||
|
|
@ -5,6 +6,7 @@ import Markdown from "components/common/Markdown";
|
|||
import SubText from "components/common/SubText";
|
||||
import UserAvatar from "components/common/UserAvatar";
|
||||
import { useEffect } from "react";
|
||||
import { getVotingRange } from "utils/plus";
|
||||
import { getFullUsername } from "utils/strings";
|
||||
import usePlusVoting from "../hooks/usePlusVoting";
|
||||
import { PlusVotingButton } from "./PlusVotingButton";
|
||||
|
|
@ -19,17 +21,29 @@ export default function PlusVotingPage() {
|
|||
progress,
|
||||
previousUser,
|
||||
goBack,
|
||||
submit,
|
||||
status,
|
||||
hasVoted,
|
||||
} = usePlusVoting();
|
||||
|
||||
useEffect(() => {
|
||||
//redirect!!
|
||||
}, [shouldRedirect]);
|
||||
|
||||
if (isLoading || !plusStatus || !currentUser) return null;
|
||||
if (isLoading || !plusStatus) return null;
|
||||
|
||||
if (hasVoted)
|
||||
return (
|
||||
<Alert status="success" variant="subtle">
|
||||
<AlertIcon />
|
||||
Votes succesfully recorded. Voting ends{" "}
|
||||
{getVotingRange().endDate.toLocaleString()}.
|
||||
</Alert>
|
||||
);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
{previousUser && (
|
||||
{previousUser ? (
|
||||
<Box textAlign="center" mb={6}>
|
||||
<UserAvatar user={previousUser} isSmall />
|
||||
<Box my={2} fontSize="sm">
|
||||
|
|
@ -47,55 +61,87 @@ export default function PlusVotingPage() {
|
|||
{previousUser.score}
|
||||
</Button>
|
||||
</Box>
|
||||
) : (
|
||||
<Box textAlign="center" mb={14} visibility="hidden">
|
||||
<Box my={2} fontSize="sm">
|
||||
asd
|
||||
</Box>
|
||||
<Button
|
||||
borderRadius="50%"
|
||||
height={10}
|
||||
width={10}
|
||||
variant="outline"
|
||||
colorScheme="theme"
|
||||
onClick={goBack}
|
||||
>
|
||||
{2}
|
||||
</Button>
|
||||
</Box>
|
||||
)}
|
||||
<Progress value={progress} size="xs" colorScheme="pink" />
|
||||
<Box mt={6} textAlign="center">
|
||||
<UserAvatar user={currentUser} size="2xl" mx="auto" />
|
||||
<Box fontSize="2rem" fontWeight="bold" mt={2}>
|
||||
{getFullUsername(currentUser)}
|
||||
</Box>
|
||||
</Box>
|
||||
<HStack justify="center" spacing={4} mt={2}>
|
||||
{currentUser.region === plusStatus.region && (
|
||||
<PlusVotingButton
|
||||
number={-2}
|
||||
onClick={() =>
|
||||
handleVote({ userId: currentUser.userId, score: -2 })
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<PlusVotingButton
|
||||
number={-1}
|
||||
onClick={() => handleVote({ userId: currentUser.userId, score: -1 })}
|
||||
/>
|
||||
<PlusVotingButton
|
||||
number={1}
|
||||
onClick={() => handleVote({ userId: currentUser.userId, score: 1 })}
|
||||
/>
|
||||
{currentUser.region === plusStatus.region && (
|
||||
<PlusVotingButton
|
||||
number={2}
|
||||
onClick={() => handleVote({ userId: currentUser.userId, score: 2 })}
|
||||
/>
|
||||
)}
|
||||
</HStack>
|
||||
{currentUser.suggestions && (
|
||||
<Box mt={5}>
|
||||
<SubText>Suggestions</SubText>
|
||||
{currentUser.suggestions.map((suggestion) => {
|
||||
return (
|
||||
<Box key={suggestion.suggesterUser.id} mt={4} fontSize="sm">
|
||||
"{suggestion.description}" -{" "}
|
||||
{getFullUsername(suggestion.suggesterUser)}
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
{currentUser && (
|
||||
<>
|
||||
<Box mt={6} textAlign="center">
|
||||
<UserAvatar user={currentUser} size="2xl" mx="auto" />
|
||||
<Box fontSize="2rem" fontWeight="bold" mt={2}>
|
||||
{getFullUsername(currentUser)}
|
||||
</Box>
|
||||
</Box>
|
||||
<HStack justify="center" spacing={4} mt={2}>
|
||||
{currentUser.region === plusStatus.region && (
|
||||
<PlusVotingButton
|
||||
number={-2}
|
||||
onClick={() =>
|
||||
handleVote({ userId: currentUser.userId, score: -2 })
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<PlusVotingButton
|
||||
number={-1}
|
||||
onClick={() =>
|
||||
handleVote({ userId: currentUser.userId, score: -1 })
|
||||
}
|
||||
/>
|
||||
<PlusVotingButton
|
||||
number={1}
|
||||
onClick={() =>
|
||||
handleVote({ userId: currentUser.userId, score: 1 })
|
||||
}
|
||||
/>
|
||||
{currentUser.region === plusStatus.region && (
|
||||
<PlusVotingButton
|
||||
number={2}
|
||||
onClick={() =>
|
||||
handleVote({ userId: currentUser.userId, score: 2 })
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</HStack>
|
||||
{currentUser.suggestions && (
|
||||
<Box mt={5}>
|
||||
<SubText>Suggestions</SubText>
|
||||
{currentUser.suggestions.map((suggestion) => {
|
||||
return (
|
||||
<Box key={suggestion.suggesterUser.id} mt={4} fontSize="sm">
|
||||
"{suggestion.description}" -{" "}
|
||||
{getFullUsername(suggestion.suggesterUser)}
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
)}
|
||||
{currentUser.bio && (
|
||||
<Box mt={4}>
|
||||
<SubText mb={4}>Bio</SubText>
|
||||
<Markdown value={currentUser.bio} smallHeaders />
|
||||
</Box>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{currentUser.bio && (
|
||||
<Box mt={4}>
|
||||
<SubText mb={4}>Bio</SubText>
|
||||
<Markdown value={currentUser.bio} smallHeaders />
|
||||
{previousUser && !currentUser && (
|
||||
<Box onClick={submit} mt={6} textAlign="center">
|
||||
{" "}
|
||||
<Button isLoading={status === "loading"}>Submit</Button>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { useToast } from "@chakra-ui/toast";
|
||||
import { useUser } from "hooks/common";
|
||||
import { useState } from "react";
|
||||
import { getToastOptions } from "utils/getToastOptions";
|
||||
import { trpc } from "utils/trpc";
|
||||
import { Unpacked } from "utils/types";
|
||||
import { votesSchema } from "utils/validators/votes";
|
||||
|
|
@ -9,29 +11,41 @@ export default function usePlusVoting() {
|
|||
const [currentIndex, setCurrentIndex] = useState(0);
|
||||
const [votes, setVotes] = useState<z.infer<typeof votesSchema>>([]);
|
||||
|
||||
const toast = useToast();
|
||||
const [user] = useUser();
|
||||
const { data: ballotsData, isLoading: isLoadingBallots } = trpc.useQuery([
|
||||
|
||||
const { data: hasVoted, isLoading: hasVotedIsLoading } = trpc.useQuery([
|
||||
"plus.hasVoted",
|
||||
]);
|
||||
const { data: usersForVoting, isLoading: isLoadingBallots } = trpc.useQuery([
|
||||
"plus.usersForVoting",
|
||||
]);
|
||||
const { data: statusesData, isLoading: isLoadingStatuses } = trpc.useQuery([
|
||||
const { data: statuses, isLoading: isLoadingStatuses } = trpc.useQuery([
|
||||
"plus.statuses",
|
||||
]);
|
||||
const { mutate, status } = trpc.useMutation("plus.vote", {
|
||||
onSuccess() {
|
||||
toast(getToastOptions("Successfully voted", "success"));
|
||||
trpc.invalidateQuery(["plus.hasVoted"]);
|
||||
},
|
||||
onError(error) {
|
||||
toast(getToastOptions(error.message, "error"));
|
||||
},
|
||||
});
|
||||
|
||||
const ownPlusStatus = statusesData?.find(
|
||||
(status) => status.user.id === user?.id
|
||||
);
|
||||
const ownPlusStatus = statuses?.find((status) => status.user.id === user?.id);
|
||||
|
||||
return {
|
||||
isLoading: isLoadingBallots || isLoadingStatuses,
|
||||
shouldRedirect: !isLoadingBallots && !ballotsData,
|
||||
isLoading: isLoadingBallots || isLoadingStatuses || hasVotedIsLoading,
|
||||
shouldRedirect: !isLoadingBallots && !usersForVoting,
|
||||
plusStatus: ownPlusStatus,
|
||||
currentUser: ballotsData?.[currentIndex],
|
||||
currentUser: usersForVoting?.[currentIndex],
|
||||
previousUser:
|
||||
currentIndex > 0 && ballotsData
|
||||
? { ...ballotsData[currentIndex - 1], ...votes[votes.length - 1] }
|
||||
currentIndex > 0 && usersForVoting
|
||||
? { ...usersForVoting[currentIndex - 1], ...votes[votes.length - 1] }
|
||||
: undefined,
|
||||
progress: ballotsData
|
||||
? ((currentIndex + 1) / ballotsData.length) * 100
|
||||
progress: usersForVoting
|
||||
? (currentIndex / usersForVoting.length) * 100
|
||||
: undefined,
|
||||
handleVote: (vote: Unpacked<z.infer<typeof votesSchema>>) => {
|
||||
setVotes([...votes, vote]);
|
||||
|
|
@ -42,5 +56,8 @@ export default function usePlusVoting() {
|
|||
setVotes(votes.slice(0, votes.length - 1));
|
||||
setCurrentIndex(currentIndex - 1);
|
||||
},
|
||||
submit: () => mutate(votes),
|
||||
status,
|
||||
hasVoted,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -261,6 +261,14 @@ const getUsersForVoting = async (userId: number) => {
|
|||
return shuffleArray(result).sort((a, b) => a.region.localeCompare(b.region));
|
||||
};
|
||||
|
||||
const hasVoted = async (userId: number) => {
|
||||
return Boolean(
|
||||
await prisma.plusBallot.count({
|
||||
where: { voterId: userId, isStale: false },
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const addSuggestion = async ({
|
||||
input,
|
||||
userId,
|
||||
|
|
@ -443,15 +451,15 @@ const addVotes = async ({
|
|||
if (
|
||||
input.some((vote) => {
|
||||
const region = allowedUsers.get(vote.userId);
|
||||
if (!region) return false;
|
||||
if (!region) return true;
|
||||
|
||||
if (region === usersPlusStatus.region) {
|
||||
if (![-2, -1, 1, 2].includes(vote.score)) return false;
|
||||
if (![-2, -1, 1, 2].includes(vote.score)) return true;
|
||||
} else {
|
||||
if (![-1, 1].includes(vote.score)) return false;
|
||||
if (![-1, 1].includes(vote.score)) return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
return false;
|
||||
})
|
||||
) {
|
||||
throw httpError.badRequest("invalid vote provided");
|
||||
|
|
@ -479,4 +487,5 @@ export default {
|
|||
addSuggestion,
|
||||
addVouch,
|
||||
addVotes,
|
||||
hasVoted,
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user