[questions][refactor] refactor useVote hooks

pull/521/head
Jeff Sieu 3 years ago
parent b18f68d36f
commit 785456c8f4

@ -1,7 +1,7 @@
import { format } from 'date-fns';
import { ChatBubbleLeftRightIcon } from '@heroicons/react/24/outline';
import { useAnswerVote } from '~/utils/questions/useVote';
import useAnswerVote from '~/utils/questions/vote/useAnswerVote';
import type { VotingButtonsProps } from '../VotingButtons';
import VotingButtons from '../VotingButtons';

@ -10,7 +10,7 @@ import type { QuestionsQuestionType } from '@prisma/client';
import { Button } from '@tih/ui';
import { useProtectedCallback } from '~/utils/questions/useProtectedCallback';
import { useQuestionVote } from '~/utils/questions/useVote';
import { useQuestionVote } from '~/utils/questions/vote/useQuestionVote';
import AddToListDropdown from '../../AddToListDropdown';
import type { CreateQuestionEncounterData } from '../../forms/CreateQuestionEncounterForm';

@ -1,4 +1,4 @@
import { useAnswerCommentVote } from '~/utils/questions/useVote';
import useAnswerCommentVote from '~/utils/questions/vote/useAnswerCommentVote';
import type { CommentListItemProps } from './CommentListItem';
import CommentListItem from './CommentListItem';

@ -1,4 +1,4 @@
import { useQuestionCommentVote } from '~/utils/questions/useVote';
import useQuestionCommentVote from '~/utils/questions/vote/useQuestionCommentVote';
import type { CommentListItemProps } from './CommentListItem';
import CommentListItem from './CommentListItem';

