]> Untitled Git - lemmy-ui.git/blob - src/shared/components/home/admin-settings.tsx
67a000c5df44e751575ac9d2490f6cb60715fe48
[lemmy-ui.git] / src / shared / components / home / admin-settings.tsx
1 import { None, Option, Some } from "@sniptt/monads";
2 import autosize from "autosize";
3 import { Component, linkEvent } from "inferno";
4 import {
5   BannedPersonsResponse,
6   GetBannedPersons,
7   GetSiteConfig,
8   GetSiteConfigResponse,
9   GetSiteResponse,
10   PersonViewSafe,
11   SaveSiteConfig,
12   SiteResponse,
13   toUndefined,
14   UserOperation,
15   wsJsonToRes,
16   wsUserOp,
17 } from "lemmy-js-client";
18 import { Subscription } from "rxjs";
19 import { i18n } from "../../i18next";
20 import { InitialFetchRequest } from "../../interfaces";
21 import { WebSocketService } from "../../services";
22 import {
23   auth,
24   capitalizeFirstLetter,
25   isBrowser,
26   randomStr,
27   setIsoData,
28   showLocal,
29   toast,
30   wsClient,
31   wsSubscribe,
32 } from "../../utils";
33 import { HtmlTags } from "../common/html-tags";
34 import { Spinner } from "../common/icon";
35 import { PersonListing } from "../person/person-listing";
36 import { SiteForm } from "./site-form";
37
38 interface AdminSettingsState {
39   siteRes: GetSiteResponse;
40   siteConfigRes: Option<GetSiteConfigResponse>;
41   siteConfigHjson: Option<string>;
42   banned: PersonViewSafe[];
43   loading: boolean;
44   siteConfigLoading: boolean;
45   leaveAdminTeamLoading: boolean;
46 }
47
48 export class AdminSettings extends Component<any, AdminSettingsState> {
49   private siteConfigTextAreaId = `site-config-${randomStr()}`;
50   private isoData = setIsoData(
51     this.context,
52     GetSiteConfigResponse,
53     BannedPersonsResponse
54   );
55   private subscription: Subscription;
56   private emptyState: AdminSettingsState = {
57     siteRes: this.isoData.site_res,
58     siteConfigHjson: None,
59     siteConfigRes: None,
60     banned: [],
61     loading: true,
62     siteConfigLoading: null,
63     leaveAdminTeamLoading: null,
64   };
65
66   constructor(props: any, context: any) {
67     super(props, context);
68
69     this.state = this.emptyState;
70
71     this.parseMessage = this.parseMessage.bind(this);
72     this.subscription = wsSubscribe(this.parseMessage);
73
74     // Only fetch the data if coming from another route
75     if (this.isoData.path == this.context.router.route.match.url) {
76       this.state.siteConfigRes = Some(
77         this.isoData.routeData[0] as GetSiteConfigResponse
78       );
79       this.state.siteConfigHjson = this.state.siteConfigRes.map(
80         s => s.config_hjson
81       );
82       this.state.banned = (
83         this.isoData.routeData[1] as BannedPersonsResponse
84       ).banned;
85       this.state.siteConfigLoading = false;
86       this.state.loading = false;
87     } else {
88       WebSocketService.Instance.send(
89         wsClient.getSiteConfig({
90           auth: auth().unwrap(),
91         })
92       );
93       WebSocketService.Instance.send(
94         wsClient.getBannedPersons({
95           auth: auth().unwrap(),
96         })
97       );
98     }
99   }
100
101   static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
102     let promises: Promise<any>[] = [];
103
104     let siteConfigForm = new GetSiteConfig({ auth: req.auth.unwrap() });
105     promises.push(req.client.getSiteConfig(siteConfigForm));
106
107     let bannedPersonsForm = new GetBannedPersons({ auth: req.auth.unwrap() });
108     promises.push(req.client.getBannedPersons(bannedPersonsForm));
109
110     return promises;
111   }
112
113   componentDidMount() {
114     if (isBrowser()) {
115       var textarea: any = document.getElementById(this.siteConfigTextAreaId);
116       autosize(textarea);
117     }
118   }
119
120   componentWillUnmount() {
121     if (isBrowser()) {
122       this.subscription.unsubscribe();
123     }
124   }
125
126   get documentTitle(): string {
127     return this.state.siteRes.site_view.match({
128       some: siteView => `${i18n.t("admin_settings")} - ${siteView.site.name}`,
129       none: "",
130     });
131   }
132
133   render() {
134     return (
135       <div class="container">
136         {this.state.loading ? (
137           <h5>
138             <Spinner large />
139           </h5>
140         ) : (
141           <div class="row">
142             <div class="col-12 col-md-6">
143               <HtmlTags
144                 title={this.documentTitle}
145                 path={this.context.router.route.match.url}
146                 description={None}
147                 image={None}
148               />
149               {this.state.siteRes.site_view.match({
150                 some: siteView => (
151                   <SiteForm
152                     site={Some(siteView.site)}
153                     showLocal={showLocal(this.isoData)}
154                   />
155                 ),
156                 none: <></>,
157               })}
158               {this.admins()}
159               {this.bannedUsers()}
160             </div>
161             <div class="col-12 col-md-6">{this.adminSettings()}</div>
162           </div>
163         )}
164       </div>
165     );
166   }
167
168   admins() {
169     return (
170       <>
171         <h5>{capitalizeFirstLetter(i18n.t("admins"))}</h5>
172         <ul class="list-unstyled">
173           {this.state.siteRes.admins.map(admin => (
174             <li class="list-inline-item">
175               <PersonListing person={admin.person} />
176             </li>
177           ))}
178         </ul>
179         {this.leaveAdmin()}
180       </>
181     );
182   }
183
184   leaveAdmin() {
185     return (
186       <button
187         onClick={linkEvent(this, this.handleLeaveAdminTeam)}
188         class="btn btn-danger mb-2"
189       >
190         {this.state.leaveAdminTeamLoading ? (
191           <Spinner />
192         ) : (
193           i18n.t("leave_admin_team")
194         )}
195       </button>
196     );
197   }
198
199   bannedUsers() {
200     return (
201       <>
202         <h5>{i18n.t("banned_users")}</h5>
203         <ul class="list-unstyled">
204           {this.state.banned.map(banned => (
205             <li class="list-inline-item">
206               <PersonListing person={banned.person} />
207             </li>
208           ))}
209         </ul>
210       </>
211     );
212   }
213
214   adminSettings() {
215     return (
216       <div>
217         <h5>{i18n.t("admin_settings")}</h5>
218         <form onSubmit={linkEvent(this, this.handleSiteConfigSubmit)}>
219           <div class="form-group row">
220             <label
221               class="col-12 col-form-label"
222               htmlFor={this.siteConfigTextAreaId}
223             >
224               {i18n.t("site_config")}
225             </label>
226             <div class="col-12">
227               <textarea
228                 id={this.siteConfigTextAreaId}
229                 value={toUndefined(this.state.siteConfigHjson)}
230                 onInput={linkEvent(this, this.handleSiteConfigHjsonChange)}
231                 class="form-control text-monospace"
232                 rows={3}
233               />
234             </div>
235           </div>
236           <div class="form-group row">
237             <div class="col-12">
238               <button type="submit" class="btn btn-secondary mr-2">
239                 {this.state.siteConfigLoading ? (
240                   <Spinner />
241                 ) : (
242                   capitalizeFirstLetter(i18n.t("save"))
243                 )}
244               </button>
245             </div>
246           </div>
247         </form>
248       </div>
249     );
250   }
251
252   handleSiteConfigSubmit(i: AdminSettings, event: any) {
253     event.preventDefault();
254     i.state.siteConfigLoading = true;
255     let form = new SaveSiteConfig({
256       config_hjson: toUndefined(i.state.siteConfigHjson),
257       auth: auth().unwrap(),
258     });
259     WebSocketService.Instance.send(wsClient.saveSiteConfig(form));
260     i.setState(i.state);
261   }
262
263   handleSiteConfigHjsonChange(i: AdminSettings, event: any) {
264     i.state.siteConfigHjson = event.target.value;
265     i.setState(i.state);
266   }
267
268   handleLeaveAdminTeam(i: AdminSettings) {
269     i.state.leaveAdminTeamLoading = true;
270     WebSocketService.Instance.send(
271       wsClient.leaveAdmin({ auth: auth().unwrap() })
272     );
273     i.setState(i.state);
274   }
275
276   parseMessage(msg: any) {
277     let op = wsUserOp(msg);
278     console.log(msg);
279     if (msg.error) {
280       toast(i18n.t(msg.error), "danger");
281       this.context.router.history.push("/");
282       this.state.loading = false;
283       this.setState(this.state);
284       return;
285     } else if (op == UserOperation.EditSite) {
286       let data = wsJsonToRes<SiteResponse>(msg, SiteResponse);
287       this.state.siteRes.site_view = Some(data.site_view);
288       this.setState(this.state);
289       toast(i18n.t("site_saved"));
290     } else if (op == UserOperation.GetBannedPersons) {
291       let data = wsJsonToRes<BannedPersonsResponse>(msg, BannedPersonsResponse);
292       this.state.banned = data.banned;
293       this.setState(this.state);
294     } else if (op == UserOperation.GetSiteConfig) {
295       let data = wsJsonToRes<GetSiteConfigResponse>(msg, GetSiteConfigResponse);
296       this.state.siteConfigRes = Some(data);
297       this.state.loading = false;
298       this.state.siteConfigHjson = this.state.siteConfigRes.map(
299         s => s.config_hjson
300       );
301       this.setState(this.state);
302       var textarea: any = document.getElementById(this.siteConfigTextAreaId);
303       autosize(textarea);
304     } else if (op == UserOperation.LeaveAdmin) {
305       let data = wsJsonToRes<GetSiteResponse>(msg, GetSiteResponse);
306       this.state.siteRes.site_view = data.site_view;
307       this.setState(this.state);
308       this.state.leaveAdminTeamLoading = false;
309       toast(i18n.t("left_admin_team"));
310       this.setState(this.state);
311       this.context.router.history.push("/");
312     } else if (op == UserOperation.SaveSiteConfig) {
313       let data = wsJsonToRes<GetSiteConfigResponse>(msg, GetSiteConfigResponse);
314       this.state.siteConfigRes = Some(data);
315       this.state.siteConfigHjson = this.state.siteConfigRes.map(
316         s => s.config_hjson
317       );
318       this.state.siteConfigLoading = false;
319       toast(i18n.t("site_saved"));
320       this.setState(this.state);
321     }
322   }
323 }