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