]> Untitled Git - lemmy-ui.git/commitdiff
Admin Settings: Bugfixes (#1313)
authorAlec Armbruster <35377827+alectrocute@users.noreply.github.com>
Fri, 16 Jun 2023 15:52:47 +0000 (11:52 -0400)
committerGitHub <noreply@github.com>
Fri, 16 Jun 2023 15:52:47 +0000 (11:52 -0400)
* move loading state to AdminSettings, pass as prop, tweak margin on some labels, add missing bind

* default loading state to false on setup.tsx, add util

* rename util to make more sense

* make @dessalines suggested changes

src/shared/components/home/admin-settings.tsx
src/shared/components/home/emojis-form.tsx
src/shared/components/home/rate-limit-form.tsx
src/shared/components/home/setup.tsx
src/shared/components/home/site-form.tsx
src/shared/components/home/tagline-form.tsx
src/shared/utils.ts

index 9b7256d03a507e561f5bf2e92cecc0586b216b60..302e96bdf85e4a3798ee9af1e5c28e8c2305450d 100644 (file)
@@ -39,6 +39,8 @@ interface AdminSettingsState {
   instancesRes: RequestState<GetFederatedInstancesResponse>;
   bannedRes: RequestState<BannedPersonsResponse>;
   leaveAdminTeamRes: RequestState<GetSiteResponse>;
+  emojiLoading: boolean;
+  loading: boolean;
   themeList: string[];
   isIsomorphic: boolean;
 }
@@ -52,6 +54,8 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
     bannedRes: { state: "empty" },
     instancesRes: { state: "empty" },
     leaveAdminTeamRes: { state: "empty" },
+    emojiLoading: false,
+    loading: false,
     themeList: [],
     isIsomorphic: false,
   };
@@ -81,6 +85,7 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
       bannedRes: { state: "loading" },
       instancesRes: { state: "loading" },
       themeList: [],
+      loading: true,
     });
 
     const auth = myAuthRequired();
@@ -95,6 +100,7 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
       bannedRes,
       instancesRes,
       themeList,
+      loading: false,
     });
   }
 
@@ -156,6 +162,7 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
                       onSaveSite={this.handleEditSite}
                       siteRes={this.state.siteRes}
                       themeList={this.state.themeList}
+                      loading={this.state.loading}
                     />
                   </div>
                   <div className="col-12 col-md-6">
@@ -174,6 +181,7 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
                     this.state.siteRes.site_view.local_site_rate_limit
                   }
                   onSaveSite={this.handleEditSite}
+                  loading={this.state.loading}
                 />
               ),
             },
@@ -185,6 +193,7 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
                   <TaglineForm
                     taglines={this.state.siteRes.taglines}
                     onSaveSite={this.handleEditSite}
+                    loading={this.state.loading}
                   />
                 </div>
               ),
@@ -198,6 +207,7 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
                     onCreate={this.handleCreateEmoji}
                     onDelete={this.handleDeleteEmoji}
                     onEdit={this.handleEditEmoji}
+                    loading={this.state.emojiLoading}
                   />
                 </div>
               ),
@@ -266,6 +276,8 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
   }
 
   async handleEditSite(form: EditSite) {
+    this.setState({ loading: true });
+
     const editRes = await HttpService.client.editSite(form);
 
     if (editRes.state === "success") {
@@ -278,6 +290,8 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
       toast(i18n.t("site_saved"));
     }
 
+    this.setState({ loading: false });
+
     return editRes;
   }
 
@@ -300,23 +314,35 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
   }
 
   async handleEditEmoji(form: EditCustomEmoji) {
+    this.setState({ emojiLoading: true });
+
     const res = await HttpService.client.editCustomEmoji(form);
     if (res.state === "success") {
       updateEmojiDataModel(res.data.custom_emoji);
     }
+
+    this.setState({ emojiLoading: false });
   }
 
   async handleDeleteEmoji(form: DeleteCustomEmoji) {
+    this.setState({ emojiLoading: true });
+
     const res = await HttpService.client.deleteCustomEmoji(form);
     if (res.state === "success") {
       removeFromEmojiDataModel(res.data.id);
     }
+
+    this.setState({ emojiLoading: false });
   }
 
   async handleCreateEmoji(form: CreateCustomEmoji) {
+    this.setState({ emojiLoading: true });
+
     const res = await HttpService.client.createCustomEmoji(form);
     if (res.state === "success") {
       updateEmojiDataModel(res.data.custom_emoji);
     }
+
+    this.setState({ emojiLoading: false });
   }
 }