@ -1,567 +0,0 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { useCallback } from 'react';
import type { InfiniteData } from 'react-query';
import { Vote } from '@prisma/client';
import { trpc } from '../trpc';
import type {
Answer,
AnswerComment,
Question,
QuestionComment,
} from '~/types/questions';
type UseVoteOptions = {
setDownVote: () => void;
setNoVote: () => void;
setUpVote: () => void;
};
type BackendVote = {
id: string;
vote: Vote;
};
const createVoteCallbacks = (
vote: BackendVote | null,
opts: UseVoteOptions,
) => {
const { setDownVote, setNoVote, setUpVote } = opts;
const handleUpvote = () => {
// Either upvote or remove upvote
if (vote && vote.vote === 'UPVOTE') {
setNoVote();
} else {
setUpVote();
}
};
const handleDownvote = () => {
// Either downvote or remove downvote
if (vote && vote.vote === 'DOWNVOTE') {
setNoVote();
} else {
setDownVote();
}
};
return { handleDownvote, handleUpvote };
};
type MutationKey = Parameters<typeof trpc.useMutation>[0];
type QueryKey = Parameters<typeof trpc.useQuery>[0][0];
const getVoteValue = (vote: Vote | null) => {
if (vote === Vote.UPVOTE) {
return 1;
}
if (vote === Vote.DOWNVOTE) {
return -1;
}
return 0;
};
export const useQuestionVote = (id: string) => {
const utils = trpc.useContext();
return useVote(id, {
idKey: 'questionId',
invalidateKeys: [
// 'questions.questions.getQuestionById',
// 'questions.questions.getQuestionsByFilterAndContent',
],
onMutate: async (voteValueChange) => {
// Update question list
const questionQueries = utils.queryClient.getQueriesData([
'questions.questions.getQuestionsByFilterAndContent',
]);
const revertFunctions: Array<() => void> = [];
if (questionQueries !== undefined) {
for (const [key, query] of questionQueries) {
if (query === undefined) {
continue;
}
const { pages, ...restQuery } = query as InfiniteData<{
data: Array<Question>;
}>;
const newQuery = {
pages: pages.map(({ data, ...restPage }) => ({
data: data.map((question) => {
if (question.id === id) {
const { numVotes, ...restQuestion } = question;
return {
numVotes: numVotes + voteValueChange,
...restQuestion,
};
}
return question;
}),
...restPage,
})),
...restQuery,
};
utils.queryClient.setQueryData(key, newQuery);
revertFunctions.push(() => {
utils.queryClient.setQueryData(key, query);
});
}
}
const prevQuestion = utils.queryClient.getQueryData([
'questions.questions.getQuestionById',
{
id,
},
]) as Question | undefined;
if (prevQuestion !== undefined) {
const newQuestion = {
...prevQuestion,
numVotes: prevQuestion.numVotes + voteValueChange,
};
utils.queryClient.setQueryData(
['questions.questions.getQuestionById', { id }],
newQuestion,
);
revertFunctions.push(() => {
utils.queryClient.setQueryData(
['questions.questions.getQuestionById', { id }],
prevQuestion,
);
});
}
return () => {
for (const revertFunction of revertFunctions) {
revertFunction();
}
};
},
query: 'questions.questions.user.getVote',
setDownVoteKey: 'questions.questions.user.setDownVote',
setNoVoteKey: 'questions.questions.user.setNoVote',
setUpVoteKey: 'questions.questions.user.setUpVote',
});
};
export const useAnswerVote = (id: string) => {
const utils = trpc.useContext();
return useVote(id, {
idKey: 'answerId',
invalidateKeys: [
// 'questions.answers.getAnswerById',
// 'questions.answers.getAnswers',
],
onMutate: async (voteValueChange) => {
// Update question answer list
const answerQueries = utils.queryClient.getQueriesData([
'questions.answers.getAnswers',
]);
const revertFunctions: Array<() => void> = [];
if (answerQueries !== undefined) {
for (const [key, query] of answerQueries) {
if (query === undefined) {
continue;
}
const { pages, ...restQuery } = query as InfiniteData<{
data: Array<Answer>;
}>;
const newQuery = {
pages: pages.map(({ data, ...restPage }) => ({
data: data.map((answer) => {
if (answer.id === id) {
const { numVotes, ...restAnswer } = answer;
return {
numVotes: numVotes + voteValueChange,
...restAnswer,
};
}
return answer;
}),
...restPage,
})),
...restQuery,
};
utils.queryClient.setQueryData(key, newQuery);
revertFunctions.push(() => {
utils.queryClient.setQueryData(key, query);
});
}
}
const prevAnswer = utils.queryClient.getQueryData([
'questions.answers.getAnswerById',
{
answerId: id,
},
]) as Answer | undefined;
if (prevAnswer !== undefined) {
const newAnswer = {
...prevAnswer,
numVotes: prevAnswer.numVotes + voteValueChange,
};
utils.queryClient.setQueryData(
['questions.answers.getAnswerById', { answerId: id }],
newAnswer,
);
revertFunctions.push(() => {
utils.queryClient.setQueryData(
['questions.answers.getAnswerById', { answerId: id }],
prevAnswer,
);
});
}
return () => {
for (const revertFunction of revertFunctions) {
revertFunction();
}
};
},
query: 'questions.answers.user.getVote',
setDownVoteKey: 'questions.answers.user.setDownVote',
setNoVoteKey: 'questions.answers.user.setNoVote',
setUpVoteKey: 'questions.answers.user.setUpVote',
});
};
export const useQuestionCommentVote = (id: string) => {
const utils = trpc.useContext();
return useVote(id, {
idKey: 'questionCommentId',
invalidateKeys: [],
onMutate: async (voteValueChange) => {
// Update question comment list
const questionCommentQueries = utils.queryClient.getQueriesData([
'questions.questions.comments.getQuestionComments',
]);
const revertFunctions: Array<() => void> = [];
if (questionCommentQueries !== undefined) {
for (const [key, query] of questionCommentQueries) {
if (query === undefined) {
continue;
}
const { pages, ...restQuery } = query as InfiniteData<{
data: Array<QuestionComment>;
}>;
const newQuery = {
pages: pages.map(({ data, ...restPage }) => ({
data: data.map((questionComment) => {
if (questionComment.id === id) {
const { numVotes, ...restQuestionComment } = questionComment;
return {
numVotes: numVotes + voteValueChange,
...restQuestionComment,
};
}
return questionComment;
}),
...restPage,
})),
...restQuery,
};
utils.queryClient.setQueryData(key, newQuery);
revertFunctions.push(() => {
utils.queryClient.setQueryData(key, query);
});
}
}
return () => {
for (const revertFunction of revertFunctions) {
revertFunction();
}
};
},
query: 'questions.questions.comments.user.getVote',
setDownVoteKey: 'questions.questions.comments.user.setDownVote',
setNoVoteKey: 'questions.questions.comments.user.setNoVote',
setUpVoteKey: 'questions.questions.comments.user.setUpVote',
});
};
export const useAnswerCommentVote = (id: string) => {
const utils = trpc.useContext();
return useVote(id, {
idKey: 'answerCommentId',
invalidateKeys: [],
onMutate: async (voteValueChange) => {
// Update answer comment list
const answerCommentQueries = utils.queryClient.getQueriesData([
'questions.answers.comments.getAnswerComments',
]);
const revertFunctions: Array<() => void> = [];
if (answerCommentQueries !== undefined) {
for (const [key, query] of answerCommentQueries) {
if (query === undefined) {
continue;
}
const { pages, ...restQuery } = query as InfiniteData<{
data: Array<AnswerComment>;
}>;
const newQuery = {
pages: pages.map(({ data, ...restPage }) => ({
data: data.map((answerComment) => {
if (answerComment.id === id) {
const { numVotes, ...restAnswerComment } = answerComment;
return {
numVotes: numVotes + voteValueChange,
...restAnswerComment,
};
}
return answerComment;
}),
...restPage,
})),
...restQuery,
};
utils.queryClient.setQueryData(key, newQuery);
revertFunctions.push(() => {
utils.queryClient.setQueryData(key, query);
});
}
}
return () => {
for (const revertFunction of revertFunctions) {
revertFunction();
}
};
},
query: 'questions.answers.comments.user.getVote',
setDownVoteKey: 'questions.answers.comments.user.setDownVote',
setNoVoteKey: 'questions.answers.comments.user.setNoVote',
setUpVoteKey: 'questions.answers.comments.user.setUpVote',
});
};
type RevertFunction = () => void;
type InvalidateFunction = (voteValueChange: number) => Promise<RevertFunction>;
type VoteProps<VoteQueryKey extends QueryKey = QueryKey> = {
idKey: string;
invalidateKeys: Array<QueryKey>;
onMutate?: InvalidateFunction;
query: VoteQueryKey;
setDownVoteKey: MutationKey;
setNoVoteKey: MutationKey;
setUpVoteKey: MutationKey;
};
type UseVoteMutationContext = {
currentData: any;
previousData: any;
revert: RevertFunction | undefined;
};
export const useVote = <VoteQueryKey extends QueryKey = QueryKey>(
id: string,
opts: VoteProps<VoteQueryKey>,
) => {
const {
idKey,
invalidateKeys,
onMutate,
query,
setDownVoteKey,
setNoVoteKey,
setUpVoteKey,
} = opts;
const utils = trpc.useContext();
const onVoteUpdateSettled = useCallback(() => {
// TODO: Optimise query invalidation
// utils.invalidateQueries([query, { [idKey]: id } as any]);
for (const invalidateKey of invalidateKeys) {
utils.invalidateQueries(invalidateKey);
// If (invalidateFunction === null) {
// utils.invalidateQueries([invalidateKey as QueryKey]);
// } else {
// invalidateFunction(utils, previousVote, currentVote);
// }
}
}, [utils, invalidateKeys]);
const { data } = trpc.useQuery([
query,
{
[idKey]: id,
},
] as any);
const backendVote = data as BackendVote;
const { mutate: setUpVote } = trpc.useMutation<any, UseVoteMutationContext>(
setUpVoteKey,
{
onError: (_error, _variables, context) => {
if (context !== undefined) {
utils.setQueryData([query], context.previousData);
context.revert?.();
}
},
onMutate: async (vote) => {
await utils.queryClient.cancelQueries([query, { [idKey]: id } as any]);
const previousData = utils.queryClient.getQueryData<BackendVote | null>(
[query, { [idKey]: id } as any],
);
const currentData = {
...(vote as any),
vote: Vote.UPVOTE,
} as BackendVote;
utils.setQueryData(
[
query,
{
[idKey]: id,
} as any,
],
currentData as any,
);
const voteValueChange =
getVoteValue(currentData?.vote ?? null) -
getVoteValue(previousData?.vote ?? null);
const revert = await onMutate?.(voteValueChange);
return { currentData, previousData, revert };
},
onSettled: onVoteUpdateSettled,
},
);
const { mutate: setDownVote } = trpc.useMutation<any, UseVoteMutationContext>(
setDownVoteKey,
{
onError: (_error, _variables, context) => {
if (context !== undefined) {
utils.setQueryData([query], context.previousData);
context.revert?.();
}
},
onMutate: async (vote) => {
await utils.queryClient.cancelQueries([query, { [idKey]: id } as any]);
const previousData = utils.queryClient.getQueryData<BackendVote | null>(
[query, { [idKey]: id } as any],
);
const currentData = {
...vote,
vote: Vote.DOWNVOTE,
} as BackendVote;
utils.setQueryData(
[
query,
{
[idKey]: id,
} as any,
],
currentData as any,
);
const voteValueChange =
getVoteValue(currentData?.vote ?? null) -
getVoteValue(previousData?.vote ?? null);
const revert = await onMutate?.(voteValueChange);
return { currentData, previousData, revert };
},
onSettled: onVoteUpdateSettled,
},
);
const { mutate: setNoVote } = trpc.useMutation<any, UseVoteMutationContext>(
setNoVoteKey,
{
onError: (_error, _variables, context) => {
if (context !== undefined) {
utils.setQueryData([query], context.previousData);
context.revert?.();
}
},
onMutate: async () => {
await utils.queryClient.cancelQueries([query, { [idKey]: id } as any]);
const previousData = utils.queryClient.getQueryData<BackendVote | null>(
[query, { [idKey]: id } as any],
);
const currentData: BackendVote | null = null;
utils.queryClient.setQueryData<BackendVote | null>(
[
query,
{
[idKey]: id,
} as any,
],
currentData,
);
const voteValueChange =
getVoteValue(null) - getVoteValue(previousData?.vote ?? null);
const revert = await onMutate?.(voteValueChange);
return { currentData, previousData, revert };
},
onSettled: onVoteUpdateSettled,
},
);
const { handleDownvote, handleUpvote } = createVoteCallbacks(
backendVote ?? null,
{
setDownVote: () => {
setDownVote({
[idKey]: id,
});
},
setNoVote: () => {
setNoVote({
[idKey]: id,
});
},
setUpVote: () => {
setUpVote({
[idKey]: id,
});
},
},
);
return { handleDownvote, handleUpvote, vote: backendVote ?? null };
};

