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