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