mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-03-21 18:04:39 -05:00
Closes #625
This commit is contained in:
parent
0f3da0e3f8
commit
7b67a96b30
|
|
@ -7,20 +7,19 @@ import {
|
|||
FormLabel,
|
||||
Text,
|
||||
Textarea,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { Trans } from "@lingui/macro";
|
||||
import MyLink from "components/common/MyLink";
|
||||
import SubText from "components/common/SubText";
|
||||
import UserAvatar from "components/common/UserAvatar";
|
||||
import { useMutation } from "hooks/common";
|
||||
import type { SuggestionsGet } from "pages/api/plus/suggestions";
|
||||
import { useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { Suggestions } from "services/plus";
|
||||
import { getToastOptions } from "utils/objects";
|
||||
import { mutate } from "swr";
|
||||
import { getVotingRange } from "utils/plus";
|
||||
import { getFullUsername } from "utils/strings";
|
||||
import { trpc } from "utils/trpc";
|
||||
import { Unpacked } from "utils/types";
|
||||
import {
|
||||
resuggestionSchema,
|
||||
|
|
@ -28,32 +27,28 @@ import {
|
|||
} from "utils/validators/suggestion";
|
||||
import * as z from "zod";
|
||||
|
||||
type FormData = z.infer<typeof resuggestionSchema>;
|
||||
type SuggestionsData = z.infer<typeof resuggestionSchema>;
|
||||
|
||||
const Suggestion = ({
|
||||
suggestion,
|
||||
canSuggest,
|
||||
}: {
|
||||
suggestion: Unpacked<Suggestions>;
|
||||
suggestion: Unpacked<SuggestionsGet>;
|
||||
canSuggest: boolean;
|
||||
}) => {
|
||||
const toast = useToast();
|
||||
const [showTextarea, setShowTextarea] = useState(false);
|
||||
const { handleSubmit, errors, register, watch } = useForm<FormData>({
|
||||
const { handleSubmit, errors, register, watch } = useForm<SuggestionsData>({
|
||||
resolver: zodResolver(resuggestionSchema),
|
||||
});
|
||||
const utils = trpc.useQueryUtils();
|
||||
|
||||
const { mutate, status } = trpc.useMutation("plus.suggestion", {
|
||||
onSuccess() {
|
||||
toast(getToastOptions("Comment added", "success"));
|
||||
// TODO:
|
||||
utils.invalidateQuery(["plus.suggestions"]);
|
||||
const suggestionMutation = useMutation<SuggestionsData>({
|
||||
url: "/api/plus/suggestions",
|
||||
method: "POST",
|
||||
successToastMsg: "Comment added",
|
||||
afterSuccess: () => {
|
||||
mutate("/api/plus/suggestions");
|
||||
setShowTextarea(false);
|
||||
},
|
||||
onError(error) {
|
||||
toast(getToastOptions(error.message, "error"));
|
||||
},
|
||||
});
|
||||
|
||||
const watchDescription = watch("description", "");
|
||||
|
|
@ -109,10 +104,10 @@ const Suggestion = ({
|
|||
{showTextarea && (
|
||||
<form
|
||||
onSubmit={handleSubmit((values) =>
|
||||
mutate({
|
||||
suggestionMutation.mutate({
|
||||
...values,
|
||||
// region doesn't matter as it is not updated after the first suggestion
|
||||
region: "NA",
|
||||
// @ts-expect-error region doesn't matter as it is not updated after the first suggestion
|
||||
region: "NA" as const,
|
||||
tier: suggestion.tier,
|
||||
suggestedId: suggestion.suggestedUser.id,
|
||||
})
|
||||
|
|
@ -136,7 +131,7 @@ const Suggestion = ({
|
|||
size="sm"
|
||||
mr={3}
|
||||
type="submit"
|
||||
isLoading={status === "loading"}
|
||||
isLoading={suggestionMutation.isMutating}
|
||||
data-cy="submit-button"
|
||||
>
|
||||
<Trans>Save</Trans>
|
||||
|
|
|
|||
|
|
@ -13,14 +13,13 @@ import {
|
|||
ModalOverlay,
|
||||
Select,
|
||||
Textarea,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import UserSelector from "components/common/UserSelector";
|
||||
import { useMutation } from "hooks/common";
|
||||
import { useState } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { getToastOptions } from "utils/objects";
|
||||
import { trpc } from "utils/trpc";
|
||||
import { mutate } from "swr";
|
||||
import {
|
||||
suggestionFullSchema,
|
||||
SUGGESTION_DESCRIPTION_LIMIT,
|
||||
|
|
@ -31,24 +30,23 @@ interface Props {
|
|||
userPlusMembershipTier: number;
|
||||
}
|
||||
|
||||
type FormData = z.infer<typeof suggestionFullSchema>;
|
||||
type SuggestionsData = z.infer<typeof suggestionFullSchema>;
|
||||
|
||||
const SuggestionModal: React.FC<Props> = ({ userPlusMembershipTier }) => {
|
||||
const toast = useToast();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const { handleSubmit, errors, register, watch, control } = useForm<FormData>({
|
||||
resolver: zodResolver(suggestionFullSchema),
|
||||
});
|
||||
const utils = trpc.useQueryUtils();
|
||||
const { mutate, status } = trpc.useMutation("plus.suggestion", {
|
||||
onSuccess() {
|
||||
toast(getToastOptions("New suggestion submitted", "success"));
|
||||
utils.invalidateQuery(["plus.suggestions"]);
|
||||
const { handleSubmit, errors, register, watch, control } =
|
||||
useForm<SuggestionsData>({
|
||||
resolver: zodResolver(suggestionFullSchema),
|
||||
});
|
||||
|
||||
const suggestionMutation = useMutation<SuggestionsData>({
|
||||
url: "/api/plus/suggestions",
|
||||
method: "POST",
|
||||
successToastMsg: "New suggestion submitted",
|
||||
afterSuccess: () => {
|
||||
mutate("/api/plus/suggestions");
|
||||
setIsOpen(false);
|
||||
},
|
||||
onError(error) {
|
||||
toast(getToastOptions(error.message, "error"));
|
||||
},
|
||||
});
|
||||
|
||||
const watchDescription = watch("description", "");
|
||||
|
|
@ -74,7 +72,11 @@ const SuggestionModal: React.FC<Props> = ({ userPlusMembershipTier }) => {
|
|||
<ModalContent>
|
||||
<ModalHeader>Adding a new suggestion</ModalHeader>
|
||||
<ModalCloseButton borderRadius="50%" />
|
||||
<form onSubmit={handleSubmit((data) => mutate(data))}>
|
||||
<form
|
||||
onSubmit={handleSubmit((data) =>
|
||||
suggestionMutation.mutate(data)
|
||||
)}
|
||||
>
|
||||
<ModalBody pb={2}>
|
||||
<FormLabel>Tier</FormLabel>
|
||||
<Controller
|
||||
|
|
@ -154,7 +156,7 @@ const SuggestionModal: React.FC<Props> = ({ userPlusMembershipTier }) => {
|
|||
<Button
|
||||
mr={3}
|
||||
type="submit"
|
||||
isLoading={status === "loading"}
|
||||
isLoading={suggestionMutation.isMutating}
|
||||
data-cy="submit-button"
|
||||
>
|
||||
Save
|
||||
|
|
|
|||
|
|
@ -12,14 +12,12 @@ import {
|
|||
ModalHeader,
|
||||
ModalOverlay,
|
||||
Select,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import UserSelector from "components/common/UserSelector";
|
||||
import { useMutation } from "hooks/common";
|
||||
import { useState } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { getToastOptions } from "utils/objects";
|
||||
import { trpc } from "utils/trpc";
|
||||
import { vouchSchema } from "utils/validators/vouch";
|
||||
import * as z from "zod";
|
||||
|
||||
|
|
@ -27,24 +25,20 @@ interface Props {
|
|||
canVouchFor: number;
|
||||
}
|
||||
|
||||
type FormData = z.infer<typeof vouchSchema>;
|
||||
type VouchData = z.infer<typeof vouchSchema>;
|
||||
|
||||
const VouchModal: React.FC<Props> = ({ canVouchFor }) => {
|
||||
const toast = useToast();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const { handleSubmit, errors, register, control } = useForm<FormData>({
|
||||
const { handleSubmit, errors, register, control } = useForm<VouchData>({
|
||||
resolver: zodResolver(vouchSchema),
|
||||
});
|
||||
const utils = trpc.useQueryUtils();
|
||||
const { mutate, status } = trpc.useMutation("plus.vouch", {
|
||||
onSuccess() {
|
||||
toast(getToastOptions("Successfully vouched", "success"));
|
||||
utils.invalidateQuery(["plus.statuses"]);
|
||||
const vouchMutation = useMutation<VouchData>({
|
||||
url: "/api/plus/vouches",
|
||||
method: "POST",
|
||||
successToastMsg: "Successfully vouched",
|
||||
afterSuccess: () => {
|
||||
setIsOpen(false);
|
||||
},
|
||||
onError(error) {
|
||||
toast(getToastOptions(error.message, "error"));
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
|
|
@ -69,7 +63,9 @@ const VouchModal: React.FC<Props> = ({ canVouchFor }) => {
|
|||
<ModalContent>
|
||||
<ModalHeader>Vouching</ModalHeader>
|
||||
<ModalCloseButton borderRadius="50%" />
|
||||
<form onSubmit={handleSubmit((data) => mutate(data))}>
|
||||
<form
|
||||
onSubmit={handleSubmit((data) => vouchMutation.mutate(data))}
|
||||
>
|
||||
<ModalBody pb={2}>
|
||||
<FormLabel>Tier</FormLabel>
|
||||
<Controller
|
||||
|
|
@ -127,7 +123,7 @@ const VouchModal: React.FC<Props> = ({ canVouchFor }) => {
|
|||
<Button
|
||||
mr={3}
|
||||
type="submit"
|
||||
isLoading={status === "loading"}
|
||||
isLoading={vouchMutation.isMutating}
|
||||
data-cy="submit-button"
|
||||
>
|
||||
Save
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import { Button } from "@chakra-ui/button";
|
||||
import { Box, Grid } from "@chakra-ui/layout";
|
||||
import MyLink from "components/common/MyLink";
|
||||
import { PlusStatusesGet } from "pages/api/plus";
|
||||
import { Fragment, useState } from "react";
|
||||
import { PlusStatuses } from "services/plus";
|
||||
import { getFullUsername } from "utils/strings";
|
||||
|
||||
const VouchesList = ({ vouches }: { vouches: PlusStatuses }) => {
|
||||
const VouchesList = ({ vouches }: { vouches: PlusStatusesGet }) => {
|
||||
const [show, setShow] = useState(false);
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ export const useMutation = <T>({
|
|||
afterSuccess,
|
||||
}: {
|
||||
url: string;
|
||||
method?: "POST" | "DELETE" | "PUT";
|
||||
method?: "POST" | "DELETE" | "PATCH" | "PUT";
|
||||
data?: T;
|
||||
successToastMsg?: string;
|
||||
afterSuccess?: () => void;
|
||||
|
|
|
|||
140
hooks/plus.ts
140
hooks/plus.ts
|
|
@ -1,43 +1,39 @@
|
|||
import { useToast } from "@chakra-ui/toast";
|
||||
import { useUser } from "hooks/common";
|
||||
import { useMutation, useUser } from "hooks/common";
|
||||
import { PlusStatusesGet } from "pages/api/plus";
|
||||
import type { SuggestionsGet } from "pages/api/plus/suggestions";
|
||||
import { UsersForVotingGet } from "pages/api/plus/users-for-voting";
|
||||
import { VotesGet } from "pages/api/plus/votes";
|
||||
import { useState } from "react";
|
||||
import { getToastOptions } from "utils/objects";
|
||||
import { getVotingRange } from "utils/plus";
|
||||
import { trpc } from "utils/trpc";
|
||||
import { Unpacked } from "utils/types";
|
||||
import { votesSchema } from "utils/validators/votes";
|
||||
import { PlusStatuses } from "services/plus";
|
||||
import useSWR from "swr";
|
||||
import { Serialized, Unpacked } from "utils/types";
|
||||
import { voteSchema, votesSchema } from "utils/validators/votes";
|
||||
import * as z from "zod";
|
||||
|
||||
export function usePlusHomePage() {
|
||||
const [user] = useUser();
|
||||
export function usePlusHomePage(statuses: Serialized<PlusStatuses>) {
|
||||
const [user, userIsLoading] = useUser();
|
||||
const [suggestionsFilter, setSuggestionsFilter] = useState<
|
||||
number | undefined
|
||||
>(undefined);
|
||||
|
||||
const { data: suggestionsData } = trpc.useQuery(["plus.suggestions"], {
|
||||
enabled: !getVotingRange().isHappening,
|
||||
});
|
||||
const { data: plusStatusData } = trpc.useQuery(["plus.statuses"]);
|
||||
const { data: votingProgress } = trpc.useQuery(["plus.votingProgress"], {
|
||||
enabled: getVotingRange().isHappening,
|
||||
});
|
||||
const suggestionsQuery = useSWR<SuggestionsGet>("/api/plus/suggestions");
|
||||
|
||||
const suggestions = suggestionsData ?? [];
|
||||
const suggestions = suggestionsQuery.data ?? [];
|
||||
|
||||
return {
|
||||
plusStatusData: plusStatusData?.find(
|
||||
(status) => status.user.id === user?.id
|
||||
),
|
||||
vouchStatuses: plusStatusData
|
||||
plusStatusData: statuses?.find((status) => status.user.id === user?.id),
|
||||
plusStatusDataLoading: userIsLoading,
|
||||
vouchStatuses: statuses
|
||||
?.filter((status) => status.voucher)
|
||||
.sort((a, b) => a.vouchTier! - b.vouchTier!),
|
||||
vouchedPlusStatusData: plusStatusData?.find(
|
||||
vouchedPlusStatusData: statuses?.find(
|
||||
(status) => status.voucher?.id === user?.id
|
||||
),
|
||||
suggestionsData: suggestions.filter(
|
||||
(suggestion) =>
|
||||
!suggestionsFilter || suggestion.tier === suggestionsFilter
|
||||
),
|
||||
suggestionsLoading: !suggestionsQuery.data,
|
||||
suggestionCounts: suggestions.reduce(
|
||||
(counts, suggestion) => {
|
||||
const tierString = [null, "ONE", "TWO", "THREE"][
|
||||
|
|
@ -53,74 +49,76 @@ export function usePlusHomePage() {
|
|||
(suggestion) => suggestion.suggesterUser.id === user?.id
|
||||
),
|
||||
setSuggestionsFilter,
|
||||
votingProgress,
|
||||
};
|
||||
}
|
||||
|
||||
type VoteInput = z.infer<typeof votesSchema>;
|
||||
type EditVoteInput = z.infer<typeof voteSchema>;
|
||||
|
||||
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: votedUserScores, isLoading: hasVotedIsLoading } = trpc.useQuery(
|
||||
["plus.votedUserScores"]
|
||||
const votedUserScores = useSWR<VotesGet>("/api/plus/votes");
|
||||
const usersForVoting = useSWR<UsersForVotingGet>(
|
||||
"/api/plus/users-for-voting"
|
||||
);
|
||||
const { data: usersForVoting, isLoading: isLoadingBallots } = trpc.useQuery([
|
||||
"plus.usersForVoting",
|
||||
]);
|
||||
const { data: statuses, isLoading: isLoadingStatuses } = trpc.useQuery([
|
||||
"plus.statuses",
|
||||
]);
|
||||
const utils = trpc.useQueryUtils();
|
||||
const { mutate: mutateVote, status: voteStatus } = trpc.useMutation(
|
||||
"plus.vote",
|
||||
{
|
||||
onSuccess() {
|
||||
toast(getToastOptions("Successfully voted", "success"));
|
||||
utils.invalidateQuery(["plus.votedUserScores"]);
|
||||
utils.invalidateQuery(["plus.votingProgress"]);
|
||||
},
|
||||
onError(error) {
|
||||
toast(getToastOptions(error.message, "error"));
|
||||
},
|
||||
}
|
||||
);
|
||||
const { mutate: editVoteMutate, isLoading: isLoadingEditVote } =
|
||||
trpc.useMutation("plus.editVote", {
|
||||
onSuccess() {
|
||||
toast(getToastOptions("Successfully edited vote", "success"));
|
||||
utils.invalidateQuery(["plus.votedUserScores"]);
|
||||
},
|
||||
onError(error) {
|
||||
toast(getToastOptions(error.message, "error"));
|
||||
},
|
||||
});
|
||||
const statuses = useSWR<PlusStatusesGet>("/api/plus");
|
||||
const voteMutation = useMutation<VoteInput>({
|
||||
url: "/api/plus/votes",
|
||||
method: "POST",
|
||||
successToastMsg: "Successfully voted",
|
||||
afterSuccess: () => {
|
||||
votedUserScores.mutate();
|
||||
},
|
||||
});
|
||||
|
||||
const ownPlusStatus = statuses?.find((status) => status.user.id === user?.id);
|
||||
const editVoteMutation = useMutation<EditVoteInput>({
|
||||
url: "/api/plus/votes",
|
||||
method: "PATCH",
|
||||
successToastMsg: "Successfully edited vote",
|
||||
afterSuccess: () => {
|
||||
votedUserScores.mutate();
|
||||
},
|
||||
});
|
||||
|
||||
const ownPlusStatus = statuses.data?.find(
|
||||
(status) => status.user.id === user?.id
|
||||
);
|
||||
|
||||
const getVotedUsers = () => {
|
||||
if (!votedUserScores || !usersForVoting) return undefined;
|
||||
if (
|
||||
!usersForVoting.data ||
|
||||
Object.keys(votedUserScores.data ?? {}).length === 0 ||
|
||||
usersForVoting.data.length === 0
|
||||
)
|
||||
return undefined;
|
||||
|
||||
return usersForVoting
|
||||
return usersForVoting.data
|
||||
.map((u) => {
|
||||
return { ...u, score: votedUserScores.get(u.userId)! };
|
||||
return { ...u, score: votedUserScores.data?.[u.userId]! };
|
||||
})
|
||||
.sort((a, b) => a.username.localeCompare(b.username));
|
||||
};
|
||||
|
||||
return {
|
||||
isLoading: isLoadingBallots || isLoadingStatuses || hasVotedIsLoading,
|
||||
shouldRedirect: !isLoadingBallots && !usersForVoting,
|
||||
isLoading: !usersForVoting.data || !statuses.data || !votedUserScores.data,
|
||||
shouldRedirect: !usersForVoting.data && !usersForVoting,
|
||||
plusStatus: ownPlusStatus,
|
||||
currentUser: usersForVoting?.[currentIndex],
|
||||
currentUser: usersForVoting.data?.[currentIndex],
|
||||
previousUser:
|
||||
currentIndex > 0 && usersForVoting
|
||||
? { ...usersForVoting[currentIndex - 1], ...votes[votes.length - 1] }
|
||||
currentIndex > 0 &&
|
||||
usersForVoting &&
|
||||
(usersForVoting.data ?? []).length > 0
|
||||
? {
|
||||
...usersForVoting.data![currentIndex - 1],
|
||||
...votes[votes.length - 1],
|
||||
}
|
||||
: undefined,
|
||||
progress: usersForVoting
|
||||
? (currentIndex / usersForVoting.length) * 100
|
||||
? (currentIndex / (usersForVoting.data?.length ?? 0)) * 100
|
||||
: undefined,
|
||||
handleVote: (vote: Unpacked<z.infer<typeof votesSchema>>) => {
|
||||
const nextIndex = currentIndex + 1;
|
||||
|
|
@ -129,7 +127,7 @@ export default function usePlusVoting() {
|
|||
(<HTMLElement>document.activeElement).blur();
|
||||
|
||||
// preload next avatar
|
||||
const next = usersForVoting?.[nextIndex + 1];
|
||||
const next = usersForVoting.data?.[nextIndex + 1];
|
||||
if (next) {
|
||||
new Image().src = `https://cdn.discordapp.com/avatars/${next.discordId}/${next.discordAvatar}.jpg`;
|
||||
}
|
||||
|
|
@ -138,10 +136,10 @@ export default function usePlusVoting() {
|
|||
setVotes(votes.slice(0, votes.length - 1));
|
||||
setCurrentIndex(currentIndex - 1);
|
||||
},
|
||||
submit: () => mutateVote(votes),
|
||||
voteStatus,
|
||||
submit: () => voteMutation.mutate(votes),
|
||||
voteMutating: voteMutation.isMutating,
|
||||
votedUsers: getVotedUsers(),
|
||||
editVote: editVoteMutate,
|
||||
isLoadingEditVote,
|
||||
editVote: editVoteMutation.mutate,
|
||||
isLoadingEditVote: editVoteMutation.isMutating,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
273
package-lock.json
generated
273
package-lock.json
generated
|
|
@ -19,9 +19,6 @@
|
|||
"@next/bundle-analyzer": "^11.1.0",
|
||||
"@prisma/client": "^2.25.0",
|
||||
"@sendou/react-sketch": "^0.5.2",
|
||||
"@trpc/client": "5.0.0",
|
||||
"@trpc/react": "5.0.0",
|
||||
"@trpc/server": "5.0.0",
|
||||
"countries-list": "^2.6.1",
|
||||
"framer-motion": "^4.1.17",
|
||||
"next": "^11.1.0",
|
||||
|
|
@ -40,12 +37,10 @@
|
|||
"react-icons": "^4.2.0",
|
||||
"react-infinite-scroller": "^1.2.4",
|
||||
"react-markdown": "^5.0.3",
|
||||
"react-query": "^3.17.2",
|
||||
"react-select": "^4.3.1",
|
||||
"react-string-replace": "^0.4.4",
|
||||
"recharts": "^2.1.2",
|
||||
"remark-gfm": "^1.0.0",
|
||||
"superjson": "^1.7.4",
|
||||
"swr": "^0.5.6",
|
||||
"ts-trueskill": "^3.2.2",
|
||||
"uuid": "^8.3.2",
|
||||
|
|
@ -2096,43 +2091,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.3.tgz",
|
||||
"integrity": "sha512-O3uyB/JbkAEMZaP3YqyHH7TMnex7tWyCbCI4EfJdOCoN6HIhqdJBWTM6aCCiWQ/5f5wxjgU735QAIpJbjDvmzg=="
|
||||
},
|
||||
"node_modules/@trpc/client": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@trpc/client/-/client-5.0.0.tgz",
|
||||
"integrity": "sha512-w7rDaDeTB2vUsZqctjnB0A7f5m1oLzpCH+LMZreoHJevrN4idT7ooicTaFs7YYobm2Ww9iGVWKTAy2FiO4ihlg==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.9.0",
|
||||
"@trpc/server": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@trpc/react": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@trpc/react/-/react-5.0.0.tgz",
|
||||
"integrity": "sha512-TLcbkiXoZFSUdmJNfrvXKOsLRT3exwQM9sf2St271fceouRZWRNpRaUyw8exAdRwqr12FW5MjlbQ7WidvaRKbw==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.9.0",
|
||||
"@trpc/client": "^5.0.0",
|
||||
"@trpc/server": "^5.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0",
|
||||
"react-query": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@trpc/server": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@trpc/server/-/server-5.0.0.tgz",
|
||||
"integrity": "sha512-XI9qbrbZplA3Lrt7od3+zg3BUcP5zVJpZuSQ6wBQObBcqj5SU49aDnGxEoYYgBXAvUzuzToGvHFL+jAr8K72fw==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@trpc/server/node_modules/tslib": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
|
||||
"integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w=="
|
||||
},
|
||||
"node_modules/@tsconfig/node10": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.7.tgz",
|
||||
|
|
@ -2885,14 +2843,6 @@
|
|||
"is-decimal": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/big-integer": {
|
||||
"version": "1.6.48",
|
||||
"resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz",
|
||||
"integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==",
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/big.js": {
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
|
||||
|
|
@ -2945,20 +2895,6 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/broadcast-channel": {
|
||||
"version": "3.5.3",
|
||||
"resolved": "https://registry.npmjs.org/broadcast-channel/-/broadcast-channel-3.5.3.tgz",
|
||||
"integrity": "sha512-OLOXfwReZa2AAAh9yOUyiALB3YxBe0QpThwwuyRHLgpl8bSznSDmV6Mz7LeBJg1VZsMcDcNMy7B53w12qHrIhQ==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.7.2",
|
||||
"detect-node": "^2.0.4",
|
||||
"js-sha3": "0.8.0",
|
||||
"microseconds": "0.2.0",
|
||||
"nano-time": "1.0.0",
|
||||
"rimraf": "3.0.2",
|
||||
"unload": "2.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/brorand": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
|
||||
|
|
@ -3752,11 +3688,6 @@
|
|||
"minimalistic-assert": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/detect-node": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.5.tgz",
|
||||
"integrity": "sha512-qi86tE6hRcFHy8jI1m2VG+LaPUR1LhqDa5G8tVjuUXmOrpuAgqsA1pN0+ldgr3aKUH+QLI9hCY/OcRYisERejw=="
|
||||
},
|
||||
"node_modules/detect-node-es": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
|
||||
|
|
@ -5883,11 +5814,6 @@
|
|||
"url": "https://github.com/sponsors/panva"
|
||||
}
|
||||
},
|
||||
"node_modules/js-sha3": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz",
|
||||
"integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q=="
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
|
|
@ -6149,7 +6075,8 @@
|
|||
"node_modules/lodash.clonedeep": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
|
||||
"integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8="
|
||||
"integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/lodash.debounce": {
|
||||
"version": "4.0.8",
|
||||
|
|
@ -6302,15 +6229,6 @@
|
|||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/match-sorter": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.0.tgz",
|
||||
"integrity": "sha512-efYOf/wUpNb8FgNY+cOD2EIJI1S5I7YPKsw0LBp7wqPh5pmMS6i/wr3ZWwfwrAw1NvqTA2KUReVRWDX84lUcOQ==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"remove-accents": "0.4.2"
|
||||
}
|
||||
},
|
||||
"node_modules/material-colors": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz",
|
||||
|
|
@ -6632,11 +6550,6 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/microseconds": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/microseconds/-/microseconds-0.2.0.tgz",
|
||||
"integrity": "sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA=="
|
||||
},
|
||||
"node_modules/miller-rabin": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
|
||||
|
|
@ -6732,14 +6645,6 @@
|
|||
"thenify-all": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/nano-time": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/nano-time/-/nano-time-1.0.0.tgz",
|
||||
"integrity": "sha1-sFVPaa2J4i0JB/ehKwmTpdlhN+8=",
|
||||
"dependencies": {
|
||||
"big-integer": "^1.6.16"
|
||||
}
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.1.25",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz",
|
||||
|
|
@ -8140,31 +8045,6 @@
|
|||
"react": "^16.8.0 || ^17"
|
||||
}
|
||||
},
|
||||
"node_modules/react-query": {
|
||||
"version": "3.17.2",
|
||||
"resolved": "https://registry.npmjs.org/react-query/-/react-query-3.17.2.tgz",
|
||||
"integrity": "sha512-icMtYL/+jUv0Q3n8GRz6CTyMYKh+l5EBKwhkr8qhbtYRORr43YGiN6EJSyw9WFaSQ9bwD1G9ZhaFxow9NMV0Sw==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"broadcast-channel": "^3.4.1",
|
||||
"match-sorter": "^6.0.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
},
|
||||
"react-native": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-refresh": {
|
||||
"version": "0.8.3",
|
||||
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.8.3.tgz",
|
||||
|
|
@ -8575,11 +8455,6 @@
|
|||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/remove-accents": {
|
||||
"version": "0.4.2",
|
||||
"resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.4.2.tgz",
|
||||
"integrity": "sha1-CkPTqq4egNuRngeuJUsoXZ4ce7U="
|
||||
},
|
||||
"node_modules/repeat-string": {
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
|
||||
|
|
@ -8682,6 +8557,7 @@
|
|||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"glob": "^7.1.3"
|
||||
},
|
||||
|
|
@ -9186,18 +9062,6 @@
|
|||
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.7.tgz",
|
||||
"integrity": "sha512-OFFeUXFgwnGOKvEXaSv0D0KQ5ADP0n6g3SVONx6I/85JzNZ3u50FRwB3lVIk1QO2HNdI75tbVzc4Z66Gdp9voA=="
|
||||
},
|
||||
"node_modules/superjson": {
|
||||
"version": "1.7.4",
|
||||
"resolved": "https://registry.npmjs.org/superjson/-/superjson-1.7.4.tgz",
|
||||
"integrity": "sha512-A6DYTe04+x4L9NPywHeGZNy6/gLe8qqKCwhEfTH9M4eXpTjiTsF83JZ3j4hwXx1ogRb4779nWxsDlJGIECOJkw==",
|
||||
"dependencies": {
|
||||
"debug": "^4.3.1",
|
||||
"lodash.clonedeep": "^4.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
|
|
@ -9782,15 +9646,6 @@
|
|||
"node": ">= 10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/unload": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/unload/-/unload-2.2.0.tgz",
|
||||
"integrity": "sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.6.2",
|
||||
"detect-node": "^2.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/unpipe": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||
|
|
@ -11818,40 +11673,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.3.tgz",
|
||||
"integrity": "sha512-O3uyB/JbkAEMZaP3YqyHH7TMnex7tWyCbCI4EfJdOCoN6HIhqdJBWTM6aCCiWQ/5f5wxjgU735QAIpJbjDvmzg=="
|
||||
},
|
||||
"@trpc/client": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@trpc/client/-/client-5.0.0.tgz",
|
||||
"integrity": "sha512-w7rDaDeTB2vUsZqctjnB0A7f5m1oLzpCH+LMZreoHJevrN4idT7ooicTaFs7YYobm2Ww9iGVWKTAy2FiO4ihlg==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.9.0",
|
||||
"@trpc/server": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"@trpc/react": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@trpc/react/-/react-5.0.0.tgz",
|
||||
"integrity": "sha512-TLcbkiXoZFSUdmJNfrvXKOsLRT3exwQM9sf2St271fceouRZWRNpRaUyw8exAdRwqr12FW5MjlbQ7WidvaRKbw==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.9.0",
|
||||
"@trpc/client": "^5.0.0",
|
||||
"@trpc/server": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"@trpc/server": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@trpc/server/-/server-5.0.0.tgz",
|
||||
"integrity": "sha512-XI9qbrbZplA3Lrt7od3+zg3BUcP5zVJpZuSQ6wBQObBcqj5SU49aDnGxEoYYgBXAvUzuzToGvHFL+jAr8K72fw==",
|
||||
"requires": {
|
||||
"tslib": "^2.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
|
||||
"integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@tsconfig/node10": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.7.tgz",
|
||||
|
|
@ -12475,11 +12296,6 @@
|
|||
"is-decimal": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"big-integer": {
|
||||
"version": "1.6.48",
|
||||
"resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz",
|
||||
"integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w=="
|
||||
},
|
||||
"big.js": {
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
|
||||
|
|
@ -12523,20 +12339,6 @@
|
|||
"fill-range": "^7.0.1"
|
||||
}
|
||||
},
|
||||
"broadcast-channel": {
|
||||
"version": "3.5.3",
|
||||
"resolved": "https://registry.npmjs.org/broadcast-channel/-/broadcast-channel-3.5.3.tgz",
|
||||
"integrity": "sha512-OLOXfwReZa2AAAh9yOUyiALB3YxBe0QpThwwuyRHLgpl8bSznSDmV6Mz7LeBJg1VZsMcDcNMy7B53w12qHrIhQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.7.2",
|
||||
"detect-node": "^2.0.4",
|
||||
"js-sha3": "0.8.0",
|
||||
"microseconds": "0.2.0",
|
||||
"nano-time": "1.0.0",
|
||||
"rimraf": "3.0.2",
|
||||
"unload": "2.2.0"
|
||||
}
|
||||
},
|
||||
"brorand": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
|
||||
|
|
@ -13202,11 +13004,6 @@
|
|||
"minimalistic-assert": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"detect-node": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.5.tgz",
|
||||
"integrity": "sha512-qi86tE6hRcFHy8jI1m2VG+LaPUR1LhqDa5G8tVjuUXmOrpuAgqsA1pN0+ldgr3aKUH+QLI9hCY/OcRYisERejw=="
|
||||
},
|
||||
"detect-node-es": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
|
||||
|
|
@ -14890,11 +14687,6 @@
|
|||
"@panva/asn1.js": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"js-sha3": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz",
|
||||
"integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q=="
|
||||
},
|
||||
"js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
|
|
@ -15116,7 +14908,8 @@
|
|||
"lodash.clonedeep": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
|
||||
"integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8="
|
||||
"integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.debounce": {
|
||||
"version": "4.0.8",
|
||||
|
|
@ -15249,15 +15042,6 @@
|
|||
"repeat-string": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"match-sorter": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.0.tgz",
|
||||
"integrity": "sha512-efYOf/wUpNb8FgNY+cOD2EIJI1S5I7YPKsw0LBp7wqPh5pmMS6i/wr3ZWwfwrAw1NvqTA2KUReVRWDX84lUcOQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"remove-accents": "0.4.2"
|
||||
}
|
||||
},
|
||||
"material-colors": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz",
|
||||
|
|
@ -15489,11 +15273,6 @@
|
|||
"picomatch": "^2.0.5"
|
||||
}
|
||||
},
|
||||
"microseconds": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/microseconds/-/microseconds-0.2.0.tgz",
|
||||
"integrity": "sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA=="
|
||||
},
|
||||
"miller-rabin": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
|
||||
|
|
@ -15570,14 +15349,6 @@
|
|||
"thenify-all": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"nano-time": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/nano-time/-/nano-time-1.0.0.tgz",
|
||||
"integrity": "sha1-sFVPaa2J4i0JB/ehKwmTpdlhN+8=",
|
||||
"requires": {
|
||||
"big-integer": "^1.6.16"
|
||||
}
|
||||
},
|
||||
"nanoid": {
|
||||
"version": "3.1.25",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz",
|
||||
|
|
@ -16703,16 +16474,6 @@
|
|||
"warning": "^4.0.2"
|
||||
}
|
||||
},
|
||||
"react-query": {
|
||||
"version": "3.17.2",
|
||||
"resolved": "https://registry.npmjs.org/react-query/-/react-query-3.17.2.tgz",
|
||||
"integrity": "sha512-icMtYL/+jUv0Q3n8GRz6CTyMYKh+l5EBKwhkr8qhbtYRORr43YGiN6EJSyw9WFaSQ9bwD1G9ZhaFxow9NMV0Sw==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"broadcast-channel": "^3.4.1",
|
||||
"match-sorter": "^6.0.2"
|
||||
}
|
||||
},
|
||||
"react-refresh": {
|
||||
"version": "0.8.3",
|
||||
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.8.3.tgz",
|
||||
|
|
@ -17028,11 +16789,6 @@
|
|||
"mdast-util-from-markdown": "^0.8.0"
|
||||
}
|
||||
},
|
||||
"remove-accents": {
|
||||
"version": "0.4.2",
|
||||
"resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.4.2.tgz",
|
||||
"integrity": "sha1-CkPTqq4egNuRngeuJUsoXZ4ce7U="
|
||||
},
|
||||
"repeat-string": {
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
|
||||
|
|
@ -17109,6 +16865,7 @@
|
|||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"glob": "^7.1.3"
|
||||
}
|
||||
|
|
@ -17539,15 +17296,6 @@
|
|||
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.7.tgz",
|
||||
"integrity": "sha512-OFFeUXFgwnGOKvEXaSv0D0KQ5ADP0n6g3SVONx6I/85JzNZ3u50FRwB3lVIk1QO2HNdI75tbVzc4Z66Gdp9voA=="
|
||||
},
|
||||
"superjson": {
|
||||
"version": "1.7.4",
|
||||
"resolved": "https://registry.npmjs.org/superjson/-/superjson-1.7.4.tgz",
|
||||
"integrity": "sha512-A6DYTe04+x4L9NPywHeGZNy6/gLe8qqKCwhEfTH9M4eXpTjiTsF83JZ3j4hwXx1ogRb4779nWxsDlJGIECOJkw==",
|
||||
"requires": {
|
||||
"debug": "^4.3.1",
|
||||
"lodash.clonedeep": "^4.5.0"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
|
|
@ -17931,15 +17679,6 @@
|
|||
"integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
|
||||
"dev": true
|
||||
},
|
||||
"unload": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/unload/-/unload-2.2.0.tgz",
|
||||
"integrity": "sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.6.2",
|
||||
"detect-node": "^2.0.4"
|
||||
}
|
||||
},
|
||||
"unpipe": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||
|
|
|
|||
|
|
@ -35,9 +35,6 @@
|
|||
"@next/bundle-analyzer": "^11.1.0",
|
||||
"@prisma/client": "^2.25.0",
|
||||
"@sendou/react-sketch": "^0.5.2",
|
||||
"@trpc/client": "5.0.0",
|
||||
"@trpc/react": "5.0.0",
|
||||
"@trpc/server": "5.0.0",
|
||||
"countries-list": "^2.6.1",
|
||||
"framer-motion": "^4.1.17",
|
||||
"next": "^11.1.0",
|
||||
|
|
@ -56,12 +53,10 @@
|
|||
"react-icons": "^4.2.0",
|
||||
"react-infinite-scroller": "^1.2.4",
|
||||
"react-markdown": "^5.0.3",
|
||||
"react-query": "^3.17.2",
|
||||
"react-select": "^4.3.1",
|
||||
"react-string-replace": "^0.4.4",
|
||||
"recharts": "^2.1.2",
|
||||
"remark-gfm": "^1.0.0",
|
||||
"superjson": "^1.7.4",
|
||||
"swr": "^0.5.6",
|
||||
"ts-trueskill": "^3.2.2",
|
||||
"uuid": "^8.3.2",
|
||||
|
|
|
|||
|
|
@ -7,13 +7,10 @@ import { DefaultSeo } from "next-seo";
|
|||
import type { AppProps } from "next/app";
|
||||
import { Router } from "next/router";
|
||||
import NProgress from "nprogress";
|
||||
import { useEffect, useState } from "react";
|
||||
import { QueryClient, QueryClientProvider } from "react-query";
|
||||
import { Hydrate } from "react-query/hydration";
|
||||
import { useEffect } from "react";
|
||||
import { CSSVariables } from "utils/CSSVariables";
|
||||
import { activateLocale } from "utils/i18n";
|
||||
import { locales } from "utils/lists/locales";
|
||||
import { trpc } from "utils/trpc";
|
||||
import "./styles.css";
|
||||
|
||||
NProgress.configure({ showSpinner: false });
|
||||
|
|
@ -149,18 +146,6 @@ const setDisplayedLanguage = () => {
|
|||
|
||||
const MyApp = ({ Component, pageProps }: AppProps) => {
|
||||
useEffect(setDisplayedLanguage, []);
|
||||
const [queryClient] = useState(
|
||||
() =>
|
||||
new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
// queries never go stale to save some work
|
||||
// on our poor database
|
||||
staleTime: Infinity,
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
@ -191,13 +176,7 @@ const MyApp = ({ Component, pageProps }: AppProps) => {
|
|||
<ChakraProvider theme={extendedTheme} cssVarsRoot="body">
|
||||
<I18nProvider i18n={i18n}>
|
||||
<Layout>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<Hydrate
|
||||
state={trpc.useDehydratedState(pageProps.dehydratedState)}
|
||||
>
|
||||
<Component {...pageProps} />
|
||||
</Hydrate>
|
||||
</QueryClientProvider>
|
||||
<Component {...pageProps} />
|
||||
</Layout>
|
||||
</I18nProvider>
|
||||
</ChakraProvider>
|
||||
|
|
|
|||
19
pages/api/plus/index.ts
Normal file
19
pages/api/plus/index.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
import { createHandler } from "utils/api";
|
||||
import plusService, { PlusStatuses } from "services/plus";
|
||||
import { Serialized } from "utils/types";
|
||||
|
||||
export type PlusStatusesGet = Serialized<PlusStatuses>;
|
||||
|
||||
const GET = async (
|
||||
_req: NextApiRequest,
|
||||
res: NextApiResponse<PlusStatuses>
|
||||
) => {
|
||||
const statuses = await plusService.getPlusStatuses();
|
||||
res.status(200).json(statuses);
|
||||
};
|
||||
|
||||
const plushandler = (req: NextApiRequest, res: NextApiResponse) =>
|
||||
createHandler(req, res, { GET });
|
||||
|
||||
export default plushandler;
|
||||
36
pages/api/plus/suggestions.ts
Normal file
36
pages/api/plus/suggestions.ts
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
import { createHandler, getMySession } from "utils/api";
|
||||
import plusService, { Suggestions } from "services/plus";
|
||||
import { Serialized } from "utils/types";
|
||||
import { suggestionFullSchema } from "utils/validators/suggestion";
|
||||
|
||||
export type SuggestionsGet = Serialized<Suggestions>;
|
||||
|
||||
const GET = async (_req: NextApiRequest, res: NextApiResponse<Suggestions>) => {
|
||||
const suggestions = await plusService.getSuggestions();
|
||||
res.status(200).json(suggestions);
|
||||
};
|
||||
|
||||
const POST = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const user = await getMySession(req);
|
||||
if (!user) {
|
||||
return res.status(401).end();
|
||||
}
|
||||
|
||||
const parsed = suggestionFullSchema.safeParse(req.body);
|
||||
if (!parsed.success) {
|
||||
return res.status(400).json(parsed.error);
|
||||
}
|
||||
|
||||
await plusService.addSuggestion({
|
||||
input: parsed.data,
|
||||
userId: user.id,
|
||||
});
|
||||
|
||||
res.status(200).end();
|
||||
};
|
||||
|
||||
const suggestionsHandler = (req: NextApiRequest, res: NextApiResponse) =>
|
||||
createHandler(req, res, { GET, POST });
|
||||
|
||||
export default suggestionsHandler;
|
||||
24
pages/api/plus/users-for-voting.ts
Normal file
24
pages/api/plus/users-for-voting.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
import { createHandler, getMySession } from "utils/api";
|
||||
import plusService, { GetUsersForVoting } from "services/plus";
|
||||
import { Serialized } from "utils/types";
|
||||
|
||||
export type UsersForVotingGet = Serialized<GetUsersForVoting>;
|
||||
|
||||
const GET = async (
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse<GetUsersForVoting>
|
||||
) => {
|
||||
const user = await getMySession(req);
|
||||
if (!user) {
|
||||
return res.status(401).end();
|
||||
}
|
||||
|
||||
const usersForVoting = await plusService.getUsersForVoting(user.id);
|
||||
res.status(200).json(usersForVoting);
|
||||
};
|
||||
|
||||
const usersForVotingHandler = (req: NextApiRequest, res: NextApiResponse) =>
|
||||
createHandler(req, res, { GET });
|
||||
|
||||
export default usersForVotingHandler;
|
||||
61
pages/api/plus/votes.ts
Normal file
61
pages/api/plus/votes.ts
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
import { createHandler, getMySession } from "utils/api";
|
||||
import plusService, { VotedUserScores } from "services/plus";
|
||||
import { Serialized } from "utils/types";
|
||||
import { voteSchema, votesSchema } from "utils/validators/votes";
|
||||
|
||||
export type VotesGet = Serialized<VotedUserScores>;
|
||||
|
||||
const GET = async (
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse<VotedUserScores>
|
||||
) => {
|
||||
const user = await getMySession(req);
|
||||
if (!user) {
|
||||
return res.status(401).end();
|
||||
}
|
||||
|
||||
const votedUserScores = await plusService.votedUserScores(user.id);
|
||||
|
||||
res.status(200).json(votedUserScores);
|
||||
};
|
||||
|
||||
const POST = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const user = await getMySession(req);
|
||||
if (!user) {
|
||||
return res.status(401).end();
|
||||
}
|
||||
|
||||
const parsed = votesSchema.safeParse(req.body);
|
||||
if (!parsed.success) {
|
||||
return res.status(400).json(parsed.error);
|
||||
}
|
||||
|
||||
const votedUserScores = await plusService.addVotes({
|
||||
input: parsed.data,
|
||||
userId: user.id,
|
||||
});
|
||||
|
||||
res.status(200).json(votedUserScores);
|
||||
};
|
||||
|
||||
const PATCH = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const user = await getMySession(req);
|
||||
if (!user) {
|
||||
return res.status(401).end();
|
||||
}
|
||||
|
||||
const parsed = voteSchema.safeParse(req.body);
|
||||
if (!parsed.success) {
|
||||
return res.status(400).json(parsed.error);
|
||||
}
|
||||
|
||||
await plusService.editVote({ input: parsed.data, userId: user.id });
|
||||
|
||||
res.status(200).end();
|
||||
};
|
||||
|
||||
const votesHandler = (req: NextApiRequest, res: NextApiResponse) =>
|
||||
createHandler(req, res, { GET, POST, PATCH });
|
||||
|
||||
export default votesHandler;
|
||||
28
pages/api/plus/vouches.ts
Normal file
28
pages/api/plus/vouches.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
import plusService from "services/plus";
|
||||
import { createHandler, getMySession } from "utils/api";
|
||||
import { vouchSchema } from "utils/validators/vouch";
|
||||
|
||||
const POST = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const user = await getMySession(req);
|
||||
if (!user) {
|
||||
return res.status(401).end();
|
||||
}
|
||||
|
||||
const parsed = vouchSchema.safeParse(req.body);
|
||||
if (!parsed.success) {
|
||||
return res.status(400).json(parsed.error);
|
||||
}
|
||||
|
||||
await plusService.addVouch({
|
||||
input: parsed.data,
|
||||
userId: user.id,
|
||||
});
|
||||
|
||||
res.status(200).end();
|
||||
};
|
||||
|
||||
const vouchesHandler = (req: NextApiRequest, res: NextApiResponse) =>
|
||||
createHandler(req, res, { POST });
|
||||
|
||||
export default vouchesHandler;
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
import * as trpc from "@trpc/server";
|
||||
import { inferAsyncReturnType, inferProcedureOutput } from "@trpc/server";
|
||||
import * as trpcNext from "@trpc/server/adapters/next";
|
||||
import plusApi from "routers/plus";
|
||||
import superjson from "superjson";
|
||||
import { getMySession } from "utils/api";
|
||||
import { trpc as trcpReactQuery } from "utils/trpc";
|
||||
|
||||
const createContext = async ({ req }: trpcNext.CreateNextContextOptions) => {
|
||||
const user = await getMySession(req);
|
||||
return { user };
|
||||
};
|
||||
|
||||
type Context = inferAsyncReturnType<typeof createContext>;
|
||||
|
||||
export function createRouter() {
|
||||
return trpc.router<Context>();
|
||||
}
|
||||
// Important: only use this export with SSR/SSG
|
||||
export const appRouter = createRouter().merge("plus.", plusApi);
|
||||
|
||||
// Exporting type _type_ AppRouter only exposes types that can be used for inference
|
||||
// https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export
|
||||
export type AppRouter = typeof appRouter;
|
||||
|
||||
/**
|
||||
* This is a helper method to infer the output of a query resolver
|
||||
* @example type HelloOutput = inferQueryOutput<'hello'>
|
||||
*/
|
||||
export type inferQueryOutput<
|
||||
TRouteKey extends keyof AppRouter["_def"]["queries"]
|
||||
> = inferProcedureOutput<AppRouter["_def"]["queries"][TRouteKey]>;
|
||||
|
||||
export const ssr = trcpReactQuery.ssr(appRouter, { user: null });
|
||||
|
||||
export default trpcNext.createNextApiHandler({
|
||||
router: appRouter,
|
||||
createContext,
|
||||
transformer: superjson,
|
||||
});
|
||||
|
|
@ -17,24 +17,34 @@ import VouchesList from "components/plus/VouchesList";
|
|||
import VouchModal from "components/plus/VouchModal";
|
||||
import { useUser } from "hooks/common";
|
||||
import { usePlusHomePage } from "hooks/plus";
|
||||
import { ssr } from "pages/api/trpc/[trpc]";
|
||||
import { Fragment } from "react";
|
||||
import { getVotingRange } from "utils/plus";
|
||||
import { getFullUsername } from "utils/strings";
|
||||
import plusService, { PlusStatuses, VotingProgress } from "services/plus";
|
||||
import { Serialized } from "utils/types";
|
||||
import { serializeDataForGetStaticProps } from "utils/objects";
|
||||
|
||||
const PlusHomePage = () => {
|
||||
const PlusHomePage = ({
|
||||
statuses,
|
||||
votingProgress,
|
||||
}: {
|
||||
statuses: Serialized<PlusStatuses>;
|
||||
votingProgress: VotingProgress;
|
||||
}) => {
|
||||
const [user] = useUser();
|
||||
const {
|
||||
plusStatusData,
|
||||
plusStatusDataLoading,
|
||||
vouchStatuses,
|
||||
suggestionsData,
|
||||
suggestionsLoading,
|
||||
ownSuggestion,
|
||||
suggestionCounts,
|
||||
setSuggestionsFilter,
|
||||
vouchedPlusStatusData,
|
||||
votingProgress,
|
||||
} = usePlusHomePage();
|
||||
} = usePlusHomePage(statuses);
|
||||
|
||||
if (plusStatusDataLoading) return null;
|
||||
if (!plusStatusData?.membershipTier) {
|
||||
return (
|
||||
<Box>
|
||||
|
|
@ -44,7 +54,11 @@ const PlusHomePage = () => {
|
|||
<Heading size="md">Suggested players this month:</Heading>
|
||||
<Flex flexWrap="wrap" data-cy="alt-suggestions-container">
|
||||
{suggestionsData
|
||||
.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime())
|
||||
.sort(
|
||||
(a, b) =>
|
||||
new Date(a.createdAt).getTime() -
|
||||
new Date(b.createdAt).getTime()
|
||||
)
|
||||
.map((suggestion) => (
|
||||
<Box
|
||||
key={suggestion.tier + "+" + suggestion.suggestedUser.id}
|
||||
|
|
@ -142,7 +156,10 @@ const PlusHomePage = () => {
|
|||
{plusStatusData?.canVouchAgainAfter && (
|
||||
<Box>
|
||||
Can vouch again after:{" "}
|
||||
{plusStatusData.canVouchAgainAfter.toLocaleDateString()}{" "}
|
||||
{new Date(
|
||||
// @ts-expect-error TODO: make Serialized<T> handle null union
|
||||
plusStatusData.canVouchAgainAfter
|
||||
).toLocaleDateString()}{" "}
|
||||
(resets after voting)
|
||||
</Box>
|
||||
)}
|
||||
|
|
@ -205,7 +222,7 @@ const PlusHomePage = () => {
|
|||
</Box>
|
||||
)}
|
||||
{suggestionCounts.ONE + suggestionCounts.TWO + suggestionCounts.THREE ===
|
||||
0 ? (
|
||||
0 && !suggestionsLoading ? (
|
||||
<Box mt={4}>No suggestions yet for this month</Box>
|
||||
) : (
|
||||
<>
|
||||
|
|
@ -239,14 +256,14 @@ const PlusHomePage = () => {
|
|||
};
|
||||
|
||||
export const getStaticProps = async () => {
|
||||
await Promise.all([
|
||||
ssr.prefetchQuery("plus.suggestions"),
|
||||
ssr.prefetchQuery("plus.statuses"),
|
||||
const [statuses, votingProgress] = await Promise.all([
|
||||
plusService.getPlusStatuses(),
|
||||
getVotingRange().isHappening ? plusService.votingProgress() : null,
|
||||
]);
|
||||
|
||||
return {
|
||||
props: {
|
||||
dehydratedState: ssr.dehydrate(),
|
||||
statuses: serializeDataForGetStaticProps(statuses),
|
||||
votingProgress: serializeDataForGetStaticProps(votingProgress),
|
||||
},
|
||||
revalidate: 60,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ export default function PlusVotingPage() {
|
|||
previousUser,
|
||||
goBack,
|
||||
submit,
|
||||
voteStatus,
|
||||
voteMutating,
|
||||
votedUsers,
|
||||
editVote,
|
||||
isLoadingEditVote,
|
||||
|
|
@ -172,7 +172,7 @@ export default function PlusVotingPage() {
|
|||
{previousUser && !currentUser && (
|
||||
<Box onClick={submit} mt={6} textAlign="center">
|
||||
{" "}
|
||||
<Button isLoading={voteStatus === "loading"}>Submit</Button>
|
||||
<Button isLoading={voteMutating}>Submit</Button>
|
||||
</Box>
|
||||
)}
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -1,64 +0,0 @@
|
|||
import { createRouter } from "pages/api/trpc/[trpc]";
|
||||
import service from "services/plus";
|
||||
import { throwIfNotLoggedIn } from "utils/api";
|
||||
import { suggestionFullSchema } from "utils/validators/suggestion";
|
||||
import { voteSchema, votesSchema } from "utils/validators/votes";
|
||||
import { vouchSchema } from "utils/validators/vouch";
|
||||
|
||||
const plusApi = createRouter()
|
||||
.query("suggestions", {
|
||||
resolve() {
|
||||
return service.getSuggestions();
|
||||
},
|
||||
})
|
||||
.query("statuses", {
|
||||
resolve() {
|
||||
return service.getPlusStatuses();
|
||||
},
|
||||
})
|
||||
.query("usersForVoting", {
|
||||
resolve({ ctx }) {
|
||||
const user = throwIfNotLoggedIn(ctx.user);
|
||||
return service.getUsersForVoting(user.id);
|
||||
},
|
||||
})
|
||||
.query("votedUserScores", {
|
||||
resolve({ ctx }) {
|
||||
const user = throwIfNotLoggedIn(ctx.user);
|
||||
return service.votedUserScores(user.id);
|
||||
},
|
||||
})
|
||||
.query("votingProgress", {
|
||||
resolve() {
|
||||
return service.votingProgress();
|
||||
},
|
||||
})
|
||||
.mutation("suggestion", {
|
||||
input: suggestionFullSchema,
|
||||
resolve({ input, ctx }) {
|
||||
const user = throwIfNotLoggedIn(ctx.user);
|
||||
return service.addSuggestion({ input, userId: user.id });
|
||||
},
|
||||
})
|
||||
.mutation("vouch", {
|
||||
input: vouchSchema,
|
||||
resolve({ input, ctx }) {
|
||||
const user = throwIfNotLoggedIn(ctx.user);
|
||||
return service.addVouch({ input, userId: user.id });
|
||||
},
|
||||
})
|
||||
.mutation("vote", {
|
||||
input: votesSchema,
|
||||
resolve({ input, ctx }) {
|
||||
const user = throwIfNotLoggedIn(ctx.user);
|
||||
return service.addVotes({ input, userId: user.id });
|
||||
},
|
||||
})
|
||||
.mutation("editVote", {
|
||||
input: voteSchema,
|
||||
resolve({ input, ctx }) {
|
||||
const user = throwIfNotLoggedIn(ctx.user);
|
||||
return service.editVote({ input, userId: user.id });
|
||||
},
|
||||
});
|
||||
export default plusApi;
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
import { Prisma, User } from "@prisma/client";
|
||||
import { httpError } from "@trpc/server";
|
||||
import prisma from "prisma/client";
|
||||
import { freeAgentPostSchema } from "utils/validators/fapost";
|
||||
import * as z from "zod";
|
||||
|
|
@ -93,7 +92,7 @@ const likes = async ({ user }: { user: User }) => {
|
|||
dateMonthAgo.setMonth(dateMonthAgo.getMonth() - 1);
|
||||
|
||||
if (!post || post.updatedAt.getTime() < dateMonthAgo.getTime()) {
|
||||
throw httpError.badRequest("no post");
|
||||
throw new Error("no post");
|
||||
}
|
||||
|
||||
const likerPostIds = new Set(
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { PlusRegion, Prisma } from "@prisma/client";
|
||||
import { httpError } from "@trpc/server";
|
||||
import prisma from "prisma/client";
|
||||
import { shuffleArray } from "utils/arrays";
|
||||
import { getPercentageFromCounts, getVotingRange } from "utils/plus";
|
||||
|
|
@ -160,11 +159,15 @@ const getDistinctSummaryMonths = () => {
|
|||
});
|
||||
};
|
||||
|
||||
export type GetUsersForVoting = Prisma.PromiseReturnType<
|
||||
typeof getUsersForVoting
|
||||
>;
|
||||
|
||||
const getUsersForVoting = async (userId: number) => {
|
||||
if (!getVotingRange().isHappening) return null;
|
||||
if (!getVotingRange().isHappening) return [];
|
||||
const plusStatus = await prisma.plusStatus.findUnique({ where: { userId } });
|
||||
|
||||
if (!plusStatus?.membershipTier) return null;
|
||||
if (!plusStatus?.membershipTier) return [];
|
||||
|
||||
const [plusStatuses, suggestions] = await Promise.all([
|
||||
prisma.plusStatus.findMany({
|
||||
|
|
@ -256,24 +259,24 @@ const getUsersForVoting = async (userId: number) => {
|
|||
return shuffleArray(result).sort((a, b) => a.region.localeCompare(b.region));
|
||||
};
|
||||
|
||||
export type VotedUserScores = Prisma.PromiseReturnType<typeof votedUserScores>;
|
||||
|
||||
const votedUserScores = async (userId: number) => {
|
||||
const ballots = await prisma.plusBallot.findMany({
|
||||
where: { isStale: false, voterId: userId },
|
||||
});
|
||||
|
||||
if (ballots.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const result = new Map<number, number>();
|
||||
const result: Record<number, number> = {};
|
||||
|
||||
for (const ballot of ballots) {
|
||||
result.set(ballot.votedId, ballot.score);
|
||||
result[ballot.votedId] = ballot.score;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export type VotingProgress = Prisma.PromiseReturnType<typeof votingProgress>;
|
||||
|
||||
const votingProgress = async () => {
|
||||
const [ballots, statuses] = await Promise.all([
|
||||
prisma.plusBallot.findMany({
|
||||
|
|
@ -333,7 +336,7 @@ const addSuggestion = async ({
|
|||
isResuggestion === false && suggesterId === userId
|
||||
);
|
||||
if (usersSuggestion) {
|
||||
throw httpError.badRequest("already made a new suggestion");
|
||||
throw new Error("already made a new suggestion");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -346,17 +349,17 @@ const addSuggestion = async ({
|
|||
!suggesterPlusStatus.membershipTier ||
|
||||
suggesterPlusStatus.membershipTier > input.tier
|
||||
) {
|
||||
throw httpError.badRequest(
|
||||
throw new Error(
|
||||
"not a member of high enough tier to suggest for this tier"
|
||||
);
|
||||
}
|
||||
|
||||
if (suggestedUserAlreadyHasAccess()) {
|
||||
throw httpError.badRequest("suggested user already has access");
|
||||
throw new Error("suggested user already has access");
|
||||
}
|
||||
|
||||
if (getVotingRange().isHappening) {
|
||||
throw httpError.badRequest("voting has already started");
|
||||
throw new Error("voting has already started");
|
||||
}
|
||||
|
||||
return prisma.$transaction([
|
||||
|
|
@ -418,24 +421,22 @@ const addVouch = async ({
|
|||
|
||||
if (summary.userId === input.vouchedId && summary.tier === input.tier) {
|
||||
// can't vouch if they were just kicked
|
||||
throw httpError.badRequest(
|
||||
throw new Error(
|
||||
"can't vouch the user because they were kicked last month"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ((suggesterPlusStatus?.canVouchFor ?? Infinity) > input.tier) {
|
||||
throw httpError.badRequest(
|
||||
"not a member of high enough tier to vouch for this tier"
|
||||
);
|
||||
throw new Error("not a member of high enough tier to vouch for this tier");
|
||||
}
|
||||
|
||||
if (vouchedUserAlreadyHasAccess()) {
|
||||
throw httpError.badRequest("vouched user already has access");
|
||||
throw new Error("vouched user already has access");
|
||||
}
|
||||
|
||||
if (getVotingRange().isHappening) {
|
||||
throw httpError.badRequest("voting has already started");
|
||||
throw new Error("voting has already started");
|
||||
}
|
||||
|
||||
return prisma.$transaction([
|
||||
|
|
@ -478,7 +479,7 @@ const addVotes = async ({
|
|||
userId: number;
|
||||
}) => {
|
||||
if (!getVotingRange().isHappening) {
|
||||
throw httpError.badRequest("voting is not happening right now");
|
||||
throw new Error("voting is not happening right now");
|
||||
}
|
||||
|
||||
const [plusStatuses, suggestions] = await Promise.all([
|
||||
|
|
@ -492,8 +493,7 @@ const addVotes = async ({
|
|||
|
||||
const usersMembership = usersPlusStatus?.membershipTier;
|
||||
|
||||
if (!usersPlusStatus || !usersMembership)
|
||||
throw httpError.badRequest("not a member");
|
||||
if (!usersPlusStatus || !usersMembership) throw new Error("not a member");
|
||||
|
||||
const allowedUsers = new Map<number, "EU" | "NA">();
|
||||
|
||||
|
|
@ -516,14 +516,13 @@ const addVotes = async ({
|
|||
const status = plusStatuses.find(
|
||||
(status) => status.userId === suggestion.suggestedId
|
||||
);
|
||||
if (!status)
|
||||
throw httpError.badRequest("unexpected no status for suggested user");
|
||||
if (!status) throw new Error("unexpected no status for suggested user");
|
||||
|
||||
allowedUsers.set(suggestion.suggestedId, status.region);
|
||||
}
|
||||
|
||||
if (input.length !== allowedUsers.size) {
|
||||
throw httpError.badRequest("didn't vote on every user exactly once");
|
||||
throw new Error("didn't vote on every user exactly once");
|
||||
}
|
||||
|
||||
if (
|
||||
|
|
@ -540,7 +539,7 @@ const addVotes = async ({
|
|||
return false;
|
||||
})
|
||||
) {
|
||||
throw httpError.badRequest("invalid vote provided");
|
||||
throw new Error("invalid vote provided");
|
||||
}
|
||||
|
||||
return prisma.plusBallot.createMany({
|
||||
|
|
@ -563,7 +562,7 @@ const editVote = async ({
|
|||
userId: number;
|
||||
}) => {
|
||||
if (!getVotingRange().isHappening) {
|
||||
throw httpError.badRequest("voting is not happening right now");
|
||||
throw new Error("voting is not happening right now");
|
||||
}
|
||||
|
||||
const statuses = await prisma.plusStatus.findMany({
|
||||
|
|
@ -579,14 +578,14 @@ const editVote = async ({
|
|||
statuses[0].region !== statuses[1].region &&
|
||||
![-1, 1].includes(input.score)
|
||||
) {
|
||||
throw httpError.badRequest("invalid score");
|
||||
throw new Error("invalid score");
|
||||
}
|
||||
|
||||
if (
|
||||
statuses[0].region === statuses[1].region &&
|
||||
![-2, -1, 1, 2].includes(input.score)
|
||||
) {
|
||||
throw httpError.badRequest("invalid score");
|
||||
throw new Error("invalid score");
|
||||
}
|
||||
|
||||
return prisma.plusBallot.update({
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { User } from "@prisma/client";
|
||||
import { httpError } from "@trpc/server";
|
||||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
import { getSession } from "next-auth/client";
|
||||
|
||||
|
|
@ -15,18 +14,12 @@ export const getMySession = (req: NextApiRequest): Promise<User | null> => {
|
|||
return getSession({ req });
|
||||
};
|
||||
|
||||
export const throwIfNotLoggedIn = (user: User | null) => {
|
||||
if (!user) throw httpError.unauthorized();
|
||||
|
||||
return user;
|
||||
};
|
||||
|
||||
export const createHandler = (
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse,
|
||||
handlers: Partial<
|
||||
Record<
|
||||
"GET" | "POST" | "PUT" | "DELETE",
|
||||
"GET" | "POST" | "PUT" | "PATCH" | "DELETE",
|
||||
(req: NextApiRequest, res: NextApiResponse) => Promise<any>
|
||||
>
|
||||
>
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
import { createReactQueryHooks, createTRPCClient } from "@trpc/react";
|
||||
import superjson from "superjson";
|
||||
// Type-only import:
|
||||
// https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export
|
||||
import type { AppRouter } from "../pages/api/trpc/[trpc]";
|
||||
|
||||
// create helper methods for queries, mutations, and subscriptionos
|
||||
export const client = createTRPCClient<AppRouter>({
|
||||
url: "/api/trpc",
|
||||
transformer: superjson,
|
||||
});
|
||||
|
||||
// create react query hooks for trpc
|
||||
export const trpc = createReactQueryHooks({
|
||||
client,
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user