Skip to content

Commit 9ccfae7

Browse files
committed
WIP Instance reporting
1 parent f4729d5 commit 9ccfae7

11 files changed

Lines changed: 360 additions & 1 deletion

File tree

frontend/selector/src/components/modalConfig.jsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import BuildConfirmation from "./views/selector/modals/buildConfirmation";
55
import EnvironmentWarning from "./views/selector/modals/environmentWarning";
66
import LegalAgreement from "./views/selector/modals/legalAgreement";
77
import OpfsComingSoon from "./views/selector/modals/opfsComingSoon";
8+
import SubmitReport from "./views/selector/modals/submitReport";
89

910
export default {
1011
changelog: {
@@ -28,4 +29,7 @@ export default {
2829
opfsComingSoon: {
2930
Component: OpfsComingSoon,
3031
},
32+
submitReport: {
33+
Component: SubmitReport
34+
},
3135
};

frontend/selector/src/components/views/selector/main.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ export default function () {
195195
</div>
196196

197197
<Text variant="body" style={{ marginTop: "-10px" }}>
198-
Looking for patches? They've moved to the settings menu!
198+
Looking for patches or a way to report content? You can now find both options conveniently located in the Settings menu.
199199
</Text>
200200

201201
<div className="important-information">
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import Modal from "@oldcord/frontend-shared/components/modal";
2+
import Button from "@oldcord/frontend-shared/components/button";
3+
import { Text } from "@oldcord/frontend-shared/components/textComponent";
4+
5+
export default function ({ onClose, onConfirm }) {
6+
return (
7+
<Modal
8+
onClose={onClose}
9+
title="Report Content"
10+
showCloseButton={false}
11+
size="small"
12+
footerAlignment="right"
13+
footer={
14+
<>
15+
<Button variant="ghost" onClick={() => onClose()}>Cancel</Button>
16+
<Button onClick={() => onConfirm()} variant="danger" style={{
17+
padding: '20px',
18+
width: 'auto'
19+
}}>Submit Report</Button>
20+
</>
21+
}
22+
>
23+
<div style={{ paddingBottom: "20px" }}>
24+
<Text variant="body">By submitting, you <b>confirm that the information is accurate to the best of your ability.</b></Text>
25+
</div>
26+
</Modal>
27+
);
28+
}

frontend/selector/src/components/views/settings/main.jsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import Themes from "./pages/themes";
88
import DownloadQueue from "./pages/downloadQueue";
99
import OpfsSettings from "./pages/opfsSettings";
1010
import AdvancedSettings from "./pages/advancedSettings";
11+
import ReportContent from "./pages/reportContent";
1112

1213
export default function () {
1314
const { activeView } = useSettings();
@@ -28,6 +29,8 @@ export default function () {
2829
return <OpfsSettings />;
2930
case SETTINGS_VIEWS.ADVANCED_SETTINGS:
3031
return <AdvancedSettings />;
32+
case SETTINGS_VIEWS.REPORT_CONTENT:
33+
return <ReportContent />
3134
}
3235
}
3336

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import { Text } from "@oldcord/frontend-shared/components/textComponent";
2+
import Button from "@oldcord/frontend-shared/components/button";
3+
import ToggleSetting from "@oldcord/frontend-shared/components/toggleSettings";
4+
import { useModal } from "@oldcord/frontend-shared/hooks/modalHandler";
5+
import cookieManager from "../../../../lib/cookieManager";
6+
import { useState, useEffect } from "react";
7+
import PageInfo from "@oldcord/frontend-shared/components/pageInfo";
8+
import DropdownList from "@oldcord/frontend-shared/components/dropdownList";
9+
import InputField from "@oldcord/frontend-shared/components/inputfield";
10+
11+
export default function () {
12+
const { addModal, removeModal } = useModal();
13+
const [selectedReportProblem, setSelectedReportProblem] = useState("Direct Threats of Violence/Harm");
14+
const [emailAddress, setEmailAddress] = useState("");
15+
const [reportSubject, setReportSubject] = useState("");
16+
const [reportDescription, setReportDescription] = useState("");
17+
const problems = [
18+
"Child Sexual Abuse Material (CSAM)",
19+
"Threat of Self-Harm or Suicide",
20+
"Terrorism or Violent Extremism",
21+
"Direct Threats of Violence/Harm",
22+
"Targeted Harassment or Bullying",
23+
"Hate Speech or Discrimination",
24+
"Non-Consensual Intimate Imagery (NCII)",
25+
"Spam, Scams, or Malware",
26+
"Copyright or Trademark Infringement",
27+
"Pornography or Sexually Explicit Content (where prohibited)",
28+
"Impersonation or Identity Theft",
29+
"Revealing Private Information (Doxxing)",
30+
"Other"
31+
];
32+
33+
const handleSubmitReport = async () => {
34+
await fetch(`${location.protocol}//${location.host}/api/reports`, {
35+
headers: {
36+
'Content-Type' : 'application/json',
37+
},
38+
method: "POST",
39+
body: JSON.stringify({
40+
subject: reportSubject,
41+
description: reportDescription,
42+
email_address: emailAddress === "" ? null : emailAddress,
43+
problem: selectedReportProblem
44+
})
45+
});
46+
}
47+
48+
return (
49+
<>
50+
<Text variant="h1">Report Content</Text>
51+
<PageInfo title="Report Content">
52+
<>
53+
Use the form below to report any content that violates this instance's rules.
54+
<br />
55+
Please be aware that the response time depends on the nature of the report; those concerning <b>illegal content</b> (e.g, CSAM, terrorism) will be taken more seriously and prioritized.
56+
<br />
57+
Allow up to <b>one week</b> for the instance administrators to acknowledge your report. If there is no response, please attempt to contact them directly.
58+
<br />
59+
If illegal content is still present after this follow up, you may need to report it through <b>legal channels</b> to ensure the safety of all users.
60+
</>
61+
</PageInfo>
62+
<DropdownList
63+
label={"What's the problem?"}
64+
options={problems}
65+
defaultOption={selectedReportProblem}
66+
onSelected={setSelectedReportProblem}
67+
style={{ marginTop: '-20px' }}
68+
informativeText="Select the reason for your report."
69+
/>
70+
<InputField
71+
label="Your Email Address"
72+
id="email-address"
73+
placeholder=""
74+
required={false}
75+
type="email"
76+
style={{ marginBottom: '20px' }}
77+
value={emailAddress}
78+
onChange={(e) => setEmailAddress(e.target.value)}
79+
/>
80+
<InputField
81+
label="Subject"
82+
id="report-subject"
83+
placeholder=""
84+
required={true}
85+
type="text"
86+
style={{ marginBottom: '20px' }}
87+
value={reportSubject}
88+
onChange={(e) => setReportSubject(e.target.value)}
89+
/>
90+
<InputField
91+
label="Description"
92+
id="report-description"
93+
placeholder=""
94+
required={true}
95+
type="textarea"
96+
style={{ marginBottom: '20px' }}
97+
value={reportDescription}
98+
onChange={(e) => setReportDescription(e.target.value)}
99+
/>
100+
<span style={{
101+
marginTop: '0px',
102+
marginBottom: '20px',
103+
color: '#868686',
104+
fontSize: '13px'
105+
}}><b>Please include all relevant IDs or message links in the Description.</b> Without this specific information, instance administrators cannot take action on your report.</span>
106+
<div className="divider" />
107+
<Button
108+
style={{ width: "100%" }}
109+
onClick={() => {
110+
addModal("submitReport", {
111+
onClose: () => {
112+
removeModal();
113+
},
114+
onConfirm: async () => {
115+
await handleSubmitReport();
116+
removeModal();
117+
},
118+
});
119+
}}
120+
variant="danger"
121+
>
122+
Submit Report
123+
</Button>
124+
</>
125+
);
126+
}

frontend/selector/src/components/views/settings/settingsNavigationList.jsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export const SETTINGS_VIEWS = {
1111
DOWNLOAD_QUEUE: "download_queue",
1212
OPFS_SETTINGS: "opfs_settings",
1313
CHANGELOG: "changelog",
14+
REPORT_CONTENT: "report_content",
1415
ADVANCED_SETTINGS: "advanced_settings",
1516
};
1617

@@ -65,6 +66,13 @@ export default function () {
6566
label: "Advanced Settings",
6667
view: SETTINGS_VIEWS.ADVANCED_SETTINGS,
6768
},
69+
{ type: "separator" },
70+
{ type: "header", label: "Instance Specific" },
71+
{
72+
type: "item",
73+
label: "Report Content",
74+
view: SETTINGS_VIEWS.REPORT_CONTENT
75+
},
6876
];
6977

7078
function handleItemClick(view) {
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
.input-container {
2+
position: relative;
3+
width: 100%;
4+
}
5+
6+
.input-label {
7+
font-size: 13px;
8+
line-height: 16px;
9+
font-weight: 600;
10+
text-transform: uppercase;
11+
margin-bottom: 8px;
12+
color: #b9bbbe;
13+
transform: scaleY(
14+
1.04
15+
); /* for some reason Nebula Sans' semi-bold and above's height is smaller than the rest */
16+
}
17+
18+
.input-field {
19+
display: flex;
20+
justify-content: space-between;
21+
align-items: center;
22+
width: 100%;
23+
height: 40px;
24+
padding: 0 10px;
25+
margin-top: 10px;
26+
background-color: rgba(0, 0, 0, 0.1);
27+
border: 1px solid rgba(0, 0, 0, 0.3);
28+
border-radius: 3px;
29+
color: #f6f6f7;
30+
font-size: 16px;
31+
text-align: left;
32+
transition: border-color 0.15s ease;
33+
}
34+
35+
.required-text {
36+
color: #ff4444;
37+
text-align: left;
38+
display: inline;
39+
margin-left: 4px;
40+
}
41+
42+
.input-field:hover {
43+
border-color: #040405;
44+
}
45+
46+
.input-field::selection {
47+
border-color: #040405;
48+
border-bottom-left-radius: 0;
49+
border-bottom-right-radius: 0;
50+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import "./inputfield.css";
2+
3+
export default function ({
4+
label,
5+
id,
6+
placeholder,
7+
required,
8+
type,
9+
style,
10+
value,
11+
maxLength=1250,
12+
onChange
13+
}) {
14+
15+
const isTextarea = type === 'textarea';
16+
17+
const commonProps = {
18+
id: id,
19+
placeholder: placeholder,
20+
required: required,
21+
value: value,
22+
onChange: onChange,
23+
className: "input-field"
24+
};
25+
26+
return (
27+
<div className="input-container" style={style}>
28+
<label className="input-label" htmlFor={id}>{label} {required ? (<span className="required-text">(Required)</span>) : <></>}</label>
29+
30+
{isTextarea ? (
31+
<textarea
32+
{...commonProps}
33+
style={{ minHeight: '150px', padding: '10px', resize: 'vertical' }} maxLength={maxLength}
34+
></textarea>
35+
) : (
36+
<input
37+
{...commonProps}
38+
type={type} maxLength={maxLength}
39+
/>
40+
)}
41+
</div>
42+
);
43+
}

server/api/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const oauth2 = require('./oauth2/index');
1818
const entitlements = require('./entitlements');
1919
const activities = require('./activities');
2020
const integrations = require('./integrations');
21+
const reports = require('./reports');
2122

2223
global.config = globalUtils.config;
2324
//just in case
@@ -85,6 +86,8 @@ app.get("/voice/ice", (req, res) => {
8586
})
8687
});
8788

89+
app.use("/reports", reports);
90+
8891
app.use(authMiddleware);
8992

9093
app.use("/admin", instanceMiddleware("VERIFIED_EMAIL_REQUIRED"), admin);

0 commit comments

Comments
 (0)