@ -0,0 +1,68 @@
import type { InfiniteData } from 'react-query';
import { trpc } from '~/utils/trpc';
import useVote from './useVote';
import type { AnswerComment } from '~/types/questions';
export default function useAnswerCommentVote(id: string) {
const utils = trpc.useContext();
return useVote(id, {
idKey: 'answerCommentId',
invalidateKeys: [],
onMutate: async (voteValueChange) => {
// Update answer comment list
const answerCommentQueries = utils.queryClient.getQueriesData([
'questions.answers.comments.getAnswerComments',
]);
const revertFunctions: Array<() => void> = [];
if (answerCommentQueries !== undefined) {
for (const [key, query] of answerCommentQueries) {
if (query === undefined) {
continue;
}
const { pages, ...restQuery } = query as InfiniteData<{
data: Array<AnswerComment>;
}>;
const newQuery = {
pages: pages.map(({ data, ...restPage }) => ({
data: data.map((answerComment) => {
if (answerComment.id === id) {
const { numVotes, ...restAnswerComment } = answerComment;
return {
numVotes: numVotes + voteValueChange,
...restAnswerComment,
};
}
return answerComment;
}),
...restPage,
})),
...restQuery,
};
utils.queryClient.setQueryData(key, newQuery);
revertFunctions.push(() => {
utils.queryClient.setQueryData(key, query);
});
}
}
return () => {
for (const revertFunction of revertFunctions) {
revertFunction();
}
};
},
query: 'questions.answers.comments.user.getVote',
setDownVoteKey: 'questions.answers.comments.user.setDownVote',
setNoVoteKey: 'questions.answers.comments.user.setNoVote',
setUpVoteKey: 'questions.answers.comments.user.setUpVote',
});
}

