]> Untitled Git - lemmy-ui.git/blob - src/shared/components/home/site-form.tsx
Legal info dess (#666)
[lemmy-ui.git] / src / shared / components / home / site-form.tsx
1 import { Component, linkEvent } from "inferno";
2 import { Prompt } from "inferno-router";
3 import { CreateSite, EditSite, ListingType, Site } from "lemmy-js-client";
4 import { i18n } from "../../i18next";
5 import { WebSocketService } from "../../services";
6 import {
7   authField,
8   capitalizeFirstLetter,
9   fetchThemeList,
10   wsClient,
11 } from "../../utils";
12 import { Spinner } from "../common/icon";
13 import { ImageUploadForm } from "../common/image-upload-form";
14 import { ListingTypeSelect } from "../common/listing-type-select";
15 import { MarkdownTextArea } from "../common/markdown-textarea";
16
17 interface SiteFormProps {
18   site?: Site; // If a site is given, that means this is an edit
19   showLocal: boolean;
20   onCancel?(): any;
21   onEdit?(): any;
22 }
23
24 interface SiteFormState {
25   siteForm: EditSite;
26   loading: boolean;
27   themeList: string[];
28 }
29
30 export class SiteForm extends Component<SiteFormProps, SiteFormState> {
31   private emptyState: SiteFormState = {
32     siteForm: {
33       enable_downvotes: true,
34       open_registration: true,
35       enable_nsfw: true,
36       name: null,
37       icon: null,
38       banner: null,
39       require_email_verification: null,
40       require_application: null,
41       application_question: null,
42       private_instance: null,
43       default_theme: null,
44       default_post_listing_type: null,
45       legal_information: null,
46       auth: authField(false),
47     },
48     loading: false,
49     themeList: [],
50   };
51
52   constructor(props: any, context: any) {
53     super(props, context);
54
55     this.state = this.emptyState;
56     this.handleSiteSidebarChange = this.handleSiteSidebarChange.bind(this);
57     this.handleSiteLegalInfoChange = this.handleSiteLegalInfoChange.bind(this);
58     this.handleSiteApplicationQuestionChange =
59       this.handleSiteApplicationQuestionChange.bind(this);
60
61     this.handleIconUpload = this.handleIconUpload.bind(this);
62     this.handleIconRemove = this.handleIconRemove.bind(this);
63
64     this.handleBannerUpload = this.handleBannerUpload.bind(this);
65     this.handleBannerRemove = this.handleBannerRemove.bind(this);
66
67     this.handleDefaultPostListingTypeChange =
68       this.handleDefaultPostListingTypeChange.bind(this);
69
70     if (this.props.site) {
71       let site = this.props.site;
72       this.state.siteForm = {
73         name: site.name,
74         sidebar: site.sidebar,
75         description: site.description,
76         enable_downvotes: site.enable_downvotes,
77         open_registration: site.open_registration,
78         enable_nsfw: site.enable_nsfw,
79         community_creation_admin_only: site.community_creation_admin_only,
80         icon: site.icon,
81         banner: site.banner,
82         require_email_verification: site.require_email_verification,
83         require_application: site.require_application,
84         application_question: site.application_question,
85         private_instance: site.private_instance,
86         default_theme: site.default_theme,
87         default_post_listing_type: site.default_post_listing_type,
88         legal_information: site.legal_information,
89         auth: authField(false),
90       };
91     }
92   }
93
94   async componentDidMount() {
95     this.state.themeList = await fetchThemeList();
96     this.setState(this.state);
97   }
98
99   // Necessary to stop the loading
100   componentWillReceiveProps() {
101     this.state.loading = false;
102     this.setState(this.state);
103   }
104
105   componentDidUpdate() {
106     if (
107       !this.state.loading &&
108       !this.props.site &&
109       (this.state.siteForm.name ||
110         this.state.siteForm.sidebar ||
111         this.state.siteForm.application_question ||
112         this.state.siteForm.description)
113     ) {
114       window.onbeforeunload = () => true;
115     } else {
116       window.onbeforeunload = undefined;
117     }
118   }
119
120   componentWillUnmount() {
121     window.onbeforeunload = null;
122   }
123
124   render() {
125     return (
126       <>
127         <Prompt
128           when={
129             !this.state.loading &&
130             !this.props.site &&
131             (this.state.siteForm.name ||
132               this.state.siteForm.sidebar ||
133               this.state.siteForm.application_question ||
134               this.state.siteForm.description)
135           }
136           message={i18n.t("block_leaving")}
137         />
138         <form onSubmit={linkEvent(this, this.handleCreateSiteSubmit)}>
139           <h5>{`${
140             this.props.site
141               ? capitalizeFirstLetter(i18n.t("save"))
142               : capitalizeFirstLetter(i18n.t("name"))
143           } ${i18n.t("your_site")}`}</h5>
144           <div class="form-group row">
145             <label class="col-12 col-form-label" htmlFor="create-site-name">
146               {i18n.t("name")}
147             </label>
148             <div class="col-12">
149               <input
150                 type="text"
151                 id="create-site-name"
152                 class="form-control"
153                 value={this.state.siteForm.name}
154                 onInput={linkEvent(this, this.handleSiteNameChange)}
155                 required
156                 minLength={3}
157                 maxLength={20}
158               />
159             </div>
160           </div>
161           <div class="form-group">
162             <label>{i18n.t("icon")}</label>
163             <ImageUploadForm
164               uploadTitle={i18n.t("upload_icon")}
165               imageSrc={this.state.siteForm.icon}
166               onUpload={this.handleIconUpload}
167               onRemove={this.handleIconRemove}
168               rounded
169             />
170           </div>
171           <div class="form-group">
172             <label>{i18n.t("banner")}</label>
173             <ImageUploadForm
174               uploadTitle={i18n.t("upload_banner")}
175               imageSrc={this.state.siteForm.banner}
176               onUpload={this.handleBannerUpload}
177               onRemove={this.handleBannerRemove}
178             />
179           </div>
180           <div class="form-group row">
181             <label class="col-12 col-form-label" htmlFor="site-desc">
182               {i18n.t("description")}
183             </label>
184             <div class="col-12">
185               <input
186                 type="text"
187                 class="form-control"
188                 id="site-desc"
189                 value={this.state.siteForm.description}
190                 onInput={linkEvent(this, this.handleSiteDescChange)}
191                 maxLength={150}
192               />
193             </div>
194           </div>
195           <div class="form-group row">
196             <label class="col-12 col-form-label">{i18n.t("sidebar")}</label>
197             <div class="col-12">
198               <MarkdownTextArea
199                 initialContent={this.state.siteForm.sidebar}
200                 onContentChange={this.handleSiteSidebarChange}
201                 hideNavigationWarnings
202               />
203             </div>
204           </div>
205           <div class="form-group row">
206             <label class="col-12 col-form-label">
207               {i18n.t("legal_information")}
208             </label>
209             <div class="col-12">
210               <MarkdownTextArea
211                 initialContent={this.state.siteForm.legal_information}
212                 onContentChange={this.handleSiteLegalInfoChange}
213                 hideNavigationWarnings
214               />
215             </div>
216           </div>
217           {this.state.siteForm.require_application && (
218             <div class="form-group row">
219               <label class="col-12 col-form-label">
220                 {i18n.t("application_questionnaire")}
221               </label>
222               <div class="col-12">
223                 <MarkdownTextArea
224                   initialContent={this.state.siteForm.application_question}
225                   onContentChange={this.handleSiteApplicationQuestionChange}
226                   hideNavigationWarnings
227                 />
228               </div>
229             </div>
230           )}
231           <div class="form-group row">
232             <div class="col-12">
233               <div class="form-check">
234                 <input
235                   class="form-check-input"
236                   id="create-site-downvotes"
237                   type="checkbox"
238                   checked={this.state.siteForm.enable_downvotes}
239                   onChange={linkEvent(
240                     this,
241                     this.handleSiteEnableDownvotesChange
242                   )}
243                 />
244                 <label class="form-check-label" htmlFor="create-site-downvotes">
245                   {i18n.t("enable_downvotes")}
246                 </label>
247               </div>
248             </div>
249           </div>
250           <div class="form-group row">
251             <div class="col-12">
252               <div class="form-check">
253                 <input
254                   class="form-check-input"
255                   id="create-site-enable-nsfw"
256                   type="checkbox"
257                   checked={this.state.siteForm.enable_nsfw}
258                   onChange={linkEvent(this, this.handleSiteEnableNsfwChange)}
259                 />
260                 <label
261                   class="form-check-label"
262                   htmlFor="create-site-enable-nsfw"
263                 >
264                   {i18n.t("enable_nsfw")}
265                 </label>
266               </div>
267             </div>
268           </div>
269           <div class="form-group row">
270             <div class="col-12">
271               <div class="form-check">
272                 <input
273                   class="form-check-input"
274                   id="create-site-open-registration"
275                   type="checkbox"
276                   checked={this.state.siteForm.open_registration}
277                   onChange={linkEvent(
278                     this,
279                     this.handleSiteOpenRegistrationChange
280                   )}
281                 />
282                 <label
283                   class="form-check-label"
284                   htmlFor="create-site-open-registration"
285                 >
286                   {i18n.t("open_registration")}
287                 </label>
288               </div>
289             </div>
290           </div>
291           <div class="form-group row">
292             <div class="col-12">
293               <div class="form-check">
294                 <input
295                   class="form-check-input"
296                   id="create-site-community-creation-admin-only"
297                   type="checkbox"
298                   checked={this.state.siteForm.community_creation_admin_only}
299                   onChange={linkEvent(
300                     this,
301                     this.handleSiteCommunityCreationAdminOnly
302                   )}
303                 />
304                 <label
305                   class="form-check-label"
306                   htmlFor="create-site-community-creation-admin-only"
307                 >
308                   {i18n.t("community_creation_admin_only")}
309                 </label>
310               </div>
311             </div>
312           </div>
313           <div class="form-group row">
314             <div class="col-12">
315               <div class="form-check">
316                 <input
317                   class="form-check-input"
318                   id="create-site-require-email-verification"
319                   type="checkbox"
320                   checked={this.state.siteForm.require_email_verification}
321                   onChange={linkEvent(
322                     this,
323                     this.handleSiteRequireEmailVerification
324                   )}
325                 />
326                 <label
327                   class="form-check-label"
328                   htmlFor="create-site-require-email-verification"
329                 >
330                   {i18n.t("require_email_verification")}
331                 </label>
332               </div>
333             </div>
334           </div>
335           <div class="form-group row">
336             <div class="col-12">
337               <div class="form-check">
338                 <input
339                   class="form-check-input"
340                   id="create-site-require-application"
341                   type="checkbox"
342                   checked={this.state.siteForm.require_application}
343                   onChange={linkEvent(this, this.handleSiteRequireApplication)}
344                 />
345                 <label
346                   class="form-check-label"
347                   htmlFor="create-site-require-application"
348                 >
349                   {i18n.t("require_registration_application")}
350                 </label>
351               </div>
352             </div>
353           </div>
354           <div class="form-group row">
355             <div class="col-12">
356               <label
357                 class="form-check-label mr-2"
358                 htmlFor="create-site-default-theme"
359               >
360                 {i18n.t("theme")}
361               </label>
362               <select
363                 id="create-site-default-theme"
364                 value={this.state.siteForm.default_theme}
365                 onChange={linkEvent(this, this.handleSiteDefaultTheme)}
366                 class="custom-select w-auto"
367               >
368                 <option value="browser">{i18n.t("browser_default")}</option>
369                 {this.state.themeList.map(theme => (
370                   <option value={theme}>{theme}</option>
371                 ))}
372               </select>
373             </div>
374           </div>
375           {this.props.showLocal && (
376             <form className="form-group row">
377               <label class="col-sm-3">{i18n.t("listing_type")}</label>
378               <div class="col-sm-9">
379                 <ListingTypeSelect
380                   type_={
381                     ListingType[this.state.siteForm.default_post_listing_type]
382                   }
383                   showLocal
384                   showSubscribed={false}
385                   onChange={this.handleDefaultPostListingTypeChange}
386                 />
387               </div>
388             </form>
389           )}
390           <div class="form-group row">
391             <div class="col-12">
392               <div class="form-check">
393                 <input
394                   class="form-check-input"
395                   id="create-site-private-instance"
396                   type="checkbox"
397                   value={this.state.siteForm.default_theme}
398                   onChange={linkEvent(this, this.handleSitePrivateInstance)}
399                 />
400                 <label
401                   class="form-check-label"
402                   htmlFor="create-site-private-instance"
403                 >
404                   {i18n.t("private_instance")}
405                 </label>
406               </div>
407             </div>
408           </div>
409           <div class="form-group row">
410             <div class="col-12">
411               <button
412                 type="submit"
413                 class="btn btn-secondary mr-2"
414                 disabled={this.state.loading}
415               >
416                 {this.state.loading ? (
417                   <Spinner />
418                 ) : this.props.site ? (
419                   capitalizeFirstLetter(i18n.t("save"))
420                 ) : (
421                   capitalizeFirstLetter(i18n.t("create"))
422                 )}
423               </button>
424               {this.props.site && (
425                 <button
426                   type="button"
427                   class="btn btn-secondary"
428                   onClick={linkEvent(this, this.handleCancel)}
429                 >
430                   {i18n.t("cancel")}
431                 </button>
432               )}
433             </div>
434           </div>
435         </form>
436       </>
437     );
438   }
439
440   handleCreateSiteSubmit(i: SiteForm, event: any) {
441     event.preventDefault();
442     i.state.loading = true;
443     if (i.props.site) {
444       WebSocketService.Instance.send(wsClient.editSite(i.state.siteForm));
445       i.props.onEdit();
446     } else {
447       let form: CreateSite = {
448         name: i.state.siteForm.name || "My site",
449         ...i.state.siteForm,
450       };
451       WebSocketService.Instance.send(wsClient.createSite(form));
452     }
453     i.setState(i.state);
454   }
455
456   handleSiteNameChange(i: SiteForm, event: any) {
457     i.state.siteForm.name = event.target.value;
458     i.setState(i.state);
459   }
460
461   handleSiteSidebarChange(val: string) {
462     this.state.siteForm.sidebar = val;
463     this.setState(this.state);
464   }
465
466   handleSiteLegalInfoChange(val: string) {
467     this.state.siteForm.legal_information = val;
468     this.setState(this.state);
469   }
470
471   handleSiteApplicationQuestionChange(val: string) {
472     this.state.siteForm.application_question = val;
473     this.setState(this.state);
474   }
475
476   handleSiteDescChange(i: SiteForm, event: any) {
477     i.state.siteForm.description = event.target.value;
478     i.setState(i.state);
479   }
480
481   handleSiteEnableNsfwChange(i: SiteForm, event: any) {
482     i.state.siteForm.enable_nsfw = event.target.checked;
483     i.setState(i.state);
484   }
485
486   handleSiteOpenRegistrationChange(i: SiteForm, event: any) {
487     i.state.siteForm.open_registration = event.target.checked;
488     i.setState(i.state);
489   }
490
491   handleSiteCommunityCreationAdminOnly(i: SiteForm, event: any) {
492     i.state.siteForm.community_creation_admin_only = event.target.checked;
493     i.setState(i.state);
494   }
495
496   handleSiteEnableDownvotesChange(i: SiteForm, event: any) {
497     i.state.siteForm.enable_downvotes = event.target.checked;
498     i.setState(i.state);
499   }
500
501   handleSiteRequireApplication(i: SiteForm, event: any) {
502     i.state.siteForm.require_application = event.target.checked;
503     i.setState(i.state);
504   }
505
506   handleSiteRequireEmailVerification(i: SiteForm, event: any) {
507     i.state.siteForm.require_email_verification = event.target.checked;
508     i.setState(i.state);
509   }
510
511   handleSitePrivateInstance(i: SiteForm, event: any) {
512     i.state.siteForm.private_instance = event.target.checked;
513     i.setState(i.state);
514   }
515
516   handleSiteDefaultTheme(i: SiteForm, event: any) {
517     i.state.siteForm.default_theme = event.target.value;
518     i.setState(i.state);
519   }
520
521   handleCancel(i: SiteForm) {
522     i.props.onCancel();
523   }
524
525   handleIconUpload(url: string) {
526     this.state.siteForm.icon = url;
527     this.setState(this.state);
528   }
529
530   handleIconRemove() {
531     this.state.siteForm.icon = "";
532     this.setState(this.state);
533   }
534
535   handleBannerUpload(url: string) {
536     this.state.siteForm.banner = url;
537     this.setState(this.state);
538   }
539
540   handleBannerRemove() {
541     this.state.siteForm.banner = "";
542     this.setState(this.state);
543   }
544
545   handleDefaultPostListingTypeChange(val: ListingType) {
546     this.state.siteForm.default_post_listing_type =
547       ListingType[ListingType[val]];
548     this.setState(this.state);
549   }
550 }