]> Untitled Git - lemmy-ui.git/blob - src/shared/components/home/site-form.tsx
Upgrade inferno v8.0.0 try2 (#790)
[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                 placeholder={None}
216                 buttonTitle={None}
217                 maxLength={None}
218                 onContentChange={this.handleSiteSidebarChange}
219                 hideNavigationWarnings
220               />
221             </div>
222           </div>
223           <div className="form-group row">
224             <label className="col-12 col-form-label">
225               {i18n.t("legal_information")}
226             </label>
227             <div className="col-12">
228               <MarkdownTextArea
229                 initialContent={this.state.siteForm.legal_information}
230                 placeholder={None}
231                 buttonTitle={None}
232                 maxLength={None}
233                 onContentChange={this.handleSiteLegalInfoChange}
234                 hideNavigationWarnings
235               />
236             </div>
237           </div>
238           {this.state.siteForm.require_application.unwrapOr(false) && (
239             <div className="form-group row">
240               <label className="col-12 col-form-label">
241                 {i18n.t("application_questionnaire")}
242               </label>
243               <div className="col-12">
244                 <MarkdownTextArea
245                   initialContent={this.state.siteForm.application_question}
246                   placeholder={None}
247                   buttonTitle={None}
248                   maxLength={None}
249                   onContentChange={this.handleSiteApplicationQuestionChange}
250                   hideNavigationWarnings
251                 />
252               </div>
253             </div>
254           )}
255           <div className="form-group row">
256             <div className="col-12">
257               <div className="form-check">
258                 <input
259                   className="form-check-input"
260                   id="create-site-downvotes"
261                   type="checkbox"
262                   checked={toUndefined(this.state.siteForm.enable_downvotes)}
263                   onChange={linkEvent(
264                     this,
265                     this.handleSiteEnableDownvotesChange
266                   )}
267                 />
268                 <label
269                   className="form-check-label"
270                   htmlFor="create-site-downvotes"
271                 >
272                   {i18n.t("enable_downvotes")}
273                 </label>
274               </div>
275             </div>
276           </div>
277           <div className="form-group row">
278             <div className="col-12">
279               <div className="form-check">
280                 <input
281                   className="form-check-input"
282                   id="create-site-enable-nsfw"
283                   type="checkbox"
284                   checked={toUndefined(this.state.siteForm.enable_nsfw)}
285                   onChange={linkEvent(this, this.handleSiteEnableNsfwChange)}
286                 />
287                 <label
288                   className="form-check-label"
289                   htmlFor="create-site-enable-nsfw"
290                 >
291                   {i18n.t("enable_nsfw")}
292                 </label>
293               </div>
294             </div>
295           </div>
296           <div className="form-group row">
297             <div className="col-12">
298               <div className="form-check">
299                 <input
300                   className="form-check-input"
301                   id="create-site-open-registration"
302                   type="checkbox"
303                   checked={toUndefined(this.state.siteForm.open_registration)}
304                   onChange={linkEvent(
305                     this,
306                     this.handleSiteOpenRegistrationChange
307                   )}
308                 />
309                 <label
310                   className="form-check-label"
311                   htmlFor="create-site-open-registration"
312                 >
313                   {i18n.t("open_registration")}
314                 </label>
315               </div>
316             </div>
317           </div>
318           <div className="form-group row">
319             <div className="col-12">
320               <div className="form-check">
321                 <input
322                   className="form-check-input"
323                   id="create-site-community-creation-admin-only"
324                   type="checkbox"
325                   checked={toUndefined(
326                     this.state.siteForm.community_creation_admin_only
327                   )}
328                   onChange={linkEvent(
329                     this,
330                     this.handleSiteCommunityCreationAdminOnly
331                   )}
332                 />
333                 <label
334                   className="form-check-label"
335                   htmlFor="create-site-community-creation-admin-only"
336                 >
337                   {i18n.t("community_creation_admin_only")}
338                 </label>
339               </div>
340             </div>
341           </div>
342           <div className="form-group row">
343             <div className="col-12">
344               <div className="form-check">
345                 <input
346                   className="form-check-input"
347                   id="create-site-require-email-verification"
348                   type="checkbox"
349                   checked={toUndefined(
350                     this.state.siteForm.require_email_verification
351                   )}
352                   onChange={linkEvent(
353                     this,
354                     this.handleSiteRequireEmailVerification
355                   )}
356                 />
357                 <label
358                   className="form-check-label"
359                   htmlFor="create-site-require-email-verification"
360                 >
361                   {i18n.t("require_email_verification")}
362                 </label>
363               </div>
364             </div>
365           </div>
366           <div className="form-group row">
367             <div className="col-12">
368               <div className="form-check">
369                 <input
370                   className="form-check-input"
371                   id="create-site-require-application"
372                   type="checkbox"
373                   checked={toUndefined(this.state.siteForm.require_application)}
374                   onChange={linkEvent(this, this.handleSiteRequireApplication)}
375                 />
376                 <label
377                   className="form-check-label"
378                   htmlFor="create-site-require-application"
379                 >
380                   {i18n.t("require_registration_application")}
381                 </label>
382               </div>
383             </div>
384           </div>
385           <div className="form-group row">
386             <div className="col-12">
387               <label
388                 className="form-check-label mr-2"
389                 htmlFor="create-site-default-theme"
390               >
391                 {i18n.t("theme")}
392               </label>
393               <select
394                 id="create-site-default-theme"
395                 value={toUndefined(this.state.siteForm.default_theme)}
396                 onChange={linkEvent(this, this.handleSiteDefaultTheme)}
397                 className="custom-select w-auto"
398               >
399                 <option value="browser">{i18n.t("browser_default")}</option>
400                 {this.state.themeList.unwrapOr([]).map(theme => (
401                   <option key={theme} value={theme}>
402                     {theme}
403                   </option>
404                 ))}
405               </select>
406             </div>
407           </div>
408           {this.props.showLocal && (
409             <form className="form-group row">
410               <label className="col-sm-3">{i18n.t("listing_type")}</label>
411               <div className="col-sm-9">
412                 <ListingTypeSelect
413                   type_={
414                     ListingType[
415                       this.state.siteForm.default_post_listing_type.unwrapOr(
416                         "Local"
417                       )
418                     ]
419                   }
420                   showLocal
421                   showSubscribed={false}
422                   onChange={this.handleDefaultPostListingTypeChange}
423                 />
424               </div>
425             </form>
426           )}
427           <div className="form-group row">
428             <div className="col-12">
429               <div className="form-check">
430                 <input
431                   className="form-check-input"
432                   id="create-site-private-instance"
433                   type="checkbox"
434                   checked={toUndefined(this.state.siteForm.private_instance)}
435                   onChange={linkEvent(this, this.handleSitePrivateInstance)}
436                 />
437                 <label
438                   className="form-check-label"
439                   htmlFor="create-site-private-instance"
440                 >
441                   {i18n.t("private_instance")}
442                 </label>
443               </div>
444             </div>
445           </div>
446           <div className="form-group row">
447             <div className="col-12">
448               <div className="form-check">
449                 <input
450                   className="form-check-input"
451                   id="create-site-hide-modlog-mod-names"
452                   type="checkbox"
453                   checked={toUndefined(
454                     this.state.siteForm.hide_modlog_mod_names
455                   )}
456                   onChange={linkEvent(this, this.handleSiteHideModlogModNames)}
457                 />
458                 <label
459                   className="form-check-label"
460                   htmlFor="create-site-hide-modlog-mod-names"
461                 >
462                   {i18n.t("hide_modlog_mod_names")}
463                 </label>
464               </div>
465             </div>
466           </div>
467           <div className="form-group row">
468             <div className="col-12">
469               <button
470                 type="submit"
471                 className="btn btn-secondary mr-2"
472                 disabled={this.state.loading}
473               >
474                 {this.state.loading ? (
475                   <Spinner />
476                 ) : this.props.site.isSome() ? (
477                   capitalizeFirstLetter(i18n.t("save"))
478                 ) : (
479                   capitalizeFirstLetter(i18n.t("create"))
480                 )}
481               </button>
482               {this.props.site.isSome() && (
483                 <button
484                   type="button"
485                   className="btn btn-secondary"
486                   onClick={linkEvent(this, this.handleCancel)}
487                 >
488                   {i18n.t("cancel")}
489                 </button>
490               )}
491             </div>
492           </div>
493         </form>
494       </>
495     );
496   }
497
498   handleCreateSiteSubmit(i: SiteForm, event: any) {
499     event.preventDefault();
500     i.setState({ loading: true });
501     i.setState(s => ((s.siteForm.auth = auth().unwrap()), s));
502
503     if (i.props.site.isSome()) {
504       WebSocketService.Instance.send(wsClient.editSite(i.state.siteForm));
505       i.props.onEdit();
506     } else {
507       let sForm = i.state.siteForm;
508       let form = new CreateSite({
509         name: sForm.name.unwrapOr("My site"),
510         sidebar: sForm.sidebar,
511         description: sForm.description,
512         icon: sForm.icon,
513         banner: sForm.banner,
514         community_creation_admin_only: sForm.community_creation_admin_only,
515         enable_nsfw: sForm.enable_nsfw,
516         enable_downvotes: sForm.enable_downvotes,
517         require_application: sForm.require_application,
518         application_question: sForm.application_question,
519         open_registration: sForm.open_registration,
520         require_email_verification: sForm.require_email_verification,
521         private_instance: sForm.private_instance,
522         default_theme: sForm.default_theme,
523         default_post_listing_type: sForm.default_post_listing_type,
524         auth: auth().unwrap(),
525         hide_modlog_mod_names: sForm.hide_modlog_mod_names,
526       });
527       WebSocketService.Instance.send(wsClient.createSite(form));
528     }
529     i.setState(i.state);
530   }
531
532   handleSiteNameChange(i: SiteForm, event: any) {
533     i.state.siteForm.name = Some(event.target.value);
534     i.setState(i.state);
535   }
536
537   handleSiteSidebarChange(val: string) {
538     this.setState(s => ((s.siteForm.sidebar = Some(val)), s));
539   }
540
541   handleSiteLegalInfoChange(val: string) {
542     this.setState(s => ((s.siteForm.legal_information = Some(val)), s));
543   }
544
545   handleSiteApplicationQuestionChange(val: string) {
546     this.setState(s => ((s.siteForm.application_question = Some(val)), s));
547   }
548
549   handleSiteDescChange(i: SiteForm, event: any) {
550     i.state.siteForm.description = Some(event.target.value);
551     i.setState(i.state);
552   }
553
554   handleSiteEnableNsfwChange(i: SiteForm, event: any) {
555     i.state.siteForm.enable_nsfw = Some(event.target.checked);
556     i.setState(i.state);
557   }
558
559   handleSiteOpenRegistrationChange(i: SiteForm, event: any) {
560     i.state.siteForm.open_registration = Some(event.target.checked);
561     i.setState(i.state);
562   }
563
564   handleSiteCommunityCreationAdminOnly(i: SiteForm, event: any) {
565     i.state.siteForm.community_creation_admin_only = Some(event.target.checked);
566     i.setState(i.state);
567   }
568
569   handleSiteEnableDownvotesChange(i: SiteForm, event: any) {
570     i.state.siteForm.enable_downvotes = Some(event.target.checked);
571     i.setState(i.state);
572   }
573
574   handleSiteRequireApplication(i: SiteForm, event: any) {
575     i.state.siteForm.require_application = Some(event.target.checked);
576     i.setState(i.state);
577   }
578
579   handleSiteRequireEmailVerification(i: SiteForm, event: any) {
580     i.state.siteForm.require_email_verification = Some(event.target.checked);
581     i.setState(i.state);
582   }
583
584   handleSitePrivateInstance(i: SiteForm, event: any) {
585     i.state.siteForm.private_instance = Some(event.target.checked);
586     i.setState(i.state);
587   }
588
589   handleSiteHideModlogModNames(i: SiteForm, event: any) {
590     i.state.siteForm.hide_modlog_mod_names = Some(event.target.checked);
591     i.setState(i.state);
592   }
593
594   handleSiteDefaultTheme(i: SiteForm, event: any) {
595     i.state.siteForm.default_theme = Some(event.target.value);
596     i.setState(i.state);
597   }
598
599   handleCancel(i: SiteForm) {
600     i.props.onCancel();
601   }
602
603   handleIconUpload(url: string) {
604     this.setState(s => ((s.siteForm.icon = Some(url)), s));
605   }
606
607   handleIconRemove() {
608     this.setState(s => ((s.siteForm.icon = Some("")), s));
609   }
610
611   handleBannerUpload(url: string) {
612     this.setState(s => ((s.siteForm.banner = Some(url)), s));
613   }
614
615   handleBannerRemove() {
616     this.setState(s => ((s.siteForm.banner = Some("")), s));
617   }
618
619   handleDefaultPostListingTypeChange(val: ListingType) {
620     this.setState(
621       s => (
622         (s.siteForm.default_post_listing_type = Some(
623           ListingType[ListingType[val]]
624         )),
625         s
626       )
627     );
628   }
629 }