@ -0,0 +1,98 @@
import type { InfiniteData } from 'react-query';
import { trpc } from '~/utils/trpc';
import useVote from './useVote';
import type { Answer } from '~/types/questions';
export default function useAnswerVote(id: string) {
const utils = trpc.useContext();
return useVote(id, {
idKey: 'answerId',
invalidateKeys: [
// 'questions.answers.getAnswerById',
// 'questions.answers.getAnswers',
],
onMutate: async (voteValueChange) => {
// Update question answer list
const answerQueries = utils.queryClient.getQueriesData([
'questions.answers.getAnswers',
]);
const revertFunctions: Array<() => void> = [];
if (answerQueries !== undefined) {
for (const [key, query] of answerQueries) {
if (query === undefined) {
continue;
}
const { pages, ...restQuery } = query as InfiniteData<{
data: Array<Answer>;
}>;
const newQuery = {
pages: pages.map(({ data, ...restPage }) => ({
data: data.map((answer) => {
if (answer.id === id) {
const { numVotes, ...restAnswer } = answer;
return {
numVotes: numVotes + voteValueChange,
...restAnswer,
};
}
return answer;
}),
...restPage,
})),
...restQuery,
};
utils.queryClient.setQueryData(key, newQuery);
revertFunctions.push(() => {
utils.queryClient.setQueryData(key, query);
});
}
}
const prevAnswer = utils.queryClient.getQueryData([
'questions.answers.getAnswerById',
{
answerId: id,
},
]) as Answer | undefined;
if (prevAnswer !== undefined) {
const newAnswer = {
...prevAnswer,
numVotes: prevAnswer.numVotes + voteValueChange,
};
utils.queryClient.setQueryData(
['questions.answers.getAnswerById', { answerId: id }],
newAnswer,
);
revertFunctions.push(() => {
utils.queryClient.setQueryData(
['questions.answers.getAnswerById', { answerId: id }],
prevAnswer,
);
});
}
return () => {
for (const revertFunction of revertFunctions) {
revertFunction();
}
};
},
query: 'questions.answers.user.getVote',
setDownVoteKey: 'questions.answers.user.setDownVote',
setNoVoteKey: 'questions.answers.user.setNoVote',
setUpVoteKey: 'questions.answers.user.setUpVote',
});
}

