]> Untitled Git - lemmy-ui.git/blob - src/shared/components/home/site-form.tsx
Add support for filtering mod logs
[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                   value={toUndefined(this.state.siteForm.default_theme)}
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(this.state.siteForm.hide_modlog_mod_names)}
450                   onChange={linkEvent(this, this.handleSiteHideModlogModNames)}
451                 />
452                 <label
453                   class="form-check-label"
454                   htmlFor="create-site-hide-modlog-mod-names"
455                 >
456                   {i18n.t("hide_modlog_mod_names")}
457                 </label>
458               </div>
459             </div>
460           </div>
461           <div class="form-group row">
462             <div class="col-12">
463               <button
464                 type="submit"
465                 class="btn btn-secondary mr-2"
466                 disabled={this.state.loading}
467               >
468                 {this.state.loading ? (
469                   <Spinner />
470                 ) : this.props.site.isSome() ? (
471                   capitalizeFirstLetter(i18n.t("save"))
472                 ) : (
473                   capitalizeFirstLetter(i18n.t("create"))
474                 )}
475               </button>
476               {this.props.site.isSome() && (
477                 <button
478                   type="button"
479                   class="btn btn-secondary"
480                   onClick={linkEvent(this, this.handleCancel)}
481                 >
482                   {i18n.t("cancel")}
483                 </button>
484               )}
485             </div>
486           </div>
487         </form>
488       </>
489     );
490   }
491
492   handleCreateSiteSubmit(i: SiteForm, event: any) {
493     event.preventDefault();
494     i.state.loading = true;
495     i.state.siteForm.auth = auth().unwrap();
496
497     if (i.props.site.isSome()) {
498       WebSocketService.Instance.send(wsClient.editSite(i.state.siteForm));
499       i.props.onEdit();
500     } else {
501       let sForm = i.state.siteForm;
502       let form = new CreateSite({
503         name: sForm.name.unwrapOr("My site"),
504         sidebar: sForm.sidebar,
505         description: sForm.description,
506         icon: sForm.icon,
507         banner: sForm.banner,
508         community_creation_admin_only: sForm.community_creation_admin_only,
509         enable_nsfw: sForm.enable_nsfw,
510         enable_downvotes: sForm.enable_downvotes,
511         require_application: sForm.require_application,
512         application_question: sForm.application_question,
513         open_registration: sForm.open_registration,
514         require_email_verification: sForm.require_email_verification,
515         private_instance: sForm.private_instance,
516         default_theme: sForm.default_theme,
517         default_post_listing_type: sForm.default_post_listing_type,
518         auth: auth().unwrap(),
519         hide_modlog_mod_names: sForm.hide_modlog_mod_names
520       });
521       WebSocketService.Instance.send(wsClient.createSite(form));
522     }
523     i.setState(i.state);
524   }
525
526   handleSiteNameChange(i: SiteForm, event: any) {
527     i.state.siteForm.name = Some(event.target.value);
528     i.setState(i.state);
529   }
530
531   handleSiteSidebarChange(val: string) {
532     this.state.siteForm.sidebar = Some(val);
533     this.setState(this.state);
534   }
535
536   handleSiteLegalInfoChange(val: string) {
537     this.state.siteForm.legal_information = Some(val);
538     this.setState(this.state);
539   }
540
541   handleSiteApplicationQuestionChange(val: string) {
542     this.state.siteForm.application_question = Some(val);
543     this.setState(this.state);
544   }
545
546   handleSiteDescChange(i: SiteForm, event: any) {
547     i.state.siteForm.description = Some(event.target.value);
548     i.setState(i.state);
549   }
550
551   handleSiteEnableNsfwChange(i: SiteForm, event: any) {
552     i.state.siteForm.enable_nsfw = Some(event.target.checked);
553     i.setState(i.state);
554   }
555
556   handleSiteOpenRegistrationChange(i: SiteForm, event: any) {
557     i.state.siteForm.open_registration = Some(event.target.checked);
558     i.setState(i.state);
559   }
560
561   handleSiteCommunityCreationAdminOnly(i: SiteForm, event: any) {
562     i.state.siteForm.community_creation_admin_only = Some(event.target.checked);
563     i.setState(i.state);
564   }
565
566   handleSiteEnableDownvotesChange(i: SiteForm, event: any) {
567     i.state.siteForm.enable_downvotes = Some(event.target.checked);
568     i.setState(i.state);
569   }
570
571   handleSiteRequireApplication(i: SiteForm, event: any) {
572     i.state.siteForm.require_application = Some(event.target.checked);
573     i.setState(i.state);
574   }
575
576   handleSiteRequireEmailVerification(i: SiteForm, event: any) {
577     i.state.siteForm.require_email_verification = Some(event.target.checked);
578     i.setState(i.state);
579   }
580
581   handleSitePrivateInstance(i: SiteForm, event: any) {
582     i.state.siteForm.private_instance = Some(event.target.checked);
583     i.setState(i.state);
584   }
585
586   handleSiteHideModlogModNames(i: SiteForm, event: any) {
587     i.state.siteForm.hide_modlog_mod_names = Some(event.target.checked);
588     i.setState(i.state);
589   }
590
591   handleSiteDefaultTheme(i: SiteForm, event: any) {
592     i.state.siteForm.default_theme = Some(event.target.value);
593     i.setState(i.state);
594   }
595
596   handleCancel(i: SiteForm) {
597     i.props.onCancel();
598   }
599
600   handleIconUpload(url: string) {
601     this.state.siteForm.icon = Some(url);
602     this.setState(this.state);
603   }
604
605   handleIconRemove() {
606     this.state.siteForm.icon = Some("");
607     this.setState(this.state);
608   }
609
610   handleBannerUpload(url: string) {
611     this.state.siteForm.banner = Some(url);
612     this.setState(this.state);
613   }
614
615   handleBannerRemove() {
616     this.state.siteForm.banner = Some("");
617     this.setState(this.state);
618   }
619
620   handleDefaultPostListingTypeChange(val: ListingType) {
621     this.state.siteForm.default_post_listing_type = Some(
622       ListingType[ListingType[val]]
623     );
624     this.setState(this.state);
625   }
626 }