From 53493eaa5a9ca850b07cdeb228a6e6a6db39e04e Mon Sep 17 00:00:00 2001 From: GreenAsJade Date: Mon, 18 Sep 2023 21:17:56 +0930 Subject: [PATCH 01/14] Work in progress - updated previous functionality around moderator powers to suit voting community moderators, receives and displays voting options, just doesn't send the choice to the back end yet. --- src/components/ModerateUser/ModerateUser.tsx | 19 +- src/components/NavBar/NavBar.tsx | 5 +- src/lib/misc.ts | 2 +- src/lib/report_manager.ts | 2 + .../ModerationActionSelector.tsx | 79 +++++++ src/views/ReportsCenter/ReportedGame.tsx | 57 ++--- src/views/ReportsCenter/ViewReport.styl | 21 ++ src/views/ReportsCenter/ViewReport.tsx | 209 ++++++++++-------- 8 files changed, 261 insertions(+), 133 deletions(-) create mode 100644 src/views/ReportsCenter/ModerationActionSelector.tsx diff --git a/src/components/ModerateUser/ModerateUser.tsx b/src/components/ModerateUser/ModerateUser.tsx index b405067f4c..f4cd4ed727 100644 --- a/src/components/ModerateUser/ModerateUser.tsx +++ b/src/components/ModerateUser/ModerateUser.tsx @@ -19,13 +19,11 @@ import * as React from "react"; import * as data from "data"; import { _ } from "translate"; import { put, get, del } from "requests"; -import { errorAlerter } from "misc"; +import { MOD_POWER_HANDLE_SCORE_CHEAT, errorAlerter } from "misc"; import { proRankList } from "rank_utils"; import { Modal, openModal } from "Modal"; import { lookup } from "player_cache"; -import { MOD_POWER_ANNUL } from "misc"; - interface Events {} interface ModerateUserProperties { @@ -52,7 +50,8 @@ export class ModerateUser extends Modal { this.setState( Object.assign({ loading: false }, result.user, { bot_owner: result.user.bot_owner ? result.user.bot_owner.id : null, - can_annul: result.user.moderator_powers & MOD_POWER_ANNUL, + can_handle_score_cheat: + result.user.moderator_powers & MOD_POWER_HANDLE_SCORE_CHEAT, }), ); }) @@ -95,9 +94,9 @@ export class ModerateUser extends Modal { settings[f] = this.state[f]; } - // Can-annul is bit zero of moderator_powers + // handle_score_cheat is bit zero of moderator_powers settings["moderator_powers"] = - (this.state.moderator_powers & ~1) | this.state.can_annul; + (this.state.moderator_powers & ~1) | this.state.can_handle_score_cheat; settings.moderation_note = reason; @@ -111,7 +110,7 @@ export class ModerateUser extends Modal { setLockedUsername = (ev) => this.setState({ locked_username: ev.target.checked }); setSupporter = (ev) => this.setState({ supporter: ev.target.checked }); setAnnouncer = (ev) => this.setState({ is_announcer: ev.target.checked }); - setCanAnnul = (ev) => this.setState({ can_annul: ev.target.checked }); + setHandleScoreCheat = (ev) => this.setState({ can_handle_score_cheat: ev.target.checked }); setProfessional = (ev) => this.setState({ professional: ev.target.checked }); //setBanned = (ev) => this.setState({ is_banned: ev.target.checked }); setShadowbanned = (ev) => this.setState({ is_shadowbanned: ev.target.checked }); @@ -231,14 +230,14 @@ export class ModerateUser extends Modal { )}
- +
diff --git a/src/components/NavBar/NavBar.tsx b/src/components/NavBar/NavBar.tsx index fcbc7daa82..1a3aad830b 100644 --- a/src/components/NavBar/NavBar.tsx +++ b/src/components/NavBar/NavBar.tsx @@ -41,7 +41,7 @@ import { logout } from "auth"; import { useUser, useData } from "hooks"; import { OmniSearch } from "./OmniSearch"; -import { MOD_POWER_ANNUL } from "misc"; +import { MOD_POWER_HANDLE_SCORE_CHEAT } from "misc"; const body = $(document.body); @@ -277,7 +277,8 @@ export function NavBar(): JSX.Element { {_("Rating Calculator")} - {(user.is_moderator || !!(user.moderator_powers & MOD_POWER_ANNUL)) && ( + {(user.is_moderator || + !!(user.moderator_powers & MOD_POWER_HANDLE_SCORE_CHEAT)) && ( {_("Reports Center")} diff --git a/src/lib/misc.ts b/src/lib/misc.ts index ca17ea2a82..fd9b9291b4 100644 --- a/src/lib/misc.ts +++ b/src/lib/misc.ts @@ -21,7 +21,7 @@ import { browserHistory } from "ogsHistory"; import * as preferences from "preferences"; import { alert } from "swal_config"; -export const MOD_POWER_ANNUL = 1; // Matches back-end MOD_POWER +export const MOD_POWER_HANDLE_SCORE_CHEAT = 1; // Matches back-end MOD_POWER export type Timeout = ReturnType; diff --git a/src/lib/report_manager.ts b/src/lib/report_manager.ts index 5a5d8d1d20..2835ffa7df 100644 --- a/src/lib/report_manager.ts +++ b/src/lib/report_manager.ts @@ -63,6 +63,8 @@ export interface Report { automod_to_reporter?: string; automod_to_reported?: string; + available_actions: Array; // community moderator actions + unclaim: () => void; claim: () => void; steal: () => void; diff --git a/src/views/ReportsCenter/ModerationActionSelector.tsx b/src/views/ReportsCenter/ModerationActionSelector.tsx new file mode 100644 index 0000000000..036e14935b --- /dev/null +++ b/src/views/ReportsCenter/ModerationActionSelector.tsx @@ -0,0 +1,79 @@ +/* + * Copyright (C) Online-Go.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import * as React from "react"; +import { Report } from "report_manager"; +import { _, pgettext } from "translate"; + +interface ModerationActionSelectorProps { + report: Report; + enable: boolean; + submit: (action: string) => void; +} + +const ACTIONS = { + annul_score_cheat: pgettext( + "Label for a moderator to select this option", + "Annul the game and warn the cheater", + ), + warn_score_cheat: pgettext( + "Label for a moderator to select this option", + "Just warn the cheater", + ), +}; + +export function ModerationActionSelector({ + report, + enable, + submit, +}: ModerationActionSelectorProps): JSX.Element { + const [selectedOption, setSelectedOption] = React.useState(""); + + const updateSelectedAction = (e: React.ChangeEvent) => { + setSelectedOption(e.target.value); + }; + + return ( +
+

+ {pgettext("The heading for community moderation action choices section", "Actions")} +

+ {report.available_actions.map((a) => ( +
+ + +
+ ))} + {(report.available_actions || null) && ( + + )} +
+ ); +} diff --git a/src/views/ReportsCenter/ReportedGame.tsx b/src/views/ReportsCenter/ReportedGame.tsx index 6eca587a4a..add0889b40 100644 --- a/src/views/ReportsCenter/ReportedGame.tsx +++ b/src/views/ReportsCenter/ReportedGame.tsx @@ -174,34 +174,39 @@ export function ReportedGame({ game_id }: { game_id: number }): JSX.Element { )} - {goban.engine.phase === "finished" ? ( -
- {goban.engine.config.ranked && !annulled && ( - + {user.is_moderator && ( + <> + {goban.engine.phase === "finished" ? ( +
+ {goban.engine.config.ranked && !annulled && ( + + )} + {goban.engine.config.ranked && annulled && ( + + )} +
+ ) : ( +
+ + +
)} - {goban.engine.config.ranked && annulled && ( - - )} -
- ) : ( -
- - -
+ )} - {((goban.engine.phase === "finished" && goban.engine.game_id === game_id && ((goban.engine.width === 19 && goban.engine.height === 19) || diff --git a/src/views/ReportsCenter/ViewReport.styl b/src/views/ReportsCenter/ViewReport.styl index c94b62f070..51fcf158c0 100644 --- a/src/views/ReportsCenter/ViewReport.styl +++ b/src/views/ReportsCenter/ViewReport.styl @@ -107,6 +107,27 @@ margin-right: 0; } } + + .notes { + flex-basis: 50%; + } + + .voting { + flex-basis: 50%; + + .action-selector { + display: flex; + align-items: center + + input { + margin-bottom: 3px; // override bizarre default 0 margin bottom so text lines up properly + } + } + } + + .reported-game h3 { + margin-bottom: 0; + } } @media (max-width: 767px) { diff --git a/src/views/ReportsCenter/ViewReport.tsx b/src/views/ReportsCenter/ViewReport.tsx index 09ef7fc883..d1a3abf148 100644 --- a/src/views/ReportsCenter/ViewReport.tsx +++ b/src/views/ReportsCenter/ViewReport.tsx @@ -33,6 +33,7 @@ import { ReportedGame } from "./ReportedGame"; import { AppealView } from "./AppealView"; import { get } from "requests"; import { MessageTemplate, WARNING_TEMPLATES, REPORTER_RESPONSE_TEMPLATES } from "./MessageTemplate"; +import { ModerationActionSelector } from "./ModerationActionSelector"; // Used for saving updates to the report let report_note_id = 0; @@ -55,6 +56,7 @@ export function ViewReport({ report_id, reports, onChange }: ViewReportProps): J const [error, setError] = React.useState(null); const [moderator_id, setModeratorId] = React.useState(report?.moderator?.id); const [reportState, setReportState] = React.useState(report?.state); + const related = report_manager.getRelatedReports(report_id); React.useEffect(() => { @@ -227,6 +229,10 @@ export function ViewReport({ report_id, reports, onChange }: ViewReportProps): J } }; + const onActionSubmitted = (action: string) => { + console.log(action); + }; + return (
@@ -371,11 +377,11 @@ export function ViewReport({ report_id, reports, onChange }: ViewReportProps): J
- {(report.reporter_note || null) && ( -
-

Reporter Notes

-
- {report.reporter_note_translation ? ( +
+

Reporter Notes

+
+ {(report.reporter_note || null) && + (report.reporter_note_translation ? ( <> {report.reporter_note_translation.source_text} {(report.reporter_note_translation.target_language !== @@ -395,10 +401,9 @@ export function ViewReport({ report_id, reports, onChange }: ViewReportProps): J ) : ( - )} -
+ ))}
- )} +
{(report.system_note || null) && (
@@ -413,97 +418,113 @@ export function ViewReport({ report_id, reports, onChange }: ViewReportProps): J