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