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';
6 PrivateMessageForm as PrivateMessageFormI,
7 EditPrivateMessageForm,
8 PrivateMessageFormParams,
10 PrivateMessageResponse,
16 WebSocketJsonResponse,
17 } from '../interfaces';
18 import { WebSocketService } from '../services';
20 capitalizeFirstLetter,
29 import { UserListing } from './user-listing';
30 import Tribute from 'tributejs/src/Tribute.js';
31 import autosize from 'autosize';
32 import { i18n } from '../i18next';
33 import { T } from 'inferno-i18next';
35 interface PrivateMessageFormProps {
36 privateMessage?: PrivateMessage; // If a pm is given, that means this is an edit
37 params?: PrivateMessageFormParams;
39 onCreate?(message: PrivateMessage): any;
40 onEdit?(message: PrivateMessage): any;
43 interface PrivateMessageFormState {
44 privateMessageForm: PrivateMessageFormI;
48 showDisclaimer: boolean;
51 export class PrivateMessageForm extends Component<
52 PrivateMessageFormProps,
53 PrivateMessageFormState
55 private id = `message-form-${randomStr()}`;
56 private tribute: Tribute;
57 private subscription: Subscription;
58 private emptyState: PrivateMessageFormState = {
66 showDisclaimer: false,
69 constructor(props: any, context: any) {
70 super(props, context);
72 this.tribute = setupTribute();
73 this.state = this.emptyState;
75 if (this.props.privateMessage) {
76 this.state.privateMessageForm = {
77 content: this.props.privateMessage.content,
78 recipient_id: this.props.privateMessage.recipient_id,
82 if (this.props.params) {
83 this.state.privateMessageForm.recipient_id = this.props.params.recipient_id;
84 let form: GetUserDetailsForm = {
85 user_id: this.state.privateMessageForm.recipient_id,
86 sort: SortType[SortType.New],
89 WebSocketService.Instance.getUserDetails(form);
92 this.subscription = WebSocketService.Instance.subject
93 .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
95 msg => this.parseMessage(msg),
96 err => console.error(err),
97 () => console.log('complete')
101 componentDidMount() {
102 var textarea: any = document.getElementById(this.id);
104 this.tribute.attach(textarea);
105 textarea.addEventListener('tribute-replaced', () => {
106 this.state.privateMessageForm.content = textarea.value;
107 this.setState(this.state);
108 autosize.update(textarea);
113 componentWillUnmount() {
114 this.subscription.unsubscribe();
121 when={!this.state.loading && this.state.privateMessageForm.content}
122 message={i18n.t('block_leaving')}
124 <form onSubmit={linkEvent(this, this.handlePrivateMessageSubmit)}>
125 {!this.props.privateMessage && (
126 <div class="form-group row">
127 <label class="col-sm-2 col-form-label">
128 {capitalizeFirstLetter(i18n.t('to'))}
131 {this.state.recipient && (
132 <div class="col-sm-10 form-control-plaintext">
135 name: this.state.recipient.name,
136 avatar: this.state.recipient.avatar,
137 id: this.state.recipient.id,
138 local: this.state.recipient.local,
139 actor_id: this.state.recipient.actor_id,
146 <div class="form-group row">
147 <label class="col-sm-2 col-form-label">{i18n.t('message')}</label>
148 <div class="col-sm-10">
151 value={this.state.privateMessageForm.content}
152 onInput={linkEvent(this, this.handleContentChange)}
153 className={`form-control ${this.state.previewMode && 'd-none'}`}
157 {this.state.previewMode && (
159 className="card card-body md-div"
160 dangerouslySetInnerHTML={mdToHtml(
161 this.state.privateMessageForm.content
168 {this.state.showDisclaimer && (
169 <div class="form-group row">
170 <div class="offset-sm-2 col-sm-10">
171 <div class="alert alert-danger" role="alert">
172 <T i18nKey="private_message_disclaimer">
178 href="https://about.riot.im/"
187 <div class="form-group row">
188 <div class="offset-sm-2 col-sm-10">
191 class="btn btn-secondary mr-2"
192 disabled={this.state.loading}
194 {this.state.loading ? (
195 <svg class="icon icon-spinner spin">
196 <use xlinkHref="#icon-spinner"></use>
198 ) : this.props.privateMessage ? (
199 capitalizeFirstLetter(i18n.t('save'))
201 capitalizeFirstLetter(i18n.t('send_message'))
204 {this.state.privateMessageForm.content && (
206 className={`btn btn-secondary mr-2 ${
207 this.state.previewMode && 'active'
209 onClick={linkEvent(this, this.handlePreviewToggle)}
214 {this.props.privateMessage && (
217 class="btn btn-secondary"
218 onClick={linkEvent(this, this.handleCancel)}
223 <ul class="d-inline-block float-right list-inline mb-1 text-muted font-weight-bold">
224 <li class="list-inline-item">
226 onClick={linkEvent(this, this.handleShowDisclaimer)}
228 data-tippy-content={i18n.t('disclaimer')}
230 <svg class={`icon icon-inline`}>
231 <use xlinkHref="#icon-alert-triangle"></use>
235 <li class="list-inline-item">
237 href={markdownHelpUrl}
241 title={i18n.t('formatting_help')}
243 <svg class="icon icon-inline">
244 <use xlinkHref="#icon-help-circle"></use>
256 handlePrivateMessageSubmit(i: PrivateMessageForm, event: any) {
257 event.preventDefault();
258 if (i.props.privateMessage) {
259 let editForm: EditPrivateMessageForm = {
260 edit_id: i.props.privateMessage.id,
261 content: i.state.privateMessageForm.content,
263 WebSocketService.Instance.editPrivateMessage(editForm);
265 WebSocketService.Instance.createPrivateMessage(
266 i.state.privateMessageForm
269 i.state.loading = true;
273 handleRecipientChange(i: PrivateMessageForm, event: any) {
274 i.state.recipient = event.target.value;
278 handleContentChange(i: PrivateMessageForm, event: any) {
279 i.state.privateMessageForm.content = event.target.value;
283 handleCancel(i: PrivateMessageForm) {
287 handlePreviewToggle(i: PrivateMessageForm, event: any) {
288 event.preventDefault();
289 i.state.previewMode = !i.state.previewMode;
293 handleShowDisclaimer(i: PrivateMessageForm) {
294 i.state.showDisclaimer = !i.state.showDisclaimer;
298 parseMessage(msg: WebSocketJsonResponse) {
299 let res = wsJsonToRes(msg);
301 toast(i18n.t(msg.error), 'danger');
302 this.state.loading = false;
303 this.setState(this.state);
305 } else if (res.op == UserOperation.EditPrivateMessage) {
306 let data = res.data as PrivateMessageResponse;
307 this.state.loading = false;
308 this.props.onEdit(data.message);
309 } else if (res.op == UserOperation.GetUserDetails) {
310 let data = res.data as UserDetailsResponse;
311 this.state.recipient = data.user;
312 this.state.privateMessageForm.recipient_id = data.user.id;
313 this.setState(this.state);
314 } else if (res.op == UserOperation.CreatePrivateMessage) {
315 let data = res.data as PrivateMessageResponse;
316 this.state.loading = false;
317 this.props.onCreate(data.message);
318 this.setState(this.state);