ban users

This commit is contained in:
Brandon Thomas committed 2021-09-02 04:48:33 -04:00
1 parent 791fd98c8c
commit 8e554f6ed1
6 files changed
+221 -2

No files matched your search

+4
View File
@@ -246,6 +246,10 @@ class ApiConfig {
return `${this.getScheme()}://${this.getNewApiHost()}/moderation/ip_bans/${ipAddress}/delete`;
}
banUser() : string {
return `${this.getScheme()}://${this.getNewApiHost()}/moderation/user_bans/manage_ban`;
}
private getScheme() : string {
return this.isLocalDev ? "http" : "https";
}
+8
View File
@@ -71,6 +71,14 @@ class FrontendUrlConfig {
static userProfilePage(userDisplayName: string) : string {
return `/profile/${userDisplayName}`;
}
static userProfileEditPage(userDisplayName: string) : string {
return `/profile/${userDisplayName}/edit`;
}
static userProfileBanPage(userDisplayName: string) : string {
return `/profile/${userDisplayName}/ban`;
}
}
export { FrontendUrlConfig }
+8 -1
View File
@@ -14,7 +14,6 @@ export interface User {
profile_markdown: string,
profile_rendered_html: string,
user_role_slug: string,
banned: boolean,
dark_mode: string,
avatar_public_bucket_hash: string,
disable_gravatar: boolean,
@@ -28,6 +27,7 @@ export interface User {
cashapp_username: string | undefined | null,
created_at: string,
badges: ProfileBadge[],
maybe_moderator_fields: UserProfileModeratorFields | null | undefined,
}
export interface ProfileBadge {
@@ -38,6 +38,13 @@ export interface ProfileBadge {
granted_at: string,
}
export interface UserProfileModeratorFields {
is_banned: boolean,
maybe_mod_comments: string | null | undefined,
maybe_mod_user_token: string | null | undefined,
}
export async function GetUserByUsername(username: string) : Promise<User | undefined> {
const usernameLower = username.toLowerCase(); // NB: Until I standardize on display name vs username lookup.
const endpoint = new ApiConfig().getProfile(usernameLower);
@@ -36,6 +36,7 @@ import { W2lResultDeleteFc } from './w2l/w2l_result_delete/W2lResultDeleteFc';
import { W2lTemplateApproveFc } from './w2l/w2l_template_approve/W2lTemplateApproveFc';
import { TtsModelListFc } from './tts/tts_model_list/TtsModelListFc';
import { TtsModelListItem } from '../api/tts/ListTtsModels';
import { ProfileBanFc } from './profile/profile_ban/ProfileBanFc';
interface Props {
sessionWrapper: SessionWrapper,
@@ -109,6 +110,12 @@ class NewVocodesContainer extends React.Component<Props, State> {
/>
</Route>
<Route path="/profile/:username/ban">
<ProfileBanFc
sessionWrapper={this.props.sessionWrapper}
/>
</Route>
<Route path="/profile/:username">
<ProfileFc
sessionWrapper={this.props.sessionWrapper}
@@ -0,0 +1,171 @@
import React, { useCallback, useEffect, useState } from 'react';
import { ApiConfig } from '../../../../common/ApiConfig';
import { Link, useHistory } from 'react-router-dom';
import { SessionWrapper } from '../../../../session/SessionWrapper';
import { useParams } from 'react-router-dom';
import { GetUserByUsername, User } from '../../../api/user/GetUserByUsername';
interface Props {
sessionWrapper: SessionWrapper,
}
function ProfileBanFc(props: Props) {
const { username } : { username: string }= useParams();
const userProfilePage = `/profile/${username}`;
const history = useHistory();
// From endpoint
const [userData, setUserData] = useState<User|undefined>(undefined);
// Form values
const [modComments, setModComments] = useState<string>("");
const [isBanned, setIsBanned] = useState<boolean>(false);
const getUserProfile = useCallback(async (username) => {
const user = await GetUserByUsername(username);
if (user) {
setUserData(user);
setIsBanned(user?.maybe_moderator_fields?.is_banned || false);
setModComments(user?.maybe_moderator_fields?.maybe_mod_comments || "");
}
}, []);
useEffect(() => {
getUserProfile(username);
}, [username, getUserProfile]);
const handleFormSubmit = (ev: React.FormEvent<HTMLFormElement>) : boolean => {
ev.preventDefault();
const api = new ApiConfig();
const endpointUrl = api.banUser();
const request = {
username: userData?.username,
is_banned: isBanned,
mod_notes: modComments,
}
fetch(endpointUrl, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
credentials: 'include',
body: JSON.stringify(request),
})
.then(res => res.json())
.then(res => {
if (res.success) {
history.push(userProfilePage);
}
})
.catch(e => {
});
return false;
}
if (!userData) {
// Waiting for load.
return <span />;
}
if (!!userData && !props.sessionWrapper.canEditUserProfile(username)) {
// Loading and we don't have access.
history.push(userProfilePage);
}
const handleModCommentsChange = (ev: React.FormEvent<HTMLInputElement>) => {
ev.preventDefault();
const textValue = (ev.target as HTMLInputElement).value;
setModComments(textValue);
return false;
};
const handleIsBannedChange = (ev: React.FormEvent<HTMLSelectElement>) => {
let bannedState = false;
switch ((ev.target as HTMLSelectElement).value) {
case "true":
bannedState = true;
break;
case "TRUE":
bannedState = true;
break;
}
setIsBanned(bannedState)
};
let viewLinkUrl = `/profile/${userData?.username}`;
let isDisabled = userData === undefined;
return (
<div>
<h2 className="subtitle is-2">Profile &amp; Preferences</h2>
<Link to={viewLinkUrl}>&lt; Back to profile</Link>
<br />
<br />
<form onSubmit={handleFormSubmit}>
<fieldset disabled={isDisabled}>
<div className="field">
<label className="label">Is Banned?</label>
<div className="control select">
<select
name="default_pretrained_vocoder"
onChange={handleIsBannedChange}
value={isBanned ? "true" : "false"}
>
<option value="true">Banned</option>
<option value="false">Not Banned</option>
</select>
</div>
</div>
<div className="field">
<label className="label">Moderator Comments (Short)</label>
<div className="control has-icons-left has-icons-right">
<input
onChange={handleModCommentsChange}
className="input"
type="text"
placeholder="Moderator Comments"
value={modComments}
/>
</div>
{/*<p className="help">{invalidReason}</p>*/}
</div>
<br />
<button className="button is-danger is-large is-fullwidth">Update Ban</button>
</fieldset>
</form>
<br />
<div>
<p>Notes on banned users:</p>
<ul>
<li></li>
<li></li>
<li></li>
</ul>
</div>
</div>
)
}
export { ProfileBanFc };
@@ -12,6 +12,7 @@ import { faDiscord, faFirefox, faGithub, faTwitch, faTwitter } from '@fortawesom
import { faClock, faDollarSign } from '@fortawesome/free-solid-svg-icons';
import { GetUserByUsername, User } from '../../../api/user/GetUserByUsername';
import { format } from 'date-fns';
import { FrontendUrlConfig } from '../../../../common/FrontendUrlConfig';
interface Props {
sessionWrapper: SessionWrapper,
@@ -43,7 +44,7 @@ function ProfileFc(props: Props) {
let editProfileButton = <span />
if (props.sessionWrapper.canEditUserProfile(userData.username)) {
const editLinkUrl = `/profile/${userData.username}/edit`;
const editLinkUrl = FrontendUrlConfig.userProfileEditPage(userData.username);
// Mods shouldn't edit preferences.
const buttonLabel = props.sessionWrapper.userTokenMatches(userData.user_token) ?
@@ -59,6 +60,26 @@ function ProfileFc(props: Props) {
);
}
let banUserButton = <span />
if (props.sessionWrapper.canBanUsers()) {
const currentlyBanned = userData.maybe_moderator_fields?.is_banned;
const banLinkUrl = FrontendUrlConfig.userProfileBanPage(userData.username);
const buttonLabel = currentlyBanned ? "Unban User" : "Ban User";
const banButtonCss = currentlyBanned ?
"button is-warning is-large is-fullwidth" :
"button is-danger is-large is-fullwidth";
banUserButton = (
<>
<Link
className={banButtonCss}
to={banLinkUrl}>{buttonLabel}</Link>
<br />
</>
);
}
let profileRows : Array<JSX.Element> = [];
if (userData.website_url !== undefined && userData.website_url !== null) {
@@ -209,6 +230,7 @@ function ProfileFc(props: Props) {
</h1>
{editProfileButton}
{banUserButton}
<div
className="profile content is-medium"