Skip to content
This repository has been archived by the owner on Dec 1, 2024. It is now read-only.

Commit

Permalink
Reporting posts
Browse files Browse the repository at this point in the history
  • Loading branch information
mybearworld committed Jul 28, 2024
1 parent ac57e06 commit eced696
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 2 deletions.
70 changes: 68 additions & 2 deletions src/components/Post.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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";
Expand All @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -258,7 +262,21 @@ const PostBase = memo((props: PostBaseProps) => {
</IconButton>
}
>
<MenuItem disabled>Report</MenuItem>
{credentials ?
<Popup
trigger={<MenuItem dontClose>Report</MenuItem>}
triggerAsChild
controlled={{
open: reportOpen,
onOpenChange: setReportOpen,
}}
>
<ReportModal
post={props.post.post_id}
onSuccess={() => setReportOpen(false)}
/>
</Popup>
: undefined}
{credentials.username !== props.post.u ?
<MenuItem
onClick={() =>
Expand Down Expand Up @@ -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<string>();
const [comment, setComment] = useState("");
const [error, setError] = useState<string>();

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 (
<form className="flex flex-col gap-2" onSubmit={handleReport}>
<Dialog.Title className="text-lg font-bold">
Report this post
</Dialog.Title>
<Select label="Reason" onInput={(e) => setReason(e.currentTarget.value)}>
<Option selected disabled>
Choose a reason...
</Option>
{REPORT_REASONS.map((reason) => (
<Option value={reason} key={reason}>
{reason}
</Option>
))}
</Select>
<Input
label="Comment"
value={comment}
onInput={(e) => setComment(e.currentTarget.value)}
/>
<Button type="submit">Report</Button>
{error ?
<div className="text-red-500">{error}</div>
: undefined}
</form>
);
};

type AttachmentsProps = {
attachments: Attachment[];
};
Expand Down
40 changes: 40 additions & 0 deletions src/components/Select.tsx
Original file line number Diff line number Diff line change
@@ -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<HTMLSelectElement, SelectProps>(
(props: SelectProps, ref) => {
const selectProps: Omit<SelectProps, "label"> & { label?: string } = {
...props,
};
delete selectProps.label;
return (
<label className="flex flex-col">
<span className="text-sm font-bold">{props.label}</span>
<select
{...selectProps}
ref={ref}
className={twMerge(
"rounded-xl border border-gray-200 bg-transparent px-2 py-1 transition-colors disabled:opacity-70 dark:border-gray-700",
props.className,
)}
/>
</label>
);
},
);

export type OptionProps = ComponentPropsWithoutRef<"option">;
export const Option = forwardRef<HTMLOptionElement, OptionProps>(
(props: OptionProps, ref) => {
return (
<option
{...props}
className={twMerge("bg-white dark:bg-gray-900", props.className)}
ref={ref}
/>
);
},
);
20 changes: 20 additions & 0 deletions src/lib/api/posts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,11 @@ export type PostsSlice = {
emoji: string,
type: "add" | "delete",
) => Promise<Errorable>;
reportPost: (
id: string,
reason: string | undefined,
comment: string,
) => Promise<Errorable>;
loadMoreReactionUsers: (id: string, emoji: string) => Promise<Errorable>;
loadReactionUsersByAmount: (
id: string,
Expand Down Expand Up @@ -504,6 +509,21 @@ export const createPostsSlice: Slice<PostsSlice> = (set, get) => {
);
return response;
},
reportPost: async (id, comment, reason) => {
const state = get();
const response = await request(
fetch(`https://api.meower.org/posts/${id}/report`, {
headers: {
...(state.credentials ? { Token: state.credentials.token } : {}),
"Content-Type": "application/json",
},
method: "POST",
body: JSON.stringify({ comment, reason }),
}),
z.object({}),
);
return response;
},
loadReactionUsers: async (id: string, emoji: string) => {
const state = get();
if (state.reactionUsers[`${id}/${emoji}`]) {
Expand Down
13 changes: 13 additions & 0 deletions src/lib/reportReasons.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// See https://github.com/meower-media-co/Meower-Svelte/blob/29b7bbd68fa209e2ec439ee45a7581a036a88451/src/lib/modals/safety/ReportPost.svelte#L68-L78, licensed under MIT by Meower Media

export const REPORT_REASONS = [
"Spam",
"Harassment or abuse towards others",
"Rude, vulgar or offensive language",
"NSFW (sexual, alcohol, violence, gore, etc.)",
"Scams, hacks, phishing or other malicious content",
"Threatening violence or real world harm",
"Illegal activity",
"Self-harm/suicide",
"Other",
];

0 comments on commit eced696

Please sign in to comment.