]> Untitled Git - lemmy-ui.git/blob - src/shared/components/community/community-form.tsx
45cd5ec4f81a73a3206b3fc3dc4bc9827581e512
[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 class="col-12 col-form-label" htmlFor="community-name">
125                 {i18n.t("name")}
126                 <span
127                   class="pointer unselectable ml-2 text-muted"
128                   data-tippy-content={i18n.t("name_explain")}
129                 >
130                   <Icon icon="help-circle" classes="icon-inline" />
131                 </span>
132               </label>
133               <div class="col-12">
134                 <input
135                   type="text"
136                   id="community-name"
137                   class="form-control"
138                   value={this.state.communityForm.name}
139                   onInput={linkEvent(this, this.handleCommunityNameChange)}
140                   required
141                   minLength={3}
142                   pattern="[a-z0-9_]+"
143                   title={i18n.t("community_reqs")}
144                 />
145               </div>
146             </div>
147           )}
148           <div class="form-group row">
149             <label class="col-12 col-form-label" htmlFor="community-title">
150               {i18n.t("display_name")}
151               <span
152                 class="pointer unselectable ml-2 text-muted"
153                 data-tippy-content={i18n.t("display_name_explain")}
154               >
155                 <Icon icon="help-circle" classes="icon-inline" />
156               </span>
157             </label>
158             <div class="col-12">
159               <input
160                 type="text"
161                 id="community-title"
162                 value={this.state.communityForm.title}
163                 onInput={linkEvent(this, this.handleCommunityTitleChange)}
164                 class="form-control"
165                 required
166                 minLength={3}
167                 maxLength={100}
168               />
169             </div>
170           </div>
171           <div class="form-group">
172             <label>{i18n.t("icon")}</label>
173             <ImageUploadForm
174               uploadTitle={i18n.t("upload_icon")}
175               imageSrc={this.state.communityForm.icon}
176               onUpload={this.handleIconUpload}
177               onRemove={this.handleIconRemove}
178               rounded
179             />
180           </div>
181           <div class="form-group">
182             <label>{i18n.t("banner")}</label>
183             <ImageUploadForm
184               uploadTitle={i18n.t("upload_banner")}
185               imageSrc={this.state.communityForm.banner}
186               onUpload={this.handleBannerUpload}
187               onRemove={this.handleBannerRemove}
188             />
189           </div>
190           <div class="form-group row">
191             <label class="col-12 col-form-label" htmlFor={this.id}>
192               {i18n.t("sidebar")}
193             </label>
194             <div class="col-12">
195               <MarkdownTextArea
196                 initialContent={this.state.communityForm.description}
197                 onContentChange={this.handleCommunityDescriptionChange}
198               />
199             </div>
200           </div>
201
202           {this.props.enableNsfw && (
203             <div class="form-group row">
204               <div class="col-12">
205                 <div class="form-check">
206                   <input
207                     class="form-check-input"
208                     id="community-nsfw"
209                     type="checkbox"
210                     checked={this.state.communityForm.nsfw}
211                     onChange={linkEvent(this, this.handleCommunityNsfwChange)}
212                   />
213                   <label class="form-check-label" htmlFor="community-nsfw">
214                     {i18n.t("nsfw")}
215                   </label>
216                 </div>
217               </div>
218             </div>
219           )}
220           <div class="form-group row">
221             <div class="col-12">
222               <button
223                 type="submit"
224                 class="btn btn-secondary mr-2"
225                 disabled={this.state.loading}
226               >
227                 {this.state.loading ? (
228                   <Spinner />
229                 ) : this.props.community_view ? (
230                   capitalizeFirstLetter(i18n.t("save"))
231                 ) : (
232                   capitalizeFirstLetter(i18n.t("create"))
233                 )}
234               </button>
235               {this.props.community_view && (
236                 <button
237                   type="button"
238                   class="btn btn-secondary"
239                   onClick={linkEvent(this, this.handleCancel)}
240                 >
241                   {i18n.t("cancel")}
242                 </button>
243               )}
244             </div>
245           </div>
246         </form>
247       </>
248     );
249   }
250
251   handleCreateCommunitySubmit(i: CommunityForm, event: any) {
252     event.preventDefault();
253     i.state.loading = true;
254     if (i.props.community_view) {
255       let form: EditCommunity = {
256         ...i.state.communityForm,
257         community_id: i.props.community_view.community.id,
258       };
259       WebSocketService.Instance.send(wsClient.editCommunity(form));
260     } else {
261       WebSocketService.Instance.send(
262         wsClient.createCommunity(i.state.communityForm)
263       );
264     }
265     i.setState(i.state);
266   }
267
268   handleCommunityNameChange(i: CommunityForm, event: any) {
269     i.state.communityForm.name = event.target.value;
270     i.setState(i.state);
271   }
272
273   handleCommunityTitleChange(i: CommunityForm, event: any) {
274     i.state.communityForm.title = event.target.value;
275     i.setState(i.state);
276   }
277
278   handleCommunityDescriptionChange(val: string) {
279     this.state.communityForm.description = val;
280     this.setState(this.state);
281   }
282
283   handleCommunityNsfwChange(i: CommunityForm, event: any) {
284     i.state.communityForm.nsfw = event.target.checked;
285     i.setState(i.state);
286   }
287
288   handleCancel(i: CommunityForm) {
289     i.props.onCancel();
290   }
291
292   handleIconUpload(url: string) {
293     this.state.communityForm.icon = url;
294     this.setState(this.state);
295   }
296
297   handleIconRemove() {
298     this.state.communityForm.icon = "";
299     this.setState(this.state);
300   }
301
302   handleBannerUpload(url: string) {
303     this.state.communityForm.banner = url;
304     this.setState(this.state);
305   }
306
307   handleBannerRemove() {
308     this.state.communityForm.banner = "";
309     this.setState(this.state);
310   }
311
312   parseMessage(msg: any) {
313     let op = wsUserOp(msg);
314     console.log(msg);
315     if (msg.error) {
316       toast(i18n.t(msg.error), "danger");
317       this.state.loading = false;
318       this.setState(this.state);
319       return;
320     } else if (op == UserOperation.CreateCommunity) {
321       let data = wsJsonToRes<CommunityResponse>(msg).data;
322       this.state.loading = false;
323       this.props.onCreate(data.community_view);
324
325       // Update myUserInfo
326       let community = data.community_view.community;
327       let person = UserService.Instance.myUserInfo.local_user_view.person;
328       UserService.Instance.myUserInfo.follows.push({
329         community,
330         follower: person,
331       });
332       UserService.Instance.myUserInfo.moderates.push({
333         community,
334         moderator: person,
335       });
336     } else if (op == UserOperation.EditCommunity) {
337       let data = wsJsonToRes<CommunityResponse>(msg).data;
338       this.state.loading = false;
339       this.props.onEdit(data.community_view);
340       let community = data.community_view.community;
341
342       let followFound = UserService.Instance.myUserInfo.follows.findIndex(
343         f => f.community.id == community.id
344       );
345       if (followFound) {
346         UserService.Instance.myUserInfo.follows[followFound].community =
347           community;
348       }
349
350       let moderatesFound = UserService.Instance.myUserInfo.moderates.findIndex(
351         f => f.community.id == community.id
352       );
353       if (moderatesFound) {
354         UserService.Instance.myUserInfo.moderates[moderatesFound].community =
355           community;
356       }
357     }
358   }
359 }