1 import { myAuthRequired } from "@utils/app";
2 import { capitalizeFirstLetter, validInstanceTLD } from "@utils/helpers";
15 } from "lemmy-js-client";
16 import { I18NextService } from "../../services";
17 import { Icon, Spinner } from "../common/icon";
18 import { ImageUploadForm } from "../common/image-upload-form";
19 import { LanguageSelect } from "../common/language-select";
20 import { ListingTypeSelect } from "../common/listing-type-select";
21 import { MarkdownTextArea } from "../common/markdown-textarea";
22 import NavigationPrompt from "../common/navigation-prompt";
24 interface SiteFormProps {
25 blockedInstances?: Instance[];
26 allowedInstances?: Instance[];
29 onSaveSite(form: EditSite): void;
30 siteRes: GetSiteResponse;
34 interface SiteFormState {
37 allowed_instances: string;
38 blocked_instances: string;
43 type InstanceKey = "allowed_instances" | "blocked_instances";
45 export class SiteForm extends Component<SiteFormProps, SiteFormState> {
46 state: SiteFormState = {
47 siteForm: this.initSiteForm(),
49 allowed_instances: "",
50 blocked_instances: "",
55 initSiteForm(): EditSite {
56 const site = this.props.siteRes.site_view.site;
57 const ls = this.props.siteRes.site_view.local_site;
60 sidebar: site.sidebar,
61 description: site.description,
62 enable_downvotes: ls.enable_downvotes,
63 registration_mode: ls.registration_mode,
64 enable_nsfw: ls.enable_nsfw,
65 community_creation_admin_only: ls.community_creation_admin_only,
68 require_email_verification: ls.require_email_verification,
69 application_question: ls.application_question,
70 private_instance: ls.private_instance,
71 default_theme: ls.default_theme,
72 default_post_listing_type: ls.default_post_listing_type,
73 legal_information: ls.legal_information,
74 application_email_admins: ls.application_email_admins,
75 reports_email_admins: ls.reports_email_admins,
76 hide_modlog_mod_names: ls.hide_modlog_mod_names,
77 discussion_languages: this.props.siteRes.discussion_languages,
78 slur_filter_regex: ls.slur_filter_regex,
79 actor_name_max_length: ls.actor_name_max_length,
80 federation_enabled: ls.federation_enabled,
81 federation_worker_count: ls.federation_worker_count,
82 captcha_enabled: ls.captcha_enabled,
83 captcha_difficulty: ls.captcha_difficulty,
84 allowed_instances: this.props.allowedInstances?.map(i => i.domain),
85 blocked_instances: this.props.blockedInstances?.map(i => i.domain),
90 constructor(props: any, context: any) {
91 super(props, context);
93 this.handleSiteSidebarChange = this.handleSiteSidebarChange.bind(this);
94 this.handleSiteLegalInfoChange = this.handleSiteLegalInfoChange.bind(this);
95 this.handleSiteApplicationQuestionChange =
96 this.handleSiteApplicationQuestionChange.bind(this);
98 this.handleIconUpload = this.handleIconUpload.bind(this);
99 this.handleIconRemove = this.handleIconRemove.bind(this);
101 this.handleBannerUpload = this.handleBannerUpload.bind(this);
102 this.handleBannerRemove = this.handleBannerRemove.bind(this);
104 this.handleDefaultPostListingTypeChange =
105 this.handleDefaultPostListingTypeChange.bind(this);
107 this.handleDiscussionLanguageChange =
108 this.handleDiscussionLanguageChange.bind(this);
110 this.handleAddInstance = this.handleAddInstance.bind(this);
111 this.handleRemoveInstance = this.handleRemoveInstance.bind(this);
113 this.handleInstanceEnterPress = this.handleInstanceEnterPress.bind(this);
114 this.handleInstanceTextChange = this.handleInstanceTextChange.bind(this);
118 const siteSetup = this.props.siteRes.site_view.local_site.site_setup;
121 className="site-form"
122 onSubmit={linkEvent(this, this.handleSaveSiteSubmit)}
126 !this.props.loading &&
129 this.state.siteForm.name ||
130 this.state.siteForm.sidebar ||
131 this.state.siteForm.application_question ||
132 this.state.siteForm.description
134 !this.state.submitted
139 ? capitalizeFirstLetter(I18NextService.i18n.t("edit"))
140 : capitalizeFirstLetter(I18NextService.i18n.t("setup"))
141 } ${I18NextService.i18n.t("your_site")}`}</h5>
142 <div className="mb-3 row">
143 <label className="col-12 col-form-label" htmlFor="create-site-name">
144 {I18NextService.i18n.t("name")}
146 <div className="col-12">
149 id="create-site-name"
150 className="form-control"
151 value={this.state.siteForm.name}
152 onInput={linkEvent(this, this.handleSiteNameChange)}
159 <div className="input-group mb-3">
160 <label className="me-2 col-form-label">
161 {I18NextService.i18n.t("icon")}
164 uploadTitle={I18NextService.i18n.t("upload_icon")}
165 imageSrc={this.state.siteForm.icon}
166 onUpload={this.handleIconUpload}
167 onRemove={this.handleIconRemove}
171 <div className="input-group mb-3">
172 <label className="me-2 col-form-label">
173 {I18NextService.i18n.t("banner")}
176 uploadTitle={I18NextService.i18n.t("upload_banner")}
177 imageSrc={this.state.siteForm.banner}
178 onUpload={this.handleBannerUpload}
179 onRemove={this.handleBannerRemove}
182 <div className="mb-3 row">
183 <label className="col-12 col-form-label" htmlFor="site-desc">
184 {I18NextService.i18n.t("description")}
186 <div className="col-12">
189 className="form-control"
191 value={this.state.siteForm.description}
192 onInput={linkEvent(this, this.handleSiteDescChange)}
197 <div className="mb-3 row">
198 <label className="col-12 col-form-label">
199 {I18NextService.i18n.t("sidebar")}
201 <div className="col-12">
203 initialContent={this.state.siteForm.sidebar}
204 onContentChange={this.handleSiteSidebarChange}
205 hideNavigationWarnings
211 <div className="mb-3 row">
212 <label className="col-12 col-form-label">
213 {I18NextService.i18n.t("legal_information")}
215 <div className="col-12">
217 initialContent={this.state.siteForm.legal_information}
218 onContentChange={this.handleSiteLegalInfoChange}
219 hideNavigationWarnings
225 <div className="mb-3 row">
226 <div className="col-12">
227 <div className="form-check">
229 className="form-check-input"
230 id="create-site-downvotes"
232 checked={this.state.siteForm.enable_downvotes}
233 onChange={linkEvent(this, this.handleSiteEnableDownvotesChange)}
236 className="form-check-label"
237 htmlFor="create-site-downvotes"
239 {I18NextService.i18n.t("enable_downvotes")}
244 <div className="mb-3 row">
245 <div className="col-12">
246 <div className="form-check">
248 className="form-check-input"
249 id="create-site-enable-nsfw"
251 checked={this.state.siteForm.enable_nsfw}
252 onChange={linkEvent(this, this.handleSiteEnableNsfwChange)}
255 className="form-check-label"
256 htmlFor="create-site-enable-nsfw"
258 {I18NextService.i18n.t("enable_nsfw")}
263 <div className="mb-3 row">
264 <div className="col-12">
266 className="form-check-label me-2"
267 htmlFor="create-site-registration-mode"
269 {I18NextService.i18n.t("registration_mode")}
272 id="create-site-registration-mode"
273 value={this.state.siteForm.registration_mode}
274 onChange={linkEvent(this, this.handleSiteRegistrationModeChange)}
275 className="form-select d-inline-block w-auto"
277 <option value={"RequireApplication"}>
278 {I18NextService.i18n.t("require_registration_application")}
280 <option value={"Open"}>
281 {I18NextService.i18n.t("open_registration")}
283 <option value={"Closed"}>
284 {I18NextService.i18n.t("close_registration")}
289 {this.state.siteForm.registration_mode == "RequireApplication" && (
290 <div className="mb-3 row">
291 <label className="col-12 col-form-label">
292 {I18NextService.i18n.t("application_questionnaire")}
294 <div className="col-12">
296 initialContent={this.state.siteForm.application_question}
297 onContentChange={this.handleSiteApplicationQuestionChange}
298 hideNavigationWarnings
305 <div className="mb-3 row">
306 <div className="col-12">
307 <div className="form-check">
309 className="form-check-input"
310 id="create-site-community-creation-admin-only"
312 checked={this.state.siteForm.community_creation_admin_only}
315 this.handleSiteCommunityCreationAdminOnly
319 className="form-check-label"
320 htmlFor="create-site-community-creation-admin-only"
322 {I18NextService.i18n.t("community_creation_admin_only")}
327 <div className="mb-3 row">
328 <div className="col-12">
329 <div className="form-check">
331 className="form-check-input"
332 id="create-site-require-email-verification"
334 checked={this.state.siteForm.require_email_verification}
337 this.handleSiteRequireEmailVerification
341 className="form-check-label"
342 htmlFor="create-site-require-email-verification"
344 {I18NextService.i18n.t("require_email_verification")}
349 <div className="mb-3 row">
350 <div className="col-12">
351 <div className="form-check">
353 className="form-check-input"
354 id="create-site-application-email-admins"
356 checked={this.state.siteForm.application_email_admins}
359 this.handleSiteApplicationEmailAdmins
363 className="form-check-label"
364 htmlFor="create-site-email-admins"
366 {I18NextService.i18n.t("application_email_admins")}
371 <div className="mb-3 row">
372 <div className="col-12">
373 <div className="form-check">
375 className="form-check-input"
376 id="create-site-reports-email-admins"
378 checked={this.state.siteForm.reports_email_admins}
379 onChange={linkEvent(this, this.handleSiteReportsEmailAdmins)}
382 className="form-check-label"
383 htmlFor="create-site-reports-email-admins"
385 {I18NextService.i18n.t("reports_email_admins")}
390 <div className="mb-3 row">
391 <div className="col-12">
393 className="form-check-label me-2"
394 htmlFor="create-site-default-theme"
396 {I18NextService.i18n.t("theme")}
399 id="create-site-default-theme"
400 value={this.state.siteForm.default_theme}
401 onChange={linkEvent(this, this.handleSiteDefaultTheme)}
402 className="form-select d-inline-block w-auto"
404 <option value="browser">
405 {I18NextService.i18n.t("browser_default")}
407 {this.props.themeList?.map(theme => (
408 <option key={theme} value={theme}>
415 {this.props.showLocal && (
416 <form className="mb-3 row">
417 <label className="col-sm-3 col-form-label">
418 {I18NextService.i18n.t("listing_type")}
420 <div className="col-sm-9">
422 type_={this.state.siteForm.default_post_listing_type ?? "Local"}
424 showSubscribed={false}
425 onChange={this.handleDefaultPostListingTypeChange}
430 <div className="mb-3 row">
431 <div className="col-12">
432 <div className="form-check">
434 className="form-check-input"
435 id="create-site-private-instance"
437 checked={this.state.siteForm.private_instance}
438 onChange={linkEvent(this, this.handleSitePrivateInstance)}
441 className="form-check-label"
442 htmlFor="create-site-private-instance"
444 {I18NextService.i18n.t("private_instance")}
449 <div className="mb-3 row">
450 <div className="col-12">
451 <div className="form-check">
453 className="form-check-input"
454 id="create-site-hide-modlog-mod-names"
456 checked={this.state.siteForm.hide_modlog_mod_names}
457 onChange={linkEvent(this, this.handleSiteHideModlogModNames)}
460 className="form-check-label"
461 htmlFor="create-site-hide-modlog-mod-names"
463 {I18NextService.i18n.t("hide_modlog_mod_names")}
468 <div className="mb-3 row">
470 className="col-12 col-form-label"
471 htmlFor="create-site-slur-filter-regex"
473 {I18NextService.i18n.t("slur_filter_regex")}
475 <div className="col-12">
478 id="create-site-slur-filter-regex"
479 placeholder="(word1|word2)"
480 className="form-control"
481 value={this.state.siteForm.slur_filter_regex}
482 onInput={linkEvent(this, this.handleSiteSlurFilterRegex)}
488 allLanguages={this.props.siteRes.all_languages}
489 siteLanguages={this.props.siteRes.discussion_languages}
490 selectedLanguageIds={this.state.siteForm.discussion_languages}
492 onChange={this.handleDiscussionLanguageChange}
495 <div className="mb-3 row">
497 className="col-12 col-form-label"
498 htmlFor="create-site-actor-name"
500 {I18NextService.i18n.t("actor_name_max_length")}
502 <div className="col-12">
505 id="create-site-actor-name"
506 className="form-control"
508 value={this.state.siteForm.actor_name_max_length}
509 onInput={linkEvent(this, this.handleSiteActorNameMaxLength)}
513 <div className="mb-3 row">
514 <div className="col-12">
515 <div className="form-check">
517 className="form-check-input"
518 id="create-site-federation-enabled"
520 checked={this.state.siteForm.federation_enabled}
521 onChange={linkEvent(this, this.handleSiteFederationEnabled)}
524 className="form-check-label"
525 htmlFor="create-site-federation-enabled"
527 {I18NextService.i18n.t("federation_enabled")}
532 {this.state.siteForm.federation_enabled && (
534 <div className="mb-3 row">
535 {this.federatedInstanceSelect("allowed_instances")}
536 {this.federatedInstanceSelect("blocked_instances")}
538 <div className="mb-3 row">
539 <div className="col-12">
540 <div className="form-check">
542 className="form-check-input"
543 id="create-site-federation-debug"
545 checked={this.state.siteForm.federation_debug}
546 onChange={linkEvent(this, this.handleSiteFederationDebug)}
549 className="form-check-label"
550 htmlFor="create-site-federation-debug"
552 {I18NextService.i18n.t("federation_debug")}
557 <div className="mb-3 row">
559 className="col-12 col-form-label"
560 htmlFor="create-site-federation-worker-count"
562 {I18NextService.i18n.t("federation_worker_count")}
564 <div className="col-12">
567 id="create-site-federation-worker-count"
568 className="form-control"
570 value={this.state.siteForm.federation_worker_count}
573 this.handleSiteFederationWorkerCount
580 <div className="mb-3 row">
581 <div className="col-12">
582 <div className="form-check">
584 className="form-check-input"
585 id="create-site-captcha-enabled"
587 checked={this.state.siteForm.captcha_enabled}
588 onChange={linkEvent(this, this.handleSiteCaptchaEnabled)}
591 className="form-check-label"
592 htmlFor="create-site-captcha-enabled"
594 {I18NextService.i18n.t("captcha_enabled")}
599 {this.state.siteForm.captcha_enabled && (
600 <div className="mb-3 row">
601 <div className="col-12">
603 className="form-check-label me-2"
604 htmlFor="create-site-captcha-difficulty"
606 {I18NextService.i18n.t("captcha_difficulty")}
609 id="create-site-captcha-difficulty"
610 value={this.state.siteForm.captcha_difficulty}
611 onChange={linkEvent(this, this.handleSiteCaptchaDifficulty)}
612 className="form-select d-inline-block w-auto"
614 <option value="easy">{I18NextService.i18n.t("easy")}</option>
615 <option value="medium">
616 {I18NextService.i18n.t("medium")}
618 <option value="hard">{I18NextService.i18n.t("hard")}</option>
623 <div className="mb-3 row">
624 <div className="col-12">
627 className="btn btn-secondary me-2"
628 disabled={this.props.loading}
630 {this.props.loading ? (
633 capitalizeFirstLetter(I18NextService.i18n.t("save"))
635 capitalizeFirstLetter(I18NextService.i18n.t("create"))
644 federatedInstanceSelect(key: InstanceKey) {
645 const id = `create_site_${key}`;
646 const value = this.state.instance_select[key];
647 const selectedInstances = this.state.siteForm[key];
649 <div className="col-12 col-md-6">
650 <label className="col-form-label" htmlFor={id}>
651 {I18NextService.i18n.t(key)}
653 <div className="d-flex justify-content-between align-items-center">
656 placeholder="instance.tld"
658 className="form-control"
660 onInput={linkEvent(key, this.handleInstanceTextChange)}
661 onKeyUp={linkEvent(key, this.handleInstanceEnterPress)}
665 className="btn btn-sm bg-success ms-2"
666 onClick={linkEvent(key, this.handleAddInstance)}
667 style={"width: 2rem; height: 2rem;"}
669 -1 /* Making this untabble because handling enter key in text input makes keyboard support for this button redundant */
674 classes="icon-inline text-light m-auto d-block position-static"
678 {selectedInstances && selectedInstances.length > 0 && (
679 <ul className="mt-3 list-unstyled w-100 d-flex flex-column justify-content-around align-items-center">
680 {selectedInstances.map(instance => (
683 className="my-1 w-100 w-md-75 d-flex align-items-center justify-content-between"
685 <label className="d-block m-0 w-100 " htmlFor={instance}>
686 <strong>{instance}</strong>
691 style={"width: 2rem; height: 2rem;"}
692 className="btn btn-sm bg-danger"
695 this.handleRemoveInstance
700 classes="icon-inline text-light m-auto d-block position-static"
711 handleInstanceTextChange(type: InstanceKey, event: any) {
712 this.setState(s => ({
715 ...s.instance_select,
716 [type]: event.target.value,
721 handleInstanceEnterPress(
723 event: InfernoKeyboardEvent<HTMLInputElement>
725 if (event.code.toLowerCase() === "enter") {
726 event.preventDefault();
728 this.handleAddInstance(key);
732 handleSaveSiteSubmit(i: SiteForm, event: any) {
733 event.preventDefault();
734 const auth = myAuthRequired();
735 i.setState(s => ((s.siteForm.auth = auth), s));
736 i.setState({ submitted: true });
738 const stateSiteForm = i.state.siteForm;
740 let form: EditSite | CreateSite;
742 if (i.props.siteRes.site_view.local_site.site_setup) {
743 form = stateSiteForm;
746 name: stateSiteForm.name ?? "My site",
747 sidebar: stateSiteForm.sidebar,
748 description: stateSiteForm.description,
749 icon: stateSiteForm.icon,
750 banner: stateSiteForm.banner,
751 community_creation_admin_only:
752 stateSiteForm.community_creation_admin_only,
753 enable_nsfw: stateSiteForm.enable_nsfw,
754 enable_downvotes: stateSiteForm.enable_downvotes,
755 application_question: stateSiteForm.application_question,
756 registration_mode: stateSiteForm.registration_mode,
757 require_email_verification: stateSiteForm.require_email_verification,
758 private_instance: stateSiteForm.private_instance,
759 default_theme: stateSiteForm.default_theme,
760 default_post_listing_type: stateSiteForm.default_post_listing_type,
761 application_email_admins: stateSiteForm.application_email_admins,
762 hide_modlog_mod_names: stateSiteForm.hide_modlog_mod_names,
763 legal_information: stateSiteForm.legal_information,
764 slur_filter_regex: stateSiteForm.slur_filter_regex,
765 actor_name_max_length: stateSiteForm.actor_name_max_length,
766 rate_limit_message: stateSiteForm.rate_limit_message,
767 rate_limit_message_per_second:
768 stateSiteForm.rate_limit_message_per_second,
769 rate_limit_comment: stateSiteForm.rate_limit_comment,
770 rate_limit_comment_per_second:
771 stateSiteForm.rate_limit_comment_per_second,
772 rate_limit_image: stateSiteForm.rate_limit_image,
773 rate_limit_image_per_second: stateSiteForm.rate_limit_image_per_second,
774 rate_limit_post: stateSiteForm.rate_limit_post,
775 rate_limit_post_per_second: stateSiteForm.rate_limit_post_per_second,
776 rate_limit_register: stateSiteForm.rate_limit_register,
777 rate_limit_register_per_second:
778 stateSiteForm.rate_limit_register_per_second,
779 rate_limit_search: stateSiteForm.rate_limit_search,
780 rate_limit_search_per_second:
781 stateSiteForm.rate_limit_search_per_second,
782 federation_enabled: stateSiteForm.federation_enabled,
783 federation_debug: stateSiteForm.federation_debug,
784 federation_worker_count: stateSiteForm.federation_worker_count,
785 captcha_enabled: stateSiteForm.captcha_enabled,
786 captcha_difficulty: stateSiteForm.captcha_difficulty,
787 allowed_instances: stateSiteForm.allowed_instances,
788 blocked_instances: stateSiteForm.blocked_instances,
789 discussion_languages: stateSiteForm.discussion_languages,
794 i.props.onSaveSite(form);
797 handleAddInstance(key: InstanceKey) {
798 const instance = this.state.instance_select[key].trim();
800 if (!validInstanceTLD(instance)) {
804 if (!this.state.siteForm[key]?.includes(instance)) {
805 this.setState(s => ({
809 [key]: [...(s.siteForm[key] ?? []), instance],
812 ...s.instance_select,
817 const oppositeKey: InstanceKey =
818 key === "allowed_instances" ? "blocked_instances" : "allowed_instances";
819 if (this.state.siteForm[oppositeKey]?.includes(instance)) {
820 this.handleRemoveInstance({ key: oppositeKey, instance });
825 handleRemoveInstance({
832 this.setState(s => ({
836 [key]: s.siteForm[key]?.filter(i => i !== instance),
841 handleSiteNameChange(i: SiteForm, event: any) {
842 i.state.siteForm.name = event.target.value;
846 handleSiteSidebarChange(val: string) {
847 this.setState(s => ((s.siteForm.sidebar = val), s));
850 handleSiteLegalInfoChange(val: string) {
851 this.setState(s => ((s.siteForm.legal_information = val), s));
854 handleTaglineChange(i: SiteForm, index: number, val: string) {
855 const taglines = i.state.siteForm.taglines;
857 taglines[index] = val;
862 handleDeleteTaglineClick(
865 event: InfernoMouseEvent<HTMLButtonElement>
867 event.preventDefault();
868 const taglines = i.state.siteForm.taglines;
870 taglines.splice(index, 1);
871 i.state.siteForm.taglines = undefined;
873 i.state.siteForm.taglines = taglines;
878 handleAddTaglineClick(
880 event: InfernoMouseEvent<HTMLButtonElement>
882 event.preventDefault();
883 if (!i.state.siteForm.taglines) {
884 i.state.siteForm.taglines = [];
886 i.state.siteForm.taglines.push("");
890 handleSiteApplicationQuestionChange(val: string) {
891 this.setState(s => ((s.siteForm.application_question = val), s));
894 handleSiteDescChange(i: SiteForm, event: any) {
895 i.state.siteForm.description = event.target.value;
899 handleSiteEnableNsfwChange(i: SiteForm, event: any) {
900 i.state.siteForm.enable_nsfw = event.target.checked;
904 handleSiteRegistrationModeChange(i: SiteForm, event: any) {
905 i.state.siteForm.registration_mode = event.target.value;
909 handleSiteCommunityCreationAdminOnly(i: SiteForm, event: any) {
910 i.state.siteForm.community_creation_admin_only = event.target.checked;
914 handleSiteEnableDownvotesChange(i: SiteForm, event: any) {
915 i.state.siteForm.enable_downvotes = event.target.checked;
919 handleSiteRequireEmailVerification(i: SiteForm, event: any) {
920 i.state.siteForm.require_email_verification = event.target.checked;
924 handleSiteApplicationEmailAdmins(i: SiteForm, event: any) {
925 i.state.siteForm.application_email_admins = event.target.checked;
929 handleSiteReportsEmailAdmins(i: SiteForm, event: any) {
930 i.state.siteForm.reports_email_admins = event.target.checked;
934 handleSitePrivateInstance(i: SiteForm, event: any) {
935 i.state.siteForm.private_instance = event.target.checked;
939 handleSiteHideModlogModNames(i: SiteForm, event: any) {
940 i.state.siteForm.hide_modlog_mod_names = event.target.checked;
944 handleSiteDefaultTheme(i: SiteForm, event: any) {
945 i.state.siteForm.default_theme = event.target.value;
949 handleIconUpload(url: string) {
950 this.setState(s => ((s.siteForm.icon = url), s));
954 this.setState(s => ((s.siteForm.icon = ""), s));
957 handleBannerUpload(url: string) {
958 this.setState(s => ((s.siteForm.banner = url), s));
961 handleBannerRemove() {
962 this.setState(s => ((s.siteForm.banner = ""), s));
965 handleSiteSlurFilterRegex(i: SiteForm, event: any) {
966 i.setState(s => ((s.siteForm.slur_filter_regex = event.target.value), s));
969 handleSiteActorNameMaxLength(i: SiteForm, event: any) {
971 s => ((s.siteForm.actor_name_max_length = Number(event.target.value)), s)
975 handleSiteFederationEnabled(i: SiteForm, event: any) {
976 i.state.siteForm.federation_enabled = event.target.checked;
980 handleSiteFederationDebug(i: SiteForm, event: any) {
981 i.state.siteForm.federation_debug = event.target.checked;
985 handleSiteFederationWorkerCount(i: SiteForm, event: any) {
988 (s.siteForm.federation_worker_count = Number(event.target.value)), s
993 handleSiteCaptchaEnabled(i: SiteForm, event: any) {
994 i.state.siteForm.captcha_enabled = event.target.checked;
998 handleSiteCaptchaDifficulty(i: SiteForm, event: any) {
999 i.setState(s => ((s.siteForm.captcha_difficulty = event.target.value), s));
1002 handleDiscussionLanguageChange(val: number[]) {
1003 this.setState(s => ((s.siteForm.discussion_languages = val), s));
1006 handleDefaultPostListingTypeChange(val: ListingType) {
1007 this.setState(s => ((s.siteForm.default_post_listing_type = val), s));