sendou.ink/hooks/plus.ts
Kalle 1589b84c4b
New layout (#427) closes #405
* side layout initial

* add elements to side nav

* side buttons links

* remove clog

* calendar page initial

* position sticky working

* x trends page initial

* new table

* same mode selector

* mobile friendly table

* no underline for nav links

* xsearch

* x trends page outlined

* sr initial

* relocate calendar components

* calendar fix flex

* topnav fancier look

* layout looking good edition

* relocate xtrends

* xtrends remove linecharts

* x trends new

* calender page new

* delete headbanner, new login

* remove calendar stuff from api

* rename stuff in utils

* fix user item margin

* new home page initial

* remove page concept

* no pointer xtrends

* remove xrank from app

* xtrends service

* move fa from app

* move plus

* maps tweaks

* new table for plus history

* navigational sidebar flex tweaks

* builds page

* analyzer

* user page

* free agents

* plans

* remove mx

* tweaks

* change layout to grid

* home page finalized

* mobile nav

* restrict main content width

* tweaks style

* language switcher

* container in css

* sticky nav

* use duplicate icons for now

* change mapsketch width to old

* chara tour vid

* borzoic icons
2021-04-21 17:26:50 +03:00

147 lines
4.5 KiB
TypeScript

import { useToast } from "@chakra-ui/toast";
import { useUser } from "hooks/common";
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 * as z from "zod";
export function usePlusHomePage() {
const [user] = 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 suggestions = suggestionsData ?? [];
return {
plusStatusData: plusStatusData?.find(
(status) => status.user.id === user?.id
),
vouchedPlusStatusData: plusStatusData?.find(
(status) => status.voucher?.id === user?.id
),
suggestionsData: suggestions.filter(
(suggestion) =>
!suggestionsFilter || suggestion.tier === suggestionsFilter
),
suggestionCounts: suggestions.reduce(
(counts, suggestion) => {
const tierString = [null, "ONE", "TWO", "THREE"][
suggestion.tier
] as keyof typeof counts;
counts[tierString]++;
return counts;
},
{ ONE: 0, TWO: 0, THREE: 0 }
),
ownSuggestion: suggestions.find(
(suggestion) => suggestion.suggesterUser.id === user?.id
),
setSuggestionsFilter,
votingProgress,
};
}
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 { 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, status: editVoteStatus } = trpc.useMutation(
"plus.editVote",
{
onSuccess() {
toast(getToastOptions("Successfully edited vote", "success"));
utils.invalidateQuery(["plus.votedUserScores"]);
},
onError(error) {
toast(getToastOptions(error.message, "error"));
},
}
);
const ownPlusStatus = statuses?.find((status) => status.user.id === user?.id);
const getVotedUsers = () => {
if (!votedUserScores || !usersForVoting) return undefined;
return usersForVoting
.map((u) => {
return { ...u, score: votedUserScores.get(u.userId)! };
})
.sort((a, b) => a.username.localeCompare(b.username));
};
return {
isLoading: isLoadingBallots || isLoadingStatuses || hasVotedIsLoading,
shouldRedirect: !isLoadingBallots && !usersForVoting,
plusStatus: ownPlusStatus,
currentUser: usersForVoting?.[currentIndex],
previousUser:
currentIndex > 0 && usersForVoting
? { ...usersForVoting[currentIndex - 1], ...votes[votes.length - 1] }
: undefined,
progress: usersForVoting
? (currentIndex / usersForVoting.length) * 100
: undefined,
handleVote: (vote: Unpacked<z.infer<typeof votesSchema>>) => {
const nextIndex = currentIndex + 1;
setVotes([...votes, vote]);
setCurrentIndex(nextIndex);
(<HTMLElement>document.activeElement).blur();
// preload next avatar
const next = usersForVoting?.[nextIndex + 1];
if (next) {
new Image().src = `https://cdn.discordapp.com/avatars/${next.discordId}/${next.discordAvatar}.jpg`;
}
},
goBack: () => {
setVotes(votes.slice(0, votes.length - 1));
setCurrentIndex(currentIndex - 1);
},
submit: () => mutateVote(votes),
voteStatus,
votedUsers: getVotedUsers(),
editVote: editVoteMutate,
};
}