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