]> Untitled Git - lemmy-ui.git/blob - src/shared/components/home/site-form.tsx
Hide create community (#787)
[lemmy-ui.git] / src / shared / components / home / site-form.tsx
1 import { None, Option, Some } from "@sniptt/monads";
2 import { Component, linkEvent } from "inferno";
3 import { Prompt } from "inferno-router";
4 import {
5   CreateSite,
6   EditSite,
7   ListingType,
8   Site,
9   toUndefined,
10 } from "lemmy-js-client";
11 import { i18n } from "../../i18next";
12 import { WebSocketService } from "../../services";
13 import {
14   auth,
15   capitalizeFirstLetter,
16   fetchThemeList,
17   wsClient,
18 } from "../../utils";
19 import { Spinner } from "../common/icon";
20 import { ImageUploadForm } from "../common/image-upload-form";
21 import { ListingTypeSelect } from "../common/listing-type-select";
22 import { MarkdownTextArea } from "../common/markdown-textarea";
23
24 interface SiteFormProps {
25   site: Option<Site>; // If a site is given, that means this is an edit
26   showLocal?: boolean;
27   onCancel?(): void;
28   onEdit?(): void;
29 }
30
31 interface SiteFormState {
32   siteForm: EditSite;
33   loading: boolean;
34   themeList: Option<string[]>;
35 }
36
37 export class SiteForm extends Component<SiteFormProps, SiteFormState> {
38   private emptyState: SiteFormState = {
39     siteForm: new EditSite({
40       enable_downvotes: Some(true),
41       open_registration: Some(true),
42       enable_nsfw: Some(true),
43       name: None,
44       icon: None,
45       banner: None,
46       require_email_verification: None,
47       require_application: None,
48       application_question: None,
49       private_instance: None,
50       default_theme: None,
51       sidebar: None,
52       default_post_listing_type: None,
53       legal_information: None,
54       description: None,
55       community_creation_admin_only: None,
56       auth: undefined,
57       hide_modlog_mod_names: Some(true),
58     }),
59     loading: false,
60     themeList: None,
61   };
62
63   constructor(props: any, context: any) {
64     super(props, context);
65
66     this.state = this.emptyState;
67     this.handleSiteSidebarChange = this.handleSiteSidebarChange.bind(this);
68     this.handleSiteLegalInfoChange = this.handleSiteLegalInfoChange.bind(this);
69     this.handleSiteApplicationQuestionChange =
70       this.handleSiteApplicationQuestionChange.bind(this);
71
72     this.handleIconUpload = this.handleIconUpload.bind(this);
73     this.handleIconRemove = this.handleIconRemove.bind(this);
74
75     this.handleBannerUpload = this.handleBannerUpload.bind(this);
76     this.handleBannerRemove = this.handleBannerRemove.bind(this);
77
78     this.handleDefaultPostListingTypeChange =
79       this.handleDefaultPostListingTypeChange.bind(this);
80
81     if (this.props.site.isSome()) {
82       let site = this.props.site.unwrap();
83       this.state = {
84         ...this.state,
85         siteForm: new EditSite({
86           name: Some(site.name),
87           sidebar: site.sidebar,
88           description: site.description,
89           enable_downvotes: Some(site.enable_downvotes),
90           open_registration: Some(site.open_registration),
91           enable_nsfw: Some(site.enable_nsfw),
92           community_creation_admin_only: Some(
93             site.community_creation_admin_only
94           ),
95           icon: site.icon,
96           banner: site.banner,
97           require_email_verification: Some(site.require_email_verification),
98           require_application: Some(site.require_application),
99           application_question: site.application_question,
100           private_instance: Some(site.private_instance),
101           default_theme: Some(site.default_theme),
102           default_post_listing_type: Some(site.default_post_listing_type),
103           legal_information: site.legal_information,
104           hide_modlog_mod_names: site.hide_modlog_mod_names,
105           auth: undefined,
106         }),
107       };
108     }
109   }
110
111   async componentDidMount() {
112     this.setState({ themeList: Some(await fetchThemeList()) });
113   }
114
115   // Necessary to stop the loading
116   componentWillReceiveProps() {
117     this.setState({ loading: false });
118   }
119
120   componentDidUpdate() {
121     if (
122       !this.state.loading &&
123       this.props.site.isNone() &&
124       (this.state.siteForm.name ||
125         this.state.siteForm.sidebar ||
126         this.state.siteForm.application_question ||
127         this.state.siteForm.description)
128     ) {
129       window.onbeforeunload = () => true;
130     } else {
131       window.onbeforeunload = undefined;
132     }
133   }
134
135   componentWillUnmount() {
136     window.onbeforeunload = null;
137   }
138
139   render() {
140     return (
141       <>
142         <Prompt
143           when={
144             !this.state.loading &&
145             this.props.site.isNone() &&
146             (this.state.siteForm.name ||
147               this.state.siteForm.sidebar ||
148               this.state.siteForm.application_question ||
149               this.state.siteForm.description)
150           }
151           message={i18n.t("block_leaving")}
152         />
153         <form onSubmit={linkEvent(this, this.handleCreateSiteSubmit)}>
154           <h5>{`${
155             this.props.site.isSome()
156               ? capitalizeFirstLetter(i18n.t("save"))
157               : capitalizeFirstLetter(i18n.t("name"))
158           } ${i18n.t("your_site")}`}</h5>
159           <div className="form-group row">
160             <label className="col-12 col-form-label" htmlFor="create-site-name">
161               {i18n.t("name")}
162             </label>
163             <div className="col-12">
164               <input
165                 type="text"
166                 id="create-site-name"
167                 className="form-control"
168                 value={toUndefined(this.state.siteForm.name)}
169                 onInput={linkEvent(this, this.handleSiteNameChange)}
170                 required
171                 minLength={3}
172                 maxLength={20}
173               />
174             </div>
175           </div>
176           <div className="form-group">
177             <label>{i18n.t("icon")}</label>
178             <ImageUploadForm
179               uploadTitle={i18n.t("upload_icon")}
180               imageSrc={this.state.siteForm.icon}
181               onUpload={this.handleIconUpload}
182               onRemove={this.handleIconRemove}
183               rounded
184             />
185           </div>
186           <div className="form-group">
187             <label>{i18n.t("banner")}</label>
188             <ImageUploadForm
189               uploadTitle={i18n.t("upload_banner")}
190               imageSrc={this.state.siteForm.banner}
191               onUpload={this.handleBannerUpload}
192               onRemove={this.handleBannerRemove}
193             />
194           </div>
195           <div className="form-group row">
196             <label className="col-12 col-form-label" htmlFor="site-desc">
197               {i18n.t("description")}
198             </label>
199             <div className="col-12">
200               <input
201                 type="text"
202                 className="form-control"
203                 id="site-desc"
204                 value={toUndefined(this.state.siteForm.description)}
205                 onInput={linkEvent(this, this.handleSiteDescChange)}
206                 maxLength={150}
207               />
208             </div>
209           </div>
210           <div className="form-group row">
211             <label className="col-12 col-form-label">{i18n.t("sidebar")}</label>
212             <div className="col-12">
213               <MarkdownTextArea
214                 initialContent={this.state.siteForm.sidebar}
215                 initialLanguageId={None}
216                 placeholder={None}
217                 buttonTitle={None}
218                 maxLength={None}
219                 onContentChange={this.handleSiteSidebarChange}
220                 hideNavigationWarnings
221                 allLanguages={[]}
222               />
223             </div>
224           </div>
225           <div className="form-group row">
226             <label className="col-12 col-form-label">
227               {i18n.t("legal_information")}
228             </label>
229             <div className="col-12">
230               <MarkdownTextArea
231                 initialContent={this.state.siteForm.legal_information}
232                 initialLanguageId={None}
233                 placeholder={None}
234                 buttonTitle={None}
235                 maxLength={None}
236                 onContentChange={this.handleSiteLegalInfoChange}
237                 hideNavigationWarnings
238                 allLanguages={[]}
239               />
240             </div>
241           </div>
242           {this.state.siteForm.require_application.unwrapOr(false) && (
243             <div className="form-group row">
244               <label className="col-12 col-form-label">
245                 {i18n.t("application_questionnaire")}
246               </label>
247               <div className="col-12">
248                 <MarkdownTextArea
249                   initialContent={this.state.siteForm.application_question}
250                   initialLanguageId={None}
251                   placeholder={None}
252                   buttonTitle={None}
253                   maxLength={None}
254                   onContentChange={this.handleSiteApplicationQuestionChange}
255                   hideNavigationWarnings
256                   allLanguages={[]}
257                 />
258               </div>
259             </div>
260           )}
261           <div className="form-group row">
262             <div className="col-12">
263               <div className="form-check">
264                 <input
265                   className="form-check-input"
266                   id="create-site-downvotes"
267                   type="checkbox"
268                   checked={toUndefined(this.state.siteForm.enable_downvotes)}
269                   onChange={linkEvent(
270                     this,
271                     this.handleSiteEnableDownvotesChange
272                   )}
273                 />
274                 <label
275                   className="form-check-label"
276                   htmlFor="create-site-downvotes"
277                 >
278                   {i18n.t("enable_downvotes")}
279                 </label>
280               </div>
281             </div>
282           </div>
283           <div className="form-group row">
284             <div className="col-12">
285               <div className="form-check">
286                 <input
287                   className="form-check-input"
288                   id="create-site-enable-nsfw"
289                   type="checkbox"
290                   checked={toUndefined(this.state.siteForm.enable_nsfw)}
291                   onChange={linkEvent(this, this.handleSiteEnableNsfwChange)}
292                 />
293                 <label
294                   className="form-check-label"
295                   htmlFor="create-site-enable-nsfw"
296                 >
297                   {i18n.t("enable_nsfw")}
298                 </label>
299               </div>
300             </div>
301           </div>
302           <div className="form-group row">
303             <div className="col-12">
304               <div className="form-check">
305                 <input
306                   className="form-check-input"
307                   id="create-site-open-registration"
308                   type="checkbox"
309                   checked={toUndefined(this.state.siteForm.open_registration)}
310                   onChange={linkEvent(
311                     this,
312                     this.handleSiteOpenRegistrationChange
313                   )}
314                 />
315                 <label
316                   className="form-check-label"
317                   htmlFor="create-site-open-registration"
318                 >
319                   {i18n.t("open_registration")}
320                 </label>
321               </div>
322             </div>
323           </div>
324           <div className="form-group row">
325             <div className="col-12">
326               <div className="form-check">
327                 <input
328                   className="form-check-input"
329                   id="create-site-community-creation-admin-only"
330                   type="checkbox"
331                   checked={toUndefined(
332                     this.state.siteForm.community_creation_admin_only
333                   )}
334                   onChange={linkEvent(
335                     this,
336                     this.handleSiteCommunityCreationAdminOnly
337                   )}
338                 />
339                 <label
340                   className="form-check-label"
341                   htmlFor="create-site-community-creation-admin-only"
342                 >
343                   {i18n.t("community_creation_admin_only")}
344                 </label>
345               </div>
346             </div>
347           </div>
348           <div className="form-group row">
349             <div className="col-12">
350               <div className="form-check">
351                 <input
352                   className="form-check-input"
353                   id="create-site-require-email-verification"
354                   type="checkbox"
355                   checked={toUndefined(
356                     this.state.siteForm.require_email_verification
357                   )}
358                   onChange={linkEvent(
359                     this,
360                     this.handleSiteRequireEmailVerification
361                   )}
362                 />
363                 <label
364                   className="form-check-label"
365                   htmlFor="create-site-require-email-verification"
366                 >
367                   {i18n.t("require_email_verification")}
368                 </label>
369               </div>
370             </div>
371           </div>
372           <div className="form-group row">
373             <div className="col-12">
374               <div className="form-check">
375                 <input
376                   className="form-check-input"
377                   id="create-site-require-application"
378                   type="checkbox"
379                   checked={toUndefined(this.state.siteForm.require_application)}
380                   onChange={linkEvent(this, this.handleSiteRequireApplication)}
381                 />
382                 <label
383                   className="form-check-label"
384                   htmlFor="create-site-require-application"
385                 >
386                   {i18n.t("require_registration_application")}
387                 </label>
388               </div>
389             </div>
390           </div>
391           <div className="form-group row">
392             <div className="col-12">
393               <label
394                 className="form-check-label mr-2"
395                 htmlFor="create-site-default-theme"
396               >
397                 {i18n.t("theme")}
398               </label>
399               <select
400                 id="create-site-default-theme"
401                 value={toUndefined(this.state.siteForm.default_theme)}
402                 onChange={linkEvent(this, this.handleSiteDefaultTheme)}
403                 className="custom-select w-auto"
404               >
405                 <option value="browser">{i18n.t("browser_default")}</option>
406                 {this.state.themeList.unwrapOr([]).map(theme => (
407                   <option key={theme} value={theme}>
408                     {theme}
409                   </option>
410                 ))}
411               </select>
412             </div>
413           </div>
414           {this.props.showLocal && (
415             <form className="form-group row">
416               <label className="col-sm-3">{i18n.t("listing_type")}</label>
417               <div className="col-sm-9">
418                 <ListingTypeSelect
419                   type_={
420                     ListingType[
421                       this.state.siteForm.default_post_listing_type.unwrapOr(
422                         "Local"
423                       )
424                     ]
425                   }
426                   showLocal
427                   showSubscribed={false}
428                   onChange={this.handleDefaultPostListingTypeChange}
429                 />
430               </div>
431             </form>
432           )}
433           <div className="form-group row">
434             <div className="col-12">
435               <div className="form-check">
436                 <input
437                   className="form-check-input"
438                   id="create-site-private-instance"
439                   type="checkbox"
440                   checked={toUndefined(this.state.siteForm.private_instance)}
441                   onChange={linkEvent(this, this.handleSitePrivateInstance)}
442                 />
443                 <label
444                   className="form-check-label"
445                   htmlFor="create-site-private-instance"
446                 >
447                   {i18n.t("private_instance")}
448                 </label>
449               </div>
450             </div>
451           </div>
452           <div className="form-group row">
453             <div className="col-12">
454               <div className="form-check">
455                 <input
456                   className="form-check-input"
457                   id="create-site-hide-modlog-mod-names"
458                   type="checkbox"
459                   checked={toUndefined(
460                     this.state.siteForm.hide_modlog_mod_names
461                   )}
462                   onChange={linkEvent(this, this.handleSiteHideModlogModNames)}
463                 />
464                 <label
465                   className="form-check-label"
466                   htmlFor="create-site-hide-modlog-mod-names"
467                 >
468                   {i18n.t("hide_modlog_mod_names")}
469                 </label>
470               </div>
471             </div>
472           </div>
473           <div className="form-group row">
474             <div className="col-12">
475               <button
476                 type="submit"
477                 className="btn btn-secondary mr-2"
478                 disabled={this.state.loading}
479               >
480                 {this.state.loading ? (
481                   <Spinner />
482                 ) : this.props.site.isSome() ? (
483                   capitalizeFirstLetter(i18n.t("save"))
484                 ) : (
485                   capitalizeFirstLetter(i18n.t("create"))
486                 )}
487               </button>
488               {this.props.site.isSome() && (
489                 <button
490                   type="button"
491                   className="btn btn-secondary"
492                   onClick={linkEvent(this, this.handleCancel)}
493                 >
494                   {i18n.t("cancel")}
495                 </button>
496               )}
497             </div>
498           </div>
499         </form>
500       </>
501     );
502   }
503
504   handleCreateSiteSubmit(i: SiteForm, event: any) {
505     event.preventDefault();
506     i.setState({ loading: true });
507     i.setState(s => ((s.siteForm.auth = auth().unwrap()), s));
508
509     if (i.props.site.isSome()) {
510       WebSocketService.Instance.send(wsClient.editSite(i.state.siteForm));
511       i.props.onEdit();
512     } else {
513       let sForm = i.state.siteForm;
514       let form = new CreateSite({
515         name: sForm.name.unwrapOr("My site"),
516         sidebar: sForm.sidebar,
517         description: sForm.description,
518         icon: sForm.icon,
519         banner: sForm.banner,
520         community_creation_admin_only: sForm.community_creation_admin_only,
521         enable_nsfw: sForm.enable_nsfw,
522         enable_downvotes: sForm.enable_downvotes,
523         require_application: sForm.require_application,
524         application_question: sForm.application_question,
525         open_registration: sForm.open_registration,
526         require_email_verification: sForm.require_email_verification,
527         private_instance: sForm.private_instance,
528         default_theme: sForm.default_theme,
529         default_post_listing_type: sForm.default_post_listing_type,
530         auth: auth().unwrap(),
531         hide_modlog_mod_names: sForm.hide_modlog_mod_names,
532       });
533       WebSocketService.Instance.send(wsClient.createSite(form));
534     }
535     i.setState(i.state);
536   }
537
538   handleSiteNameChange(i: SiteForm, event: any) {
539     i.state.siteForm.name = Some(event.target.value);
540     i.setState(i.state);
541   }
542
543   handleSiteSidebarChange(val: string) {
544     this.setState(s => ((s.siteForm.sidebar = Some(val)), s));
545   }
546
547   handleSiteLegalInfoChange(val: string) {
548     this.setState(s => ((s.siteForm.legal_information = Some(val)), s));
549   }
550
551   handleSiteApplicationQuestionChange(val: string) {
552     this.setState(s => ((s.siteForm.application_question = Some(val)), s));
553   }
554
555   handleSiteDescChange(i: SiteForm, event: any) {
556     i.state.siteForm.description = Some(event.target.value);
557     i.setState(i.state);
558   }
559
560   handleSiteEnableNsfwChange(i: SiteForm, event: any) {
561     i.state.siteForm.enable_nsfw = Some(event.target.checked);
562     i.setState(i.state);
563   }
564
565   handleSiteOpenRegistrationChange(i: SiteForm, event: any) {
566     i.state.siteForm.open_registration = Some(event.target.checked);
567     i.setState(i.state);
568   }
569
570   handleSiteCommunityCreationAdminOnly(i: SiteForm, event: any) {
571     i.state.siteForm.community_creation_admin_only = Some(event.target.checked);
572     i.setState(i.state);
573   }
574
575   handleSiteEnableDownvotesChange(i: SiteForm, event: any) {
576     i.state.siteForm.enable_downvotes = Some(event.target.checked);
577     i.setState(i.state);
578   }
579
580   handleSiteRequireApplication(i: SiteForm, event: any) {
581     i.state.siteForm.require_application = Some(event.target.checked);
582     i.setState(i.state);
583   }
584
585   handleSiteRequireEmailVerification(i: SiteForm, event: any) {
586     i.state.siteForm.require_email_verification = Some(event.target.checked);
587     i.setState(i.state);
588   }
589
590   handleSitePrivateInstance(i: SiteForm, event: any) {
591     i.state.siteForm.private_instance = Some(event.target.checked);
592     i.setState(i.state);
593   }
594
595   handleSiteHideModlogModNames(i: SiteForm, event: any) {
596     i.state.siteForm.hide_modlog_mod_names = Some(event.target.checked);
597     i.setState(i.state);
598   }
599
600   handleSiteDefaultTheme(i: SiteForm, event: any) {
601     i.state.siteForm.default_theme = Some(event.target.value);
602     i.setState(i.state);
603   }
604
605   handleCancel(i: SiteForm) {
606     i.props.onCancel();
607   }
608
609   handleIconUpload(url: string) {
610     this.setState(s => ((s.siteForm.icon = Some(url)), s));
611   }
612
613   handleIconRemove() {
614     this.setState(s => ((s.siteForm.icon = Some("")), s));
615   }
616
617   handleBannerUpload(url: string) {
618     this.setState(s => ((s.siteForm.banner = Some(url)), s));
619   }
620
621   handleBannerRemove() {
622     this.setState(s => ((s.siteForm.banner = Some("")), s));
623   }
624
625   handleDefaultPostListingTypeChange(val: ListingType) {
626     this.setState(
627       s => (
628         (s.siteForm.default_post_listing_type = Some(
629           ListingType[ListingType[val]]
630         )),
631         s
632       )
633     );
634   }
635 }