index.community/frontend/src/components/screens/AdminScreen.tsx

174 lines
5.2 KiB
TypeScript

import { Button, FormGroup, H1, H2, Intent, NonIdealState, Spinner, Switch } from "@blueprintjs/core";
import { IconNames } from "@blueprintjs/icons";
import { push } from "connected-react-router";
import React from "react";
import { connect } from "react-redux";
import { Redirect } from "react-router";
import { Dispatch } from "redux";
import styled from "styled-components";
import AppToaster from "../../toaster";
import { getAuthToken, getFromApi, postToApi, unsetAuthToken } from "../../util";
import { Page } from "../atoms";
const ButtonContainer = styled.div`
display: flex;
flex-direction: row;
width: 100%;
justify-content: space-between;
`;
interface AdminSettings {
domain: string;
optIn: boolean;
optOut: boolean;
userCount: number;
statusCount: number;
}
interface AdminScreenProps {
navigate: (path: string) => void;
}
interface AdminScreenState {
settings?: AdminSettings;
isUpdating: boolean;
}
class AdminScreen extends React.PureComponent<AdminScreenProps, AdminScreenState> {
private authToken = getAuthToken();
public constructor(props: AdminScreenProps) {
super(props);
this.state = { isUpdating: false };
}
public componentDidMount() {
// Load instance settings from server
if (this.authToken) {
getFromApi(`admin`, this.authToken)
.then((response) => {
this.setState({ settings: response });
})
.catch(() => {
AppToaster.show({
icon: IconNames.ERROR,
intent: Intent.DANGER,
message: "Failed to load settings.",
timeout: 0,
});
unsetAuthToken();
});
}
}
public render() {
if (!this.authToken) {
return <Redirect to="/admin/login" />;
}
const { settings, isUpdating } = this.state;
let content;
if (!settings) {
content = <NonIdealState icon={<Spinner />} />;
} else {
content = (
<>
<H2>{settings.domain}</H2>
<p>{`${settings.userCount} users with ${settings.statusCount || "(unknown)"} statuses.`}</p>
<form onSubmit={this.updateSettings}>
{settings.userCount < 10 && (
<FormGroup helperText="Check this if you'd like your personal instance to be crawled by fediverse.space. This takes up to 24 hours to take effect.">
<Switch
id="opt-in-switch"
checked={!!settings.optIn}
large
label="Opt in"
disabled={!!isUpdating}
onChange={this.updateOptIn}
/>
</FormGroup>
)}
<FormGroup helperText="Check this if you don't want to your instance to be crawled. You won't appear on fediverse.space. The change is immediate.">
<Switch
id="opt-out-switch"
checked={!!settings.optOut}
large
label="Opt out"
disabled={!!isUpdating}
onChange={this.updateOptOut}
/>
</FormGroup>
<ButtonContainer>
<Button intent={Intent.PRIMARY} type="submit" loading={!!isUpdating}>
Update settings
</Button>
<Button intent={Intent.DANGER} onClick={this.logout} icon={IconNames.LOG_OUT}>
Log out
</Button>
</ButtonContainer>
</form>
</>
);
}
return (
<Page>
<H1>Instance administration</H1>
{content}
</Page>
);
}
private updateOptIn = (e: React.FormEvent<HTMLInputElement>) => {
const settings = this.state.settings as AdminSettings;
const optIn = e.currentTarget.checked;
let { optOut } = settings;
if (optIn) {
optOut = false;
}
this.setState({ settings: { ...settings, optIn, optOut } });
};
private updateOptOut = (e: React.FormEvent<HTMLInputElement>) => {
const settings = this.state.settings as AdminSettings;
const optOut = e.currentTarget.checked;
let { optIn } = settings;
if (optOut) {
optIn = false;
}
this.setState({ settings: { ...settings, optIn, optOut } });
};
private updateSettings = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
this.setState({ isUpdating: true });
const body = {
optIn: this.state.settings!.optIn,
optOut: this.state.settings!.optOut,
};
postToApi(`admin`, body, this.authToken!)
.then((response) => {
this.setState({ settings: response, isUpdating: false });
AppToaster.show({
icon: IconNames.TICK,
intent: Intent.SUCCESS,
message: "Successfully updated settings.",
});
})
.catch(() => {
this.setState({ isUpdating: false });
AppToaster.show({ intent: Intent.DANGER, icon: IconNames.ERROR, message: "Failed to update settings." });
});
};
private logout = () => {
unsetAuthToken();
AppToaster.show({
icon: IconNames.LOG_OUT,
message: "Logged out.",
});
this.props.navigate("/admin/login");
};
}
const mapDispatchToProps = (dispatch: Dispatch) => ({
navigate: (path: string) => dispatch(push(path)),
});
export default connect(undefined, mapDispatchToProps)(AdminScreen);