]> Untitled Git - lemmy-ui.git/blob - src/shared/components/home/rate-limit-form.tsx
Merge branch 'main' into fix/fix-badges-spacing-componentize
[lemmy-ui.git] / src / shared / components / home / rate-limit-form.tsx
1 import { myAuthRequired } from "@utils/app";
2 import { capitalizeFirstLetter } from "@utils/helpers";
3 import classNames from "classnames";
4 import { Component, FormEventHandler, linkEvent } from "inferno";
5 import { EditSite, LocalSiteRateLimit } from "lemmy-js-client";
6 import { I18NextService } from "../../services";
7 import { Spinner } from "../common/icon";
8 import Tabs from "../common/tabs";
9
10 const rateLimitTypes = [
11   "message",
12   "post",
13   "image",
14   "comment",
15   "search",
16   "register",
17 ] as const;
18
19 interface RateLimitsProps {
20   handleRateLimit: FormEventHandler<HTMLInputElement>;
21   handleRateLimitPerSecond: FormEventHandler<HTMLInputElement>;
22   rateLimitValue?: number;
23   rateLimitPerSecondValue?: number;
24   className?: string;
25 }
26
27 interface RateLimitFormProps {
28   rateLimits: LocalSiteRateLimit;
29   onSaveSite(form: EditSite): void;
30   loading: boolean;
31 }
32
33 interface RateLimitFormState {
34   form: {
35     message?: number;
36     message_per_second?: number;
37     post?: number;
38     post_per_second?: number;
39     comment?: number;
40     comment_per_second?: number;
41     image?: number;
42     image_per_second?: number;
43     search?: number;
44     search_per_second?: number;
45     register?: number;
46     register_per_second?: number;
47   };
48 }
49
50 function RateLimits({
51   handleRateLimit,
52   handleRateLimitPerSecond,
53   rateLimitPerSecondValue,
54   rateLimitValue,
55   className,
56 }: RateLimitsProps) {
57   return (
58     <div role="tabpanel" className={classNames("mb-3 row", className)}>
59       <div className="col-md-6">
60         <label htmlFor="rate-limit">
61           {I18NextService.i18n.t("rate_limit")}
62         </label>
63         <input
64           type="number"
65           id="rate-limit"
66           className="form-control"
67           min={0}
68           value={rateLimitValue}
69           onInput={handleRateLimit}
70         />
71       </div>
72       <div className="col-md-6">
73         <label htmlFor="rate-limit-per-second">
74           {I18NextService.i18n.t("per_second")}
75         </label>
76         <input
77           type="number"
78           id="rate-limit-per-second"
79           className="form-control"
80           min={0}
81           value={rateLimitPerSecondValue}
82           onInput={handleRateLimitPerSecond}
83         />
84       </div>
85     </div>
86   );
87 }
88
89 function handleRateLimitChange(
90   { rateLimitType, ctx }: { rateLimitType: string; ctx: RateLimitsForm },
91   event: any
92 ) {
93   ctx.setState(prev => ({
94     ...prev,
95     form: {
96       ...prev.form,
97       [rateLimitType]: Number(event.target.value),
98     },
99   }));
100 }
101
102 function handlePerSecondChange(
103   { rateLimitType, ctx }: { rateLimitType: string; ctx: RateLimitsForm },
104   event: any
105 ) {
106   ctx.setState(prev => ({
107     ...prev,
108     form: {
109       ...prev.form,
110       [`${rateLimitType}_per_second`]: Number(event.target.value),
111     },
112   }));
113 }
114
115 function submitRateLimitForm(i: RateLimitsForm, event: any) {
116   event.preventDefault();
117   const auth = myAuthRequired();
118   const form: EditSite = Object.entries(i.state.form).reduce(
119     (acc, [key, val]) => {
120       acc[`rate_limit_${key}`] = val;
121       return acc;
122     },
123     {
124       auth,
125     }
126   );
127
128   i.props.onSaveSite(form);
129 }
130
131 export default class RateLimitsForm extends Component<
132   RateLimitFormProps,
133   RateLimitFormState
134 > {
135   state: RateLimitFormState = {
136     form: this.props.rateLimits,
137   };
138   constructor(props: RateLimitFormProps, context: any) {
139     super(props, context);
140   }
141
142   render() {
143     return (
144       <form
145         className="rate-limit-form"
146         onSubmit={linkEvent(this, submitRateLimitForm)}
147       >
148         <h1 className="h4 mb-4">
149           {I18NextService.i18n.t("rate_limit_header")}
150         </h1>
151         <Tabs
152           tabs={rateLimitTypes.map(rateLimitType => ({
153             key: rateLimitType,
154             label: I18NextService.i18n.t(`rate_limit_${rateLimitType}`),
155             getNode: isSelected => (
156               <RateLimits
157                 className={classNames("tab-pane show", {
158                   active: isSelected,
159                 })}
160                 handleRateLimit={linkEvent(
161                   { rateLimitType, ctx: this },
162                   handleRateLimitChange
163                 )}
164                 handleRateLimitPerSecond={linkEvent(
165                   { rateLimitType, ctx: this },
166                   handlePerSecondChange
167                 )}
168                 rateLimitValue={this.state.form[rateLimitType]}
169                 rateLimitPerSecondValue={
170                   this.state.form[`${rateLimitType}_per_second`]
171                 }
172               />
173             ),
174           }))}
175         />
176         <div className="col-12 mb-3">
177           <button
178             type="submit"
179             className="btn btn-secondary me-2"
180             disabled={this.props.loading}
181           >
182             {this.props.loading ? (
183               <Spinner />
184             ) : (
185               capitalizeFirstLetter(I18NextService.i18n.t("save"))
186             )}
187           </button>
188         </div>
189       </form>
190     );
191   }
192 }