import { Component, linkEvent } from 'inferno'; import { Subscription } from 'rxjs'; import { retryWhen, delay, take } from 'rxjs/operators'; import { Prompt } from 'inferno-router'; import { CommentNode as CommentNodeI, CommentForm as CommentFormI, WebSocketJsonResponse, UserOperation, CommentResponse, } from '../interfaces'; import { capitalizeFirstLetter, mdToHtml, randomStr, markdownHelpUrl, toast, setupTribute, wsJsonToRes, pictrsDeleteToast, } from '../utils'; import { WebSocketService, UserService } from '../services'; import autosize from 'autosize'; import Tribute from 'tributejs/src/Tribute.js'; import emojiShortName from 'emoji-short-name'; import { i18n } from '../i18next'; interface CommentFormProps { postId?: number; node?: CommentNodeI; onReplyCancel?(): any; edit?: boolean; disabled?: boolean; } interface CommentFormState { commentForm: CommentFormI; buttonTitle: string; previewMode: boolean; loading: boolean; imageLoading: boolean; } export class CommentForm extends Component { private id = `comment-textarea-${randomStr()}`; private formId = `comment-form-${randomStr()}`; private tribute: Tribute; private subscription: Subscription; private emptyState: CommentFormState = { commentForm: { auth: null, content: null, post_id: this.props.node ? this.props.node.comment.post_id : this.props.postId, creator_id: UserService.Instance.user ? UserService.Instance.user.id : null, }, buttonTitle: !this.props.node ? capitalizeFirstLetter(i18n.t('post')) : this.props.edit ? capitalizeFirstLetter(i18n.t('save')) : capitalizeFirstLetter(i18n.t('reply')), previewMode: false, loading: false, imageLoading: false, }; constructor(props: any, context: any) { super(props, context); this.tribute = setupTribute(); this.state = this.emptyState; if (this.props.node) { if (this.props.edit) { this.state.commentForm.edit_id = this.props.node.comment.id; this.state.commentForm.parent_id = this.props.node.comment.parent_id; this.state.commentForm.content = this.props.node.comment.content; this.state.commentForm.creator_id = this.props.node.comment.creator_id; } else { // A reply gets a new parent id this.state.commentForm.parent_id = this.props.node.comment.id; } } 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() { let textarea: any = document.getElementById(this.id); autosize(textarea); this.tribute.attach(textarea); textarea.addEventListener('tribute-replaced', () => { this.state.commentForm.content = textarea.value; this.setState(this.state); autosize.update(textarea); }); // Quoting of selected text let selectedText = window.getSelection().toString(); if (selectedText) { let quotedText = selectedText .split('\n') .map(t => `> ${t}`) .join('\n') + '\n\n'; this.state.commentForm.content = quotedText; this.setState(this.state); // Not sure why this needs a delay setTimeout(() => autosize.update(textarea), 10); } textarea.focus(); } componentDidUpdate() { if (this.state.commentForm.content) { window.onbeforeunload = () => true; } else { window.onbeforeunload = undefined; } } componentWillUnmount() { this.subscription.unsubscribe(); window.onbeforeunload = null; } render() { return (