]> Untitled Git - lemmy-ui.git/blob - src/shared/components/home/admin-settings.tsx
Merge branch 'main' into route-data-refactor
[lemmy-ui.git] / src / shared / components / home / admin-settings.tsx
1 import autosize from "autosize";
2 import { Component, linkEvent } from "inferno";
3 import {
4   BannedPersonsResponse,
5   GetFederatedInstancesResponse,
6   GetSiteResponse,
7   PersonView,
8   SiteResponse,
9   UserOperation,
10   wsJsonToRes,
11   wsUserOp,
12 } from "lemmy-js-client";
13 import { Subscription } from "rxjs";
14 import { i18n } from "../../i18next";
15 import { InitialFetchRequest } from "../../interfaces";
16 import { WebSocketService } from "../../services";
17 import {
18   WithPromiseKeys,
19   capitalizeFirstLetter,
20   isBrowser,
21   myAuth,
22   randomStr,
23   setIsoData,
24   showLocal,
25   toast,
26   wsClient,
27   wsSubscribe,
28 } from "../../utils";
29 import { HtmlTags } from "../common/html-tags";
30 import { Spinner } from "../common/icon";
31 import Tabs from "../common/tabs";
32 import { PersonListing } from "../person/person-listing";
33 import { EmojiForm } from "./emojis-form";
34 import RateLimitForm from "./rate-limit-form";
35 import { SiteForm } from "./site-form";
36 import { TaglineForm } from "./tagline-form";
37
38 interface AdminSettingsData {
39   bannedPersonsResponse: BannedPersonsResponse;
40   federatedInstancesResponse: GetFederatedInstancesResponse;
41 }
42
43 interface AdminSettingsState {
44   siteRes: GetSiteResponse;
45   instancesRes?: GetFederatedInstancesResponse;
46   banned: PersonView[];
47   loading: boolean;
48   leaveAdminTeamLoading: boolean;
49 }
50
51 export class AdminSettings extends Component<any, AdminSettingsState> {
52   private siteConfigTextAreaId = `site-config-${randomStr()}`;
53   private isoData = setIsoData<AdminSettingsData>(this.context);
54   private subscription?: Subscription;
55   state: AdminSettingsState = {
56     siteRes: this.isoData.site_res,
57     banned: [],
58     loading: true,
59     leaveAdminTeamLoading: false,
60   };
61
62   constructor(props: any, context: any) {
63     super(props, context);
64
65     this.parseMessage = this.parseMessage.bind(this);
66     this.subscription = wsSubscribe(this.parseMessage);
67
68     // Only fetch the data if coming from another route
69     if (this.isoData.path == this.context.router.route.match.url) {
70       const { bannedPersonsResponse, federatedInstancesResponse } =
71         this.isoData.routeData;
72
73       this.state = {
74         ...this.state,
75         banned: bannedPersonsResponse.banned,
76         instancesRes: federatedInstancesResponse,
77         loading: false,
78       };
79     } else {
80       let cAuth = myAuth();
81       if (cAuth) {
82         WebSocketService.Instance.send(
83           wsClient.getBannedPersons({
84             auth: cAuth,
85           })
86         );
87         WebSocketService.Instance.send(
88           wsClient.getFederatedInstances({ auth: cAuth })
89         );
90       }
91     }
92   }
93
94   static fetchInitialData({
95     auth,
96     client,
97   }: InitialFetchRequest): WithPromiseKeys<AdminSettingsData> {
98     return {
99       bannedPersonsResponse: client.getBannedPersons({ auth: auth as string }),
100       federatedInstancesResponse: client.getFederatedInstances({
101         auth: auth as string,
102       }) as Promise<GetFederatedInstancesResponse>,
103     };
104   }
105
106   componentDidMount() {
107     if (isBrowser()) {
108       var textarea: any = document.getElementById(this.siteConfigTextAreaId);
109       autosize(textarea);
110     }
111   }
112
113   componentWillUnmount() {
114     if (isBrowser()) {
115       this.subscription?.unsubscribe();
116     }
117   }
118
119   get documentTitle(): string {
120     return `${i18n.t("admin_settings")} - ${
121       this.state.siteRes.site_view.site.name
122     }`;
123   }
124
125   render() {
126     return (
127       <div className="container-lg">
128         <HtmlTags
129           title={this.documentTitle}
130           path={this.context.router.route.match.url}
131         />
132         {this.state.loading ? (
133           <h5>
134             <Spinner large />
135           </h5>
136         ) : (
137           <Tabs
138             tabs={[
139               {
140                 key: "site",
141                 label: i18n.t("site"),
142                 getNode: () => (
143                   <div className="row">
144                     <div className="col-12 col-md-6">
145                       <SiteForm
146                         siteRes={this.state.siteRes}
147                         instancesRes={this.state.instancesRes}
148                         showLocal={showLocal(this.isoData)}
149                       />
150                     </div>
151                     <div className="col-12 col-md-6">
152                       {this.admins()}
153                       {this.bannedUsers()}
154                     </div>
155                   </div>
156                 ),
157               },
158               {
159                 key: "rate_limiting",
160                 label: "Rate Limiting",
161                 getNode: () => (
162                   <RateLimitForm
163                     localSiteRateLimit={
164                       this.state.siteRes.site_view.local_site_rate_limit
165                     }
166                     applicationQuestion={
167                       this.state.siteRes.site_view.local_site
168                         .application_question
169                     }
170                   />
171                 ),
172               },
173               {
174                 key: "taglines",
175                 label: i18n.t("taglines"),
176                 getNode: () => (
177                   <div className="row">
178                     <TaglineForm siteRes={this.state.siteRes} />
179                   </div>
180                 ),
181               },
182               {
183                 key: "emojis",
184                 label: i18n.t("emojis"),
185                 getNode: () => (
186                   <div className="row">
187                     <EmojiForm />
188                   </div>
189                 ),
190               },
191             ]}
192           />
193         )}
194       </div>
195     );
196   }
197
198   admins() {
199     return (
200       <>
201         <h5>{capitalizeFirstLetter(i18n.t("admins"))}</h5>
202         <ul className="list-unstyled">
203           {this.state.siteRes.admins.map(admin => (
204             <li key={admin.person.id} className="list-inline-item">
205               <PersonListing person={admin.person} />
206             </li>
207           ))}
208         </ul>
209         {this.leaveAdmin()}
210       </>
211     );
212   }
213
214   leaveAdmin() {
215     return (
216       <button
217         onClick={linkEvent(this, this.handleLeaveAdminTeam)}
218         className="btn btn-danger mb-2"
219       >
220         {this.state.leaveAdminTeamLoading ? (
221           <Spinner />
222         ) : (
223           i18n.t("leave_admin_team")
224         )}
225       </button>
226     );
227   }
228
229   bannedUsers() {
230     return (
231       <>
232         <h5>{i18n.t("banned_users")}</h5>
233         <ul className="list-unstyled">
234           {this.state.banned.map(banned => (
235             <li key={banned.person.id} className="list-inline-item">
236               <PersonListing person={banned.person} />
237             </li>
238           ))}
239         </ul>
240       </>
241     );
242   }
243
244   handleLeaveAdminTeam(i: AdminSettings) {
245     let auth = myAuth();
246     if (auth) {
247       i.setState({ leaveAdminTeamLoading: true });
248       WebSocketService.Instance.send(wsClient.leaveAdmin({ auth }));
249     }
250   }
251
252   parseMessage(msg: any) {
253     let op = wsUserOp(msg);
254     console.log(msg);
255     if (msg.error) {
256       toast(i18n.t(msg.error), "danger");
257       this.context.router.history.push("/");
258       this.setState({ loading: false });
259       return;
260     } else if (op == UserOperation.EditSite) {
261       let data = wsJsonToRes<SiteResponse>(msg);
262       this.setState(s => ((s.siteRes.site_view = data.site_view), s));
263       toast(i18n.t("site_saved"));
264     } else if (op == UserOperation.GetBannedPersons) {
265       let data = wsJsonToRes<BannedPersonsResponse>(msg);
266       this.setState({ banned: data.banned, loading: false });
267     } else if (op == UserOperation.LeaveAdmin) {
268       let data = wsJsonToRes<GetSiteResponse>(msg);
269       this.setState(s => ((s.siteRes.site_view = data.site_view), s));
270       this.setState({ leaveAdminTeamLoading: false });
271       toast(i18n.t("left_admin_team"));
272       this.context.router.history.push("/");
273     } else if (op == UserOperation.GetFederatedInstances) {
274       let data = wsJsonToRes<GetFederatedInstancesResponse>(msg);
275       this.setState({ instancesRes: data });
276     }
277   }
278 }