index 171b7c99b2d8f4e71543e14612377d7cb1be1888..f77f51258e14c584ce9cc3192873a5b7ac1bd726 100644 (file)
@@ -23,12 +23,12 @@ interface EmojiFormProps {
   onEdit(form: EditCustomEmoji): void;
   onCreate(form: CreateCustomEmoji): void;
   onDelete(form: DeleteCustomEmoji): void;
+  loading: boolean;
 }
 
 interface EmojiFormState {
   siteRes: GetSiteResponse;
   customEmojis: CustomEmojiViewForm[];
-  loading: boolean;
   page: number;
 }
 
@@ -47,7 +47,6 @@ export class EmojiForm extends Component<EmojiFormProps, EmojiFormState> {
   private isoData = setIsoData(this.context);
   private itemsPerPage = 15;
   private emptyState: EmojiFormState = {
-    loading: false,
     siteRes: this.isoData.site_res,
     customEmojis: this.isoData.site_res.custom_emojis.map((x, index) => ({
       id: x.custom_emoji.id,
@@ -223,7 +222,7 @@ export class EmojiForm extends Component<EmojiFormProps, EmojiFormState> {
                             data-tippy-content={i18n.t("save")}
                             aria-label={i18n.t("save")}
                             disabled={
-                              this.state.loading ||
+                              this.props.loading ||
                               !this.canEdit(cv) ||
                               !cv.changed
                             }
@@ -243,7 +242,7 @@ export class EmojiForm extends Component<EmojiFormProps, EmojiFormState> {
                           )}
                           data-tippy-content={i18n.t("delete")}
                           aria-label={i18n.t("delete")}
-                          disabled={this.state.loading}
+                          disabled={this.props.loading}
                           title={i18n.t("delete")}
                         >
                           <Icon
index 74ed18e32c81bd19ab67325997561950cb3fd8f3..0ce01260088f3b616e56cfb3a5700348f7889367 100644 (file)
@@ -24,6 +24,7 @@ interface RateLimitsProps {
 interface RateLimitFormProps {
   rateLimits: LocalSiteRateLimit;
   onSaveSite(form: EditSite): void;
+  loading: boolean;
 }
 
 interface RateLimitFormState {
@@ -41,7 +42,6 @@ interface RateLimitFormState {
     register?: number;
     register_per_second?: number;
   };
-  loading: boolean;
 }
 
 function RateLimits({
@@ -117,7 +117,6 @@ function submitRateLimitForm(i: RateLimitsForm, event: any) {
     }
   );
 
-  i.setState({ loading: true });
   i.props.onSaveSite(form);
 }
 
@@ -126,7 +125,6 @@ export default class RateLimitsForm extends Component<
   RateLimitFormState
 > {
   state: RateLimitFormState = {
-    loading: false,
     form: this.props.rateLimits,
   };
   constructor(props: RateLimitFormProps, context: any) {
@@ -164,9 +162,9 @@ export default class RateLimitsForm extends Component<
             <button
               type="submit"
               className="btn btn-secondary mr-2"
-              disabled={this.state.loading}
+              disabled={this.props.loading}
             >
-              {this.state.loading ? (
+              {this.props.loading ? (
                 <Spinner />
               ) : (
                 capitalizeFirstLetter(i18n.t("save"))
index 14350a58805afe362b4d3158f9d28b658872806d..b658bd2432cc1703346b4150f31ce925f019d3b5 100644 (file)
@@ -73,6 +73,7 @@ export class Setup extends Component<any, State> {
                 onSaveSite={this.handleCreateSite}
                 siteRes={this.state.siteRes}
                 themeList={this.state.themeList}
+                loading={false}
               />
             )}
           </div>
index 3b451e66ab10bd04493faea8b1ece5e4deda08e7..8b56808e4c53d2bdf748af736d04b9f76f4f36c7 100644 (file)
@@ -12,7 +12,11 @@ import {
   ListingType,
 } from "lemmy-js-client";
 import { i18n } from "../../i18next";
-import { capitalizeFirstLetter, myAuthRequired } from "../../utils";
+import {
+  capitalizeFirstLetter,
+  myAuthRequired,
+  validInstanceTLD,
+} from "../../utils";
 import { Icon, Spinner } from "../common/icon";
 import { ImageUploadForm } from "../common/image-upload-form";
 import { LanguageSelect } from "../common/language-select";
@@ -27,11 +31,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 +48,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: "",
@@ -107,23 +110,21 @@ 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)}>
         <NavigationPrompt
           when={
-            !this.state.loading &&
+            !this.props.loading &&
             !siteSetup &&
             !!(
               this.state.siteForm.name ||
@@ -136,8 +137,8 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
         />
         <h5>{`${
           siteSetup
-            ? capitalizeFirstLetter(i18n.t("save"))
-            : capitalizeFirstLetter(i18n.t("name"))
+            ? capitalizeFirstLetter(i18n.t("edit"))
+            : capitalizeFirstLetter(i18n.t("setup"))
         } ${i18n.t("your_site")}`}</h5>
         <div className="form-group row">
           <label className="col-12 col-form-label" htmlFor="create-site-name">
@@ -157,7 +158,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
           </div>
         </div>
         <div className="form-group">
-          <label>{i18n.t("icon")}</label>
+          <label className="mr-2">{i18n.t("icon")}</label>
           <ImageUploadForm
             uploadTitle={i18n.t("upload_icon")}
             imageSrc={this.state.siteForm.icon}
@@ -167,7 +168,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
           />
         </div>
         <div className="form-group">
-          <label>{i18n.t("banner")}</label>
+          <label className="mr-2">{i18n.t("banner")}</label>
           <ImageUploadForm
             uploadTitle={i18n.t("upload_banner")}
             imageSrc={this.state.siteForm.banner}
@@ -609,9 +610,9 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
             <button
               type="submit"
               className="btn btn-secondary mr-2"
-              disabled={this.state.loading}
+              disabled={this.props.loading}
             >
-              {this.state.loading ? (
+              {this.props.loading ? (
                 <Spinner />
               ) : siteSetup ? (
                 capitalizeFirstLetter(i18n.t("save"))
@@ -717,7 +718,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;
 
@@ -780,6 +781,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,
index 44ca4fc02f4c1172689da10913e6d39f0a3182ab..59e8dec4a2e7b2e7b99c710a830edf43963780f1 100644 (file)
@@ -9,17 +9,16 @@ import { MarkdownTextArea } from "../common/markdown-textarea";
 interface TaglineFormProps {
   taglines: Array<Tagline>;
   onSaveSite(form: EditSite): void;
+  loading: boolean;
 }
 
 interface TaglineFormState {
   taglines: Array<string>;
-  loading: boolean;
   editingRow?: number;
 }
 
 export class TaglineForm extends Component<TaglineFormProps, TaglineFormState> {
   state: TaglineFormState = {
-    loading: false,
     editingRow: undefined,
     taglines: this.props.taglines.map(x => x.content),
   };
@@ -30,10 +29,6 @@ export class TaglineForm extends Component<TaglineFormProps, TaglineFormState> {
     return i18n.t("taglines");
   }
 
-  componentWillReceiveProps() {
-    this.setState({ loading: false });
-  }
-
   render() {
     return (
       <div className="col-12">
@@ -110,9 +105,9 @@ export class TaglineForm extends Component<TaglineFormProps, TaglineFormState> {
               <button
                 onClick={linkEvent(this, this.handleSaveClick)}
                 className="btn btn-secondary mr-2"
-                disabled={this.state.loading}
+                disabled={this.props.loading}
               >
-                {this.state.loading ? (
+                {this.props.loading ? (
                   <Spinner />
                 ) : (
                   capitalizeFirstLetter(i18n.t("save"))
@@ -153,7 +148,6 @@ export class TaglineForm extends Component<TaglineFormProps, TaglineFormState> {
   }
 
   async handleSaveClick(i: TaglineForm) {
-    i.setState({ loading: true });
     i.props.onSaveSite({
       taglines: i.state.taglines,
       auth: myAuthRequired(),
index 766e38a847ed97d177941bf29b48dde87739704a..df7673a47b6a1e8e860a41b764bf70c58745b464 100644 (file)
@@ -316,6 +316,7 @@ export function amTopMod(
 
 const imageRegex = /(http)?s?:?(\/\/[^"']*\.(?:jpg|jpeg|gif|png|svg|webp))/;
 const videoRegex = /(http)?s?:?(\/\/[^"']*\.(?:mp4|webm))/;
+const tldRegex = /([a-z0-9]+\.)*[a-z0-9]+\.[a-z]+/;
 
 export function isImage(url: string) {
   return imageRegex.test(url);
@@ -329,6 +330,10 @@ export function validURL(str: string) {
   return !!new URL(str);
 }
 
+export function validInstanceTLD(str: string) {
+  return tldRegex.test(str);
+}
+
 export function communityRSSUrl(actorId: string, sort: string): string {
   const url = new URL(actorId);
   return `${url.origin}/feeds${url.pathname}.xml?sort=${sort}`;