]> Untitled Git - lemmy-ui.git/blob - src/shared/components/private_message/private-message-form.tsx
add instance url
[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 {
4   CreatePrivateMessage,
5   EditPrivateMessage,
6   Person,
7   PrivateMessageResponse,
8   PrivateMessageView,
9   UserOperation,
10   wsJsonToRes,
11   wsUserOp,
12 } from "lemmy-js-client";
13 import { Subscription } from "rxjs";
14 import { i18n } from "../../i18next";
15 import { WebSocketService } from "../../services";
16 import {
17   capitalizeFirstLetter,
18   isBrowser,
19   myAuth,
20   relTags,
21   setupTippy,
22   toast,
23   wsClient,
24   wsSubscribe,
25 } from "../../utils";
26 import { Icon, Spinner } from "../common/icon";
27 import { MarkdownTextArea } from "../common/markdown-textarea";
28 import NavigationPrompt from "../common/navigation-prompt";
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         <NavigationPrompt when={!this.state.loading && !!this.state.content} />
95         <form onSubmit={linkEvent(this, this.handlePrivateMessageSubmit)}>
96           {!this.props.privateMessageView && (
97             <div className="form-group row">
98               <label className="col-sm-2 col-form-label">
99                 {capitalizeFirstLetter(i18n.t("to"))}
100               </label>
101
102               <div className="col-sm-10 form-control-plaintext">
103                 <PersonListing person={this.props.recipient} />
104               </div>
105             </div>
106           )}
107           <div className="form-group row">
108             <label className="col-sm-2 col-form-label">
109               {i18n.t("message")}
110               <button
111                 className="btn btn-link text-warning d-inline-block"
112                 onClick={linkEvent(this, this.handleShowDisclaimer)}
113                 data-tippy-content={i18n.t("private_message_disclaimer")}
114                 aria-label={i18n.t("private_message_disclaimer")}
115               >
116                 <Icon icon="alert-triangle" classes="icon-inline" />
117               </button>
118             </label>
119             <div className="col-sm-10">
120               <MarkdownTextArea
121                 initialContent={this.state.content}
122                 onContentChange={this.handleContentChange}
123                 allLanguages={[]}
124                 siteLanguages={[]}
125               />
126             </div>
127           </div>
128
129           {this.state.showDisclaimer && (
130             <div className="form-group row">
131               <div className="offset-sm-2 col-sm-10">
132                 <div className="alert alert-danger" role="alert">
133                   <T i18nKey="private_message_disclaimer">
134                     #
135                     <a
136                       className="alert-link"
137                       rel={relTags}
138                       href="https://element.io/get-started"
139                     >
140                       #
141                     </a>
142                   </T>
143                 </div>
144               </div>
145             </div>
146           )}
147           <div className="form-group row">
148             <div className="offset-sm-2 col-sm-10">
149               <button
150                 type="submit"
151                 className="btn btn-secondary mr-2"
152                 disabled={this.state.loading}
153               >
154                 {this.state.loading ? (
155                   <Spinner />
156                 ) : this.props.privateMessageView ? (
157                   capitalizeFirstLetter(i18n.t("save"))
158                 ) : (
159                   capitalizeFirstLetter(i18n.t("send_message"))
160                 )}
161               </button>
162               {this.props.privateMessageView && (
163                 <button
164                   type="button"
165                   className="btn btn-secondary"
166                   onClick={linkEvent(this, this.handleCancel)}
167                 >
168                   {i18n.t("cancel")}
169                 </button>
170               )}
171               <ul className="d-inline-block float-right list-inline mb-1 text-muted font-weight-bold">
172                 <li className="list-inline-item"></li>
173               </ul>
174             </div>
175           </div>
176         </form>
177       </div>
178     );
179   }
180
181   handlePrivateMessageSubmit(i: PrivateMessageForm, event: any) {
182     event.preventDefault();
183     const pm = i.props.privateMessageView;
184     const auth = myAuth();
185     const content = i.state.content;
186     if (auth && content) {
187       if (pm) {
188         const form: EditPrivateMessage = {
189           private_message_id: pm.private_message.id,
190           content,
191           auth,
192         };
193         WebSocketService.Instance.send(wsClient.editPrivateMessage(form));
194       } else {
195         const form: CreatePrivateMessage = {
196           content,
197           recipient_id: i.props.recipient.id,
198           auth,
199         };
200         WebSocketService.Instance.send(wsClient.createPrivateMessage(form));
201       }
202       i.setState({ loading: true });
203     }
204   }
205
206   handleContentChange(val: string) {
207     this.setState({ content: val });
208   }
209
210   handleCancel(i: PrivateMessageForm) {
211     i.props.onCancel?.();
212   }
213
214   handlePreviewToggle(i: PrivateMessageForm, event: any) {
215     event.preventDefault();
216     i.setState({ previewMode: !i.state.previewMode });
217   }
218
219   handleShowDisclaimer(i: PrivateMessageForm) {
220     i.setState({ showDisclaimer: !i.state.showDisclaimer });
221   }
222
223   parseMessage(msg: any) {
224     const op = wsUserOp(msg);
225     console.log(msg);
226     if (msg.error) {
227       toast(i18n.t(msg.error), "danger");
228       this.setState({ loading: false });
229       return;
230     } else if (
231       op == UserOperation.EditPrivateMessage ||
232       op == UserOperation.DeletePrivateMessage ||
233       op == UserOperation.MarkPrivateMessageAsRead
234     ) {
235       const data = wsJsonToRes<PrivateMessageResponse>(msg);
236       this.setState({ loading: false });
237       this.props.onEdit?.(data.private_message_view);
238     } else if (op == UserOperation.CreatePrivateMessage) {
239       const data = wsJsonToRes<PrivateMessageResponse>(msg);
240       this.props.onCreate?.(data.private_message_view);
241     }
242   }
243 }