]> Untitled Git - lemmy-ui.git/blob - src/shared/components/home/site-form.tsx
Using auto-generated types from ts-rs. (#1003)
[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   GetFederatedInstancesResponse,
7   GetSiteResponse,
8   ListingType,
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   instancesRes?: GetFederatedInstancesResponse;
27   showLocal?: boolean;
28 }
29
30 interface SiteFormState {
31   siteForm: EditSite;
32   loading: boolean;
33   themeList?: string[];
34 }
35
36 export class SiteForm extends Component<SiteFormProps, SiteFormState> {
37   state: SiteFormState = {
38     siteForm: {
39       auth: "TODO",
40     },
41     loading: false,
42   };
43
44   constructor(props: any, context: any) {
45     super(props, context);
46
47     this.handleSiteSidebarChange = this.handleSiteSidebarChange.bind(this);
48     this.handleSiteLegalInfoChange = this.handleSiteLegalInfoChange.bind(this);
49     this.handleSiteApplicationQuestionChange =
50       this.handleSiteApplicationQuestionChange.bind(this);
51
52     this.handleIconUpload = this.handleIconUpload.bind(this);
53     this.handleIconRemove = this.handleIconRemove.bind(this);
54
55     this.handleBannerUpload = this.handleBannerUpload.bind(this);
56     this.handleBannerRemove = this.handleBannerRemove.bind(this);
57
58     this.handleDefaultPostListingTypeChange =
59       this.handleDefaultPostListingTypeChange.bind(this);
60
61     this.handleDiscussionLanguageChange =
62       this.handleDiscussionLanguageChange.bind(this);
63
64     let site = this.props.siteRes.site_view.site;
65     let ls = this.props.siteRes.site_view.local_site;
66     let lsrl = this.props.siteRes.site_view.local_site_rate_limit;
67     this.state = {
68       ...this.state,
69       siteForm: {
70         name: site.name,
71         sidebar: site.sidebar,
72         description: site.description,
73         enable_downvotes: ls.enable_downvotes,
74         registration_mode: ls.registration_mode,
75         enable_nsfw: ls.enable_nsfw,
76         community_creation_admin_only: ls.community_creation_admin_only,
77         icon: site.icon,
78         banner: site.banner,
79         require_email_verification: ls.require_email_verification,
80         application_question: ls.application_question,
81         private_instance: ls.private_instance,
82         default_theme: ls.default_theme,
83         default_post_listing_type: ls.default_post_listing_type,
84         legal_information: ls.legal_information,
85         application_email_admins: ls.application_email_admins,
86         reports_email_admins: ls.reports_email_admins,
87         hide_modlog_mod_names: ls.hide_modlog_mod_names,
88         discussion_languages: this.props.siteRes.discussion_languages,
89         slur_filter_regex: ls.slur_filter_regex,
90         actor_name_max_length: ls.actor_name_max_length,
91         rate_limit_message: lsrl.message,
92         rate_limit_message_per_second: lsrl.message_per_second,
93         rate_limit_comment: lsrl.comment,
94         rate_limit_comment_per_second: lsrl.comment_per_second,
95         rate_limit_image: lsrl.image,
96         rate_limit_image_per_second: lsrl.image_per_second,
97         rate_limit_post: lsrl.post,
98         rate_limit_post_per_second: lsrl.post_per_second,
99         rate_limit_register: lsrl.register,
100         rate_limit_register_per_second: lsrl.register_per_second,
101         rate_limit_search: lsrl.search,
102         rate_limit_search_per_second: lsrl.search_per_second,
103         federation_enabled: ls.federation_enabled,
104         federation_debug: ls.federation_debug,
105         federation_worker_count: ls.federation_worker_count,
106         captcha_enabled: ls.captcha_enabled,
107         captcha_difficulty: ls.captcha_difficulty,
108         allowed_instances:
109           this.props.instancesRes?.federated_instances?.allowed.map(
110             i => i.domain
111           ),
112         blocked_instances:
113           this.props.instancesRes?.federated_instances?.blocked.map(
114             i => i.domain
115           ),
116         auth: "TODO",
117       },
118     };
119   }
120
121   async componentDidMount() {
122     this.setState({ themeList: await fetchThemeList() });
123   }
124
125   // Necessary to stop the loading
126   componentWillReceiveProps() {
127     this.setState({ loading: false });
128   }
129
130   componentDidUpdate() {
131     if (
132       !this.state.loading &&
133       !this.props.siteRes.site_view.local_site.site_setup &&
134       (this.state.siteForm.name ||
135         this.state.siteForm.sidebar ||
136         this.state.siteForm.application_question ||
137         this.state.siteForm.description)
138     ) {
139       window.onbeforeunload = () => true;
140     } else {
141       window.onbeforeunload = null;
142     }
143   }
144
145   componentWillUnmount() {
146     window.onbeforeunload = null;
147   }
148
149   render() {
150     let siteSetup = this.props.siteRes.site_view.local_site.site_setup;
151     return (
152       <>
153         <Prompt
154           when={
155             !this.state.loading &&
156             !siteSetup &&
157             (this.state.siteForm.name ||
158               this.state.siteForm.sidebar ||
159               this.state.siteForm.application_question ||
160               this.state.siteForm.description)
161           }
162           message={i18n.t("block_leaving")}
163         />
164         <form onSubmit={linkEvent(this, this.handleCreateSiteSubmit)}>
165           <h5>{`${
166             siteSetup
167               ? capitalizeFirstLetter(i18n.t("save"))
168               : capitalizeFirstLetter(i18n.t("name"))
169           } ${i18n.t("your_site")}`}</h5>
170           <div className="form-group row">
171             <label className="col-12 col-form-label" htmlFor="create-site-name">
172               {i18n.t("name")}
173             </label>
174             <div className="col-12">
175               <input
176                 type="text"
177                 id="create-site-name"
178                 className="form-control"
179                 value={this.state.siteForm.name}
180                 onInput={linkEvent(this, this.handleSiteNameChange)}
181                 required
182                 minLength={3}
183                 maxLength={20}
184               />
185             </div>
186           </div>
187           <div className="form-group">
188             <label>{i18n.t("icon")}</label>
189             <ImageUploadForm
190               uploadTitle={i18n.t("upload_icon")}
191               imageSrc={this.state.siteForm.icon}
192               onUpload={this.handleIconUpload}
193               onRemove={this.handleIconRemove}
194               rounded
195             />
196           </div>
197           <div className="form-group">
198             <label>{i18n.t("banner")}</label>
199             <ImageUploadForm
200               uploadTitle={i18n.t("upload_banner")}
201               imageSrc={this.state.siteForm.banner}
202               onUpload={this.handleBannerUpload}
203               onRemove={this.handleBannerRemove}
204             />
205           </div>
206           <div className="form-group row">
207             <label className="col-12 col-form-label" htmlFor="site-desc">
208               {i18n.t("description")}
209             </label>
210             <div className="col-12">
211               <input
212                 type="text"
213                 className="form-control"
214                 id="site-desc"
215                 value={this.state.siteForm.description}
216                 onInput={linkEvent(this, this.handleSiteDescChange)}
217                 maxLength={150}
218               />
219             </div>
220           </div>
221           <div className="form-group row">
222             <label className="col-12 col-form-label">{i18n.t("sidebar")}</label>
223             <div className="col-12">
224               <MarkdownTextArea
225                 initialContent={this.state.siteForm.sidebar}
226                 onContentChange={this.handleSiteSidebarChange}
227                 hideNavigationWarnings
228                 allLanguages={[]}
229                 siteLanguages={[]}
230               />
231             </div>
232           </div>
233           <div className="form-group row">
234             <label className="col-12 col-form-label">
235               {i18n.t("legal_information")}
236             </label>
237             <div className="col-12">
238               <MarkdownTextArea
239                 initialContent={this.state.siteForm.legal_information}
240                 onContentChange={this.handleSiteLegalInfoChange}
241                 hideNavigationWarnings
242                 allLanguages={[]}
243                 siteLanguages={[]}
244               />
245             </div>
246           </div>
247           <div className="form-group row">
248             <div className="col-12">
249               <div className="form-check">
250                 <input
251                   className="form-check-input"
252                   id="create-site-downvotes"
253                   type="checkbox"
254                   checked={this.state.siteForm.enable_downvotes}
255                   onChange={linkEvent(
256                     this,
257                     this.handleSiteEnableDownvotesChange
258                   )}
259                 />
260                 <label
261                   className="form-check-label"
262                   htmlFor="create-site-downvotes"
263                 >
264                   {i18n.t("enable_downvotes")}
265                 </label>
266               </div>
267             </div>
268           </div>
269           <div className="form-group row">
270             <div className="col-12">
271               <div className="form-check">
272                 <input
273                   className="form-check-input"
274                   id="create-site-enable-nsfw"
275                   type="checkbox"
276                   checked={this.state.siteForm.enable_nsfw}
277                   onChange={linkEvent(this, this.handleSiteEnableNsfwChange)}
278                 />
279                 <label
280                   className="form-check-label"
281                   htmlFor="create-site-enable-nsfw"
282                 >
283                   {i18n.t("enable_nsfw")}
284                 </label>
285               </div>
286             </div>
287           </div>
288           <div className="form-group row">
289             <div className="col-12">
290               <label
291                 className="form-check-label mr-2"
292                 htmlFor="create-site-registration-mode"
293               >
294                 {i18n.t("registration_mode")}
295               </label>
296               <select
297                 id="create-site-registration-mode"
298                 value={this.state.siteForm.registration_mode}
299                 onChange={linkEvent(
300                   this,
301                   this.handleSiteRegistrationModeChange
302                 )}
303                 className="custom-select w-auto"
304               >
305                 <option value={"RequireApplication"}>
306                   {i18n.t("require_registration_application")}
307                 </option>
308                 <option value={"Open"}>{i18n.t("open_registration")}</option>
309                 <option value={"Closed"}>{i18n.t("close_registration")}</option>
310               </select>
311             </div>
312           </div>
313           {this.state.siteForm.registration_mode == "RequireApplication" && (
314             <div className="form-group row">
315               <label className="col-12 col-form-label">
316                 {i18n.t("application_questionnaire")}
317               </label>
318               <div className="col-12">
319                 <MarkdownTextArea
320                   initialContent={this.state.siteForm.application_question}
321                   onContentChange={this.handleSiteApplicationQuestionChange}
322                   hideNavigationWarnings
323                   allLanguages={[]}
324                   siteLanguages={[]}
325                 />
326               </div>
327             </div>
328           )}
329           <div className="form-group row">
330             <div className="col-12">
331               <div className="form-check">
332                 <input
333                   className="form-check-input"
334                   id="create-site-community-creation-admin-only"
335                   type="checkbox"
336                   checked={this.state.siteForm.community_creation_admin_only}
337                   onChange={linkEvent(
338                     this,
339                     this.handleSiteCommunityCreationAdminOnly
340                   )}
341                 />
342                 <label
343                   className="form-check-label"
344                   htmlFor="create-site-community-creation-admin-only"
345                 >
346                   {i18n.t("community_creation_admin_only")}
347                 </label>
348               </div>
349             </div>
350           </div>
351           <div className="form-group row">
352             <div className="col-12">
353               <div className="form-check">
354                 <input
355                   className="form-check-input"
356                   id="create-site-require-email-verification"
357                   type="checkbox"
358                   checked={this.state.siteForm.require_email_verification}
359                   onChange={linkEvent(
360                     this,
361                     this.handleSiteRequireEmailVerification
362                   )}
363                 />
364                 <label
365                   className="form-check-label"
366                   htmlFor="create-site-require-email-verification"
367                 >
368                   {i18n.t("require_email_verification")}
369                 </label>
370               </div>
371             </div>
372           </div>
373           <div className="form-group row">
374             <div className="col-12">
375               <div className="form-check">
376                 <input
377                   className="form-check-input"
378                   id="create-site-application-email-admins"
379                   type="checkbox"
380                   checked={this.state.siteForm.application_email_admins}
381                   onChange={linkEvent(
382                     this,
383                     this.handleSiteApplicationEmailAdmins
384                   )}
385                 />
386                 <label
387                   className="form-check-label"
388                   htmlFor="create-site-email-admins"
389                 >
390                   {i18n.t("application_email_admins")}
391                 </label>
392               </div>
393             </div>
394           </div>
395           <div className="form-group row">
396             <div className="col-12">
397               <div className="form-check">
398                 <input
399                   className="form-check-input"
400                   id="create-site-reports-email-admins"
401                   type="checkbox"
402                   checked={this.state.siteForm.reports_email_admins}
403                   onChange={linkEvent(this, this.handleSiteReportsEmailAdmins)}
404                 />
405                 <label
406                   className="form-check-label"
407                   htmlFor="create-site-reports-email-admins"
408                 >
409                   {i18n.t("reports_email_admins")}
410                 </label>
411               </div>
412             </div>
413           </div>
414           <div className="form-group row">
415             <div className="col-12">
416               <label
417                 className="form-check-label mr-2"
418                 htmlFor="create-site-default-theme"
419               >
420                 {i18n.t("theme")}
421               </label>
422               <select
423                 id="create-site-default-theme"
424                 value={this.state.siteForm.default_theme}
425                 onChange={linkEvent(this, this.handleSiteDefaultTheme)}
426                 className="custom-select w-auto"
427               >
428                 <option value="browser">{i18n.t("browser_default")}</option>
429                 {this.state.themeList?.map(theme => (
430                   <option key={theme} value={theme}>
431                     {theme}
432                   </option>
433                 ))}
434               </select>
435             </div>
436           </div>
437           {this.props.showLocal && (
438             <form className="form-group row">
439               <label className="col-sm-3">{i18n.t("listing_type")}</label>
440               <div className="col-sm-9">
441                 <ListingTypeSelect
442                   type_={
443                     this.state.siteForm.default_post_listing_type ?? "Local"
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(s => ((s.siteForm.default_post_listing_type = val), s));
1260   }
1261 }
1262
1263 function splitToList(commaList: string): string[] {
1264   if (commaList !== "") {
1265     let list = commaList.trim().split(",");
1266     return list;
1267   } else {
1268     return [];
1269   }
1270 }