mirror of https://github.com/vector-im/riot-web
				
				
				
			Merge pull request #3712 from matrix-org/t3chguy/react16_refs
Migrate away from React Legacy string refspull/21833/head
						commit
						c1b1f98201
					
				|  | @ -174,12 +174,6 @@ React | |||
|   <Foo onClick={this.onFooClick}> // Best, if onFooClick would do anything other than directly calling doStuff | ||||
|   ``` | ||||
| 
 | ||||
|   Not doing so is acceptable in a single case: in function-refs: | ||||
| 
 | ||||
|   ```jsx | ||||
|   <Foo ref={(self) => this.component = self}> | ||||
|   ``` | ||||
| 
 | ||||
| - Prefer classes that extend `React.Component` (or `React.PureComponent`) instead of `React.createClass` | ||||
|   - You can avoid the need to bind handler functions by using [property initializers](https://reactjs.org/docs/react-component.html#constructor): | ||||
| 
 | ||||
|  | @ -208,3 +202,5 @@ React | |||
|   ``` | ||||
| - Think about whether your component really needs state: are you duplicating | ||||
|   information in component state that could be derived from the model? | ||||
| 
 | ||||
| - Avoid things marked as Legacy or Deprecated in React 16 (e.g string refs and legacy contexts) | ||||
|  |  | |||
|  | @ -15,7 +15,7 @@ limitations under the License. | |||
| */ | ||||
| 
 | ||||
