]> Untitled Git - lemmy-ui.git/blob - src/shared/components/private-message-form.tsx
Partly functioning fuse-box, but moving te webpack now.
[lemmy-ui.git] / src / shared / components / private-message-form.tsx
1 import { Component, linkEvent } from 'inferno';
2 import { Prompt } from 'inferno-router';
3 import { Subscription } from 'rxjs';
4 import { retryWhen, delay, take } from 'rxjs/operators';
5 import {
6   PrivateMessageForm as PrivateMessageFormI,
7   EditPrivateMessageForm,
8   PrivateMessageFormParams,
9   PrivateMessage,
10   PrivateMessageResponse,
11   UserView,
12   UserOperation,
13   UserDetailsResponse,
14   GetUserDetailsForm,
15   SortType,
16   WebSocketJsonResponse,
17 } from 'lemmy-js-client';
18 import { WebSocketService } from '../services';
19 import {
20   capitalizeFirstLetter,
21   wsJsonToRes,
22   toast,
23   setupTippy,
24 } from '../utils';
25 import { UserListing } from './user-listing';
26 import { MarkdownTextArea } from './markdown-textarea';
27 import { i18n } from '../i18next';
28 import { T } from 'inferno-i18next';
29
30 interface PrivateMessageFormProps {
31   privateMessage?: PrivateMessage; // If a pm is given, that means this is an edit
32   params?: PrivateMessageFormParams;
33   onCancel?(): any;
34   onCreate?(message: PrivateMessage): any;
35   onEdit?(message: PrivateMessage): any;
36 }
37
38 interface PrivateMessageFormState {
39   privateMessageForm: PrivateMessageFormI;
40   recipient: UserView;
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: null,
55     },
56     recipient: null,
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     if (this.props.privateMessage) {
70       this.state.privateMessageForm = {
71         content: this.props.privateMessage.content,
72         recipient_id: this.props.privateMessage.recipient_id,
73       };
74     }
75
76     if (this.props.params) {
77       this.state.privateMessageForm.recipient_id = this.props.params.recipient_id;
78       let form: GetUserDetailsForm = {
79         user_id: this.state.privateMessageForm.recipient_id,
80         sort: SortType.New,
81         saved_only: false,
82       };
83       WebSocketService.Instance.getUserDetails(form);
84     }
85
86     this.subscription = WebSocketService.Instance.subject
87       .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
88       .subscribe(
89         msg => this.parseMessage(msg),
90         err => console.error(err),
91         () => console.log('complete')
92       );
93   }
94
95   componentDidMount() {
96     setupTippy();
97   }
98
99   componentDidUpdate() {
100     if (!this.state.loading && this.state.privateMessageForm.content) {
101       window.onbeforeunload = () => true;
102     } else {
103       window.onbeforeunload = undefined;
104     }
105   }
106
107   componentWillUnmount() {
108     this.subscription.unsubscribe();
109     window.onbeforeunload = null;
110   }
111
112   render() {
113     return (
114       <div>
115         <Prompt
116           when={!this.state.loading && this.state.privateMessageForm.content}
117           message={i18n.t('block_leaving')}
118         />
119         <form onSubmit={linkEvent(this, this.handlePrivateMessageSubmit)}>
120           {!this.props.privateMessage && (
121             <div class="form-group row">
122               <label class="col-sm-2 col-form-label">
123                 {capitalizeFirstLetter(i18n.t('to'))}
124               </label>
125
126               {this.state.recipient && (
127                 <div class="col-sm-10 form-control-plaintext">
128                   <UserListing
129                     user={{
130                       name: this.state.recipient.name,
131                       preferred_username: this.state.recipient
132                         .preferred_username,
133                       avatar: this.state.recipient.avatar,
134                       id: this.state.recipient.id,
135                       local: this.state.recipient.local,
136                       actor_id: this.state.recipient.actor_id,
137                     }}
138                   />
139                 </div>
140               )}
141             </div>
142           )}
143           <div class="form-group row">
144             <label class="col-sm-2 col-form-label">
145               {i18n.t('message')}
146               <span
147                 onClick={linkEvent(this, this.handleShowDisclaimer)}
148                 class="ml-2 pointer text-danger"
149                 data-tippy-content={i18n.t('disclaimer')}
150               >
151                 <svg class={`icon icon-inline`}>
152                   <use xlinkHref="#icon-alert-triangle"></use>
153                 </svg>
154               </span>
155             </label>
156             <div class="col-sm-10">
157               <MarkdownTextArea
158                 initialContent={this.state.privateMessageForm.content}
159                 onContentChange={this.handleContentChange}
160               />
161             </div>
162           </div>
163
164           {this.state.showDisclaimer && (
165             <div class="form-group row">
166               <div class="offset-sm-2 col-sm-10">
167                 <div class="alert alert-danger" role="alert">
168                   <T i18nKey="private_message_disclaimer">
169                     #
170                     <a
171                       class="alert-link"
172                       target="_blank"
173                       rel="noopener"
174                       href="https://element.io/get-started"
175                     >
176                       #
177                     </a>
178                   </T>
179                 </div>
180               </div>
181             </div>
182           )}
183           <div class="form-group row">
184             <div class="offset-sm-2 col-sm-10">
185               <button
186                 type="submit"
187                 class="btn btn-secondary mr-2"
188                 disabled={this.state.loading}
189               >
190                 {this.state.loading ? (
191                   <svg class="icon icon-spinner spin">
192                     <use xlinkHref="#icon-spinner"></use>
193                   </svg>
194                 ) : this.props.privateMessage ? (
195                   capitalizeFirstLetter(i18n.t('save'))
196                 ) : (
197                   capitalizeFirstLetter(i18n.t('send_message'))
198                 )}
199               </button>
200               {this.props.privateMessage && (
201                 <button
202                   type="button"
203                   class="btn btn-secondary"
204                   onClick={linkEvent(this, this.handleCancel)}
205                 >
206                   {i18n.t('cancel')}
207                 </button>
208               )}
209               <ul class="d-inline-block float-right list-inline mb-1 text-muted font-weight-bold">
210                 <li class="list-inline-item"></li>
211               </ul>
212             </div>
213           </div>
214         </form>
215       </div>
216     );
217   }
218
219   handlePrivateMessageSubmit(i: PrivateMessageForm, event: any) {
220     event.preventDefault();
221     if (i.props.privateMessage) {
222       let editForm: EditPrivateMessageForm = {
223         edit_id: i.props.privateMessage.id,
224         content: i.state.privateMessageForm.content,
225       };
226       WebSocketService.Instance.editPrivateMessage(editForm);
227     } else {
228       WebSocketService.Instance.createPrivateMessage(
229         i.state.privateMessageForm
230       );
231     }
232     i.state.loading = true;
233     i.setState(i.state);
234   }
235
236   handleRecipientChange(i: PrivateMessageForm, event: any) {
237     i.state.recipient = event.target.value;
238     i.setState(i.state);
239   }
240
241   handleContentChange(val: string) {
242     this.state.privateMessageForm.content = val;
243     this.setState(this.state);
244   }
245
246   handleCancel(i: PrivateMessageForm) {
247     i.props.onCancel();
248   }
249
250   handlePreviewToggle(i: PrivateMessageForm, event: any) {
251     event.preventDefault();
252     i.state.previewMode = !i.state.previewMode;
253     i.setState(i.state);
254   }
255
256   handleShowDisclaimer(i: PrivateMessageForm) {
257     i.state.showDisclaimer = !i.state.showDisclaimer;
258     i.setState(i.state);
259   }
260
261   parseMessage(msg: WebSocketJsonResponse) {
262     let res = wsJsonToRes(msg);
263     if (msg.error) {
264       toast(i18n.t(msg.error), 'danger');
265       this.state.loading = false;
266       this.setState(this.state);
267       return;
268     } else if (
269       res.op == UserOperation.EditPrivateMessage ||
270       res.op == UserOperation.DeletePrivateMessage ||
271       res.op == UserOperation.MarkPrivateMessageAsRead
272     ) {
273       let data = res.data as PrivateMessageResponse;
274       this.state.loading = false;
275       this.props.onEdit(data.message);
276     } else if (res.op == UserOperation.GetUserDetails) {
277       let data = res.data as UserDetailsResponse;
278       this.state.recipient = data.user;
279       this.state.privateMessageForm.recipient_id = data.user.id;
280       this.setState(this.state);
281     } else if (res.op == UserOperation.CreatePrivateMessage) {
282       let data = res.data as PrivateMessageResponse;
283       this.state.loading = false;
284       this.props.onCreate(data.message);
285       this.setState(this.state);
286     }
287   }
288 }