import { Comment } from "@prisma/client"; import axios from "axios"; import clsx from "clsx"; import { formatRelative } from "date-fns"; import { AnimatePresence, motion } from "framer-motion"; import { usePlausible } from "next-plausible"; import * as React from "react"; import { Controller, useForm } from "react-hook-form"; import { useMutation, useQuery, useQueryClient } from "react-query"; import { createComment, CreateCommentPayload, } from "../../api-client/create-comment"; import { requiredString } from "../../utils/form-validation"; import Badge from "../badge"; import Button from "../button"; import CompactButton from "../compact-button"; import Dropdown, { DropdownItem } from "../dropdown"; import DotsHorizontal from "../icons/dots-horizontal.svg"; import Trash from "../icons/trash.svg"; import NameInput from "../name-input"; import TruncatedLinkify from "../poll/truncated-linkify"; import UserAvatar from "../poll/user-avatar"; import { usePoll } from "../poll-context"; import { usePreferences } from "../preferences/use-preferences"; import { useSession } from "../session"; export interface DiscussionProps { pollId: string; } interface CommentForm { authorName: string; content: string; } const Discussion: React.VoidFunctionComponent = ({ pollId, }) => { const { locale } = usePreferences(); const getCommentsQueryKey = ["poll", pollId, "comments"]; const queryClient = useQueryClient(); const { data: comments } = useQuery( getCommentsQueryKey, async () => { const res = await axios.get<{ comments: Array & { createdAt: string }>; }>(`/api/poll/${pollId}/comments`); return res.data.comments; }, { refetchInterval: 10000, // refetch every 10 seconds }, ); const plausible = usePlausible(); const { mutate: createCommentMutation } = useMutation( (payload: CreateCommentPayload) => { // post comment return createComment(payload); }, { onSuccess: (newComment) => { session.refresh(); queryClient.setQueryData(getCommentsQueryKey, (comments) => { if (Array.isArray(comments)) { return [...comments, newComment]; } return [newComment]; }); plausible("Created comment"); }, }, ); const { poll } = usePoll(); const { mutate: deleteCommentMutation } = useMutation( async (payload: { pollId: string; commentId: string }) => { await axios.delete(`/api/poll/${pollId}/comments/${payload.commentId}`); }, { onMutate: () => { plausible("Deleted comment"); }, onSuccess: () => { queryClient.invalidateQueries(getCommentsQueryKey); }, }, ); const session = useSession(); const { register, setValue, control, handleSubmit, formState } = useForm({ defaultValues: { authorName: "", content: "", }, }); const handleDelete = React.useCallback( (commentId: string) => { deleteCommentMutation({ pollId, commentId }); }, [deleteCommentMutation, pollId], ); if (!comments) { return null; } return (
Comments
0, })} > {comments.map((comment) => { const canDelete = poll.role === "admin" || session.ownsObject(comment); return (
{formatRelative( new Date(comment.createdAt), Date.now(), { locale, }, )}
{canDelete ? ( } > { handleDelete(comment.id); }} /> ) : null}
{comment.content}
); })}
{ return new Promise((resolve, reject) => { createCommentMutation( { ...data, pollId, }, { onSuccess: () => { setValue("content", ""); resolve(data); }, onError: reject, }, ); }); })} >