| import FileSaver from 'file-saver'; | ||||
| import React from 'react'; | ||||
| import React, {createRef} from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import createReactClass from 'create-react-class'; | ||||
| import { _t } from '../../../languageHandler'; | ||||
|  | @ -44,6 +44,9 @@ export default createReactClass({ | |||
| 
 | ||||
|     componentWillMount: function() { | ||||
|         this._unmounted = false; | ||||
| 
 | ||||
|         this._passphrase1 = createRef(); | ||||
|         this._passphrase2 = createRef(); | ||||
|     }, | ||||
| 
 | ||||
|     componentWillUnmount: function() { | ||||
|  | @ -53,8 +56,8 @@ export default createReactClass({ | |||
|     _onPassphraseFormSubmit: function(ev) { | ||||
|         ev.preventDefault(); | ||||
| 
 | ||||
|         const passphrase = this.refs.passphrase1.value; | ||||
|         if (passphrase !== this.refs.passphrase2.value) { | ||||
|         const passphrase = this._passphrase1.current.value; | ||||
|         if (passphrase !== this._passphrase2.current.value) { | ||||
|             this.setState({errStr: _t('Passphrases must match')}); | ||||
|             return false; | ||||
|         } | ||||
|  | @ -148,7 +151,7 @@ export default createReactClass({ | |||
|                                     </label> | ||||
|                                 </div> | ||||
|                                 <div className='mx_E2eKeysDialog_inputCell'> | ||||
|                                     <input ref='passphrase1' id='passphrase1' | ||||
|                                     <input ref={this._passphrase1} id='passphrase1' | ||||
|                                         autoFocus={true} size='64' type='password' | ||||
|                                         disabled={disableForm} | ||||
|                                     /> | ||||
|  | @ -161,7 +164,7 @@ export default createReactClass({ | |||
|                                     </label> | ||||
|                                 </div> | ||||
|                                 <div className='mx_E2eKeysDialog_inputCell'> | ||||
|                                     <input ref='passphrase2' id='passphrase2' | ||||
|                                     <input ref={this._passphrase2} id='passphrase2' | ||||
|                                         size='64' type='password' | ||||
|                                         disabled={disableForm} | ||||
|                                     /> | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ See the License for the specific language governing permissions and | |||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| import React from 'react'; | ||||
| import React, {createRef} from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import createReactClass from 'create-react-class'; | ||||
| 
 | ||||
|  | @ -56,6 +56,9 @@ export default createReactClass({ | |||
| 
 | ||||
|     componentWillMount: function() { | ||||
|         this._unmounted = false; | ||||
| 
 | ||||
|         this._file = createRef(); | ||||
|         this._passphrase = createRef(); | ||||
|     }, | ||||
| 
 | ||||
|     componentWillUnmount: function() { | ||||
|  | @ -63,15 +66,15 @@ export default createReactClass({ | |||
|     }, | ||||
| 
 | ||||
|     _onFormChange: function(ev) { | ||||
|         const files = this.refs.file.files || []; | ||||
|         const files = this._file.current.files || []; | ||||
|         this.setState({ | ||||
|             enableSubmit: (this.refs.passphrase.value !== "" && files.length > 0), | ||||
|             enableSubmit: (this._passphrase.current.value !== "" && files.length > 0), | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     _onFormSubmit: function(ev) { | ||||
|         ev.preventDefault(); | ||||
|         this._startImport(this.refs.file.files[0], this.refs.passphrase.value); | ||||
|         this._startImport(this._file.current.files[0], this._passphrase.current.value); | ||||
|         return false; | ||||
|     }, | ||||
| 
 | ||||
|  | @ -146,7 +149,10 @@ export default createReactClass({ | |||
|                                    </label> | ||||
|                                </div> | ||||
|                                <div className='mx_E2eKeysDialog_inputCell'> | ||||
|                                    <input ref='file' id='importFile' type='file' | ||||
|                                    <input | ||||
|                                        ref={this._file} | ||||
|                                        id='importFile' | ||||
|                                        type='file' | ||||
|                                        autoFocus={true} | ||||
|                                        onChange={this._onFormChange} | ||||
|                                        disabled={disableForm} /> | ||||
|  | @ -159,8 +165,11 @@ export default createReactClass({ | |||
|                                    </label> | ||||
|                                </div> | ||||
|                                <div className='mx_E2eKeysDialog_inputCell'> | ||||
|                                    <input ref='passphrase' id='passphrase' | ||||
|                                        size='64' type='password' | ||||
|                                    <input | ||||
|                                        ref={this._passphrase} | ||||
|                                        id='passphrase' | ||||
|                                        size='64' | ||||
|                                        type='password' | ||||
|                                        onChange={this._onFormChange} | ||||
|                                        disabled={disableForm} /> | ||||
|                                </div> | ||||
|  |  | |||
|  | @ -1214,25 +1214,25 @@ export default createReactClass({ | |||
| 
 | ||||
|                 const EditableText = sdk.getComponent("elements.EditableText"); | ||||
| 
 | ||||
|                 nameNode = <EditableText ref="nameEditor" | ||||
|                      className="mx_GroupView_editable" | ||||
|                      placeholderClassName="mx_GroupView_placeholder" | ||||
|                      placeholder={_t('Community Name')} | ||||
|                      blurToCancel={false} | ||||
|                      initialValue={this.state.profileForm.name} | ||||
|                      onValueChanged={this._onNameChange} | ||||
|                      tabIndex="0" | ||||
|                      dir="auto" />; | ||||
|                 nameNode = <EditableText | ||||
|                     className="mx_GroupView_editable" | ||||
|                     placeholderClassName="mx_GroupView_placeholder" | ||||
|                     placeholder={_t('Community Name')} | ||||
|                     blurToCancel={false} | ||||
|                     initialValue={this.state.profileForm.name} | ||||
|                     onValueChanged={this._onNameChange} | ||||
|                     tabIndex="0" | ||||
|                     dir="auto" />; | ||||
| 
 | ||||
|                 shortDescNode = <EditableText ref="descriptionEditor" | ||||
|                      className="mx_GroupView_editable" | ||||
|                      placeholderClassName="mx_GroupView_placeholder" | ||||
|                      placeholder={_t("Description")} | ||||
|                      blurToCancel={false} | ||||
|                      initialValue={this.state.profileForm.short_description} | ||||
|                      onValueChanged={this._onShortDescChange} | ||||
|                      tabIndex="0" | ||||
|                      dir="auto" />; | ||||
|                 shortDescNode = <EditableText | ||||
|                     className="mx_GroupView_editable" | ||||
|                     placeholderClassName="mx_GroupView_placeholder" | ||||
|                     placeholder={_t("Description")} | ||||
|                     blurToCancel={false} | ||||
|                     initialValue={this.state.profileForm.short_description} | ||||
|                     onValueChanged={this._onShortDescChange} | ||||
|                     tabIndex="0" | ||||
|                     dir="auto" />; | ||||
|             } else { | ||||
|                 const onGroupHeaderItemClick = this.state.isUserMember ? this._onEditClick : null; | ||||
|                 const groupAvatarUrl = summary.profile ? summary.profile.avatar_url : null; | ||||
|  |  | |||
|  | @ -18,7 +18,7 @@ limitations under the License. | |||
| import Matrix from 'matrix-js-sdk'; | ||||
| const InteractiveAuth = Matrix.InteractiveAuth; | ||||
| 
 | ||||
| import React from 'react'; | ||||
| import React, {createRef} from 'react'; | ||||
| import createReactClass from 'create-react-class'; | ||||
| import PropTypes from 'prop-types'; | ||||
| 
 | ||||
|  | @ -129,6 +129,8 @@ export default createReactClass({ | |||
|                 this._authLogic.poll(); | ||||
|             }, 2000); | ||||
|         } | ||||
| 
 | ||||
|         this._stageComponent = createRef(); | ||||
|     }, | ||||
| 
 | ||||
|     componentWillUnmount: function() { | ||||
|  | @ -153,8 +155,8 @@ export default createReactClass({ | |||
|     }, | ||||
| 
 | ||||
|     tryContinue: function() { | ||||
|         if (this.refs.stageComponent && this.refs.stageComponent.tryContinue) { | ||||
|             this.refs.stageComponent.tryContinue(); | ||||
|         if (this._stageComponent.current && this._stageComponent.current.tryContinue) { | ||||
|             this._stageComponent.current.tryContinue(); | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|  | @ -192,8 +194,8 @@ export default createReactClass({ | |||
|     }, | ||||
| 
 | ||||
|     _setFocus: function() { | ||||
|         if (this.refs.stageComponent && this.refs.stageComponent.focus) { | ||||
|             this.refs.stageComponent.focus(); | ||||
|         if (this._stageComponent.current && this._stageComponent.current.focus) { | ||||
|             this._stageComponent.current.focus(); | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|  | @ -214,7 +216,8 @@ export default createReactClass({ | |||
| 
 | ||||
|         const StageComponent = getEntryComponentForLoginType(stage); | ||||
|         return ( | ||||
|             <StageComponent ref="stageComponent" | ||||
|             <StageComponent | ||||
|                 ref={this._stageComponent} | ||||
|                 loginType={stage} | ||||
|                 matrixClient={this.props.matrixClient} | ||||
|                 authSessionId={this._authLogic.getSessionId()} | ||||
|  |  | |||
|  | @ -17,7 +17,7 @@ limitations under the License. | |||
| */ | ||||
| 
 | ||||
| import { MatrixClient } from 'matrix-js-sdk'; | ||||
| import React from 'react'; | ||||
| import React, {createRef} from 'react'; | ||||
| import createReactClass from 'create-react-class'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import { DragDropContext } from 'react-beautiful-dnd'; | ||||
|  | @ -129,6 +129,8 @@ const LoggedInView = createReactClass({ | |||
|         this._matrixClient.on("RoomState.events", this.onRoomStateEvents); | ||||
| 
 | ||||
|         fixupColorFonts(); | ||||
| 
 | ||||
|         this._roomView = createRef(); | ||||
|     }, | ||||
| 
 | ||||
|     componentDidUpdate(prevProps) { | ||||
|  | @ -165,10 +167,10 @@ const LoggedInView = createReactClass({ | |||
|     }, | ||||
| 
 | ||||
|     canResetTimelineInRoom: function(roomId) { | ||||
|         if (!this.refs.roomView) { | ||||
|         if (!this._roomView.current) { | ||||
|             return true; | ||||
|         } | ||||
|         return this.refs.roomView.canResetTimeline(); | ||||
|         return this._roomView.current.canResetTimeline(); | ||||
|     }, | ||||
| 
 | ||||
|     _setStateFromSessionStore() { | ||||
|  | @ -428,8 +430,8 @@ const LoggedInView = createReactClass({ | |||
|      * @param {Object} ev The key event | ||||
|      */ | ||||
|     _onScrollKeyPressed: function(ev) { | ||||
|         if (this.refs.roomView) { | ||||
|             this.refs.roomView.handleScrollKey(ev); | ||||
|         if (this._roomView.current) { | ||||
|             this._roomView.current.handleScrollKey(ev); | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|  | @ -543,7 +545,7 @@ const LoggedInView = createReactClass({ | |||
|         switch (this.props.page_type) { | ||||
|             case PageTypes.RoomView: | ||||
|                 pageElement = <RoomView | ||||
|                         ref='roomView' | ||||
|                         ref={this._roomView} | ||||
|                         autoJoin={this.props.autoJoin} | ||||
|                         onRegistered={this.props.onRegistered} | ||||
|                         thirdPartyInvite={this.props.thirdPartyInvite} | ||||
|  |  | |||
|  | @ -16,7 +16,7 @@ See the License for the specific language governing permissions and | |||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| import React from 'react'; | ||||
| import React, {createRef} from 'react'; | ||||
| import ReactDOM from 'react-dom'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import classNames from 'classnames'; | ||||
|  | @ -159,6 +159,10 @@ export default class MessagePanel extends React.Component { | |||
|             SettingsStore.getValue("showHiddenEventsInTimeline"); | ||||
| 
 | ||||
|         this._isMounted = false; | ||||
| 
 | ||||
|         this._readMarkerNode = createRef(); | ||||
|         this._whoIsTyping = createRef(); | ||||
|         this._scrollPanel = createRef(); | ||||
|     } | ||||
| 
 | ||||
|     componentDidMount() { | ||||
|  | @ -191,8 +195,7 @@ export default class MessagePanel extends React.Component { | |||
|     /* return true if the content is fully scrolled down right now; else false. | ||||
|      */ | ||||
|     isAtBottom() { | ||||
|         return this.refs.scrollPanel | ||||
|             && this.refs.scrollPanel.isAtBottom(); | ||||
|         return this._scrollPanel.current && this._scrollPanel.current.isAtBottom(); | ||||
|     } | ||||
| 
 | ||||
|     /* get the current scroll state. See ScrollPanel.getScrollState for | ||||
|  | @ -201,8 +204,7 @@ export default class MessagePanel extends React.Component { | |||
|      * returns null if we are not mounted. | ||||
|      */ | ||||
|     getScrollState() { | ||||
|         if (!this.refs.scrollPanel) { return null; } | ||||
|         return this.refs.scrollPanel.getScrollState(); | ||||
|         return this._scrollPanel.current ? this._scrollPanel.current.getScrollState() : null; | ||||
|     } | ||||
| 
 | ||||
|     // returns one of:
 | ||||
|  | @ -212,8 +214,8 @@ export default class MessagePanel extends React.Component { | |||
|     //   0: read marker is within the window
 | ||||
|     //  +1: read marker is below the window
 | ||||
|     getReadMarkerPosition() { | ||||
|         const readMarker = this.refs.readMarkerNode; | ||||
|         const messageWrapper = this.refs.scrollPanel; | ||||
|         const readMarker = this._readMarkerNode.current; | ||||
|         const messageWrapper = this._scrollPanel.current; | ||||
| 
 | ||||
|         if (!readMarker || !messageWrapper) { | ||||
|             return null; | ||||
|  | @ -236,16 +238,16 @@ export default class MessagePanel extends React.Component { | |||
|     /* jump to the top of the content. | ||||
|      */ | ||||
|     scrollToTop() { | ||||
|         if (this.refs.scrollPanel) { | ||||
|             this.refs.scrollPanel.scrollToTop(); | ||||
|         if (this._scrollPanel.current) { | ||||
|             this._scrollPanel.current.scrollToTop(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /* jump to the bottom of the content. | ||||
|      */ | ||||
|     scrollToBottom() { | ||||
|         if (this.refs.scrollPanel) { | ||||
|             this.refs.scrollPanel.scrollToBottom(); | ||||
|         if (this._scrollPanel.current) { | ||||
|             this._scrollPanel.current.scrollToBottom(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -255,8 +257,8 @@ export default class MessagePanel extends React.Component { | |||
|      * @param {number} mult: -1 to page up, +1 to page down | ||||
|      */ | ||||
|     scrollRelative(mult) { | ||||
|         if (this.refs.scrollPanel) { | ||||
|             this.refs.scrollPanel.scrollRelative(mult); | ||||
|         if (this._scrollPanel.current) { | ||||
|             this._scrollPanel.current.scrollRelative(mult); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -266,8 +268,8 @@ export default class MessagePanel extends React.Component { | |||
|      * @param {KeyboardEvent} ev: the keyboard event to handle | ||||
|      */ | ||||
|     handleScrollKey(ev) { | ||||
|         if (this.refs.scrollPanel) { | ||||
|             this.refs.scrollPanel.handleScrollKey(ev); | ||||
|         if (this._scrollPanel.current) { | ||||
|             this._scrollPanel.current.handleScrollKey(ev); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -282,8 +284,8 @@ export default class MessagePanel extends React.Component { | |||
|      * defaults to 0. | ||||
|      */ | ||||
|     scrollToEvent(eventId, pixelOffset, offsetBase) { | ||||
|         if (this.refs.scrollPanel) { | ||||
|             this.refs.scrollPanel.scrollToToken(eventId, pixelOffset, offsetBase); | ||||
|         if (this._scrollPanel.current) { | ||||
|             this._scrollPanel.current.scrollToToken(eventId, pixelOffset, offsetBase); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -297,8 +299,8 @@ export default class MessagePanel extends React.Component { | |||
|     /* check the scroll state and send out pagination requests if necessary. | ||||
|      */ | ||||
|     checkFillState() { | ||||
|         if (this.refs.scrollPanel) { | ||||
|             this.refs.scrollPanel.checkFillState(); | ||||
|         if (this._scrollPanel.current) { | ||||
|             this._scrollPanel.current.checkFillState(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -345,7 +347,7 @@ export default class MessagePanel extends React.Component { | |||
|             } | ||||
| 
 | ||||
|             return ( | ||||
|                 <li key={"readMarker_"+eventId} ref="readMarkerNode" | ||||
|                 <li key={"readMarker_"+eventId} ref={this._readMarkerNode} | ||||
|                       className="mx_RoomView_myReadMarker_container"> | ||||
|                     { hr } | ||||
|                 </li> | ||||
|  | @ -829,14 +831,14 @@ export default class MessagePanel extends React.Component { | |||
|     // once dynamic content in the events load, make the scrollPanel check the
 | ||||
|     // scroll offsets.
 | ||||
|     _onHeightChanged = () => { | ||||
|         const scrollPanel = this.refs.scrollPanel; | ||||
|         const scrollPanel = this._scrollPanel.current; | ||||
|         if (scrollPanel) { | ||||
|             scrollPanel.checkScroll(); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     _onTypingShown = () => { | ||||
|         const scrollPanel = this.refs.scrollPanel; | ||||
|         const scrollPanel = this._scrollPanel.current; | ||||
|         // this will make the timeline grow, so checkScroll
 | ||||
|         scrollPanel.checkScroll(); | ||||
|         if (scrollPanel && scrollPanel.getScrollState().stuckAtBottom) { | ||||
|  | @ -845,7 +847,7 @@ export default class MessagePanel extends React.Component { | |||
|     }; | ||||
| 
 | ||||
|     _onTypingHidden = () => { | ||||
|         const scrollPanel = this.refs.scrollPanel; | ||||
|         const scrollPanel = this._scrollPanel.current; | ||||
|         if (scrollPanel) { | ||||
|             // as hiding the typing notifications doesn't
 | ||||
|             // update the scrollPanel, we tell it to apply
 | ||||
|  | @ -858,11 +860,11 @@ export default class MessagePanel extends React.Component { | |||
|     }; | ||||
| 
 | ||||
|     updateTimelineMinHeight() { | ||||
|         const scrollPanel = this.refs.scrollPanel; | ||||
|         const scrollPanel = this._scrollPanel.current; | ||||
| 
 | ||||
|         if (scrollPanel) { | ||||
|             const isAtBottom = scrollPanel.isAtBottom(); | ||||
|             const whoIsTyping = this.refs.whoIsTyping; | ||||
|             const whoIsTyping = this._whoIsTyping.current; | ||||
|             const isTypingVisible = whoIsTyping && whoIsTyping.isVisible(); | ||||
|             // when messages get added to the timeline,
 | ||||
|             // but somebody else is still typing,
 | ||||
|  | @ -875,7 +877,7 @@ export default class MessagePanel extends React.Component { | |||
|     } | ||||
| 
 | ||||
|     onTimelineReset() { | ||||
|         const scrollPanel = this.refs.scrollPanel; | ||||
|         const scrollPanel = this._scrollPanel.current; | ||||
|         if (scrollPanel) { | ||||
|             scrollPanel.clearPreventShrinking(); | ||||
|         } | ||||
|  | @ -909,19 +911,22 @@ export default class MessagePanel extends React.Component { | |||
|                 room={this.props.room} | ||||
|                 onShown={this._onTypingShown} | ||||
|                 onHidden={this._onTypingHidden} | ||||
|                 ref="whoIsTyping" /> | ||||
|                 ref={this._whoIsTyping} /> | ||||
|             ); | ||||
|         } | ||||
| 
 | ||||
|         return ( | ||||
|             <ScrollPanel ref="scrollPanel" className={className} | ||||
|                     onScroll={this.props.onScroll} | ||||
|                     onResize={this.onResize} | ||||
|                     onFillRequest={this.props.onFillRequest} | ||||
|                     onUnfillRequest={this.props.onUnfillRequest} | ||||
|                     style={style} | ||||
|                     stickyBottom={this.props.stickyBottom} | ||||
|                     resizeNotifier={this.props.resizeNotifier}> | ||||
|             <ScrollPanel | ||||
|                 ref={this._scrollPanel} | ||||
|                 className={className} | ||||
|                 onScroll={this.props.onScroll} | ||||
|                 onResize={this.onResize} | ||||
|                 onFillRequest={this.props.onFillRequest} | ||||
|                 onUnfillRequest={this.props.onUnfillRequest} | ||||
|                 style={style} | ||||
|                 stickyBottom={this.props.stickyBottom} | ||||
|                 resizeNotifier={this.props.resizeNotifier} | ||||
|             > | ||||
|                 { topSpinner } | ||||
|                 { this._getEventTiles() } | ||||
|                 { whoIsTyping } | ||||
|  |  | |||
|  | @ -572,7 +572,7 @@ module.exports = createReactClass({ | |||
|             if (rows.length === 0 && !this.state.loading) { | ||||
|                 scrollpanel_content = <i>{ _t('No rooms to show') }</i>; | ||||
|             } else { | ||||
|                 scrollpanel_content = <table ref="directory_table" className="mx_RoomDirectory_table"> | ||||
|                 scrollpanel_content = <table className="mx_RoomDirectory_table"> | ||||
|                     <tbody> | ||||
|                         { rows } | ||||
|                     </tbody> | ||||
|  |  | |||
|  | @ -18,7 +18,6 @@ limitations under the License. | |||
| */ | ||||
| 
 | ||||
| import React, {createRef} from 'react'; | ||||
| import createReactClass from 'create-react-class'; | ||||
| import classNames from 'classnames'; | ||||
| import sdk from '../../index'; | ||||
| import dis from '../../dispatcher'; | ||||
|  | @ -36,12 +35,11 @@ import {_t} from "../../languageHandler"; | |||
| // turn this on for drop & drag console debugging galore
 | ||||
| const debug = false; | ||||
| 
 | ||||
| const RoomSubList = createReactClass({ | ||||
|     displayName: 'RoomSubList', | ||||
| export default class RoomSubList extends React.PureComponent { | ||||
|     static displayName = 'RoomSubList'; | ||||
|     static debug = debug; | ||||
| 
 | ||||
|     debug: debug, | ||||
| 
 | ||||
|     propTypes: { | ||||
|     static propTypes = { | ||||
|         list: PropTypes.arrayOf(PropTypes.object).isRequired, | ||||
|         label: PropTypes.string.isRequired, | ||||
|         tagName: PropTypes.string, | ||||
|  | @ -59,10 +57,26 @@ const RoomSubList = createReactClass({ | |||
|         incomingCall: PropTypes.object, | ||||
|         extraTiles: PropTypes.arrayOf(PropTypes.node), // extra elements added beneath tiles
 | ||||
|         forceExpand: PropTypes.bool, | ||||
|     }, | ||||
|     }; | ||||
| 
 | ||||
|     getInitialState: function() { | ||||
|     static defaultProps = { | ||||
|         onHeaderClick: function() { | ||||
|         }, // NOP
 | ||||
|         extraTiles: [], | ||||
|         isInvite: false, | ||||
|     }; | ||||
| 
 | ||||
|     static getDerivedStateFromProps(props, state) { | ||||
|         return { | ||||
|             listLength: props.list.length, | ||||
|             scrollTop: props.list.length === state.listLength ? state.scrollTop : 0, | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     constructor(props) { | ||||
|         super(props); | ||||
| 
 | ||||
|         this.state = { | ||||
|             hidden: this.props.startAsHidden || false, | ||||
|             // some values to get LazyRenderList starting
 | ||||
|             scrollerHeight: 800, | ||||
|  | @ -71,47 +85,33 @@ const RoomSubList = createReactClass({ | |||
|             // we have to store the length of the list here so we can see if it's changed or not...
 | ||||
|             listLength: null, | ||||
|         }; | ||||
|     }, | ||||
| 
 | ||||
|     getDefaultProps: function() { | ||||
|         return { | ||||
|             onHeaderClick: function() { | ||||
|             }, // NOP
 | ||||
|             extraTiles: [], | ||||
|             isInvite: false, | ||||
|         }; | ||||
|     }, | ||||
| 
 | ||||
|     componentDidMount: function() { | ||||
|         this._header = createRef(); | ||||
|         this._subList = createRef(); | ||||
|         this._scroller = createRef(); | ||||
|         this._headerButton = createRef(); | ||||
|     } | ||||
| 
 | ||||
|     componentDidMount() { | ||||
|         this.dispatcherRef = dis.register(this.onAction); | ||||
|     }, | ||||
|     } | ||||
| 
 | ||||
|     statics: { | ||||
|         getDerivedStateFromProps: function(props, state) { | ||||
|             return { | ||||
|                 listLength: props.list.length, | ||||
|                 scrollTop: props.list.length === state.listLength ? state.scrollTop : 0, | ||||
|             }; | ||||
|         }, | ||||
|     }, | ||||
| 
 | ||||
|     componentWillUnmount: function() { | ||||
|     componentWillUnmount() { | ||||
|         dis.unregister(this.dispatcherRef); | ||||
|     }, | ||||
|     } | ||||
| 
 | ||||
|     // The header is collapsible if it is hidden or not stuck
 | ||||
|     // The dataset elements are added in the RoomList _initAndPositionStickyHeaders method
 | ||||
|     isCollapsibleOnClick: function() { | ||||
|         const stuck = this.refs.header.dataset.stuck; | ||||
|     isCollapsibleOnClick() { | ||||
|         const stuck = this._header.current.dataset.stuck; | ||||
|         if (!this.props.forceExpand && (this.state.hidden || stuck === undefined || stuck === "none")) { | ||||
|             return true; | ||||
|         } else { | ||||
|             return false; | ||||
|         } | ||||
|     }, | ||||
|     } | ||||
| 
 | ||||
|     onAction: function(payload) { | ||||
|     onAction = (payload) => { | ||||
|         // XXX: Previously RoomList would forceUpdate whenever on_room_read is dispatched,
 | ||||
|         // but this is no longer true, so we must do it here (and can apply the small
 | ||||
|         // optimisation of checking that we care about the room being read).
 | ||||
|  | @ -124,9 +124,9 @@ const RoomSubList = createReactClass({ | |||
|         ) { | ||||
|             this.forceUpdate(); | ||||
|         } | ||||
|     }, | ||||
|     }; | ||||
| 
 | ||||
|     onClick: function(ev) { | ||||
|     onClick = (ev) => { | ||||
|         if (this.isCollapsibleOnClick()) { | ||||
|             // The header isCollapsible, so the click is to be interpreted as collapse and truncation logic
 | ||||
|             const isHidden = !this.state.hidden; | ||||
|  | @ -135,11 +135,11 @@ const RoomSubList = createReactClass({ | |||
|             }); | ||||
|         } else { | ||||
|             // The header is stuck, so the click is to be interpreted as a scroll to the header
 | ||||
|             this.props.onHeaderClick(this.state.hidden, this.refs.header.dataset.originalPosition); | ||||
|             this.props.onHeaderClick(this.state.hidden, this._header.current.dataset.originalPosition); | ||||
|         } | ||||
|     }, | ||||
|     }; | ||||
| 
 | ||||
|     onHeaderKeyDown: function(ev) { | ||||
|     onHeaderKeyDown = (ev) => { | ||||
|         switch (ev.key) { | ||||
|             case Key.TAB: | ||||
|                 // Prevent LeftPanel handling Tab if focus is on the sublist header itself
 | ||||
|  | @ -159,7 +159,7 @@ const RoomSubList = createReactClass({ | |||
|                     this.onClick(); | ||||
|                 } else if (!this.props.forceExpand) { | ||||
|                     // sublist is expanded, go to first room
 | ||||
|                     const element = this.refs.subList && this.refs.subList.querySelector(".mx_RoomTile"); | ||||
|                     const element = this._subList.current && this._subList.current.querySelector(".mx_RoomTile"); | ||||
|                     if (element) { | ||||
|                         element.focus(); | ||||
|                     } | ||||
|  | @ -167,9 +167,9 @@ const RoomSubList = createReactClass({ | |||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|     }; | ||||
| 
 | ||||
|     onKeyDown: function(ev) { | ||||
|     onKeyDown = (ev) => { | ||||
|         switch (ev.key) { | ||||
|             // On ARROW_LEFT go to the sublist header
 | ||||
|             case Key.ARROW_LEFT: | ||||
|  | @ -180,24 +180,24 @@ const RoomSubList = createReactClass({ | |||
|             case Key.ARROW_RIGHT: | ||||
|                 ev.stopPropagation(); | ||||
|         } | ||||
|     }, | ||||
|     }; | ||||
| 
 | ||||
|     onRoomTileClick(roomId, ev) { | ||||
|     onRoomTileClick = (roomId, ev) => { | ||||
|         dis.dispatch({ | ||||
|             action: 'view_room', | ||||
|             room_id: roomId, | ||||
|             clear_search: (ev && (ev.keyCode === KeyCode.ENTER || ev.keyCode === KeyCode.SPACE)), | ||||
|         }); | ||||
|     }, | ||||
|     }; | ||||
| 
 | ||||
|     _updateSubListCount: function() { | ||||
|     _updateSubListCount = () => { | ||||
|         // Force an update by setting the state to the current state
 | ||||
|         // Doing it this way rather than using forceUpdate(), so that the shouldComponentUpdate()
 | ||||
|         // method is honoured
 | ||||
|         this.setState(this.state); | ||||
|     }, | ||||
|     }; | ||||
| 
 | ||||
|     makeRoomTile: function(room) { | ||||
|     makeRoomTile = (room) => { | ||||
|         return <RoomTile | ||||
|             room={room} | ||||
|             roomSubList={this} | ||||
|  | @ -212,9 +212,9 @@ const RoomSubList = createReactClass({ | |||
|             incomingCall={null} | ||||
|             onClick={this.onRoomTileClick} | ||||
|         />; | ||||
|     }, | ||||
|     }; | ||||
| 
 | ||||
|     _onNotifBadgeClick: function(e) { | ||||
|     _onNotifBadgeClick = (e) => { | ||||
|         // prevent the roomsublist collapsing
 | ||||
|         e.preventDefault(); | ||||
|         e.stopPropagation(); | ||||
|  | @ -225,9 +225,9 @@ const RoomSubList = createReactClass({ | |||
|                 room_id: room.roomId, | ||||
|             }); | ||||
|         } | ||||
|     }, | ||||
|     }; | ||||
| 
 | ||||
|     _onInviteBadgeClick: function(e) { | ||||
|     _onInviteBadgeClick = (e) => { | ||||
|         // prevent the roomsublist collapsing
 | ||||
|         e.preventDefault(); | ||||
|         e.stopPropagation(); | ||||
|  | @ -247,14 +247,14 @@ const RoomSubList = createReactClass({ | |||
|                 }); | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|     }; | ||||
| 
 | ||||
|     onAddRoom: function(e) { | ||||
|     onAddRoom = (e) => { | ||||
|         e.stopPropagation(); | ||||
|         if (this.props.onAddRoom) this.props.onAddRoom(); | ||||
|     }, | ||||
|     }; | ||||
| 
 | ||||
|     _getHeaderJsx: function(isCollapsed) { | ||||
|     _getHeaderJsx(isCollapsed) { | ||||
|         const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); | ||||
|         const AccessibleTooltipButton = sdk.getComponent('elements.AccessibleTooltipButton'); | ||||
|         const subListNotifications = !this.props.isInvite ? | ||||
|  | @ -328,7 +328,7 @@ const RoomSubList = createReactClass({ | |||
|         } | ||||
| 
 | ||||
|         return ( | ||||
|             <div className="mx_RoomSubList_labelContainer" title={title} ref="header" onKeyDown={this.onHeaderKeyDown}> | ||||
|             <div className="mx_RoomSubList_labelContainer" title={title} ref={this._header} onKeyDown={this.onHeaderKeyDown}> | ||||
|                 <AccessibleButton | ||||
|                     onClick={this.onClick} | ||||
|                     className="mx_RoomSubList_label" | ||||
|  | @ -346,36 +346,36 @@ const RoomSubList = createReactClass({ | |||
|                 { addRoomButton } | ||||
|             </div> | ||||
|         ); | ||||
|     }, | ||||
|     } | ||||
| 
 | ||||
|     checkOverflow: function() { | ||||
|         if (this.refs.scroller) { | ||||
|             this.refs.scroller.checkOverflow(); | ||||
|     checkOverflow = () => { | ||||
|         if (this._scroller.current) { | ||||
|             this._scroller.current.checkOverflow(); | ||||
|         } | ||||
|     }, | ||||
|     }; | ||||
| 
 | ||||
|     setHeight: function(height) { | ||||
|         if (this.refs.subList) { | ||||
|             this.refs.subList.style.height = `${height}px`; | ||||
|     setHeight = (height) => { | ||||
|         if (this._subList.current) { | ||||
|             this._subList.current.style.height = `${height}px`; | ||||
|         } | ||||
|         this._updateLazyRenderHeight(height); | ||||
|     }, | ||||
|     }; | ||||
| 
 | ||||
|     _updateLazyRenderHeight: function(height) { | ||||
|     _updateLazyRenderHeight(height) { | ||||
|         this.setState({scrollerHeight: height}); | ||||
|     }, | ||||
|     } | ||||
| 
 | ||||
|     _onScroll: function() { | ||||
|         this.setState({scrollTop: this.refs.scroller.getScrollTop()}); | ||||
|     }, | ||||
|     _onScroll = () => { | ||||
|         this.setState({scrollTop: this._scroller.current.getScrollTop()}); | ||||
|     }; | ||||
| 
 | ||||
|     _canUseLazyListRendering() { | ||||
|         // for now disable lazy rendering as they are already rendered tiles
 | ||||
|         // not rooms like props.list we pass to LazyRenderList
 | ||||
|         return !this.props.extraTiles || !this.props.extraTiles.length; | ||||
|     }, | ||||
|     } | ||||
| 
 | ||||
|     render: function() { | ||||
|     render() { | ||||
|         const len = this.props.list.length + this.props.extraTiles.length; | ||||
|         const isCollapsed = this.state.hidden && !this.props.forceExpand; | ||||
| 
 | ||||
|  | @ -391,7 +391,7 @@ const RoomSubList = createReactClass({ | |||
|                 // no body
 | ||||
|             } else if (this._canUseLazyListRendering()) { | ||||
|                 content = ( | ||||
|                     <IndicatorScrollbar ref="scroller" className="mx_RoomSubList_scroll" onScroll={this._onScroll}> | ||||
|                     <IndicatorScrollbar ref={this._scroller} className="mx_RoomSubList_scroll" onScroll={this._onScroll}> | ||||
|                         <LazyRenderList | ||||
|                             scrollTop={this.state.scrollTop } | ||||
|                             height={ this.state.scrollerHeight } | ||||
|  | @ -404,7 +404,7 @@ const RoomSubList = createReactClass({ | |||
|                 const roomTiles = this.props.list.map(r => this.makeRoomTile(r)); | ||||
|                 const tiles = roomTiles.concat(this.props.extraTiles); | ||||
|                 content = ( | ||||
|                     <IndicatorScrollbar ref="scroller" className="mx_RoomSubList_scroll" onScroll={this._onScroll}> | ||||
|                     <IndicatorScrollbar ref={this._scroller} className="mx_RoomSubList_scroll" onScroll={this._onScroll}> | ||||
|                         { tiles } | ||||
|                     </IndicatorScrollbar> | ||||
|                 ); | ||||
|  | @ -418,7 +418,7 @@ const RoomSubList = createReactClass({ | |||
| 
 | ||||
|         return ( | ||||
|             <div | ||||
|                 ref="subList" | ||||
|                 ref={this._subList} | ||||
|                 className={subListClasses} | ||||
|                 role="group" | ||||
|                 aria-label={this.props.label} | ||||
|  | @ -428,7 +428,5 @@ const RoomSubList = createReactClass({ | |||
|                 { content } | ||||
|             </div> | ||||
|         ); | ||||
|     }, | ||||
| }); | ||||
| 
 | ||||
| module.exports = RoomSubList; | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -23,7 +23,7 @@ limitations under the License. | |||
| 
 | ||||
| import shouldHideEvent from '../../shouldHideEvent'; | ||||
| 
 | ||||
| import React from 'react'; | ||||
| import React, {createRef} from 'react'; | ||||
| import createReactClass from 'create-react-class'; | ||||
| import ReactDOM from 'react-dom'; | ||||
| import PropTypes from 'prop-types'; | ||||
|  | @ -207,6 +207,9 @@ module.exports = createReactClass({ | |||
|         this._onCiderUpdated(); | ||||
|         this._ciderWatcherRef = SettingsStore.watchSetting( | ||||
|             "useCiderComposer", null, this._onCiderUpdated); | ||||
| 
 | ||||
|         this._roomView = createRef(); | ||||
|         this._searchResultsPanel = createRef(); | ||||
|     }, | ||||
| 
 | ||||
|     _onCiderUpdated: function() { | ||||
|  | @ -459,8 +462,8 @@ module.exports = createReactClass({ | |||
|     }, | ||||
| 
 | ||||
|     componentDidUpdate: function() { | ||||
|         if (this.refs.roomView) { | ||||
|             const roomView = ReactDOM.findDOMNode(this.refs.roomView); | ||||
|         if (this._roomView.current) { | ||||
|             const roomView = ReactDOM.findDOMNode(this._roomView.current); | ||||
|             if (!roomView.ondrop) { | ||||
|                 roomView.addEventListener('drop', this.onDrop); | ||||
|                 roomView.addEventListener('dragover', this.onDragOver); | ||||
|  | @ -474,10 +477,10 @@ module.exports = createReactClass({ | |||
|         // in render() prevents the ref from being set on first mount, so we try and
 | ||||
|         // catch the messagePanel when it does mount. Because we only want the ref once,
 | ||||
|         // we use a boolean flag to avoid duplicate work.
 | ||||
|         if (this.refs.messagePanel && !this.state.atEndOfLiveTimelineInit) { | ||||
|         if (this._messagePanel && !this.state.atEndOfLiveTimelineInit) { | ||||
|             this.setState({ | ||||
|                 atEndOfLiveTimelineInit: true, | ||||
|                 atEndOfLiveTimeline: this.refs.messagePanel.isAtEndOfLiveTimeline(), | ||||
|                 atEndOfLiveTimeline: this._messagePanel.isAtEndOfLiveTimeline(), | ||||
|             }); | ||||
|         } | ||||
|     }, | ||||
|  | @ -499,12 +502,12 @@ module.exports = createReactClass({ | |||
|         // stop tracking room changes to format permalinks
 | ||||
|         this._stopAllPermalinkCreators(); | ||||
| 
 | ||||
|         if (this.refs.roomView) { | ||||
|         if (this._roomView.current) { | ||||
|             // disconnect the D&D event listeners from the room view. This
 | ||||
|             // is really just for hygiene - we're going to be
 | ||||
|             // deleted anyway, so it doesn't matter if the event listeners
 | ||||
|             // don't get cleaned up.
 | ||||
|             const roomView = ReactDOM.findDOMNode(this.refs.roomView); | ||||
|             const roomView = ReactDOM.findDOMNode(this._roomView.current); | ||||
|             roomView.removeEventListener('drop', this.onDrop); | ||||
|             roomView.removeEventListener('dragover', this.onDragOver); | ||||
|             roomView.removeEventListener('dragleave', this.onDragLeaveOrEnd); | ||||
|  | @ -701,10 +704,10 @@ module.exports = createReactClass({ | |||
|     }, | ||||
| 
 | ||||
|     canResetTimeline: function() { | ||||
|         if (!this.refs.messagePanel) { | ||||
|         if (!this._messagePanel) { | ||||
|             return true; | ||||
|         } | ||||
|         return this.refs.messagePanel.canResetTimeline(); | ||||
|         return this._messagePanel.canResetTimeline(); | ||||
|     }, | ||||
| 
 | ||||
|     // called when state.room is first initialised (either at initial load,
 | ||||
|  | @ -1046,7 +1049,7 @@ module.exports = createReactClass({ | |||
|     }, | ||||
| 
 | ||||
|     onMessageListScroll: function(ev) { | ||||
|         if (this.refs.messagePanel.isAtEndOfLiveTimeline()) { | ||||
|         if (this._messagePanel.isAtEndOfLiveTimeline()) { | ||||
|             this.setState({ | ||||
|                 numUnreadMessages: 0, | ||||
|                 atEndOfLiveTimeline: true, | ||||
|  | @ -1119,8 +1122,8 @@ module.exports = createReactClass({ | |||
| 
 | ||||
|         // if we already have a search panel, we need to tell it to forget
 | ||||
|         // about its scroll state.
 | ||||
|         if (this.refs.searchResultsPanel) { | ||||
|             this.refs.searchResultsPanel.resetScrollState(); | ||||
|         if (this._searchResultsPanel.current) { | ||||
|             this._searchResultsPanel.current.resetScrollState(); | ||||
|         } | ||||
| 
 | ||||
|         // make sure that we don't end up showing results from
 | ||||
|  | @ -1225,7 +1228,7 @@ module.exports = createReactClass({ | |||
|         // once dynamic content in the search results load, make the scrollPanel check
 | ||||
|         // the scroll offsets.
 | ||||
|         const onHeightChanged = () => { | ||||
|             const scrollPanel = this.refs.searchResultsPanel; | ||||
|             const scrollPanel = this._searchResultsPanel.current; | ||||
|             if (scrollPanel) { | ||||
|                 scrollPanel.checkScroll(); | ||||
|             } | ||||
|  | @ -1370,28 +1373,28 @@ module.exports = createReactClass({ | |||
| 
 | ||||
|     // jump down to the bottom of this room, where new events are arriving
 | ||||
|     jumpToLiveTimeline: function() { | ||||
|         this.refs.messagePanel.jumpToLiveTimeline(); | ||||
|         this._messagePanel.jumpToLiveTimeline(); | ||||
|         dis.dispatch({action: 'focus_composer'}); | ||||
|     }, | ||||
| 
 | ||||
|     // jump up to wherever our read marker is
 | ||||
|     jumpToReadMarker: function() { | ||||
|         this.refs.messagePanel.jumpToReadMarker(); | ||||
|         this._messagePanel.jumpToReadMarker(); | ||||
|     }, | ||||
| 
 | ||||
|     // update the read marker to match the read-receipt
 | ||||
|     forgetReadMarker: function(ev) { | ||||
|         ev.stopPropagation(); | ||||
|         this.refs.messagePanel.forgetReadMarker(); | ||||
|         this._messagePanel.forgetReadMarker(); | ||||
|     }, | ||||
| 
 | ||||
|     // decide whether or not the top 'unread messages' bar should be shown
 | ||||
|     _updateTopUnreadMessagesBar: function() { | ||||
|         if (!this.refs.messagePanel) { | ||||
|         if (!this._messagePanel) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         const showBar = this.refs.messagePanel.canJumpToReadMarker(); | ||||
|         const showBar = this._messagePanel.canJumpToReadMarker(); | ||||
|         if (this.state.showTopUnreadMessagesBar != showBar) { | ||||
|             this.setState({showTopUnreadMessagesBar: showBar}); | ||||
|         } | ||||
|  | @ -1401,7 +1404,7 @@ module.exports = createReactClass({ | |||
|     // restored when we switch back to it.
 | ||||
|     //
 | ||||
|     _getScrollState: function() { | ||||
|         const messagePanel = this.refs.messagePanel; | ||||
|         const messagePanel = this._messagePanel; | ||||
|         if (!messagePanel) return null; | ||||
| 
 | ||||
|         // if we're following the live timeline, we want to return null; that
 | ||||
|  | @ -1506,10 +1509,10 @@ module.exports = createReactClass({ | |||
|      */ | ||||
|     handleScrollKey: function(ev) { | ||||
|         let panel; | ||||
|         if (this.refs.searchResultsPanel) { | ||||
|             panel = this.refs.searchResultsPanel; | ||||
|         } else if (this.refs.messagePanel) { | ||||
|             panel = this.refs.messagePanel; | ||||
|         if (this._searchResultsPanel.current) { | ||||
|             panel = this._searchResultsPanel.current; | ||||
|         } else if (this._messagePanel) { | ||||
|             panel = this._messagePanel; | ||||
|         } | ||||
| 
 | ||||
|         if (panel) { | ||||
|  | @ -1530,7 +1533,7 @@ module.exports = createReactClass({ | |||
|     // this has to be a proper method rather than an unnamed function,
 | ||||
|     // otherwise react calls it with null on each update.
 | ||||
|     _gatherTimelinePanelRef: function(r) { | ||||
|         this.refs.messagePanel = r; | ||||
|         this._messagePanel = r; | ||||
|         if (r) { | ||||
|             console.log("updateTint from RoomView._gatherTimelinePanelRef"); | ||||
|             this.updateTint(); | ||||
|  | @ -1719,7 +1722,7 @@ module.exports = createReactClass({ | |||
|             aux = <ForwardMessage onCancelClick={this.onCancelClick} />; | ||||
|         } else if (this.state.searching) { | ||||
|             hideCancel = true; // has own cancel
 | ||||
|             aux = <SearchBar ref="search_bar" searchInProgress={this.state.searchInProgress} onCancelClick={this.onCancelSearchClick} onSearch={this.onSearch} />; | ||||
|             aux = <SearchBar searchInProgress={this.state.searchInProgress} onCancelClick={this.onCancelSearchClick} onSearch={this.onSearch} />; | ||||
|         } else if (showRoomUpgradeBar) { | ||||
|             aux = <RoomUpgradeWarningBar room={this.state.room} recommendation={roomVersionRecommendation} />; | ||||
|             hideCancel = true; | ||||
|  | @ -1775,7 +1778,7 @@ module.exports = createReactClass({ | |||
|         } | ||||
| 
 | ||||
|         const auxPanel = ( | ||||
|             <AuxPanel ref="auxPanel" room={this.state.room} | ||||
|             <AuxPanel room={this.state.room} | ||||
|               fullHeight={false} | ||||
|               userId={MatrixClientPeg.get().credentials.userId} | ||||
|               conferenceHandler={this.props.ConferenceHandler} | ||||
|  | @ -1875,7 +1878,7 @@ module.exports = createReactClass({ | |||
|                 searchResultsPanel = (<div className="mx_RoomView_messagePanel mx_RoomView_messagePanelSearchSpinner" />); | ||||
|             } else { | ||||
|                 searchResultsPanel = ( | ||||
|                     <ScrollPanel ref="searchResultsPanel" | ||||
|                     <ScrollPanel ref={this._searchResultsPanel} | ||||
|                         className="mx_RoomView_messagePanel mx_RoomView_searchResultsPanel" | ||||
|                         onFillRequest={this.onSearchResultsFillRequest} | ||||
|                         resizeNotifier={this.props.resizeNotifier} | ||||
|  | @ -1898,7 +1901,8 @@ module.exports = createReactClass({ | |||
| 
 | ||||
|         // console.info("ShowUrlPreview for %s is %s", this.state.room.roomId, this.state.showUrlPreview);
 | ||||
|         const messagePanel = ( | ||||
|             <TimelinePanel ref={this._gatherTimelinePanelRef} | ||||
|             <TimelinePanel | ||||
|                 ref={this._gatherTimelinePanelRef} | ||||
|                 timelineSet={this.state.room.getUnfilteredTimelineSet()} | ||||
|                 showReadReceipts={SettingsStore.getValue('showReadReceipts')} | ||||
|                 manageReadReceipts={!this.state.isPeeking} | ||||
|  | @ -1952,9 +1956,11 @@ module.exports = createReactClass({ | |||
|         const collapsedRhs = hideRightPanel || this.props.collapsedRhs; | ||||
| 
 | ||||
|         return ( | ||||
|             <main className={"mx_RoomView" + (inCall ? " mx_RoomView_inCall" : "")} ref="roomView"> | ||||
|             <main className={"mx_RoomView" + (inCall ? " mx_RoomView_inCall" : "")} ref={this._roomView}> | ||||
|                 <ErrorBoundary> | ||||
|                     <RoomHeader ref="header" room={this.state.room} searchInfo={searchInfo} | ||||
|                     <RoomHeader | ||||
|                         room={this.state.room} | ||||
|                         searchInfo={searchInfo} | ||||
|                         oobData={this.props.oobData} | ||||
|                         inRoom={myMembership === 'join'} | ||||
|                         collapsedRhs={collapsedRhs} | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ See the License for the specific language governing permissions and | |||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| import React from "react"; | ||||
| import React, {createRef} from "react"; | ||||
| import createReactClass from 'create-react-class'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import { KeyCode } from '../../Keyboard'; | ||||
|  | @ -166,6 +166,8 @@ module.exports = createReactClass({ | |||
|         } | ||||
| 
 | ||||
|         this.resetScrollState(); | ||||
| 
 | ||||
|         this._itemlist = createRef(); | ||||
|     }, | ||||
| 
 | ||||
|     componentDidMount: function() { | ||||
|  | @ -328,7 +330,7 @@ module.exports = createReactClass({ | |||
|             this._isFilling = true; | ||||
|         } | ||||
| 
 | ||||
|         const itemlist = this.refs.itemlist; | ||||
|         const itemlist = this._itemlist.current; | ||||
|         const firstTile = itemlist && itemlist.firstElementChild; | ||||
|         const contentTop = firstTile && firstTile.offsetTop; | ||||
|         const fillPromises = []; | ||||
|  | @ -373,7 +375,7 @@ module.exports = createReactClass({ | |||
| 
 | ||||
|         const origExcessHeight = excessHeight; | ||||
| 
 | ||||
|         const tiles = this.refs.itemlist.children; | ||||
|         const tiles = this._itemlist.current.children; | ||||
| 
 | ||||
|         // The scroll token of the first/last tile to be unpaginated
 | ||||
|         let markerScrollToken = null; | ||||
|  | @ -602,7 +604,7 @@ module.exports = createReactClass({ | |||
|         const scrollNode = this._getScrollNode(); | ||||
|         const viewportBottom = scrollNode.scrollHeight - (scrollNode.scrollTop + scrollNode.clientHeight); | ||||
| 
 | ||||
|         const itemlist = this.refs.itemlist; | ||||
|         const itemlist = this._itemlist.current; | ||||
|         const messages = itemlist.children; | ||||
|         let node = null; | ||||
| 
 | ||||
|  | @ -644,7 +646,7 @@ module.exports = createReactClass({ | |||
|             const sn = this._getScrollNode(); | ||||
|             sn.scrollTop = sn.scrollHeight; | ||||
|         } else if (scrollState.trackedScrollToken) { | ||||
|             const itemlist = this.refs.itemlist; | ||||
|             const itemlist = this._itemlist.current; | ||||
|             const trackedNode = this._getTrackedNode(); | ||||
|             if (trackedNode) { | ||||
|                 const newBottomOffset = this._topFromBottom(trackedNode); | ||||
|  | @ -682,7 +684,7 @@ module.exports = createReactClass({ | |||
|         } | ||||
| 
 | ||||
|         const sn = this._getScrollNode(); | ||||
|         const itemlist = this.refs.itemlist; | ||||
|         const itemlist = this._itemlist.current; | ||||
|         const contentHeight = this._getMessagesHeight(); | ||||
|         const minHeight = sn.clientHeight; | ||||
|         const height = Math.max(minHeight, contentHeight); | ||||
|  | @ -724,7 +726,7 @@ module.exports = createReactClass({ | |||
| 
 | ||||
|         if (!trackedNode || !trackedNode.parentElement) { | ||||
|             let node; | ||||
|             const messages = this.refs.itemlist.children; | ||||
|             const messages = this._itemlist.current.children; | ||||
|             const scrollToken = scrollState.trackedScrollToken; | ||||
| 
 | ||||
|             for (let i = messages.length-1; i >= 0; --i) { | ||||
|  | @ -756,7 +758,7 @@ module.exports = createReactClass({ | |||
|     }, | ||||
| 
 | ||||
|     _getMessagesHeight() { | ||||
|         const itemlist = this.refs.itemlist; | ||||
|         const itemlist = this._itemlist.current; | ||||
|         const lastNode = itemlist.lastElementChild; | ||||
|         const lastNodeBottom = lastNode ? lastNode.offsetTop + lastNode.clientHeight : 0; | ||||
|         const firstNodeTop = itemlist.firstElementChild ? itemlist.firstElementChild.offsetTop : 0; | ||||
|  | @ -765,7 +767,7 @@ module.exports = createReactClass({ | |||
|     }, | ||||
| 
 | ||||
|     _topFromBottom(node) { | ||||
|         return this.refs.itemlist.clientHeight - node.offsetTop; | ||||
|         return this._itemlist.current.clientHeight - node.offsetTop; | ||||
|     }, | ||||
| 
 | ||||
|     /* get the DOM node which has the scrollTop property we care about for our | ||||
|  | @ -797,7 +799,7 @@ module.exports = createReactClass({ | |||
|     the same minimum bottom offset, effectively preventing the timeline to shrink. | ||||
|     */ | ||||
|     preventShrinking: function() { | ||||
|         const messageList = this.refs.itemlist; | ||||
|         const messageList = this._itemlist.current; | ||||
|         const tiles = messageList && messageList.children; | ||||
|         if (!messageList) { | ||||
|             return; | ||||
|  | @ -824,7 +826,7 @@ module.exports = createReactClass({ | |||
| 
 | ||||
|     /** Clear shrinking prevention. Used internally, and when the timeline is reloaded. */ | ||||
|     clearPreventShrinking: function() { | ||||
|         const messageList = this.refs.itemlist; | ||||
|         const messageList = this._itemlist.current; | ||||
|         const balanceElement = messageList && messageList.parentElement; | ||||
|         if (balanceElement) balanceElement.style.paddingBottom = null; | ||||
|         this.preventShrinkingState = null; | ||||
|  | @ -843,7 +845,7 @@ module.exports = createReactClass({ | |||
|         if (this.preventShrinkingState) { | ||||
|             const sn = this._getScrollNode(); | ||||
|             const scrollState = this.scrollState; | ||||
|             const messageList = this.refs.itemlist; | ||||
|             const messageList = this._itemlist.current; | ||||
|             const {offsetNode, offsetFromBottom} = this.preventShrinkingState; | ||||
|             // element used to set paddingBottom to balance the typing notifs disappearing
 | ||||
|             const balanceElement = messageList.parentElement; | ||||
|  | @ -879,7 +881,7 @@ module.exports = createReactClass({ | |||
|                 onScroll={this.onScroll} | ||||
|                 className={`mx_ScrollPanel ${this.props.className}`} style={this.props.style}> | ||||
|                     <div className="mx_RoomView_messageListWrapper"> | ||||
|                         <ol ref="itemlist" className="mx_RoomView_MessageList" aria-live="polite"> | ||||
|                         <ol ref={this._itemlist} className="mx_RoomView_MessageList" aria-live="polite"> | ||||
|                             { this.props.children } | ||||
|                         </ol> | ||||
|                     </div> | ||||
|  |  | |||
|  | @ -15,7 +15,7 @@ See the License for the specific language governing permissions and | |||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| import React from 'react'; | ||||
| import React, {createRef} from 'react'; | ||||
| import createReactClass from 'create-react-class'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import { KeyCode } from '../../Keyboard'; | ||||
|  | @ -53,6 +53,10 @@ module.exports = createReactClass({ | |||
|         }; | ||||
|     }, | ||||
| 
 | ||||
|     UNSAFE_componentWillMount: function() { | ||||
|         this._search = createRef(); | ||||
|     }, | ||||
| 
 | ||||
|     componentDidMount: function() { | ||||
|         this.dispatcherRef = dis.register(this.onAction); | ||||
|     }, | ||||
|  | @ -66,26 +70,26 @@ module.exports = createReactClass({ | |||
| 
 | ||||
|         switch (payload.action) { | ||||
|             case 'view_room': | ||||
|                 if (this.refs.search && payload.clear_search) { | ||||
|                 if (this._search.current && payload.clear_search) { | ||||
|                     this._clearSearch(); | ||||
|                 } | ||||
|                 break; | ||||
|             case 'focus_room_filter': | ||||
|                 if (this.refs.search) { | ||||
|                     this.refs.search.focus(); | ||||
|                 if (this._search.current) { | ||||
|                     this._search.current.focus(); | ||||
|                 } | ||||
|                 break; | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     onChange: function() { | ||||
|         if (!this.refs.search) return; | ||||
|         this.setState({ searchTerm: this.refs.search.value }); | ||||
|         if (!this._search.current) return; | ||||
|         this.setState({ searchTerm: this._search.current.value }); | ||||
|         this.onSearch(); | ||||
|     }, | ||||
| 
 | ||||
|     onSearch: throttle(function() { | ||||
|         this.props.onSearch(this.refs.search.value); | ||||
|         this.props.onSearch(this._search.current.value); | ||||
|     }, 200, {trailing: true, leading: true}), | ||||
| 
 | ||||
|     _onKeyDown: function(ev) { | ||||
|  | @ -113,7 +117,7 @@ module.exports = createReactClass({ | |||
|     }, | ||||
| 
 | ||||
|     _clearSearch: function(source) { | ||||
|         this.refs.search.value = ""; | ||||
|         this._search.current.value = ""; | ||||
|         this.onChange(); | ||||
|         if (this.props.onCleared) { | ||||
|             this.props.onCleared(source); | ||||
|  | @ -146,7 +150,7 @@ module.exports = createReactClass({ | |||
|                 <input | ||||
|                     key="searchfield" | ||||
|                     type="text" | ||||
|                     ref="search" | ||||
|                     ref={this._search} | ||||
|                     className={"mx_textinput_icon mx_textinput_search " + className} | ||||
|                     value={ this.state.searchTerm } | ||||
|                     onFocus={ this._onFocus } | ||||
|  |  | |||
|  | @ -19,7 +19,7 @@ limitations under the License. | |||
| 
 | ||||
| import SettingsStore from "../../settings/SettingsStore"; | ||||
| 
 | ||||
| import React from 'react'; | ||||
| import React, {createRef} from 'react'; | ||||
| import createReactClass from 'create-react-class'; | ||||
| import ReactDOM from "react-dom"; | ||||
| import PropTypes from 'prop-types'; | ||||
|  | @ -203,6 +203,8 @@ const TimelinePanel = createReactClass({ | |||
|         this.lastRRSentEventId = undefined; | ||||
|         this.lastRMSentEventId = undefined; | ||||
| 
 | ||||
|         this._messagePanel = createRef(); | ||||
| 
 | ||||
|         if (this.props.manageReadReceipts) { | ||||
|             this.updateReadReceiptOnUserActivity(); | ||||
|         } | ||||
|  | @ -425,8 +427,8 @@ const TimelinePanel = createReactClass({ | |||
|         if (payload.action === "edit_event") { | ||||
|             const editState = payload.event ? new EditorStateTransfer(payload.event) : null; | ||||
|             this.setState({editState}, () => { | ||||
|                 if (payload.event && this.refs.messagePanel) { | ||||
|                     this.refs.messagePanel.scrollToEventIfNeeded( | ||||
|                 if (payload.event && this._messagePanel.current) { | ||||
|                     this._messagePanel.current.scrollToEventIfNeeded( | ||||
|                         payload.event.getId(), | ||||
|                     ); | ||||
|                 } | ||||
|  | @ -442,9 +444,9 @@ const TimelinePanel = createReactClass({ | |||
|         // updates from pagination will happen when the paginate completes.
 | ||||
|         if (toStartOfTimeline || !data || !data.liveEvent) return; | ||||
| 
 | ||||
|         if (!this.refs.messagePanel) return; | ||||
|         if (!this._messagePanel.current) return; | ||||
| 
 | ||||
|         if (!this.refs.messagePanel.getScrollState().stuckAtBottom) { | ||||
|         if (!this._messagePanel.current.getScrollState().stuckAtBottom) { | ||||
|             // we won't load this event now, because we don't want to push any
 | ||||
|             // events off the other end of the timeline. But we need to note
 | ||||
|             // that we can now paginate.
 | ||||
|  | @ -499,7 +501,7 @@ const TimelinePanel = createReactClass({ | |||
|             } | ||||
| 
 | ||||
|             this.setState(updatedState, () => { | ||||
|                 this.refs.messagePanel.updateTimelineMinHeight(); | ||||
|                 this._messagePanel.current.updateTimelineMinHeight(); | ||||
|                 if (callRMUpdated) { | ||||
|                     this.props.onReadMarkerUpdated(); | ||||
|                 } | ||||
|  | @ -510,13 +512,13 @@ const TimelinePanel = createReactClass({ | |||
|     onRoomTimelineReset: function(room, timelineSet) { | ||||
|         if (timelineSet !== this.props.timelineSet) return; | ||||
| 
 | ||||
|         if (this.refs.messagePanel && this.refs.messagePanel.isAtBottom()) { | ||||
|         if (this._messagePanel.current && this._messagePanel.current.isAtBottom()) { | ||||
|             this._loadTimeline(); | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     canResetTimeline: function() { | ||||
|         return this.refs.messagePanel && this.refs.messagePanel.isAtBottom(); | ||||
|         return this._messagePanel.current && this._messagePanel.current.isAtBottom(); | ||||
|     }, | ||||
| 
 | ||||
|     onRoomRedaction: function(ev, room) { | ||||
|  | @ -629,7 +631,7 @@ const TimelinePanel = createReactClass({ | |||
|     sendReadReceipt: function() { | ||||
|         if (SettingsStore.getValue("lowBandwidth")) return; | ||||
| 
 | ||||
|         if (!this.refs.messagePanel) return; | ||||
|         if (!this._messagePanel.current) return; | ||||
|         if (!this.props.manageReadReceipts) return; | ||||
|         // This happens on user_activity_end which is delayed, and it's
 | ||||
|         // very possible have logged out within that timeframe, so check
 | ||||
|  | @ -815,8 +817,8 @@ const TimelinePanel = createReactClass({ | |||
|         if (this._timelineWindow.canPaginate(EventTimeline.FORWARDS)) { | ||||
|             this._loadTimeline(); | ||||
|         } else { | ||||
|             if (this.refs.messagePanel) { | ||||
|                 this.refs.messagePanel.scrollToBottom(); | ||||
|             if (this._messagePanel.current) { | ||||
|                 this._messagePanel.current.scrollToBottom(); | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|  | @ -826,7 +828,7 @@ const TimelinePanel = createReactClass({ | |||
|      */ | ||||
|     jumpToReadMarker: function() { | ||||
|         if (!this.props.manageReadMarkers) return; | ||||
|         if (!this.refs.messagePanel) return; | ||||
|         if (!this._messagePanel.current) return; | ||||
|         if (!this.state.readMarkerEventId) return; | ||||
| 
 | ||||
|         // we may not have loaded the event corresponding to the read-marker
 | ||||
|  | @ -835,11 +837,11 @@ const TimelinePanel = createReactClass({ | |||
|         //
 | ||||
|         // a quick way to figure out if we've loaded the relevant event is
 | ||||
|         // simply to check if the messagepanel knows where the read-marker is.
 | ||||
|         const ret = this.refs.messagePanel.getReadMarkerPosition(); | ||||
|         const ret = this._messagePanel.current.getReadMarkerPosition(); | ||||
|         if (ret !== null) { | ||||
|             // The messagepanel knows where the RM is, so we must have loaded
 | ||||
|             // the relevant event.
 | ||||
|             this.refs.messagePanel.scrollToEvent(this.state.readMarkerEventId, | ||||
|             this._messagePanel.current.scrollToEvent(this.state.readMarkerEventId, | ||||
|                                                  0, 1/3); | ||||
|             return; | ||||
|         } | ||||
|  | @ -874,8 +876,8 @@ const TimelinePanel = createReactClass({ | |||
|      * at the end of the live timeline. | ||||
|      */ | ||||
|     isAtEndOfLiveTimeline: function() { | ||||
|         return this.refs.messagePanel | ||||
|             && this.refs.messagePanel.isAtBottom() | ||||
|         return this._messagePanel.current | ||||
|             && this._messagePanel.current.isAtBottom() | ||||
|             && this._timelineWindow | ||||
|             && !this._timelineWindow.canPaginate(EventTimeline.FORWARDS); | ||||
|     }, | ||||
|  | @ -887,8 +889,8 @@ const TimelinePanel = createReactClass({ | |||
|      * returns null if we are not mounted. | ||||
|      */ | ||||
|     getScrollState: function() { | ||||
|         if (!this.refs.messagePanel) { return null; } | ||||
|         return this.refs.messagePanel.getScrollState(); | ||||
|         if (!this._messagePanel.current) { return null; } | ||||
|         return this._messagePanel.current.getScrollState(); | ||||
|     }, | ||||
| 
 | ||||
|     // returns one of:
 | ||||
|  | @ -899,9 +901,9 @@ const TimelinePanel = createReactClass({ | |||
|     //  +1: read marker is below the window
 | ||||
|     getReadMarkerPosition: function() { | ||||
|         if (!this.props.manageReadMarkers) return null; | ||||
|         if (!this.refs.messagePanel) return null; | ||||
|         if (!this._messagePanel.current) return null; | ||||
| 
 | ||||
|         const ret = this.refs.messagePanel.getReadMarkerPosition(); | ||||
|         const ret = this._messagePanel.current.getReadMarkerPosition(); | ||||
|         if (ret !== null) { | ||||
|             return ret; | ||||
|         } | ||||
|  | @ -936,7 +938,7 @@ const TimelinePanel = createReactClass({ | |||
|      * We pass it down to the scroll panel. | ||||
|      */ | ||||
|     handleScrollKey: function(ev) { | ||||
|         if (!this.refs.messagePanel) { return; } | ||||
|         if (!this._messagePanel.current) { return; } | ||||
| 
 | ||||
|         // jump to the live timeline on ctrl-end, rather than the end of the
 | ||||
|         // timeline window.
 | ||||
|  | @ -944,7 +946,7 @@ const TimelinePanel = createReactClass({ | |||
|             ev.keyCode == KeyCode.END) { | ||||
|             this.jumpToLiveTimeline(); | ||||
|         } else { | ||||
|             this.refs.messagePanel.handleScrollKey(ev); | ||||
|             this._messagePanel.current.handleScrollKey(ev); | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|  | @ -986,8 +988,8 @@ const TimelinePanel = createReactClass({ | |||
|         const onLoaded = () => { | ||||
|             // clear the timeline min-height when
 | ||||
|             // (re)loading the timeline
 | ||||
|             if (this.refs.messagePanel) { | ||||
|                 this.refs.messagePanel.onTimelineReset(); | ||||
|             if (this._messagePanel.current) { | ||||
|                 this._messagePanel.current.onTimelineReset(); | ||||
|             } | ||||
|             this._reloadEvents(); | ||||
| 
 | ||||
|  | @ -1002,7 +1004,7 @@ const TimelinePanel = createReactClass({ | |||
|                 timelineLoading: false, | ||||
|             }, () => { | ||||
|                 // initialise the scroll state of the message panel
 | ||||
|                 if (!this.refs.messagePanel) { | ||||
|                 if (!this._messagePanel.current) { | ||||
|                     // this shouldn't happen - we know we're mounted because
 | ||||
|                     // we're in a setState callback, and we know
 | ||||
|                     // timelineLoading is now false, so render() should have
 | ||||
|  | @ -1012,10 +1014,10 @@ const TimelinePanel = createReactClass({ | |||
|                     return; | ||||
|                 } | ||||
|                 if (eventId) { | ||||
|                     this.refs.messagePanel.scrollToEvent(eventId, pixelOffset, | ||||
|                     this._messagePanel.current.scrollToEvent(eventId, pixelOffset, | ||||
|                                                          offsetBase); | ||||
|                 } else { | ||||
|                     this.refs.messagePanel.scrollToBottom(); | ||||
|                     this._messagePanel.current.scrollToBottom(); | ||||
|                 } | ||||
| 
 | ||||
|                 this.sendReadReceipt(); | ||||
|  | @ -1134,7 +1136,7 @@ const TimelinePanel = createReactClass({ | |||
|         const ignoreOwn = opts.ignoreOwn || false; | ||||
|         const allowPartial = opts.allowPartial || false; | ||||
| 
 | ||||
|         const messagePanel = this.refs.messagePanel; | ||||
|         const messagePanel = this._messagePanel.current; | ||||
|         if (messagePanel === undefined) return null; | ||||
| 
 | ||||
|         const EventTile = sdk.getComponent('rooms.EventTile'); | ||||
|  | @ -1313,7 +1315,8 @@ const TimelinePanel = createReactClass({ | |||
|             ['PREPARED', 'CATCHUP'].includes(this.state.clientSyncState) | ||||
|         ); | ||||
|         return ( | ||||
|             <MessagePanel ref="messagePanel" | ||||
|             <MessagePanel | ||||
|                 ref={this._messagePanel} | ||||
|                 room={this.props.timelineSet.room} | ||||
|                 permalinkCreator={this.props.permalinkCreator} | ||||
|                 hidden={this.props.hidden} | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ See the License for the specific language governing permissions and | |||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| import React from 'react'; | ||||
| import React, {createRef} from 'react'; | ||||
| import createReactClass from 'create-react-class'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import { _t } from '../../../languageHandler'; | ||||
|  | @ -48,6 +48,8 @@ module.exports = createReactClass({ | |||
| 
 | ||||
|     componentWillMount: function() { | ||||
|         this._captchaWidgetId = null; | ||||
| 
 | ||||
|         this._recaptchaContainer = createRef(); | ||||
|     }, | ||||
| 
 | ||||
|     componentDidMount: function() { | ||||
|  | @ -67,7 +69,7 @@ module.exports = createReactClass({ | |||
|             scriptTag.setAttribute( | ||||
|                 'src', `${protocol}//www.recaptcha.net/recaptcha/api.js?onload=mx_on_recaptcha_loaded&render=explicit`, | ||||
|             ); | ||||
|             this.refs.recaptchaContainer.appendChild(scriptTag); | ||||
|             this._recaptchaContainer.current.appendChild(scriptTag); | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|  | @ -124,11 +126,11 @@ module.exports = createReactClass({ | |||
|         } | ||||
| 
 | ||||
|         return ( | ||||
|             <div ref="recaptchaContainer"> | ||||
|             <div ref={this._recaptchaContainer}> | ||||
|                 <p>{_t( | ||||
|                     "This homeserver would like to make sure you are not a robot.", | ||||
|                 )}</p> | ||||
|                 <div id={DIV_ID}></div> | ||||
|                 <div id={DIV_ID} /> | ||||
|                 { error } | ||||
|             </div> | ||||
|         ); | ||||
|  |  | |||
|  | @ -16,7 +16,7 @@ See the License for the specific language governing permissions and | |||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| import React from 'react'; | ||||
| import React, {createRef} from 'react'; | ||||
| import createReactClass from 'create-react-class'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import url from 'url'; | ||||
|  | @ -581,6 +581,8 @@ export const FallbackAuthEntry = createReactClass({ | |||
|         // the popup if we open it immediately.
 | ||||
|         this._popupWindow = null; | ||||
|         window.addEventListener("message", this._onReceiveMessage); | ||||
| 
 | ||||
|         this._fallbackButton = createRef(); | ||||
|     }, | ||||
| 
 | ||||
|     componentWillUnmount: function() { | ||||
|  | @ -591,8 +593,8 @@ export const FallbackAuthEntry = createReactClass({ | |||
|     }, | ||||
| 
 | ||||
|     focus: function() { | ||||
|         if (this.refs.fallbackButton) { | ||||
|             this.refs.fallbackButton.focus(); | ||||
|         if (this._fallbackButton.current) { | ||||
|             this._fallbackButton.current.focus(); | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|  | @ -624,7 +626,7 @@ export const FallbackAuthEntry = createReactClass({ | |||
|         } | ||||
|         return ( | ||||
|             <div> | ||||
|                 <a ref="fallbackButton" onClick={this._onShowFallbackClick}>{ _t("Start authentication") }</a> | ||||
|                 <a ref={this._fallbackButton} onClick={this._onShowFallbackClick}>{ _t("Start authentication") }</a> | ||||
|                 {errorSection} | ||||
|             </div> | ||||
|         ); | ||||
|  |  | |||
|  | @ -17,7 +17,7 @@ See the License for the specific language governing permissions and | |||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| import React from 'react'; | ||||
| import React, {createRef} from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import createReactClass from 'create-react-class'; | ||||
| 
 | ||||
|  | @ -106,10 +106,14 @@ module.exports = createReactClass({ | |||
|         }; | ||||
|     }, | ||||
| 
 | ||||
|     UNSAFE_componentWillMount: function() { | ||||
|         this._textinput = createRef(); | ||||
|     }, | ||||
| 
 | ||||
|     componentDidMount: function() { | ||||
|         if (this.props.focus) { | ||||
|             // Set the cursor at the end of the text input
 | ||||
|             this.refs.textinput.value = this.props.value; | ||||
|             this._textinput.current.value = this.props.value; | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|  | @ -126,8 +130,8 @@ module.exports = createReactClass({ | |||
|         let selectedList = this.state.selectedList.slice(); | ||||
|         // Check the text input field to see if user has an unconverted address
 | ||||
|         // If there is and it's valid add it to the local selectedList
 | ||||
|         if (this.refs.textinput.value !== '') { | ||||
|             selectedList = this._addAddressesToList([this.refs.textinput.value]); | ||||
|         if (this._textinput.current.value !== '') { | ||||
|             selectedList = this._addAddressesToList([this._textinput.current.value]); | ||||
|             if (selectedList === null) return; | ||||
|         } | ||||
|         this.props.onFinished(true, selectedList); | ||||
|  | @ -154,23 +158,23 @@ module.exports = createReactClass({ | |||
|             e.stopPropagation(); | ||||
|             e.preventDefault(); | ||||
|             if (this.addressSelector) this.addressSelector.chooseSelection(); | ||||
|         } else if (this.refs.textinput.value.length === 0 && this.state.selectedList.length && e.keyCode === 8) { // backspace
 | ||||
|         } else if (this._textinput.current.value.length === 0 && this.state.selectedList.length && e.keyCode === 8) { // backspace
 | ||||
|             e.stopPropagation(); | ||||
|             e.preventDefault(); | ||||
|             this.onDismissed(this.state.selectedList.length - 1)(); | ||||
|         } else if (e.keyCode === 13) { // enter
 | ||||
|             e.stopPropagation(); | ||||
|             e.preventDefault(); | ||||
|             if (this.refs.textinput.value === '') { | ||||
|             if (this._textinput.current.value === '') { | ||||
|                 // if there's nothing in the input box, submit the form
 | ||||
|                 this.onButtonClick(); | ||||
|             } else { | ||||
|                 this._addAddressesToList([this.refs.textinput.value]); | ||||
|                 this._addAddressesToList([this._textinput.current.value]); | ||||
|             } | ||||
|         } else if (e.keyCode === 188 || e.keyCode === 9) { // comma or tab
 | ||||
|             e.stopPropagation(); | ||||
|             e.preventDefault(); | ||||
|             this._addAddressesToList([this.refs.textinput.value]); | ||||
|             this._addAddressesToList([this._textinput.current.value]); | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|  | @ -647,7 +651,7 @@ module.exports = createReactClass({ | |||
|                 onPaste={this._onPaste} | ||||
|                 rows="1" | ||||
|                 id="textinput" | ||||
|                 ref="textinput" | ||||
|                 ref={this._textinput} | ||||
|                 className="mx_AddressPickerDialog_input" | ||||
|                 onChange={this.onQueryChanged} | ||||
|                 placeholder={this.getPlaceholder()} | ||||
|  |  | |||
|  | @ -15,7 +15,7 @@ See the License for the specific language governing permissions and | |||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| import React from 'react'; | ||||
| import React, {createRef} from 'react'; | ||||
| import createReactClass from 'create-react-class'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import sdk from '../../../index'; | ||||
|  | @ -62,8 +62,13 @@ export default createReactClass({ | |||
|         }; | ||||
|     }, | ||||
| 
 | ||||
|     UNSAFE_componentWillMount: function() { | ||||
|         this._input_value = createRef(); | ||||
|         this._uiAuth = createRef(); | ||||
|     }, | ||||
| 
 | ||||
|     componentDidMount: function() { | ||||
|         this.refs.input_value.select(); | ||||
|         this._input_value.current.select(); | ||||
| 
 | ||||
|         this._matrixClient = MatrixClientPeg.get(); | ||||
|     }, | ||||
|  | @ -102,8 +107,8 @@ export default createReactClass({ | |||
|     }, | ||||
| 
 | ||||
|     onSubmit: function(ev) { | ||||
|         if (this.refs.uiAuth) { | ||||
|             this.refs.uiAuth.tryContinue(); | ||||
|         if (this._uiAuth.current) { | ||||
|             this._uiAuth.current.tryContinue(); | ||||
|         } | ||||
|         this.setState({ | ||||
|             doingUIAuth: true, | ||||
|  | @ -215,7 +220,7 @@ export default createReactClass({ | |||
|                 onAuthFinished={this._onUIAuthFinished} | ||||
|                 inputs={{}} | ||||
|                 poll={true} | ||||
|                 ref="uiAuth" | ||||
|                 ref={this._uiAuth} | ||||
|                 continueIsManaged={true} | ||||
|             />; | ||||
|         } | ||||
|  | @ -257,7 +262,7 @@ export default createReactClass({ | |||
|             > | ||||
|                 <div className="mx_Dialog_content" id='mx_Dialog_content'> | ||||
|                     <div className="mx_SetMxIdDialog_input_group"> | ||||
|                         <input type="text" ref="input_value" value={this.state.username} | ||||
|                         <input type="text" ref={this._input_value} value={this.state.username} | ||||
|                             autoFocus={true} | ||||
|                             onChange={this.onValueChange} | ||||
|                             onKeyUp={this.onKeyUp} | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ See the License for the specific language governing permissions and | |||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| import React from 'react'; | ||||
| import React, {createRef} from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import {Room, User, Group, RoomMember, MatrixEvent} from 'matrix-js-sdk'; | ||||
| import sdk from '../../../index'; | ||||
|  | @ -74,6 +74,8 @@ export default class ShareDialog extends React.Component { | |||
|             // MatrixEvent defaults to share linkSpecificEvent
 | ||||
|             linkSpecificEvent: this.props.target instanceof MatrixEvent, | ||||
|         }; | ||||
| 
 | ||||
|         this._link = createRef(); | ||||
|     } | ||||
| 
 | ||||
|     static _selectText(target) { | ||||
|  | @ -94,7 +96,7 @@ export default class ShareDialog extends React.Component { | |||
|     onCopyClick(e) { | ||||
|         e.preventDefault(); | ||||
| 
 | ||||
|         ShareDialog._selectText(this.refs.link); | ||||
|         ShareDialog._selectText(this._link.current); | ||||
| 
 | ||||
|         let successful; | ||||
|         try { | ||||
|  | @ -195,7 +197,7 @@ export default class ShareDialog extends React.Component { | |||
|         > | ||||
|             <div className="mx_ShareDialog_content"> | ||||
|                 <div className="mx_ShareDialog_matrixto"> | ||||
|                     <a ref="link" | ||||
|                     <a ref={this._link} | ||||
|                        href={matrixToUrl} | ||||
|                        onClick={ShareDialog.onLinkClick} | ||||
|                        className="mx_ShareDialog_matrixto_link" | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ See the License for the specific language governing permissions and | |||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| import React from 'react'; | ||||
| import React, {createRef} from 'react'; | ||||
| import createReactClass from 'create-react-class'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import sdk from '../../../index'; | ||||
|  | @ -42,15 +42,19 @@ export default createReactClass({ | |||
|         }; | ||||
|     }, | ||||
| 
 | ||||
|     UNSAFE_componentWillMount: function() { | ||||
|         this._textinput = createRef(); | ||||
|     }, | ||||
| 
 | ||||
|     componentDidMount: function() { | ||||
|         if (this.props.focus) { | ||||
|             // Set the cursor at the end of the text input
 | ||||
|             this.refs.textinput.value = this.props.value; | ||||
|             this._textinput.current.value = this.props.value; | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     onOk: function() { | ||||
|         this.props.onFinished(true, this.refs.textinput.value); | ||||
|         this.props.onFinished(true, this._textinput.current.value); | ||||
|     }, | ||||
| 
 | ||||
|     onCancel: function() { | ||||
|  | @ -70,7 +74,13 @@ export default createReactClass({ | |||
|                             <label htmlFor="textinput"> { this.props.description } </label> | ||||
|                         </div> | ||||
|                         <div> | ||||
|                             <input id="textinput" ref="textinput" className="mx_TextInputDialog_input" defaultValue={this.props.value} autoFocus={this.props.focus} size="64" /> | ||||
|                             <input | ||||
|                                 id="textinput" | ||||
|                                 ref={this._textinput} | ||||
|                                 className="mx_TextInputDialog_input" | ||||
|                                 defaultValue={this.props.value} | ||||
|                                 autoFocus={this.props.focus} | ||||
|                                 size="64" /> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 </form> | ||||
|  |  | |||
|  | @ -64,6 +64,8 @@ export default class AppTile extends React.Component { | |||
|         this._onReloadWidgetClick = this._onReloadWidgetClick.bind(this); | ||||
| 
 | ||||
|         this._contextMenuButton = createRef(); | ||||
|         this._appFrame = createRef(); | ||||
|         this._menu_bar = createRef(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | @ -337,14 +339,14 @@ export default class AppTile extends React.Component { | |||
|                     // HACK: This is a really dirty way to ensure that Jitsi cleans up
 | ||||
|                     // its hold on the webcam. Without this, the widget holds a media
 | ||||
|                     // stream open, even after death. See https://github.com/vector-im/riot-web/issues/7351
 | ||||
|                     if (this.refs.appFrame) { | ||||
|                     if (this._appFrame.current) { | ||||
|                         // In practice we could just do `+= ''` to trick the browser
 | ||||
|                         // into thinking the URL changed, however I can foresee this
 | ||||
|                         // being optimized out by a browser. Instead, we'll just point
 | ||||
|                         // the iframe at a page that is reasonably safe to use in the
 | ||||
|                         // event the iframe doesn't wink away.
 | ||||
|                         // This is relative to where the Riot instance is located.
 | ||||
|                         this.refs.appFrame.src = 'about:blank'; | ||||
|                         this._appFrame.current.src = 'about:blank'; | ||||
|                     } | ||||
| 
 | ||||
|                     WidgetUtils.setRoomWidget( | ||||
|  | @ -389,7 +391,7 @@ export default class AppTile extends React.Component { | |||
|         // FIXME: There's probably no reason to do this here: it should probably be done entirely
 | ||||
|         // in ActiveWidgetStore.
 | ||||
|         const widgetMessaging = new WidgetMessaging( | ||||
|             this.props.id, this.props.url, this.props.userWidget, this.refs.appFrame.contentWindow); | ||||
|             this.props.id, this.props.url, this.props.userWidget, this._appFrame.current.contentWindow); | ||||
|         ActiveWidgetStore.setWidgetMessaging(this.props.id, widgetMessaging); | ||||
|         widgetMessaging.getCapabilities().then((requestedCapabilities) => { | ||||
|             console.log(`Widget ${this.props.id} requested capabilities: ` + requestedCapabilities); | ||||
|  | @ -496,7 +498,7 @@ export default class AppTile extends React.Component { | |||
|         ev.preventDefault(); | ||||
| 
 | ||||
|         // Ignore clicks on menu bar children
 | ||||
|         if (ev.target !== this.refs.menu_bar) { | ||||
|         if (ev.target !== this._menu_bar.current) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|  | @ -555,7 +557,7 @@ export default class AppTile extends React.Component { | |||
| 
 | ||||
|     _onReloadWidgetClick() { | ||||
|         // Reload iframe in this way to avoid cross-origin restrictions
 | ||||
|         this.refs.appFrame.src = this.refs.appFrame.src; | ||||
|         this._appFrame.current.src = this._appFrame.current.src; | ||||
|     } | ||||
| 
 | ||||
|     _onContextMenuClick = () => { | ||||
|  | @ -626,7 +628,7 @@ export default class AppTile extends React.Component { | |||
|                             { this.state.loading && loadingElement } | ||||
|                             <iframe | ||||
|                                 allow={iframeFeatures} | ||||
|                                 ref="appFrame" | ||||
|                                 ref={this._appFrame} | ||||
|                                 src={this._getSafeUrl()} | ||||
|                                 allowFullScreen={true} | ||||
|                                 sandbox={sandboxFlags} | ||||
|  | @ -694,7 +696,7 @@ export default class AppTile extends React.Component { | |||
|         return <React.Fragment> | ||||
|             <div className={appTileClass} id={this.props.id}> | ||||
|                 { this.props.showMenubar && | ||||
|                 <div ref="menu_bar" className={menuBarClasses} onClick={this.onClickMenuBar}> | ||||
|                 <div ref={this._menu_bar} className={menuBarClasses} onClick={this.onClickMenuBar}> | ||||
|                     <span className="mx_AppTileMenuBarTitle" style={{pointerEvents: (this.props.handleMinimisePointerEvents ? 'all' : false)}}> | ||||
|                         { /* Minimise widget */ } | ||||
|                         { showMinimiseButton && <AccessibleButton | ||||
|  |  | |||
|  | @ -15,7 +15,7 @@ See the License for the specific language governing permissions and | |||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| import React from 'react'; | ||||
| import React, {createRef} from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import createReactClass from 'create-react-class'; | ||||
| import {Key} from "../../../Keyboard"; | ||||
|  | @ -65,7 +65,7 @@ module.exports = createReactClass({ | |||
|     componentWillReceiveProps: function(nextProps) { | ||||
|         if (nextProps.initialValue !== this.props.initialValue) { | ||||
|             this.value = nextProps.initialValue; | ||||
|             if (this.refs.editable_div) { | ||||
|             if (this._editable_div.current) { | ||||
|                 this.showPlaceholder(!this.value); | ||||
|             } | ||||
|         } | ||||
|  | @ -76,24 +76,27 @@ module.exports = createReactClass({ | |||
|         // as React doesn't play nice with contentEditable.
 | ||||
|         this.value = ''; | ||||
|         this.placeholder = false; | ||||
| 
 | ||||
|         this._editable_div = createRef(); | ||||
|     }, | ||||
| 
 | ||||
|     componentDidMount: function() { | ||||
|         this.value = this.props.initialValue; | ||||
|         if (this.refs.editable_div) { | ||||
|         if (this._editable_div.current) { | ||||
|             this.showPlaceholder(!this.value); | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     showPlaceholder: function(show) { | ||||
|         if (show) { | ||||
|             this.refs.editable_div.textContent = this.props.placeholder; | ||||
|             this.refs.editable_div.setAttribute("class", this.props.className + " " + this.props.placeholderClassName); | ||||
|             this._editable_div.current.textContent = this.props.placeholder; | ||||
|             this._editable_div.current.setAttribute("class", this.props.className | ||||
|                 + " " + this.props.placeholderClassName); | ||||
|             this.placeholder = true; | ||||
|             this.value = ''; | ||||
|         } else { | ||||
|             this.refs.editable_div.textContent = this.value; | ||||
|             this.refs.editable_div.setAttribute("class", this.props.className); | ||||
|             this._editable_div.current.textContent = this.value; | ||||
|             this._editable_div.current.setAttribute("class", this.props.className); | ||||
|             this.placeholder = false; | ||||
|         } | ||||
|     }, | ||||
|  | @ -120,7 +123,7 @@ module.exports = createReactClass({ | |||
|         this.value = this.props.initialValue; | ||||
|         this.showPlaceholder(!this.value); | ||||
|         this.onValueChanged(false); | ||||
|         this.refs.editable_div.blur(); | ||||
|         this._editable_div.current.blur(); | ||||
|     }, | ||||
| 
 | ||||
|     onValueChanged: function(shouldSubmit) { | ||||
|  | @ -219,7 +222,7 @@ module.exports = createReactClass({ | |||
|             </div>; | ||||
|         } else { | ||||
|             // show the content editable div, but manually manage its contents as react and contentEditable don't play nice together
 | ||||
|             editableEl = <div ref="editable_div" | ||||
|             editableEl = <div ref={this._editable_div} | ||||
|                               contentEditable={true} | ||||
|                               className={className} | ||||
|                               onKeyDown={this.onKeyDown} | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ See the License for the specific language governing permissions and | |||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| import React from 'react'; | ||||
| import React, {createRef} from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import createReactClass from 'create-react-class'; | ||||
| import { _t } from '../../../languageHandler'; | ||||
|  | @ -34,6 +34,10 @@ module.exports = createReactClass({ | |||
|         }; | ||||
|     }, | ||||
| 
 | ||||
|     UNSAFE_componentWillMount: function() { | ||||
|         this._user_id_input = createRef(); | ||||
|     }, | ||||
| 
 | ||||
|     addUser: function(user_id) { | ||||
|         if (this.props.selected_users.indexOf(user_id == -1)) { | ||||
|             this.props.onChange(this.props.selected_users.concat([user_id])); | ||||
|  | @ -47,20 +51,20 @@ module.exports = createReactClass({ | |||
|     }, | ||||
| 
 | ||||
|     onAddUserId: function() { | ||||
|         this.addUser(this.refs.user_id_input.value); | ||||
|         this.refs.user_id_input.value = ""; | ||||
|         this.addUser(this._user_id_input.current.value); | ||||
|         this._user_id_input.current.value = ""; | ||||
|     }, | ||||
| 
 | ||||
|     render: function() { | ||||
|         const self = this; | ||||
|         return ( | ||||
|             <div> | ||||
|                 <ul className="mx_UserSelector_UserIdList" ref="list"> | ||||
|                 <ul className="mx_UserSelector_UserIdList"> | ||||
|                     { this.props.selected_users.map(function(user_id, i) { | ||||
|                         return <li key={user_id}>{ user_id } - <span onClick={function() {self.removeUser(user_id);}}>X</span></li>; | ||||
|                     }) } | ||||
|                 </ul> | ||||
|                 <input type="text" ref="user_id_input" defaultValue="" className="mx_UserSelector_userIdInput" placeholder={_t("ex. @bob:example.com")} /> | ||||
|                 <input type="text" ref={this._user_id_input} defaultValue="" className="mx_UserSelector_userIdInput" placeholder={_t("ex. @bob:example.com")} /> | ||||
|                 <button onClick={this.onAddUserId} className="mx_UserSelector_AddUserId"> | ||||
|                     { _t("Add User") } | ||||
|                 </button> | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ See the License for the specific language governing permissions and | |||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| import React from 'react'; | ||||
| import React, {createRef} from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import * as HtmlUtils from '../../../HtmlUtils'; | ||||
| import { editBodyDiffToHtml } from '../../../utils/MessageDiffUtils'; | ||||
|  | @ -51,6 +51,8 @@ export default class EditHistoryMessage extends React.PureComponent { | |||
|         } | ||||
|         const canRedact = room.currentState.maySendRedactionForEvent(event, userId); | ||||
|         this.state = {canRedact, sendStatus: event.getAssociatedStatus()}; | ||||
| 
 | ||||
|         this._content = createRef(); | ||||
|     } | ||||
| 
 | ||||
|     _onAssociatedStatusChanged = () => { | ||||
|  | @ -78,8 +80,8 @@ export default class EditHistoryMessage extends React.PureComponent { | |||
| 
 | ||||
|     pillifyLinks() { | ||||
|         // not present for redacted events
 | ||||
|         if (this.refs.content) { | ||||
|             pillifyLinks(this.refs.content.children, this.props.mxEvent); | ||||
|         if (this._content.current) { | ||||
|             pillifyLinks(this._content.current.children, this.props.mxEvent); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -140,13 +142,13 @@ export default class EditHistoryMessage extends React.PureComponent { | |||
|             if (mxEvent.getContent().msgtype === "m.emote") { | ||||
|                 const name = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender(); | ||||
|                 contentContainer = ( | ||||
|                     <div className="mx_EventTile_content" ref="content">*  | ||||
|                     <div className="mx_EventTile_content" ref={this._content}>*  | ||||
|                         <span className="mx_MEmoteBody_sender">{ name }</span> | ||||
|                          {contentElements} | ||||
|                     </div> | ||||
|                 ); | ||||
|             } else { | ||||
|                 contentContainer = <div className="mx_EventTile_content" ref="content">{contentElements}</div>; | ||||
|                 contentContainer = <div className="mx_EventTile_content" ref={this._content}>{contentElements}</div>; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|  |  | |||
|  | @ -80,7 +80,7 @@ export default class MAudioBody extends React.Component { | |||
| 
 | ||||
|         if (this.state.error !== null) { | ||||
|             return ( | ||||
|                 <span className="mx_MAudioBody" ref="body"> | ||||
|                 <span className="mx_MAudioBody"> | ||||
|                     <img src={require("../../../../res/img/warning.svg")} width="16" height="16" /> | ||||
|                     { _t("Error decrypting audio") } | ||||
|                 </span> | ||||
|  |  | |||
|  | @ -15,7 +15,7 @@ See the License for the specific language governing permissions and | |||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| import React from 'react'; | ||||
| import React, {createRef} from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import createReactClass from 'create-react-class'; | ||||
| import filesize from 'filesize'; | ||||
|  | @ -251,6 +251,12 @@ module.exports = createReactClass({ | |||
|         return MatrixClientPeg.get().mxcUrlToHttp(content.url); | ||||
|     }, | ||||
| 
 | ||||
|     UNSAFE_componentWillMount: function() { | ||||
|         this._iframe = createRef(); | ||||
|         this._dummyLink = createRef(); | ||||
|         this._downloadImage = createRef(); | ||||
|     }, | ||||
| 
 | ||||
|     componentDidMount: function() { | ||||
|         // Add this to the list of mounted components to receive notifications
 | ||||
|         // when the tint changes.
 | ||||
|  | @ -272,17 +278,17 @@ module.exports = createReactClass({ | |||
| 
 | ||||
|     tint: function() { | ||||
|         // Update our tinted copy of require("../../../../res/img/download.svg")
 | ||||
|         if (this.refs.downloadImage) { | ||||
|             this.refs.downloadImage.src = tintedDownloadImageURL; | ||||
|         if (this._downloadImage.current) { | ||||
|             this._downloadImage.current.src = tintedDownloadImageURL; | ||||
|         } | ||||
|         if (this.refs.iframe) { | ||||
|         if (this._iframe.current) { | ||||
|             // If the attachment is encrypted then the download image
 | ||||
|             // will be inside the iframe so we wont be able to update
 | ||||
|             // it directly.
 | ||||
|             this.refs.iframe.contentWindow.postMessage({ | ||||
|             this._iframe.current.contentWindow.postMessage({ | ||||
|                 code: remoteSetTint.toString(), | ||||
|                 imgSrc: tintedDownloadImageURL, | ||||
|                 style: computedStyle(this.refs.dummyLink), | ||||
|                 style: computedStyle(this._dummyLink.current), | ||||
|             }, "*"); | ||||
|         } | ||||
|     }, | ||||
|  | @ -325,7 +331,7 @@ module.exports = createReactClass({ | |||
|                 }; | ||||
| 
 | ||||
|                 return ( | ||||
|                     <span className="mx_MFileBody" ref="body"> | ||||
|                     <span className="mx_MFileBody"> | ||||
|                         <div className="mx_MFileBody_download"> | ||||
|                             <a href="javascript:void(0)" onClick={decrypt}> | ||||
|                                 { _t("Decrypt %(text)s", { text: text }) } | ||||
|  | @ -340,7 +346,7 @@ module.exports = createReactClass({ | |||
|                 ev.target.contentWindow.postMessage({ | ||||
|                     code: remoteRender.toString(), | ||||
|                     imgSrc: tintedDownloadImageURL, | ||||
|                     style: computedStyle(this.refs.dummyLink), | ||||
|                     style: computedStyle(this._dummyLink.current), | ||||
|                     blob: this.state.decryptedBlob, | ||||
|                     // Set a download attribute for encrypted files so that the file
 | ||||
|                     // will have the correct name when the user tries to download it.
 | ||||
|  | @ -367,9 +373,9 @@ module.exports = createReactClass({ | |||
|                               * We'll use it to learn how the download link | ||||
|                               * would have been styled if it was rendered inline. | ||||
|                               */ } | ||||
|                             <a ref="dummyLink" /> | ||||
|                             <a ref={this._dummyLink} /> | ||||
|                         </div> | ||||
|                         <iframe src={renderer_url} onLoad={onIframeLoad} ref="iframe" /> | ||||
|                         <iframe src={renderer_url} onLoad={onIframeLoad} ref={this._iframe} /> | ||||
|                     </div> | ||||
|                 </span> | ||||
|             ); | ||||
|  | @ -439,7 +445,7 @@ module.exports = createReactClass({ | |||
|                     <span className="mx_MFileBody"> | ||||
|                         <div className="mx_MFileBody_download"> | ||||
|                             <a {...downloadProps}> | ||||
|                                 <img src={tintedDownloadImageURL} width="12" height="14" ref="downloadImage" /> | ||||
|                                 <img src={tintedDownloadImageURL} width="12" height="14" ref={this._downloadImage} /> | ||||
|                                 { _t("Download %(text)s", { text: text }) } | ||||
|                             </a> | ||||
|                         </div> | ||||
|  |  | |||
|  | @ -16,7 +16,7 @@ See the License for the specific language governing permissions and | |||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| import React from 'react'; | ||||
| import React, {createRef} from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import { MatrixClient } from 'matrix-js-sdk'; | ||||
| 
 | ||||
|  | @ -65,6 +65,8 @@ export default class MImageBody extends React.Component { | |||
|             hover: false, | ||||
|             showImage: SettingsStore.getValue("showImages"), | ||||
|         }; | ||||
| 
 | ||||
|         this._image = createRef(); | ||||
|     } | ||||
| 
 | ||||
|     componentWillMount() { | ||||
|  | @ -158,8 +160,8 @@ export default class MImageBody extends React.Component { | |||
| 
 | ||||
|         let loadedImageDimensions; | ||||
| 
 | ||||
|         if (this.refs.image) { | ||||
|             const { naturalWidth, naturalHeight } = this.refs.image; | ||||
|         if (this._image.current) { | ||||
|             const { naturalWidth, naturalHeight } = this._image.current; | ||||
|             // this is only used as a fallback in case content.info.w/h is missing
 | ||||
|             loadedImageDimensions = { naturalWidth, naturalHeight }; | ||||
|         } | ||||
|  | @ -342,7 +344,7 @@ export default class MImageBody extends React.Component { | |||
|                     imageElement = <HiddenImagePlaceholder />; | ||||
|                 } else { | ||||
|                     imageElement = ( | ||||
|                         <img style={{display: 'none'}} src={thumbUrl} ref="image" | ||||
|                         <img style={{display: 'none'}} src={thumbUrl} ref={this._image} | ||||
|                              alt={content.body} | ||||
|                              onError={this.onImageError} | ||||
|                              onLoad={this.onImageLoad} | ||||
|  | @ -385,7 +387,7 @@ export default class MImageBody extends React.Component { | |||
|             // which has the same width as the timeline
 | ||||
|             // mx_MImageBody_thumbnail resizes img to exactly container size
 | ||||
|             img = ( | ||||
|                 <img className="mx_MImageBody_thumbnail" src={thumbUrl} ref="image" | ||||
|                 <img className="mx_MImageBody_thumbnail" src={thumbUrl} ref={this._image} | ||||
|                      style={{ maxWidth: maxWidth + "px" }} | ||||
|                      alt={content.body} | ||||
|                      onError={this.onImageError} | ||||
|  | @ -459,7 +461,7 @@ export default class MImageBody extends React.Component { | |||
| 
 | ||||
|         if (this.state.error !== null) { | ||||
|             return ( | ||||
|                 <span className="mx_MImageBody" ref="body"> | ||||
|                 <span className="mx_MImageBody"> | ||||
|                     <img src={require("../../../../res/img/warning.svg")} width="16" height="16" /> | ||||
|                     { _t("Error decrypting image") } | ||||
|                 </span> | ||||
|  | @ -477,7 +479,7 @@ export default class MImageBody extends React.Component { | |||
|         const thumbnail = this._messageContent(contentUrl, thumbUrl, content); | ||||
|         const fileBody = this.getFileBody(); | ||||
| 
 | ||||
|         return <span className="mx_MImageBody" ref="body"> | ||||
|         return <span className="mx_MImageBody"> | ||||
|             { thumbnail } | ||||
|             { fileBody } | ||||
|         </span>; | ||||
|  |  | |||
|  | @ -132,7 +132,7 @@ module.exports = createReactClass({ | |||
| 
 | ||||
|         if (this.state.error !== null) { | ||||
|             return ( | ||||
|                 <span className="mx_MVideoBody" ref="body"> | ||||
|                 <span className="mx_MVideoBody"> | ||||
|                     <img src={require("../../../../res/img/warning.svg")} width="16" height="16" /> | ||||
|                     { _t("Error decrypting video") } | ||||
|                 </span> | ||||
|  | @ -144,8 +144,8 @@ module.exports = createReactClass({ | |||
|             // The attachment is decrypted in componentDidMount.
 | ||||
|             // For now add an img tag with a spinner.
 | ||||
|             return ( | ||||
|                 <span className="mx_MVideoBody" ref="body"> | ||||
|                     <div className="mx_MImageBody_thumbnail mx_MImageBody_thumbnail_spinner" ref="image"> | ||||
|                 <span className="mx_MVideoBody"> | ||||
|                     <div className="mx_MImageBody_thumbnail mx_MImageBody_thumbnail_spinner"> | ||||
|                         <img src={require("../../../../res/img/spinner.gif")} alt={content.body} width="16" height="16" /> | ||||
|                     </div> | ||||
|                 </span> | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ See the License for the specific language governing permissions and | |||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| import React from 'react'; | ||||
| import React, {createRef} from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import createReactClass from 'create-react-class'; | ||||
| import sdk from '../../../index'; | ||||
|  | @ -47,8 +47,12 @@ module.exports = createReactClass({ | |||
|         maxImageHeight: PropTypes.number, | ||||
|     }, | ||||
| 
 | ||||
|     UNSAFE_componentWillMount: function() { | ||||
|         this._body = createRef(); | ||||
|     }, | ||||
| 
 | ||||
|     getEventTileOps: function() { | ||||
|         return this.refs.body && this.refs.body.getEventTileOps ? this.refs.body.getEventTileOps() : null; | ||||
|         return this._body.current && this._body.current.getEventTileOps ? this._body.current.getEventTileOps() : null; | ||||
|     }, | ||||
| 
 | ||||
|     onTileUpdate: function() { | ||||
|  | @ -103,7 +107,8 @@ module.exports = createReactClass({ | |||
|         } | ||||
| 
 | ||||
|         return <BodyType | ||||
|             ref="body" mxEvent={this.props.mxEvent} | ||||
|             ref={this._body} | ||||
|             mxEvent={this.props.mxEvent} | ||||
|             highlights={this.props.highlights} | ||||
|             highlightLink={this.props.highlightLink} | ||||
|             showUrlPreview={this.props.showUrlPreview} | ||||
|  |  | |||
|  | @ -16,7 +16,7 @@ See the License for the specific language governing permissions and | |||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| import React from 'react'; | ||||
| import React, {createRef} from 'react'; | ||||
| import ReactDOM from 'react-dom'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import createReactClass from 'create-react-class'; | ||||
|  | @ -86,6 +86,10 @@ module.exports = createReactClass({ | |||
|         return successful; | ||||
|     }, | ||||
| 
 | ||||
|     UNSAFE_componentWillMount: function() { | ||||
|         this._content = createRef(); | ||||
|     }, | ||||
| 
 | ||||
|     componentDidMount: function() { | ||||
|         this._unmounted = false; | ||||
|         if (!this.props.editState) { | ||||
|  | @ -94,13 +98,13 @@ module.exports = createReactClass({ | |||
|     }, | ||||
| 
 | ||||
|     _applyFormatting() { | ||||
|         this.activateSpoilers(this.refs.content.children); | ||||
|         this.activateSpoilers(this._content.current.children); | ||||
| 
 | ||||
|         // pillifyLinks BEFORE linkifyElement because plain room/user URLs in the composer
 | ||||
|         // are still sent as plaintext URLs. If these are ever pillified in the composer,
 | ||||
|         // we should be pillify them here by doing the linkifying BEFORE the pillifying.
 | ||||
|         pillifyLinks(this.refs.content.children, this.props.mxEvent); | ||||
|         HtmlUtils.linkifyElement(this.refs.content); | ||||
|         pillifyLinks(this._content.current.children, this.props.mxEvent); | ||||
|         HtmlUtils.linkifyElement(this._content.current); | ||||
|         this.calculateUrlPreview(); | ||||
| 
 | ||||
|         if (this.props.mxEvent.getContent().format === "org.matrix.custom.html") { | ||||
|  | @ -163,7 +167,7 @@ module.exports = createReactClass({ | |||
|         //console.info("calculateUrlPreview: ShowUrlPreview for %s is %s", this.props.mxEvent.getId(), this.props.showUrlPreview);
 | ||||
| 
 | ||||
|         if (this.props.showUrlPreview) { | ||||
|             let links = this.findLinks(this.refs.content.children); | ||||
|             let links = this.findLinks(this._content.current.children); | ||||
|             if (links.length) { | ||||
|                 // de-dup the links (but preserve ordering)
 | ||||
|                 const seen = new Set(); | ||||
|  | @ -327,7 +331,7 @@ module.exports = createReactClass({ | |||
|             }, | ||||
| 
 | ||||
|             getInnerText: () => { | ||||
|                 return this.refs.content.innerText; | ||||
|                 return this._content.current.innerText; | ||||
|             }, | ||||
|         }; | ||||
|     }, | ||||
|  | @ -457,7 +461,7 @@ module.exports = createReactClass({ | |||
|             case "m.emote": | ||||
|                 const name = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender(); | ||||
|                 return ( | ||||
|                     <span ref="content" className="mx_MEmoteBody mx_EventTile_content"> | ||||
|                     <span ref={this._content} className="mx_MEmoteBody mx_EventTile_content"> | ||||
|                         *  | ||||
|                         <span | ||||
|                             className="mx_MEmoteBody_sender" | ||||
|  | @ -472,14 +476,14 @@ module.exports = createReactClass({ | |||
|                 ); | ||||
|             case "m.notice": | ||||
|                 return ( | ||||
|                     <span ref="content" className="mx_MNoticeBody mx_EventTile_content"> | ||||
|                     <span ref={this._content} className="mx_MNoticeBody mx_EventTile_content"> | ||||
|                         { body } | ||||
|                         { widgets } | ||||
|                     </span> | ||||
|                 ); | ||||
|             default: // including "m.text"
 | ||||
|                 return ( | ||||
|                     <span ref="content" className="mx_MTextBody mx_EventTile_content"> | ||||
|                     <span ref={this._content} className="mx_MTextBody mx_EventTile_content"> | ||||
|                         { body } | ||||
|                         { widgets } | ||||
|                     </span> | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ See the License for the specific language governing permissions and | |||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| import React from 'react'; | ||||
| import React, {createRef} from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import {_t} from "../../../languageHandler"; | ||||
| import MatrixClientPeg from "../../../MatrixClientPeg"; | ||||
|  | @ -58,13 +58,15 @@ export default class RoomProfileSettings extends React.Component { | |||
|             canSetTopic: room.currentState.maySendStateEvent('m.room.topic', client.getUserId()), | ||||
|             canSetAvatar: room.currentState.maySendStateEvent('m.room.avatar', client.getUserId()), | ||||
|         }; | ||||
| 
 | ||||
|         this._avatarUpload = createRef(); | ||||
|     } | ||||
| 
 | ||||
|     _uploadAvatar = (e) => { | ||||
|         e.stopPropagation(); | ||||
|         e.preventDefault(); | ||||
| 
 | ||||
|         this.refs.avatarUpload.click(); | ||||
|         this._avatarUpload.current.click(); | ||||
|     }; | ||||
| 
 | ||||
|     _saveProfile = async (e) => { | ||||
|  | @ -178,7 +180,7 @@ export default class RoomProfileSettings extends React.Component { | |||
| 
 | ||||
|         return ( | ||||
|             <form onSubmit={this._saveProfile} autoComplete="off" noValidate={true}> | ||||
|                 <input type="file" ref="avatarUpload" className="mx_ProfileSettings_avatarUpload" | ||||
|                 <input type="file" ref={this._avatarUpload} className="mx_ProfileSettings_avatarUpload" | ||||
|                        onChange={this._onAvatarChanged} accept="image/*" /> | ||||
|                 <div className="mx_ProfileSettings_profile"> | ||||
|                     <div className="mx_ProfileSettings_controls"> | ||||
|  |  | |||
|  | @ -188,14 +188,15 @@ module.exports = createReactClass({ | |||
|         } | ||||
| 
 | ||||
|         const callView = ( | ||||
|             <CallView ref="callView" room={this.props.room} | ||||
|             <CallView | ||||
|                 room={this.props.room} | ||||
|                 ConferenceHandler={this.props.conferenceHandler} | ||||
|                 onResize={this.props.onResize} | ||||
|                 maxVideoHeight={this.props.maxHeight} | ||||
|             /> | ||||
|         ); | ||||
| 
 | ||||
|         const appsDrawer = <AppsDrawer ref="appsDrawer" | ||||
|         const appsDrawer = <AppsDrawer | ||||
|             room={this.props.room} | ||||
|             userId={this.props.userId} | ||||
|             maxHeight={this.props.maxHeight} | ||||
|  |  | |||
|  | @ -19,7 +19,7 @@ limitations under the License. | |||
| 
 | ||||
| import ReplyThread from "../elements/ReplyThread"; | ||||
| 
 | ||||
| import React from 'react'; | ||||
| import React, {createRef} from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import createReactClass from 'create-react-class'; | ||||
| const classNames = require("classnames"); | ||||
|  | @ -224,6 +224,9 @@ module.exports = createReactClass({ | |||
|         // don't do RR animations until we are mounted
 | ||||
|         this._suppressReadReceiptAnimation = true; | ||||
|         this._verifyEvent(this.props.mxEvent); | ||||
| 
 | ||||
|         this._tile = createRef(); | ||||
|         this._replyThread = createRef(); | ||||
|     }, | ||||
| 
 | ||||
|     componentDidMount: function() { | ||||
|  | @ -512,11 +515,11 @@ module.exports = createReactClass({ | |||
|     }, | ||||
| 
 | ||||
|     getTile() { | ||||
|         return this.refs.tile; | ||||
|         return this._tile.current; | ||||
|     }, | ||||
| 
 | ||||
|     getReplyThread() { | ||||
|         return this.refs.replyThread; | ||||
|         return this._replyThread.current; | ||||
|     }, | ||||
| 
 | ||||
|     getReactions() { | ||||
|  | @ -748,7 +751,7 @@ module.exports = createReactClass({ | |||
|                             </a> | ||||
|                         </div> | ||||
|                         <div className="mx_EventTile_line"> | ||||
|                             <EventTileType ref="tile" | ||||
|                             <EventTileType ref={this._tile} | ||||
|                                            mxEvent={this.props.mxEvent} | ||||
|                                            highlights={this.props.highlights} | ||||
|                                            highlightLink={this.props.highlightLink} | ||||
|  | @ -762,7 +765,7 @@ module.exports = createReactClass({ | |||
|                 return ( | ||||
|                     <div className={classes}> | ||||
|                         <div className="mx_EventTile_line"> | ||||
|                             <EventTileType ref="tile" | ||||
|                             <EventTileType ref={this._tile} | ||||
|                                            mxEvent={this.props.mxEvent} | ||||
|                                            highlights={this.props.highlights} | ||||
|                                            highlightLink={this.props.highlightLink} | ||||
|  | @ -792,7 +795,7 @@ module.exports = createReactClass({ | |||
|                         this.props.mxEvent, | ||||
|                         this.props.onHeightChanged, | ||||
|                         this.props.permalinkCreator, | ||||
|                         'replyThread', | ||||
|                         this._replyThread, | ||||
|                     ); | ||||
|                 } | ||||
|                 return ( | ||||
|  | @ -805,7 +808,7 @@ module.exports = createReactClass({ | |||
|                             </a> | ||||
|                             { !isBubbleMessage && this._renderE2EPadlock() } | ||||
|                             { thread } | ||||
|                             <EventTileType ref="tile" | ||||
|                             <EventTileType ref={this._tile} | ||||
|                                            mxEvent={this.props.mxEvent} | ||||
|                                            highlights={this.props.highlights} | ||||
|                                            highlightLink={this.props.highlightLink} | ||||
|  | @ -820,7 +823,7 @@ module.exports = createReactClass({ | |||
|                     this.props.mxEvent, | ||||
|                     this.props.onHeightChanged, | ||||
|                     this.props.permalinkCreator, | ||||
|                     'replyThread', | ||||
|                     this._replyThread, | ||||
|                 ); | ||||
|                 // tab-index=-1 to allow it to be focusable but do not add tab stop for it, primarily for screen readers
 | ||||
|                 return ( | ||||
|  | @ -839,7 +842,7 @@ module.exports = createReactClass({ | |||
|                             </a> | ||||
|                             { !isBubbleMessage && this._renderE2EPadlock() } | ||||
|                             { thread } | ||||
|                             <EventTileType ref="tile" | ||||
|                             <EventTileType ref={this._tile} | ||||
|                                            mxEvent={this.props.mxEvent} | ||||
|                                            replacingEventId={this.props.replacingEventId} | ||||
|                                            editState={this.props.editState} | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ See the License for the specific language governing permissions and | |||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| import React from 'react'; | ||||
| import React, {createRef} from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import createReactClass from 'create-react-class'; | ||||
| import { linkifyElement } from '../../../HtmlUtils'; | ||||
|  | @ -54,17 +54,19 @@ module.exports = createReactClass({ | |||
|         }, (error)=>{ | ||||
|             console.error("Failed to get URL preview: " + error); | ||||
|         }); | ||||
| 
 | ||||
|         this._description = createRef(); | ||||
|     }, | ||||
| 
 | ||||
|     componentDidMount: function() { | ||||
|         if (this.refs.description) { | ||||
|             linkifyElement(this.refs.description); | ||||
|         if (this._description.current) { | ||||
|             linkifyElement(this._description.current); | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     componentDidUpdate: function() { | ||||
|         if (this.refs.description) { | ||||
|             linkifyElement(this.refs.description); | ||||
|         if (this._description.current) { | ||||
|             linkifyElement(this._description.current); | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|  | @ -129,7 +131,7 @@ module.exports = createReactClass({ | |||
|                 <div className="mx_LinkPreviewWidget_caption"> | ||||
|                     <div className="mx_LinkPreviewWidget_title"><a href={this.props.link} target="_blank" rel="noopener">{ p["og:title"] }</a></div> | ||||
|                     <div className="mx_LinkPreviewWidget_siteName">{ p["og:site_name"] ? (" - " + p["og:site_name"]) : null }</div> | ||||
|                     <div className="mx_LinkPreviewWidget_description" ref="description"> | ||||
|                     <div className="mx_LinkPreviewWidget_description" ref={this._description}> | ||||
|                         { p["og:description"] } | ||||
|                     </div> | ||||
|                 </div> | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
| import React from 'react'; | ||||
| import React, {createRef} from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import { _t } from '../../../languageHandler'; | ||||
| import CallHandler from '../../../CallHandler'; | ||||
|  | @ -111,6 +111,8 @@ class UploadButton extends React.Component { | |||
|         super(props, context); | ||||
|         this.onUploadClick = this.onUploadClick.bind(this); | ||||
|         this.onUploadFileInputChange = this.onUploadFileInputChange.bind(this); | ||||
| 
 | ||||
|         this._uploadInput = createRef(); | ||||
|     } | ||||
| 
 | ||||
|     onUploadClick(ev) { | ||||
|  | @ -118,7 +120,7 @@ class UploadButton extends React.Component { | |||
|             dis.dispatch({action: 'require_registration'}); | ||||
|             return; | ||||
|         } | ||||
|         this.refs.uploadInput.click(); | ||||
|         this._uploadInput.current.click(); | ||||
|     } | ||||
| 
 | ||||
|     onUploadFileInputChange(ev) { | ||||
|  | @ -150,7 +152,9 @@ class UploadButton extends React.Component { | |||
|                 onClick={this.onUploadClick} | ||||
|                 title={_t('Upload file')} | ||||
|             > | ||||
|                 <input ref="uploadInput" type="file" | ||||
|                 <input | ||||
|                     ref={this._uploadInput} | ||||
|                     type="file" | ||||
|                     style={uploadInputStyle} | ||||
|                     multiple | ||||
|                     onChange={this.onUploadFileInputChange} | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ See the License for the specific language governing permissions and | |||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| import React from "react"; | ||||
| import React, {createRef} from "react"; | ||||
| import dis from "../../../dispatcher"; | ||||
| import MatrixClientPeg from "../../../MatrixClientPeg"; | ||||
| import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; | ||||
|  | @ -45,6 +45,8 @@ export default class RoomBreadcrumbs extends React.Component { | |||
|         // The room IDs we're waiting to come down the Room handler and when we
 | ||||
|         // started waiting for them. Used to track a room over an upgrade/autojoin.
 | ||||
|         this._waitingRoomQueue = [/* { roomId, addedTs } */]; | ||||
| 
 | ||||
|         this._scroller = createRef(); | ||||
|     } | ||||
| 
 | ||||
|     componentWillMount() { | ||||
|  | @ -284,8 +286,8 @@ export default class RoomBreadcrumbs extends React.Component { | |||
|         } | ||||
|         this.setState({rooms}); | ||||
| 
 | ||||
|         if (this.refs.scroller) { | ||||
|             this.refs.scroller.moveToOrigin(); | ||||
|         if (this._scroller.current) { | ||||
|             this._scroller.current.moveToOrigin(); | ||||
|         } | ||||
| 
 | ||||
|         // We don't track room aesthetics (badges, membership, etc) over the wire so we
 | ||||
|  | @ -390,7 +392,7 @@ export default class RoomBreadcrumbs extends React.Component { | |||
|         return ( | ||||
|             <div role="toolbar" aria-label={_t("Recent rooms")}> | ||||
|                 <IndicatorScrollbar | ||||
|                     ref="scroller" | ||||
|                     ref={this._scroller} | ||||
|                     className="mx_RoomBreadcrumbs" | ||||
|                     trackHorizontalOverflow={true} | ||||
|                     verticalScrollsHorizontally={true} | ||||
|  |  | |||
|  | @ -55,7 +55,7 @@ export default createReactClass({ | |||
|         if (rows.length === 0) { | ||||
|             rooms = <i>{ _t('No rooms to show') }</i>; | ||||
|         } else { | ||||
|             rooms = <table ref="directory_table" className="mx_RoomDirectory_table"> | ||||
|             rooms = <table className="mx_RoomDirectory_table"> | ||||
|                 <tbody> | ||||
|                     { this.getRows() } | ||||
|                 </tbody> | ||||
|  |  | |||
|  | @ -15,7 +15,7 @@ limitations under the License. | |||
| */ | ||||
| 
 | ||||
| import sdk from '../../../index'; | ||||
| import React from 'react'; | ||||
| import React, {createRef} from 'react'; | ||||
| import { _t } from '../../../languageHandler'; | ||||
| import { linkifyElement } from '../../../HtmlUtils'; | ||||
| import { ContentRepo } from 'matrix-js-sdk'; | ||||
|  | @ -49,11 +49,15 @@ export default createReactClass({ | |||
|     }, | ||||
| 
 | ||||
|     _linkifyTopic: function() { | ||||
|         if (this.refs.topic) { | ||||
|             linkifyElement(this.refs.topic); | ||||
|         if (this._topic.current) { | ||||
|             linkifyElement(this._topic.current); | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     UNSAFE_componentWillMount: function() { | ||||
|         this._topic = createRef(); | ||||
|     }, | ||||
| 
 | ||||
|     componentDidMount: function() { | ||||
|         this._linkifyTopic(); | ||||
|     }, | ||||
|  | @ -104,7 +108,7 @@ export default createReactClass({ | |||
|             <td className="mx_RoomDirectory_roomDescription"> | ||||
|                 <div className="mx_RoomDirectory_name">{ name }</div>  | ||||
|                 { perms } | ||||
|                 <div className="mx_RoomDirectory_topic" ref="topic" onClick={this.onTopicClick}> | ||||
|                 <div className="mx_RoomDirectory_topic" ref={this._topic} onClick={this.onTopicClick}> | ||||
|                     { room.topic } | ||||
|                 </div> | ||||
|                 <div className="mx_RoomDirectory_alias">{ getDisplayAliasForRoom(room) }</div> | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ See the License for the specific language governing permissions and | |||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| import React from 'react'; | ||||
| import React, {createRef} from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import createReactClass from 'create-react-class'; | ||||
| import classNames from 'classnames'; | ||||
|  | @ -56,6 +56,10 @@ module.exports = createReactClass({ | |||
|         }; | ||||
|     }, | ||||
| 
 | ||||
|     UNSAFE_componentWillMount: function() { | ||||
|         this._topic = createRef(); | ||||
|     }, | ||||
| 
 | ||||
|     componentDidMount: function() { | ||||
|         const cli = MatrixClientPeg.get(); | ||||
|         cli.on("RoomState.events", this._onRoomStateEvents); | ||||
|  | @ -70,8 +74,8 @@ module.exports = createReactClass({ | |||
|     }, | ||||
| 
 | ||||
|     componentDidUpdate: function() { | ||||
|         if (this.refs.topic) { | ||||
|             linkifyElement(this.refs.topic); | ||||
|         if (this._topic.current) { | ||||
|             linkifyElement(this._topic.current); | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|  | @ -204,7 +208,7 @@ module.exports = createReactClass({ | |||
|             } | ||||
|         } | ||||
|         const topicElement = | ||||
|             <div className="mx_RoomHeader_topic" ref="topic" title={topic} dir="auto">{ topic }</div>; | ||||
|             <div className="mx_RoomHeader_topic" ref={this._topic} title={topic} dir="auto">{ topic }</div>; | ||||
|         const avatarSize = 28; | ||||
|         let roomAvatar; | ||||
|         if (this.props.room) { | ||||
|  |  | |||
|  | @ -65,14 +65,14 @@ module.exports = createReactClass({ | |||
| 
 | ||||
|         return ( | ||||
|                 <div className="mx_RoomHeader_name"> | ||||
|                     <EditableText ref="editor" | ||||
|                          className="mx_RoomHeader_nametext mx_RoomHeader_editable" | ||||
|                          placeholderClassName="mx_RoomHeader_placeholder" | ||||
|                          placeholder={this._placeholderName} | ||||
|                          blurToCancel={false} | ||||
|                          initialValue={this.state.name} | ||||
|                          onValueChanged={this._onValueChanged} | ||||
|                          dir="auto" /> | ||||
|                     <EditableText | ||||
|                         className="mx_RoomHeader_nametext mx_RoomHeader_editable" | ||||
|                         placeholderClassName="mx_RoomHeader_placeholder" | ||||
|                         placeholder={this._placeholderName} | ||||
|                         blurToCancel={false} | ||||
|                         initialValue={this.state.name} | ||||
|                         onValueChanged={this._onValueChanged} | ||||
|                         dir="auto" /> | ||||
|                 </div> | ||||
|         ); | ||||
|     }, | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ See the License for the specific language governing permissions and | |||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| import React from 'react'; | ||||
| import React, {createRef} from 'react'; | ||||
| import createReactClass from 'create-react-class'; | ||||
| const classNames = require('classnames'); | ||||
| const AccessibleButton = require('../../../components/views/elements/AccessibleButton'); | ||||
|  | @ -29,6 +29,10 @@ module.exports = createReactClass({ | |||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     UNSAFE_componentWillMount: function() { | ||||
|         this._search_term = createRef(); | ||||
|     }, | ||||
| 
 | ||||
|     onThisRoomClick: function() { | ||||
|         this.setState({ scope: 'Room' }, () => this._searchIfQuery()); | ||||
|     }, | ||||
|  | @ -47,13 +51,13 @@ module.exports = createReactClass({ | |||
|     }, | ||||
| 
 | ||||
|     _searchIfQuery: function() { | ||||
|         if (this.refs.search_term.value) { | ||||
|         if (this._search_term.current.value) { | ||||
|             this.onSearch(); | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     onSearch: function() { | ||||
|         this.props.onSearch(this.refs.search_term.value, this.state.scope); | ||||
|         this.props.onSearch(this._search_term.current.value, this.state.scope); | ||||
|     }, | ||||
| 
 | ||||
|     render: function() { | ||||
|  | @ -78,7 +82,7 @@ module.exports = createReactClass({ | |||
|                     </AccessibleButton> | ||||
|                 </div> | ||||
|                 <div className="mx_SearchBar_input mx_textinput"> | ||||
|                     <input ref="search_term" type="text" autoFocus={true} placeholder={_t("Search…")} onKeyDown={this.onSearchChange} /> | ||||
|                     <input ref={this._search_term} type="text" autoFocus={true} placeholder={_t("Search…")} onKeyDown={this.onSearchChange} /> | ||||
|                     <AccessibleButton className={ searchButtonClasses } onClick={this.onSearch} /> | ||||
|                 </div> | ||||
|                 <AccessibleButton className="mx_SearchBar_cancel" onClick={this.props.onCancelClick} /> | ||||
|  |  | |||
|  | @ -14,12 +14,11 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
| import React from 'react'; | ||||
| import React, {createRef} from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import { _t, _td } from '../../../languageHandler'; | ||||
| import CallHandler from '../../../CallHandler'; | ||||
| import MatrixClientPeg from '../../../MatrixClientPeg'; | ||||
| import Modal from '../../../Modal'; | ||||
| import sdk from '../../../index'; | ||||
| import dis from '../../../dispatcher'; | ||||
| import RoomViewStore from '../../../stores/RoomViewStore'; | ||||
|  | @ -27,7 +26,6 @@ import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; | |||
| import Stickerpicker from './Stickerpicker'; | ||||
| import { makeRoomPermalink } from '../../../utils/permalinks/Permalinks'; | ||||
| import ContentMessages from '../../../ContentMessages'; | ||||
| import classNames from 'classnames'; | ||||
| 
 | ||||
| import E2EIcon from './E2EIcon'; | ||||
| 
 | ||||
|  | @ -143,6 +141,8 @@ class UploadButton extends React.Component { | |||
|         super(props, context); | ||||
|         this.onUploadClick = this.onUploadClick.bind(this); | ||||
|         this.onUploadFileInputChange = this.onUploadFileInputChange.bind(this); | ||||
| 
 | ||||
|         this._uploadInput = createRef(); | ||||
|     } | ||||
| 
 | ||||
|     onUploadClick(ev) { | ||||
|  | @ -150,7 +150,7 @@ class UploadButton extends React.Component { | |||
|             dis.dispatch({action: 'require_registration'}); | ||||
|             return; | ||||
|         } | ||||
|         this.refs.uploadInput.click(); | ||||
|         this._uploadInput.current.click(); | ||||
|     } | ||||
| 
 | ||||
|     onUploadFileInputChange(ev) { | ||||
|  | @ -182,7 +182,7 @@ class UploadButton extends React.Component { | |||
|                 onClick={this.onUploadClick} | ||||
|                 title={_t('Upload file')} | ||||
|             > | ||||
|                 <input ref="uploadInput" type="file" | ||||
|                 <input ref={this._uploadInput} type="file" | ||||
|                     style={uploadInputStyle} | ||||
|                     multiple | ||||
|                     onChange={this.onUploadFileInputChange} | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ See the License for the specific language governing permissions and | |||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| import React from 'react'; | ||||
| import React, {createRef} from 'react'; | ||||
| import {_t} from "../../../languageHandler"; | ||||
| import MatrixClientPeg from "../../../MatrixClientPeg"; | ||||
| import Field from "../elements/Field"; | ||||
|  | @ -48,13 +48,15 @@ export default class ProfileSettings extends React.Component { | |||
|             avatarFile: null, | ||||
|             enableProfileSave: false, | ||||
|         }; | ||||
| 
 | ||||
|         this._avatarUpload = createRef(); | ||||
|     } | ||||
| 
 | ||||
|     _uploadAvatar = (e) => { | ||||
|         e.stopPropagation(); | ||||
|         e.preventDefault(); | ||||
| 
 | ||||
|         this.refs.avatarUpload.click(); | ||||
|         this._avatarUpload.current.click(); | ||||
|     }; | ||||
| 
 | ||||
|     _saveProfile = async (e) => { | ||||
|  | @ -156,7 +158,7 @@ export default class ProfileSettings extends React.Component { | |||
| 
 | ||||
|         return ( | ||||
|             <form onSubmit={this._saveProfile} autoComplete="off" noValidate={true}> | ||||
|                 <input type="file" ref="avatarUpload" className="mx_ProfileSettings_avatarUpload" | ||||
|                 <input type="file" ref={this._avatarUpload} className="mx_ProfileSettings_avatarUpload" | ||||
|                        onChange={this._onAvatarChanged} accept="image/*" /> | ||||
|                 <div className="mx_ProfileSettings_profile"> | ||||
|                     <div className="mx_ProfileSettings_controls"> | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ See the License for the specific language governing permissions and | |||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| import React from 'react'; | ||||
| import React, {createRef} from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import {_t} from "../../../../../languageHandler"; | ||||
| import MatrixClientPeg from "../../../../../MatrixClientPeg"; | ||||
|  | @ -44,13 +44,15 @@ export default class NotificationsSettingsTab extends React.Component { | |||
|             } | ||||
|             this.setState({currentSound: soundData.name || soundData.url}); | ||||
|         }); | ||||
| 
 | ||||
|         this._soundUpload = createRef(); | ||||
|     } | ||||
| 
 | ||||
|     async _triggerUploader(e) { | ||||
|         e.stopPropagation(); | ||||
|         e.preventDefault(); | ||||
| 
 | ||||
|         this.refs.soundUpload.click(); | ||||
|         this._soundUpload.current.click(); | ||||
|     } | ||||
| 
 | ||||
|     async _onSoundUploadChanged(e) { | ||||
|  | @ -157,7 +159,7 @@ export default class NotificationsSettingsTab extends React.Component { | |||
|                     <div> | ||||
|                         <h3>{_t("Set a new custom sound")}</h3> | ||||
|                         <form autoComplete="off" noValidate={true}> | ||||
|                             <input ref="soundUpload" className="mx_NotificationSound_soundUpload" type="file" onChange={this._onSoundUploadChanged.bind(this)} accept="audio/*" /> | ||||
|                             <input ref={this._soundUpload} className="mx_NotificationSound_soundUpload" type="file" onChange={this._onSoundUploadChanged.bind(this)} accept="audio/*" /> | ||||
|                         </form> | ||||
| 
 | ||||
|                         {currentUploadedFile} | ||||
|  |  | |||
|  | @ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
| import React from 'react'; | ||||
| import React, {createRef} from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import createReactClass from 'create-react-class'; | ||||
| import dis from '../../../dispatcher'; | ||||
|  | @ -56,6 +56,10 @@ module.exports = createReactClass({ | |||
|         }; | ||||
|     }, | ||||
| 
 | ||||
|     UNSAFE_componentWillMount: function() { | ||||
|         this._video = createRef(); | ||||
|     }, | ||||
| 
 | ||||
|     componentDidMount: function() { | ||||
|         this.dispatcherRef = dis.register(this.onAction); | ||||
|         this.showCall(); | ||||
|  | @ -128,7 +132,7 @@ module.exports = createReactClass({ | |||
|     }, | ||||
| 
 | ||||
|     getVideoView: function() { | ||||
|         return this.refs.video; | ||||
|         return this._video.current; | ||||
|     }, | ||||
| 
 | ||||
|     render: function() { | ||||
|  | @ -147,7 +151,9 @@ module.exports = createReactClass({ | |||
| 
 | ||||
|         return ( | ||||
|             <div> | ||||
|                 <VideoView ref="video" onClick={this.props.onClick} | ||||
|                 <VideoView | ||||
|                     ref={this._video} | ||||
|                     onClick={this.props.onClick} | ||||
|                     onResize={this.props.onResize} | ||||
|                     maxHeight={this.props.maxVideoHeight} | ||||
|                 /> | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ See the License for the specific language governing permissions and | |||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| import React from 'react'; | ||||
| import React, {createRef} from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import createReactClass from 'create-react-class'; | ||||
| 
 | ||||
|  | @ -30,12 +30,16 @@ module.exports = createReactClass({ | |||
|         onResize: PropTypes.func, | ||||
|     }, | ||||
| 
 | ||||
|     UNSAFE_componentWillMount() { | ||||
|         this._vid = createRef(); | ||||
|     }, | ||||
| 
 | ||||
|     componentDidMount() { | ||||
|         this.refs.vid.addEventListener('resize', this.onResize); | ||||
|         this._vid.current.addEventListener('resize', this.onResize); | ||||
|     }, | ||||
| 
 | ||||
|     componentWillUnmount() { | ||||
|         this.refs.vid.removeEventListener('resize', this.onResize); | ||||
|         this._vid.current.removeEventListener('resize', this.onResize); | ||||
|     }, | ||||
| 
 | ||||
|     onResize: function(e) { | ||||
|  | @ -46,7 +50,7 @@ module.exports = createReactClass({ | |||
| 
 | ||||
|     render: function() { | ||||
|         return ( | ||||
|             <video ref="vid" style={{maxHeight: this.props.maxHeight}}> | ||||
|             <video ref={this._vid} style={{maxHeight: this.props.maxHeight}}> | ||||
|             </video> | ||||
|         ); | ||||
|     }, | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ See the License for the specific language governing permissions and | |||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| import React from 'react'; | ||||
| import React, {createRef} from 'react'; | ||||
| import ReactDOM from 'react-dom'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import createReactClass from 'create-react-class'; | ||||
|  | @ -49,6 +49,11 @@ module.exports = createReactClass({ | |||
|         onResize: PropTypes.func, | ||||
|     }, | ||||
| 
 | ||||
|     UNSAFE_componentWillMount: function() { | ||||
|         this._local = createRef(); | ||||
|         this._remote = createRef(); | ||||
|     }, | ||||
| 
 | ||||
|     componentDidMount: function() { | ||||
|         this.dispatcherRef = dis.register(this.onAction); | ||||
|     }, | ||||
|  | @ -58,7 +63,7 @@ module.exports = createReactClass({ | |||
|     }, | ||||
| 
 | ||||
|     getRemoteVideoElement: function() { | ||||
|         return ReactDOM.findDOMNode(this.refs.remote); | ||||
|         return ReactDOM.findDOMNode(this._remote.current); | ||||
|     }, | ||||
| 
 | ||||
|     getRemoteAudioElement: function() { | ||||
|  | @ -74,7 +79,7 @@ module.exports = createReactClass({ | |||
|     }, | ||||
| 
 | ||||
|     getLocalVideoElement: function() { | ||||
|         return ReactDOM.findDOMNode(this.refs.local); | ||||
|         return ReactDOM.findDOMNode(this._local.current); | ||||
|     }, | ||||
| 
 | ||||
|     setContainer: function(c) { | ||||
|  | @ -125,11 +130,11 @@ module.exports = createReactClass({ | |||
|         return ( | ||||
|             <div className="mx_VideoView" ref={this.setContainer} onClick={this.props.onClick}> | ||||
|                 <div className="mx_VideoView_remoteVideoFeed"> | ||||
|                     <VideoFeed ref="remote" onResize={this.props.onResize} | ||||
|                     <VideoFeed ref={this._remote} onResize={this.props.onResize} | ||||
|                         maxHeight={maxVideoHeight} /> | ||||
|                 </div> | ||||
|                 <div className={localVideoFeedClasses}> | ||||
|                     <VideoFeed ref="local" /> | ||||
|                     <VideoFeed ref={this._local} /> | ||||
|                 </div> | ||||
|             </div> | ||||
|         ); | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Michael Telatynski
						Michael Telatynski