]> Untitled Git - lemmy-ui.git/blob - src/shared/components/home/site-form.tsx
Removing monads. Fixes #884 (#886)
[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 { Icon, 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         hide_modlog_mod_names: ls.hide_modlog_mod_names,
86         discussion_languages: this.props.siteRes.discussion_languages,
87         slur_filter_regex: ls.slur_filter_regex,
88         actor_name_max_length: ls.actor_name_max_length,
89         rate_limit_message: lsrl.message,
90         rate_limit_message_per_second: lsrl.message_per_second,
91         rate_limit_comment: lsrl.comment,
92         rate_limit_comment_per_second: lsrl.comment_per_second,
93         rate_limit_image: lsrl.image,
94         rate_limit_image_per_second: lsrl.image_per_second,
95         rate_limit_post: lsrl.post,
96         rate_limit_post_per_second: lsrl.post_per_second,
97         rate_limit_register: lsrl.register,
98         rate_limit_register_per_second: lsrl.register_per_second,
99         rate_limit_search: lsrl.search,
100         rate_limit_search_per_second: lsrl.search_per_second,
101         federation_enabled: ls.federation_enabled,
102         federation_debug: ls.federation_debug,
103         federation_worker_count: ls.federation_worker_count,
104         captcha_enabled: ls.captcha_enabled,
105         captcha_difficulty: ls.captcha_difficulty,
106         allowed_instances: this.props.siteRes.federated_instances?.allowed,
107         blocked_instances: this.props.siteRes.federated_instances?.blocked,
108         taglines: this.props.siteRes.taglines?.map(x => x.content),
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               <label
396                 className="form-check-label mr-2"
397                 htmlFor="create-site-default-theme"
398               >
399                 {i18n.t("theme")}
400               </label>
401               <select
402                 id="create-site-default-theme"
403                 value={this.state.siteForm.default_theme}
404                 onChange={linkEvent(this, this.handleSiteDefaultTheme)}
405                 className="custom-select w-auto"
406               >
407                 <option value="browser">{i18n.t("browser_default")}</option>
408                 {this.state.themeList?.map(theme => (
409                   <option key={theme} value={theme}>
410                     {theme}
411                   </option>
412                 ))}
413               </select>
414             </div>
415           </div>
416           {this.props.showLocal && (
417             <form className="form-group row">
418               <label className="col-sm-3">{i18n.t("listing_type")}</label>
419               <div className="col-sm-9">
420                 <ListingTypeSelect
421                   type_={
422                     ListingType[
423                       this.state.siteForm.default_post_listing_type ?? "Local"
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={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={this.state.siteForm.hide_modlog_mod_names}
460                   onChange={linkEvent(this, this.handleSiteHideModlogModNames)}
461                 />
462                 <label
463                   className="form-check-label"
464                   htmlFor="create-site-hide-modlog-mod-names"
465                 >
466                   {i18n.t("hide_modlog_mod_names")}
467                 </label>
468               </div>
469             </div>
470           </div>
471           <div className="form-group row">
472             <label
473               className="col-12 col-form-label"
474               htmlFor="create-site-slur-filter-regex"
475             >
476               {i18n.t("slur_filter_regex")}
477             </label>
478             <div className="col-12">
479               <input
480                 type="text"
481                 id="create-site-slur-filter-regex"
482                 placeholder="(word1|word2)"
483                 className="form-control"
484                 value={this.state.siteForm.slur_filter_regex}
485                 onInput={linkEvent(this, this.handleSiteSlurFilterRegex)}
486                 minLength={3}
487               />
488             </div>
489           </div>
490           <LanguageSelect
491             allLanguages={this.props.siteRes.all_languages}
492             siteLanguages={this.props.siteRes.discussion_languages}
493             selectedLanguageIds={this.state.siteForm.discussion_languages}
494             multiple={true}
495             onChange={this.handleDiscussionLanguageChange}
496             showAll
497           />
498           <div className="form-group row">
499             <label
500               className="col-12 col-form-label"
501               htmlFor="create-site-actor-name"
502             >
503               {i18n.t("actor_name_max_length")}
504             </label>
505             <div className="col-12">
506               <input
507                 type="number"
508                 id="create-site-actor-name"
509                 className="form-control"
510                 min={5}
511                 value={this.state.siteForm.actor_name_max_length}
512                 onInput={linkEvent(this, this.handleSiteActorNameMaxLength)}
513               />
514             </div>
515           </div>
516           <div className="form-group row">
517             <div className="col-12">
518               <div className="form-check">
519                 <input
520                   className="form-check-input"
521                   id="create-site-federation-enabled"
522                   type="checkbox"
523                   checked={this.state.siteForm.federation_enabled}
524                   onChange={linkEvent(this, this.handleSiteFederationEnabled)}
525                 />
526                 <label
527                   className="form-check-label"
528                   htmlFor="create-site-federation-enabled"
529                 >
530                   {i18n.t("federation_enabled")}
531                 </label>
532               </div>
533             </div>
534           </div>
535           {this.state.siteForm.federation_enabled && (
536             <>
537               <div className="form-group row">
538                 <label
539                   className="col-12 col-form-label"
540                   htmlFor="create-site-allowed-instances"
541                 >
542                   {i18n.t("allowed_instances")}
543                 </label>
544                 <div className="col-12">
545                   <input
546                     type="text"
547                     placeholder="instance1.tld,instance2.tld"
548                     id="create-site-allowed-instances"
549                     className="form-control"
550                     value={this.instancesToString(
551                       this.state.siteForm.allowed_instances
552                     )}
553                     onInput={linkEvent(this, this.handleSiteAllowedInstances)}
554                   />
555                 </div>
556               </div>
557               <div className="form-group row">
558                 <label
559                   className="col-12 col-form-label"
560                   htmlFor="create-site-blocked-instances"
561                 >
562                   {i18n.t("blocked_instances")}
563                 </label>
564                 <div className="col-12">
565                   <input
566                     type="text"
567                     placeholder="instance1.tld,instance2.tld"
568                     id="create-site-blocked-instances"
569                     className="form-control"
570                     value={this.instancesToString(
571                       this.state.siteForm.blocked_instances
572                     )}
573                     onInput={linkEvent(this, this.handleSiteBlockedInstances)}
574                   />
575                 </div>
576               </div>
577               <div className="form-group row">
578                 <div className="col-12">
579                   <div className="form-check">
580                     <input
581                       className="form-check-input"
582                       id="create-site-federation-debug"
583                       type="checkbox"
584                       checked={this.state.siteForm.federation_debug}
585                       onChange={linkEvent(this, this.handleSiteFederationDebug)}
586                     />
587                     <label
588                       className="form-check-label"
589                       htmlFor="create-site-federation-debug"
590                     >
591                       {i18n.t("federation_debug")}
592                     </label>
593                   </div>
594                 </div>
595               </div>
596               <div className="form-group row">
597                 <label
598                   className="col-12 col-form-label"
599                   htmlFor="create-site-federation-worker-count"
600                 >
601                   {i18n.t("federation_worker_count")}
602                 </label>
603                 <div className="col-12">
604                   <input
605                     type="number"
606                     id="create-site-federation-worker-count"
607                     className="form-control"
608                     min={0}
609                     value={this.state.siteForm.federation_worker_count}
610                     onInput={linkEvent(
611                       this,
612                       this.handleSiteFederationWorkerCount
613                     )}
614                   />
615                 </div>
616               </div>
617             </>
618           )}
619           <div className="form-group row">
620             <div className="col-12">
621               <div className="form-check">
622                 <input
623                   className="form-check-input"
624                   id="create-site-captcha-enabled"
625                   type="checkbox"
626                   checked={this.state.siteForm.captcha_enabled}
627                   onChange={linkEvent(this, this.handleSiteCaptchaEnabled)}
628                 />
629                 <label
630                   className="form-check-label"
631                   htmlFor="create-site-captcha-enabled"
632                 >
633                   {i18n.t("captcha_enabled")}
634                 </label>
635               </div>
636             </div>
637           </div>
638           {this.state.siteForm.captcha_enabled && (
639             <div className="form-group row">
640               <div className="col-12">
641                 <label
642                   className="form-check-label mr-2"
643                   htmlFor="create-site-captcha-difficulty"
644                 >
645                   {i18n.t("captcha_difficulty")}
646                 </label>
647                 <select
648                   id="create-site-captcha-difficulty"
649                   value={this.state.siteForm.captcha_difficulty}
650                   onChange={linkEvent(this, this.handleSiteCaptchaDifficulty)}
651                   className="custom-select w-auto"
652                 >
653                   <option value="easy">{i18n.t("easy")}</option>
654                   <option value="medium">{i18n.t("medium")}</option>
655                   <option value="hard">{i18n.t("hard")}</option>
656                 </select>
657               </div>
658             </div>
659           )}
660           <div className="form-group row">
661             <label
662               className="col-12 col-form-label"
663               htmlFor="create-site-rate-limit-message"
664             >
665               {i18n.t("rate_limit_message")}
666             </label>
667             <div className="col-12">
668               <input
669                 type="number"
670                 id="create-site-rate-limit-message"
671                 className="form-control"
672                 min={0}
673                 value={this.state.siteForm.rate_limit_message}
674                 onInput={linkEvent(this, this.handleSiteRateLimitMessage)}
675               />
676             </div>
677           </div>
678           <div className="form-group row">
679             <label
680               className="col-12 col-form-label"
681               htmlFor="create-site-rate-limit-message-per-second"
682             >
683               {i18n.t("per_second")}
684             </label>
685             <div className="col-12">
686               <input
687                 type="number"
688                 id="create-site-rate-limit-message-per-second"
689                 className="form-control"
690                 min={0}
691                 value={this.state.siteForm.rate_limit_message_per_second}
692                 onInput={linkEvent(
693                   this,
694                   this.handleSiteRateLimitMessagePerSecond
695                 )}
696               />
697             </div>
698           </div>
699           <div className="form-group row">
700             <label
701               className="col-12 col-form-label"
702               htmlFor="create-site-rate-limit-post"
703             >
704               {i18n.t("rate_limit_post")}
705             </label>
706             <div className="col-12">
707               <input
708                 type="number"
709                 id="create-site-rate-limit-post"
710                 className="form-control"
711                 min={0}
712                 value={this.state.siteForm.rate_limit_post}
713                 onInput={linkEvent(this, this.handleSiteRateLimitPost)}
714               />
715             </div>
716           </div>
717           <div className="form-group row">
718             <label
719               className="col-12 col-form-label"
720               htmlFor="create-site-rate-limit-post-per-second"
721             >
722               {i18n.t("per_second")}
723             </label>
724             <div className="col-12">
725               <input
726                 type="number"
727                 id="create-site-rate-limit-post-per-second"
728                 className="form-control"
729                 min={0}
730                 value={this.state.siteForm.rate_limit_post_per_second}
731                 onInput={linkEvent(this, this.handleSiteRateLimitPostPerSecond)}
732               />
733             </div>
734           </div>
735           <div className="form-group row">
736             <label
737               className="col-12 col-form-label"
738               htmlFor="create-site-rate-limit-register"
739             >
740               {i18n.t("rate_limit_register")}
741             </label>
742             <div className="col-12">
743               <input
744                 type="number"
745                 id="create-site-rate-limit-register"
746                 className="form-control"
747                 min={0}
748                 value={this.state.siteForm.rate_limit_register}
749                 onInput={linkEvent(this, this.handleSiteRateLimitRegister)}
750               />
751             </div>
752           </div>
753           <div className="form-group row">
754             <label
755               className="col-12 col-form-label"
756               htmlFor="create-site-rate-limit-register-per-second"
757             >
758               {i18n.t("per_second")}
759             </label>
760             <div className="col-12">
761               <input
762                 type="number"
763                 id="create-site-rate-limit-register-per-second"
764                 className="form-control"
765                 min={0}
766                 value={this.state.siteForm.rate_limit_register_per_second}
767                 onInput={linkEvent(
768                   this,
769                   this.handleSiteRateLimitRegisterPerSecond
770                 )}
771               />
772             </div>
773           </div>
774           <div className="form-group row">
775             <label
776               className="col-12 col-form-label"
777               htmlFor="create-site-rate-limit-image"
778             >
779               {i18n.t("rate_limit_image")}
780             </label>
781             <div className="col-12">
782               <input
783                 type="number"
784                 id="create-site-rate-limit-image"
785                 className="form-control"
786                 min={0}
787                 value={this.state.siteForm.rate_limit_image}
788                 onInput={linkEvent(this, this.handleSiteRateLimitImage)}
789               />
790             </div>
791           </div>
792           <div className="form-group row">
793             <label
794               className="col-12 col-form-label"
795               htmlFor="create-site-rate-limit-image-per-second"
796             >
797               {i18n.t("per_second")}
798             </label>
799             <div className="col-12">
800               <input
801                 type="number"
802                 id="create-site-rate-limit-image-per-second"
803                 className="form-control"
804                 min={0}
805                 value={this.state.siteForm.rate_limit_image_per_second}
806                 onInput={linkEvent(
807                   this,
808                   this.handleSiteRateLimitImagePerSecond
809                 )}
810               />
811             </div>
812           </div>
813           <div className="form-group row">
814             <label
815               className="col-12 col-form-label"
816               htmlFor="create-site-rate-limit-comment"
817             >
818               {i18n.t("rate_limit_comment")}
819             </label>
820             <div className="col-12">
821               <input
822                 type="number"
823                 id="create-site-rate-limit-comment"
824                 className="form-control"
825                 min={0}
826                 value={this.state.siteForm.rate_limit_comment}
827                 onInput={linkEvent(this, this.handleSiteRateLimitComment)}
828               />
829             </div>
830           </div>
831           <div className="form-group row">
832             <label
833               className="col-12 col-form-label"
834               htmlFor="create-site-rate-limit-comment-per-second"
835             >
836               {i18n.t("per_second")}
837             </label>
838             <div className="col-12">
839               <input
840                 type="number"
841                 id="create-site-rate-limit-comment-per-second"
842                 className="form-control"
843                 min={0}
844                 value={this.state.siteForm.rate_limit_comment_per_second}
845                 onInput={linkEvent(
846                   this,
847                   this.handleSiteRateLimitCommentPerSecond
848                 )}
849               />
850             </div>
851           </div>
852           <div className="form-group row">
853             <label
854               className="col-12 col-form-label"
855               htmlFor="create-site-rate-limit-search"
856             >
857               {i18n.t("rate_limit_search")}
858             </label>
859             <div className="col-12">
860               <input
861                 type="number"
862                 id="create-site-rate-limit-search"
863                 className="form-control"
864                 min={0}
865                 value={this.state.siteForm.rate_limit_search}
866                 onInput={linkEvent(this, this.handleSiteRateLimitSearch)}
867               />
868             </div>
869           </div>
870           <div className="form-group row">
871             <label
872               className="col-12 col-form-label"
873               htmlFor="create-site-rate-limit-search-per-second"
874             >
875               {i18n.t("per_second")}
876             </label>
877             <div className="col-12">
878               <input
879                 type="number"
880                 id="create-site-rate-limit-search-per-second"
881                 className="form-control"
882                 min={0}
883                 value={this.state.siteForm.rate_limit_search_per_second}
884                 onInput={linkEvent(
885                   this,
886                   this.handleSiteRateLimitSearchPerSecond
887                 )}
888               />
889             </div>
890           </div>
891           <div className="form-group row">
892             <h5 className="col-12">{i18n.t("taglines")}</h5>
893             <div className="table-responsive col-12">
894               <table id="taglines_table" className="table table-sm table-hover">
895                 <thead className="pointer"></thead>
896                 <tbody>
897                   {this.state.siteForm.taglines?.map((cv, index) => (
898                     <tr key={index}>
899                       <td>
900                         <MarkdownTextArea
901                           initialContent={cv}
902                           onContentChange={s =>
903                             this.handleTaglineChange(this, index, s)
904                           }
905                           hideNavigationWarnings
906                           allLanguages={this.props.siteRes.all_languages}
907                           siteLanguages={
908                             this.props.siteRes.discussion_languages
909                           }
910                         />
911                       </td>
912                       <td className="text-right">
913                         <button
914                           className="btn btn-link btn-animate text-muted"
915                           onClick={e =>
916                             this.handleDeleteTaglineClick(this, index, e)
917                           }
918                           data-tippy-content={i18n.t("delete")}
919                           aria-label={i18n.t("delete")}
920                         >
921                           <Icon
922                             icon="trash"
923                             classes={`icon-inline text-danger`}
924                           />
925                         </button>
926                       </td>
927                     </tr>
928                   ))}
929                 </tbody>
930               </table>
931               <button
932                 className="btn btn-sm btn-secondary mr-2"
933                 onClick={e => this.handleAddTaglineClick(this, e)}
934               >
935                 {i18n.t("add_tagline")}
936               </button>
937             </div>
938           </div>
939           <div className="form-group row">
940             <div className="col-12">
941               <button
942                 type="submit"
943                 className="btn btn-secondary mr-2"
944                 disabled={this.state.loading}
945               >
946                 {this.state.loading ? (
947                   <Spinner />
948                 ) : siteSetup ? (
949                   capitalizeFirstLetter(i18n.t("save"))
950                 ) : (
951                   capitalizeFirstLetter(i18n.t("create"))
952                 )}
953               </button>
954             </div>
955           </div>
956         </form>
957       </>
958     );
959   }
960
961   handleCreateSiteSubmit(i: SiteForm, event: any) {
962     event.preventDefault();
963     i.setState({ loading: true });
964     let auth = myAuth() ?? "TODO";
965     i.setState(s => ((s.siteForm.auth = auth), s));
966     if (i.props.siteRes.site_view.local_site.site_setup) {
967       WebSocketService.Instance.send(wsClient.editSite(i.state.siteForm));
968     } else {
969       let sForm = i.state.siteForm;
970       let form: CreateSite = {
971         name: sForm.name ?? "My site",
972         sidebar: sForm.sidebar,
973         description: sForm.description,
974         icon: sForm.icon,
975         banner: sForm.banner,
976         community_creation_admin_only: sForm.community_creation_admin_only,
977         enable_nsfw: sForm.enable_nsfw,
978         enable_downvotes: sForm.enable_downvotes,
979         application_question: sForm.application_question,
980         registration_mode: sForm.registration_mode,
981         require_email_verification: sForm.require_email_verification,
982         private_instance: sForm.private_instance,
983         default_theme: sForm.default_theme,
984         default_post_listing_type: sForm.default_post_listing_type,
985         application_email_admins: sForm.application_email_admins,
986         hide_modlog_mod_names: sForm.hide_modlog_mod_names,
987         legal_information: sForm.legal_information,
988         slur_filter_regex: sForm.slur_filter_regex,
989         actor_name_max_length: sForm.actor_name_max_length,
990         rate_limit_message: sForm.rate_limit_message,
991         rate_limit_message_per_second: sForm.rate_limit_message_per_second,
992         rate_limit_comment: sForm.rate_limit_comment,
993         rate_limit_comment_per_second: sForm.rate_limit_comment_per_second,
994         rate_limit_image: sForm.rate_limit_image,
995         rate_limit_image_per_second: sForm.rate_limit_image_per_second,
996         rate_limit_post: sForm.rate_limit_post,
997         rate_limit_post_per_second: sForm.rate_limit_post_per_second,
998         rate_limit_register: sForm.rate_limit_register,
999         rate_limit_register_per_second: sForm.rate_limit_register_per_second,
1000         rate_limit_search: sForm.rate_limit_search,
1001         rate_limit_search_per_second: sForm.rate_limit_search_per_second,
1002         federation_enabled: sForm.federation_enabled,
1003         federation_debug: sForm.federation_debug,
1004         federation_worker_count: sForm.federation_worker_count,
1005         captcha_enabled: sForm.captcha_enabled,
1006         captcha_difficulty: sForm.captcha_difficulty,
1007         allowed_instances: sForm.allowed_instances,
1008         blocked_instances: sForm.blocked_instances,
1009         discussion_languages: sForm.discussion_languages,
1010         taglines: sForm.taglines,
1011         auth,
1012       };
1013       WebSocketService.Instance.send(wsClient.createSite(form));
1014     }
1015     i.setState(i.state);
1016   }
1017
1018   instancesToString(opt?: string[]): string {
1019     return opt ? opt.join(",") : "";
1020   }
1021
1022   handleSiteAllowedInstances(i: SiteForm, event: any) {
1023     let list = splitToList(event.target.value);
1024     i.setState(s => ((s.siteForm.allowed_instances = list), s));
1025   }
1026
1027   handleSiteBlockedInstances(i: SiteForm, event: any) {
1028     let list = splitToList(event.target.value);
1029     i.setState(s => ((s.siteForm.blocked_instances = list), s));
1030   }
1031
1032   handleSiteNameChange(i: SiteForm, event: any) {
1033     i.state.siteForm.name = event.target.value;
1034     i.setState(i.state);
1035   }
1036
1037   handleSiteSidebarChange(val: string) {
1038     this.setState(s => ((s.siteForm.sidebar = val), s));
1039   }
1040
1041   handleSiteLegalInfoChange(val: string) {
1042     this.setState(s => ((s.siteForm.legal_information = val), s));
1043   }
1044
1045   handleTaglineChange(i: SiteForm, index: number, val: string) {
1046     let taglines = i.state.siteForm.taglines;
1047     if (taglines) {
1048       taglines[index] = val;
1049       i.setState(i.state);
1050     }
1051   }
1052
1053   handleDeleteTaglineClick(
1054     i: SiteForm,
1055     index: number,
1056     event: InfernoMouseEvent<HTMLButtonElement>
1057   ) {
1058     event.preventDefault();
1059     let taglines = i.state.siteForm.taglines;
1060     if (taglines) {
1061       taglines.splice(index, 1);
1062       i.state.siteForm.taglines = undefined;
1063       i.setState(i.state);
1064       i.state.siteForm.taglines = taglines;
1065       i.setState(i.state);
1066     }
1067   }
1068
1069   handleAddTaglineClick(
1070     i: SiteForm,
1071     event: InfernoMouseEvent<HTMLButtonElement>
1072   ) {
1073     event.preventDefault();
1074     if (!i.state.siteForm.taglines) {
1075       i.state.siteForm.taglines = [];
1076     }
1077     i.state.siteForm.taglines.push("");
1078     i.setState(i.state);
1079   }
1080
1081   handleSiteApplicationQuestionChange(val: string) {
1082     this.setState(s => ((s.siteForm.application_question = val), s));
1083   }
1084
1085   handleSiteDescChange(i: SiteForm, event: any) {
1086     i.state.siteForm.description = event.target.value;
1087     i.setState(i.state);
1088   }
1089
1090   handleSiteEnableNsfwChange(i: SiteForm, event: any) {
1091     i.state.siteForm.enable_nsfw = event.target.checked;
1092     i.setState(i.state);
1093   }
1094
1095   handleSiteRegistrationModeChange(i: SiteForm, event: any) {
1096     i.state.siteForm.registration_mode = event.target.value;
1097     i.setState(i.state);
1098   }
1099
1100   handleSiteCommunityCreationAdminOnly(i: SiteForm, event: any) {
1101     i.state.siteForm.community_creation_admin_only = event.target.checked;
1102     i.setState(i.state);
1103   }
1104
1105   handleSiteEnableDownvotesChange(i: SiteForm, event: any) {
1106     i.state.siteForm.enable_downvotes = event.target.checked;
1107     i.setState(i.state);
1108   }
1109
1110   handleSiteRequireEmailVerification(i: SiteForm, event: any) {
1111     i.state.siteForm.require_email_verification = event.target.checked;
1112     i.setState(i.state);
1113   }
1114
1115   handleSiteApplicationEmailAdmins(i: SiteForm, event: any) {
1116     i.state.siteForm.application_email_admins = event.target.checked;
1117     i.setState(i.state);
1118   }
1119
1120   handleSitePrivateInstance(i: SiteForm, event: any) {
1121     i.state.siteForm.private_instance = event.target.checked;
1122     i.setState(i.state);
1123   }
1124
1125   handleSiteHideModlogModNames(i: SiteForm, event: any) {
1126     i.state.siteForm.hide_modlog_mod_names = event.target.checked;
1127     i.setState(i.state);
1128   }
1129
1130   handleSiteDefaultTheme(i: SiteForm, event: any) {
1131     i.state.siteForm.default_theme = event.target.value;
1132     i.setState(i.state);
1133   }
1134
1135   handleIconUpload(url: string) {
1136     this.setState(s => ((s.siteForm.icon = url), s));
1137   }
1138
1139   handleIconRemove() {
1140     this.setState(s => ((s.siteForm.icon = ""), s));
1141   }
1142
1143   handleBannerUpload(url: string) {
1144     this.setState(s => ((s.siteForm.banner = url), s));
1145   }
1146
1147   handleBannerRemove() {
1148     this.setState(s => ((s.siteForm.banner = ""), s));
1149   }
1150
1151   handleSiteSlurFilterRegex(i: SiteForm, event: any) {
1152     i.setState(s => ((s.siteForm.slur_filter_regex = event.target.value), s));
1153   }
1154
1155   handleSiteActorNameMaxLength(i: SiteForm, event: any) {
1156     i.setState(
1157       s => ((s.siteForm.actor_name_max_length = Number(event.target.value)), s)
1158     );
1159   }
1160
1161   handleSiteRateLimitMessage(i: SiteForm, event: any) {
1162     i.setState(
1163       s => ((s.siteForm.rate_limit_message = Number(event.target.value)), s)
1164     );
1165   }
1166
1167   handleSiteRateLimitMessagePerSecond(i: SiteForm, event: any) {
1168     i.setState(
1169       s => (
1170         (s.siteForm.rate_limit_message_per_second = Number(event.target.value)),
1171         s
1172       )
1173     );
1174   }
1175
1176   handleSiteRateLimitPost(i: SiteForm, event: any) {
1177     i.setState(
1178       s => ((s.siteForm.rate_limit_post = Number(event.target.value)), s)
1179     );
1180   }
1181
1182   handleSiteRateLimitPostPerSecond(i: SiteForm, event: any) {
1183     i.setState(
1184       s => (
1185         (s.siteForm.rate_limit_post_per_second = Number(event.target.value)), s
1186       )
1187     );
1188   }
1189
1190   handleSiteRateLimitImage(i: SiteForm, event: any) {
1191     i.setState(
1192       s => ((s.siteForm.rate_limit_image = Number(event.target.value)), s)
1193     );
1194   }
1195
1196   handleSiteRateLimitImagePerSecond(i: SiteForm, event: any) {
1197     i.setState(
1198       s => (
1199         (s.siteForm.rate_limit_image_per_second = Number(event.target.value)), s
1200       )
1201     );
1202   }
1203
1204   handleSiteRateLimitComment(i: SiteForm, event: any) {
1205     i.setState(
1206       s => ((s.siteForm.rate_limit_comment = Number(event.target.value)), s)
1207     );
1208   }
1209
1210   handleSiteRateLimitCommentPerSecond(i: SiteForm, event: any) {
1211     i.setState(
1212       s => (
1213         (s.siteForm.rate_limit_comment_per_second = Number(event.target.value)),
1214         s
1215       )
1216     );
1217   }
1218
1219   handleSiteRateLimitSearch(i: SiteForm, event: any) {
1220     i.setState(
1221       s => ((s.siteForm.rate_limit_search = Number(event.target.value)), s)
1222     );
1223   }
1224
1225   handleSiteRateLimitSearchPerSecond(i: SiteForm, event: any) {
1226     i.setState(
1227       s => (
1228         (s.siteForm.rate_limit_search_per_second = Number(event.target.value)),
1229         s
1230       )
1231     );
1232   }
1233
1234   handleSiteRateLimitRegister(i: SiteForm, event: any) {
1235     i.setState(
1236       s => ((s.siteForm.rate_limit_register = Number(event.target.value)), s)
1237     );
1238   }
1239
1240   handleSiteRateLimitRegisterPerSecond(i: SiteForm, event: any) {
1241     i.setState(
1242       s => (
1243         (s.siteForm.rate_limit_register_per_second = Number(
1244           event.target.value
1245         )),
1246         s
1247       )
1248     );
1249   }
1250
1251   handleSiteFederationEnabled(i: SiteForm, event: any) {
1252     i.state.siteForm.federation_enabled = event.target.checked;
1253     i.setState(i.state);
1254   }
1255
1256   handleSiteFederationDebug(i: SiteForm, event: any) {
1257     i.state.siteForm.federation_debug = event.target.checked;
1258     i.setState(i.state);
1259   }
1260
1261   handleSiteFederationWorkerCount(i: SiteForm, event: any) {
1262     i.setState(
1263       s => (
1264         (s.siteForm.federation_worker_count = Number(event.target.value)), s
1265       )
1266     );
1267   }
1268
1269   handleSiteCaptchaEnabled(i: SiteForm, event: any) {
1270     i.state.siteForm.captcha_enabled = event.target.checked;
1271     i.setState(i.state);
1272   }
1273
1274   handleSiteCaptchaDifficulty(i: SiteForm, event: any) {
1275     i.setState(s => ((s.siteForm.captcha_difficulty = event.target.value), s));
1276   }
1277
1278   handleDiscussionLanguageChange(val: number[]) {
1279     this.setState(s => ((s.siteForm.discussion_languages = val), s));
1280   }
1281
1282   handleDefaultPostListingTypeChange(val: ListingType) {
1283     this.setState(
1284       s => (
1285         (s.siteForm.default_post_listing_type = ListingType[ListingType[val]]),
1286         s
1287       )
1288     );
1289   }
1290 }
1291
1292 function splitToList(commaList: string): string[] {
1293   if (commaList !== "") {
1294     let list = commaList.trim().split(",");
1295     return list;
1296   } else {
1297     return [];
1298   }
1299 }