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 componentDidUpdate() {
114 if (!this.state.loading && this.state.privateMessageForm.content) {
115 window.onbeforeunload = () => true;
117 window.onbeforeunload = undefined;
121 componentWillUnmount() {
122 this.subscription.unsubscribe();
123 window.onbeforeunload = null;
130 when={!this.state.loading && this.state.privateMessageForm.content}
131 message={i18n.t('block_leaving')}
133 <form onSubmit={linkEvent(this, this.handlePrivateMessageSubmit)}>
134 {!this.props.privateMessage && (
135 <div class="form-group row">
136 <label class="col-sm-2 col-form-label">
137 {capitalizeFirstLetter(i18n.t('to'))}
140 {this.state.recipient && (
141 <div class="col-sm-10 form-control-plaintext">
144 name: this.state.recipient.name,
145 avatar: this.state.recipient.avatar,
146 id: this.state.recipient.id,
147 local: this.state.recipient.local,
148 actor_id: this.state.recipient.actor_id,
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 && (
168 className="card card-body md-div"
169 dangerouslySetInnerHTML={mdToHtml(
170 this.state.privateMessageForm.content
177 {this.state.showDisclaimer && (
178 <div class="form-group row">
179 <div class="offset-sm-2 col-sm-10">
180 <div class="alert alert-danger" role="alert">
181 <T i18nKey="private_message_disclaimer">
187 href="https://about.riot.im/"
196 <div class="form-group row">
197 <div class="offset-sm-2 col-sm-10">
200 class="btn btn-secondary mr-2"
201 disabled={this.state.loading}
203 {this.state.loading ? (
204 <svg class="icon icon-spinner spin">
205 <use xlinkHref="#icon-spinner"></use>
207 ) : this.props.privateMessage ? (
208 capitalizeFirstLetter(i18n.t('save'))
210 capitalizeFirstLetter(i18n.t('send_message'))
213 {this.state.privateMessageForm.content && (
215 className={`btn btn-secondary mr-2 ${
216 this.state.previewMode && 'active'
218 onClick={linkEvent(this, this.handlePreviewToggle)}
223 {this.props.privateMessage && (
226 class="btn btn-secondary"
227 onClick={linkEvent(this, this.handleCancel)}
232 <ul class="d-inline-block float-right list-inline mb-1 text-muted font-weight-bold">
233 <li class="list-inline-item">
235 onClick={linkEvent(this, this.handleShowDisclaimer)}
237 data-tippy-content={i18n.t('disclaimer')}
239 <svg class={`icon icon-inline`}>
240 <use xlinkHref="#icon-alert-triangle"></use>
244 <li class="list-inline-item">
246 href={markdownHelpUrl}
250 title={i18n.t('formatting_help')}
252 <svg class="icon icon-inline">
253 <use xlinkHref="#icon-help-circle"></use>
265 handlePrivateMessageSubmit(i: PrivateMessageForm, event: any) {
266 event.preventDefault();
267 if (i.props.privateMessage) {
268 let editForm: EditPrivateMessageForm = {
269 edit_id: i.props.privateMessage.id,
270 content: i.state.privateMessageForm.content,
272 WebSocketService.Instance.editPrivateMessage(editForm);
274 WebSocketService.Instance.createPrivateMessage(
275 i.state.privateMessageForm
278 i.state.loading = true;
282 handleRecipientChange(i: PrivateMessageForm, event: any) {
283 i.state.recipient = event.target.value;
287 handleContentChange(i: PrivateMessageForm, event: any) {
288 i.state.privateMessageForm.content = event.target.value;
292 handleCancel(i: PrivateMessageForm) {
296 handlePreviewToggle(i: PrivateMessageForm, event: any) {
297 event.preventDefault();
298 i.state.previewMode = !i.state.previewMode;
302 handleShowDisclaimer(i: PrivateMessageForm) {
303 i.state.showDisclaimer = !i.state.showDisclaimer;
307 parseMessage(msg: WebSocketJsonResponse) {
308 let res = wsJsonToRes(msg);
310 toast(i18n.t(msg.error), 'danger');
311 this.state.loading = false;
312 this.setState(this.state);
314 } else if (res.op == UserOperation.EditPrivateMessage) {
315 let data = res.data as PrivateMessageResponse;
316 this.state.loading = false;
317 this.props.onEdit(data.message);
318 } else if (res.op == UserOperation.GetUserDetails) {
319 let data = res.data as UserDetailsResponse;
320 this.state.recipient = data.user;
321 this.state.privateMessageForm.recipient_id = data.user.id;
322 this.setState(this.state);
323 } else if (res.op == UserOperation.CreatePrivateMessage) {
324 let data = res.data as PrivateMessageResponse;
325 this.state.loading = false;
326 this.props.onCreate(data.message);
327 this.setState(this.state);