From eced696c0432e61b2fc68251695c16e7ce15ed69 Mon Sep 17 00:00:00 2001 From: mybearworld <130385691+mybearworld@users.noreply.github.com> Date: Sun, 28 Jul 2024 19:27:24 +0200 Subject: [PATCH] Reporting posts --- src/components/Post.tsx | 70 +++++++++++++++++++++++++++++++++++++-- src/components/Select.tsx | 40 ++++++++++++++++++++++ src/lib/api/posts.ts | 20 +++++++++++ src/lib/reportReasons.ts | 13 ++++++++ 4 files changed, 141 insertions(+), 2 deletions(-) create mode 100644 src/components/Select.tsx create mode 100644 src/lib/reportReasons.ts diff --git a/src/components/Post.tsx b/src/components/Post.tsx index 9abb318..a3b5f93 100644 --- a/src/components/Post.tsx +++ b/src/components/Post.tsx @@ -1,6 +1,6 @@ import { File, Menu as MenuIcon, SmilePlus, Reply, X } from "lucide-react"; import * as Dialog from "@radix-ui/react-dialog"; -import { ReactNode, useRef, useState, memo } from "react"; +import { ReactNode, useRef, useState, memo, FormEventHandler } from "react"; import { useShallow } from "zustand/react/shallow"; import { useAPI } from "../lib/api"; import { getReply, PostWithReplies } from "../lib/reply"; @@ -11,9 +11,11 @@ import { byteToHuman } from "../lib/byteToHuman"; import { Button } from "./Button"; import { Popup } from "./Popup"; import { User } from "./User"; +import { Input } from "./Input"; import { Menu, MenuItem } from "./Menu"; import { Markdown } from "./Markdown"; import { Mention } from "./Mention"; +import { Select, Option } from "./Select"; import { MarkdownInput } from "./MarkdownInput"; import { ProfilePicture, ProfilePictureBase } from "./ProfilePicture"; import { ReactionUsers } from "./ReactionUsers"; @@ -22,6 +24,7 @@ import { twMerge } from "tailwind-merge"; import { EmojiPicker } from "./EmojiPicker"; import { DiscordEmoji } from "../lib/discordEmoji"; import { IconButton } from "./IconButton"; +import { REPORT_REASONS } from "../lib/reportReasons"; export type PostProps = { id: string; @@ -112,6 +115,7 @@ const PostBase = memo((props: PostBaseProps) => { const [viewState, setViewState] = useState<"view" | "edit" | "source">( "view", ); + const [reportOpen, setReportOpen] = useState(false); const [credentials, editPost, deletePost, reactToPost] = useAPI( useShallow((state) => [ state.credentials, @@ -258,7 +262,21 @@ const PostBase = memo((props: PostBaseProps) => { } > - Report + {credentials ? + Report} + triggerAsChild + controlled={{ + open: reportOpen, + onOpenChange: setReportOpen, + }} + > + setReportOpen(false)} + /> + + : undefined} {credentials.username !== props.post.u ? @@ -445,6 +463,54 @@ const SpeechBubble = (props: SpeechBubbleProps) => { ); }; +type ReportModalProps = { + post: string; + onSuccess: () => void; +}; +const ReportModal = (props: ReportModalProps) => { + const reportPost = useAPI((state) => state.reportPost); + const [reason, setReason] = useState(); + const [comment, setComment] = useState(""); + const [error, setError] = useState(); + + const handleReport: FormEventHandler = async (e) => { + e.preventDefault(); + const response = await reportPost(props.post, reason, comment); + if (response.error) { + setError(response.message); + return; + } + props.onSuccess(); + }; + + return ( +
+ + Report this post + + + setComment(e.currentTarget.value)} + /> + + {error ? +
{error}
+ : undefined} +
+ ); +}; + type AttachmentsProps = { attachments: Attachment[]; }; diff --git a/src/components/Select.tsx b/src/components/Select.tsx new file mode 100644 index 0000000..d1edb0c --- /dev/null +++ b/src/components/Select.tsx @@ -0,0 +1,40 @@ +import { forwardRef, ComponentPropsWithoutRef } from "react"; +import { twMerge } from "tailwind-merge"; + +export type SelectProps = ComponentPropsWithoutRef<"select"> & { + label: string; +}; +export const Select = forwardRef( + (props: SelectProps, ref) => { + const selectProps: Omit & { label?: string } = { + ...props, + }; + delete selectProps.label; + return ( +