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