]> Untitled Git - lemmy-ui.git/blobdiff - src/shared/components/home/site-form.tsx
Merge branch 'main' into fix/fix-badges-spacing-componentize
[lemmy-ui.git] / src / shared / components / home / site-form.tsx
index 3b451e66ab10bd04493faea8b1ece5e4deda08e7..36859225a4db2c4521d8ba622757714f3c10b655 100644 (file)
@@ -1,7 +1,10 @@
+import { myAuthRequired } from "@utils/app";
+import { capitalizeFirstLetter, validInstanceTLD } from "@utils/helpers";
 import {
   Component,
   InfernoKeyboardEvent,
   InfernoMouseEvent,
+  InfernoNode,
   linkEvent,
 } from "inferno";
 import {
@@ -11,8 +14,8 @@ import {
   Instance,
   ListingType,
 } from "lemmy-js-client";
-import { i18n } from "../../i18next";
-import { capitalizeFirstLetter, myAuthRequired } from "../../utils";
+import deepEqual from "lodash.isequal";
+import { I18NextService } from "../../services";
 import { Icon, Spinner } from "../common/icon";
 import { ImageUploadForm } from "../common/image-upload-form";
 import { LanguageSelect } from "../common/language-select";
@@ -27,11 +30,11 @@ interface SiteFormProps {
   themeList?: string[];
   onSaveSite(form: EditSite): void;
   siteRes: GetSiteResponse;
+  loading: boolean;
 }
 
 interface SiteFormState {
   siteForm: EditSite;
-  loading: boolean;
   instance_select: {
     allowed_instances: string;
     blocked_instances: string;
@@ -44,7 +47,6 @@ type InstanceKey = "allowed_instances" | "blocked_instances";
 export class SiteForm extends Component<SiteFormProps, SiteFormState> {
   state: SiteFormState = {
     siteForm: this.initSiteForm(),
-    loading: false,
     instance_select: {
       allowed_instances: "",
       blocked_instances: "",
@@ -55,6 +57,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
   initSiteForm(): EditSite {
     const site = this.props.siteRes.site_view.site;
     const ls = this.props.siteRes.site_view.local_site;
+
     return {
       name: site.name,
       sidebar: site.sidebar,
@@ -78,8 +81,6 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
       slur_filter_regex: ls.slur_filter_regex,
       actor_name_max_length: ls.actor_name_max_length,
       federation_enabled: ls.federation_enabled,
-      federation_debug: ls.federation_debug,
-      federation_worker_count: ls.federation_worker_count,
       captcha_enabled: ls.captcha_enabled,
       captcha_difficulty: ls.captcha_difficulty,
       allowed_instances: this.props.allowedInstances?.map(i => i.domain),
@@ -107,23 +108,24 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
 
     this.handleDiscussionLanguageChange =
       this.handleDiscussionLanguageChange.bind(this);
+
     this.handleAddInstance = this.handleAddInstance.bind(this);
+    this.handleRemoveInstance = this.handleRemoveInstance.bind(this);
+
     this.handleInstanceEnterPress = this.handleInstanceEnterPress.bind(this);
     this.handleInstanceTextChange = this.handleInstanceTextChange.bind(this);
   }
 
-  // Necessary to stop the loading
-  componentWillReceiveProps() {
-    this.setState({ loading: false });
-  }
-
   render() {
     const siteSetup = this.props.siteRes.site_view.local_site.site_setup;
     return (
-      <form onSubmit={linkEvent(this, this.handleSaveSiteSubmit)}>
+      <form
+        className="site-form"
+        onSubmit={linkEvent(this, this.handleSaveSiteSubmit)}
+      >
         <NavigationPrompt
           when={
-            !this.state.loading &&
+            !this.props.loading &&
             !siteSetup &&
             !!(
               this.state.siteForm.name ||
@@ -136,12 +138,12 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
         />
         <h5>{`${
           siteSetup
-            ? capitalizeFirstLetter(i18n.t("save"))
-            : capitalizeFirstLetter(i18n.t("name"))
-        } ${i18n.t("your_site")}`}</h5>
-        <div className="form-group row">
+            ? capitalizeFirstLetter(I18NextService.i18n.t("edit"))
+            : capitalizeFirstLetter(I18NextService.i18n.t("setup"))
+        } ${I18NextService.i18n.t("your_site")}`}</h5>
+        <div className="mb-3 row">
           <label className="col-12 col-form-label" htmlFor="create-site-name">
-            {i18n.t("name")}
+            {I18NextService.i18n.t("name")}
           </label>
           <div className="col-12">
             <input
@@ -156,28 +158,36 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
             />
           </div>
         </div>
-        <div className="form-group">
-          <label>{i18n.t("icon")}</label>
-          <ImageUploadForm
-            uploadTitle={i18n.t("upload_icon")}
-            imageSrc={this.state.siteForm.icon}
-            onUpload={this.handleIconUpload}
-            onRemove={this.handleIconRemove}
-            rounded
-          />
+        <div className="row mb-3">
+          <label className="col-sm-2 col-form-label">
+            {I18NextService.i18n.t("icon")}
+          </label>
+          <div className="col-sm-10">
+            <ImageUploadForm
+              uploadTitle={I18NextService.i18n.t("upload_icon")}
+              imageSrc={this.state.siteForm.icon}
+              onUpload={this.handleIconUpload}
+              onRemove={this.handleIconRemove}
+              rounded
+            />
+          </div>
         </div>
-        <div className="form-group">
-          <label>{i18n.t("banner")}</label>
-          <ImageUploadForm
-            uploadTitle={i18n.t("upload_banner")}
-            imageSrc={this.state.siteForm.banner}
-            onUpload={this.handleBannerUpload}
-            onRemove={this.handleBannerRemove}
-          />
+        <div className="row mb-3">
+          <label className="col-sm-2 col-form-label">
+            {I18NextService.i18n.t("banner")}
+          </label>
+          <div className="col-sm-10">
+            <ImageUploadForm
+              uploadTitle={I18NextService.i18n.t("upload_banner")}
+              imageSrc={this.state.siteForm.banner}
+              onUpload={this.handleBannerUpload}
+              onRemove={this.handleBannerRemove}
+            />
+          </div>
         </div>
-        <div className="form-group row">
+        <div className="mb-3 row">
           <label className="col-12 col-form-label" htmlFor="site-desc">
-            {i18n.t("description")}
+            {I18NextService.i18n.t("description")}
           </label>
           <div className="col-12">
             <input
@@ -190,8 +200,10 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
             />
           </div>
         </div>
-        <div className="form-group row">
-          <label className="col-12 col-form-label">{i18n.t("sidebar")}</label>
+        <div className="mb-3 row">
+          <label className="col-12 col-form-label">
+            {I18NextService.i18n.t("sidebar")}
+          </label>
           <div className="col-12">
             <MarkdownTextArea
               initialContent={this.state.siteForm.sidebar}
@@ -202,9 +214,9 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
             />
           </div>
         </div>
-        <div className="form-group row">
+        <div className="mb-3 row">
           <label className="col-12 col-form-label">
-            {i18n.t("legal_information")}
+            {I18NextService.i18n.t("legal_information")}
           </label>
           <div className="col-12">
             <MarkdownTextArea
@@ -216,7 +228,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
             />
           </div>
         </div>
-        <div className="form-group row">
+        <div className="mb-3 row">
           <div className="col-12">
             <div className="form-check">
               <input
@@ -230,12 +242,12 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 className="form-check-label"
                 htmlFor="create-site-downvotes"
               >
-                {i18n.t("enable_downvotes")}
+                {I18NextService.i18n.t("enable_downvotes")}
               </label>
             </div>
           </div>
         </div>
-        <div className="form-group row">
+        <div className="mb-3 row">
           <div className="col-12">
             <div className="form-check">
               <input
@@ -249,37 +261,41 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 className="form-check-label"
                 htmlFor="create-site-enable-nsfw"
               >
-                {i18n.t("enable_nsfw")}
+                {I18NextService.i18n.t("enable_nsfw")}
               </label>
             </div>
           </div>
         </div>
-        <div className="form-group row">
+        <div className="mb-3 row">
           <div className="col-12">
             <label
-              className="form-check-label mr-2"
+              className="form-check-label me-2"
               htmlFor="create-site-registration-mode"
             >
-              {i18n.t("registration_mode")}
+              {I18NextService.i18n.t("registration_mode")}
             </label>
             <select
               id="create-site-registration-mode"
               value={this.state.siteForm.registration_mode}
               onChange={linkEvent(this, this.handleSiteRegistrationModeChange)}
-              className="custom-select w-auto"
+              className="form-select d-inline-block w-auto"
             >
               <option value={"RequireApplication"}>
-                {i18n.t("require_registration_application")}
+                {I18NextService.i18n.t("require_registration_application")}
+              </option>
+              <option value={"Open"}>
+                {I18NextService.i18n.t("open_registration")}
+              </option>
+              <option value={"Closed"}>
+                {I18NextService.i18n.t("close_registration")}
               </option>
-              <option value={"Open"}>{i18n.t("open_registration")}</option>
-              <option value={"Closed"}>{i18n.t("close_registration")}</option>
             </select>
           </div>
         </div>
         {this.state.siteForm.registration_mode == "RequireApplication" && (
-          <div className="form-group row">
+          <div className="mb-3 row">
             <label className="col-12 col-form-label">
-              {i18n.t("application_questionnaire")}
+              {I18NextService.i18n.t("application_questionnaire")}
             </label>
             <div className="col-12">
               <MarkdownTextArea
@@ -292,7 +308,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
             </div>
           </div>
         )}
-        <div className="form-group row">
+        <div className="mb-3 row">
           <div className="col-12">
             <div className="form-check">
               <input
@@ -309,12 +325,12 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 className="form-check-label"
                 htmlFor="create-site-community-creation-admin-only"
               >
-                {i18n.t("community_creation_admin_only")}
+                {I18NextService.i18n.t("community_creation_admin_only")}
               </label>
             </div>
           </div>
         </div>
-        <div className="form-group row">
+        <div className="mb-3 row">
           <div className="col-12">
             <div className="form-check">
               <input
@@ -331,12 +347,12 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 className="form-check-label"
                 htmlFor="create-site-require-email-verification"
               >
-                {i18n.t("require_email_verification")}
+                {I18NextService.i18n.t("require_email_verification")}
               </label>
             </div>
           </div>
         </div>
-        <div className="form-group row">
+        <div className="mb-3 row">
           <div className="col-12">
             <div className="form-check">
               <input
@@ -353,12 +369,12 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 className="form-check-label"
                 htmlFor="create-site-email-admins"
               >
-                {i18n.t("application_email_admins")}
+                {I18NextService.i18n.t("application_email_admins")}
               </label>
             </div>
           </div>
         </div>
-        <div className="form-group row">
+        <div className="mb-3 row">
           <div className="col-12">
             <div className="form-check">
               <input
@@ -372,26 +388,28 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 className="form-check-label"
                 htmlFor="create-site-reports-email-admins"
               >
-                {i18n.t("reports_email_admins")}
+                {I18NextService.i18n.t("reports_email_admins")}
               </label>
             </div>
           </div>
         </div>
-        <div className="form-group row">
+        <div className="mb-3 row">
           <div className="col-12">
             <label
-              className="form-check-label mr-2"
+              className="form-check-label me-2"
               htmlFor="create-site-default-theme"
             >
-              {i18n.t("theme")}
+              {I18NextService.i18n.t("theme")}
             </label>
             <select
               id="create-site-default-theme"
               value={this.state.siteForm.default_theme}
               onChange={linkEvent(this, this.handleSiteDefaultTheme)}
-              className="custom-select w-auto"
+              className="form-select d-inline-block w-auto"
             >
-              <option value="browser">{i18n.t("browser_default")}</option>
+              <option value="browser">
+                {I18NextService.i18n.t("browser_default")}
+              </option>
               {this.props.themeList?.map(theme => (
                 <option key={theme} value={theme}>
                   {theme}
@@ -401,8 +419,10 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
           </div>
         </div>
         {this.props.showLocal && (
-          <form className="form-group row">
-            <label className="col-sm-3">{i18n.t("listing_type")}</label>
+          <form className="mb-3 row">
+            <label className="col-sm-3 col-form-label">
+              {I18NextService.i18n.t("listing_type")}
+            </label>
             <div className="col-sm-9">
               <ListingTypeSelect
                 type_={this.state.siteForm.default_post_listing_type ?? "Local"}
@@ -413,7 +433,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
             </div>
           </form>
         )}
-        <div className="form-group row">
+        <div className="mb-3 row">
           <div className="col-12">
             <div className="form-check">
               <input
@@ -427,12 +447,12 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 className="form-check-label"
                 htmlFor="create-site-private-instance"
               >
-                {i18n.t("private_instance")}
+                {I18NextService.i18n.t("private_instance")}
               </label>
             </div>
           </div>
         </div>
-        <div className="form-group row">
+        <div className="mb-3 row">
           <div className="col-12">
             <div className="form-check">
               <input
@@ -446,17 +466,17 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 className="form-check-label"
                 htmlFor="create-site-hide-modlog-mod-names"
               >
-                {i18n.t("hide_modlog_mod_names")}
+                {I18NextService.i18n.t("hide_modlog_mod_names")}
               </label>
             </div>
           </div>
         </div>
-        <div className="form-group row">
+        <div className="mb-3 row">
           <label
             className="col-12 col-form-label"
             htmlFor="create-site-slur-filter-regex"
           >
-            {i18n.t("slur_filter_regex")}
+            {I18NextService.i18n.t("slur_filter_regex")}
           </label>
           <div className="col-12">
             <input
@@ -478,12 +498,12 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
           onChange={this.handleDiscussionLanguageChange}
           showAll
         />
-        <div className="form-group row">
+        <div className="mb-3 row">
           <label
             className="col-12 col-form-label"
             htmlFor="create-site-actor-name"
           >
-            {i18n.t("actor_name_max_length")}
+            {I18NextService.i18n.t("actor_name_max_length")}
           </label>
           <div className="col-12">
             <input
@@ -496,7 +516,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
             />
           </div>
         </div>
-        <div className="form-group row">
+        <div className="mb-3 row">
           <div className="col-12">
             <div className="form-check">
               <input
@@ -510,18 +530,18 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 className="form-check-label"
                 htmlFor="create-site-federation-enabled"
               >
-                {i18n.t("federation_enabled")}
+                {I18NextService.i18n.t("federation_enabled")}
               </label>
             </div>
           </div>
         </div>
         {this.state.siteForm.federation_enabled && (
           <>
-            <div className="form-group row">
+            <div className="mb-3 row">
               {this.federatedInstanceSelect("allowed_instances")}
               {this.federatedInstanceSelect("blocked_instances")}
             </div>
-            <div className="form-group row">
+            <div className="mb-3 row">
               <div className="col-12">
                 <div className="form-check">
                   <input
@@ -535,35 +555,14 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                     className="form-check-label"
                     htmlFor="create-site-federation-debug"
                   >
-                    {i18n.t("federation_debug")}
+                    {I18NextService.i18n.t("federation_debug")}
                   </label>
                 </div>
               </div>
             </div>
-            <div className="form-group row">
-              <label
-                className="col-12 col-form-label"
-                htmlFor="create-site-federation-worker-count"
-              >
-                {i18n.t("federation_worker_count")}
-              </label>
-              <div className="col-12">
-                <input
-                  type="number"
-                  id="create-site-federation-worker-count"
-                  className="form-control"
-                  min={0}
-                  value={this.state.siteForm.federation_worker_count}
-                  onInput={linkEvent(
-                    this,
-                    this.handleSiteFederationWorkerCount
-                  )}
-                />
-              </div>
-            </div>
           </>
         )}
-        <div className="form-group row">
+        <div className="mb-3 row">
           <div className="col-12">
             <div className="form-check">
               <input
@@ -577,46 +576,48 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
                 className="form-check-label"
                 htmlFor="create-site-captcha-enabled"
               >
-                {i18n.t("captcha_enabled")}
+                {I18NextService.i18n.t("captcha_enabled")}
               </label>
             </div>
           </div>
         </div>
         {this.state.siteForm.captcha_enabled && (
-          <div className="form-group row">
+          <div className="mb-3 row">
             <div className="col-12">
               <label
-                className="form-check-label mr-2"
+                className="form-check-label me-2"
                 htmlFor="create-site-captcha-difficulty"
               >
-                {i18n.t("captcha_difficulty")}
+                {I18NextService.i18n.t("captcha_difficulty")}
               </label>
               <select
                 id="create-site-captcha-difficulty"
                 value={this.state.siteForm.captcha_difficulty}
                 onChange={linkEvent(this, this.handleSiteCaptchaDifficulty)}
-                className="custom-select w-auto"
+                className="form-select d-inline-block w-auto"
               >
-                <option value="easy">{i18n.t("easy")}</option>
-                <option value="medium">{i18n.t("medium")}</option>
-                <option value="hard">{i18n.t("hard")}</option>
+                <option value="easy">{I18NextService.i18n.t("easy")}</option>
+                <option value="medium">
+                  {I18NextService.i18n.t("medium")}
+                </option>
+                <option value="hard">{I18NextService.i18n.t("hard")}</option>
               </select>
             </div>
           </div>
         )}
-        <div className="form-group row">
+        <div className="mb-3 row">
           <div className="col-12">
             <button
               type="submit"
-              className="btn btn-secondary mr-2"
-              disabled={this.state.loading}
+              className="btn btn-secondary me-2"
+              disabled={this.props.loading}
             >
-              {this.state.loading ? (
+              {this.props.loading ? (
                 <Spinner />
               ) : siteSetup ? (
-                capitalizeFirstLetter(i18n.t("save"))
+                capitalizeFirstLetter(I18NextService.i18n.t("save"))
               ) : (
-                capitalizeFirstLetter(i18n.t("create"))
+                capitalizeFirstLetter(I18NextService.i18n.t("create"))
               )}
             </button>
           </div>
@@ -625,6 +626,19 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
     );
   }
 
+  componentDidUpdate(
+    prevProps: Readonly<{ children?: InfernoNode } & SiteFormProps>
+  ) {
+    if (
+      !(
+        deepEqual(prevProps.allowedInstances, this.props.allowedInstances) ||
+        deepEqual(prevProps.blockedInstances, this.props.blockedInstances)
+      )
+    ) {
+      this.setState({ siteForm: this.initSiteForm() });
+    }
+  }
+
   federatedInstanceSelect(key: InstanceKey) {
     const id = `create_site_${key}`;
     const value = this.state.instance_select[key];
@@ -632,7 +646,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
     return (
       <div className="col-12 col-md-6">
         <label className="col-form-label" htmlFor={id}>
-          {i18n.t(key)}
+          {I18NextService.i18n.t(key)}
         </label>
         <div className="d-flex justify-content-between align-items-center">
           <input
@@ -646,7 +660,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
           />
           <button
             type="button"
-            className="btn btn-sm bg-success ml-2"
+            className="btn btn-sm bg-success ms-2"
             onClick={linkEvent(key, this.handleAddInstance)}
             style={"width: 2rem; height: 2rem;"}
             tabIndex={
@@ -717,7 +731,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
     event.preventDefault();
     const auth = myAuthRequired();
     i.setState(s => ((s.siteForm.auth = auth), s));
-    i.setState({ loading: true, submitted: true });
+    i.setState({ submitted: true });
 
     const stateSiteForm = i.state.siteForm;
 
@@ -765,7 +779,6 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
           stateSiteForm.rate_limit_search_per_second,
         federation_enabled: stateSiteForm.federation_enabled,
         federation_debug: stateSiteForm.federation_debug,
-        federation_worker_count: stateSiteForm.federation_worker_count,
         captcha_enabled: stateSiteForm.captcha_enabled,
         captcha_difficulty: stateSiteForm.captcha_difficulty,
         allowed_instances: stateSiteForm.allowed_instances,
@@ -780,6 +793,11 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
 
   handleAddInstance(key: InstanceKey) {
     const instance = this.state.instance_select[key].trim();
+
+    if (!validInstanceTLD(instance)) {
+      return;
+    }
+
     if (!this.state.siteForm[key]?.includes(instance)) {
       this.setState(s => ({
         ...s,
@@ -961,14 +979,6 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
     i.setState(i.state);
   }
 
-  handleSiteFederationWorkerCount(i: SiteForm, event: any) {
-    i.setState(
-      s => (
-        (s.siteForm.federation_worker_count = Number(event.target.value)), s
-      )
-    );
-  }
-
   handleSiteCaptchaEnabled(i: SiteForm, event: any) {
     i.state.siteForm.captcha_enabled = event.target.checked;
     i.setState(i.state);