lemmy-ui/src/shared/components/home/admin-settings.tsx
Alec Armbruster 80e2c9602e
Admin Settings: Bugfixes (#1313)
* move loading state to AdminSettings, pass as prop, tweak margin on some labels, add missing bind

* default loading state to false on setup.tsx, add util

* rename util to make more sense

* make @dessalines suggested changes
2023-06-16 11:52:47 -04:00

348 lines
9.3 KiB
TypeScript

import { Component, linkEvent } from "inferno";
import {
BannedPersonsResponse,
CreateCustomEmoji,
DeleteCustomEmoji,
EditCustomEmoji,
EditSite,
GetFederatedInstancesResponse,
GetSiteResponse,
PersonView,
} from "lemmy-js-client";
import { i18n } from "../../i18next";
import { InitialFetchRequest } from "../../interfaces";
import { FirstLoadService } from "../../services/FirstLoadService";
import { HttpService, RequestState } from "../../services/HttpService";
import {
capitalizeFirstLetter,
fetchThemeList,
myAuthRequired,
removeFromEmojiDataModel,
setIsoData,
showLocal,
toast,
updateEmojiDataModel,
} from "../../utils";
import { HtmlTags } from "../common/html-tags";
import { Spinner } from "../common/icon";
import Tabs from "../common/tabs";
import { PersonListing } from "../person/person-listing";
import { EmojiForm } from "./emojis-form";
import RateLimitForm from "./rate-limit-form";
import { SiteForm } from "./site-form";
import { TaglineForm } from "./tagline-form";
interface AdminSettingsState {
siteRes: GetSiteResponse;
banned: PersonView[];
currentTab: string;
instancesRes: RequestState<GetFederatedInstancesResponse>;
bannedRes: RequestState<BannedPersonsResponse>;
leaveAdminTeamRes: RequestState<GetSiteResponse>;
emojiLoading: boolean;
loading: boolean;
themeList: string[];
isIsomorphic: boolean;
}
export class AdminSettings extends Component<any, AdminSettingsState> {
private isoData = setIsoData(this.context);
state: AdminSettingsState = {
siteRes: this.isoData.site_res,
banned: [],
currentTab: "site",
bannedRes: { state: "empty" },
instancesRes: { state: "empty" },
leaveAdminTeamRes: { state: "empty" },
emojiLoading: false,
loading: false,
themeList: [],
isIsomorphic: false,
};
constructor(props: any, context: any) {
super(props, context);
this.handleEditSite = this.handleEditSite.bind(this);
this.handleEditEmoji = this.handleEditEmoji.bind(this);
this.handleDeleteEmoji = this.handleDeleteEmoji.bind(this);
this.handleCreateEmoji = this.handleCreateEmoji.bind(this);
// Only fetch the data if coming from another route
if (FirstLoadService.isFirstLoad) {
const [bannedRes, instancesRes] = this.isoData.routeData;
this.state = {
...this.state,
bannedRes,
instancesRes,
isIsomorphic: true,
};
}
}
async fetchData() {
this.setState({
bannedRes: { state: "loading" },
instancesRes: { state: "loading" },
themeList: [],
loading: true,
});
const auth = myAuthRequired();
const [bannedRes, instancesRes, themeList] = await Promise.all([
HttpService.client.getBannedPersons({ auth }),
HttpService.client.getFederatedInstances({ auth }),
fetchThemeList(),
]);
this.setState({
bannedRes,
instancesRes,
themeList,
loading: false,
});
}
static fetchInitialData({
auth,
client,
}: InitialFetchRequest): Promise<any>[] {
const promises: Promise<RequestState<any>>[] = [];
if (auth) {
promises.push(client.getBannedPersons({ auth }));
promises.push(client.getFederatedInstances({ auth }));
} else {
promises.push(
Promise.resolve({ state: "empty" }),
Promise.resolve({ state: "empty" })
);
}
return promises;
}
async componentDidMount() {
if (!this.state.isIsomorphic) {
await this.fetchData();
}
}
get documentTitle(): string {
return `${i18n.t("admin_settings")} - ${
this.state.siteRes.site_view.site.name
}`;
}
render() {
const federationData =
this.state.instancesRes.state === "success"
? this.state.instancesRes.data.federated_instances
: undefined;
return (
<div className="container-lg">
<HtmlTags
title={this.documentTitle}
path={this.context.router.route.match.url}
/>
<Tabs
tabs={[
{
key: "site",
label: i18n.t("site"),
getNode: () => (
<div className="row">
<div className="col-12 col-md-6">
<SiteForm
showLocal={showLocal(this.isoData)}
allowedInstances={federationData?.allowed}
blockedInstances={federationData?.blocked}
onSaveSite={this.handleEditSite}
siteRes={this.state.siteRes}
themeList={this.state.themeList}
loading={this.state.loading}
/>
</div>
<div className="col-12 col-md-6">
{this.admins()}
{this.bannedUsers()}
</div>
</div>
),
},
{
key: "rate_limiting",
label: "Rate Limiting",
getNode: () => (
<RateLimitForm
rateLimits={
this.state.siteRes.site_view.local_site_rate_limit
}
onSaveSite={this.handleEditSite}
loading={this.state.loading}
/>
),
},
{
key: "taglines",
label: i18n.t("taglines"),
getNode: () => (
<div className="row">
<TaglineForm
taglines={this.state.siteRes.taglines}
onSaveSite={this.handleEditSite}
loading={this.state.loading}
/>
</div>
),
},
{
key: "emojis",
label: i18n.t("emojis"),
getNode: () => (
<div className="row">
<EmojiForm
onCreate={this.handleCreateEmoji}
onDelete={this.handleDeleteEmoji}
onEdit={this.handleEditEmoji}
loading={this.state.emojiLoading}
/>
</div>
),
},
]}
/>
</div>
);
}
admins() {
return (
<>
<h5>{capitalizeFirstLetter(i18n.t("admins"))}</h5>
<ul className="list-unstyled">
{this.state.siteRes.admins.map(admin => (
<li key={admin.person.id} className="list-inline-item">
<PersonListing person={admin.person} />
</li>
))}
</ul>
{this.leaveAdmin()}
</>
);
}
leaveAdmin() {
return (
<button
onClick={linkEvent(this, this.handleLeaveAdminTeam)}
className="btn btn-danger mb-2"
>
{this.state.leaveAdminTeamRes.state == "loading" ? (
<Spinner />
) : (
i18n.t("leave_admin_team")
)}
</button>
);
}
bannedUsers() {
switch (this.state.bannedRes.state) {
case "loading":
return (
<h5>
<Spinner large />
</h5>
);
case "success": {
const bans = this.state.bannedRes.data.banned;
return (
<>
<h5>{i18n.t("banned_users")}</h5>
<ul className="list-unstyled">
{bans.map(banned => (
<li key={banned.person.id} className="list-inline-item">
<PersonListing person={banned.person} />
</li>
))}
</ul>
</>
);
}
}
}
async handleEditSite(form: EditSite) {
this.setState({ loading: true });
const editRes = await HttpService.client.editSite(form);
if (editRes.state === "success") {
this.setState(s => {
s.siteRes.site_view = editRes.data.site_view;
// TODO: Where to get taglines from?
s.siteRes.taglines = editRes.data.taglines;
return s;
});
toast(i18n.t("site_saved"));
}
this.setState({ loading: false });
return editRes;
}
handleSwitchTab(i: { ctx: AdminSettings; tab: string }) {
i.ctx.setState({ currentTab: i.tab });
}
async handleLeaveAdminTeam(i: AdminSettings) {
i.setState({ leaveAdminTeamRes: { state: "loading" } });
this.setState({
leaveAdminTeamRes: await HttpService.client.leaveAdmin({
auth: myAuthRequired(),
}),
});
if (this.state.leaveAdminTeamRes.state === "success") {
toast(i18n.t("left_admin_team"));
this.context.router.history.replace("/");
}
}
async handleEditEmoji(form: EditCustomEmoji) {
this.setState({ emojiLoading: true });
const res = await HttpService.client.editCustomEmoji(form);
if (res.state === "success") {
updateEmojiDataModel(res.data.custom_emoji);
}
this.setState({ emojiLoading: false });
}
async handleDeleteEmoji(form: DeleteCustomEmoji) {
this.setState({ emojiLoading: true });
const res = await HttpService.client.deleteCustomEmoji(form);
if (res.state === "success") {
removeFromEmojiDataModel(res.data.id);
}
this.setState({ emojiLoading: false });
}
async handleCreateEmoji(form: CreateCustomEmoji) {
this.setState({ emojiLoading: true });
const res = await HttpService.client.createCustomEmoji(form);
if (res.state === "success") {
updateEmojiDataModel(res.data.custom_emoji);
}
this.setState({ emojiLoading: false });
}
}