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";
18 capitalizeFirstLetter,
21 removeFromEmojiDataModel,
27 import { HtmlTags } from "../common/html-tags";
28 import { Spinner } from "../common/icon";
29 import Tabs from "../common/tabs";
30 import { PersonListing } from "../person/person-listing";
31 import { EmojiForm } from "./emojis-form";
32 import RateLimitForm from "./rate-limit-form";
33 import { SiteForm } from "./site-form";
34 import { TaglineForm } from "./tagline-form";
36 type AdminSettingsData = RouteDataResponse<{
37 bannedPersonsResponse: BannedPersonsResponse;
38 federatedInstancesResponse: GetFederatedInstancesResponse;
41 interface AdminSettingsState {
42 siteRes: GetSiteResponse;
45 instancesRes: RequestState<GetFederatedInstancesResponse>;
46 bannedRes: RequestState<BannedPersonsResponse>;
47 leaveAdminTeamRes: RequestState<GetSiteResponse>;
49 isIsomorphic: boolean;
52 export class AdminSettings extends Component<any, AdminSettingsState> {
53 private isoData = setIsoData<AdminSettingsData>(this.context);
54 state: AdminSettingsState = {
55 siteRes: this.isoData.site_res,
58 bannedRes: { state: "empty" },
59 instancesRes: { state: "empty" },
60 leaveAdminTeamRes: { state: "empty" },
65 constructor(props: any, context: any) {
66 super(props, context);
68 this.handleEditSite = this.handleEditSite.bind(this);
69 this.handleEditEmoji = this.handleEditEmoji.bind(this);
70 this.handleDeleteEmoji = this.handleDeleteEmoji.bind(this);
71 this.handleCreateEmoji = this.handleCreateEmoji.bind(this);
73 // Only fetch the data if coming from another route
74 if (FirstLoadService.isFirstLoad) {
76 bannedPersonsResponse: bannedRes,
77 federatedInstancesResponse: instancesRes,
78 } = this.isoData.routeData;
89 static async fetchInitialData({
92 }: InitialFetchRequest): Promise<AdminSettingsData> {
94 bannedPersonsResponse: await client.getBannedPersons({
97 federatedInstancesResponse: await client.getFederatedInstances({
103 async componentDidMount() {
104 if (!this.state.isIsomorphic) {
105 await this.fetchData();
109 get documentTitle(): string {
110 return `${i18n.t("admin_settings")} - ${
111 this.state.siteRes.site_view.site.name
116 const federationData =
117 this.state.instancesRes.state === "success"
118 ? this.state.instancesRes.data.federated_instances
122 <div className="container-lg">
124 title={this.documentTitle}
125 path={this.context.router.route.match.url}
131 label: i18n.t("site"),
133 <div className="row">
134 <div className="col-12 col-md-6">
136 showLocal={showLocal(this.isoData)}
137 allowedInstances={federationData?.allowed}
138 blockedInstances={federationData?.blocked}
139 onSaveSite={this.handleEditSite}
140 siteRes={this.state.siteRes}
141 themeList={this.state.themeList}
144 <div className="col-12 col-md-6">
152 key: "rate_limiting",
153 label: "Rate Limiting",
157 this.state.siteRes.site_view.local_site_rate_limit
159 onSaveSite={this.handleEditSite}
165 label: i18n.t("taglines"),
167 <div className="row">
169 taglines={this.state.siteRes.taglines}
170 onSaveSite={this.handleEditSite}
177 label: i18n.t("emojis"),
179 <div className="row">
181 onCreate={this.handleCreateEmoji}
182 onDelete={this.handleDeleteEmoji}
183 onEdit={this.handleEditEmoji}
196 bannedRes: { state: "loading" },
197 instancesRes: { state: "loading" },
201 const auth = myAuthRequired();
203 const [bannedRes, instancesRes, themeList] = await Promise.all([
204 HttpService.client.getBannedPersons({ auth }),
205 HttpService.client.getFederatedInstances({ auth }),
219 <h5>{capitalizeFirstLetter(i18n.t("admins"))}</h5>
220 <ul className="list-unstyled">
221 {this.state.siteRes.admins.map(admin => (
222 <li key={admin.person.id} className="list-inline-item">
223 <PersonListing person={admin.person} />
235 onClick={linkEvent(this, this.handleLeaveAdminTeam)}
236 className="btn btn-danger mb-2"
238 {this.state.leaveAdminTeamRes.state == "loading" ? (
241 i18n.t("leave_admin_team")
248 switch (this.state.bannedRes.state) {
256 const bans = this.state.bannedRes.data.banned;
259 <h5>{i18n.t("banned_users")}</h5>
260 <ul className="list-unstyled">
261 {bans.map(banned => (
262 <li key={banned.person.id} className="list-inline-item">
263 <PersonListing person={banned.person} />
273 async handleEditSite(form: EditSite) {
274 const editRes = await HttpService.client.editSite(form);
276 if (editRes.state === "success") {
278 s.siteRes.site_view = editRes.data.site_view;
279 // TODO: Where to get taglines from?
280 s.siteRes.taglines = editRes.data.taglines;
283 toast(i18n.t("site_saved"));
289 handleSwitchTab(i: { ctx: AdminSettings; tab: string }) {
290 i.ctx.setState({ currentTab: i.tab });
293 async handleLeaveAdminTeam(i: AdminSettings) {
294 i.setState({ leaveAdminTeamRes: { state: "loading" } });
296 leaveAdminTeamRes: await HttpService.client.leaveAdmin({
297 auth: myAuthRequired(),
301 if (this.state.leaveAdminTeamRes.state === "success") {
302 toast(i18n.t("left_admin_team"));
303 this.context.router.history.replace("/");
307 async handleEditEmoji(form: EditCustomEmoji) {
308 const res = await HttpService.client.editCustomEmoji(form);
309 if (res.state === "success") {
310 updateEmojiDataModel(res.data.custom_emoji);
314 async handleDeleteEmoji(form: DeleteCustomEmoji) {
315 const res = await HttpService.client.deleteCustomEmoji(form);
316 if (res.state === "success") {
317 removeFromEmojiDataModel(res.data.id);
321 async handleCreateEmoji(form: CreateCustomEmoji) {
322 const res = await HttpService.client.createCustomEmoji(form);
323 if (res.state === "success") {
324 updateEmojiDataModel(res.data.custom_emoji);