[resumes][refactor] abstract comment votes fetching from comments

pull/389/head
Terence Ho 3 years ago
parent 2900dbeb1a
commit 41963ef28b

@ -54,25 +54,32 @@ export default function ResumeCommentListItem({
}, },
}, },
); );
const commentUpvoteUpsertMutation = trpc.useMutation(
'resumes.comments.upvotes.user.upsert', // COMMENT VOTES
const commentVotesQuery = trpc.useQuery([
'resumes.comments.votes.list',
{ commentId: comment.id },
]);
const commentVotesUpsertMutation = trpc.useMutation(
'resumes.comments.votes.user.upsert',
{ {
onSuccess: () => { onSuccess: () => {
// Comment updated, invalidate query to trigger refetch // Comment updated, invalidate query to trigger refetch
trpcContext.invalidateQueries(['resumes.comments.list']); trpcContext.invalidateQueries(['resumes.comments.votes.list']);
}, },
}, },
); );
const commentUpvoteDeleteMutation = trpc.useMutation( const commentVotesDeleteMutation = trpc.useMutation(
'resumes.comments.upvotes.user.delete', 'resumes.comments.votes.user.delete',
{ {
onSuccess: () => { onSuccess: () => {
// Comment updated, invalidate query to trigger refetch // Comment updated, invalidate query to trigger refetch
trpcContext.invalidateQueries(['resumes.comments.list']); trpcContext.invalidateQueries(['resumes.comments.votes.list']);
}, },
}, },
); );
// FORM ACTIONS
const onCancel = () => { const onCancel = () => {
reset({ description: comment.description }); reset({ description: comment.description });
setIsEditingComment(false); setIsEditingComment(false);
@ -98,14 +105,13 @@ export default function ResumeCommentListItem({
}; };
const onVote = async (value: Vote) => { const onVote = async (value: Vote) => {
if (comment.userVote?.value === value) { if (commentVotesQuery.data?.userVote?.value === value) {
return commentUpvoteDeleteMutation.mutate({ return commentVotesDeleteMutation.mutate({
commentId: comment.id, commentId: comment.id,
}); });
} }
return commentUpvoteUpsertMutation.mutate({ return commentVotesUpsertMutation.mutate({
commentId: comment.id, commentId: comment.id,
id: comment.userVote?.id,
value, value,
}); });
}; };
@ -190,15 +196,16 @@ export default function ResumeCommentListItem({
<button <button
disabled={ disabled={
!userId || !userId ||
commentUpvoteUpsertMutation.isLoading || commentVotesQuery.isLoading ||
commentUpvoteDeleteMutation.isLoading commentVotesUpsertMutation.isLoading ||
commentVotesDeleteMutation.isLoading
} }
type="button" type="button"
onClick={() => onVote(Vote.UPVOTE)}> onClick={() => onVote(Vote.UPVOTE)}>
<ArrowUpCircleIcon <ArrowUpCircleIcon
className={clsx( className={clsx(
'h-4 w-4', 'h-4 w-4',
comment.userVote?.value === Vote.UPVOTE commentVotesQuery.data?.userVote?.value === Vote.UPVOTE
? 'fill-indigo-500' ? 'fill-indigo-500'
: 'fill-gray-400', : 'fill-gray-400',
userId && 'hover:fill-indigo-500', userId && 'hover:fill-indigo-500',
@ -206,20 +213,23 @@ export default function ResumeCommentListItem({
/> />
</button> </button>
<div className="text-xs">{comment.numVotes}</div> <div className="text-xs">
{commentVotesQuery.data?.numVotes ?? 0}
</div>
<button <button
disabled={ disabled={
!userId || !userId ||
commentUpvoteUpsertMutation.isLoading || commentVotesQuery.isLoading ||
commentUpvoteDeleteMutation.isLoading commentVotesUpsertMutation.isLoading ||
commentVotesDeleteMutation.isLoading
} }
type="button" type="button"
onClick={() => onVote(Vote.DOWNVOTE)}> onClick={() => onVote(Vote.DOWNVOTE)}>
<ArrowDownCircleIcon <ArrowDownCircleIcon
className={clsx( className={clsx(
'h-4 w-4', 'h-4 w-4',
comment.userVote?.value === Vote.DOWNVOTE commentVotesQuery.data?.userVote?.value === Vote.DOWNVOTE
? 'fill-red-500' ? 'fill-red-500'
: 'fill-gray-400', : 'fill-gray-400',
userId && 'hover:fill-red-500', userId && 'hover:fill-red-500',

@ -12,8 +12,9 @@ import { questionsAnswerRouter } from './questions-answer-router';
import { questionsQuestionCommentRouter } from './questions-question-comment-router'; import { questionsQuestionCommentRouter } from './questions-question-comment-router';
import { questionsQuestionRouter } from './questions-question-router'; import { questionsQuestionRouter } from './questions-question-router';
import { resumeCommentsRouter } from './resumes/resumes-comments-router'; import { resumeCommentsRouter } from './resumes/resumes-comments-router';
import { resumesCommentsUpvotesUserRouter } from './resumes/resumes-comments-upvotes-user-router';
import { resumesCommentsUserRouter } from './resumes/resumes-comments-user-router'; import { resumesCommentsUserRouter } from './resumes/resumes-comments-user-router';
import { resumesCommentsVotesRouter } from './resumes/resumes-comments-votes-router';
import { resumesCommentsVotesUserRouter } from './resumes/resumes-comments-votes-user-router';
import { resumesRouter } from './resumes/resumes-resume-router'; import { resumesRouter } from './resumes/resumes-resume-router';
import { resumesResumeUserRouter } from './resumes/resumes-resume-user-router'; import { resumesResumeUserRouter } from './resumes/resumes-resume-user-router';
import { resumesStarUserRouter } from './resumes/resumes-star-user-router'; import { resumesStarUserRouter } from './resumes/resumes-star-user-router';
@ -34,7 +35,8 @@ export const appRouter = createRouter()
.merge('resumes.resume.', resumesStarUserRouter) .merge('resumes.resume.', resumesStarUserRouter)
.merge('resumes.comments.', resumeCommentsRouter) .merge('resumes.comments.', resumeCommentsRouter)
.merge('resumes.comments.user.', resumesCommentsUserRouter) .merge('resumes.comments.user.', resumesCommentsUserRouter)
.merge('resumes.comments.upvotes.user.', resumesCommentsUpvotesUserRouter) .merge('resumes.comments.votes.', resumesCommentsVotesRouter)
.merge('resumes.comments.votes.user.', resumesCommentsVotesUserRouter)
.merge('questions.answers.comments.', questionsAnswerCommentRouter) .merge('questions.answers.comments.', questionsAnswerCommentRouter)
.merge('questions.answers.', questionsAnswerRouter) .merge('questions.answers.', questionsAnswerRouter)
.merge('questions.questions.comments.', questionsQuestionCommentRouter) .merge('questions.questions.comments.', questionsQuestionCommentRouter)

@ -1,6 +1,4 @@
import { z } from 'zod'; import { z } from 'zod';
import type { ResumesCommentVote } from '@prisma/client';
import { Vote } from '@prisma/client';
import { createRouter } from '../context'; import { createRouter } from '../context';
@ -11,7 +9,6 @@ export const resumeCommentsRouter = createRouter().query('list', {
resumeId: z.string(), resumeId: z.string(),
}), }),
async resolve({ ctx, input }) { async resolve({ ctx, input }) {
const userId = ctx.session?.user?.id;
const { resumeId } = input; const { resumeId } = input;
// For this resume, we retrieve every comment's information, along with: // For this resume, we retrieve every comment's information, along with:
@ -36,19 +33,10 @@ export const resumeCommentsRouter = createRouter().query('list', {
}); });
return comments.map((data) => { return comments.map((data) => {
let userVote: ResumesCommentVote | undefined = undefined;
let numVotes = 0;
data.votes.forEach((vote) => {
numVotes += vote.value === Vote.UPVOTE ? 1 : -1;
userVote = vote.userId === userId ? vote : undefined;
});
const comment: ResumeComment = { const comment: ResumeComment = {
createdAt: data.createdAt, createdAt: data.createdAt,
description: data.description, description: data.description,
id: data.id, id: data.id,
numVotes,
resumeId: data.resumeId, resumeId: data.resumeId,
section: data.section, section: data.section,
updatedAt: data.updatedAt, updatedAt: data.updatedAt,
@ -57,7 +45,6 @@ export const resumeCommentsRouter = createRouter().query('list', {
name: data.user.name, name: data.user.name,
userId: data.userId, userId: data.userId,
}, },
userVote,
}; };
return comment; return comment;

@ -0,0 +1,38 @@
import { z } from 'zod';
import type { ResumesCommentVote } from '@prisma/client';
import { Vote } from '@prisma/client';
import { createRouter } from '../context';
import type { ResumeCommentVote } from '~/types/resume-comments';
export const resumesCommentsVotesRouter = createRouter().query('list', {
input: z.object({
commentId: z.string(),
}),
async resolve({ ctx, input }) {
const userId = ctx.session?.user?.id;
const { commentId } = input;
const votes = await ctx.prisma.resumesCommentVote.findMany({
where: {
commentId,
},
});
let userVote: ResumesCommentVote | null = null;
let numVotes = 0;
votes.forEach((vote) => {
numVotes += vote.value === Vote.UPVOTE ? 1 : -1;
userVote = vote.userId === userId ? vote : null;
});
const resumeCommentVote: ResumeCommentVote = {
numVotes,
userVote,
};
return resumeCommentVote;
},
});

@ -3,16 +3,15 @@ import { Vote } from '@prisma/client';
import { createProtectedRouter } from '../context'; import { createProtectedRouter } from '../context';
export const resumesCommentsUpvotesUserRouter = createProtectedRouter() export const resumesCommentsVotesUserRouter = createProtectedRouter()
.mutation('upsert', { .mutation('upsert', {
input: z.object({ input: z.object({
commentId: z.string(), commentId: z.string(),
id: z.string().optional(),
value: z.nativeEnum(Vote), value: z.nativeEnum(Vote),
}), }),
async resolve({ ctx, input }) { async resolve({ ctx, input }) {
const userId = ctx.session.user.id; const userId = ctx.session.user.id;
const { id, commentId, value } = input; const { commentId, value } = input;
await ctx.prisma.resumesCommentVote.upsert({ await ctx.prisma.resumesCommentVote.upsert({
create: { create: {
@ -21,9 +20,6 @@ export const resumesCommentsUpvotesUserRouter = createProtectedRouter()
value, value,
}, },
update: { update: {
commentId,
id,
userId,
value, value,
}, },
where: { where: {

@ -8,7 +8,6 @@ export type ResumeComment = Readonly<{
createdAt: Date; createdAt: Date;
description: string; description: string;
id: string; id: string;
numVotes: number;
resumeId: string; resumeId: string;
section: ResumesSection; section: ResumesSection;
updatedAt: Date; updatedAt: Date;
@ -17,5 +16,9 @@ export type ResumeComment = Readonly<{
name: string?; name: string?;
userId: string; userId: string;
}; };
userVote: ResumesCommentVote | undefined; }>;
export type ResumeCommentVote = Readonly<{
numVotes: number;
userVote: ResumesCommentVote?;
}>; }>;

Loading…
Cancel
Save