@ -0,0 +1,69 @@
import type { InfiniteData } from 'react-query';
import { trpc } from '~/utils/trpc';
import useVote from './useVote';
import type { QuestionComment } from '~/types/questions';
export default function useQuestionCommentVote(id: string) {
const utils = trpc.useContext();
return useVote(id, {
idKey: 'questionCommentId',
invalidateKeys: [],
onMutate: async (voteValueChange) => {
// Update question comment list
const questionCommentQueries = utils.queryClient.getQueriesData([
'questions.questions.comments.getQuestionComments',
]);
const revertFunctions: Array<() => void> = [];
if (questionCommentQueries !== undefined) {
for (const [key, query] of questionCommentQueries) {
if (query === undefined) {
continue;
}
const { pages, ...restQuery } = query as InfiniteData<{
data: Array<QuestionComment>;
}>;
const newQuery = {
pages: pages.map(({ data, ...restPage }) => ({
data: data.map((questionComment) => {
if (questionComment.id === id) {
const { numVotes, ...restQuestionComment } = questionComment;
return {
numVotes: numVotes + voteValueChange,
...restQuestionComment,
};
}
return questionComment;
}),
...restPage,
})),
...restQuery,
};
utils.queryClient.setQueryData(key, newQuery);
revertFunctions.push(() => {
utils.queryClient.setQueryData(key, query);
});
}
}
return () => {
for (const revertFunction of revertFunctions) {
revertFunction();
}
};
},
query: 'questions.questions.comments.user.getVote',
setDownVoteKey: 'questions.questions.comments.user.setDownVote',
setNoVoteKey: 'questions.questions.comments.user.setNoVote',
setUpVoteKey: 'questions.questions.comments.user.setUpVote',
});
}

