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

174 lines
5.2 KiB
TypeScript
Raw Normal View History

2019-07-26 14:34:23 +00:00
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;
`;
2020-05-19 13:45:27 +00:00
interface AdminSettings {
2019-07-26 14:34:23 +00:00
domain: string;
optIn: boolean;
optOut: boolean;
userCount: number;
statusCount: number;
}
2020-05-19 13:45:27 +00:00
interface AdminScreenProps {
2019-07-26 14:34:23 +00:00
navigate: (path: string) => void;
}
2020-05-19 13:45:27 +00:00
interface AdminScreenState {
settings?: AdminSettings;
2019-07-26 14:34:23 +00:00
isUpdating: boolean;
}
2020-05-19 13:45:27 +00:00
class AdminScreen extends React.PureComponent<AdminScreenProps, AdminScreenState> {
2019-07-26 14:34:23 +00:00
private authToken = getAuthToken();
2020-05-19 13:45:27 +00:00
public constructor(props: AdminScreenProps) {
2019-07-26 14:34:23 +00:00
super(props);
this.state = { isUpdating: false };
}
public componentDidMount() {
// Load instance settings from server
2020-05-19 13:45:27 +00:00
if (this.authToken) {
getFromApi(`admin`, this.authToken)
.then((response) => {
2019-07-26 22:30:11 +00:00
this.setState({ settings: response });
})
.catch(() => {
AppToaster.show({
icon: IconNames.ERROR,
intent: Intent.DANGER,
message: "Failed to load settings.",
2020-05-19 13:45:27 +00:00
timeout: 0,
2019-07-26 22:30:11 +00:00
});
2019-08-23 13:08:05 +00:00
unsetAuthToken();
2019-07-26 14:34:23 +00:00
});
2019-07-26 22:30:11 +00:00
}
2019-07-26 14:34:23 +00:00
}
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 && (
2021-09-18 15:16:21 +00:00
<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.">
2019-07-26 14:34:23 +00:00
<Switch
id="opt-in-switch"
checked={!!settings.optIn}
2020-05-19 13:45:27 +00:00
large
2019-07-26 14:34:23 +00:00
label="Opt in"
disabled={!!isUpdating}
onChange={this.updateOptIn}
/>
</FormGroup>
)}
2021-09-18 15:16:21 +00:00
<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.">
2019-07-26 14:34:23 +00:00
<Switch
id="opt-out-switch"
checked={!!settings.optOut}
2020-05-19 13:45:27 +00:00
large
2019-07-26 14:34:23 +00:00
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>) => {
2020-05-19 13:45:27 +00:00
const settings = this.state.settings as AdminSettings;
2019-07-26 14:34:23 +00:00
const optIn = e.currentTarget.checked;
2020-05-19 13:45:27 +00:00
let { optOut } = settings;
2019-07-26 14:34:23 +00:00
if (optIn) {
optOut = false;
}
this.setState({ settings: { ...settings, optIn, optOut } });
};
private updateOptOut = (e: React.FormEvent<HTMLInputElement>) => {
2020-05-19 13:45:27 +00:00
const settings = this.state.settings as AdminSettings;
2019-07-26 14:34:23 +00:00
const optOut = e.currentTarget.checked;
2020-05-19 13:45:27 +00:00
let { optIn } = settings;
2019-07-26 14:34:23 +00:00
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,
2020-05-19 13:45:27 +00:00
optOut: this.state.settings!.optOut,
2019-07-26 14:34:23 +00:00
};
postToApi(`admin`, body, this.authToken!)
2020-05-19 13:45:27 +00:00
.then((response) => {
2019-07-26 14:34:23 +00:00
this.setState({ settings: response, isUpdating: false });
AppToaster.show({
icon: IconNames.TICK,
intent: Intent.SUCCESS,
2020-05-19 13:45:27 +00:00
message: "Successfully updated settings.",
2019-07-26 14:34:23 +00:00
});
})
.catch(() => {
this.setState({ isUpdating: false });
AppToaster.show({ intent: Intent.DANGER, icon: IconNames.ERROR, message: "Failed to update settings." });
});
};
private logout = () => {
unsetAuthToken();
2019-08-23 13:08:05 +00:00
AppToaster.show({
icon: IconNames.LOG_OUT,
2020-05-19 13:45:27 +00:00
message: "Logged out.",
2019-08-23 13:08:05 +00:00
});
2019-07-26 14:34:23 +00:00
this.props.navigate("/admin/login");
};
}
const mapDispatchToProps = (dispatch: Dispatch) => ({
2020-05-19 13:45:27 +00:00
navigate: (path: string) => dispatch(push(path)),
2019-07-26 14:34:23 +00:00
});
2020-05-19 13:45:27 +00:00
export default connect(undefined, mapDispatchToProps)(AdminScreen);