diff --git a/external/lang b/external/lang
index 5bf84e27..79d9b042 160000
--- a/external/lang
+++ b/external/lang
@@ -1 +1 @@
-Subproject commit 5bf84e27074daeab3055f299e54be955aae5042d
+Subproject commit 79d9b042866d466f39e3f4d053833416137491f8
diff --git a/src/controllers/modals/ModalController.tsx b/src/controllers/modals/ModalController.tsx
index cbcf9be0..cd97cf49 100644
--- a/src/controllers/modals/ModalController.tsx
+++ b/src/controllers/modals/ModalController.tsx
@@ -41,6 +41,7 @@ import MFARecovery from "./components/MFARecovery";
import ModifyAccount from "./components/ModifyAccount";
import OutOfDate from "./components/OutOfDate";
import PendingFriendRequests from "./components/PendingFriendRequests";
+import ReportContent from "./components/Report";
import ServerIdentity from "./components/ServerIdentity";
import ServerInfo from "./components/ServerInfo";
import ShowToken from "./components/ShowToken";
@@ -276,4 +277,5 @@ export const modalController = new ModalControllerExtended({
sign_out_sessions: SignOutSessions,
user_picker: UserPicker,
user_profile: UserProfile,
+ report: ReportContent,
});
diff --git a/src/controllers/modals/components/Report.tsx b/src/controllers/modals/components/Report.tsx
new file mode 100644
index 00000000..8474855c
--- /dev/null
+++ b/src/controllers/modals/components/Report.tsx
@@ -0,0 +1,125 @@
+import { API, Message as MessageInterface, User } from "revolt.js";
+
+import { Text } from "preact-i18n";
+
+import { ModalForm, Row } from "@revoltchat/ui";
+
+import Message from "../../../components/common/messaging/Message";
+import UserShort from "../../../components/common/user/UserShort";
+import { useClient } from "../../client/ClientController";
+import { ModalProps } from "../types";
+
+const CONTENT_REASONS: API.ContentReportReason[] = [
+ "NoneSpecified",
+ "Illegal",
+ "PromotesHarm",
+ "SpamAbuse",
+ "Malware",
+ "Harassment",
+];
+
+const USER_REASONS: API.UserReportReason[] = [
+ "NoneSpecified",
+ "SpamAbuse",
+ "InappropriateProfile",
+ "Impersonation",
+ "BanEvasion",
+ "Underage",
+];
+
+/**
+ * Report creation modal
+ */
+export default function ReportContent({
+ target,
+ ...props
+}: ModalProps<"report">) {
+ const client = useClient();
+
+ return (
+
+ ) : (
+
+ )
+ }
+ schema={{
+ selected: "custom",
+ reason: "combo",
+ additional_context: "text",
+ }}
+ data={{
+ selected: {
+ element:
+ target instanceof MessageInterface ? (
+
+ ) : target instanceof User ? (
+
+
+
+ ) : (
+ <>>
+ ),
+ },
+ reason: {
+ field: (
+
+ ) as React.ReactChild,
+ options: (target instanceof User
+ ? USER_REASONS
+ : CONTENT_REASONS
+ ).map((value) => ({
+ name: (
+
+ ),
+ value,
+ })),
+ },
+ additional_context: {
+ field: (
+
+ ) as React.ReactChild,
+ },
+ }}
+ callback={async ({ reason, additional_context }) => {
+ await client.api.post("/safety/report", {
+ content: {
+ id: target._id,
+ type:
+ target instanceof MessageInterface
+ ? "Message"
+ : target instanceof User
+ ? "User"
+ : "Server",
+ report_reason: reason as any,
+ },
+ additional_context,
+ });
+ }}
+ submit={{
+ children: ,
+ }}
+ />
+ );
+}
diff --git a/src/controllers/modals/components/ServerInfo.tsx b/src/controllers/modals/components/ServerInfo.tsx
index 786026ad..ea15df7c 100644
--- a/src/controllers/modals/components/ServerInfo.tsx
+++ b/src/controllers/modals/components/ServerInfo.tsx
@@ -28,16 +28,24 @@ export default function ServerInfo({
}
actions={[
{
- onClick: () =>
+ onClick: () => {
modalController.push({
type: "server_identity",
member: server.member!,
- }),
+ });
+ return true;
+ },
children: "Edit Identity",
palette: "primary",
},
{
- onClick: () => report(server),
+ onClick: () => {
+ modalController.push({
+ type: "report",
+ target: server,
+ });
+ return true;
+ },
children: ,
palette: "error",
},
diff --git a/src/controllers/modals/types.ts b/src/controllers/modals/types.ts
index 033776a6..e09736a2 100644
--- a/src/controllers/modals/types.ts
+++ b/src/controllers/modals/types.ts
@@ -179,6 +179,10 @@ export type Modal = {
| {
type: "import_theme";
}
+ | {
+ type: "report";
+ target: Server | User | Message;
+ }
);
export type ModalProps = Modal & { type: T } & {
diff --git a/src/lib/ContextMenus.tsx b/src/lib/ContextMenus.tsx
index 16dd239a..ab7a674e 100644
--- a/src/lib/ContextMenus.tsx
+++ b/src/lib/ContextMenus.tsx
@@ -111,7 +111,8 @@ type Action =
action: "set_notification_state";
key: string;
state?: NotificationState;
- };
+ }
+ | { action: "report"; target: User | Server | Message };
// ! FIXME: I dare someone to re-write this
// Tip: This should just be split into separate context menus per logical area.
@@ -449,6 +450,12 @@ export default function ContextMenus() {
case "open_server_settings":
history.push(`/server/${data.id}/settings`);
break;
+ case "report":
+ modalController.push({
+ type: "report",
+ target: data.target,
+ });
+ break;
}
})().catch((err) => {
modalController.push({
@@ -669,6 +676,19 @@ export default function ContextMenus() {
} as unknown as Action);
}
}
+
+ if (user._id !== userId) {
+ generateAction(
+ {
+ action: "report",
+ target: user,
+ },
+ "report_user",
+ undefined,
+ undefined,
+ "var(--error)",
+ );
+ }
}
if (contextualChannel) {
@@ -795,14 +815,33 @@ export default function ContextMenus() {
});
}
+ if (message.author_id !== userId) {
+ generateAction(
+ {
+ action: "report",
+ target: message,
+ },
+ "report_message",
+ undefined,
+ undefined,
+ "var(--error)",
+ );
+ }
+
if (
message.author_id === userId ||
channelPermissions & Permission.ManageMessages
) {
- generateAction({
- action: "delete_message",
- target: message,
- });
+ generateAction(
+ {
+ action: "delete_message",
+ target: message,
+ },
+ undefined,
+ undefined,
+ undefined,
+ "var(--error)",
+ );
}
if (
@@ -1035,6 +1074,17 @@ export default function ContextMenus() {
"var(--error)",
);
} else {
+ generateAction(
+ {
+ action: "report",
+ target: server,
+ },
+ "report_server",
+ undefined,
+ undefined,
+ "var(--error)",
+ );
+
generateAction(
{ action: "leave_server", target: server },
"leave_server",