@ -0,0 +1,98 @@
import type { InfiniteData } from 'react-query';
import { trpc } from '~/utils/trpc';
import useVote from './useVote';
import type { Question } from '~/types/questions';
export const useQuestionVote = (id: string) => {
const utils = trpc.useContext();
return useVote(id, {
idKey: 'questionId',
invalidateKeys: [
// 'questions.questions.getQuestionById',
// 'questions.questions.getQuestionsByFilterAndContent',
],
onMutate: async (voteValueChange) => {
// Update question list
const questionQueries = utils.queryClient.getQueriesData([
'questions.questions.getQuestionsByFilterAndContent',
]);
const revertFunctions: Array<() => void> = [];
if (questionQueries !== undefined) {
for (const [key, query] of questionQueries) {
if (query === undefined) {
continue;
}
const { pages, ...restQuery } = query as InfiniteData<{
data: Array<Question>;
}>;
const newQuery = {
pages: pages.map(({ data, ...restPage }) => ({
data: data.map((question) => {
if (question.id === id) {
const { numVotes, ...restQuestion } = question;
return {
numVotes: numVotes + voteValueChange,
...restQuestion,
};
}
return question;
}),
...restPage,
})),
...restQuery,
};
utils.queryClient.setQueryData(key, newQuery);
revertFunctions.push(() => {
utils.queryClient.setQueryData(key, query);
});
}
}
const prevQuestion = utils.queryClient.getQueryData([
'questions.questions.getQuestionById',
{
id,
},
]) as Question | undefined;
if (prevQuestion !== undefined) {
const newQuestion = {
...prevQuestion,
numVotes: prevQuestion.numVotes + voteValueChange,
};
utils.queryClient.setQueryData(
['questions.questions.getQuestionById', { id }],
newQuestion,
);
revertFunctions.push(() => {
utils.queryClient.setQueryData(
['questions.questions.getQuestionById', { id }],
prevQuestion,
);
});
}
return () => {
for (const revertFunction of revertFunctions) {
revertFunction();
}
};
},
query: 'questions.questions.user.getVote',
setDownVoteKey: 'questions.questions.user.setDownVote',
setNoVoteKey: 'questions.questions.user.setNoVote',
setUpVoteKey: 'questions.questions.user.setUpVote',
});
};

