[questions][fix] fix upvotes (#521)
parent
ffbb6a29f2
commit
a7c9f58ef3
@ -0,0 +1,28 @@
|
|||||||
|
import useAnswerCommentVote from '~/utils/questions/vote/useAnswerCommentVote';
|
||||||
|
|
||||||
|
import type { CommentListItemProps } from './CommentListItem';
|
||||||
|
import CommentListItem from './CommentListItem';
|
||||||
|
|
||||||
|
export type AnswerCommentListItemProps = Omit<
|
||||||
|
CommentListItemProps,
|
||||||
|
'onDownvote' | 'onUpvote' | 'vote'
|
||||||
|
> & {
|
||||||
|
answerCommentId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function AnswerCommentListItem({
|
||||||
|
answerCommentId,
|
||||||
|
...restProps
|
||||||
|
}: AnswerCommentListItemProps) {
|
||||||
|
const { handleDownvote, handleUpvote, vote } =
|
||||||
|
useAnswerCommentVote(answerCommentId);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CommentListItem
|
||||||
|
vote={vote}
|
||||||
|
onDownvote={handleDownvote}
|
||||||
|
onUpvote={handleUpvote}
|
||||||
|
{...restProps}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
@ -1,37 +1,37 @@
|
|||||||
import { format } from 'date-fns';
|
import { format } from 'date-fns';
|
||||||
|
|
||||||
import { useAnswerCommentVote } from '~/utils/questions/useVote';
|
import type { BackendVote } from '../VotingButtons';
|
||||||
|
import VotingButtons from '../VotingButtons';
|
||||||
|
|
||||||
import VotingButtons from './VotingButtons';
|
export type CommentListItemProps = {
|
||||||
|
|
||||||
export type AnswerCommentListItemProps = {
|
|
||||||
answerCommentId: string;
|
|
||||||
authorImageUrl: string;
|
authorImageUrl: string;
|
||||||
authorName: string;
|
authorName: string;
|
||||||
content: string;
|
content: string;
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
|
onDownvote: () => void;
|
||||||
|
onUpvote: () => void;
|
||||||
upvoteCount: number;
|
upvoteCount: number;
|
||||||
|
vote: BackendVote;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function AnswerCommentListItem({
|
export default function CommentListItem({
|
||||||
authorImageUrl,
|
authorImageUrl,
|
||||||
authorName,
|
authorName,
|
||||||
content,
|
content,
|
||||||
createdAt,
|
createdAt,
|
||||||
upvoteCount,
|
upvoteCount,
|
||||||
answerCommentId,
|
vote,
|
||||||
}: AnswerCommentListItemProps) {
|
onDownvote,
|
||||||
const { handleDownvote, handleUpvote, vote } =
|
onUpvote,
|
||||||
useAnswerCommentVote(answerCommentId);
|
}: CommentListItemProps) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex gap-4 rounded-md border bg-white p-2">
|
<div className="flex gap-4 rounded-md border bg-white p-2">
|
||||||
<VotingButtons
|
<VotingButtons
|
||||||
size="sm"
|
size="sm"
|
||||||
upvoteCount={upvoteCount}
|
upvoteCount={upvoteCount}
|
||||||
vote={vote}
|
vote={vote}
|
||||||
onDownvote={handleDownvote}
|
onDownvote={onDownvote}
|
||||||
onUpvote={handleUpvote}
|
onUpvote={onUpvote}
|
||||||
/>
|
/>
|
||||||
<div className="mt-1 flex flex-col gap-1">
|
<div className="mt-1 flex flex-col gap-1">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
@ -0,0 +1,28 @@
|
|||||||
|
import useQuestionCommentVote from '~/utils/questions/vote/useQuestionCommentVote';
|
||||||
|
|
||||||
|
import type { CommentListItemProps } from './CommentListItem';
|
||||||
|
import CommentListItem from './CommentListItem';
|
||||||
|
|
||||||
|
export type QuestionCommentListItemProps = Omit<
|
||||||
|
CommentListItemProps,
|
||||||
|
'onDownvote' | 'onUpvote' | 'vote'
|
||||||
|
> & {
|
||||||
|
questionCommentId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function QuestionCommentListItem({
|
||||||
|
questionCommentId,
|
||||||
|
...restProps
|
||||||
|
}: QuestionCommentListItemProps) {
|
||||||
|
const { handleDownvote, handleUpvote, vote } =
|
||||||
|
useQuestionCommentVote(questionCommentId);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CommentListItem
|
||||||
|
vote={vote}
|
||||||
|
onDownvote={handleDownvote}
|
||||||
|
onUpvote={handleUpvote}
|
||||||
|
{...restProps}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
@ -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',
|
||||||
|
});
|
||||||
|
};
|
Loading…
Reference in new issue