diff --git a/plugins/talk-plugin-rich-text/client/components/Editor.js b/plugins/talk-plugin-rich-text/client/components/Editor.js index 9a5bf67a6..9dbca33e1 100644 --- a/plugins/talk-plugin-rich-text/client/components/Editor.js +++ b/plugins/talk-plugin-rich-text/client/components/Editor.js @@ -40,6 +40,9 @@ class Editor extends React.Component { } }); } + if (this.props.isReply) { + this.ref.focus(); + } } componentWillUnmount() { @@ -66,6 +69,7 @@ class Editor extends React.Component { value={this.getHTML()} disabled={disabled} placeholder={placeholder} + ref={this.handleRef} buttons={[ diff --git a/plugins/talk-plugin-rich-text/client/components/rte/RTE.js b/plugins/talk-plugin-rich-text/client/components/rte/RTE.js index 358228d01..a6da5783a 100644 --- a/plugins/talk-plugin-rich-text/client/components/rte/RTE.js +++ b/plugins/talk-plugin-rich-text/client/components/rte/RTE.js @@ -12,6 +12,7 @@ import { cloneNodeAndRange, replaceNodeChildren, selectEndOfNode, + isSelectionInside, } from './lib/dom'; import API from './lib/api'; import Undo from './lib/undo'; @@ -31,6 +32,11 @@ class RTE extends React.Component { // Refs to the buttons. buttonsRef = {}; + // Export this for parent components. + focus = () => this.ref.htmlEl.focus(); + + unmounted = false; + // Should be called on every change to feed // our Undo stack. We save the innerHTML and if available // a copy of the contentEditable node and a copy of the range. @@ -61,14 +67,16 @@ class RTE extends React.Component { // Ref to react-contenteditable. handleRef = ref => ( (this.ref = ref), - (this.api = new API( - this.ref.htmlEl, - this.handleChange, - () => this.undo.canUndo(), - () => this.undo.canRedo(), - this.handleUndo, - this.handleRedo - )) + (this.api = + ref && + new API( + this.ref.htmlEl, + this.handleChange, + () => this.undo.canUndo(), + () => this.undo.canRedo(), + this.handleUndo, + this.handleRedo + )) ); forEachButton(callback) { @@ -80,9 +88,18 @@ class RTE extends React.Component { if (props.value !== this.ref.htmlEl.innerHTML) { this.undo.clear(); this.saveCheckpoint(props.value); + if (isSelectionInside(this.ref.htmlEl)) { + setTimeout(() => !this.unmounted && selectEndOfNode(this.ref.htmlEl)); + } } } + componentWillUnmount() { + // Cancel pending stuff. + this.saveCheckpoint.cancel(); + this.unmounted = true; + } + handleChange = () => { this.handleSelectionChange(); this.props.onChange({ @@ -127,7 +144,7 @@ class RTE extends React.Component { handleCut = () => { // IE has issues not firing the onChange event. if (bowser.msie) { - setTimeout(this.handleChange); + setTimeout(() => !this.unmounted && this.handleChange()); } }; @@ -149,13 +166,13 @@ class RTE extends React.Component { }; handleMouseUp = () => { - setTimeout(() => this.handleSelectionChange()); + setTimeout(() => !this.unmounted && this.handleSelectionChange()); }; handleKeyDown = e => { // IE has issues not firing the onChange event. if (bowser.msie) { - setTimeout(this.handleChange); + setTimeout(() => !this.unmounted && this.handleChange); } // Undo Redo @@ -188,7 +205,7 @@ class RTE extends React.Component { handleKeyUp = () => { // IE has issues not firing the onChange event. if (bowser.msie) { - setTimeout(this.handleChange); + setTimeout(() => !this.unmounted && this.handleChange); } this.handleSelectionChange(); }; diff --git a/plugins/talk-plugin-rich-text/client/components/rte/factories/createToggle.js b/plugins/talk-plugin-rich-text/client/components/rte/factories/createToggle.js index a12f9f4d9..6e1ca5b71 100644 --- a/plugins/talk-plugin-rich-text/client/components/rte/factories/createToggle.js +++ b/plugins/talk-plugin-rich-text/client/components/rte/factories/createToggle.js @@ -20,6 +20,11 @@ const createToggle = ( isActive = () => isActive.apply(this.props.api); isDisabled = () => isDisabled.apply(this.props.api); onEnter = (...args) => onEnter && onEnter.apply(this.props.api, args); + unmounted = false; + + componentWillUnmount() { + this.unmounted = true; + } formatToggle = () => { this.execCommand(); @@ -29,7 +34,7 @@ const createToggle = ( this.props.api.focus(); this.formatToggle(); this.props.api.focus(); - setTimeout(this.syncState); + setTimeout(() => !this.unmounted && this.syncState()); }; syncState = () => {