@ -0,0 +1,254 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { useCallback } from 'react';
import { Vote } from '@prisma/client';
import { trpc } from '../../trpc';
type UseVoteOptions = {
setDownVote: () => void;
setNoVote: () => void;
setUpVote: () => void;
};
type BackendVote = {
id: string;
vote: Vote;
};
const createVoteCallbacks = (
vote: BackendVote | null,
opts: UseVoteOptions,
) => {
const { setDownVote, setNoVote, setUpVote } = opts;
const handleUpvote = () => {
// Either upvote or remove upvote
if (vote && vote.vote === 'UPVOTE') {
setNoVote();
} else {
setUpVote();
}
};
const handleDownvote = () => {
// Either downvote or remove downvote
if (vote && vote.vote === 'DOWNVOTE') {
setNoVote();
} else {
setDownVote();
}
};
return { handleDownvote, handleUpvote };
};
type MutationKey = Parameters<typeof trpc.useMutation>[0];
type QueryKey = Parameters<typeof trpc.useQuery>[0][0];
const getVoteValue = (vote: Vote | null) => {
if (vote === Vote.UPVOTE) {
return 1;
}
if (vote === Vote.DOWNVOTE) {
return -1;
}
return 0;
};
type RevertFunction = () => void;
type InvalidateFunction = (voteValueChange: number) => Promise<RevertFunction>;
type VoteProps<VoteQueryKey extends QueryKey = QueryKey> = {
idKey: string;
invalidateKeys: Array<QueryKey>;
onMutate?: InvalidateFunction;
query: VoteQueryKey;
setDownVoteKey: MutationKey;
setNoVoteKey: MutationKey;
setUpVoteKey: MutationKey;
};
type UseVoteMutationContext = {
currentData: any;
previousData: any;
revert: RevertFunction | undefined;
};
export default function useVote<VoteQueryKey extends QueryKey = QueryKey>(
id: string,
opts: VoteProps<VoteQueryKey>,
) {
const {
idKey,
invalidateKeys,
onMutate,
query,
setDownVoteKey,
setNoVoteKey,
setUpVoteKey,
} = opts;
const utils = trpc.useContext();
const onVoteUpdateSettled = useCallback(() => {
// TODO: Optimise query invalidation
// utils.invalidateQueries([query, { [idKey]: id } as any]);
for (const invalidateKey of invalidateKeys) {
utils.invalidateQueries(invalidateKey);
// If (invalidateFunction === null) {
// utils.invalidateQueries([invalidateKey as QueryKey]);
// } else {
// invalidateFunction(utils, previousVote, currentVote);
// }
}
}, [utils, invalidateKeys]);
const { data } = trpc.useQuery([
query,
{
[idKey]: id,
},
] as any);
const backendVote = data as BackendVote;
const { mutate: setUpVote } = trpc.useMutation<any, UseVoteMutationContext>(
setUpVoteKey,
{
onError: (_error, _variables, context) => {
if (context !== undefined) {
utils.setQueryData([query], context.previousData);
context.revert?.();
}
},
onMutate: async (vote) => {
await utils.queryClient.cancelQueries([query, { [idKey]: id } as any]);
const previousData = utils.queryClient.getQueryData<BackendVote | null>(
[query, { [idKey]: id } as any],
);
const currentData = {
...(vote as any),
vote: Vote.UPVOTE,
} as BackendVote;
utils.setQueryData(
[
query,
{
[idKey]: id,
} as any,
],
currentData as any,
);
const voteValueChange =
getVoteValue(currentData?.vote ?? null) -
getVoteValue(previousData?.vote ?? null);
const revert = await onMutate?.(voteValueChange);
return { currentData, previousData, revert };
},
onSettled: onVoteUpdateSettled,
},
);
const { mutate: setDownVote } = trpc.useMutation<any, UseVoteMutationContext>(
setDownVoteKey,
{
onError: (_error, _variables, context) => {
if (context !== undefined) {
utils.setQueryData([query], context.previousData);
context.revert?.();
}
},
onMutate: async (vote) => {
await utils.queryClient.cancelQueries([query, { [idKey]: id } as any]);
const previousData = utils.queryClient.getQueryData<BackendVote | null>(
[query, { [idKey]: id } as any],
);
const currentData = {
...vote,
vote: Vote.DOWNVOTE,
} as BackendVote;
utils.setQueryData(
[
query,
{
[idKey]: id,
} as any,
],
currentData as any,
);
const voteValueChange =
getVoteValue(currentData?.vote ?? null) -
getVoteValue(previousData?.vote ?? null);
const revert = await onMutate?.(voteValueChange);
return { currentData, previousData, revert };
},
onSettled: onVoteUpdateSettled,
},
);
const { mutate: setNoVote } = trpc.useMutation<any, UseVoteMutationContext>(
setNoVoteKey,
{
onError: (_error, _variables, context) => {
if (context !== undefined) {
utils.setQueryData([query], context.previousData);
context.revert?.();
}
},
onMutate: async () => {
await utils.queryClient.cancelQueries([query, { [idKey]: id } as any]);
const previousData = utils.queryClient.getQueryData<BackendVote | null>(
[query, { [idKey]: id } as any],
);
const currentData: BackendVote | null = null;
utils.queryClient.setQueryData<BackendVote | null>(
[
query,
{
[idKey]: id,
} as any,
],
currentData,
);
const voteValueChange =
getVoteValue(null) - getVoteValue(previousData?.vote ?? null);
const revert = await onMutate?.(voteValueChange);
return { currentData, previousData, revert };
},
onSettled: onVoteUpdateSettled,
},
);
const { handleDownvote, handleUpvote } = createVoteCallbacks(
backendVote ?? null,
{
setDownVote: () => {
setDownVote({
[idKey]: id,
});
},
setNoVote: () => {
setNoVote({
[idKey]: id,
});
},
setUpVote: () => {
setUpVote({
[idKey]: id,
});
},
},
);
return { handleDownvote, handleUpvote, vote: backendVote ?? null };
}
Loading…
Cancel
Save