parent
18a9f62b12
commit
452bd8e27e
@ -1,72 +1,26 @@
|
|||||||
import type { QuestionsQuestionType } from '@prisma/client';
|
import type { QuestionCardProps } from './QuestionCard';
|
||||||
import { Badge } from '@tih/ui';
|
import QuestionCard from './QuestionCard';
|
||||||
|
|
||||||
import QuestionTypeBadge from '../QuestionTypeBadge';
|
export type QuestionOverviewCardProps = Omit<
|
||||||
import type { VotingButtonsCallbackProps } from '../VotingButtons';
|
QuestionCardProps & {
|
||||||
import VotingButtons from '../VotingButtons';
|
showActionButton: false;
|
||||||
|
showUserStatistics: false;
|
||||||
|
showVoteButtons: true;
|
||||||
|
},
|
||||||
|
| 'actionButtonLabel'
|
||||||
|
| 'onActionButtonClick'
|
||||||
|
| 'showActionButton'
|
||||||
|
| 'showUserStatistics'
|
||||||
|
| 'showVoteButtons'
|
||||||
|
>;
|
||||||
|
|
||||||
type UpvoteProps =
|
export default function FullQuestionCard(props: QuestionOverviewCardProps) {
|
||||||
| {
|
|
||||||
showVoteButtons: true;
|
|
||||||
upvoteCount: number;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
showVoteButtons?: false;
|
|
||||||
upvoteCount?: never;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type FullQuestionCardProps = UpvoteProps &
|
|
||||||
VotingButtonsCallbackProps & {
|
|
||||||
company: string;
|
|
||||||
content: string;
|
|
||||||
location: string;
|
|
||||||
receivedCount: number;
|
|
||||||
role: string;
|
|
||||||
timestamp: string;
|
|
||||||
type: QuestionsQuestionType;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function FullQuestionCard({
|
|
||||||
voteState,
|
|
||||||
onDownvote,
|
|
||||||
onUpvote,
|
|
||||||
company,
|
|
||||||
content,
|
|
||||||
showVoteButtons,
|
|
||||||
upvoteCount,
|
|
||||||
timestamp,
|
|
||||||
role,
|
|
||||||
location,
|
|
||||||
type,
|
|
||||||
}: FullQuestionCardProps) {
|
|
||||||
const altText = `${company} logo`;
|
|
||||||
return (
|
return (
|
||||||
<article className="flex gap-4 rounded-md border border-slate-300 bg-white p-4">
|
<QuestionCard
|
||||||
{showVoteButtons && (
|
{...props}
|
||||||
<VotingButtons
|
showActionButton={false}
|
||||||
upvoteCount={upvoteCount}
|
showUserStatistics={false}
|
||||||
voteState={voteState}
|
showVoteButtons={true}
|
||||||
onDownvote={onDownvote}
|
/>
|
||||||
onUpvote={onUpvote}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<div className="flex flex-col gap-2">
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<img alt={altText} src="https://logo.clearbit.com/google.com"></img>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-baseline justify-between">
|
|
||||||
<div className="flex items-center gap-2 text-slate-500">
|
|
||||||
<Badge label={company} variant="primary" />
|
|
||||||
<QuestionTypeBadge type={type} />
|
|
||||||
<p className="text-xs">
|
|
||||||
{timestamp} · {location} · {role}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="mx-2 mb-2">
|
|
||||||
<p>{content}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,165 @@
|
|||||||
import { useState } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
import type { Vote } from '@prisma/client';
|
||||||
|
|
||||||
export const enum VoteState {
|
import { trpc } from '../trpc';
|
||||||
NO_VOTE,
|
|
||||||
UPVOTE,
|
|
||||||
DOWNVOTE,
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useVote = () => {
|
type UseVoteOptions = {
|
||||||
const [voteState, setVoteState] = useState(VoteState.NO_VOTE);
|
createVote: (opts: { vote: Vote }) => void;
|
||||||
|
deleteVote: (opts: { id: string }) => void;
|
||||||
|
updateVote: (opts: BackendVote) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
type BackendVote = {
|
||||||
|
id: string;
|
||||||
|
vote: Vote;
|
||||||
|
};
|
||||||
|
|
||||||
|
const createVoteCallbacks = (
|
||||||
|
vote: BackendVote | null,
|
||||||
|
opts: UseVoteOptions,
|
||||||
|
) => {
|
||||||
|
const { createVote, updateVote, deleteVote } = opts;
|
||||||
|
|
||||||
const handleUpvote = (id: string, voteState) => {
|
const handleUpvote = () => {
|
||||||
//
|
// Either upvote or remove upvote
|
||||||
|
if (vote) {
|
||||||
|
if (vote.vote === 'DOWNVOTE') {
|
||||||
|
updateVote({
|
||||||
|
id: vote.id,
|
||||||
|
vote: 'UPVOTE',
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
deleteVote({
|
||||||
|
id: vote.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Update vote to an upvote
|
||||||
|
} else {
|
||||||
|
createVote({
|
||||||
|
vote: 'UPVOTE',
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDownvote = (id: string) => {};
|
const handleDownvote = () => {
|
||||||
|
// Either downvote or remove downvote
|
||||||
|
if (vote) {
|
||||||
|
if (vote.vote === 'UPVOTE') {
|
||||||
|
updateVote({
|
||||||
|
id: vote.id,
|
||||||
|
vote: 'DOWNVOTE',
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
deleteVote({
|
||||||
|
id: vote.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Update vote to an upvote
|
||||||
|
} else {
|
||||||
|
createVote({
|
||||||
|
vote: 'DOWNVOTE',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return { handleDownvote, handleUpvote };
|
||||||
|
};
|
||||||
|
|
||||||
|
type MutationKey = Parameters<typeof trpc.useMutation>[0];
|
||||||
|
type QueryKey = Parameters<typeof trpc.useQuery>[0][0];
|
||||||
|
|
||||||
|
export const useQuestionVote = (id: string) => {
|
||||||
|
return useVote(id, {
|
||||||
|
create: 'questions.questions.createVote',
|
||||||
|
deleteKey: 'questions.questions.deleteVote',
|
||||||
|
idKey: 'questionId',
|
||||||
|
query: 'questions.questions.getVote',
|
||||||
|
update: 'questions.questions.updateVote',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useAnswerVote = (id: string) => {
|
||||||
|
return useVote(id, {
|
||||||
|
create: 'questions.answers.createVote',
|
||||||
|
deleteKey: 'questions.answers.deleteVote',
|
||||||
|
idKey: 'answerId',
|
||||||
|
query: 'questions.answers.getVote',
|
||||||
|
update: 'questions.answers.updateVote',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useQuestionCommentVote = (id: string) => {
|
||||||
|
return useVote(id, {
|
||||||
|
create: 'questions.questions.comments.createVote',
|
||||||
|
deleteKey: 'questions.questions.comments.deleteVote',
|
||||||
|
idKey: 'questionCommentId',
|
||||||
|
query: 'questions.questions.comments.getVote',
|
||||||
|
update: 'questions.questions.comments.updateVote',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useAnswerCommentVote = (id: string) => {
|
||||||
|
return useVote(id, {
|
||||||
|
create: 'questions.answers.comments.createVote',
|
||||||
|
deleteKey: 'questions.answers.comments.deleteVote',
|
||||||
|
idKey: 'answerCommentId',
|
||||||
|
query: 'questions.answers.comments.getVote',
|
||||||
|
update: 'questions.answers.comments.updateVote',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
type VoteProps<VoteQueryKey extends QueryKey = QueryKey> = {
|
||||||
|
create: MutationKey;
|
||||||
|
deleteKey: MutationKey;
|
||||||
|
idKey: string;
|
||||||
|
query: VoteQueryKey;
|
||||||
|
update: MutationKey;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useVote = <VoteQueryKey extends QueryKey = QueryKey>(
|
||||||
|
id: string,
|
||||||
|
opts: VoteProps<VoteQueryKey>,
|
||||||
|
) => {
|
||||||
|
const { create, deleteKey, query, update, idKey } = opts;
|
||||||
|
const utils = trpc.useContext();
|
||||||
|
|
||||||
|
const onVoteUpdate = useCallback(() => {
|
||||||
|
utils.invalidateQueries([query, { [idKey]: id } as any]);
|
||||||
|
}, [id, idKey, utils, query]);
|
||||||
|
|
||||||
|
const { data } = trpc.useQuery([
|
||||||
|
query,
|
||||||
|
{
|
||||||
|
[idKey]: id,
|
||||||
|
},
|
||||||
|
] as any);
|
||||||
|
|
||||||
|
const backendVote = data as BackendVote;
|
||||||
|
|
||||||
|
const { mutate: createVote } = trpc.useMutation(create, {
|
||||||
|
onSuccess: onVoteUpdate,
|
||||||
|
});
|
||||||
|
const { mutate: updateVote } = trpc.useMutation(update, {
|
||||||
|
onSuccess: onVoteUpdate,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { mutate: deleteVote } = trpc.useMutation(deleteKey, {
|
||||||
|
onSuccess: onVoteUpdate,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { handleDownvote, handleUpvote } = createVoteCallbacks(
|
||||||
|
backendVote ?? null,
|
||||||
|
{
|
||||||
|
createVote: ({ vote }) => {
|
||||||
|
createVote({
|
||||||
|
[idKey]: id,
|
||||||
|
vote,
|
||||||
|
} as any);
|
||||||
|
},
|
||||||
|
deleteVote,
|
||||||
|
updateVote,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
return [handleUpvote, handleDownvote, voteState] as const;
|
return { handleDownvote, handleUpvote, vote: backendVote ?? null };
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in new issue