1 import { Component, linkEvent } from 'inferno';
2 import { Link } 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,
24 pictshareAvatarThumbnail,
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);
112 componentWillUnmount() {
113 this.subscription.unsubscribe();
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'))}
126 {this.state.recipient && (
127 <div class="col-sm-10 form-control-plaintext">
129 className="text-body font-weight-bold"
130 to={`/u/${this.state.recipient.name}`}
132 {this.state.recipient.avatar && showAvatars() && (
136 src={pictshareAvatarThumbnail(
137 this.state.recipient.avatar
139 class="rounded-circle mr-1"
142 <span>{this.state.recipient.name}</span>
148 <div class="form-group row">
149 <label class="col-sm-2 col-form-label">{i18n.t('message')}</label>
150 <div class="col-sm-10">
153 value={this.state.privateMessageForm.content}
154 onInput={linkEvent(this, this.handleContentChange)}
155 className={`form-control ${this.state.previewMode && 'd-none'}`}
159 {this.state.previewMode && (
162 dangerouslySetInnerHTML={mdToHtml(
163 this.state.privateMessageForm.content
168 <ul class="float-right list-inline mb-1 text-muted small font-weight-bold">
169 <li class="list-inline-item">
171 onClick={linkEvent(this, this.handleShowDisclaimer)}
174 {i18n.t('disclaimer')}
177 <li class="list-inline-item">
178 <a href={markdownHelpUrl} target="_blank" class="text-muted">
179 {i18n.t('formatting_help')}
186 {this.state.showDisclaimer && (
187 <div class="form-group row">
188 <div class="col-sm-10">
189 <div class="alert alert-danger" role="alert">
190 <T i18nKey="private_message_disclaimer">
195 href="https://about.riot.im/"
204 <div class="form-group row">
205 <div class="col-sm-10">
206 <button type="submit" class="btn btn-secondary mr-2">
207 {this.state.loading ? (
208 <svg class="icon icon-spinner spin">
209 <use xlinkHref="#icon-spinner"></use>
211 ) : this.props.privateMessage ? (
212 capitalizeFirstLetter(i18n.t('save'))
214 capitalizeFirstLetter(i18n.t('send_message'))
217 {this.state.privateMessageForm.content && (
219 className={`btn btn-secondary mr-2 ${this.state.previewMode &&
221 onClick={linkEvent(this, this.handlePreviewToggle)}
226 {this.props.privateMessage && (
229 class="btn btn-secondary"
230 onClick={linkEvent(this, this.handleCancel)}
242 handlePrivateMessageSubmit(i: PrivateMessageForm, event: any) {
243 event.preventDefault();
244 if (i.props.privateMessage) {
245 let editForm: EditPrivateMessageForm = {
246 edit_id: i.props.privateMessage.id,
247 content: i.state.privateMessageForm.content,
249 WebSocketService.Instance.editPrivateMessage(editForm);
251 WebSocketService.Instance.createPrivateMessage(
252 i.state.privateMessageForm
255 i.state.loading = true;
259 handleRecipientChange(i: PrivateMessageForm, event: any) {
260 i.state.recipient = event.target.value;
264 handleContentChange(i: PrivateMessageForm, event: any) {
265 i.state.privateMessageForm.content = event.target.value;
269 handleCancel(i: PrivateMessageForm) {
273 handlePreviewToggle(i: PrivateMessageForm, event: any) {
274 event.preventDefault();
275 i.state.previewMode = !i.state.previewMode;
279 handleShowDisclaimer(i: PrivateMessageForm) {
280 i.state.showDisclaimer = !i.state.showDisclaimer;
284 parseMessage(msg: WebSocketJsonResponse) {
285 let res = wsJsonToRes(msg);
287 toast(i18n.t(msg.error), 'danger');
288 this.state.loading = false;
289 this.setState(this.state);
291 } else if (res.op == UserOperation.EditPrivateMessage) {
292 let data = res.data as PrivateMessageResponse;
293 this.state.loading = false;
294 this.props.onEdit(data.message);
295 } else if (res.op == UserOperation.GetUserDetails) {
296 let data = res.data as UserDetailsResponse;
297 this.state.recipient = data.user;
298 this.state.privateMessageForm.recipient_id = data.user.id;
299 this.setState(this.state);
300 } else if (res.op == UserOperation.CreatePrivateMessage) {
301 let data = res.data as PrivateMessageResponse;
302 this.state.loading = false;
303 this.props.onCreate(data.message);
304 this.setState(this.state);