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