diff options
author | Dessalines <tyhou13@gmx.com> | 2020-01-22 16:35:29 -0500 |
---|---|---|
committer | Dessalines <tyhou13@gmx.com> | 2020-01-22 16:38:16 -0500 |
commit | 253bc3e0afb6adf64b79f334a8bc1f972aa45eba (patch) | |
tree | bdbbb36ee59ea4331c7bf7cf89e8ef554eb2d63e /ui/src/components/private-message-form.tsx | |
parent | a964b4ce21cc19eb42ae4da1a1aef8bfc0a1df5c (diff) |
Adding private messaging, and matrix user ids.
- Fixes #244
Diffstat (limited to 'ui/src/components/private-message-form.tsx')
-rw-r--r-- | ui/src/components/private-message-form.tsx | 291 |
1 files changed, 291 insertions, 0 deletions
diff --git a/ui/src/components/private-message-form.tsx b/ui/src/components/private-message-form.tsx new file mode 100644 index 00000000..c628bf71 --- /dev/null +++ b/ui/src/components/private-message-form.tsx @@ -0,0 +1,291 @@ +import { Component, linkEvent } from 'inferno'; +import { Link } from 'inferno-router'; +import { Subscription } from 'rxjs'; +import { retryWhen, delay, take } from 'rxjs/operators'; +import { + PrivateMessageForm as PrivateMessageFormI, + EditPrivateMessageForm, + PrivateMessageFormParams, + PrivateMessage, + PrivateMessageResponse, + UserView, + UserOperation, + UserDetailsResponse, + GetUserDetailsForm, + SortType, +} from '../interfaces'; +import { WebSocketService } from '../services'; +import { + msgOp, + capitalizeFirstLetter, + markdownHelpUrl, + mdToHtml, + showAvatars, + pictshareAvatarThumbnail, +} from '../utils'; +import autosize from 'autosize'; +import { i18n } from '../i18next'; +import { T } from 'inferno-i18next'; + +interface PrivateMessageFormProps { + privateMessage?: PrivateMessage; // If a pm is given, that means this is an edit + params?: PrivateMessageFormParams; + onCancel?(): any; + onCreate?(message: PrivateMessage): any; + onEdit?(message: PrivateMessage): any; +} + +interface PrivateMessageFormState { + privateMessageForm: PrivateMessageFormI; + recipient: UserView; + loading: boolean; + previewMode: boolean; + showDisclaimer: boolean; +} + +export class PrivateMessageForm extends Component< + PrivateMessageFormProps, + PrivateMessageFormState +> { + private subscription: Subscription; + private emptyState: PrivateMessageFormState = { + privateMessageForm: { + content: null, + recipient_id: null, + }, + recipient: null, + loading: false, + previewMode: false, + showDisclaimer: false, + }; + + constructor(props: any, context: any) { + super(props, context); + + this.state = this.emptyState; + + if (this.props.privateMessage) { + this.state.privateMessageForm = { + content: this.props.privateMessage.content, + recipient_id: this.props.privateMessage.recipient_id, + }; + } + + if (this.props.params) { + this.state.privateMessageForm.recipient_id = this.props.params.recipient_id; + let form: GetUserDetailsForm = { + user_id: this.state.privateMessageForm.recipient_id, + sort: SortType[SortType.New], + saved_only: false, + }; + WebSocketService.Instance.getUserDetails(form); + } + + this.subscription = WebSocketService.Instance.subject + .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10)))) + .subscribe( + msg => this.parseMessage(msg), + err => console.error(err), + () => console.log('complete') + ); + } + + componentDidMount() { + autosize(document.querySelectorAll('textarea')); + } + + componentWillUnmount() { + this.subscription.unsubscribe(); + } + + render() { + return ( + <div> + <form onSubmit={linkEvent(this, this.handlePrivateMessageSubmit)}> + {!this.props.privateMessage && ( + <div class="form-group row"> + <label class="col-sm-2 col-form-label"> + {capitalizeFirstLetter(i18n.t('to'))} + </label> + + {this.state.recipient && ( + <div class="col-sm-10 form-control-plaintext"> + <Link + className="text-info" + to={`/u/${this.state.recipient.name}`} + > + {this.state.recipient.avatar && showAvatars() && ( + <img + height="32" + width="32" + src={pictshareAvatarThumbnail( + this.state.recipient.avatar + )} + class="rounded-circle mr-1" + /> + )} + <span>{this.state.recipient.name}</span> + </Link> + </div> + )} + </div> + )} + <div class="form-group row"> + <label class="col-sm-2 col-form-label">{i18n.t('message')}</label> + <div class="col-sm-10"> + <textarea + value={this.state.privateMessageForm.content} + onInput={linkEvent(this, this.handleContentChange)} + className={`form-control ${this.state.previewMode && 'd-none'}`} + rows={4} + maxLength={10000} + /> + {this.state.previewMode && ( + <div + className="md-div" + dangerouslySetInnerHTML={mdToHtml( + this.state.privateMessageForm.content + )} + /> + )} + + {this.state.privateMessageForm.content && ( + <button + className={`mt-1 mr-2 btn btn-sm btn-secondary ${this.state + .previewMode && 'active'}`} + onClick={linkEvent(this, this.handlePreviewToggle)} + > + {i18n.t('preview')} + </button> + )} + <ul class="float-right list-inline mb-1 text-muted small font-weight-bold"> + <li class="list-inline-item"> + <span + onClick={linkEvent(this, this.handleShowDisclaimer)} + class="pointer" + > + {i18n.t('disclaimer')} + </span> + </li> + <li class="list-inline-item"> + <a href={markdownHelpUrl} target="_blank" class="text-muted"> + {i18n.t('formatting_help')} + </a> + </li> + </ul> + </div> + </div> + + {this.state.showDisclaimer && ( + <div class="form-group row"> + <div class="col-sm-10"> + <div class="alert alert-danger" role="alert"> + <T i18nKey="private_message_disclaimer"> + # + <a + class="alert-link" + target="_blank" + href="https://about.riot.im/" + > + # + </a> + </T> + </div> + </div> + </div> + )} + <div class="form-group row"> + <div class="col-sm-10"> + <button type="submit" class="btn btn-secondary mr-2"> + {this.state.loading ? ( + <svg class="icon icon-spinner spin"> + <use xlinkHref="#icon-spinner"></use> + </svg> + ) : this.props.privateMessage ? ( + capitalizeFirstLetter(i18n.t('save')) + ) : ( + capitalizeFirstLetter(i18n.t('send_message')) + )} + </button> + {this.props.privateMessage && ( + <button + type="button" + class="btn btn-secondary" + onClick={linkEvent(this, this.handleCancel)} + > + {i18n.t('cancel')} + </button> + )} + </div> + </div> + </form> + </div> + ); + } + + handlePrivateMessageSubmit(i: PrivateMessageForm, event: any) { + event.preventDefault(); + if (i.props.privateMessage) { + let editForm: EditPrivateMessageForm = { + edit_id: i.props.privateMessage.id, + content: i.state.privateMessageForm.content, + }; + WebSocketService.Instance.editPrivateMessage(editForm); + } else { + WebSocketService.Instance.createPrivateMessage( + i.state.privateMessageForm + ); + } + i.state.loading = true; + i.setState(i.state); + } + + handleRecipientChange(i: PrivateMessageForm, event: any) { + i.state.recipient = event.target.value; + i.setState(i.state); + } + + handleContentChange(i: PrivateMessageForm, event: any) { + i.state.privateMessageForm.content = event.target.value; + i.setState(i.state); + } + + handleCancel(i: PrivateMessageForm) { + i.props.onCancel(); + } + + handlePreviewToggle(i: PrivateMessageForm, event: any) { + event.preventDefault(); + i.state.previewMode = !i.state.previewMode; + i.setState(i.state); + } + + handleShowDisclaimer(i: PrivateMessageForm) { + i.state.showDisclaimer = !i.state.showDisclaimer; + i.setState(i.state); + } + + parseMessage(msg: any) { + let op: UserOperation = msgOp(msg); + if (msg.error) { + alert(i18n.t(msg.error)); + this.state.loading = false; + this.setState(this.state); + return; + } else if (op == UserOperation.EditPrivateMessage) { + this.state.loading = false; + let res: PrivateMessageResponse = msg; + this.props.onEdit(res.message); + } else if (op == UserOperation.GetUserDetails) { + let res: UserDetailsResponse = msg; + this.state.recipient = res.user; + this.state.privateMessageForm.recipient_id = res.user.id; + this.setState(this.state); + } else if (op == UserOperation.CreatePrivateMessage) { + this.state.loading = false; + let res: PrivateMessageResponse = msg; + this.props.onCreate(res.message); + this.setState(this.state); + } + } +} |