]> Untitled Git - lemmy.git/blob - ui/src/components/admin-settings.tsx
Merge remote-tracking branch 'weblate/main' into main
[lemmy.git] / ui / src / components / admin-settings.tsx
1 import { Component, linkEvent } from 'inferno';
2 import { Helmet } from 'inferno-helmet';
3 import { Subscription } from 'rxjs';
4 import { retryWhen, delay, take } from 'rxjs/operators';
5 import {
6   UserOperation,
7   SiteResponse,
8   GetSiteResponse,
9   SiteConfigForm,
10   GetSiteConfigResponse,
11   WebSocketJsonResponse,
12 } from '../interfaces';
13 import { WebSocketService } from '../services';
14 import { wsJsonToRes, capitalizeFirstLetter, toast, randomStr } from '../utils';
15 import autosize from 'autosize';
16 import { SiteForm } from './site-form';
17 import { UserListing } from './user-listing';
18 import { i18n } from '../i18next';
19
20 interface AdminSettingsState {
21   siteRes: GetSiteResponse;
22   siteConfigRes: GetSiteConfigResponse;
23   siteConfigForm: SiteConfigForm;
24   loading: boolean;
25   siteConfigLoading: boolean;
26 }
27
28 export class AdminSettings extends Component<any, AdminSettingsState> {
29   private siteConfigTextAreaId = `site-config-${randomStr()}`;
30   private subscription: Subscription;
31   private emptyState: AdminSettingsState = {
32     siteRes: {
33       site: {
34         id: null,
35         name: null,
36         creator_id: null,
37         creator_name: null,
38         published: null,
39         number_of_users: null,
40         number_of_posts: null,
41         number_of_comments: null,
42         number_of_communities: null,
43         enable_downvotes: null,
44         open_registration: null,
45         enable_nsfw: null,
46       },
47       admins: [],
48       banned: [],
49       online: null,
50       version: null,
51     },
52     siteConfigForm: {
53       config_hjson: null,
54       auth: null,
55     },
56     siteConfigRes: {
57       config_hjson: null,
58     },
59     loading: true,
60     siteConfigLoading: null,
61   };
62
63   constructor(props: any, context: any) {
64     super(props, context);
65
66     this.state = this.emptyState;
67
68     this.subscription = WebSocketService.Instance.subject
69       .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
70       .subscribe(
71         msg => this.parseMessage(msg),
72         err => console.error(err),
73         () => console.log('complete')
74       );
75
76     WebSocketService.Instance.getSite();
77     WebSocketService.Instance.getSiteConfig();
78   }
79
80   componentWillUnmount() {
81     this.subscription.unsubscribe();
82   }
83
84   get documentTitle(): string {
85     if (this.state.siteRes.site.name) {
86       return `${i18n.t('admin_settings')} - ${this.state.siteRes.site.name}`;
87     } else {
88       return 'Lemmy';
89     }
90   }
91
92   render() {
93     return (
94       <div class="container">
95         <Helmet title={this.documentTitle} />
96         {this.state.loading ? (
97           <h5>
98             <svg class="icon icon-spinner spin">
99               <use xlinkHref="#icon-spinner"></use>
100             </svg>
101           </h5>
102         ) : (
103           <div class="row">
104             <div class="col-12 col-md-6">
105               {this.state.siteRes.site.id && (
106                 <SiteForm site={this.state.siteRes.site} />
107               )}
108               {this.admins()}
109               {this.bannedUsers()}
110             </div>
111             <div class="col-12 col-md-6">{this.adminSettings()}</div>
112           </div>
113         )}
114       </div>
115     );
116   }
117
118   admins() {
119     return (
120       <>
121         <h5>{capitalizeFirstLetter(i18n.t('admins'))}</h5>
122         <ul class="list-unstyled">
123           {this.state.siteRes.admins.map(admin => (
124             <li class="list-inline-item">
125               <UserListing
126                 user={{
127                   name: admin.name,
128                   preferred_username: admin.preferred_username,
129                   avatar: admin.avatar,
130                   id: admin.id,
131                   local: admin.local,
132                   actor_id: admin.actor_id,
133                 }}
134               />
135             </li>
136           ))}
137         </ul>
138       </>
139     );
140   }
141
142   bannedUsers() {
143     return (
144       <>
145         <h5>{i18n.t('banned_users')}</h5>
146         <ul class="list-unstyled">
147           {this.state.siteRes.banned.map(banned => (
148             <li class="list-inline-item">
149               <UserListing
150                 user={{
151                   name: banned.name,
152                   preferred_username: banned.preferred_username,
153                   avatar: banned.avatar,
154                   id: banned.id,
155                   local: banned.local,
156                   actor_id: banned.actor_id,
157                 }}
158               />
159             </li>
160           ))}
161         </ul>
162       </>
163     );
164   }
165
166   adminSettings() {
167     return (
168       <div>
169         <h5>{i18n.t('admin_settings')}</h5>
170         <form onSubmit={linkEvent(this, this.handleSiteConfigSubmit)}>
171           <div class="form-group row">
172             <label
173               class="col-12 col-form-label"
174               htmlFor={this.siteConfigTextAreaId}
175             >
176               {i18n.t('site_config')}
177             </label>
178             <div class="col-12">
179               <textarea
180                 id={this.siteConfigTextAreaId}
181                 value={this.state.siteConfigForm.config_hjson}
182                 onInput={linkEvent(this, this.handleSiteConfigHjsonChange)}
183                 class="form-control text-monospace"
184                 rows={3}
185               />
186             </div>
187           </div>
188           <div class="form-group row">
189             <div class="col-12">
190               <button type="submit" class="btn btn-secondary mr-2">
191                 {this.state.siteConfigLoading ? (
192                   <svg class="icon icon-spinner spin">
193                     <use xlinkHref="#icon-spinner"></use>
194                   </svg>
195                 ) : (
196                   capitalizeFirstLetter(i18n.t('save'))
197                 )}
198               </button>
199             </div>
200           </div>
201         </form>
202       </div>
203     );
204   }
205
206   handleSiteConfigSubmit(i: AdminSettings, event: any) {
207     event.preventDefault();
208     i.state.siteConfigLoading = true;
209     WebSocketService.Instance.saveSiteConfig(i.state.siteConfigForm);
210     i.setState(i.state);
211   }
212
213   handleSiteConfigHjsonChange(i: AdminSettings, event: any) {
214     i.state.siteConfigForm.config_hjson = event.target.value;
215     i.setState(i.state);
216   }
217
218   parseMessage(msg: WebSocketJsonResponse) {
219     console.log(msg);
220     let res = wsJsonToRes(msg);
221     if (msg.error) {
222       toast(i18n.t(msg.error), 'danger');
223       this.context.router.history.push('/');
224       this.state.loading = false;
225       this.setState(this.state);
226       return;
227     } else if (msg.reconnect) {
228     } else if (res.op == UserOperation.GetSite) {
229       let data = res.data as GetSiteResponse;
230
231       // This means it hasn't been set up yet
232       if (!data.site) {
233         this.context.router.history.push('/setup');
234       }
235       this.state.siteRes = data;
236       this.setState(this.state);
237     } else if (res.op == UserOperation.EditSite) {
238       let data = res.data as SiteResponse;
239       this.state.siteRes.site = data.site;
240       this.setState(this.state);
241       toast(i18n.t('site_saved'));
242     } else if (res.op == UserOperation.GetSiteConfig) {
243       let data = res.data as GetSiteConfigResponse;
244       this.state.siteConfigRes = data;
245       this.state.loading = false;
246       this.state.siteConfigForm.config_hjson = this.state.siteConfigRes.config_hjson;
247       this.setState(this.state);
248       var textarea: any = document.getElementById(this.siteConfigTextAreaId);
249       autosize(textarea);
250     } else if (res.op == UserOperation.SaveSiteConfig) {
251       let data = res.data as GetSiteConfigResponse;
252       this.state.siteConfigRes = data;
253       this.state.siteConfigForm.config_hjson = this.state.siteConfigRes.config_hjson;
254       this.state.siteConfigLoading = false;
255       toast(i18n.t('site_saved'));
256       this.setState(this.state);
257     }
258   }
259 }