]> Untitled Git - lemmy-ui.git/blob - src/shared/components/home/site-form.tsx
address review comments
[lemmy-ui.git] / src / shared / components / home / site-form.tsx
1 import { Component, linkEvent } from "inferno";
2 import { Prompt } from "inferno-router";
3 import { CreateSite, EditSite, Site } from "lemmy-js-client";
4 import { i18n } from "../../i18next";
5 import { WebSocketService } from "../../services";
6 import {
7   authField,
8   capitalizeFirstLetter,
9   themes,
10   wsClient,
11 } from "../../utils";
12 import { Spinner } from "../common/icon";
13 import { ImageUploadForm } from "../common/image-upload-form";
14 import { MarkdownTextArea } from "../common/markdown-textarea";
15
16 interface SiteFormProps {
17   site?: Site; // If a site is given, that means this is an edit
18   onCancel?(): any;
19 }
20
21 interface SiteFormState {
22   siteForm: EditSite;
23   loading: boolean;
24 }
25
26 export class SiteForm extends Component<SiteFormProps, SiteFormState> {
27   private emptyState: SiteFormState = {
28     siteForm: {
29       enable_downvotes: true,
30       open_registration: true,
31       enable_nsfw: true,
32       name: null,
33       icon: null,
34       banner: null,
35       require_email_verification: null,
36       require_application: null,
37       application_question: null,
38       private_instance: null,
39       default_theme: null,
40       auth: authField(),
41     },
42     loading: false,
43   };
44
45   constructor(props: any, context: any) {
46     super(props, context);
47
48     this.state = this.emptyState;
49     this.handleSiteSidebarChange = this.handleSiteSidebarChange.bind(this);
50     this.handleSiteApplicationQuestionChange =
51       this.handleSiteApplicationQuestionChange.bind(this);
52
53     this.handleIconUpload = this.handleIconUpload.bind(this);
54     this.handleIconRemove = this.handleIconRemove.bind(this);
55
56     this.handleBannerUpload = this.handleBannerUpload.bind(this);
57     this.handleBannerRemove = this.handleBannerRemove.bind(this);
58
59     if (this.props.site) {
60       let site = this.props.site;
61       this.state.siteForm = {
62         name: site.name,
63         sidebar: site.sidebar,
64         description: site.description,
65         enable_downvotes: site.enable_downvotes,
66         open_registration: site.open_registration,
67         enable_nsfw: site.enable_nsfw,
68         community_creation_admin_only: site.community_creation_admin_only,
69         icon: site.icon,
70         banner: site.banner,
71         require_email_verification: site.require_email_verification,
72         require_application: site.require_application,
73         application_question: site.application_question,
74         private_instance: site.private_instance,
75         default_theme: site.default_theme,
76         auth: authField(),
77       };
78     }
79   }
80
81   // Necessary to stop the loading
82   componentWillReceiveProps() {
83     this.state.loading = false;
84     this.setState(this.state);
85   }
86
87   componentDidUpdate() {
88     if (
89       !this.state.loading &&
90       !this.props.site &&
91       (this.state.siteForm.name ||
92         this.state.siteForm.sidebar ||
93         this.state.siteForm.application_question ||
94         this.state.siteForm.description)
95     ) {
96       window.onbeforeunload = () => true;
97     } else {
98       window.onbeforeunload = undefined;
99     }
100   }
101
102   componentWillUnmount() {
103     window.onbeforeunload = null;
104   }
105
106   render() {
107     return (
108       <>
109         <Prompt
110           when={
111             !this.state.loading &&
112             !this.props.site &&
113             (this.state.siteForm.name ||
114               this.state.siteForm.sidebar ||
115               this.state.siteForm.application_question ||
116               this.state.siteForm.description)
117           }
118           message={i18n.t("block_leaving")}
119         />
120         <form onSubmit={linkEvent(this, this.handleCreateSiteSubmit)}>
121           <h5>{`${
122             this.props.site
123               ? capitalizeFirstLetter(i18n.t("save"))
124               : capitalizeFirstLetter(i18n.t("name"))
125           } ${i18n.t("your_site")}`}</h5>
126           <div class="form-group row">
127             <label class="col-12 col-form-label" htmlFor="create-site-name">
128               {i18n.t("name")}
129             </label>
130             <div class="col-12">
131               <input
132                 type="text"
133                 id="create-site-name"
134                 class="form-control"
135                 value={this.state.siteForm.name}
136                 onInput={linkEvent(this, this.handleSiteNameChange)}
137                 required
138                 minLength={3}
139                 maxLength={20}
140               />
141             </div>
142           </div>
143           <div class="form-group">
144             <label>{i18n.t("icon")}</label>
145             <ImageUploadForm
146               uploadTitle={i18n.t("upload_icon")}
147               imageSrc={this.state.siteForm.icon}
148               onUpload={this.handleIconUpload}
149               onRemove={this.handleIconRemove}
150               rounded
151             />
152           </div>
153           <div class="form-group">
154             <label>{i18n.t("banner")}</label>
155             <ImageUploadForm
156               uploadTitle={i18n.t("upload_banner")}
157               imageSrc={this.state.siteForm.banner}
158               onUpload={this.handleBannerUpload}
159               onRemove={this.handleBannerRemove}
160             />
161           </div>
162           <div class="form-group row">
163             <label class="col-12 col-form-label" htmlFor="site-desc">
164               {i18n.t("description")}
165             </label>
166             <div class="col-12">
167               <input
168                 type="text"
169                 class="form-control"
170                 id="site-desc"
171                 value={this.state.siteForm.description}
172                 onInput={linkEvent(this, this.handleSiteDescChange)}
173                 maxLength={150}
174               />
175             </div>
176           </div>
177           <div class="form-group row">
178             <label class="col-12 col-form-label">{i18n.t("sidebar")}</label>
179             <div class="col-12">
180               <MarkdownTextArea
181                 initialContent={this.state.siteForm.sidebar}
182                 onContentChange={this.handleSiteSidebarChange}
183                 hideNavigationWarnings
184               />
185             </div>
186           </div>
187           {this.state.siteForm.require_application && (
188             <div class="form-group row">
189               <label class="col-12 col-form-label">
190                 {i18n.t("application_questionnaire")}
191               </label>
192               <div class="col-12">
193                 <MarkdownTextArea
194                   initialContent={this.state.siteForm.application_question}
195                   onContentChange={this.handleSiteApplicationQuestionChange}
196                   hideNavigationWarnings
197                 />
198               </div>
199             </div>
200           )}
201           <div class="form-group row">
202             <div class="col-12">
203               <div class="form-check">
204                 <input
205                   class="form-check-input"
206                   id="create-site-downvotes"
207                   type="checkbox"
208                   checked={this.state.siteForm.enable_downvotes}
209                   onChange={linkEvent(
210                     this,
211                     this.handleSiteEnableDownvotesChange
212                   )}
213                 />
214                 <label class="form-check-label" htmlFor="create-site-downvotes">
215                   {i18n.t("enable_downvotes")}
216                 </label>
217               </div>
218             </div>
219           </div>
220           <div class="form-group row">
221             <div class="col-12">
222               <div class="form-check">
223                 <input
224                   class="form-check-input"
225                   id="create-site-enable-nsfw"
226                   type="checkbox"
227                   checked={this.state.siteForm.enable_nsfw}
228                   onChange={linkEvent(this, this.handleSiteEnableNsfwChange)}
229                 />
230                 <label
231                   class="form-check-label"
232                   htmlFor="create-site-enable-nsfw"
233                 >
234                   {i18n.t("enable_nsfw")}
235                 </label>
236               </div>
237             </div>
238           </div>
239           <div class="form-group row">
240             <div class="col-12">
241               <div class="form-check">
242                 <input
243                   class="form-check-input"
244                   id="create-site-open-registration"
245                   type="checkbox"
246                   checked={this.state.siteForm.open_registration}
247                   onChange={linkEvent(
248                     this,
249                     this.handleSiteOpenRegistrationChange
250                   )}
251                 />
252                 <label
253                   class="form-check-label"
254                   htmlFor="create-site-open-registration"
255                 >
256                   {i18n.t("open_registration")}
257                 </label>
258               </div>
259             </div>
260           </div>
261           <div class="form-group row">
262             <div class="col-12">
263               <div class="form-check">
264                 <input
265                   class="form-check-input"
266                   id="create-site-community-creation-admin-only"
267                   type="checkbox"
268                   checked={this.state.siteForm.community_creation_admin_only}
269                   onChange={linkEvent(
270                     this,
271                     this.handleSiteCommunityCreationAdminOnly
272                   )}
273                 />
274                 <label
275                   class="form-check-label"
276                   htmlFor="create-site-community-creation-admin-only"
277                 >
278                   {i18n.t("community_creation_admin_only")}
279                 </label>
280               </div>
281             </div>
282           </div>
283           <div class="form-group row">
284             <div class="col-12">
285               <div class="form-check">
286                 <input
287                   class="form-check-input"
288                   id="create-site-require-email-verification"
289                   type="checkbox"
290                   checked={this.state.siteForm.require_email_verification}
291                   onChange={linkEvent(
292                     this,
293                     this.handleSiteRequireEmailVerification
294                   )}
295                 />
296                 <label
297                   class="form-check-label"
298                   htmlFor="create-site-require-email-verification"
299                 >
300                   {i18n.t("require_email_verification")}
301                 </label>
302               </div>
303             </div>
304           </div>
305           <div class="form-group row">
306             <div class="col-12">
307               <div class="form-check">
308                 <input
309                   class="form-check-input"
310                   id="create-site-require-application"
311                   type="checkbox"
312                   checked={this.state.siteForm.require_application}
313                   onChange={linkEvent(this, this.handleSiteRequireApplication)}
314                 />
315                 <label
316                   class="form-check-label"
317                   htmlFor="create-site-require-application"
318                 >
319                   {i18n.t("require_registration_application")}
320                 </label>
321               </div>
322             </div>
323           </div>
324           <div class="form-group row">
325             <div class="col-12">
326               <label
327                 class="form-check-label mr-2"
328                 htmlFor="create-site-default-theme"
329               >
330                 {i18n.t("theme")}
331               </label>
332               <select
333                 id="create-site-default-theme"
334                 value={this.state.siteForm.default_theme}
335                 onChange={linkEvent(this, this.handleSiteDefaultTheme)}
336                 class="custom-select w-auto"
337               >
338                 <option value="browser">{i18n.t("browser_default")}</option>
339                 {themes.map(theme => (
340                   <option value={theme}>{theme}</option>
341                 ))}
342               </select>
343             </div>
344           </div>
345           <div class="form-group row">
346             <div class="col-12">
347               <div class="form-check">
348                 <input
349                   class="form-check-input"
350                   id="create-site-private-instance"
351                   type="checkbox"
352                   value={this.state.siteForm.default_theme}
353                   onChange={linkEvent(this, this.handleSitePrivateInstance)}
354                 />
355                 <label
356                   class="form-check-label"
357                   htmlFor="create-site-private-instance"
358                 >
359                   {i18n.t("private_instance")}
360                 </label>
361               </div>
362             </div>
363           </div>
364           <div class="form-group row">
365             <div class="col-12">
366               <button
367                 type="submit"
368                 class="btn btn-secondary mr-2"
369                 disabled={this.state.loading}
370               >
371                 {this.state.loading ? (
372                   <Spinner />
373                 ) : this.props.site ? (
374                   capitalizeFirstLetter(i18n.t("save"))
375                 ) : (
376                   capitalizeFirstLetter(i18n.t("create"))
377                 )}
378               </button>
379               {this.props.site && (
380                 <button
381                   type="button"
382                   class="btn btn-secondary"
383                   onClick={linkEvent(this, this.handleCancel)}
384                 >
385                   {i18n.t("cancel")}
386                 </button>
387               )}
388             </div>
389           </div>
390         </form>
391       </>
392     );
393   }
394
395   handleCreateSiteSubmit(i: SiteForm, event: any) {
396     event.preventDefault();
397     i.state.loading = true;
398     if (i.props.site) {
399       WebSocketService.Instance.send(wsClient.editSite(i.state.siteForm));
400     } else {
401       let form: CreateSite = {
402         name: i.state.siteForm.name || "My site",
403         ...i.state.siteForm,
404       };
405       WebSocketService.Instance.send(wsClient.createSite(form));
406     }
407     i.setState(i.state);
408   }
409
410   handleSiteNameChange(i: SiteForm, event: any) {
411     i.state.siteForm.name = event.target.value;
412     i.setState(i.state);
413   }
414
415   handleSiteSidebarChange(val: string) {
416     this.state.siteForm.sidebar = val;
417     this.setState(this.state);
418   }
419
420   handleSiteApplicationQuestionChange(val: string) {
421     this.state.siteForm.application_question = val;
422     this.setState(this.state);
423   }
424
425   handleSiteDescChange(i: SiteForm, event: any) {
426     i.state.siteForm.description = event.target.value;
427     i.setState(i.state);
428   }
429
430   handleSiteEnableNsfwChange(i: SiteForm, event: any) {
431     i.state.siteForm.enable_nsfw = event.target.checked;
432     i.setState(i.state);
433   }
434
435   handleSiteOpenRegistrationChange(i: SiteForm, event: any) {
436     i.state.siteForm.open_registration = event.target.checked;
437     i.setState(i.state);
438   }
439
440   handleSiteCommunityCreationAdminOnly(i: SiteForm, event: any) {
441     i.state.siteForm.community_creation_admin_only = event.target.checked;
442     i.setState(i.state);
443   }
444
445   handleSiteEnableDownvotesChange(i: SiteForm, event: any) {
446     i.state.siteForm.enable_downvotes = event.target.checked;
447     i.setState(i.state);
448   }
449
450   handleSiteRequireApplication(i: SiteForm, event: any) {
451     i.state.siteForm.require_application = event.target.checked;
452     i.setState(i.state);
453   }
454
455   handleSiteRequireEmailVerification(i: SiteForm, event: any) {
456     i.state.siteForm.require_email_verification = event.target.checked;
457     i.setState(i.state);
458   }
459
460   handleSitePrivateInstance(i: SiteForm, event: any) {
461     i.state.siteForm.private_instance = event.target.checked;
462     i.setState(i.state);
463   }
464
465   handleSiteDefaultTheme(i: SiteForm, event: any) {
466     i.state.siteForm.default_theme = event.target.value;
467     i.setState(i.state);
468   }
469
470   handleCancel(i: SiteForm) {
471     i.props.onCancel();
472   }
473
474   handleIconUpload(url: string) {
475     this.state.siteForm.icon = url;
476     this.setState(this.state);
477   }
478
479   handleIconRemove() {
480     this.state.siteForm.icon = "";
481     this.setState(this.state);
482   }
483
484   handleBannerUpload(url: string) {
485     this.state.siteForm.banner = url;
486     this.setState(this.state);
487   }
488
489   handleBannerRemove() {
490     this.state.siteForm.banner = "";
491     this.setState(this.state);
492   }
493 }