[question][feat] update question answers,comments optimistically

pull/521/head
Jeff Sieu 3 years ago
parent 9fda043862
commit 3aaeaa8085

@ -245,8 +245,7 @@ export default function QuestionPage() {
/> />
</div> </div>
</div> </div>
{(commentData?.pages ?? []).flatMap( {(commentData?.pages ?? []).flatMap(({ data: comments }) =>
({ processedQuestionCommentsData: comments }) =>
comments.map((comment) => ( comments.map((comment) => (
<QuestionCommentListItem <QuestionCommentListItem
key={comment.id} key={comment.id}
@ -326,8 +325,7 @@ export default function QuestionPage() {
</div> </div>
</div> </div>
{/* TODO: Add button to load more */} {/* TODO: Add button to load more */}
{(answerData?.pages ?? []).flatMap( {(answerData?.pages ?? []).flatMap(({ data: answers }) =>
({ processedAnswersData: answers }) =>
answers.map((answer) => ( answers.map((answer) => (
<QuestionAnswerCard <QuestionAnswerCard
key={answer.id} key={answer.id}

@ -38,7 +38,6 @@ export const questionsAnswerRouter = createRouter()
}, },
]; ];
const answersData = await ctx.prisma.questionsAnswer.findMany({ const answersData = await ctx.prisma.questionsAnswer.findMany({
cursor: cursor ? { id: cursor } : undefined, cursor: cursor ? { id: cursor } : undefined,
include: { include: {
@ -104,9 +103,9 @@ export const questionsAnswerRouter = createRouter()
} }
return { return {
data: processedAnswersData,
nextCursor, nextCursor,
processedAnswersData, };
}
}, },
}) })
.query('getAnswerById', { .query('getAnswerById', {

@ -97,9 +97,9 @@ export const questionsQuestionCommentRouter = createRouter().query(
} }
return { return {
data: processedQuestionCommentsData,
nextCursor, nextCursor,
processedQuestionCommentsData, };
}
}, },
}, },
); );

@ -256,18 +256,15 @@ export const questionsQuestionCommentUserRouter = createProtectedRouter()
} }
if (vote.vote === Vote.UPVOTE) { if (vote.vote === Vote.UPVOTE) {
tx.questionsQuestionCommentVote.delete({ const updatedVote = await tx.questionsQuestionCommentVote.update({
where: {
id: vote.id,
},
});
const createdVote = await tx.questionsQuestionCommentVote.create({
data: { data: {
questionCommentId, questionCommentId,
userId, userId,
vote: Vote.DOWNVOTE, vote: Vote.DOWNVOTE,
}, },
where: {
id: vote.id,
},
}); });
await tx.questionsQuestionComment.update({ await tx.questionsQuestionComment.update({
@ -281,7 +278,7 @@ export const questionsQuestionCommentUserRouter = createProtectedRouter()
}, },
}); });
return createdVote; return updatedVote;
} }
}); });
}, },

