1 import { Component, linkEvent } from 'inferno';
2 import { Prompt } from 'inferno-router';
3 import { Link } from 'inferno-router';
4 import { Subscription } from 'rxjs';
5 import { retryWhen, delay, take } from 'rxjs/operators';
7 PrivateMessageForm as PrivateMessageFormI,
8 EditPrivateMessageForm,
9 PrivateMessageFormParams,
11 PrivateMessageResponse,
17 WebSocketJsonResponse,
18 } from '../interfaces';
19 import { WebSocketService } from '../services';
21 capitalizeFirstLetter,
25 pictshareAvatarThumbnail,
32 import Tribute from 'tributejs/src/Tribute.js';
33 import autosize from 'autosize';
34 import { i18n } from '../i18next';
35 import { T } from 'inferno-i18next';
37 interface PrivateMessageFormProps {
38 privateMessage?: PrivateMessage; // If a pm is given, that means this is an edit
39 params?: PrivateMessageFormParams;
41 onCreate?(message: PrivateMessage): any;
42 onEdit?(message: PrivateMessage): any;
45 interface PrivateMessageFormState {
46 privateMessageForm: PrivateMessageFormI;
50 showDisclaimer: boolean;
53 export class PrivateMessageForm extends Component<
54 PrivateMessageFormProps,
55 PrivateMessageFormState
57 private id = `message-form-${randomStr()}`;
58 private tribute: Tribute;
59 private subscription: Subscription;
60 private emptyState: PrivateMessageFormState = {
68 showDisclaimer: false,
71 constructor(props: any, context: any) {
72 super(props, context);
74 this.tribute = setupTribute();
75 this.state = this.emptyState;
77 if (this.props.privateMessage) {
78 this.state.privateMessageForm = {
79 content: this.props.privateMessage.content,
80 recipient_id: this.props.privateMessage.recipient_id,
84 if (this.props.params) {
85 this.state.privateMessageForm.recipient_id = this.props.params.recipient_id;
86 let form: GetUserDetailsForm = {
87 user_id: this.state.privateMessageForm.recipient_id,
88 sort: SortType[SortType.New],
91 WebSocketService.Instance.getUserDetails(form);
94 this.subscription = WebSocketService.Instance.subject
95 .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
97 msg => this.parseMessage(msg),
98 err => console.error(err),
99 () => console.log('complete')
103 componentDidMount() {
104 var textarea: any = document.getElementById(this.id);
106 this.tribute.attach(textarea);
107 textarea.addEventListener('tribute-replaced', () => {
108 this.state.privateMessageForm.content = textarea.value;
109 this.setState(this.state);
110 autosize.update(textarea);
115 componentWillUnmount() {
116 this.subscription.unsubscribe();
123 when={!this.state.loading && this.state.privateMessageForm.content}
124 message={i18n.t('block_leaving')}
126 <form onSubmit={linkEvent(this, this.handlePrivateMessageSubmit)}>
127 {!this.props.privateMessage && (
128 <div class="form-group row">
129 <label class="col-sm-2 col-form-label">
130 {capitalizeFirstLetter(i18n.t('to'))}
133 {this.state.recipient && (
134 <div class="col-sm-10 form-control-plaintext">
136 className="text-body font-weight-bold"
137 to={`/u/${this.state.recipient.name}`}
139 {this.state.recipient.avatar && showAvatars() && (
143 src={pictshareAvatarThumbnail(
144 this.state.recipient.avatar
146 class="rounded-circle mr-1"
149 <span>{this.state.recipient.name}</span>
155 <div class="form-group row">
156 <label class="col-sm-2 col-form-label">{i18n.t('message')}</label>
157 <div class="col-sm-10">
160 value={this.state.privateMessageForm.content}
161 onInput={linkEvent(this, this.handleContentChange)}
162 className={`form-control ${this.state.previewMode && 'd-none'}`}
166 {this.state.previewMode && (
169 dangerouslySetInnerHTML={mdToHtml(
170 this.state.privateMessageForm.content
175 <ul class="float-right list-inline mb-1 text-muted font-weight-bold">
176 <li class="list-inline-item">
178 onClick={linkEvent(this, this.handleShowDisclaimer)}
180 data-tippy-content={i18n.t('disclaimer')}
182 <svg class={`icon icon-inline`}>
183 <use xlinkHref="#icon-alert-triangle"></use>
187 <li class="list-inline-item">
189 href={markdownHelpUrl}
192 title={i18n.t('formatting_help')}
194 <svg class="icon icon-inline">
195 <use xlinkHref="#icon-help-circle"></use>
203 {this.state.showDisclaimer && (
204 <div class="form-group row">
205 <div class="col-sm-10">
206 <div class="alert alert-danger" role="alert">
207 <T i18nKey="private_message_disclaimer">
212 href="https://about.riot.im/"
221 <div class="form-group row">
222 <div class="col-sm-10">
223 <button type="submit" class="btn btn-secondary mr-2">
224 {this.state.loading ? (
225 <svg class="icon icon-spinner spin">
226 <use xlinkHref="#icon-spinner"></use>
228 ) : this.props.privateMessage ? (
229 capitalizeFirstLetter(i18n.t('save'))
231 capitalizeFirstLetter(i18n.t('send_message'))
234 {this.state.privateMessageForm.content && (
236 className={`btn btn-secondary mr-2 ${this.state.previewMode &&
238 onClick={linkEvent(this, this.handlePreviewToggle)}
243 {this.props.privateMessage && (
246 class="btn btn-secondary"
247 onClick={linkEvent(this, this.handleCancel)}
259 handlePrivateMessageSubmit(i: PrivateMessageForm, event: any) {
260 event.preventDefault();
261 if (i.props.privateMessage) {
262 let editForm: EditPrivateMessageForm = {
263 edit_id: i.props.privateMessage.id,
264 content: i.state.privateMessageForm.content,
266 WebSocketService.Instance.editPrivateMessage(editForm);
268 WebSocketService.Instance.createPrivateMessage(
269 i.state.privateMessageForm
272 i.state.loading = true;
276 handleRecipientChange(i: PrivateMessageForm, event: any) {
277 i.state.recipient = event.target.value;
281 handleContentChange(i: PrivateMessageForm, event: any) {
282 i.state.privateMessageForm.content = event.target.value;
286 handleCancel(i: PrivateMessageForm) {
290 handlePreviewToggle(i: PrivateMessageForm, event: any) {
291 event.preventDefault();
292 i.state.previewMode = !i.state.previewMode;
296 handleShowDisclaimer(i: PrivateMessageForm) {
297 i.state.showDisclaimer = !i.state.showDisclaimer;
301 parseMessage(msg: WebSocketJsonResponse) {
302 let res = wsJsonToRes(msg);
304 toast(i18n.t(msg.error), 'danger');
305 this.state.loading = false;
306 this.setState(this.state);
308 } else if (res.op == UserOperation.EditPrivateMessage) {
309 let data = res.data as PrivateMessageResponse;
310 this.state.loading = false;
311 this.props.onEdit(data.message);
312 } else if (res.op == UserOperation.GetUserDetails) {
313 let data = res.data as UserDetailsResponse;
314 this.state.recipient = data.user;
315 this.state.privateMessageForm.recipient_id = data.user.id;
316 this.setState(this.state);
317 } else if (res.op == UserOperation.CreatePrivateMessage) {
318 let data = res.data as PrivateMessageResponse;
319 this.state.loading = false;
320 this.props.onCreate(data.message);
321 this.setState(this.state);