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 captcha_enabled: ls.captcha_enabled,
82 captcha_difficulty: ls.captcha_difficulty,
83 allowed_instances: this.props.allowedInstances?.map(i => i.domain),
84 blocked_instances: this.props.blockedInstances?.map(i => i.domain),
89 constructor(props: any, context: any) {
90 super(props, context);
92 this.handleSiteSidebarChange = this.handleSiteSidebarChange.bind(this);
93 this.handleSiteLegalInfoChange = this.handleSiteLegalInfoChange.bind(this);
94 this.handleSiteApplicationQuestionChange =
95 this.handleSiteApplicationQuestionChange.bind(this);
97 this.handleIconUpload = this.handleIconUpload.bind(this);
98 this.handleIconRemove = this.handleIconRemove.bind(this);
100 this.handleBannerUpload = this.handleBannerUpload.bind(this);
101 this.handleBannerRemove = this.handleBannerRemove.bind(this);
103 this.handleDefaultPostListingTypeChange =
104 this.handleDefaultPostListingTypeChange.bind(this);
106 this.handleDiscussionLanguageChange =
107 this.handleDiscussionLanguageChange.bind(this);
109 this.handleAddInstance = this.handleAddInstance.bind(this);
110 this.handleRemoveInstance = this.handleRemoveInstance.bind(this);
112 this.handleInstanceEnterPress = this.handleInstanceEnterPress.bind(this);
113 this.handleInstanceTextChange = this.handleInstanceTextChange.bind(this);
117 const siteSetup = this.props.siteRes.site_view.local_site.site_setup;
120 className="site-form"
121 onSubmit={linkEvent(this, this.handleSaveSiteSubmit)}
125 !this.props.loading &&
128 this.state.siteForm.name ||
129 this.state.siteForm.sidebar ||
130 this.state.siteForm.application_question ||
131 this.state.siteForm.description
133 !this.state.submitted
138 ? capitalizeFirstLetter(I18NextService.i18n.t("edit"))
139 : capitalizeFirstLetter(I18NextService.i18n.t("setup"))
140 } ${I18NextService.i18n.t("your_site")}`}</h5>
141 <div className="mb-3 row">
142 <label className="col-12 col-form-label" htmlFor="create-site-name">
143 {I18NextService.i18n.t("name")}
145 <div className="col-12">
148 id="create-site-name"
149 className="form-control"
150 value={this.state.siteForm.name}
151 onInput={linkEvent(this, this.handleSiteNameChange)}
158 <div className="input-group mb-3">
159 <label className="me-2 col-form-label">
160 {I18NextService.i18n.t("icon")}
163 uploadTitle={I18NextService.i18n.t("upload_icon")}
164 imageSrc={this.state.siteForm.icon}
165 onUpload={this.handleIconUpload}
166 onRemove={this.handleIconRemove}
170 <div className="input-group mb-3">
171 <label className="me-2 col-form-label">
172 {I18NextService.i18n.t("banner")}
175 uploadTitle={I18NextService.i18n.t("upload_banner")}
176 imageSrc={this.state.siteForm.banner}
177 onUpload={this.handleBannerUpload}
178 onRemove={this.handleBannerRemove}
181 <div className="mb-3 row">
182 <label className="col-12 col-form-label" htmlFor="site-desc">
183 {I18NextService.i18n.t("description")}
185 <div className="col-12">
188 className="form-control"
190 value={this.state.siteForm.description}
191 onInput={linkEvent(this, this.handleSiteDescChange)}
196 <div className="mb-3 row">
197 <label className="col-12 col-form-label">
198 {I18NextService.i18n.t("sidebar")}
200 <div className="col-12">
202 initialContent={this.state.siteForm.sidebar}
203 onContentChange={this.handleSiteSidebarChange}
204 hideNavigationWarnings
210 <div className="mb-3 row">
211 <label className="col-12 col-form-label">
212 {I18NextService.i18n.t("legal_information")}
214 <div className="col-12">
216 initialContent={this.state.siteForm.legal_information}
217 onContentChange={this.handleSiteLegalInfoChange}
218 hideNavigationWarnings
224 <div className="mb-3 row">
225 <div className="col-12">
226 <div className="form-check">
228 className="form-check-input"
229 id="create-site-downvotes"
231 checked={this.state.siteForm.enable_downvotes}
232 onChange={linkEvent(this, this.handleSiteEnableDownvotesChange)}
235 className="form-check-label"
236 htmlFor="create-site-downvotes"
238 {I18NextService.i18n.t("enable_downvotes")}
243 <div className="mb-3 row">
244 <div className="col-12">
245 <div className="form-check">
247 className="form-check-input"
248 id="create-site-enable-nsfw"
250 checked={this.state.siteForm.enable_nsfw}
251 onChange={linkEvent(this, this.handleSiteEnableNsfwChange)}
254 className="form-check-label"
255 htmlFor="create-site-enable-nsfw"
257 {I18NextService.i18n.t("enable_nsfw")}
262 <div className="mb-3 row">
263 <div className="col-12">
265 className="form-check-label me-2"
266 htmlFor="create-site-registration-mode"
268 {I18NextService.i18n.t("registration_mode")}
271 id="create-site-registration-mode"
272 value={this.state.siteForm.registration_mode}
273 onChange={linkEvent(this, this.handleSiteRegistrationModeChange)}
274 className="form-select d-inline-block w-auto"
276 <option value={"RequireApplication"}>
277 {I18NextService.i18n.t("require_registration_application")}
279 <option value={"Open"}>
280 {I18NextService.i18n.t("open_registration")}
282 <option value={"Closed"}>
283 {I18NextService.i18n.t("close_registration")}
288 {this.state.siteForm.registration_mode == "RequireApplication" && (
289 <div className="mb-3 row">
290 <label className="col-12 col-form-label">
291 {I18NextService.i18n.t("application_questionnaire")}
293 <div className="col-12">
295 initialContent={this.state.siteForm.application_question}
296 onContentChange={this.handleSiteApplicationQuestionChange}
297 hideNavigationWarnings
304 <div className="mb-3 row">
305 <div className="col-12">
306 <div className="form-check">
308 className="form-check-input"
309 id="create-site-community-creation-admin-only"
311 checked={this.state.siteForm.community_creation_admin_only}
314 this.handleSiteCommunityCreationAdminOnly
318 className="form-check-label"
319 htmlFor="create-site-community-creation-admin-only"
321 {I18NextService.i18n.t("community_creation_admin_only")}
326 <div className="mb-3 row">
327 <div className="col-12">
328 <div className="form-check">
330 className="form-check-input"
331 id="create-site-require-email-verification"
333 checked={this.state.siteForm.require_email_verification}
336 this.handleSiteRequireEmailVerification
340 className="form-check-label"
341 htmlFor="create-site-require-email-verification"
343 {I18NextService.i18n.t("require_email_verification")}
348 <div className="mb-3 row">
349 <div className="col-12">
350 <div className="form-check">
352 className="form-check-input"
353 id="create-site-application-email-admins"
355 checked={this.state.siteForm.application_email_admins}
358 this.handleSiteApplicationEmailAdmins
362 className="form-check-label"
363 htmlFor="create-site-email-admins"
365 {I18NextService.i18n.t("application_email_admins")}
370 <div className="mb-3 row">
371 <div className="col-12">
372 <div className="form-check">
374 className="form-check-input"
375 id="create-site-reports-email-admins"
377 checked={this.state.siteForm.reports_email_admins}
378 onChange={linkEvent(this, this.handleSiteReportsEmailAdmins)}
381 className="form-check-label"
382 htmlFor="create-site-reports-email-admins"
384 {I18NextService.i18n.t("reports_email_admins")}
389 <div className="mb-3 row">
390 <div className="col-12">
392 className="form-check-label me-2"
393 htmlFor="create-site-default-theme"
395 {I18NextService.i18n.t("theme")}
398 id="create-site-default-theme"
399 value={this.state.siteForm.default_theme}
400 onChange={linkEvent(this, this.handleSiteDefaultTheme)}
401 className="form-select d-inline-block w-auto"
403 <option value="browser">
404 {I18NextService.i18n.t("browser_default")}
406 {this.props.themeList?.map(theme => (
407 <option key={theme} value={theme}>
414 {this.props.showLocal && (
415 <form className="mb-3 row">
416 <label className="col-sm-3 col-form-label">
417 {I18NextService.i18n.t("listing_type")}
419 <div className="col-sm-9">
421 type_={this.state.siteForm.default_post_listing_type ?? "Local"}
423 showSubscribed={false}
424 onChange={this.handleDefaultPostListingTypeChange}
429 <div className="mb-3 row">
430 <div className="col-12">
431 <div className="form-check">
433 className="form-check-input"
434 id="create-site-private-instance"
436 checked={this.state.siteForm.private_instance}
437 onChange={linkEvent(this, this.handleSitePrivateInstance)}
440 className="form-check-label"
441 htmlFor="create-site-private-instance"
443 {I18NextService.i18n.t("private_instance")}
448 <div className="mb-3 row">
449 <div className="col-12">
450 <div className="form-check">
452 className="form-check-input"
453 id="create-site-hide-modlog-mod-names"
455 checked={this.state.siteForm.hide_modlog_mod_names}
456 onChange={linkEvent(this, this.handleSiteHideModlogModNames)}
459 className="form-check-label"
460 htmlFor="create-site-hide-modlog-mod-names"
462 {I18NextService.i18n.t("hide_modlog_mod_names")}
467 <div className="mb-3 row">
469 className="col-12 col-form-label"
470 htmlFor="create-site-slur-filter-regex"
472 {I18NextService.i18n.t("slur_filter_regex")}
474 <div className="col-12">
477 id="create-site-slur-filter-regex"
478 placeholder="(word1|word2)"
479 className="form-control"
480 value={this.state.siteForm.slur_filter_regex}
481 onInput={linkEvent(this, this.handleSiteSlurFilterRegex)}
487 allLanguages={this.props.siteRes.all_languages}
488 siteLanguages={this.props.siteRes.discussion_languages}
489 selectedLanguageIds={this.state.siteForm.discussion_languages}
491 onChange={this.handleDiscussionLanguageChange}
494 <div className="mb-3 row">
496 className="col-12 col-form-label"
497 htmlFor="create-site-actor-name"
499 {I18NextService.i18n.t("actor_name_max_length")}
501 <div className="col-12">
504 id="create-site-actor-name"
505 className="form-control"
507 value={this.state.siteForm.actor_name_max_length}
508 onInput={linkEvent(this, this.handleSiteActorNameMaxLength)}
512 <div className="mb-3 row">
513 <div className="col-12">
514 <div className="form-check">
516 className="form-check-input"
517 id="create-site-federation-enabled"
519 checked={this.state.siteForm.federation_enabled}
520 onChange={linkEvent(this, this.handleSiteFederationEnabled)}
523 className="form-check-label"
524 htmlFor="create-site-federation-enabled"
526 {I18NextService.i18n.t("federation_enabled")}
531 {this.state.siteForm.federation_enabled && (
533 <div className="mb-3 row">
534 {this.federatedInstanceSelect("allowed_instances")}
535 {this.federatedInstanceSelect("blocked_instances")}
537 <div className="mb-3 row">
538 <div className="col-12">
539 <div className="form-check">
541 className="form-check-input"
542 id="create-site-federation-debug"
544 checked={this.state.siteForm.federation_debug}
545 onChange={linkEvent(this, this.handleSiteFederationDebug)}
548 className="form-check-label"
549 htmlFor="create-site-federation-debug"
551 {I18NextService.i18n.t("federation_debug")}
558 <div className="mb-3 row">
559 <div className="col-12">
560 <div className="form-check">
562 className="form-check-input"
563 id="create-site-captcha-enabled"
565 checked={this.state.siteForm.captcha_enabled}
566 onChange={linkEvent(this, this.handleSiteCaptchaEnabled)}
569 className="form-check-label"
570 htmlFor="create-site-captcha-enabled"
572 {I18NextService.i18n.t("captcha_enabled")}
577 {this.state.siteForm.captcha_enabled && (
578 <div className="mb-3 row">
579 <div className="col-12">
581 className="form-check-label me-2"
582 htmlFor="create-site-captcha-difficulty"
584 {I18NextService.i18n.t("captcha_difficulty")}
587 id="create-site-captcha-difficulty"
588 value={this.state.siteForm.captcha_difficulty}
589 onChange={linkEvent(this, this.handleSiteCaptchaDifficulty)}
590 className="form-select d-inline-block w-auto"
592 <option value="easy">{I18NextService.i18n.t("easy")}</option>
593 <option value="medium">
594 {I18NextService.i18n.t("medium")}
596 <option value="hard">{I18NextService.i18n.t("hard")}</option>
601 <div className="mb-3 row">
602 <div className="col-12">
605 className="btn btn-secondary me-2"
606 disabled={this.props.loading}
608 {this.props.loading ? (
611 capitalizeFirstLetter(I18NextService.i18n.t("save"))
613 capitalizeFirstLetter(I18NextService.i18n.t("create"))
622 federatedInstanceSelect(key: InstanceKey) {
623 const id = `create_site_${key}`;
624 const value = this.state.instance_select[key];
625 const selectedInstances = this.state.siteForm[key];
627 <div className="col-12 col-md-6">
628 <label className="col-form-label" htmlFor={id}>
629 {I18NextService.i18n.t(key)}
631 <div className="d-flex justify-content-between align-items-center">
634 placeholder="instance.tld"
636 className="form-control"
638 onInput={linkEvent(key, this.handleInstanceTextChange)}
639 onKeyUp={linkEvent(key, this.handleInstanceEnterPress)}
643 className="btn btn-sm bg-success ms-2"
644 onClick={linkEvent(key, this.handleAddInstance)}
645 style={"width: 2rem; height: 2rem;"}
647 -1 /* Making this untabble because handling enter key in text input makes keyboard support for this button redundant */
652 classes="icon-inline text-light m-auto d-block position-static"
656 {selectedInstances && selectedInstances.length > 0 && (
657 <ul className="mt-3 list-unstyled w-100 d-flex flex-column justify-content-around align-items-center">
658 {selectedInstances.map(instance => (
661 className="my-1 w-100 w-md-75 d-flex align-items-center justify-content-between"
663 <label className="d-block m-0 w-100 " htmlFor={instance}>
664 <strong>{instance}</strong>
669 style={"width: 2rem; height: 2rem;"}
670 className="btn btn-sm bg-danger"
673 this.handleRemoveInstance
678 classes="icon-inline text-light m-auto d-block position-static"
689 handleInstanceTextChange(type: InstanceKey, event: any) {
690 this.setState(s => ({
693 ...s.instance_select,
694 [type]: event.target.value,
699 handleInstanceEnterPress(
701 event: InfernoKeyboardEvent<HTMLInputElement>
703 if (event.code.toLowerCase() === "enter") {
704 event.preventDefault();
706 this.handleAddInstance(key);
710 handleSaveSiteSubmit(i: SiteForm, event: any) {
711 event.preventDefault();
712 const auth = myAuthRequired();
713 i.setState(s => ((s.siteForm.auth = auth), s));
714 i.setState({ submitted: true });
716 const stateSiteForm = i.state.siteForm;
718 let form: EditSite | CreateSite;
720 if (i.props.siteRes.site_view.local_site.site_setup) {
721 form = stateSiteForm;
724 name: stateSiteForm.name ?? "My site",
725 sidebar: stateSiteForm.sidebar,
726 description: stateSiteForm.description,
727 icon: stateSiteForm.icon,
728 banner: stateSiteForm.banner,
729 community_creation_admin_only:
730 stateSiteForm.community_creation_admin_only,
731 enable_nsfw: stateSiteForm.enable_nsfw,
732 enable_downvotes: stateSiteForm.enable_downvotes,
733 application_question: stateSiteForm.application_question,
734 registration_mode: stateSiteForm.registration_mode,
735 require_email_verification: stateSiteForm.require_email_verification,
736 private_instance: stateSiteForm.private_instance,
737 default_theme: stateSiteForm.default_theme,
738 default_post_listing_type: stateSiteForm.default_post_listing_type,
739 application_email_admins: stateSiteForm.application_email_admins,
740 hide_modlog_mod_names: stateSiteForm.hide_modlog_mod_names,
741 legal_information: stateSiteForm.legal_information,
742 slur_filter_regex: stateSiteForm.slur_filter_regex,
743 actor_name_max_length: stateSiteForm.actor_name_max_length,
744 rate_limit_message: stateSiteForm.rate_limit_message,
745 rate_limit_message_per_second:
746 stateSiteForm.rate_limit_message_per_second,
747 rate_limit_comment: stateSiteForm.rate_limit_comment,
748 rate_limit_comment_per_second:
749 stateSiteForm.rate_limit_comment_per_second,
750 rate_limit_image: stateSiteForm.rate_limit_image,
751 rate_limit_image_per_second: stateSiteForm.rate_limit_image_per_second,
752 rate_limit_post: stateSiteForm.rate_limit_post,
753 rate_limit_post_per_second: stateSiteForm.rate_limit_post_per_second,
754 rate_limit_register: stateSiteForm.rate_limit_register,
755 rate_limit_register_per_second:
756 stateSiteForm.rate_limit_register_per_second,
757 rate_limit_search: stateSiteForm.rate_limit_search,
758 rate_limit_search_per_second:
759 stateSiteForm.rate_limit_search_per_second,
760 federation_enabled: stateSiteForm.federation_enabled,
761 federation_debug: stateSiteForm.federation_debug,
762 captcha_enabled: stateSiteForm.captcha_enabled,
763 captcha_difficulty: stateSiteForm.captcha_difficulty,
764 allowed_instances: stateSiteForm.allowed_instances,
765 blocked_instances: stateSiteForm.blocked_instances,
766 discussion_languages: stateSiteForm.discussion_languages,
771 i.props.onSaveSite(form);
774 handleAddInstance(key: InstanceKey) {
775 const instance = this.state.instance_select[key].trim();
777 if (!validInstanceTLD(instance)) {
781 if (!this.state.siteForm[key]?.includes(instance)) {
782 this.setState(s => ({
786 [key]: [...(s.siteForm[key] ?? []), instance],
789 ...s.instance_select,
794 const oppositeKey: InstanceKey =
795 key === "allowed_instances" ? "blocked_instances" : "allowed_instances";
796 if (this.state.siteForm[oppositeKey]?.includes(instance)) {
797 this.handleRemoveInstance({ key: oppositeKey, instance });
802 handleRemoveInstance({
809 this.setState(s => ({
813 [key]: s.siteForm[key]?.filter(i => i !== instance),
818 handleSiteNameChange(i: SiteForm, event: any) {
819 i.state.siteForm.name = event.target.value;
823 handleSiteSidebarChange(val: string) {
824 this.setState(s => ((s.siteForm.sidebar = val), s));
827 handleSiteLegalInfoChange(val: string) {
828 this.setState(s => ((s.siteForm.legal_information = val), s));
831 handleTaglineChange(i: SiteForm, index: number, val: string) {
832 const taglines = i.state.siteForm.taglines;
834 taglines[index] = val;
839 handleDeleteTaglineClick(
842 event: InfernoMouseEvent<HTMLButtonElement>
844 event.preventDefault();
845 const taglines = i.state.siteForm.taglines;
847 taglines.splice(index, 1);
848 i.state.siteForm.taglines = undefined;
850 i.state.siteForm.taglines = taglines;
855 handleAddTaglineClick(
857 event: InfernoMouseEvent<HTMLButtonElement>
859 event.preventDefault();
860 if (!i.state.siteForm.taglines) {
861 i.state.siteForm.taglines = [];
863 i.state.siteForm.taglines.push("");
867 handleSiteApplicationQuestionChange(val: string) {
868 this.setState(s => ((s.siteForm.application_question = val), s));
871 handleSiteDescChange(i: SiteForm, event: any) {
872 i.state.siteForm.description = event.target.value;
876 handleSiteEnableNsfwChange(i: SiteForm, event: any) {
877 i.state.siteForm.enable_nsfw = event.target.checked;
881 handleSiteRegistrationModeChange(i: SiteForm, event: any) {
882 i.state.siteForm.registration_mode = event.target.value;
886 handleSiteCommunityCreationAdminOnly(i: SiteForm, event: any) {
887 i.state.siteForm.community_creation_admin_only = event.target.checked;
891 handleSiteEnableDownvotesChange(i: SiteForm, event: any) {
892 i.state.siteForm.enable_downvotes = event.target.checked;
896 handleSiteRequireEmailVerification(i: SiteForm, event: any) {
897 i.state.siteForm.require_email_verification = event.target.checked;
901 handleSiteApplicationEmailAdmins(i: SiteForm, event: any) {
902 i.state.siteForm.application_email_admins = event.target.checked;
906 handleSiteReportsEmailAdmins(i: SiteForm, event: any) {
907 i.state.siteForm.reports_email_admins = event.target.checked;
911 handleSitePrivateInstance(i: SiteForm, event: any) {
912 i.state.siteForm.private_instance = event.target.checked;
916 handleSiteHideModlogModNames(i: SiteForm, event: any) {
917 i.state.siteForm.hide_modlog_mod_names = event.target.checked;
921 handleSiteDefaultTheme(i: SiteForm, event: any) {
922 i.state.siteForm.default_theme = event.target.value;
926 handleIconUpload(url: string) {
927 this.setState(s => ((s.siteForm.icon = url), s));
931 this.setState(s => ((s.siteForm.icon = ""), s));
934 handleBannerUpload(url: string) {
935 this.setState(s => ((s.siteForm.banner = url), s));
938 handleBannerRemove() {
939 this.setState(s => ((s.siteForm.banner = ""), s));
942 handleSiteSlurFilterRegex(i: SiteForm, event: any) {
943 i.setState(s => ((s.siteForm.slur_filter_regex = event.target.value), s));
946 handleSiteActorNameMaxLength(i: SiteForm, event: any) {
948 s => ((s.siteForm.actor_name_max_length = Number(event.target.value)), s)
952 handleSiteFederationEnabled(i: SiteForm, event: any) {
953 i.state.siteForm.federation_enabled = event.target.checked;
957 handleSiteFederationDebug(i: SiteForm, event: any) {
958 i.state.siteForm.federation_debug = event.target.checked;
962 handleSiteCaptchaEnabled(i: SiteForm, event: any) {
963 i.state.siteForm.captcha_enabled = event.target.checked;
967 handleSiteCaptchaDifficulty(i: SiteForm, event: any) {
968 i.setState(s => ((s.siteForm.captcha_difficulty = event.target.value), s));
971 handleDiscussionLanguageChange(val: number[]) {
972 this.setState(s => ((s.siteForm.discussion_languages = val), s));
975 handleDefaultPostListingTypeChange(val: ListingType) {
976 this.setState(s => ((s.siteForm.default_post_listing_type = val), s));