]> Untitled Git - lemmy-ui.git/blob - src/shared/components/community/community-form.tsx
f317c983b364fde1725c28e5e26a684ebf52679e
[lemmy-ui.git] / src / shared / components / community / community-form.tsx
1 import { Component, linkEvent } from "inferno";
2 import {
3   CommunityView,
4   CreateCommunity,
5   EditCommunity,
6   Language,
7 } from "lemmy-js-client";
8 import { i18n } from "../../i18next";
9 import { capitalizeFirstLetter, myAuthRequired, randomStr } from "../../utils";
10 import { Icon, Spinner } from "../common/icon";
11 import { ImageUploadForm } from "../common/image-upload-form";
12 import { LanguageSelect } from "../common/language-select";
13 import { MarkdownTextArea } from "../common/markdown-textarea";
14 import NavigationPrompt from "../common/navigation-prompt";
15
16 interface CommunityFormProps {
17   community_view?: CommunityView; // If a community is given, that means this is an edit
18   allLanguages: Language[];
19   siteLanguages: number[];
20   communityLanguages?: number[];
21   onCancel?(): any;
22   onUpsertCommunity(form: CreateCommunity | EditCommunity): void;
23   enableNsfw?: boolean;
24 }
25
26 interface CommunityFormState {
27   form: {
28     name?: string;
29     title?: string;
30     description?: string;
31     icon?: string;
32     banner?: string;
33     nsfw?: boolean;
34     posting_restricted_to_mods?: boolean;
35     discussion_languages?: number[];
36   };
37   loading: boolean;
38   submitted: boolean;
39 }
40
41 export class CommunityForm extends Component<
42   CommunityFormProps,
43   CommunityFormState
44 > {
45   private id = `community-form-${randomStr()}`;
46
47   state: CommunityFormState = {
48     form: {},
49     loading: false,
50     submitted: false,
51   };
52
53   constructor(props: any, context: any) {
54     super(props, context);
55
56     this.handleCommunityDescriptionChange =
57       this.handleCommunityDescriptionChange.bind(this);
58
59     this.handleIconUpload = this.handleIconUpload.bind(this);
60     this.handleIconRemove = this.handleIconRemove.bind(this);
61
62     this.handleBannerUpload = this.handleBannerUpload.bind(this);
63     this.handleBannerRemove = this.handleBannerRemove.bind(this);
64
65     this.handleDiscussionLanguageChange =
66       this.handleDiscussionLanguageChange.bind(this);
67
68     const cv = this.props.community_view;
69
70     if (cv) {
71       this.state = {
72         ...this.state,
73         form: {
74           name: cv.community.name,
75           title: cv.community.title,
76           description: cv.community.description,
77           nsfw: cv.community.nsfw,
78           icon: cv.community.icon,
79           banner: cv.community.banner,
80           posting_restricted_to_mods: cv.community.posting_restricted_to_mods,
81           discussion_languages: this.props.communityLanguages,
82         },
83         loading: false,
84       };
85     }
86   }
87
88   render() {
89     return (
90       <form onSubmit={linkEvent(this, this.handleCreateCommunitySubmit)}>
91         <NavigationPrompt
92           when={
93             !this.state.loading &&
94             !!(
95               this.state.form.name ||
96               this.state.form.title ||
97               this.state.form.description
98             ) &&
99             !this.state.submitted
100           }
101         />
102         {!this.props.community_view && (
103           <div className="form-group row">
104             <label
105               className="col-12 col-sm-2 col-form-label"
106               htmlFor="community-name"
107             >
108               {i18n.t("name")}
109               <span
110                 className="position-absolute pointer unselectable ml-2 text-muted"
111                 data-tippy-content={i18n.t("name_explain")}
112               >
113                 <Icon icon="help-circle" classes="icon-inline" />
114               </span>
115             </label>
116             <div className="col-12 col-sm-10">
117               <input
118                 type="text"
119                 id="community-name"
120                 className="form-control"
121                 value={this.state.form.name}
122                 onInput={linkEvent(this, this.handleCommunityNameChange)}
123                 required
124                 minLength={3}
125                 pattern="[a-z0-9_]+"
126                 title={i18n.t("community_reqs")}
127               />
128             </div>
129           </div>
130         )}
131         <div className="form-group row">
132           <label
133             className="col-12 col-sm-2 col-form-label"
134             htmlFor="community-title"
135           >
136             {i18n.t("display_name")}
137             <span
138               className="position-absolute pointer unselectable ml-2 text-muted"
139               data-tippy-content={i18n.t("display_name_explain")}
140             >
141               <Icon icon="help-circle" classes="icon-inline" />
142             </span>
143           </label>
144           <div className="col-12 col-sm-10">
145             <input
146               type="text"
147               id="community-title"
148               value={this.state.form.title}
149               onInput={linkEvent(this, this.handleCommunityTitleChange)}
150               className="form-control"
151               required
152               minLength={3}
153               maxLength={100}
154             />
155           </div>
156         </div>
157         <div className="form-group row">
158           <label className="col-12 col-sm-2">{i18n.t("icon")}</label>
159           <div className="col-12 col-sm-10">
160             <ImageUploadForm
161               uploadTitle={i18n.t("upload_icon")}
162               imageSrc={this.state.form.icon}
163               onUpload={this.handleIconUpload}
164               onRemove={this.handleIconRemove}
165               rounded
166             />
167           </div>
168         </div>
169         <div className="form-group row">
170           <label className="col-12 col-sm-2">{i18n.t("banner")}</label>
171           <div className="col-12 col-sm-10">
172             <ImageUploadForm
173               uploadTitle={i18n.t("upload_banner")}
174               imageSrc={this.state.form.banner}
175               onUpload={this.handleBannerUpload}
176               onRemove={this.handleBannerRemove}
177             />
178           </div>
179         </div>
180         <div className="form-group row">
181           <label className="col-12 col-sm-2 col-form-label" htmlFor={this.id}>
182             {i18n.t("sidebar")}
183           </label>
184           <div className="col-12 col-sm-10">
185             <MarkdownTextArea
186               initialContent={this.state.form.description}
187               placeholder={i18n.t("description")}
188               onContentChange={this.handleCommunityDescriptionChange}
189               hideNavigationWarnings
190               allLanguages={[]}
191               siteLanguages={[]}
192             />
193           </div>
194         </div>
195
196         {this.props.enableNsfw && (
197           <div className="form-group row">
198             <legend className="col-form-label col-sm-2 pt-0">
199               {i18n.t("nsfw")}
200             </legend>
201             <div className="col-10">
202               <div className="form-check">
203                 <input
204                   className="form-check-input position-static"
205                   id="community-nsfw"
206                   type="checkbox"
207                   checked={this.state.form.nsfw}
208                   onChange={linkEvent(this, this.handleCommunityNsfwChange)}
209                 />
210               </div>
211             </div>
212           </div>
213         )}
214         <div className="form-group row">
215           <legend className="col-form-label col-6 pt-0">
216             {i18n.t("only_mods_can_post_in_community")}
217           </legend>
218           <div className="col-6">
219             <div className="form-check">
220               <input
221                 className="form-check-input position-static"
222                 id="community-only-mods-can-post"
223                 type="checkbox"
224                 checked={this.state.form.posting_restricted_to_mods}
225                 onChange={linkEvent(
226                   this,
227                   this.handleCommunityPostingRestrictedToMods
228                 )}
229               />
230             </div>
231           </div>
232         </div>
233         <LanguageSelect
234           allLanguages={this.props.allLanguages}
235           siteLanguages={this.props.siteLanguages}
236           showSite
237           selectedLanguageIds={this.state.form.discussion_languages}
238           multiple={true}
239           onChange={this.handleDiscussionLanguageChange}
240         />
241         <div className="form-group row">
242           <div className="col-12">
243             <button
244               type="submit"
245               className="btn btn-secondary mr-2"
246               disabled={this.state.loading}
247             >
248               {this.state.loading ? (
249                 <Spinner />
250               ) : this.props.community_view ? (
251                 capitalizeFirstLetter(i18n.t("save"))
252               ) : (
253                 capitalizeFirstLetter(i18n.t("create"))
254               )}
255             </button>
256             {this.props.community_view && (
257               <button
258                 type="button"
259                 className="btn btn-secondary"
260                 onClick={linkEvent(this, this.handleCancel)}
261               >
262                 {i18n.t("cancel")}
263               </button>
264             )}
265           </div>
266         </div>
267       </form>
268     );
269   }
270
271   handleCreateCommunitySubmit(i: CommunityForm, event: any) {
272     event.preventDefault();
273     i.setState({ loading: true, submitted: true });
274     const cForm = i.state.form;
275     const auth = myAuthRequired();
276
277     const cv = i.props.community_view;
278
279     if (cv) {
280       i.props.onUpsertCommunity({
281         community_id: cv.community.id,
282         title: cForm.title,
283         description: cForm.description,
284         icon: cForm.icon,
285         banner: cForm.banner,
286         nsfw: cForm.nsfw,
287         posting_restricted_to_mods: cForm.posting_restricted_to_mods,
288         discussion_languages: cForm.discussion_languages,
289         auth,
290       });
291     } else {
292       if (cForm.title && cForm.name) {
293         i.props.onUpsertCommunity({
294           name: cForm.name,
295           title: cForm.title,
296           description: cForm.description,
297           icon: cForm.icon,
298           banner: cForm.banner,
299           nsfw: cForm.nsfw,
300           posting_restricted_to_mods: cForm.posting_restricted_to_mods,
301           discussion_languages: cForm.discussion_languages,
302           auth,
303         });
304       }
305     }
306   }
307
308   handleCommunityNameChange(i: CommunityForm, event: any) {
309     i.setState(s => ((s.form.name = event.target.value), s));
310   }
311
312   handleCommunityTitleChange(i: CommunityForm, event: any) {
313     i.setState(s => ((s.form.title = event.target.value), s));
314   }
315
316   handleCommunityDescriptionChange(val: string) {
317     this.setState(s => ((s.form.description = val), s));
318   }
319
320   handleCommunityNsfwChange(i: CommunityForm, event: any) {
321     i.setState(s => ((s.form.nsfw = event.target.checked), s));
322   }
323
324   handleCommunityPostingRestrictedToMods(i: CommunityForm, event: any) {
325     i.setState(
326       s => ((s.form.posting_restricted_to_mods = event.target.checked), s)
327     );
328   }
329
330   handleCancel(i: CommunityForm) {
331     i.props.onCancel?.();
332   }
333
334   handleIconUpload(url: string) {
335     this.setState(s => ((s.form.icon = url), s));
336   }
337
338   handleIconRemove() {
339     this.setState(s => ((s.form.icon = ""), s));
340   }
341
342   handleBannerUpload(url: string) {
343     this.setState(s => ((s.form.banner = url), s));
344   }
345
346   handleBannerRemove() {
347     this.setState(s => ((s.form.banner = ""), s));
348   }
349
350   handleDiscussionLanguageChange(val: number[]) {
351     this.setState(s => ((s.form.discussion_languages = val), s));
352   }
353 }