]> Untitled Git - lemmy-ui.git/blob - src/shared/components/home/site-form.tsx
Optimize Tagline Form page (#972)
[lemmy-ui.git] / src / shared / components / home / site-form.tsx
1 import { Component, InfernoMouseEvent, linkEvent } from "inferno";
2 import { Prompt } from "inferno-router";
3 import {
4   CreateSite,
5   EditSite,
6   GetSiteResponse,
7   ListingType,
8   RegistrationMode,
9 } from "lemmy-js-client";
10 import { i18n } from "../../i18next";
11 import { WebSocketService } from "../../services";
12 import {
13   capitalizeFirstLetter,
14   fetchThemeList,
15   myAuth,
16   wsClient,
17 } from "../../utils";
18 import { Spinner } from "../common/icon";
19 import { ImageUploadForm } from "../common/image-upload-form";
20 import { LanguageSelect } from "../common/language-select";
21 import { ListingTypeSelect } from "../common/listing-type-select";
22 import { MarkdownTextArea } from "../common/markdown-textarea";
23
24 interface SiteFormProps {
25   siteRes: GetSiteResponse;
26   showLocal?: boolean;
27 }
28
29 interface SiteFormState {
30   siteForm: EditSite;
31   loading: boolean;
32   themeList?: string[];
33 }
34
35 export class SiteForm extends Component<SiteFormProps, SiteFormState> {
36   state: SiteFormState = {
37     siteForm: {
38       auth: "TODO",
39     },
40     loading: false,
41   };
42
43   constructor(props: any, context: any) {
44     super(props, context);
45
46     this.handleSiteSidebarChange = this.handleSiteSidebarChange.bind(this);
47     this.handleSiteLegalInfoChange = this.handleSiteLegalInfoChange.bind(this);
48     this.handleSiteApplicationQuestionChange =
49       this.handleSiteApplicationQuestionChange.bind(this);
50
51     this.handleIconUpload = this.handleIconUpload.bind(this);
52     this.handleIconRemove = this.handleIconRemove.bind(this);
53
54     this.handleBannerUpload = this.handleBannerUpload.bind(this);
55     this.handleBannerRemove = this.handleBannerRemove.bind(this);
56
57     this.handleDefaultPostListingTypeChange =
58       this.handleDefaultPostListingTypeChange.bind(this);
59
60     this.handleDiscussionLanguageChange =
61       this.handleDiscussionLanguageChange.bind(this);
62
63     let site = this.props.siteRes.site_view.site;
64     let ls = this.props.siteRes.site_view.local_site;
65     let lsrl = this.props.siteRes.site_view.local_site_rate_limit;
66     this.state = {
67       ...this.state,
68       siteForm: {
69         name: site.name,
70         sidebar: site.sidebar,
71         description: site.description,
72         enable_downvotes: ls.enable_downvotes,
73         registration_mode: ls.registration_mode,
74         enable_nsfw: ls.enable_nsfw,
75         community_creation_admin_only: ls.community_creation_admin_only,
76         icon: site.icon,
77         banner: site.banner,
78         require_email_verification: ls.require_email_verification,
79         application_question: ls.application_question,
80         private_instance: ls.private_instance,
81         default_theme: ls.default_theme,
82         default_post_listing_type: ls.default_post_listing_type,
83         legal_information: ls.legal_information,
84         application_email_admins: ls.application_email_admins,
85         reports_email_admins: ls.reports_email_admins,
86         hide_modlog_mod_names: ls.hide_modlog_mod_names,
87         discussion_languages: this.props.siteRes.discussion_languages,
88         slur_filter_regex: ls.slur_filter_regex,
89         actor_name_max_length: ls.actor_name_max_length,
90         rate_limit_message: lsrl.message,
91         rate_limit_message_per_second: lsrl.message_per_second,
92         rate_limit_comment: lsrl.comment,
93         rate_limit_comment_per_second: lsrl.comment_per_second,
94         rate_limit_image: lsrl.image,
95         rate_limit_image_per_second: lsrl.image_per_second,
96         rate_limit_post: lsrl.post,
97         rate_limit_post_per_second: lsrl.post_per_second,
98         rate_limit_register: lsrl.register,
99         rate_limit_register_per_second: lsrl.register_per_second,
100         rate_limit_search: lsrl.search,
101         rate_limit_search_per_second: lsrl.search_per_second,
102         federation_enabled: ls.federation_enabled,
103         federation_debug: ls.federation_debug,
104         federation_worker_count: ls.federation_worker_count,
105         captcha_enabled: ls.captcha_enabled,
106         captcha_difficulty: ls.captcha_difficulty,
107         allowed_instances: this.props.siteRes.federated_instances?.allowed,
108         blocked_instances: this.props.siteRes.federated_instances?.blocked,
109         auth: "TODO",
110       },
111     };
112   }
113
114   async componentDidMount() {
115     this.setState({ themeList: await fetchThemeList() });
116   }
117
118   // Necessary to stop the loading
119   componentWillReceiveProps() {
120     this.setState({ loading: false });
121   }
122
123   componentDidUpdate() {
124     if (
125       !this.state.loading &&
126       !this.props.siteRes.site_view.local_site.site_setup &&
127       (this.state.siteForm.name ||
128         this.state.siteForm.sidebar ||
129         this.state.siteForm.application_question ||
130         this.state.siteForm.description)
131     ) {
132       window.onbeforeunload = () => true;
133     } else {
134       window.onbeforeunload = null;
135     }
136   }
137
138   componentWillUnmount() {
139     window.onbeforeunload = null;
140   }
141
142   render() {
143     let siteSetup = this.props.siteRes.site_view.local_site.site_setup;
144     return (
145       <>
146         <Prompt
147           when={
148             !this.state.loading &&
149             !siteSetup &&
150             (this.state.siteForm.name ||
151               this.state.siteForm.sidebar ||
152               this.state.siteForm.application_question ||
153               this.state.siteForm.description)
154           }
155           message={i18n.t("block_leaving")}
156         />
157         <form onSubmit={linkEvent(this, this.handleCreateSiteSubmit)}>
158           <h5>{`${
159             siteSetup
160               ? capitalizeFirstLetter(i18n.t("save"))
161               : capitalizeFirstLetter(i18n.t("name"))
162           } ${i18n.t("your_site")}`}</h5>
163           <div className="form-group row">
164             <label className="col-12 col-form-label" htmlFor="create-site-name">
165               {i18n.t("name")}
166             </label>
167             <div className="col-12">
168               <input
169                 type="text"
170                 id="create-site-name"
171                 className="form-control"
172                 value={this.state.siteForm.name}
173                 onInput={linkEvent(this, this.handleSiteNameChange)}
174                 required
175                 minLength={3}
176                 maxLength={20}
177               />
178             </div>
179           </div>
180           <div className="form-group">
181             <label>{i18n.t("icon")}</label>
182             <ImageUploadForm
183               uploadTitle={i18n.t("upload_icon")}
184               imageSrc={this.state.siteForm.icon}
185               onUpload={this.handleIconUpload}
186               onRemove={this.handleIconRemove}
187               rounded
188             />
189           </div>
190           <div className="form-group">
191             <label>{i18n.t("banner")}</label>
192             <ImageUploadForm
193               uploadTitle={i18n.t("upload_banner")}
194               imageSrc={this.state.siteForm.banner}
195               onUpload={this.handleBannerUpload}
196               onRemove={this.handleBannerRemove}
197             />
198           </div>
199           <div className="form-group row">
200             <label className="col-12 col-form-label" htmlFor="site-desc">
201               {i18n.t("description")}
202             </label>
203             <div className="col-12">
204               <input
205                 type="text"
206                 className="form-control"
207                 id="site-desc"
208                 value={this.state.siteForm.description}
209                 onInput={linkEvent(this, this.handleSiteDescChange)}
210                 maxLength={150}
211               />
212             </div>
213           </div>
214           <div className="form-group row">
215             <label className="col-12 col-form-label">{i18n.t("sidebar")}</label>
216             <div className="col-12">
217               <MarkdownTextArea
218                 initialContent={this.state.siteForm.sidebar}
219                 onContentChange={this.handleSiteSidebarChange}
220                 hideNavigationWarnings
221                 allLanguages={[]}
222                 siteLanguages={[]}
223               />
224             </div>
225           </div>
226           <div className="form-group row">
227             <label className="col-12 col-form-label">
228               {i18n.t("legal_information")}
229             </label>
230             <div className="col-12">
231               <MarkdownTextArea
232                 initialContent={this.state.siteForm.legal_information}
233                 onContentChange={this.handleSiteLegalInfoChange}
234                 hideNavigationWarnings
235                 allLanguages={[]}
236                 siteLanguages={[]}
237               />
238             </div>
239           </div>
240           <div className="form-group row">
241             <div className="col-12">
242               <div className="form-check">
243                 <input
244                   className="form-check-input"
245                   id="create-site-downvotes"
246                   type="checkbox"
247                   checked={this.state.siteForm.enable_downvotes}
248                   onChange={linkEvent(
249                     this,
250                     this.handleSiteEnableDownvotesChange
251                   )}
252                 />
253                 <label
254                   className="form-check-label"
255                   htmlFor="create-site-downvotes"
256                 >
257                   {i18n.t("enable_downvotes")}
258                 </label>
259               </div>
260             </div>
261           </div>
262           <div className="form-group row">
263             <div className="col-12">
264               <div className="form-check">
265                 <input
266                   className="form-check-input"
267                   id="create-site-enable-nsfw"
268                   type="checkbox"
269                   checked={this.state.siteForm.enable_nsfw}
270                   onChange={linkEvent(this, this.handleSiteEnableNsfwChange)}
271                 />
272                 <label
273                   className="form-check-label"
274                   htmlFor="create-site-enable-nsfw"
275                 >
276                   {i18n.t("enable_nsfw")}
277                 </label>
278               </div>
279             </div>
280           </div>
281           <div className="form-group row">
282             <div className="col-12">
283               <label
284                 className="form-check-label mr-2"
285                 htmlFor="create-site-registration-mode"
286               >
287                 {i18n.t("registration_mode")}
288               </label>
289               <select
290                 id="create-site-registration-mode"
291                 value={this.state.siteForm.registration_mode}
292                 onChange={linkEvent(
293                   this,
294                   this.handleSiteRegistrationModeChange
295                 )}
296                 className="custom-select w-auto"
297               >
298                 <option value={RegistrationMode.RequireApplication}>
299                   {i18n.t("require_registration_application")}
300                 </option>
301                 <option value={RegistrationMode.Open}>
302                   {i18n.t("open_registration")}
303                 </option>
304                 <option value={RegistrationMode.Closed}>
305                   {i18n.t("close_registration")}
306                 </option>
307               </select>
308             </div>
309           </div>
310           {this.state.siteForm.registration_mode ==
311             RegistrationMode.RequireApplication && (
312             <div className="form-group row">
313               <label className="col-12 col-form-label">
314                 {i18n.t("application_questionnaire")}
315               </label>
316               <div className="col-12">
317                 <MarkdownTextArea
318                   initialContent={this.state.siteForm.application_question}
319                   onContentChange={this.handleSiteApplicationQuestionChange}
320                   hideNavigationWarnings
321                   allLanguages={[]}
322                   siteLanguages={[]}
323                 />
324               </div>
325             </div>
326           )}
327           <div className="form-group row">
328             <div className="col-12">
329               <div className="form-check">
330                 <input
331                   className="form-check-input"
332                   id="create-site-community-creation-admin-only"
333                   type="checkbox"
334                   checked={this.state.siteForm.community_creation_admin_only}
335                   onChange={linkEvent(
336                     this,
337                     this.handleSiteCommunityCreationAdminOnly
338                   )}
339                 />
340                 <label
341                   className="form-check-label"
342                   htmlFor="create-site-community-creation-admin-only"
343                 >
344                   {i18n.t("community_creation_admin_only")}
345                 </label>
346               </div>
347             </div>
348           </div>
349           <div className="form-group row">
350             <div className="col-12">
351               <div className="form-check">
352                 <input
353                   className="form-check-input"
354                   id="create-site-require-email-verification"
355                   type="checkbox"
356                   checked={this.state.siteForm.require_email_verification}
357                   onChange={linkEvent(
358                     this,
359                     this.handleSiteRequireEmailVerification
360                   )}
361                 />
362                 <label
363                   className="form-check-label"
364                   htmlFor="create-site-require-email-verification"
365                 >
366                   {i18n.t("require_email_verification")}
367                 </label>
368               </div>
369             </div>
370           </div>
371           <div className="form-group row">
372             <div className="col-12">
373               <div className="form-check">
374                 <input
375                   className="form-check-input"
376                   id="create-site-application-email-admins"
377                   type="checkbox"
378                   checked={this.state.siteForm.application_email_admins}
379                   onChange={linkEvent(
380                     this,
381                     this.handleSiteApplicationEmailAdmins
382                   )}
383                 />
384                 <label
385                   className="form-check-label"
386                   htmlFor="create-site-email-admins"
387                 >
388                   {i18n.t("application_email_admins")}
389                 </label>
390               </div>
391             </div>
392           </div>
393           <div className="form-group row">
394             <div className="col-12">
395               <div className="form-check">
396                 <input
397                   className="form-check-input"
398                   id="create-site-reports-email-admins"
399                   type="checkbox"
400                   checked={this.state.siteForm.reports_email_admins}
401                   onChange={linkEvent(this, this.handleSiteReportsEmailAdmins)}
402                 />
403                 <label
404                   className="form-check-label"
405                   htmlFor="create-site-reports-email-admins"
406                 >
407                   {i18n.t("reports_email_admins")}
408                 </label>
409               </div>
410             </div>
411           </div>
412           <div className="form-group row">
413             <div className="col-12">
414               <label
415                 className="form-check-label mr-2"
416                 htmlFor="create-site-default-theme"
417               >
418                 {i18n.t("theme")}
419               </label>
420               <select
421                 id="create-site-default-theme"
422                 value={this.state.siteForm.default_theme}
423                 onChange={linkEvent(this, this.handleSiteDefaultTheme)}
424                 className="custom-select w-auto"
425               >
426                 <option value="browser">{i18n.t("browser_default")}</option>
427                 {this.state.themeList?.map(theme => (
428                   <option key={theme} value={theme}>
429                     {theme}
430                   </option>
431                 ))}
432               </select>
433             </div>
434           </div>
435           {this.props.showLocal && (
436             <form className="form-group row">
437               <label className="col-sm-3">{i18n.t("listing_type")}</label>
438               <div className="col-sm-9">
439                 <ListingTypeSelect
440                   type_={
441                     ListingType[
442                       this.state.siteForm.default_post_listing_type ?? "Local"
443                     ]
444                   }
445                   showLocal
446                   showSubscribed={false}
447                   onChange={this.handleDefaultPostListingTypeChange}
448                 />
449               </div>
450             </form>
451           )}
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-private-instance"
458                   type="checkbox"
459                   checked={this.state.siteForm.private_instance}
460                   onChange={linkEvent(this, this.handleSitePrivateInstance)}
461                 />
462                 <label
463                   className="form-check-label"
464                   htmlFor="create-site-private-instance"
465                 >
466                   {i18n.t("private_instance")}
467                 </label>
468               </div>
469             </div>
470           </div>
471           <div className="form-group row">
472             <div className="col-12">
473               <div className="form-check">
474                 <input
475                   className="form-check-input"
476                   id="create-site-hide-modlog-mod-names"
477                   type="checkbox"
478                   checked={this.state.siteForm.hide_modlog_mod_names}
479                   onChange={linkEvent(this, this.handleSiteHideModlogModNames)}
480                 />
481                 <label
482                   className="form-check-label"
483                   htmlFor="create-site-hide-modlog-mod-names"
484                 >
485                   {i18n.t("hide_modlog_mod_names")}
486                 </label>
487               </div>
488             </div>
489           </div>
490           <div className="form-group row">
491             <label
492               className="col-12 col-form-label"
493               htmlFor="create-site-slur-filter-regex"
494             >
495               {i18n.t("slur_filter_regex")}
496             </label>
497             <div className="col-12">
498               <input
499                 type="text"
500                 id="create-site-slur-filter-regex"
501                 placeholder="(word1|word2)"
502                 className="form-control"
503                 value={this.state.siteForm.slur_filter_regex}
504                 onInput={linkEvent(this, this.handleSiteSlurFilterRegex)}
505                 minLength={3}
506               />
507             </div>
508           </div>
509           <LanguageSelect
510             allLanguages={this.props.siteRes.all_languages}
511             siteLanguages={this.props.siteRes.discussion_languages}
512             selectedLanguageIds={this.state.siteForm.discussion_languages}
513             multiple={true}
514             onChange={this.handleDiscussionLanguageChange}
515             showAll
516           />
517           <div className="form-group row">
518             <label
519               className="col-12 col-form-label"
520               htmlFor="create-site-actor-name"
521             >
522               {i18n.t("actor_name_max_length")}
523             </label>
524             <div className="col-12">
525               <input
526                 type="number"
527                 id="create-site-actor-name"
528                 className="form-control"
529                 min={5}
530                 value={this.state.siteForm.actor_name_max_length}
531                 onInput={linkEvent(this, this.handleSiteActorNameMaxLength)}
532               />
533             </div>
534           </div>
535           <div className="form-group row">
536             <div className="col-12">
537               <div className="form-check">
538                 <input
539                   className="form-check-input"
540                   id="create-site-federation-enabled"
541                   type="checkbox"
542                   checked={this.state.siteForm.federation_enabled}
543                   onChange={linkEvent(this, this.handleSiteFederationEnabled)}
544                 />
545                 <label
546                   className="form-check-label"
547                   htmlFor="create-site-federation-enabled"
548                 >
549                   {i18n.t("federation_enabled")}
550                 </label>
551               </div>
552             </div>
553           </div>
554           {this.state.siteForm.federation_enabled && (
555             <>
556               <div className="form-group row">
557                 <label
558                   className="col-12 col-form-label"
559                   htmlFor="create-site-allowed-instances"
560                 >
561                   {i18n.t("allowed_instances")}
562                 </label>
563                 <div className="col-12">
564                   <input
565                     type="text"
566                     placeholder="instance1.tld,instance2.tld"
567                     id="create-site-allowed-instances"
568                     className="form-control"
569                     value={this.instancesToString(
570                       this.state.siteForm.allowed_instances
571                     )}
572                     onInput={linkEvent(this, this.handleSiteAllowedInstances)}
573                   />
574                 </div>
575               </div>
576               <div className="form-group row">
577                 <label
578                   className="col-12 col-form-label"
579                   htmlFor="create-site-blocked-instances"
580                 >
581                   {i18n.t("blocked_instances")}
582                 </label>
583                 <div className="col-12">
584                   <input
585                     type="text"
586                     placeholder="instance1.tld,instance2.tld"
587                     id="create-site-blocked-instances"
588                     className="form-control"
589                     value={this.instancesToString(
590                       this.state.siteForm.blocked_instances
591                     )}
592                     onInput={linkEvent(this, this.handleSiteBlockedInstances)}
593                   />
594                 </div>
595               </div>
596               <div className="form-group row">
597                 <div className="col-12">
598                   <div className="form-check">
599                     <input
600                       className="form-check-input"
601                       id="create-site-federation-debug"
602                       type="checkbox"
603                       checked={this.state.siteForm.federation_debug}
604                       onChange={linkEvent(this, this.handleSiteFederationDebug)}
605                     />
606                     <label
607                       className="form-check-label"
608                       htmlFor="create-site-federation-debug"
609                     >
610                       {i18n.t("federation_debug")}
611                     </label>
612                   </div>
613                 </div>
614               </div>
615               <div className="form-group row">
616                 <label
617                   className="col-12 col-form-label"
618                   htmlFor="create-site-federation-worker-count"
619                 >
620                   {i18n.t("federation_worker_count")}
621                 </label>
622                 <div className="col-12">
623                   <input
624                     type="number"
625                     id="create-site-federation-worker-count"
626                     className="form-control"
627                     min={0}
628                     value={this.state.siteForm.federation_worker_count}
629                     onInput={linkEvent(
630                       this,
631                       this.handleSiteFederationWorkerCount
632                     )}
633                   />
634                 </div>
635               </div>
636             </>
637           )}
638           <div className="form-group row">
639             <div className="col-12">
640               <div className="form-check">
641                 <input
642                   className="form-check-input"
643                   id="create-site-captcha-enabled"
644                   type="checkbox"
645                   checked={this.state.siteForm.captcha_enabled}
646                   onChange={linkEvent(this, this.handleSiteCaptchaEnabled)}
647                 />
648                 <label
649                   className="form-check-label"
650                   htmlFor="create-site-captcha-enabled"
651                 >
652                   {i18n.t("captcha_enabled")}
653                 </label>
654               </div>
655             </div>
656           </div>
657           {this.state.siteForm.captcha_enabled && (
658             <div className="form-group row">
659               <div className="col-12">
660                 <label
661                   className="form-check-label mr-2"
662                   htmlFor="create-site-captcha-difficulty"
663                 >
664                   {i18n.t("captcha_difficulty")}
665                 </label>
666                 <select
667                   id="create-site-captcha-difficulty"
668                   value={this.state.siteForm.captcha_difficulty}
669                   onChange={linkEvent(this, this.handleSiteCaptchaDifficulty)}
670                   className="custom-select w-auto"
671                 >
672                   <option value="easy">{i18n.t("easy")}</option>
673                   <option value="medium">{i18n.t("medium")}</option>
674                   <option value="hard">{i18n.t("hard")}</option>
675                 </select>
676               </div>
677             </div>
678           )}
679           <div className="form-group row">
680             <label
681               className="col-12 col-form-label"
682               htmlFor="create-site-rate-limit-message"
683             >
684               {i18n.t("rate_limit_message")}
685             </label>
686             <div className="col-12">
687               <input
688                 type="number"
689                 id="create-site-rate-limit-message"
690                 className="form-control"
691                 min={0}
692                 value={this.state.siteForm.rate_limit_message}
693                 onInput={linkEvent(this, this.handleSiteRateLimitMessage)}
694               />
695             </div>
696           </div>
697           <div className="form-group row">
698             <label
699               className="col-12 col-form-label"
700               htmlFor="create-site-rate-limit-message-per-second"
701             >
702               {i18n.t("per_second")}
703             </label>
704             <div className="col-12">
705               <input
706                 type="number"
707                 id="create-site-rate-limit-message-per-second"
708                 className="form-control"
709                 min={0}
710                 value={this.state.siteForm.rate_limit_message_per_second}
711                 onInput={linkEvent(
712                   this,
713                   this.handleSiteRateLimitMessagePerSecond
714                 )}
715               />
716             </div>
717           </div>
718           <div className="form-group row">
719             <label
720               className="col-12 col-form-label"
721               htmlFor="create-site-rate-limit-post"
722             >
723               {i18n.t("rate_limit_post")}
724             </label>
725             <div className="col-12">
726               <input
727                 type="number"
728                 id="create-site-rate-limit-post"
729                 className="form-control"
730                 min={0}
731                 value={this.state.siteForm.rate_limit_post}
732                 onInput={linkEvent(this, this.handleSiteRateLimitPost)}
733               />
734             </div>
735           </div>
736           <div className="form-group row">
737             <label
738               className="col-12 col-form-label"
739               htmlFor="create-site-rate-limit-post-per-second"
740             >
741               {i18n.t("per_second")}
742             </label>
743             <div className="col-12">
744               <input
745                 type="number"
746                 id="create-site-rate-limit-post-per-second"
747                 className="form-control"
748                 min={0}
749                 value={this.state.siteForm.rate_limit_post_per_second}
750                 onInput={linkEvent(this, this.handleSiteRateLimitPostPerSecond)}
751               />
752             </div>
753           </div>
754           <div className="form-group row">
755             <label
756               className="col-12 col-form-label"
757               htmlFor="create-site-rate-limit-register"
758             >
759               {i18n.t("rate_limit_register")}
760             </label>
761             <div className="col-12">
762               <input
763                 type="number"
764                 id="create-site-rate-limit-register"
765                 className="form-control"
766                 min={0}
767                 value={this.state.siteForm.rate_limit_register}
768                 onInput={linkEvent(this, this.handleSiteRateLimitRegister)}
769               />
770             </div>
771           </div>
772           <div className="form-group row">
773             <label
774               className="col-12 col-form-label"
775               htmlFor="create-site-rate-limit-register-per-second"
776             >
777               {i18n.t("per_second")}
778             </label>
779             <div className="col-12">
780               <input
781                 type="number"
782                 id="create-site-rate-limit-register-per-second"
783                 className="form-control"
784                 min={0}
785                 value={this.state.siteForm.rate_limit_register_per_second}
786                 onInput={linkEvent(
787                   this,
788                   this.handleSiteRateLimitRegisterPerSecond
789                 )}
790               />
791             </div>
792           </div>
793           <div className="form-group row">
794             <label
795               className="col-12 col-form-label"
796               htmlFor="create-site-rate-limit-image"
797             >
798               {i18n.t("rate_limit_image")}
799             </label>
800             <div className="col-12">
801               <input
802                 type="number"
803                 id="create-site-rate-limit-image"
804                 className="form-control"
805                 min={0}
806                 value={this.state.siteForm.rate_limit_image}
807                 onInput={linkEvent(this, this.handleSiteRateLimitImage)}
808               />
809             </div>
810           </div>
811           <div className="form-group row">
812             <label
813               className="col-12 col-form-label"
814               htmlFor="create-site-rate-limit-image-per-second"
815             >
816               {i18n.t("per_second")}
817             </label>
818             <div className="col-12">
819               <input
820                 type="number"
821                 id="create-site-rate-limit-image-per-second"
822                 className="form-control"
823                 min={0}
824                 value={this.state.siteForm.rate_limit_image_per_second}
825                 onInput={linkEvent(
826                   this,
827                   this.handleSiteRateLimitImagePerSecond
828                 )}
829               />
830             </div>
831           </div>
832           <div className="form-group row">
833             <label
834               className="col-12 col-form-label"
835               htmlFor="create-site-rate-limit-comment"
836             >
837               {i18n.t("rate_limit_comment")}
838             </label>
839             <div className="col-12">
840               <input
841                 type="number"
842                 id="create-site-rate-limit-comment"
843                 className="form-control"
844                 min={0}
845                 value={this.state.siteForm.rate_limit_comment}
846                 onInput={linkEvent(this, this.handleSiteRateLimitComment)}
847               />
848             </div>
849           </div>
850           <div className="form-group row">
851             <label
852               className="col-12 col-form-label"
853               htmlFor="create-site-rate-limit-comment-per-second"
854             >
855               {i18n.t("per_second")}
856             </label>
857             <div className="col-12">
858               <input
859                 type="number"
860                 id="create-site-rate-limit-comment-per-second"
861                 className="form-control"
862                 min={0}
863                 value={this.state.siteForm.rate_limit_comment_per_second}
864                 onInput={linkEvent(
865                   this,
866                   this.handleSiteRateLimitCommentPerSecond
867                 )}
868               />
869             </div>
870           </div>
871           <div className="form-group row">
872             <label
873               className="col-12 col-form-label"
874               htmlFor="create-site-rate-limit-search"
875             >
876               {i18n.t("rate_limit_search")}
877             </label>
878             <div className="col-12">
879               <input
880                 type="number"
881                 id="create-site-rate-limit-search"
882                 className="form-control"
883                 min={0}
884                 value={this.state.siteForm.rate_limit_search}
885                 onInput={linkEvent(this, this.handleSiteRateLimitSearch)}
886               />
887             </div>
888           </div>
889           <div className="form-group row">
890             <label
891               className="col-12 col-form-label"
892               htmlFor="create-site-rate-limit-search-per-second"
893             >
894               {i18n.t("per_second")}
895             </label>
896             <div className="col-12">
897               <input
898                 type="number"
899                 id="create-site-rate-limit-search-per-second"
900                 className="form-control"
901                 min={0}
902                 value={this.state.siteForm.rate_limit_search_per_second}
903                 onInput={linkEvent(
904                   this,
905                   this.handleSiteRateLimitSearchPerSecond
906                 )}
907               />
908             </div>
909           </div>
910
911           <div className="form-group row">
912             <div className="col-12">
913               <button
914                 type="submit"
915                 className="btn btn-secondary mr-2"
916                 disabled={this.state.loading}
917               >
918                 {this.state.loading ? (
919                   <Spinner />
920                 ) : siteSetup ? (
921                   capitalizeFirstLetter(i18n.t("save"))
922                 ) : (
923                   capitalizeFirstLetter(i18n.t("create"))
924                 )}
925               </button>
926             </div>
927           </div>
928         </form>
929       </>
930     );
931   }
932
933   handleCreateSiteSubmit(i: SiteForm, event: any) {
934     event.preventDefault();
935     i.setState({ loading: true });
936     let auth = myAuth() ?? "TODO";
937     i.setState(s => ((s.siteForm.auth = auth), s));
938     if (i.props.siteRes.site_view.local_site.site_setup) {
939       WebSocketService.Instance.send(wsClient.editSite(i.state.siteForm));
940     } else {
941       let sForm = i.state.siteForm;
942       let form: CreateSite = {
943         name: sForm.name ?? "My site",
944         sidebar: sForm.sidebar,
945         description: sForm.description,
946         icon: sForm.icon,
947         banner: sForm.banner,
948         community_creation_admin_only: sForm.community_creation_admin_only,
949         enable_nsfw: sForm.enable_nsfw,
950         enable_downvotes: sForm.enable_downvotes,
951         application_question: sForm.application_question,
952         registration_mode: sForm.registration_mode,
953         require_email_verification: sForm.require_email_verification,
954         private_instance: sForm.private_instance,
955         default_theme: sForm.default_theme,
956         default_post_listing_type: sForm.default_post_listing_type,
957         application_email_admins: sForm.application_email_admins,
958         hide_modlog_mod_names: sForm.hide_modlog_mod_names,
959         legal_information: sForm.legal_information,
960         slur_filter_regex: sForm.slur_filter_regex,
961         actor_name_max_length: sForm.actor_name_max_length,
962         rate_limit_message: sForm.rate_limit_message,
963         rate_limit_message_per_second: sForm.rate_limit_message_per_second,
964         rate_limit_comment: sForm.rate_limit_comment,
965         rate_limit_comment_per_second: sForm.rate_limit_comment_per_second,
966         rate_limit_image: sForm.rate_limit_image,
967         rate_limit_image_per_second: sForm.rate_limit_image_per_second,
968         rate_limit_post: sForm.rate_limit_post,
969         rate_limit_post_per_second: sForm.rate_limit_post_per_second,
970         rate_limit_register: sForm.rate_limit_register,
971         rate_limit_register_per_second: sForm.rate_limit_register_per_second,
972         rate_limit_search: sForm.rate_limit_search,
973         rate_limit_search_per_second: sForm.rate_limit_search_per_second,
974         federation_enabled: sForm.federation_enabled,
975         federation_debug: sForm.federation_debug,
976         federation_worker_count: sForm.federation_worker_count,
977         captcha_enabled: sForm.captcha_enabled,
978         captcha_difficulty: sForm.captcha_difficulty,
979         allowed_instances: sForm.allowed_instances,
980         blocked_instances: sForm.blocked_instances,
981         discussion_languages: sForm.discussion_languages,
982         auth,
983       };
984       WebSocketService.Instance.send(wsClient.createSite(form));
985     }
986     i.setState(i.state);
987   }
988
989   instancesToString(opt?: string[]): string {
990     return opt ? opt.join(",") : "";
991   }
992
993   handleSiteAllowedInstances(i: SiteForm, event: any) {
994     let list = splitToList(event.target.value);
995     i.setState(s => ((s.siteForm.allowed_instances = list), s));
996   }
997
998   handleSiteBlockedInstances(i: SiteForm, event: any) {
999     let list = splitToList(event.target.value);
1000     i.setState(s => ((s.siteForm.blocked_instances = list), s));
1001   }
1002
1003   handleSiteNameChange(i: SiteForm, event: any) {
1004     i.state.siteForm.name = event.target.value;
1005     i.setState(i.state);
1006   }
1007
1008   handleSiteSidebarChange(val: string) {
1009     this.setState(s => ((s.siteForm.sidebar = val), s));
1010   }
1011
1012   handleSiteLegalInfoChange(val: string) {
1013     this.setState(s => ((s.siteForm.legal_information = val), s));
1014   }
1015
1016   handleTaglineChange(i: SiteForm, index: number, val: string) {
1017     let taglines = i.state.siteForm.taglines;
1018     if (taglines) {
1019       taglines[index] = val;
1020       i.setState(i.state);
1021     }
1022   }
1023
1024   handleDeleteTaglineClick(
1025     i: SiteForm,
1026     index: number,
1027     event: InfernoMouseEvent<HTMLButtonElement>
1028   ) {
1029     event.preventDefault();
1030     let taglines = i.state.siteForm.taglines;
1031     if (taglines) {
1032       taglines.splice(index, 1);
1033       i.state.siteForm.taglines = undefined;
1034       i.setState(i.state);
1035       i.state.siteForm.taglines = taglines;
1036       i.setState(i.state);
1037     }
1038   }
1039
1040   handleAddTaglineClick(
1041     i: SiteForm,
1042     event: InfernoMouseEvent<HTMLButtonElement>
1043   ) {
1044     event.preventDefault();
1045     if (!i.state.siteForm.taglines) {
1046       i.state.siteForm.taglines = [];
1047     }
1048     i.state.siteForm.taglines.push("");
1049     i.setState(i.state);
1050   }
1051
1052   handleSiteApplicationQuestionChange(val: string) {
1053     this.setState(s => ((s.siteForm.application_question = val), s));
1054   }
1055
1056   handleSiteDescChange(i: SiteForm, event: any) {
1057     i.state.siteForm.description = event.target.value;
1058     i.setState(i.state);
1059   }
1060
1061   handleSiteEnableNsfwChange(i: SiteForm, event: any) {
1062     i.state.siteForm.enable_nsfw = event.target.checked;
1063     i.setState(i.state);
1064   }
1065
1066   handleSiteRegistrationModeChange(i: SiteForm, event: any) {
1067     i.state.siteForm.registration_mode = event.target.value;
1068     i.setState(i.state);
1069   }
1070
1071   handleSiteCommunityCreationAdminOnly(i: SiteForm, event: any) {
1072     i.state.siteForm.community_creation_admin_only = event.target.checked;
1073     i.setState(i.state);
1074   }
1075
1076   handleSiteEnableDownvotesChange(i: SiteForm, event: any) {
1077     i.state.siteForm.enable_downvotes = event.target.checked;
1078     i.setState(i.state);
1079   }
1080
1081   handleSiteRequireEmailVerification(i: SiteForm, event: any) {
1082     i.state.siteForm.require_email_verification = event.target.checked;
1083     i.setState(i.state);
1084   }
1085
1086   handleSiteApplicationEmailAdmins(i: SiteForm, event: any) {
1087     i.state.siteForm.application_email_admins = event.target.checked;
1088     i.setState(i.state);
1089   }
1090
1091   handleSiteReportsEmailAdmins(i: SiteForm, event: any) {
1092     i.state.siteForm.reports_email_admins = event.target.checked;
1093     i.setState(i.state);
1094   }
1095
1096   handleSitePrivateInstance(i: SiteForm, event: any) {
1097     i.state.siteForm.private_instance = event.target.checked;
1098     i.setState(i.state);
1099   }
1100
1101   handleSiteHideModlogModNames(i: SiteForm, event: any) {
1102     i.state.siteForm.hide_modlog_mod_names = event.target.checked;
1103     i.setState(i.state);
1104   }
1105
1106   handleSiteDefaultTheme(i: SiteForm, event: any) {
1107     i.state.siteForm.default_theme = event.target.value;
1108     i.setState(i.state);
1109   }
1110
1111   handleIconUpload(url: string) {
1112     this.setState(s => ((s.siteForm.icon = url), s));
1113   }
1114
1115   handleIconRemove() {
1116     this.setState(s => ((s.siteForm.icon = ""), s));
1117   }
1118
1119   handleBannerUpload(url: string) {
1120     this.setState(s => ((s.siteForm.banner = url), s));
1121   }
1122
1123   handleBannerRemove() {
1124     this.setState(s => ((s.siteForm.banner = ""), s));
1125   }
1126
1127   handleSiteSlurFilterRegex(i: SiteForm, event: any) {
1128     i.setState(s => ((s.siteForm.slur_filter_regex = event.target.value), s));
1129   }
1130
1131   handleSiteActorNameMaxLength(i: SiteForm, event: any) {
1132     i.setState(
1133       s => ((s.siteForm.actor_name_max_length = Number(event.target.value)), s)
1134     );
1135   }
1136
1137   handleSiteRateLimitMessage(i: SiteForm, event: any) {
1138     i.setState(
1139       s => ((s.siteForm.rate_limit_message = Number(event.target.value)), s)
1140     );
1141   }
1142
1143   handleSiteRateLimitMessagePerSecond(i: SiteForm, event: any) {
1144     i.setState(
1145       s => (
1146         (s.siteForm.rate_limit_message_per_second = Number(event.target.value)),
1147         s
1148       )
1149     );
1150   }
1151
1152   handleSiteRateLimitPost(i: SiteForm, event: any) {
1153     i.setState(
1154       s => ((s.siteForm.rate_limit_post = Number(event.target.value)), s)
1155     );
1156   }
1157
1158   handleSiteRateLimitPostPerSecond(i: SiteForm, event: any) {
1159     i.setState(
1160       s => (
1161         (s.siteForm.rate_limit_post_per_second = Number(event.target.value)), s
1162       )
1163     );
1164   }
1165
1166   handleSiteRateLimitImage(i: SiteForm, event: any) {
1167     i.setState(
1168       s => ((s.siteForm.rate_limit_image = Number(event.target.value)), s)
1169     );
1170   }
1171
1172   handleSiteRateLimitImagePerSecond(i: SiteForm, event: any) {
1173     i.setState(
1174       s => (
1175         (s.siteForm.rate_limit_image_per_second = Number(event.target.value)), s
1176       )
1177     );
1178   }
1179
1180   handleSiteRateLimitComment(i: SiteForm, event: any) {
1181     i.setState(
1182       s => ((s.siteForm.rate_limit_comment = Number(event.target.value)), s)
1183     );
1184   }
1185
1186   handleSiteRateLimitCommentPerSecond(i: SiteForm, event: any) {
1187     i.setState(
1188       s => (
1189         (s.siteForm.rate_limit_comment_per_second = Number(event.target.value)),
1190         s
1191       )
1192     );
1193   }
1194
1195   handleSiteRateLimitSearch(i: SiteForm, event: any) {
1196     i.setState(
1197       s => ((s.siteForm.rate_limit_search = Number(event.target.value)), s)
1198     );
1199   }
1200
1201   handleSiteRateLimitSearchPerSecond(i: SiteForm, event: any) {
1202     i.setState(
1203       s => (
1204         (s.siteForm.rate_limit_search_per_second = Number(event.target.value)),
1205         s
1206       )
1207     );
1208   }
1209
1210   handleSiteRateLimitRegister(i: SiteForm, event: any) {
1211     i.setState(
1212       s => ((s.siteForm.rate_limit_register = Number(event.target.value)), s)
1213     );
1214   }
1215
1216   handleSiteRateLimitRegisterPerSecond(i: SiteForm, event: any) {
1217     i.setState(
1218       s => (
1219         (s.siteForm.rate_limit_register_per_second = Number(
1220           event.target.value
1221         )),
1222         s
1223       )
1224     );
1225   }
1226
1227   handleSiteFederationEnabled(i: SiteForm, event: any) {
1228     i.state.siteForm.federation_enabled = event.target.checked;
1229     i.setState(i.state);
1230   }
1231
1232   handleSiteFederationDebug(i: SiteForm, event: any) {
1233     i.state.siteForm.federation_debug = event.target.checked;
1234     i.setState(i.state);
1235   }
1236
1237   handleSiteFederationWorkerCount(i: SiteForm, event: any) {
1238     i.setState(
1239       s => (
1240         (s.siteForm.federation_worker_count = Number(event.target.value)), s
1241       )
1242     );
1243   }
1244
1245   handleSiteCaptchaEnabled(i: SiteForm, event: any) {
1246     i.state.siteForm.captcha_enabled = event.target.checked;
1247     i.setState(i.state);
1248   }
1249
1250   handleSiteCaptchaDifficulty(i: SiteForm, event: any) {
1251     i.setState(s => ((s.siteForm.captcha_difficulty = event.target.value), s));
1252   }
1253
1254   handleDiscussionLanguageChange(val: number[]) {
1255     this.setState(s => ((s.siteForm.discussion_languages = val), s));
1256   }
1257
1258   handleDefaultPostListingTypeChange(val: ListingType) {
1259     this.setState(
1260       s => (
1261         (s.siteForm.default_post_listing_type = ListingType[ListingType[val]]),
1262         s
1263       )
1264     );
1265   }
1266 }
1267
1268 function splitToList(commaList: string): string[] {
1269   if (commaList !== "") {
1270     let list = commaList.trim().split(",");
1271     return list;
1272   } else {
1273     return [];
1274   }
1275 }