import { Transition } from "@headlessui/react";
import { Comment } from "@prisma/client";
import axios from "axios";
import { formatRelative } from "date-fns";
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 { useList } from "react-use";
import {
createComment,
CreateCommentPayload,
} from "../../api-client/create-comment";
import { requiredString } from "../../utils/form-validation";
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 UserAvater from "../poll/user-avatar";
import { useUserName } from "../user-name-context";
export interface DiscussionProps {
pollId: string;
canDelete?: boolean;
}
interface CommentForm {
authorName: string;
content: string;
}
const Comments: React.VoidFunctionComponent<{
comments: {
id: string;
authorName: string;
content: string;
createdAt: string;
}[];
deletedComments: string[];
onDelete: (commentId: string) => void;
canDelete?: boolean;
}> = ({ comments, deletedComments, onDelete, canDelete }) => {
return (
{comments.map((comment, i) => {
return (
{comment.authorName}
•
{formatRelative(new Date(comment.createdAt), Date.now())}
{canDelete ? (
}
>
{
onDelete(comment.id);
}}
/>
) : null}
{comment.content}
);
})}
);
};
const Discussion: React.VoidFunctionComponent = ({
pollId,
canDelete,
}) => {
const getCommentsQueryKey = ["poll", pollId, "comments"];
const [userName, setUserName] = useUserName();
const [deletedComments, { push }] = useList([]);
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) => {
queryClient.setQueryData(getCommentsQueryKey, (comments) => {
if (Array.isArray(comments)) {
return [...comments, newComment];
}
return [newComment];
});
plausible("Created comment");
},
},
);
const { mutate: deleteCommentMutation } = useMutation(
async (payload: { pollId: string; commentId: string }) => {
await axios.delete(`/api/poll/${pollId}/comments/${payload.commentId}`);
},
{
onMutate: ({ commentId }) => {
push(commentId);
plausible("Deleted comment");
},
onSuccess: () => {
queryClient.invalidateQueries(getCommentsQueryKey);
},
},
);
const { register, setValue, control, handleSubmit, formState } =
useForm({
defaultValues: {
authorName: userName,
content: "",
},
});
React.useEffect(() => {
setValue("authorName", userName);
}, [setValue, userName]);
const handleDelete = React.useCallback(
(commentId: string) => {
deleteCommentMutation({ pollId, commentId });
},
[deleteCommentMutation, pollId],
);
if (!comments) {
return null;
}
return (
{comments.length ? (
) : null}
);
};
export default React.memo(Discussion);