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,
30 import { UserListing } from './user-listing';
31 import Tribute from 'tributejs/src/Tribute.js';
32 import autosize from 'autosize';
33 import { i18n } from '../i18next';
34 import { T } from 'inferno-i18next';
36 interface PrivateMessageFormProps {
37 privateMessage?: PrivateMessage; // If a pm is given, that means this is an edit
38 params?: PrivateMessageFormParams;
40 onCreate?(message: PrivateMessage): any;
41 onEdit?(message: PrivateMessage): any;
44 interface PrivateMessageFormState {
45 privateMessageForm: PrivateMessageFormI;
49 showDisclaimer: boolean;
52 export class PrivateMessageForm extends Component<
53 PrivateMessageFormProps,
54 PrivateMessageFormState
56 private id = `message-form-${randomStr()}`;
57 private tribute: Tribute;
58 private subscription: Subscription;
59 private emptyState: PrivateMessageFormState = {
67 showDisclaimer: false,
70 constructor(props: any, context: any) {
71 super(props, context);
73 this.tribute = setupTribute();
74 this.state = this.emptyState;
76 if (this.props.privateMessage) {
77 this.state.privateMessageForm = {
78 content: this.props.privateMessage.content,
79 recipient_id: this.props.privateMessage.recipient_id,
83 if (this.props.params) {
84 this.state.privateMessageForm.recipient_id = this.props.params.recipient_id;
85 let form: GetUserDetailsForm = {
86 user_id: this.state.privateMessageForm.recipient_id,
87 sort: SortType[SortType.New],
90 WebSocketService.Instance.getUserDetails(form);
93 this.subscription = WebSocketService.Instance.subject
94 .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
96 msg => this.parseMessage(msg),
97 err => console.error(err),
98 () => console.log('complete')
102 componentDidMount() {
103 var textarea: any = document.getElementById(this.id);
105 this.tribute.attach(textarea);
106 textarea.addEventListener('tribute-replaced', () => {
107 this.state.privateMessageForm.content = textarea.value;
108 this.setState(this.state);
109 autosize.update(textarea);
114 componentWillUnmount() {
115 this.subscription.unsubscribe();
122 when={!this.state.loading && this.state.privateMessageForm.content}
123 message={i18n.t('block_leaving')}
125 <form onSubmit={linkEvent(this, this.handlePrivateMessageSubmit)}>
126 {!this.props.privateMessage && (
127 <div class="form-group row">
128 <label class="col-sm-2 col-form-label">
129 {capitalizeFirstLetter(i18n.t('to'))}
132 {this.state.recipient && (
133 <div class="col-sm-10 form-control-plaintext">
136 name: this.state.recipient.name,
137 avatar: this.state.recipient.avatar,
138 id: this.state.recipient.id,
139 local: this.state.recipient.local,
140 actor_id: this.state.recipient.actor_id,
147 <div class="form-group row">
148 <label class="col-sm-2 col-form-label">{i18n.t('message')}</label>
149 <div class="col-sm-10">
152 value={this.state.privateMessageForm.content}
153 onInput={linkEvent(this, this.handleContentChange)}
154 className={`form-control ${this.state.previewMode && 'd-none'}`}
158 {this.state.previewMode && (
160 className="card card-body md-div"
161 dangerouslySetInnerHTML={mdToHtml(
162 this.state.privateMessageForm.content
169 {this.state.showDisclaimer && (
170 <div class="form-group row">
171 <div class="offset-sm-2 col-sm-10">
172 <div class="alert alert-danger" role="alert">
173 <T i18nKey="private_message_disclaimer">
179 href="https://about.riot.im/"
188 <div class="form-group row">
189 <div class="offset-sm-2 col-sm-10">
192 class="btn btn-secondary mr-2"
193 disabled={this.state.loading}
195 {this.state.loading ? (
196 <svg class="icon icon-spinner spin">
197 <use xlinkHref="#icon-spinner"></use>
199 ) : this.props.privateMessage ? (
200 capitalizeFirstLetter(i18n.t('save'))
202 capitalizeFirstLetter(i18n.t('send_message'))
205 {this.state.privateMessageForm.content && (
207 className={`btn btn-secondary mr-2 ${
208 this.state.previewMode && 'active'
210 onClick={linkEvent(this, this.handlePreviewToggle)}
215 {this.props.privateMessage && (
218 class="btn btn-secondary"
219 onClick={linkEvent(this, this.handleCancel)}
224 <ul class="d-inline-block float-right list-inline mb-1 text-muted font-weight-bold">
225 <li class="list-inline-item">
227 onClick={linkEvent(this, this.handleShowDisclaimer)}
229 data-tippy-content={i18n.t('disclaimer')}
231 <svg class={`icon icon-inline`}>
232 <use xlinkHref="#icon-alert-triangle"></use>
236 <li class="list-inline-item">
238 href={markdownHelpUrl}
242 title={i18n.t('formatting_help')}
244 <svg class="icon icon-inline">
245 <use xlinkHref="#icon-help-circle"></use>
257 handlePrivateMessageSubmit(i: PrivateMessageForm, event: any) {
258 event.preventDefault();
259 if (i.props.privateMessage) {
260 let editForm: EditPrivateMessageForm = {
261 edit_id: i.props.privateMessage.id,
262 content: i.state.privateMessageForm.content,
264 WebSocketService.Instance.editPrivateMessage(editForm);
266 WebSocketService.Instance.createPrivateMessage(
267 i.state.privateMessageForm
270 i.state.loading = true;
274 handleRecipientChange(i: PrivateMessageForm, event: any) {
275 i.state.recipient = event.target.value;
279 handleContentChange(i: PrivateMessageForm, event: any) {
280 i.state.privateMessageForm.content = event.target.value;
284 handleCancel(i: PrivateMessageForm) {
288 handlePreviewToggle(i: PrivateMessageForm, event: any) {
289 event.preventDefault();
290 i.state.previewMode = !i.state.previewMode;
294 handleShowDisclaimer(i: PrivateMessageForm) {
295 i.state.showDisclaimer = !i.state.showDisclaimer;
299 parseMessage(msg: WebSocketJsonResponse) {
300 let res = wsJsonToRes(msg);
302 toast(i18n.t(msg.error), 'danger');
303 this.state.loading = false;
304 this.setState(this.state);
306 } else if (res.op == UserOperation.EditPrivateMessage) {
307 let data = res.data as PrivateMessageResponse;
308 this.state.loading = false;
309 this.props.onEdit(data.message);
310 } else if (res.op == UserOperation.GetUserDetails) {
311 let data = res.data as UserDetailsResponse;
312 this.state.recipient = data.user;
313 this.state.privateMessageForm.recipient_id = data.user.id;
314 this.setState(this.state);
315 } else if (res.op == UserOperation.CreatePrivateMessage) {
316 let data = res.data as PrivateMessageResponse;
317 this.state.loading = false;
318 this.props.onCreate(data.message);
319 this.setState(this.state);