@ -5,7 +5,7 @@ import { Vote } from '@prisma/client';
import { trpc } from '../trpc'; import { trpc } from '../trpc';
import type { Question } from '~/types/questions'; import type { Answer, Question, QuestionComment } from '~/types/questions';
type UseVoteOptions = { type UseVoteOptions = {
setDownVote: () => void; setDownVote: () => void;
@ -67,13 +67,11 @@ export const useQuestionVote = (id: string) => {
// 'questions.questions.getQuestionById', // 'questions.questions.getQuestionById',
// 'questions.questions.getQuestionsByFilterAndContent', // 'questions.questions.getQuestionsByFilterAndContent',
], ],
onMutate: async (previousVote, currentVote) => { onMutate: async (voteValueChange) => {
// Update question list // Update question list
const questionQueries = utils.queryClient.getQueriesData([ const questionQueries = utils.queryClient.getQueriesData([
'questions.questions.getQuestionsByFilterAndContent', 'questions.questions.getQuestionsByFilterAndContent',
]); ]);
const voteValueChange =
getVoteValue(currentVote) - getVoteValue(previousVote);
if (questionQueries !== undefined) { if (questionQueries !== undefined) {
for (const [key, query] of questionQueries) { for (const [key, query] of questionQueries) {
@ -133,12 +131,70 @@ export const useQuestionVote = (id: string) => {
}; };
export const useAnswerVote = (id: string) => { export const useAnswerVote = (id: string) => {
const utils = trpc.useContext();
return useVote(id, { return useVote(id, {
idKey: 'answerId', idKey: 'answerId',
invalidateKeys: [ invalidateKeys: [
'questions.answers.getAnswerById', // 'questions.answers.getAnswerById',
'questions.answers.getAnswers', // 'questions.answers.getAnswers',
], ],
onMutate: async (voteValueChange) => {
// Update question answer list
const answerQueries = utils.queryClient.getQueriesData([
'questions.answers.getAnswers',
]);
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);
}
}
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,
);
}
},
query: 'questions.answers.user.getVote', query: 'questions.answers.user.getVote',
setDownVoteKey: 'questions.answers.user.setDownVote', setDownVoteKey: 'questions.answers.user.setDownVote',
setNoVoteKey: 'questions.answers.user.setNoVote', setNoVoteKey: 'questions.answers.user.setNoVote',
@ -147,9 +203,48 @@ export const useAnswerVote = (id: string) => {
}; };
export const useQuestionCommentVote = (id: string) => { export const useQuestionCommentVote = (id: string) => {
const utils = trpc.useContext();
return useVote(id, { return useVote(id, {
idKey: 'questionCommentId', idKey: 'questionCommentId',
invalidateKeys: ['questions.questions.comments.getQuestionComments'], invalidateKeys: [],
onMutate: async (voteValueChange) => {
// Update question comment list
const questionCommentQueries = utils.queryClient.getQueriesData([
'questions.questions.comments.getQuestionComments',
]);
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);
}
}
},
query: 'questions.questions.comments.user.getVote', query: 'questions.questions.comments.user.getVote',
setDownVoteKey: 'questions.questions.comments.user.setDownVote', setDownVoteKey: 'questions.questions.comments.user.setDownVote',
setNoVoteKey: 'questions.questions.comments.user.setNoVote', setNoVoteKey: 'questions.questions.comments.user.setNoVote',
@ -168,10 +263,7 @@ export const useAnswerCommentVote = (id: string) => {
}); });
}; };
type InvalidateFunction = ( type InvalidateFunction = (voteValueChange: number) => Promise<void>;
previousVote: Vote | null,
currentVote: Vote | null,
) => Promise<void>;
type VoteProps<VoteQueryKey extends QueryKey = QueryKey> = { type VoteProps<VoteQueryKey extends QueryKey = QueryKey> = {
idKey: string; idKey: string;
@ -205,7 +297,7 @@ export const useVote = <VoteQueryKey extends QueryKey = QueryKey>(
} = opts; } = opts;
const utils = trpc.useContext(); const utils = trpc.useContext();
const onVoteUpdate = useCallback(() => { const onVoteUpdateSettled = useCallback(() => {
// TODO: Optimise query invalidation // TODO: Optimise query invalidation
// utils.invalidateQueries([query, { [idKey]: id } as any]); // utils.invalidateQueries([query, { [idKey]: id } as any]);
for (const invalidateKey of invalidateKeys) { for (const invalidateKey of invalidateKeys) {
@ -256,10 +348,14 @@ export const useVote = <VoteQueryKey extends QueryKey = QueryKey>(
currentData as any, currentData as any,
); );
await onMutate?.(previousData?.vote ?? null, currentData?.vote ?? null); const voteValueChange =
getVoteValue(currentData?.vote ?? null) -
getVoteValue(previousData?.vote ?? null);
await onMutate?.(voteValueChange);
return { currentData, previousData }; return { currentData, previousData };
}, },
onSettled: onVoteUpdate, onSettled: onVoteUpdateSettled,
}, },
); );
const { mutate: setDownVote } = trpc.useMutation<any, UseVoteMutationContext>( const { mutate: setDownVote } = trpc.useMutation<any, UseVoteMutationContext>(
@ -291,10 +387,14 @@ export const useVote = <VoteQueryKey extends QueryKey = QueryKey>(
currentData as any, currentData as any,
); );
await onMutate?.(previousData?.vote ?? null, currentData?.vote ?? null); const voteValueChange =
getVoteValue(currentData?.vote ?? null) -
getVoteValue(previousData?.vote ?? null);
await onMutate?.(voteValueChange);
return { currentData, previousData }; return { currentData, previousData };
}, },
onSettled: onVoteUpdate, onSettled: onVoteUpdateSettled,
}, },
); );
@ -323,11 +423,13 @@ export const useVote = <VoteQueryKey extends QueryKey = QueryKey>(
currentData, currentData,
); );
await onMutate?.(previousData?.vote ?? null, null); const voteValueChange =
getVoteValue(null) - getVoteValue(previousData?.vote ?? null);
await onMutate?.(voteValueChange);
return { currentData, previousData }; return { currentData, previousData };
}, },
onSettled: onVoteUpdate, onSettled: onVoteUpdateSettled,
}, },
); );

Loading…
Cancel
Save