]> Untitled Git - lemmy-ui.git/blob - src/shared/components/private_message/private-message-form.tsx
Adding nofollow to links. Fixes #542 (#543)
[lemmy-ui.git] / src / shared / components / private_message / private-message-form.tsx
1 import { Component, linkEvent } from "inferno";
2 import { T } from "inferno-i18next-dess";
3 import { Prompt } from "inferno-router";
4 import {
5   CreatePrivateMessage,
6   EditPrivateMessage,
7   PersonSafe,
8   PrivateMessageResponse,
9   PrivateMessageView,
10   UserOperation,
11 } from "lemmy-js-client";
12 import { Subscription } from "rxjs";
13 import { i18n } from "../../i18next";
14 import { WebSocketService } from "../../services";
15 import {
16   authField,
17   capitalizeFirstLetter,
18   isBrowser,
19   relTags,
20   setupTippy,
21   toast,
22   wsClient,
23   wsJsonToRes,
24   wsSubscribe,
25   wsUserOp,
26 } from "../../utils";
27 import { Icon, Spinner } from "../common/icon";
28 import { MarkdownTextArea } from "../common/markdown-textarea";
29 import { PersonListing } from "../person/person-listing";
30
31 interface PrivateMessageFormProps {
32   recipient: PersonSafe;
33   privateMessage?: PrivateMessageView; // If a pm is given, that means this is an edit
34   onCancel?(): any;
35   onCreate?(message: PrivateMessageView): any;
36   onEdit?(message: PrivateMessageView): any;
37 }
38
39 interface PrivateMessageFormState {
40   privateMessageForm: CreatePrivateMessage;
41   loading: boolean;
42   previewMode: boolean;
43   showDisclaimer: boolean;
44 }
45
46 export class PrivateMessageForm extends Component<
47   PrivateMessageFormProps,
48   PrivateMessageFormState
49 > {
50   private subscription: Subscription;
51   private emptyState: PrivateMessageFormState = {
52     privateMessageForm: {
53       content: null,
54       recipient_id: this.props.recipient.id,
55       auth: authField(),
56     },
57     loading: false,
58     previewMode: false,
59     showDisclaimer: false,
60   };
61
62   constructor(props: any, context: any) {
63     super(props, context);
64
65     this.state = this.emptyState;
66
67     this.handleContentChange = this.handleContentChange.bind(this);
68
69     this.parseMessage = this.parseMessage.bind(this);
70     this.subscription = wsSubscribe(this.parseMessage);
71
72     // Its an edit
73     if (this.props.privateMessage) {
74       this.state.privateMessageForm.content =
75         this.props.privateMessage.private_message.content;
76     }
77   }
78
79   componentDidMount() {
80     setupTippy();
81   }
82
83   componentDidUpdate() {
84     if (!this.state.loading && this.state.privateMessageForm.content) {
85       window.onbeforeunload = () => true;
86     } else {
87       window.onbeforeunload = undefined;
88     }
89   }
90
91   componentWillUnmount() {
92     if (isBrowser()) {
93       this.subscription.unsubscribe();
94       window.onbeforeunload = null;
95     }
96   }
97
98   render() {
99     return (
100       <div>
101         <Prompt
102           when={!this.state.loading && this.state.privateMessageForm.content}
103           message={i18n.t("block_leaving")}
104         />
105         <form onSubmit={linkEvent(this, this.handlePrivateMessageSubmit)}>
106           {!this.props.privateMessage && (
107             <div class="form-group row">
108               <label class="col-sm-2 col-form-label">
109                 {capitalizeFirstLetter(i18n.t("to"))}
110               </label>
111
112               <div class="col-sm-10 form-control-plaintext">
113                 <PersonListing person={this.props.recipient} />
114               </div>
115             </div>
116           )}
117           <div class="form-group row">
118             <label class="col-sm-2 col-form-label">
119               {i18n.t("message")}
120               <button
121                 class="btn btn-link text-warning d-inline-block"
122                 onClick={linkEvent(this, this.handleShowDisclaimer)}
123                 data-tippy-content={i18n.t("private_message_disclaimer")}
124                 aria-label={i18n.t("private_message_disclaimer")}
125               >
126                 <Icon icon="alert-triangle" classes="icon-inline" />
127               </button>
128             </label>
129             <div class="col-sm-10">
130               <MarkdownTextArea
131                 initialContent={this.state.privateMessageForm.content}
132                 onContentChange={this.handleContentChange}
133               />
134             </div>
135           </div>
136
137           {this.state.showDisclaimer && (
138             <div class="form-group row">
139               <div class="offset-sm-2 col-sm-10">
140                 <div class="alert alert-danger" role="alert">
141                   <T i18nKey="private_message_disclaimer">
142                     #
143                     <a
144                       class="alert-link"
145                       rel={relTags}
146                       href="https://element.io/get-started"
147                     >
148                       #
149                     </a>
150                   </T>
151                 </div>
152               </div>
153             </div>
154           )}
155           <div class="form-group row">
156             <div class="offset-sm-2 col-sm-10">
157               <button
158                 type="submit"
159                 class="btn btn-secondary mr-2"
160                 disabled={this.state.loading}
161               >
162                 {this.state.loading ? (
163                   <Spinner />
164                 ) : this.props.privateMessage ? (
165                   capitalizeFirstLetter(i18n.t("save"))
166                 ) : (
167                   capitalizeFirstLetter(i18n.t("send_message"))
168                 )}
169               </button>
170               {this.props.privateMessage && (
171                 <button
172                   type="button"
173                   class="btn btn-secondary"
174                   onClick={linkEvent(this, this.handleCancel)}
175                 >
176                   {i18n.t("cancel")}
177                 </button>
178               )}
179               <ul class="d-inline-block float-right list-inline mb-1 text-muted font-weight-bold">
180                 <li class="list-inline-item"></li>
181               </ul>
182             </div>
183           </div>
184         </form>
185       </div>
186     );
187   }
188
189   handlePrivateMessageSubmit(i: PrivateMessageForm, event: any) {
190     event.preventDefault();
191     if (i.props.privateMessage) {
192       let form: EditPrivateMessage = {
193         private_message_id: i.props.privateMessage.private_message.id,
194         content: i.state.privateMessageForm.content,
195         auth: authField(),
196       };
197       WebSocketService.Instance.send(wsClient.editPrivateMessage(form));
198     } else {
199       WebSocketService.Instance.send(
200         wsClient.createPrivateMessage(i.state.privateMessageForm)
201       );
202     }
203     i.state.loading = true;
204     i.setState(i.state);
205   }
206
207   handleContentChange(val: string) {
208     this.state.privateMessageForm.content = val;
209     this.setState(this.state);
210   }
211
212   handleCancel(i: PrivateMessageForm) {
213     i.props.onCancel();
214   }
215
216   handlePreviewToggle(i: PrivateMessageForm, event: any) {
217     event.preventDefault();
218     i.state.previewMode = !i.state.previewMode;
219     i.setState(i.state);
220   }
221
222   handleShowDisclaimer(i: PrivateMessageForm) {
223     i.state.showDisclaimer = !i.state.showDisclaimer;
224     i.setState(i.state);
225   }
226
227   parseMessage(msg: any) {
228     let op = wsUserOp(msg);
229     console.log(msg);
230     if (msg.error) {
231       toast(i18n.t(msg.error), "danger");
232       this.state.loading = false;
233       this.setState(this.state);
234       return;
235     } else if (
236       op == UserOperation.EditPrivateMessage ||
237       op == UserOperation.DeletePrivateMessage ||
238       op == UserOperation.MarkPrivateMessageAsRead
239     ) {
240       let data = wsJsonToRes<PrivateMessageResponse>(msg).data;
241       this.state.loading = false;
242       this.props.onEdit(data.private_message_view);
243     } else if (op == UserOperation.CreatePrivateMessage) {
244       let data = wsJsonToRes<PrivateMessageResponse>(msg).data;
245       this.state.loading = false;
246       this.props.onCreate(data.private_message_view);
247       this.setState(this.state);
248     }
249   }
250 }