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 } from '../interfaces';
17 import { WebSocketService } from '../services';
20 capitalizeFirstLetter,
24 pictshareAvatarThumbnail,
27 import autosize from 'autosize';
28 import { i18n } from '../i18next';
29 import { T } from 'inferno-i18next';
31 interface PrivateMessageFormProps {
32 privateMessage?: PrivateMessage; // If a pm is given, that means this is an edit
33 params?: PrivateMessageFormParams;
35 onCreate?(message: PrivateMessage): any;
36 onEdit?(message: PrivateMessage): any;
39 interface PrivateMessageFormState {
40 privateMessageForm: PrivateMessageFormI;
44 showDisclaimer: boolean;
47 export class PrivateMessageForm extends Component<
48 PrivateMessageFormProps,
49 PrivateMessageFormState
51 private subscription: Subscription;
52 private emptyState: PrivateMessageFormState = {
60 showDisclaimer: false,
63 constructor(props: any, context: any) {
64 super(props, context);
66 this.state = this.emptyState;
68 if (this.props.privateMessage) {
69 this.state.privateMessageForm = {
70 content: this.props.privateMessage.content,
71 recipient_id: this.props.privateMessage.recipient_id,
75 if (this.props.params) {
76 this.state.privateMessageForm.recipient_id = this.props.params.recipient_id;
77 let form: GetUserDetailsForm = {
78 user_id: this.state.privateMessageForm.recipient_id,
79 sort: SortType[SortType.New],
82 WebSocketService.Instance.getUserDetails(form);
85 this.subscription = WebSocketService.Instance.subject
86 .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
88 msg => this.parseMessage(msg),
89 err => console.error(err),
90 () => console.log('complete')
95 autosize(document.querySelectorAll('textarea'));
98 componentWillUnmount() {
99 this.subscription.unsubscribe();
105 <form onSubmit={linkEvent(this, this.handlePrivateMessageSubmit)}>
106 {!this.props.privateMessage && (
107 <div class="form-group row">
108 <label class="col-sm-2 col-form-label">
109 {capitalizeFirstLetter(i18n.t('to'))}
112 {this.state.recipient && (
113 <div class="col-sm-10 form-control-plaintext">
115 className="text-info"
116 to={`/u/${this.state.recipient.name}`}
118 {this.state.recipient.avatar && showAvatars() && (
122 src={pictshareAvatarThumbnail(
123 this.state.recipient.avatar
125 class="rounded-circle mr-1"
128 <span>{this.state.recipient.name}</span>
134 <div class="form-group row">
135 <label class="col-sm-2 col-form-label">{i18n.t('message')}</label>
136 <div class="col-sm-10">
138 value={this.state.privateMessageForm.content}
139 onInput={linkEvent(this, this.handleContentChange)}
140 className={`form-control ${this.state.previewMode && 'd-none'}`}
144 {this.state.previewMode && (
147 dangerouslySetInnerHTML={mdToHtml(
148 this.state.privateMessageForm.content
153 {this.state.privateMessageForm.content && (
155 className={`mt-1 mr-2 btn btn-sm btn-secondary ${this.state
156 .previewMode && 'active'}`}
157 onClick={linkEvent(this, this.handlePreviewToggle)}
162 <ul class="float-right list-inline mb-1 text-muted small font-weight-bold">
163 <li class="list-inline-item">
165 onClick={linkEvent(this, this.handleShowDisclaimer)}
168 {i18n.t('disclaimer')}
171 <li class="list-inline-item">
172 <a href={markdownHelpUrl} target="_blank" class="text-muted">
173 {i18n.t('formatting_help')}
180 {this.state.showDisclaimer && (
181 <div class="form-group row">
182 <div class="col-sm-10">
183 <div class="alert alert-danger" role="alert">
184 <T i18nKey="private_message_disclaimer">
189 href="https://about.riot.im/"
198 <div class="form-group row">
199 <div class="col-sm-10">
200 <button type="submit" class="btn btn-secondary mr-2">
201 {this.state.loading ? (
202 <svg class="icon icon-spinner spin">
203 <use xlinkHref="#icon-spinner"></use>
205 ) : this.props.privateMessage ? (
206 capitalizeFirstLetter(i18n.t('save'))
208 capitalizeFirstLetter(i18n.t('send_message'))
211 {this.props.privateMessage && (
214 class="btn btn-secondary"
215 onClick={linkEvent(this, this.handleCancel)}
227 handlePrivateMessageSubmit(i: PrivateMessageForm, event: any) {
228 event.preventDefault();
229 if (i.props.privateMessage) {
230 let editForm: EditPrivateMessageForm = {
231 edit_id: i.props.privateMessage.id,
232 content: i.state.privateMessageForm.content,
234 WebSocketService.Instance.editPrivateMessage(editForm);
236 WebSocketService.Instance.createPrivateMessage(
237 i.state.privateMessageForm
240 i.state.loading = true;
244 handleRecipientChange(i: PrivateMessageForm, event: any) {
245 i.state.recipient = event.target.value;
249 handleContentChange(i: PrivateMessageForm, event: any) {
250 i.state.privateMessageForm.content = event.target.value;
254 handleCancel(i: PrivateMessageForm) {
258 handlePreviewToggle(i: PrivateMessageForm, event: any) {
259 event.preventDefault();
260 i.state.previewMode = !i.state.previewMode;
264 handleShowDisclaimer(i: PrivateMessageForm) {
265 i.state.showDisclaimer = !i.state.showDisclaimer;
269 parseMessage(msg: any) {
270 let op: UserOperation = msgOp(msg);
272 toast(i18n.t(msg.error), 'danger');
273 this.state.loading = false;
274 this.setState(this.state);
276 } else if (op == UserOperation.EditPrivateMessage) {
277 this.state.loading = false;
278 let res: PrivateMessageResponse = msg;
279 this.props.onEdit(res.message);
280 } else if (op == UserOperation.GetUserDetails) {
281 let res: UserDetailsResponse = msg;
282 this.state.recipient = res.user;
283 this.state.privateMessageForm.recipient_id = res.user.id;
284 this.setState(this.state);
285 } else if (op == UserOperation.CreatePrivateMessage) {
286 this.state.loading = false;
287 let res: PrivateMessageResponse = msg;
288 this.props.onCreate(res.message);
289 this.setState(this.state);