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