1 import { Component, linkEvent } from "inferno";
8 GetFederatedInstancesResponse,
11 } from "lemmy-js-client";
12 import { i18n } from "../../i18next";
13 import { InitialFetchRequest } from "../../interfaces";
14 import { FirstLoadService } from "../../services/FirstLoadService";
15 import { HttpService, RequestState } from "../../services/HttpService";
17 capitalizeFirstLetter,
20 removeFromEmojiDataModel,
26 import { HtmlTags } from "../common/html-tags";
27 import { Spinner } from "../common/icon";
28 import Tabs from "../common/tabs";
29 import { PersonListing } from "../person/person-listing";
30 import { EmojiForm } from "./emojis-form";
31 import RateLimitForm from "./rate-limit-form";
32 import { SiteForm } from "./site-form";
33 import { TaglineForm } from "./tagline-form";
35 interface AdminSettingsState {
36 siteRes: GetSiteResponse;
39 instancesRes: RequestState<GetFederatedInstancesResponse>;
40 bannedRes: RequestState<BannedPersonsResponse>;
41 leaveAdminTeamRes: RequestState<GetSiteResponse>;
43 isIsomorphic: boolean;
46 export class AdminSettings extends Component<any, AdminSettingsState> {
47 private isoData = setIsoData(this.context);
48 state: AdminSettingsState = {
49 siteRes: this.isoData.site_res,
52 bannedRes: { state: "empty" },
53 instancesRes: { state: "empty" },
54 leaveAdminTeamRes: { state: "empty" },
59 constructor(props: any, context: any) {
60 super(props, context);
62 this.handleEditSite = this.handleEditSite.bind(this);
63 this.handleEditEmoji = this.handleEditEmoji.bind(this);
64 this.handleDeleteEmoji = this.handleDeleteEmoji.bind(this);
65 this.handleCreateEmoji = this.handleCreateEmoji.bind(this);
67 // Only fetch the data if coming from another route
68 if (FirstLoadService.isFirstLoad) {
69 const [bannedRes, instancesRes] = this.isoData.routeData;
81 bannedRes: { state: "loading" },
82 instancesRes: { state: "loading" },
86 const auth = myAuthRequired();
88 const [bannedRes, instancesRes, themeList] = await Promise.all([
89 HttpService.client.getBannedPersons({ auth }),
90 HttpService.client.getFederatedInstances({ auth }),
101 static fetchInitialData({
104 }: InitialFetchRequest): Promise<any>[] {
105 const promises: Promise<RequestState<any>>[] = [];
108 promises.push(client.getBannedPersons({ auth }));
109 promises.push(client.getFederatedInstances({ auth }));
112 Promise.resolve({ state: "empty" }),
113 Promise.resolve({ state: "empty" })
120 async componentDidMount() {
121 if (!this.state.isIsomorphic) {
122 await this.fetchData();
126 get documentTitle(): string {
127 return `${i18n.t("admin_settings")} - ${
128 this.state.siteRes.site_view.site.name
133 const federationData =
134 this.state.instancesRes.state === "success"
135 ? this.state.instancesRes.data.federated_instances
139 <div className="container-lg">
141 title={this.documentTitle}
142 path={this.context.router.route.match.url}
148 label: i18n.t("site"),
150 <div className="row">
151 <div className="col-12 col-md-6">
153 showLocal={showLocal(this.isoData)}
154 allowedInstances={federationData?.allowed}
155 blockedInstances={federationData?.blocked}
156 onSaveSite={this.handleEditSite}
157 siteRes={this.state.siteRes}
158 themeList={this.state.themeList}
161 <div className="col-12 col-md-6">
169 key: "rate_limiting",
170 label: "Rate Limiting",
174 this.state.siteRes.site_view.local_site_rate_limit
176 onSaveSite={this.handleEditSite}
182 label: i18n.t("taglines"),
184 <div className="row">
186 taglines={this.state.siteRes.taglines}
187 onSaveSite={this.handleEditSite}
194 label: i18n.t("emojis"),
196 <div className="row">
198 onCreate={this.handleCreateEmoji}
199 onDelete={this.handleDeleteEmoji}
200 onEdit={this.handleEditEmoji}
214 <h5>{capitalizeFirstLetter(i18n.t("admins"))}</h5>
215 <ul className="list-unstyled">
216 {this.state.siteRes.admins.map(admin => (
217 <li key={admin.person.id} className="list-inline-item">
218 <PersonListing person={admin.person} />
230 onClick={linkEvent(this, this.handleLeaveAdminTeam)}
231 className="btn btn-danger mb-2"
233 {this.state.leaveAdminTeamRes.state == "loading" ? (
236 i18n.t("leave_admin_team")
243 switch (this.state.bannedRes.state) {
251 const bans = this.state.bannedRes.data.banned;
254 <h5>{i18n.t("banned_users")}</h5>
255 <ul className="list-unstyled">
256 {bans.map(banned => (
257 <li key={banned.person.id} className="list-inline-item">
258 <PersonListing person={banned.person} />
268 async handleEditSite(form: EditSite) {
269 const editRes = await HttpService.client.editSite(form);
271 if (editRes.state === "success") {
273 s.siteRes.site_view = editRes.data.site_view;
274 // TODO: Where to get taglines from?
275 s.siteRes.taglines = editRes.data.taglines;
278 toast(i18n.t("site_saved"));
284 handleSwitchTab(i: { ctx: AdminSettings; tab: string }) {
285 i.ctx.setState({ currentTab: i.tab });
288 async handleLeaveAdminTeam(i: AdminSettings) {
289 i.setState({ leaveAdminTeamRes: { state: "loading" } });
291 leaveAdminTeamRes: await HttpService.client.leaveAdmin({
292 auth: myAuthRequired(),
296 if (this.state.leaveAdminTeamRes.state === "success") {
297 toast(i18n.t("left_admin_team"));
298 this.context.router.history.replace("/");
302 async handleEditEmoji(form: EditCustomEmoji) {
303 const res = await HttpService.client.editCustomEmoji(form);
304 if (res.state === "success") {
305 updateEmojiDataModel(res.data.custom_emoji);
309 async handleDeleteEmoji(form: DeleteCustomEmoji) {
310 const res = await HttpService.client.deleteCustomEmoji(form);
311 if (res.state === "success") {
312 removeFromEmojiDataModel(res.data.id);
316 async handleCreateEmoji(form: CreateCustomEmoji) {
317 const res = await HttpService.client.createCustomEmoji(form);
318 if (res.state === "success") {
319 updateEmojiDataModel(res.data.custom_emoji);