Migrate string refs over to createRef

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
Michael Telatynski 2019-12-08 12:16:17 +00:00
parent 4c55f3c5b5
commit d22985f12e
39 changed files with 438 additions and 302 deletions

View file

@ -174,12 +174,6 @@ React
<Foo onClick={this.onFooClick}> // Best, if onFooClick would do anything other than directly calling doStuff <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` - 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): - 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 - Think about whether your component really needs state: are you duplicating
information in component state that could be derived from the model? 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)

View file

@ -15,7 +15,7 @@ limitations under the License.
*/ */
import FileSaver from 'file-saver'; import FileSaver from 'file-saver';
import React from 'react'; import React, {createRef} from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import createReactClass from 'create-react-class'; import createReactClass from 'create-react-class';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
@ -44,6 +44,9 @@ export default createReactClass({
componentWillMount: function() { componentWillMount: function() {
this._unmounted = false; this._unmounted = false;
this._passphrase1 = createRef();
this._passphrase2 = createRef();
}, },
componentWillUnmount: function() { componentWillUnmount: function() {
@ -53,8 +56,8 @@ export default createReactClass({
_onPassphraseFormSubmit: function(ev) { _onPassphraseFormSubmit: function(ev) {
ev.preventDefault(); ev.preventDefault();
const passphrase = this.refs.passphrase1.value; const passphrase = this._passphrase1.current.value;
if (passphrase !== this.refs.passphrase2.value) { if (passphrase !== this._passphrase2.current.value) {
this.setState({errStr: _t('Passphrases must match')}); this.setState({errStr: _t('Passphrases must match')});
return false; return false;
} }
@ -148,7 +151,7 @@ export default createReactClass({
</label> </label>
</div> </div>
<div className='mx_E2eKeysDialog_inputCell'> <div className='mx_E2eKeysDialog_inputCell'>
<input ref='passphrase1' id='passphrase1' <input ref={this._passphrase1} id='passphrase1'
autoFocus={true} size='64' type='password' autoFocus={true} size='64' type='password'
disabled={disableForm} disabled={disableForm}
/> />
@ -161,7 +164,7 @@ export default createReactClass({
</label> </label>
</div> </div>
<div className='mx_E2eKeysDialog_inputCell'> <div className='mx_E2eKeysDialog_inputCell'>
<input ref='passphrase2' id='passphrase2' <input ref={this._passphrase2} id='passphrase2'
size='64' type='password' size='64' type='password'
disabled={disableForm} disabled={disableForm}
/> />

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React from 'react'; import React, {createRef} from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import createReactClass from 'create-react-class'; import createReactClass from 'create-react-class';
@ -56,6 +56,9 @@ export default createReactClass({
componentWillMount: function() { componentWillMount: function() {
this._unmounted = false; this._unmounted = false;
this._file = createRef();
this._passphrase = createRef();
}, },
componentWillUnmount: function() { componentWillUnmount: function() {
@ -63,15 +66,15 @@ export default createReactClass({
}, },
_onFormChange: function(ev) { _onFormChange: function(ev) {
const files = this.refs.file.files || []; const files = this._file.current.files || [];
this.setState({ this.setState({
enableSubmit: (this.refs.passphrase.value !== "" && files.length > 0), enableSubmit: (this._passphrase.current.value !== "" && files.length > 0),
}); });
}, },
_onFormSubmit: function(ev) { _onFormSubmit: function(ev) {
ev.preventDefault(); 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; return false;
}, },
@ -146,7 +149,10 @@ export default createReactClass({
</label> </label>
</div> </div>
<div className='mx_E2eKeysDialog_inputCell'> <div className='mx_E2eKeysDialog_inputCell'>
<input ref='file' id='importFile' type='file' <input
ref={this._file}
id='importFile'
type='file'
autoFocus={true} autoFocus={true}
onChange={this._onFormChange} onChange={this._onFormChange}
disabled={disableForm} /> disabled={disableForm} />
@ -159,8 +165,11 @@ export default createReactClass({
</label> </label>
</div> </div>
<div className='mx_E2eKeysDialog_inputCell'> <div className='mx_E2eKeysDialog_inputCell'>
<input ref='passphrase' id='passphrase' <input
size='64' type='password' ref={this._passphrase}
id='passphrase'
size='64'
type='password'
onChange={this._onFormChange} onChange={this._onFormChange}
disabled={disableForm} /> disabled={disableForm} />
</div> </div>

View file

@ -18,7 +18,7 @@ limitations under the License.
import Matrix from 'matrix-js-sdk'; import Matrix from 'matrix-js-sdk';
const InteractiveAuth = Matrix.InteractiveAuth; const InteractiveAuth = Matrix.InteractiveAuth;
import React from 'react'; import React, {createRef} from 'react';
import createReactClass from 'create-react-class'; import createReactClass from 'create-react-class';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
@ -129,6 +129,8 @@ export default createReactClass({
this._authLogic.poll(); this._authLogic.poll();
}, 2000); }, 2000);
} }
this._stageComponent = createRef();
}, },
componentWillUnmount: function() { componentWillUnmount: function() {
@ -153,8 +155,8 @@ export default createReactClass({
}, },
tryContinue: function() { tryContinue: function() {
if (this.refs.stageComponent && this.refs.stageComponent.tryContinue) { if (this._stageComponent.current && this._stageComponent.current.tryContinue) {
this.refs.stageComponent.tryContinue(); this._stageComponent.current.tryContinue();
} }
}, },
@ -192,8 +194,8 @@ export default createReactClass({
}, },
_setFocus: function() { _setFocus: function() {
if (this.refs.stageComponent && this.refs.stageComponent.focus) { if (this._stageComponent.current && this._stageComponent.current.focus) {
this.refs.stageComponent.focus(); this._stageComponent.current.focus();
} }
}, },
@ -214,7 +216,8 @@ export default createReactClass({
const StageComponent = getEntryComponentForLoginType(stage); const StageComponent = getEntryComponentForLoginType(stage);
return ( return (
<StageComponent ref="stageComponent" <StageComponent
ref={this._stageComponent}
loginType={stage} loginType={stage}
matrixClient={this.props.matrixClient} matrixClient={this.props.matrixClient}
authSessionId={this._authLogic.getSessionId()} authSessionId={this._authLogic.getSessionId()}

View file

@ -17,7 +17,7 @@ limitations under the License.
*/ */
import { MatrixClient } from 'matrix-js-sdk'; import { MatrixClient } from 'matrix-js-sdk';
import React from 'react'; import React, {createRef} from 'react';
import createReactClass from 'create-react-class'; import createReactClass from 'create-react-class';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { DragDropContext } from 'react-beautiful-dnd'; import { DragDropContext } from 'react-beautiful-dnd';
@ -129,6 +129,8 @@ const LoggedInView = createReactClass({
this._matrixClient.on("RoomState.events", this.onRoomStateEvents); this._matrixClient.on("RoomState.events", this.onRoomStateEvents);
fixupColorFonts(); fixupColorFonts();
this._roomView = createRef();
}, },
componentDidUpdate(prevProps) { componentDidUpdate(prevProps) {
@ -165,10 +167,10 @@ const LoggedInView = createReactClass({
}, },
canResetTimelineInRoom: function(roomId) { canResetTimelineInRoom: function(roomId) {
if (!this.refs.roomView) { if (!this._roomView.current) {
return true; return true;
} }
return this.refs.roomView.canResetTimeline(); return this._roomView.current.canResetTimeline();
}, },
_setStateFromSessionStore() { _setStateFromSessionStore() {
@ -428,8 +430,8 @@ const LoggedInView = createReactClass({
* @param {Object} ev The key event * @param {Object} ev The key event
*/ */
_onScrollKeyPressed: function(ev) { _onScrollKeyPressed: function(ev) {
if (this.refs.roomView) { if (this._roomView.current) {
this.refs.roomView.handleScrollKey(ev); this._roomView.current.handleScrollKey(ev);
} }
}, },
@ -543,7 +545,7 @@ const LoggedInView = createReactClass({
switch (this.props.page_type) { switch (this.props.page_type) {
case PageTypes.RoomView: case PageTypes.RoomView:
pageElement = <RoomView pageElement = <RoomView
ref='roomView' ref={this._roomView}
autoJoin={this.props.autoJoin} autoJoin={this.props.autoJoin}
onRegistered={this.props.onRegistered} onRegistered={this.props.onRegistered}
thirdPartyInvite={this.props.thirdPartyInvite} thirdPartyInvite={this.props.thirdPartyInvite}

View file

@ -16,7 +16,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React from 'react'; import React, {createRef} from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import classNames from 'classnames'; import classNames from 'classnames';
@ -159,6 +159,10 @@ export default class MessagePanel extends React.Component {
SettingsStore.getValue("showHiddenEventsInTimeline"); SettingsStore.getValue("showHiddenEventsInTimeline");
this._isMounted = false; this._isMounted = false;
this._readMarkerNode = createRef();
this._whoIsTyping = createRef();
this._scrollPanel = createRef();
} }
componentDidMount() { 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. /* return true if the content is fully scrolled down right now; else false.
*/ */
isAtBottom() { isAtBottom() {
return this.refs.scrollPanel return this._scrollPanel.current && this._scrollPanel.current.isAtBottom();
&& this.refs.scrollPanel.isAtBottom();
} }
/* get the current scroll state. See ScrollPanel.getScrollState for /* 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. * returns null if we are not mounted.
*/ */
getScrollState() { getScrollState() {
if (!this.refs.scrollPanel) { return null; } return this._scrollPanel.current ? this._scrollPanel.current.getScrollState() : null;
return this.refs.scrollPanel.getScrollState();
} }
// returns one of: // returns one of:
@ -212,8 +214,8 @@ export default class MessagePanel extends React.Component {
// 0: read marker is within the window // 0: read marker is within the window
// +1: read marker is below the window // +1: read marker is below the window
getReadMarkerPosition() { getReadMarkerPosition() {
const readMarker = this.refs.readMarkerNode; const readMarker = this._readMarkerNode.current;
const messageWrapper = this.refs.scrollPanel; const messageWrapper = this._scrollPanel.current;
if (!readMarker || !messageWrapper) { if (!readMarker || !messageWrapper) {
return null; return null;
@ -236,16 +238,16 @@ export default class MessagePanel extends React.Component {
/* jump to the top of the content. /* jump to the top of the content.
*/ */
scrollToTop() { scrollToTop() {
if (this.refs.scrollPanel) { if (this._scrollPanel.current) {
this.refs.scrollPanel.scrollToTop(); this._scrollPanel.current.scrollToTop();
} }
} }
/* jump to the bottom of the content. /* jump to the bottom of the content.
*/ */
scrollToBottom() { scrollToBottom() {
if (this.refs.scrollPanel) { if (this._scrollPanel.current) {
this.refs.scrollPanel.scrollToBottom(); 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 * @param {number} mult: -1 to page up, +1 to page down
*/ */
scrollRelative(mult) { scrollRelative(mult) {
if (this.refs.scrollPanel) { if (this._scrollPanel.current) {
this.refs.scrollPanel.scrollRelative(mult); this._scrollPanel.current.scrollRelative(mult);
} }
} }
@ -266,8 +268,8 @@ export default class MessagePanel extends React.Component {
* @param {KeyboardEvent} ev: the keyboard event to handle * @param {KeyboardEvent} ev: the keyboard event to handle
*/ */
handleScrollKey(ev) { handleScrollKey(ev) {
if (this.refs.scrollPanel) { if (this._scrollPanel.current) {
this.refs.scrollPanel.handleScrollKey(ev); this._scrollPanel.current.handleScrollKey(ev);
} }
} }
@ -282,8 +284,8 @@ export default class MessagePanel extends React.Component {
* defaults to 0. * defaults to 0.
*/ */
scrollToEvent(eventId, pixelOffset, offsetBase) { scrollToEvent(eventId, pixelOffset, offsetBase) {
if (this.refs.scrollPanel) { if (this._scrollPanel.current) {
this.refs.scrollPanel.scrollToToken(eventId, pixelOffset, offsetBase); 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. /* check the scroll state and send out pagination requests if necessary.
*/ */
checkFillState() { checkFillState() {
if (this.refs.scrollPanel) { if (this._scrollPanel.current) {
this.refs.scrollPanel.checkFillState(); this._scrollPanel.current.checkFillState();
} }
} }
@ -345,7 +347,7 @@ export default class MessagePanel extends React.Component {
} }
return ( return (
<li key={"readMarker_"+eventId} ref="readMarkerNode" <li key={"readMarker_"+eventId} ref={this._readMarkerNode}
className="mx_RoomView_myReadMarker_container"> className="mx_RoomView_myReadMarker_container">
{ hr } { hr }
</li> </li>
@ -829,14 +831,14 @@ export default class MessagePanel extends React.Component {
// once dynamic content in the events load, make the scrollPanel check the // once dynamic content in the events load, make the scrollPanel check the
// scroll offsets. // scroll offsets.
_onHeightChanged = () => { _onHeightChanged = () => {
const scrollPanel = this.refs.scrollPanel; const scrollPanel = this._scrollPanel.current;
if (scrollPanel) { if (scrollPanel) {
scrollPanel.checkScroll(); scrollPanel.checkScroll();
} }
}; };
_onTypingShown = () => { _onTypingShown = () => {
const scrollPanel = this.refs.scrollPanel; const scrollPanel = this._scrollPanel.current;
// this will make the timeline grow, so checkScroll // this will make the timeline grow, so checkScroll
scrollPanel.checkScroll(); scrollPanel.checkScroll();
if (scrollPanel && scrollPanel.getScrollState().stuckAtBottom) { if (scrollPanel && scrollPanel.getScrollState().stuckAtBottom) {
@ -845,7 +847,7 @@ export default class MessagePanel extends React.Component {
}; };
_onTypingHidden = () => { _onTypingHidden = () => {
const scrollPanel = this.refs.scrollPanel; const scrollPanel = this._scrollPanel.current;
if (scrollPanel) { if (scrollPanel) {
// as hiding the typing notifications doesn't // as hiding the typing notifications doesn't
// update the scrollPanel, we tell it to apply // update the scrollPanel, we tell it to apply
@ -858,11 +860,11 @@ export default class MessagePanel extends React.Component {
}; };
updateTimelineMinHeight() { updateTimelineMinHeight() {
const scrollPanel = this.refs.scrollPanel; const scrollPanel = this._scrollPanel.current;
if (scrollPanel) { if (scrollPanel) {
const isAtBottom = scrollPanel.isAtBottom(); const isAtBottom = scrollPanel.isAtBottom();
const whoIsTyping = this.refs.whoIsTyping; const whoIsTyping = this._whoIsTyping.current;
const isTypingVisible = whoIsTyping && whoIsTyping.isVisible(); const isTypingVisible = whoIsTyping && whoIsTyping.isVisible();
// when messages get added to the timeline, // when messages get added to the timeline,
// but somebody else is still typing, // but somebody else is still typing,
@ -875,7 +877,7 @@ export default class MessagePanel extends React.Component {
} }
onTimelineReset() { onTimelineReset() {
const scrollPanel = this.refs.scrollPanel; const scrollPanel = this._scrollPanel.current;
if (scrollPanel) { if (scrollPanel) {
scrollPanel.clearPreventShrinking(); scrollPanel.clearPreventShrinking();
} }
@ -909,19 +911,22 @@ export default class MessagePanel extends React.Component {
room={this.props.room} room={this.props.room}
onShown={this._onTypingShown} onShown={this._onTypingShown}
onHidden={this._onTypingHidden} onHidden={this._onTypingHidden}
ref="whoIsTyping" /> ref={this._whoIsTyping} />
); );
} }
return ( return (
<ScrollPanel ref="scrollPanel" className={className} <ScrollPanel
onScroll={this.props.onScroll} ref={this._scrollPanel}
onResize={this.onResize} className={className}
onFillRequest={this.props.onFillRequest} onScroll={this.props.onScroll}
onUnfillRequest={this.props.onUnfillRequest} onResize={this.onResize}
style={style} onFillRequest={this.props.onFillRequest}
stickyBottom={this.props.stickyBottom} onUnfillRequest={this.props.onUnfillRequest}
resizeNotifier={this.props.resizeNotifier}> style={style}
stickyBottom={this.props.stickyBottom}
resizeNotifier={this.props.resizeNotifier}
>
{ topSpinner } { topSpinner }
{ this._getEventTiles() } { this._getEventTiles() }
{ whoIsTyping } { whoIsTyping }

View file

@ -82,8 +82,14 @@ const RoomSubList = createReactClass({
}; };
}, },
componentDidMount: function() { UNSAFE_componentWillMount: function() {
this._header = createRef();
this._subList = createRef();
this._scroller = createRef();
this._headerButton = createRef(); this._headerButton = createRef();
},
componentDidMount: function() {
this.dispatcherRef = dis.register(this.onAction); this.dispatcherRef = dis.register(this.onAction);
}, },
@ -103,7 +109,7 @@ const RoomSubList = createReactClass({
// The header is collapsible if it is hidden or not stuck // The header is collapsible if it is hidden or not stuck
// The dataset elements are added in the RoomList _initAndPositionStickyHeaders method // The dataset elements are added in the RoomList _initAndPositionStickyHeaders method
isCollapsibleOnClick: function() { isCollapsibleOnClick: function() {
const stuck = this.refs.header.dataset.stuck; const stuck = this._header.current.dataset.stuck;
if (!this.props.forceExpand && (this.state.hidden || stuck === undefined || stuck === "none")) { if (!this.props.forceExpand && (this.state.hidden || stuck === undefined || stuck === "none")) {
return true; return true;
} else { } else {
@ -135,7 +141,7 @@ const RoomSubList = createReactClass({
}); });
} else { } else {
// The header is stuck, so the click is to be interpreted as a scroll to the header // 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);
} }
}, },
@ -159,7 +165,7 @@ const RoomSubList = createReactClass({
this.onClick(); this.onClick();
} else if (!this.props.forceExpand) { } else if (!this.props.forceExpand) {
// sublist is expanded, go to first room // 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) { if (element) {
element.focus(); element.focus();
} }
@ -328,7 +334,7 @@ const RoomSubList = createReactClass({
} }
return ( 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 <AccessibleButton
onClick={this.onClick} onClick={this.onClick}
className="mx_RoomSubList_label" className="mx_RoomSubList_label"
@ -349,14 +355,14 @@ const RoomSubList = createReactClass({
}, },
checkOverflow: function() { checkOverflow: function() {
if (this.refs.scroller) { if (this._scroller.current) {
this.refs.scroller.checkOverflow(); this._scroller.current.checkOverflow();
} }
}, },
setHeight: function(height) { setHeight: function(height) {
if (this.refs.subList) { if (this._subList.current) {
this.refs.subList.style.height = `${height}px`; this._subList.current.style.height = `${height}px`;
} }
this._updateLazyRenderHeight(height); this._updateLazyRenderHeight(height);
}, },
@ -366,7 +372,7 @@ const RoomSubList = createReactClass({
}, },
_onScroll: function() { _onScroll: function() {
this.setState({scrollTop: this.refs.scroller.getScrollTop()}); this.setState({scrollTop: this._scroller.current.getScrollTop()});
}, },
_canUseLazyListRendering() { _canUseLazyListRendering() {
@ -391,7 +397,7 @@ const RoomSubList = createReactClass({
// no body // no body
} else if (this._canUseLazyListRendering()) { } else if (this._canUseLazyListRendering()) {
content = ( content = (
<IndicatorScrollbar ref="scroller" className="mx_RoomSubList_scroll" onScroll={this._onScroll}> <IndicatorScrollbar ref={this._scroller} className="mx_RoomSubList_scroll" onScroll={this._onScroll}>
<LazyRenderList <LazyRenderList
scrollTop={this.state.scrollTop } scrollTop={this.state.scrollTop }
height={ this.state.scrollerHeight } height={ this.state.scrollerHeight }
@ -404,7 +410,7 @@ const RoomSubList = createReactClass({
const roomTiles = this.props.list.map(r => this.makeRoomTile(r)); const roomTiles = this.props.list.map(r => this.makeRoomTile(r));
const tiles = roomTiles.concat(this.props.extraTiles); const tiles = roomTiles.concat(this.props.extraTiles);
content = ( content = (
<IndicatorScrollbar ref="scroller" className="mx_RoomSubList_scroll" onScroll={this._onScroll}> <IndicatorScrollbar ref={this._scroller} className="mx_RoomSubList_scroll" onScroll={this._onScroll}>
{ tiles } { tiles }
</IndicatorScrollbar> </IndicatorScrollbar>
); );
@ -418,7 +424,7 @@ const RoomSubList = createReactClass({
return ( return (
<div <div
ref="subList" ref={this._subList}
className={subListClasses} className={subListClasses}
role="group" role="group"
aria-label={this.props.label} aria-label={this.props.label}

View file

@ -23,7 +23,7 @@ limitations under the License.
import shouldHideEvent from '../../shouldHideEvent'; import shouldHideEvent from '../../shouldHideEvent';
import React from 'react'; import React, {createRef} from 'react';
import createReactClass from 'create-react-class'; import createReactClass from 'create-react-class';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
@ -207,6 +207,9 @@ module.exports = createReactClass({
this._onCiderUpdated(); this._onCiderUpdated();
this._ciderWatcherRef = SettingsStore.watchSetting( this._ciderWatcherRef = SettingsStore.watchSetting(
"useCiderComposer", null, this._onCiderUpdated); "useCiderComposer", null, this._onCiderUpdated);
this._roomView = createRef();
this._searchResultsPanel = createRef();
}, },
_onCiderUpdated: function() { _onCiderUpdated: function() {
@ -459,8 +462,8 @@ module.exports = createReactClass({
}, },
componentDidUpdate: function() { componentDidUpdate: function() {
if (this.refs.roomView) { if (this._roomView.current) {
const roomView = ReactDOM.findDOMNode(this.refs.roomView); const roomView = ReactDOM.findDOMNode(this._roomView.current);
if (!roomView.ondrop) { if (!roomView.ondrop) {
roomView.addEventListener('drop', this.onDrop); roomView.addEventListener('drop', this.onDrop);
roomView.addEventListener('dragover', this.onDragOver); 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 // 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, // catch the messagePanel when it does mount. Because we only want the ref once,
// we use a boolean flag to avoid duplicate work. // we use a boolean flag to avoid duplicate work.
if (this.refs.messagePanel && !this.state.atEndOfLiveTimelineInit) { if (this._messagePanel && !this.state.atEndOfLiveTimelineInit) {
this.setState({ this.setState({
atEndOfLiveTimelineInit: true, 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 // stop tracking room changes to format permalinks
this._stopAllPermalinkCreators(); this._stopAllPermalinkCreators();
if (this.refs.roomView) { if (this._roomView.current) {
// disconnect the D&D event listeners from the room view. This // disconnect the D&D event listeners from the room view. This
// is really just for hygiene - we're going to be // is really just for hygiene - we're going to be
// deleted anyway, so it doesn't matter if the event listeners // deleted anyway, so it doesn't matter if the event listeners
// don't get cleaned up. // 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('drop', this.onDrop);
roomView.removeEventListener('dragover', this.onDragOver); roomView.removeEventListener('dragover', this.onDragOver);
roomView.removeEventListener('dragleave', this.onDragLeaveOrEnd); roomView.removeEventListener('dragleave', this.onDragLeaveOrEnd);
@ -701,10 +704,10 @@ module.exports = createReactClass({
}, },
canResetTimeline: function() { canResetTimeline: function() {
if (!this.refs.messagePanel) { if (!this._messagePanel) {
return true; return true;
} }
return this.refs.messagePanel.canResetTimeline(); return this._messagePanel.canResetTimeline();
}, },
// called when state.room is first initialised (either at initial load, // called when state.room is first initialised (either at initial load,
@ -1046,7 +1049,7 @@ module.exports = createReactClass({
}, },
onMessageListScroll: function(ev) { onMessageListScroll: function(ev) {
if (this.refs.messagePanel.isAtEndOfLiveTimeline()) { if (this._messagePanel.isAtEndOfLiveTimeline()) {
this.setState({ this.setState({
numUnreadMessages: 0, numUnreadMessages: 0,
atEndOfLiveTimeline: true, atEndOfLiveTimeline: true,
@ -1119,8 +1122,8 @@ module.exports = createReactClass({
// if we already have a search panel, we need to tell it to forget // if we already have a search panel, we need to tell it to forget
// about its scroll state. // about its scroll state.
if (this.refs.searchResultsPanel) { if (this._searchResultsPanel.current) {
this.refs.searchResultsPanel.resetScrollState(); this._searchResultsPanel.current.resetScrollState();
} }
// make sure that we don't end up showing results from // 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 // once dynamic content in the search results load, make the scrollPanel check
// the scroll offsets. // the scroll offsets.
const onHeightChanged = () => { const onHeightChanged = () => {
const scrollPanel = this.refs.searchResultsPanel; const scrollPanel = this._searchResultsPanel.current;
if (scrollPanel) { if (scrollPanel) {
scrollPanel.checkScroll(); scrollPanel.checkScroll();
} }
@ -1370,28 +1373,28 @@ module.exports = createReactClass({
// jump down to the bottom of this room, where new events are arriving // jump down to the bottom of this room, where new events are arriving
jumpToLiveTimeline: function() { jumpToLiveTimeline: function() {
this.refs.messagePanel.jumpToLiveTimeline(); this._messagePanel.jumpToLiveTimeline();
dis.dispatch({action: 'focus_composer'}); dis.dispatch({action: 'focus_composer'});
}, },
// jump up to wherever our read marker is // jump up to wherever our read marker is
jumpToReadMarker: function() { jumpToReadMarker: function() {
this.refs.messagePanel.jumpToReadMarker(); this._messagePanel.jumpToReadMarker();
}, },
// update the read marker to match the read-receipt // update the read marker to match the read-receipt
forgetReadMarker: function(ev) { forgetReadMarker: function(ev) {
ev.stopPropagation(); ev.stopPropagation();
this.refs.messagePanel.forgetReadMarker(); this._messagePanel.forgetReadMarker();
}, },
// decide whether or not the top 'unread messages' bar should be shown // decide whether or not the top 'unread messages' bar should be shown
_updateTopUnreadMessagesBar: function() { _updateTopUnreadMessagesBar: function() {
if (!this.refs.messagePanel) { if (!this._messagePanel) {
return; return;
} }
const showBar = this.refs.messagePanel.canJumpToReadMarker(); const showBar = this._messagePanel.canJumpToReadMarker();
if (this.state.showTopUnreadMessagesBar != showBar) { if (this.state.showTopUnreadMessagesBar != showBar) {
this.setState({showTopUnreadMessagesBar: showBar}); this.setState({showTopUnreadMessagesBar: showBar});
} }
@ -1401,7 +1404,7 @@ module.exports = createReactClass({
// restored when we switch back to it. // restored when we switch back to it.
// //
_getScrollState: function() { _getScrollState: function() {
const messagePanel = this.refs.messagePanel; const messagePanel = this._messagePanel;
if (!messagePanel) return null; if (!messagePanel) return null;
// if we're following the live timeline, we want to return null; that // if we're following the live timeline, we want to return null; that
@ -1506,10 +1509,10 @@ module.exports = createReactClass({
*/ */
handleScrollKey: function(ev) { handleScrollKey: function(ev) {
let panel; let panel;
if (this.refs.searchResultsPanel) { if (this._searchResultsPanel.current) {
panel = this.refs.searchResultsPanel; panel = this._searchResultsPanel.current;
} else if (this.refs.messagePanel) { } else if (this._messagePanel) {
panel = this.refs.messagePanel; panel = this._messagePanel;
} }
if (panel) { if (panel) {
@ -1530,7 +1533,7 @@ module.exports = createReactClass({
// this has to be a proper method rather than an unnamed function, // this has to be a proper method rather than an unnamed function,
// otherwise react calls it with null on each update. // otherwise react calls it with null on each update.
_gatherTimelinePanelRef: function(r) { _gatherTimelinePanelRef: function(r) {
this.refs.messagePanel = r; this._messagePanel = r;
if (r) { if (r) {
console.log("updateTint from RoomView._gatherTimelinePanelRef"); console.log("updateTint from RoomView._gatherTimelinePanelRef");
this.updateTint(); this.updateTint();
@ -1875,7 +1878,7 @@ module.exports = createReactClass({
searchResultsPanel = (<div className="mx_RoomView_messagePanel mx_RoomView_messagePanelSearchSpinner" />); searchResultsPanel = (<div className="mx_RoomView_messagePanel mx_RoomView_messagePanelSearchSpinner" />);
} else { } else {
searchResultsPanel = ( searchResultsPanel = (
<ScrollPanel ref="searchResultsPanel" <ScrollPanel ref={this._searchResultsPanel}
className="mx_RoomView_messagePanel mx_RoomView_searchResultsPanel" className="mx_RoomView_messagePanel mx_RoomView_searchResultsPanel"
onFillRequest={this.onSearchResultsFillRequest} onFillRequest={this.onSearchResultsFillRequest}
resizeNotifier={this.props.resizeNotifier} 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); // console.info("ShowUrlPreview for %s is %s", this.state.room.roomId, this.state.showUrlPreview);
const messagePanel = ( const messagePanel = (
<TimelinePanel ref={this._gatherTimelinePanelRef} <TimelinePanel
ref={this._gatherTimelinePanelRef}
timelineSet={this.state.room.getUnfilteredTimelineSet()} timelineSet={this.state.room.getUnfilteredTimelineSet()}
showReadReceipts={SettingsStore.getValue('showReadReceipts')} showReadReceipts={SettingsStore.getValue('showReadReceipts')}
manageReadReceipts={!this.state.isPeeking} manageReadReceipts={!this.state.isPeeking}
@ -1952,9 +1956,11 @@ module.exports = createReactClass({
const collapsedRhs = hideRightPanel || this.props.collapsedRhs; const collapsedRhs = hideRightPanel || this.props.collapsedRhs;
return ( return (
<main className={"mx_RoomView" + (inCall ? " mx_RoomView_inCall" : "")} ref="roomView"> <main className={"mx_RoomView" + (inCall ? " mx_RoomView_inCall" : "")} ref={this._roomView}>
<ErrorBoundary> <ErrorBoundary>
<RoomHeader ref="header" room={this.state.room} searchInfo={searchInfo} <RoomHeader
room={this.state.room}
searchInfo={searchInfo}
oobData={this.props.oobData} oobData={this.props.oobData}
inRoom={myMembership === 'join'} inRoom={myMembership === 'join'}
collapsedRhs={collapsedRhs} collapsedRhs={collapsedRhs}

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React from "react"; import React, {createRef} from "react";
import createReactClass from 'create-react-class'; import createReactClass from 'create-react-class';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { KeyCode } from '../../Keyboard'; import { KeyCode } from '../../Keyboard';
@ -166,6 +166,8 @@ module.exports = createReactClass({
} }
this.resetScrollState(); this.resetScrollState();
this._itemlist = createRef();
}, },
componentDidMount: function() { componentDidMount: function() {
@ -328,7 +330,7 @@ module.exports = createReactClass({
this._isFilling = true; this._isFilling = true;
} }
const itemlist = this.refs.itemlist; const itemlist = this._itemlist.current;
const firstTile = itemlist && itemlist.firstElementChild; const firstTile = itemlist && itemlist.firstElementChild;
const contentTop = firstTile && firstTile.offsetTop; const contentTop = firstTile && firstTile.offsetTop;
const fillPromises = []; const fillPromises = [];
@ -373,7 +375,7 @@ module.exports = createReactClass({
const origExcessHeight = excessHeight; 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 // The scroll token of the first/last tile to be unpaginated
let markerScrollToken = null; let markerScrollToken = null;
@ -602,7 +604,7 @@ module.exports = createReactClass({
const scrollNode = this._getScrollNode(); const scrollNode = this._getScrollNode();
const viewportBottom = scrollNode.scrollHeight - (scrollNode.scrollTop + scrollNode.clientHeight); const viewportBottom = scrollNode.scrollHeight - (scrollNode.scrollTop + scrollNode.clientHeight);
const itemlist = this.refs.itemlist; const itemlist = this._itemlist.current;
const messages = itemlist.children; const messages = itemlist.children;
let node = null; let node = null;
@ -644,7 +646,7 @@ module.exports = createReactClass({
const sn = this._getScrollNode(); const sn = this._getScrollNode();
sn.scrollTop = sn.scrollHeight; sn.scrollTop = sn.scrollHeight;
} else if (scrollState.trackedScrollToken) { } else if (scrollState.trackedScrollToken) {
const itemlist = this.refs.itemlist; const itemlist = this._itemlist.current;
const trackedNode = this._getTrackedNode(); const trackedNode = this._getTrackedNode();
if (trackedNode) { if (trackedNode) {
const newBottomOffset = this._topFromBottom(trackedNode); const newBottomOffset = this._topFromBottom(trackedNode);
@ -682,7 +684,7 @@ module.exports = createReactClass({
} }
const sn = this._getScrollNode(); const sn = this._getScrollNode();
const itemlist = this.refs.itemlist; const itemlist = this._itemlist.current;
const contentHeight = this._getMessagesHeight(); const contentHeight = this._getMessagesHeight();
const minHeight = sn.clientHeight; const minHeight = sn.clientHeight;
const height = Math.max(minHeight, contentHeight); const height = Math.max(minHeight, contentHeight);
@ -724,7 +726,7 @@ module.exports = createReactClass({
if (!trackedNode || !trackedNode.parentElement) { if (!trackedNode || !trackedNode.parentElement) {
let node; let node;
const messages = this.refs.itemlist.children; const messages = this._itemlist.current.children;
const scrollToken = scrollState.trackedScrollToken; const scrollToken = scrollState.trackedScrollToken;
for (let i = messages.length-1; i >= 0; --i) { for (let i = messages.length-1; i >= 0; --i) {
@ -756,7 +758,7 @@ module.exports = createReactClass({
}, },
_getMessagesHeight() { _getMessagesHeight() {
const itemlist = this.refs.itemlist; const itemlist = this._itemlist.current;
const lastNode = itemlist.lastElementChild; const lastNode = itemlist.lastElementChild;
const lastNodeBottom = lastNode ? lastNode.offsetTop + lastNode.clientHeight : 0; const lastNodeBottom = lastNode ? lastNode.offsetTop + lastNode.clientHeight : 0;
const firstNodeTop = itemlist.firstElementChild ? itemlist.firstElementChild.offsetTop : 0; const firstNodeTop = itemlist.firstElementChild ? itemlist.firstElementChild.offsetTop : 0;
@ -765,7 +767,7 @@ module.exports = createReactClass({
}, },
_topFromBottom(node) { _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 /* 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. the same minimum bottom offset, effectively preventing the timeline to shrink.
*/ */
preventShrinking: function() { preventShrinking: function() {
const messageList = this.refs.itemlist; const messageList = this._itemlist.current;
const tiles = messageList && messageList.children; const tiles = messageList && messageList.children;
if (!messageList) { if (!messageList) {
return; return;
@ -824,7 +826,7 @@ module.exports = createReactClass({
/** Clear shrinking prevention. Used internally, and when the timeline is reloaded. */ /** Clear shrinking prevention. Used internally, and when the timeline is reloaded. */
clearPreventShrinking: function() { clearPreventShrinking: function() {
const messageList = this.refs.itemlist; const messageList = this._itemlist.current;
const balanceElement = messageList && messageList.parentElement; const balanceElement = messageList && messageList.parentElement;
if (balanceElement) balanceElement.style.paddingBottom = null; if (balanceElement) balanceElement.style.paddingBottom = null;
this.preventShrinkingState = null; this.preventShrinkingState = null;
@ -843,7 +845,7 @@ module.exports = createReactClass({
if (this.preventShrinkingState) { if (this.preventShrinkingState) {
const sn = this._getScrollNode(); const sn = this._getScrollNode();
const scrollState = this.scrollState; const scrollState = this.scrollState;
const messageList = this.refs.itemlist; const messageList = this._itemlist.current;
const {offsetNode, offsetFromBottom} = this.preventShrinkingState; const {offsetNode, offsetFromBottom} = this.preventShrinkingState;
// element used to set paddingBottom to balance the typing notifs disappearing // element used to set paddingBottom to balance the typing notifs disappearing
const balanceElement = messageList.parentElement; const balanceElement = messageList.parentElement;
@ -879,7 +881,7 @@ module.exports = createReactClass({
onScroll={this.onScroll} onScroll={this.onScroll}
className={`mx_ScrollPanel ${this.props.className}`} style={this.props.style}> className={`mx_ScrollPanel ${this.props.className}`} style={this.props.style}>
<div className="mx_RoomView_messageListWrapper"> <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 } { this.props.children }
</ol> </ol>
</div> </div>

View file

@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React from 'react'; import React, {createRef} from 'react';
import createReactClass from 'create-react-class'; import createReactClass from 'create-react-class';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { KeyCode } from '../../Keyboard'; import { KeyCode } from '../../Keyboard';
@ -53,6 +53,10 @@ module.exports = createReactClass({
}; };
}, },
UNSAFE_componentWillMount: function() {
this._search = createRef();
},
componentDidMount: function() { componentDidMount: function() {
this.dispatcherRef = dis.register(this.onAction); this.dispatcherRef = dis.register(this.onAction);
}, },
@ -66,26 +70,26 @@ module.exports = createReactClass({
switch (payload.action) { switch (payload.action) {
case 'view_room': case 'view_room':
if (this.refs.search && payload.clear_search) { if (this._search.current && payload.clear_search) {
this._clearSearch(); this._clearSearch();
} }
break; break;
case 'focus_room_filter': case 'focus_room_filter':
if (this.refs.search) { if (this._search.current) {
this.refs.search.focus(); this._search.current.focus();
} }
break; break;
} }
}, },
onChange: function() { onChange: function() {
if (!this.refs.search) return; if (!this._search.current) return;
this.setState({ searchTerm: this.refs.search.value }); this.setState({ searchTerm: this._search.current.value });
this.onSearch(); this.onSearch();
}, },
onSearch: throttle(function() { onSearch: throttle(function() {
this.props.onSearch(this.refs.search.value); this.props.onSearch(this._search.current.value);
}, 200, {trailing: true, leading: true}), }, 200, {trailing: true, leading: true}),
_onKeyDown: function(ev) { _onKeyDown: function(ev) {
@ -113,7 +117,7 @@ module.exports = createReactClass({
}, },
_clearSearch: function(source) { _clearSearch: function(source) {
this.refs.search.value = ""; this._search.current.value = "";
this.onChange(); this.onChange();
if (this.props.onCleared) { if (this.props.onCleared) {
this.props.onCleared(source); this.props.onCleared(source);
@ -146,7 +150,7 @@ module.exports = createReactClass({
<input <input
key="searchfield" key="searchfield"
type="text" type="text"
ref="search" ref={this._search}
className={"mx_textinput_icon mx_textinput_search " + className} className={"mx_textinput_icon mx_textinput_search " + className}
value={ this.state.searchTerm } value={ this.state.searchTerm }
onFocus={ this._onFocus } onFocus={ this._onFocus }

View file

@ -19,7 +19,7 @@ limitations under the License.
import SettingsStore from "../../settings/SettingsStore"; import SettingsStore from "../../settings/SettingsStore";
import React from 'react'; import React, {createRef} from 'react';
import createReactClass from 'create-react-class'; import createReactClass from 'create-react-class';
import ReactDOM from "react-dom"; import ReactDOM from "react-dom";
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
@ -225,6 +225,8 @@ const TimelinePanel = createReactClass({
MatrixClientPeg.get().on("sync", this.onSync); MatrixClientPeg.get().on("sync", this.onSync);
this._initTimeline(this.props); this._initTimeline(this.props);
this._messagePanel = createRef();
}, },
componentWillReceiveProps: function(newProps) { componentWillReceiveProps: function(newProps) {
@ -425,8 +427,8 @@ const TimelinePanel = createReactClass({
if (payload.action === "edit_event") { if (payload.action === "edit_event") {
const editState = payload.event ? new EditorStateTransfer(payload.event) : null; const editState = payload.event ? new EditorStateTransfer(payload.event) : null;
this.setState({editState}, () => { this.setState({editState}, () => {
if (payload.event && this.refs.messagePanel) { if (payload.event && this._messagePanel.current) {
this.refs.messagePanel.scrollToEventIfNeeded( this._messagePanel.current.scrollToEventIfNeeded(
payload.event.getId(), payload.event.getId(),
); );
} }
@ -442,9 +444,9 @@ const TimelinePanel = createReactClass({
// updates from pagination will happen when the paginate completes. // updates from pagination will happen when the paginate completes.
if (toStartOfTimeline || !data || !data.liveEvent) return; 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 // 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 // events off the other end of the timeline. But we need to note
// that we can now paginate. // that we can now paginate.
@ -499,7 +501,7 @@ const TimelinePanel = createReactClass({
} }
this.setState(updatedState, () => { this.setState(updatedState, () => {
this.refs.messagePanel.updateTimelineMinHeight(); this._messagePanel.current.updateTimelineMinHeight();
if (callRMUpdated) { if (callRMUpdated) {
this.props.onReadMarkerUpdated(); this.props.onReadMarkerUpdated();
} }
@ -510,13 +512,13 @@ const TimelinePanel = createReactClass({
onRoomTimelineReset: function(room, timelineSet) { onRoomTimelineReset: function(room, timelineSet) {
if (timelineSet !== this.props.timelineSet) return; if (timelineSet !== this.props.timelineSet) return;
if (this.refs.messagePanel && this.refs.messagePanel.isAtBottom()) { if (this._messagePanel.current && this._messagePanel.current.isAtBottom()) {
this._loadTimeline(); this._loadTimeline();
} }
}, },
canResetTimeline: function() { canResetTimeline: function() {
return this.refs.messagePanel && this.refs.messagePanel.isAtBottom(); return this._messagePanel.current && this._messagePanel.current.isAtBottom();
}, },
onRoomRedaction: function(ev, room) { onRoomRedaction: function(ev, room) {
@ -629,7 +631,7 @@ const TimelinePanel = createReactClass({
sendReadReceipt: function() { sendReadReceipt: function() {
if (SettingsStore.getValue("lowBandwidth")) return; if (SettingsStore.getValue("lowBandwidth")) return;
if (!this.refs.messagePanel) return; if (!this._messagePanel.current) return;
if (!this.props.manageReadReceipts) return; if (!this.props.manageReadReceipts) return;
// This happens on user_activity_end which is delayed, and it's // This happens on user_activity_end which is delayed, and it's
// very possible have logged out within that timeframe, so check // very possible have logged out within that timeframe, so check
@ -815,8 +817,8 @@ const TimelinePanel = createReactClass({
if (this._timelineWindow.canPaginate(EventTimeline.FORWARDS)) { if (this._timelineWindow.canPaginate(EventTimeline.FORWARDS)) {
this._loadTimeline(); this._loadTimeline();
} else { } else {
if (this.refs.messagePanel) { if (this._messagePanel.current) {
this.refs.messagePanel.scrollToBottom(); this._messagePanel.current.scrollToBottom();
} }
} }
}, },
@ -826,7 +828,7 @@ const TimelinePanel = createReactClass({
*/ */
jumpToReadMarker: function() { jumpToReadMarker: function() {
if (!this.props.manageReadMarkers) return; if (!this.props.manageReadMarkers) return;
if (!this.refs.messagePanel) return; if (!this._messagePanel.current) return;
if (!this.state.readMarkerEventId) return; if (!this.state.readMarkerEventId) return;
// we may not have loaded the event corresponding to the read-marker // 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 // 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. // 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) { if (ret !== null) {
// The messagepanel knows where the RM is, so we must have loaded // The messagepanel knows where the RM is, so we must have loaded
// the relevant event. // the relevant event.
this.refs.messagePanel.scrollToEvent(this.state.readMarkerEventId, this._messagePanel.current.scrollToEvent(this.state.readMarkerEventId,
0, 1/3); 0, 1/3);
return; return;
} }
@ -874,8 +876,8 @@ const TimelinePanel = createReactClass({
* at the end of the live timeline. * at the end of the live timeline.
*/ */
isAtEndOfLiveTimeline: function() { isAtEndOfLiveTimeline: function() {
return this.refs.messagePanel return this._messagePanel.current
&& this.refs.messagePanel.isAtBottom() && this._messagePanel.current.isAtBottom()
&& this._timelineWindow && this._timelineWindow
&& !this._timelineWindow.canPaginate(EventTimeline.FORWARDS); && !this._timelineWindow.canPaginate(EventTimeline.FORWARDS);
}, },
@ -887,8 +889,8 @@ const TimelinePanel = createReactClass({
* returns null if we are not mounted. * returns null if we are not mounted.
*/ */
getScrollState: function() { getScrollState: function() {
if (!this.refs.messagePanel) { return null; } if (!this._messagePanel.current) { return null; }
return this.refs.messagePanel.getScrollState(); return this._messagePanel.current.getScrollState();
}, },
// returns one of: // returns one of:
@ -899,9 +901,9 @@ const TimelinePanel = createReactClass({
// +1: read marker is below the window // +1: read marker is below the window
getReadMarkerPosition: function() { getReadMarkerPosition: function() {
if (!this.props.manageReadMarkers) return null; 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) { if (ret !== null) {
return ret; return ret;
} }
@ -936,7 +938,7 @@ const TimelinePanel = createReactClass({
* We pass it down to the scroll panel. * We pass it down to the scroll panel.
*/ */
handleScrollKey: function(ev) { 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 // jump to the live timeline on ctrl-end, rather than the end of the
// timeline window. // timeline window.
@ -944,7 +946,7 @@ const TimelinePanel = createReactClass({
ev.keyCode == KeyCode.END) { ev.keyCode == KeyCode.END) {
this.jumpToLiveTimeline(); this.jumpToLiveTimeline();
} else { } else {
this.refs.messagePanel.handleScrollKey(ev); this._messagePanel.current.handleScrollKey(ev);
} }
}, },
@ -986,8 +988,8 @@ const TimelinePanel = createReactClass({
const onLoaded = () => { const onLoaded = () => {
// clear the timeline min-height when // clear the timeline min-height when
// (re)loading the timeline // (re)loading the timeline
if (this.refs.messagePanel) { if (this._messagePanel.current) {
this.refs.messagePanel.onTimelineReset(); this._messagePanel.current.onTimelineReset();
} }
this._reloadEvents(); this._reloadEvents();
@ -1002,7 +1004,7 @@ const TimelinePanel = createReactClass({
timelineLoading: false, timelineLoading: false,
}, () => { }, () => {
// initialise the scroll state of the message panel // 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 // this shouldn't happen - we know we're mounted because
// we're in a setState callback, and we know // we're in a setState callback, and we know
// timelineLoading is now false, so render() should have // timelineLoading is now false, so render() should have
@ -1012,10 +1014,10 @@ const TimelinePanel = createReactClass({
return; return;
} }
if (eventId) { if (eventId) {
this.refs.messagePanel.scrollToEvent(eventId, pixelOffset, this._messagePanel.current.scrollToEvent(eventId, pixelOffset,
offsetBase); offsetBase);
} else { } else {
this.refs.messagePanel.scrollToBottom(); this._messagePanel.current.scrollToBottom();
} }
this.sendReadReceipt(); this.sendReadReceipt();
@ -1134,7 +1136,7 @@ const TimelinePanel = createReactClass({
const ignoreOwn = opts.ignoreOwn || false; const ignoreOwn = opts.ignoreOwn || false;
const allowPartial = opts.allowPartial || false; const allowPartial = opts.allowPartial || false;
const messagePanel = this.refs.messagePanel; const messagePanel = this._messagePanel.current;
if (messagePanel === undefined) return null; if (messagePanel === undefined) return null;
const EventTile = sdk.getComponent('rooms.EventTile'); const EventTile = sdk.getComponent('rooms.EventTile');
@ -1313,7 +1315,8 @@ const TimelinePanel = createReactClass({
['PREPARED', 'CATCHUP'].includes(this.state.clientSyncState) ['PREPARED', 'CATCHUP'].includes(this.state.clientSyncState)
); );
return ( return (
<MessagePanel ref="messagePanel" <MessagePanel
ref={this._messagePanel}
room={this.props.timelineSet.room} room={this.props.timelineSet.room}
permalinkCreator={this.props.permalinkCreator} permalinkCreator={this.props.permalinkCreator}
hidden={this.props.hidden} hidden={this.props.hidden}

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React from 'react'; import React, {createRef} from 'react';
import createReactClass from 'create-react-class'; import createReactClass from 'create-react-class';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
@ -48,6 +48,8 @@ module.exports = createReactClass({
componentWillMount: function() { componentWillMount: function() {
this._captchaWidgetId = null; this._captchaWidgetId = null;
this._recaptchaContainer = createRef();
}, },
componentDidMount: function() { componentDidMount: function() {
@ -67,7 +69,7 @@ module.exports = createReactClass({
scriptTag.setAttribute( scriptTag.setAttribute(
'src', `${protocol}//www.recaptcha.net/recaptcha/api.js?onload=mx_on_recaptcha_loaded&render=explicit`, '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 ( return (
<div ref="recaptchaContainer"> <div ref={this._recaptchaContainer}>
<p>{_t( <p>{_t(
"This homeserver would like to make sure you are not a robot.", "This homeserver would like to make sure you are not a robot.",
)}</p> )}</p>
<div id={DIV_ID}></div> <div id={DIV_ID} />
{ error } { error }
</div> </div>
); );

View file

@ -16,7 +16,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React from 'react'; import React, {createRef} from 'react';
import createReactClass from 'create-react-class'; import createReactClass from 'create-react-class';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import url from 'url'; import url from 'url';
@ -581,6 +581,8 @@ export const FallbackAuthEntry = createReactClass({
// the popup if we open it immediately. // the popup if we open it immediately.
this._popupWindow = null; this._popupWindow = null;
window.addEventListener("message", this._onReceiveMessage); window.addEventListener("message", this._onReceiveMessage);
this._fallbackButton = createRef();
}, },
componentWillUnmount: function() { componentWillUnmount: function() {
@ -591,8 +593,8 @@ export const FallbackAuthEntry = createReactClass({
}, },
focus: function() { focus: function() {
if (this.refs.fallbackButton) { if (this._fallbackButton.current) {
this.refs.fallbackButton.focus(); this._fallbackButton.current.focus();
} }
}, },
@ -624,7 +626,7 @@ export const FallbackAuthEntry = createReactClass({
} }
return ( return (
<div> <div>
<a ref="fallbackButton" onClick={this._onShowFallbackClick}>{ _t("Start authentication") }</a> <a ref={this._fallbackButton} onClick={this._onShowFallbackClick}>{ _t("Start authentication") }</a>
{errorSection} {errorSection}
</div> </div>
); );

View file

@ -17,7 +17,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React from 'react'; import React, {createRef} from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import createReactClass from 'create-react-class'; import createReactClass from 'create-react-class';
@ -106,10 +106,14 @@ module.exports = createReactClass({
}; };
}, },
UNSAFE_componentWillMount: function() {
this._textinput = createRef();
},
componentDidMount: function() { componentDidMount: function() {
if (this.props.focus) { if (this.props.focus) {
// Set the cursor at the end of the text input // 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(); let selectedList = this.state.selectedList.slice();
// Check the text input field to see if user has an unconverted address // 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 there is and it's valid add it to the local selectedList
if (this.refs.textinput.value !== '') { if (this._textinput.current.value !== '') {
selectedList = this._addAddressesToList([this.refs.textinput.value]); selectedList = this._addAddressesToList([this._textinput.current.value]);
if (selectedList === null) return; if (selectedList === null) return;
} }
this.props.onFinished(true, selectedList); this.props.onFinished(true, selectedList);
@ -154,23 +158,23 @@ module.exports = createReactClass({
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
if (this.addressSelector) this.addressSelector.chooseSelection(); 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.stopPropagation();
e.preventDefault(); e.preventDefault();
this.onDismissed(this.state.selectedList.length - 1)(); this.onDismissed(this.state.selectedList.length - 1)();
} else if (e.keyCode === 13) { // enter } else if (e.keyCode === 13) { // enter
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
if (this.refs.textinput.value === '') { if (this._textinput.current.value === '') {
// if there's nothing in the input box, submit the form // if there's nothing in the input box, submit the form
this.onButtonClick(); this.onButtonClick();
} else { } else {
this._addAddressesToList([this.refs.textinput.value]); this._addAddressesToList([this._textinput.current.value]);
} }
} else if (e.keyCode === 188 || e.keyCode === 9) { // comma or tab } else if (e.keyCode === 188 || e.keyCode === 9) { // comma or tab
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
this._addAddressesToList([this.refs.textinput.value]); this._addAddressesToList([this._textinput.current.value]);
} }
}, },
@ -647,7 +651,7 @@ module.exports = createReactClass({
onPaste={this._onPaste} onPaste={this._onPaste}
rows="1" rows="1"
id="textinput" id="textinput"
ref="textinput" ref={this._textinput}
className="mx_AddressPickerDialog_input" className="mx_AddressPickerDialog_input"
onChange={this.onQueryChanged} onChange={this.onQueryChanged}
placeholder={this.getPlaceholder()} placeholder={this.getPlaceholder()}

View file

@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React from 'react'; import React, {createRef} from 'react';
import createReactClass from 'create-react-class'; import createReactClass from 'create-react-class';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import sdk from '../../../index'; import sdk from '../../../index';
@ -62,8 +62,13 @@ export default createReactClass({
}; };
}, },
UNSAFE_componentWillMount: function() {
this._input_value = createRef();
this._uiAuth = createRef();
},
componentDidMount: function() { componentDidMount: function() {
this.refs.input_value.select(); this._input_value.current.select();
this._matrixClient = MatrixClientPeg.get(); this._matrixClient = MatrixClientPeg.get();
}, },
@ -102,8 +107,8 @@ export default createReactClass({
}, },
onSubmit: function(ev) { onSubmit: function(ev) {
if (this.refs.uiAuth) { if (this._uiAuth.current) {
this.refs.uiAuth.tryContinue(); this._uiAuth.current.tryContinue();
} }
this.setState({ this.setState({
doingUIAuth: true, doingUIAuth: true,
@ -215,7 +220,7 @@ export default createReactClass({
onAuthFinished={this._onUIAuthFinished} onAuthFinished={this._onUIAuthFinished}
inputs={{}} inputs={{}}
poll={true} poll={true}
ref="uiAuth" ref={this._uiAuth}
continueIsManaged={true} continueIsManaged={true}
/>; />;
} }
@ -257,7 +262,7 @@ export default createReactClass({
> >
<div className="mx_Dialog_content" id='mx_Dialog_content'> <div className="mx_Dialog_content" id='mx_Dialog_content'>
<div className="mx_SetMxIdDialog_input_group"> <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} autoFocus={true}
onChange={this.onValueChange} onChange={this.onValueChange}
onKeyUp={this.onKeyUp} onKeyUp={this.onKeyUp}

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React from 'react'; import React, {createRef} from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import {Room, User, Group, RoomMember, MatrixEvent} from 'matrix-js-sdk'; import {Room, User, Group, RoomMember, MatrixEvent} from 'matrix-js-sdk';
import sdk from '../../../index'; import sdk from '../../../index';
@ -74,6 +74,8 @@ export default class ShareDialog extends React.Component {
// MatrixEvent defaults to share linkSpecificEvent // MatrixEvent defaults to share linkSpecificEvent
linkSpecificEvent: this.props.target instanceof MatrixEvent, linkSpecificEvent: this.props.target instanceof MatrixEvent,
}; };
this._link = createRef();
} }
static _selectText(target) { static _selectText(target) {
@ -94,7 +96,7 @@ export default class ShareDialog extends React.Component {
onCopyClick(e) { onCopyClick(e) {
e.preventDefault(); e.preventDefault();
ShareDialog._selectText(this.refs.link); ShareDialog._selectText(this._link.current);
let successful; let successful;
try { try {
@ -195,7 +197,7 @@ export default class ShareDialog extends React.Component {
> >
<div className="mx_ShareDialog_content"> <div className="mx_ShareDialog_content">
<div className="mx_ShareDialog_matrixto"> <div className="mx_ShareDialog_matrixto">
<a ref="link" <a ref={this._link}
href={matrixToUrl} href={matrixToUrl}
onClick={ShareDialog.onLinkClick} onClick={ShareDialog.onLinkClick}
className="mx_ShareDialog_matrixto_link" className="mx_ShareDialog_matrixto_link"

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React from 'react'; import React, {createRef} from 'react';
import createReactClass from 'create-react-class'; import createReactClass from 'create-react-class';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import sdk from '../../../index'; import sdk from '../../../index';
@ -42,15 +42,19 @@ export default createReactClass({
}; };
}, },
UNSAFE_componentWillMount: function() {
this._textinput = createRef();
},
componentDidMount: function() { componentDidMount: function() {
if (this.props.focus) { if (this.props.focus) {
// Set the cursor at the end of the text input // 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() { onOk: function() {
this.props.onFinished(true, this.refs.textinput.value); this.props.onFinished(true, this._textinput.current.value);
}, },
onCancel: function() { onCancel: function() {
@ -70,7 +74,13 @@ export default createReactClass({
<label htmlFor="textinput"> { this.props.description } </label> <label htmlFor="textinput"> { this.props.description } </label>
</div> </div>
<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>
</div> </div>
</form> </form>

View file

@ -64,6 +64,8 @@ export default class AppTile extends React.Component {
this._onReloadWidgetClick = this._onReloadWidgetClick.bind(this); this._onReloadWidgetClick = this._onReloadWidgetClick.bind(this);
this._contextMenuButton = createRef(); 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 // 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 // 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 // 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 // In practice we could just do `+= ''` to trick the browser
// into thinking the URL changed, however I can foresee this // into thinking the URL changed, however I can foresee this
// being optimized out by a browser. Instead, we'll just point // being optimized out by a browser. Instead, we'll just point
// the iframe at a page that is reasonably safe to use in the // the iframe at a page that is reasonably safe to use in the
// event the iframe doesn't wink away. // event the iframe doesn't wink away.
// This is relative to where the Riot instance is located. // This is relative to where the Riot instance is located.
this.refs.appFrame.src = 'about:blank'; this._appFrame.current.src = 'about:blank';
} }
WidgetUtils.setRoomWidget( 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 // FIXME: There's probably no reason to do this here: it should probably be done entirely
// in ActiveWidgetStore. // in ActiveWidgetStore.
const widgetMessaging = new WidgetMessaging( 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); ActiveWidgetStore.setWidgetMessaging(this.props.id, widgetMessaging);
widgetMessaging.getCapabilities().then((requestedCapabilities) => { widgetMessaging.getCapabilities().then((requestedCapabilities) => {
console.log(`Widget ${this.props.id} requested capabilities: ` + requestedCapabilities); console.log(`Widget ${this.props.id} requested capabilities: ` + requestedCapabilities);
@ -496,7 +498,7 @@ export default class AppTile extends React.Component {
ev.preventDefault(); ev.preventDefault();
// Ignore clicks on menu bar children // Ignore clicks on menu bar children
if (ev.target !== this.refs.menu_bar) { if (ev.target !== this._menu_bar.current) {
return; return;
} }
@ -555,7 +557,7 @@ export default class AppTile extends React.Component {
_onReloadWidgetClick() { _onReloadWidgetClick() {
// Reload iframe in this way to avoid cross-origin restrictions // 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 = () => { _onContextMenuClick = () => {
@ -626,7 +628,7 @@ export default class AppTile extends React.Component {
{ this.state.loading && loadingElement } { this.state.loading && loadingElement }
<iframe <iframe
allow={iframeFeatures} allow={iframeFeatures}
ref="appFrame" ref={this._appFrame}
src={this._getSafeUrl()} src={this._getSafeUrl()}
allowFullScreen={true} allowFullScreen={true}
sandbox={sandboxFlags} sandbox={sandboxFlags}
@ -694,7 +696,7 @@ export default class AppTile extends React.Component {
return <React.Fragment> return <React.Fragment>
<div className={appTileClass} id={this.props.id}> <div className={appTileClass} id={this.props.id}>
{ this.props.showMenubar && { 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)}}> <span className="mx_AppTileMenuBarTitle" style={{pointerEvents: (this.props.handleMinimisePointerEvents ? 'all' : false)}}>
{ /* Minimise widget */ } { /* Minimise widget */ }
{ showMinimiseButton && <AccessibleButton { showMinimiseButton && <AccessibleButton

View file

@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React from 'react'; import React, {createRef} from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import createReactClass from 'create-react-class'; import createReactClass from 'create-react-class';
import {Key} from "../../../Keyboard"; import {Key} from "../../../Keyboard";
@ -65,7 +65,7 @@ module.exports = createReactClass({
componentWillReceiveProps: function(nextProps) { componentWillReceiveProps: function(nextProps) {
if (nextProps.initialValue !== this.props.initialValue) { if (nextProps.initialValue !== this.props.initialValue) {
this.value = nextProps.initialValue; this.value = nextProps.initialValue;
if (this.refs.editable_div) { if (this._editable_div.current) {
this.showPlaceholder(!this.value); this.showPlaceholder(!this.value);
} }
} }
@ -76,24 +76,27 @@ module.exports = createReactClass({
// as React doesn't play nice with contentEditable. // as React doesn't play nice with contentEditable.
this.value = ''; this.value = '';
this.placeholder = false; this.placeholder = false;
this._editable_div = createRef();
}, },
componentDidMount: function() { componentDidMount: function() {
this.value = this.props.initialValue; this.value = this.props.initialValue;
if (this.refs.editable_div) { if (this._editable_div.current) {
this.showPlaceholder(!this.value); this.showPlaceholder(!this.value);
} }
}, },
showPlaceholder: function(show) { showPlaceholder: function(show) {
if (show) { if (show) {
this.refs.editable_div.textContent = this.props.placeholder; this._editable_div.current.textContent = this.props.placeholder;
this.refs.editable_div.setAttribute("class", this.props.className + " " + this.props.placeholderClassName); this._editable_div.current.setAttribute("class", this.props.className
+ " " + this.props.placeholderClassName);
this.placeholder = true; this.placeholder = true;
this.value = ''; this.value = '';
} else { } else {
this.refs.editable_div.textContent = this.value; this._editable_div.current.textContent = this.value;
this.refs.editable_div.setAttribute("class", this.props.className); this._editable_div.current.setAttribute("class", this.props.className);
this.placeholder = false; this.placeholder = false;
} }
}, },
@ -120,7 +123,7 @@ module.exports = createReactClass({
this.value = this.props.initialValue; this.value = this.props.initialValue;
this.showPlaceholder(!this.value); this.showPlaceholder(!this.value);
this.onValueChanged(false); this.onValueChanged(false);
this.refs.editable_div.blur(); this._editable_div.current.blur();
}, },
onValueChanged: function(shouldSubmit) { onValueChanged: function(shouldSubmit) {
@ -219,7 +222,7 @@ module.exports = createReactClass({
</div>; </div>;
} else { } else {
// show the content editable div, but manually manage its contents as react and contentEditable don't play nice together // 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} contentEditable={true}
className={className} className={className}
onKeyDown={this.onKeyDown} onKeyDown={this.onKeyDown}

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React from 'react'; import React, {createRef} from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import createReactClass from 'create-react-class'; import createReactClass from 'create-react-class';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
@ -34,6 +34,10 @@ module.exports = createReactClass({
}; };
}, },
UNSAFE_componentWillMount: function() {
this._user_id_input = createRef();
},
addUser: function(user_id) { addUser: function(user_id) {
if (this.props.selected_users.indexOf(user_id == -1)) { if (this.props.selected_users.indexOf(user_id == -1)) {
this.props.onChange(this.props.selected_users.concat([user_id])); this.props.onChange(this.props.selected_users.concat([user_id]));
@ -47,20 +51,20 @@ module.exports = createReactClass({
}, },
onAddUserId: function() { onAddUserId: function() {
this.addUser(this.refs.user_id_input.value); this.addUser(this._user_id_input.current.value);
this.refs.user_id_input.value = ""; this._user_id_input.current.value = "";
}, },
render: function() { render: function() {
const self = this; const self = this;
return ( return (
<div> <div>
<ul className="mx_UserSelector_UserIdList" ref="list"> <ul className="mx_UserSelector_UserIdList">
{ this.props.selected_users.map(function(user_id, i) { { 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>; return <li key={user_id}>{ user_id } - <span onClick={function() {self.removeUser(user_id);}}>X</span></li>;
}) } }) }
</ul> </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"> <button onClick={this.onAddUserId} className="mx_UserSelector_AddUserId">
{ _t("Add User") } { _t("Add User") }
</button> </button>

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React from 'react'; import React, {createRef} from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import * as HtmlUtils from '../../../HtmlUtils'; import * as HtmlUtils from '../../../HtmlUtils';
import { editBodyDiffToHtml } from '../../../utils/MessageDiffUtils'; import { editBodyDiffToHtml } from '../../../utils/MessageDiffUtils';
@ -51,6 +51,8 @@ export default class EditHistoryMessage extends React.PureComponent {
} }
const canRedact = room.currentState.maySendRedactionForEvent(event, userId); const canRedact = room.currentState.maySendRedactionForEvent(event, userId);
this.state = {canRedact, sendStatus: event.getAssociatedStatus()}; this.state = {canRedact, sendStatus: event.getAssociatedStatus()};
this._content = createRef();
} }
_onAssociatedStatusChanged = () => { _onAssociatedStatusChanged = () => {
@ -78,8 +80,8 @@ export default class EditHistoryMessage extends React.PureComponent {
pillifyLinks() { pillifyLinks() {
// not present for redacted events // not present for redacted events
if (this.refs.content) { if (this._content.current) {
pillifyLinks(this.refs.content.children, this.props.mxEvent); 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") { if (mxEvent.getContent().msgtype === "m.emote") {
const name = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender(); const name = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender();
contentContainer = ( contentContainer = (
<div className="mx_EventTile_content" ref="content">*&nbsp; <div className="mx_EventTile_content" ref={this._content}>*&nbsp;
<span className="mx_MEmoteBody_sender">{ name }</span> <span className="mx_MEmoteBody_sender">{ name }</span>
&nbsp;{contentElements} &nbsp;{contentElements}
</div> </div>
); );
} else { } else {
contentContainer = <div className="mx_EventTile_content" ref="content">{contentElements}</div>; contentContainer = <div className="mx_EventTile_content" ref={this._content}>{contentElements}</div>;
} }
} }

View file

@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React from 'react'; import React, {createRef} from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import createReactClass from 'create-react-class'; import createReactClass from 'create-react-class';
import filesize from 'filesize'; import filesize from 'filesize';
@ -251,6 +251,12 @@ module.exports = createReactClass({
return MatrixClientPeg.get().mxcUrlToHttp(content.url); return MatrixClientPeg.get().mxcUrlToHttp(content.url);
}, },
UNSAFE_componentWillMount: function() {
this._iframe = createRef();
this._dummyLink = createRef();
this._downloadImage = createRef();
},
componentDidMount: function() { componentDidMount: function() {
// Add this to the list of mounted components to receive notifications // Add this to the list of mounted components to receive notifications
// when the tint changes. // when the tint changes.
@ -272,17 +278,17 @@ module.exports = createReactClass({
tint: function() { tint: function() {
// Update our tinted copy of require("../../../../res/img/download.svg") // Update our tinted copy of require("../../../../res/img/download.svg")
if (this.refs.downloadImage) { if (this._downloadImage.current) {
this.refs.downloadImage.src = tintedDownloadImageURL; this._downloadImage.current.src = tintedDownloadImageURL;
} }
if (this.refs.iframe) { if (this._iframe.current) {
// If the attachment is encrypted then the download image // If the attachment is encrypted then the download image
// will be inside the iframe so we wont be able to update // will be inside the iframe so we wont be able to update
// it directly. // it directly.
this.refs.iframe.contentWindow.postMessage({ this._iframe.current.contentWindow.postMessage({
code: remoteSetTint.toString(), code: remoteSetTint.toString(),
imgSrc: tintedDownloadImageURL, imgSrc: tintedDownloadImageURL,
style: computedStyle(this.refs.dummyLink), style: computedStyle(this._dummyLink.current),
}, "*"); }, "*");
} }
}, },
@ -340,7 +346,7 @@ module.exports = createReactClass({
ev.target.contentWindow.postMessage({ ev.target.contentWindow.postMessage({
code: remoteRender.toString(), code: remoteRender.toString(),
imgSrc: tintedDownloadImageURL, imgSrc: tintedDownloadImageURL,
style: computedStyle(this.refs.dummyLink), style: computedStyle(this._dummyLink.current),
blob: this.state.decryptedBlob, blob: this.state.decryptedBlob,
// Set a download attribute for encrypted files so that the file // Set a download attribute for encrypted files so that the file
// will have the correct name when the user tries to download it. // 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 * We'll use it to learn how the download link
* would have been styled if it was rendered inline. * would have been styled if it was rendered inline.
*/ } */ }
<a ref="dummyLink" /> <a ref={this._dummyLink} />
</div> </div>
<iframe src={renderer_url} onLoad={onIframeLoad} ref="iframe" /> <iframe src={renderer_url} onLoad={onIframeLoad} ref={this._iframe} />
</div> </div>
</span> </span>
); );
@ -439,7 +445,7 @@ module.exports = createReactClass({
<span className="mx_MFileBody"> <span className="mx_MFileBody">
<div className="mx_MFileBody_download"> <div className="mx_MFileBody_download">
<a {...downloadProps}> <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 }) } { _t("Download %(text)s", { text: text }) }
</a> </a>
</div> </div>

View file

@ -16,7 +16,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React from 'react'; import React, {createRef} from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { MatrixClient } from 'matrix-js-sdk'; import { MatrixClient } from 'matrix-js-sdk';
@ -65,6 +65,8 @@ export default class MImageBody extends React.Component {
hover: false, hover: false,
showImage: SettingsStore.getValue("showImages"), showImage: SettingsStore.getValue("showImages"),
}; };
this._image = createRef();
} }
componentWillMount() { componentWillMount() {
@ -158,8 +160,8 @@ export default class MImageBody extends React.Component {
let loadedImageDimensions; let loadedImageDimensions;
if (this.refs.image) { if (this._image.current) {
const { naturalWidth, naturalHeight } = this.refs.image; const { naturalWidth, naturalHeight } = this._image.current;
// this is only used as a fallback in case content.info.w/h is missing // this is only used as a fallback in case content.info.w/h is missing
loadedImageDimensions = { naturalWidth, naturalHeight }; loadedImageDimensions = { naturalWidth, naturalHeight };
} }
@ -342,7 +344,7 @@ export default class MImageBody extends React.Component {
imageElement = <HiddenImagePlaceholder />; imageElement = <HiddenImagePlaceholder />;
} else { } else {
imageElement = ( imageElement = (
<img style={{display: 'none'}} src={thumbUrl} ref="image" <img style={{display: 'none'}} src={thumbUrl} ref={this._image}
alt={content.body} alt={content.body}
onError={this.onImageError} onError={this.onImageError}
onLoad={this.onImageLoad} onLoad={this.onImageLoad}
@ -385,7 +387,7 @@ export default class MImageBody extends React.Component {
// which has the same width as the timeline // which has the same width as the timeline
// mx_MImageBody_thumbnail resizes img to exactly container size // mx_MImageBody_thumbnail resizes img to exactly container size
img = ( img = (
<img className="mx_MImageBody_thumbnail" src={thumbUrl} ref="image" <img className="mx_MImageBody_thumbnail" src={thumbUrl} ref={this._image}
style={{ maxWidth: maxWidth + "px" }} style={{ maxWidth: maxWidth + "px" }}
alt={content.body} alt={content.body}
onError={this.onImageError} onError={this.onImageError}

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React from 'react'; import React, {createRef} from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import createReactClass from 'create-react-class'; import createReactClass from 'create-react-class';
import sdk from '../../../index'; import sdk from '../../../index';
@ -47,8 +47,12 @@ module.exports = createReactClass({
maxImageHeight: PropTypes.number, maxImageHeight: PropTypes.number,
}, },
UNSAFE_componentWillMount: function() {
this._body = createRef();
},
getEventTileOps: function() { 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() { onTileUpdate: function() {
@ -103,7 +107,8 @@ module.exports = createReactClass({
} }
return <BodyType return <BodyType
ref="body" mxEvent={this.props.mxEvent} ref={this._body}
mxEvent={this.props.mxEvent}
highlights={this.props.highlights} highlights={this.props.highlights}
highlightLink={this.props.highlightLink} highlightLink={this.props.highlightLink}
showUrlPreview={this.props.showUrlPreview} showUrlPreview={this.props.showUrlPreview}

View file

@ -16,7 +16,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React from 'react'; import React, {createRef} from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import createReactClass from 'create-react-class'; import createReactClass from 'create-react-class';
@ -86,6 +86,10 @@ module.exports = createReactClass({
return successful; return successful;
}, },
UNSAFE_componentWillMount: function() {
this._content = createRef();
},
componentDidMount: function() { componentDidMount: function() {
this._unmounted = false; this._unmounted = false;
if (!this.props.editState) { if (!this.props.editState) {
@ -94,13 +98,13 @@ module.exports = createReactClass({
}, },
_applyFormatting() { _applyFormatting() {
this.activateSpoilers(this.refs.content.children); this.activateSpoilers(this._content.current.children);
// pillifyLinks BEFORE linkifyElement because plain room/user URLs in the composer // 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, // 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. // we should be pillify them here by doing the linkifying BEFORE the pillifying.
pillifyLinks(this.refs.content.children, this.props.mxEvent); pillifyLinks(this._content.current.children, this.props.mxEvent);
HtmlUtils.linkifyElement(this.refs.content); HtmlUtils.linkifyElement(this._content.current);
this.calculateUrlPreview(); this.calculateUrlPreview();
if (this.props.mxEvent.getContent().format === "org.matrix.custom.html") { 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); //console.info("calculateUrlPreview: ShowUrlPreview for %s is %s", this.props.mxEvent.getId(), this.props.showUrlPreview);
if (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) { if (links.length) {
// de-dup the links (but preserve ordering) // de-dup the links (but preserve ordering)
const seen = new Set(); const seen = new Set();
@ -327,7 +331,7 @@ module.exports = createReactClass({
}, },
getInnerText: () => { getInnerText: () => {
return this.refs.content.innerText; return this._content.current.innerText;
}, },
}; };
}, },
@ -452,7 +456,7 @@ module.exports = createReactClass({
case "m.emote": case "m.emote":
const name = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender(); const name = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender();
return ( return (
<span ref="content" className="mx_MEmoteBody mx_EventTile_content"> <span ref={this._content} className="mx_MEmoteBody mx_EventTile_content">
*&nbsp; *&nbsp;
<span <span
className="mx_MEmoteBody_sender" className="mx_MEmoteBody_sender"
@ -467,14 +471,14 @@ module.exports = createReactClass({
); );
case "m.notice": case "m.notice":
return ( return (
<span ref="content" className="mx_MNoticeBody mx_EventTile_content"> <span ref={this._content} className="mx_MNoticeBody mx_EventTile_content">
{ body } { body }
{ widgets } { widgets }
</span> </span>
); );
default: // including "m.text" default: // including "m.text"
return ( return (
<span ref="content" className="mx_MTextBody mx_EventTile_content"> <span ref={this._content} className="mx_MTextBody mx_EventTile_content">
{ body } { body }
{ widgets } { widgets }
</span> </span>

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React from 'react'; import React, {createRef} from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import {_t} from "../../../languageHandler"; import {_t} from "../../../languageHandler";
import MatrixClientPeg from "../../../MatrixClientPeg"; import MatrixClientPeg from "../../../MatrixClientPeg";
@ -58,13 +58,15 @@ export default class RoomProfileSettings extends React.Component {
canSetTopic: room.currentState.maySendStateEvent('m.room.topic', client.getUserId()), canSetTopic: room.currentState.maySendStateEvent('m.room.topic', client.getUserId()),
canSetAvatar: room.currentState.maySendStateEvent('m.room.avatar', client.getUserId()), canSetAvatar: room.currentState.maySendStateEvent('m.room.avatar', client.getUserId()),
}; };
this._avatarUpload = createRef();
} }
_uploadAvatar = (e) => { _uploadAvatar = (e) => {
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
this.refs.avatarUpload.click(); this._avatarUpload.current.click();
}; };
_saveProfile = async (e) => { _saveProfile = async (e) => {
@ -178,7 +180,7 @@ export default class RoomProfileSettings extends React.Component {
return ( return (
<form onSubmit={this._saveProfile} autoComplete="off" noValidate={true}> <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/*" /> onChange={this._onAvatarChanged} accept="image/*" />
<div className="mx_ProfileSettings_profile"> <div className="mx_ProfileSettings_profile">
<div className="mx_ProfileSettings_controls"> <div className="mx_ProfileSettings_controls">

View file

@ -19,7 +19,7 @@ limitations under the License.
import ReplyThread from "../elements/ReplyThread"; import ReplyThread from "../elements/ReplyThread";
import React from 'react'; import React, {createRef} from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import createReactClass from 'create-react-class'; import createReactClass from 'create-react-class';
const classNames = require("classnames"); const classNames = require("classnames");
@ -224,6 +224,9 @@ module.exports = createReactClass({
// don't do RR animations until we are mounted // don't do RR animations until we are mounted
this._suppressReadReceiptAnimation = true; this._suppressReadReceiptAnimation = true;
this._verifyEvent(this.props.mxEvent); this._verifyEvent(this.props.mxEvent);
this._tile = createRef();
this._replyThread = createRef();
}, },
componentDidMount: function() { componentDidMount: function() {
@ -512,11 +515,11 @@ module.exports = createReactClass({
}, },
getTile() { getTile() {
return this.refs.tile; return this._tile.current;
}, },
getReplyThread() { getReplyThread() {
return this.refs.replyThread; return this._replyThread.current;
}, },
getReactions() { getReactions() {
@ -748,7 +751,7 @@ module.exports = createReactClass({
</a> </a>
</div> </div>
<div className="mx_EventTile_line"> <div className="mx_EventTile_line">
<EventTileType ref="tile" <EventTileType ref={this._tile}
mxEvent={this.props.mxEvent} mxEvent={this.props.mxEvent}
highlights={this.props.highlights} highlights={this.props.highlights}
highlightLink={this.props.highlightLink} highlightLink={this.props.highlightLink}
@ -762,7 +765,7 @@ module.exports = createReactClass({
return ( return (
<div className={classes}> <div className={classes}>
<div className="mx_EventTile_line"> <div className="mx_EventTile_line">
<EventTileType ref="tile" <EventTileType ref={this._tile}
mxEvent={this.props.mxEvent} mxEvent={this.props.mxEvent}
highlights={this.props.highlights} highlights={this.props.highlights}
highlightLink={this.props.highlightLink} highlightLink={this.props.highlightLink}
@ -792,7 +795,7 @@ module.exports = createReactClass({
this.props.mxEvent, this.props.mxEvent,
this.props.onHeightChanged, this.props.onHeightChanged,
this.props.permalinkCreator, this.props.permalinkCreator,
'replyThread', this._replyThread,
); );
} }
return ( return (
@ -805,7 +808,7 @@ module.exports = createReactClass({
</a> </a>
{ !isBubbleMessage && this._renderE2EPadlock() } { !isBubbleMessage && this._renderE2EPadlock() }
{ thread } { thread }
<EventTileType ref="tile" <EventTileType ref={this._tile}
mxEvent={this.props.mxEvent} mxEvent={this.props.mxEvent}
highlights={this.props.highlights} highlights={this.props.highlights}
highlightLink={this.props.highlightLink} highlightLink={this.props.highlightLink}
@ -820,7 +823,7 @@ module.exports = createReactClass({
this.props.mxEvent, this.props.mxEvent,
this.props.onHeightChanged, this.props.onHeightChanged,
this.props.permalinkCreator, 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 // tab-index=-1 to allow it to be focusable but do not add tab stop for it, primarily for screen readers
return ( return (
@ -839,7 +842,7 @@ module.exports = createReactClass({
</a> </a>
{ !isBubbleMessage && this._renderE2EPadlock() } { !isBubbleMessage && this._renderE2EPadlock() }
{ thread } { thread }
<EventTileType ref="tile" <EventTileType ref={this._tile}
mxEvent={this.props.mxEvent} mxEvent={this.props.mxEvent}
replacingEventId={this.props.replacingEventId} replacingEventId={this.props.replacingEventId}
editState={this.props.editState} editState={this.props.editState}

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React from 'react'; import React, {createRef} from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import createReactClass from 'create-react-class'; import createReactClass from 'create-react-class';
import { linkifyElement } from '../../../HtmlUtils'; import { linkifyElement } from '../../../HtmlUtils';
@ -54,17 +54,19 @@ module.exports = createReactClass({
}, (error)=>{ }, (error)=>{
console.error("Failed to get URL preview: " + error); console.error("Failed to get URL preview: " + error);
}); });
this._description = createRef();
}, },
componentDidMount: function() { componentDidMount: function() {
if (this.refs.description) { if (this._description.current) {
linkifyElement(this.refs.description); linkifyElement(this._description.current);
} }
}, },
componentDidUpdate: function() { componentDidUpdate: function() {
if (this.refs.description) { if (this._description.current) {
linkifyElement(this.refs.description); linkifyElement(this._description.current);
} }
}, },
@ -129,7 +131,7 @@ module.exports = createReactClass({
<div className="mx_LinkPreviewWidget_caption"> <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_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_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"] } { p["og:description"] }
</div> </div>
</div> </div>

View file

@ -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 See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React from 'react'; import React, {createRef} from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import CallHandler from '../../../CallHandler'; import CallHandler from '../../../CallHandler';
@ -111,6 +111,8 @@ class UploadButton extends React.Component {
super(props, context); super(props, context);
this.onUploadClick = this.onUploadClick.bind(this); this.onUploadClick = this.onUploadClick.bind(this);
this.onUploadFileInputChange = this.onUploadFileInputChange.bind(this); this.onUploadFileInputChange = this.onUploadFileInputChange.bind(this);
this._uploadInput = createRef();
} }
onUploadClick(ev) { onUploadClick(ev) {
@ -118,7 +120,7 @@ class UploadButton extends React.Component {
dis.dispatch({action: 'require_registration'}); dis.dispatch({action: 'require_registration'});
return; return;
} }
this.refs.uploadInput.click(); this._uploadInput.current.click();
} }
onUploadFileInputChange(ev) { onUploadFileInputChange(ev) {
@ -150,7 +152,9 @@ class UploadButton extends React.Component {
onClick={this.onUploadClick} onClick={this.onUploadClick}
title={_t('Upload file')} title={_t('Upload file')}
> >
<input ref="uploadInput" type="file" <input
ref={this._uploadInput}
type="file"
style={uploadInputStyle} style={uploadInputStyle}
multiple multiple
onChange={this.onUploadFileInputChange} onChange={this.onUploadFileInputChange}

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React from "react"; import React, {createRef} from "react";
import dis from "../../../dispatcher"; import dis from "../../../dispatcher";
import MatrixClientPeg from "../../../MatrixClientPeg"; import MatrixClientPeg from "../../../MatrixClientPeg";
import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; 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 // 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. // started waiting for them. Used to track a room over an upgrade/autojoin.
this._waitingRoomQueue = [/* { roomId, addedTs } */]; this._waitingRoomQueue = [/* { roomId, addedTs } */];
this._scroller = createRef();
} }
componentWillMount() { componentWillMount() {
@ -284,8 +286,8 @@ export default class RoomBreadcrumbs extends React.Component {
} }
this.setState({rooms}); this.setState({rooms});
if (this.refs.scroller) { if (this._scroller.current) {
this.refs.scroller.moveToOrigin(); this._scroller.current.moveToOrigin();
} }
// We don't track room aesthetics (badges, membership, etc) over the wire so we // 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 ( return (
<div role="toolbar" aria-label={_t("Recent rooms")}> <div role="toolbar" aria-label={_t("Recent rooms")}>
<IndicatorScrollbar <IndicatorScrollbar
ref="scroller" ref={this._scroller}
className="mx_RoomBreadcrumbs" className="mx_RoomBreadcrumbs"
trackHorizontalOverflow={true} trackHorizontalOverflow={true}
verticalScrollsHorizontally={true} verticalScrollsHorizontally={true}

View file

@ -15,7 +15,7 @@ limitations under the License.
*/ */
import sdk from '../../../index'; import sdk from '../../../index';
import React from 'react'; import React, {createRef} from 'react';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import { linkifyElement } from '../../../HtmlUtils'; import { linkifyElement } from '../../../HtmlUtils';
import { ContentRepo } from 'matrix-js-sdk'; import { ContentRepo } from 'matrix-js-sdk';
@ -49,11 +49,15 @@ export default createReactClass({
}, },
_linkifyTopic: function() { _linkifyTopic: function() {
if (this.refs.topic) { if (this._topic.current) {
linkifyElement(this.refs.topic); linkifyElement(this._topic.current);
} }
}, },
UNSAFE_componentWillMount: function() {
this._topic = createRef();
},
componentDidMount: function() { componentDidMount: function() {
this._linkifyTopic(); this._linkifyTopic();
}, },
@ -104,7 +108,7 @@ export default createReactClass({
<td className="mx_RoomDirectory_roomDescription"> <td className="mx_RoomDirectory_roomDescription">
<div className="mx_RoomDirectory_name">{ name }</div>&nbsp; <div className="mx_RoomDirectory_name">{ name }</div>&nbsp;
{ perms } { perms }
<div className="mx_RoomDirectory_topic" ref="topic" onClick={this.onTopicClick}> <div className="mx_RoomDirectory_topic" ref={this._topic} onClick={this.onTopicClick}>
{ room.topic } { room.topic }
</div> </div>
<div className="mx_RoomDirectory_alias">{ getDisplayAliasForRoom(room) }</div> <div className="mx_RoomDirectory_alias">{ getDisplayAliasForRoom(room) }</div>

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React from 'react'; import React, {createRef} from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import createReactClass from 'create-react-class'; import createReactClass from 'create-react-class';
import classNames from 'classnames'; import classNames from 'classnames';
@ -56,6 +56,10 @@ module.exports = createReactClass({
}; };
}, },
UNSAFE_componentWillMount: function() {
this._topic = createRef();
},
componentDidMount: function() { componentDidMount: function() {
const cli = MatrixClientPeg.get(); const cli = MatrixClientPeg.get();
cli.on("RoomState.events", this._onRoomStateEvents); cli.on("RoomState.events", this._onRoomStateEvents);
@ -70,8 +74,8 @@ module.exports = createReactClass({
}, },
componentDidUpdate: function() { componentDidUpdate: function() {
if (this.refs.topic) { if (this._topic.current) {
linkifyElement(this.refs.topic); linkifyElement(this._topic.current);
} }
}, },
@ -204,7 +208,7 @@ module.exports = createReactClass({
} }
} }
const topicElement = 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; const avatarSize = 28;
let roomAvatar; let roomAvatar;
if (this.props.room) { if (this.props.room) {

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React from 'react'; import React, {createRef} from 'react';
import createReactClass from 'create-react-class'; import createReactClass from 'create-react-class';
const classNames = require('classnames'); const classNames = require('classnames');
const AccessibleButton = require('../../../components/views/elements/AccessibleButton'); const AccessibleButton = require('../../../components/views/elements/AccessibleButton');
@ -29,6 +29,10 @@ module.exports = createReactClass({
}); });
}, },
UNSAFE_componentWillMount: function() {
this._search_term = createRef();
},
onThisRoomClick: function() { onThisRoomClick: function() {
this.setState({ scope: 'Room' }, () => this._searchIfQuery()); this.setState({ scope: 'Room' }, () => this._searchIfQuery());
}, },
@ -47,13 +51,13 @@ module.exports = createReactClass({
}, },
_searchIfQuery: function() { _searchIfQuery: function() {
if (this.refs.search_term.value) { if (this._search_term.current.value) {
this.onSearch(); this.onSearch();
} }
}, },
onSearch: function() { 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() { render: function() {
@ -66,10 +70,10 @@ module.exports = createReactClass({
<AccessibleButton className={ thisRoomClasses } onClick={this.onThisRoomClick}>{_t("This Room")}</AccessibleButton> <AccessibleButton className={ thisRoomClasses } onClick={this.onThisRoomClick}>{_t("This Room")}</AccessibleButton>
<AccessibleButton className={ allRoomsClasses } onClick={this.onAllRoomsClick}>{_t("All Rooms")}</AccessibleButton> <AccessibleButton className={ allRoomsClasses } onClick={this.onAllRoomsClick}>{_t("All Rooms")}</AccessibleButton>
<div className="mx_SearchBar_input mx_textinput"> <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}></AccessibleButton> <AccessibleButton className={ searchButtonClasses } onClick={this.onSearch} />
</div> </div>
<AccessibleButton className="mx_SearchBar_cancel" onClick={this.props.onCancelClick}></AccessibleButton> <AccessibleButton className="mx_SearchBar_cancel" onClick={this.props.onCancelClick} />
</div> </div>
); );
}, },

View file

@ -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 See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React from 'react'; import React, {createRef} from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { _t, _td } from '../../../languageHandler'; import { _t, _td } from '../../../languageHandler';
import CallHandler from '../../../CallHandler'; import CallHandler from '../../../CallHandler';
import MatrixClientPeg from '../../../MatrixClientPeg'; import MatrixClientPeg from '../../../MatrixClientPeg';
import Modal from '../../../Modal';
import sdk from '../../../index'; import sdk from '../../../index';
import dis from '../../../dispatcher'; import dis from '../../../dispatcher';
import RoomViewStore from '../../../stores/RoomViewStore'; import RoomViewStore from '../../../stores/RoomViewStore';
@ -27,7 +26,6 @@ import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore";
import Stickerpicker from './Stickerpicker'; import Stickerpicker from './Stickerpicker';
import { makeRoomPermalink } from '../../../utils/permalinks/Permalinks'; import { makeRoomPermalink } from '../../../utils/permalinks/Permalinks';
import ContentMessages from '../../../ContentMessages'; import ContentMessages from '../../../ContentMessages';
import classNames from 'classnames';
import E2EIcon from './E2EIcon'; import E2EIcon from './E2EIcon';
@ -143,6 +141,8 @@ class UploadButton extends React.Component {
super(props, context); super(props, context);
this.onUploadClick = this.onUploadClick.bind(this); this.onUploadClick = this.onUploadClick.bind(this);
this.onUploadFileInputChange = this.onUploadFileInputChange.bind(this); this.onUploadFileInputChange = this.onUploadFileInputChange.bind(this);
this._uploadInput = createRef();
} }
onUploadClick(ev) { onUploadClick(ev) {
@ -150,7 +150,7 @@ class UploadButton extends React.Component {
dis.dispatch({action: 'require_registration'}); dis.dispatch({action: 'require_registration'});
return; return;
} }
this.refs.uploadInput.click(); this._uploadInput.current.click();
} }
onUploadFileInputChange(ev) { onUploadFileInputChange(ev) {
@ -182,7 +182,7 @@ class UploadButton extends React.Component {
onClick={this.onUploadClick} onClick={this.onUploadClick}
title={_t('Upload file')} title={_t('Upload file')}
> >
<input ref="uploadInput" type="file" <input ref={this._uploadInput} type="file"
style={uploadInputStyle} style={uploadInputStyle}
multiple multiple
onChange={this.onUploadFileInputChange} onChange={this.onUploadFileInputChange}

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React from 'react'; import React, {createRef} from 'react';
import {_t} from "../../../languageHandler"; import {_t} from "../../../languageHandler";
import MatrixClientPeg from "../../../MatrixClientPeg"; import MatrixClientPeg from "../../../MatrixClientPeg";
import Field from "../elements/Field"; import Field from "../elements/Field";
@ -48,13 +48,15 @@ export default class ProfileSettings extends React.Component {
avatarFile: null, avatarFile: null,
enableProfileSave: false, enableProfileSave: false,
}; };
this._avatarUpload = createRef();
} }
_uploadAvatar = (e) => { _uploadAvatar = (e) => {
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
this.refs.avatarUpload.click(); this._avatarUpload.current.click();
}; };
_saveProfile = async (e) => { _saveProfile = async (e) => {
@ -156,7 +158,7 @@ export default class ProfileSettings extends React.Component {
return ( return (
<form onSubmit={this._saveProfile} autoComplete="off" noValidate={true}> <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/*" /> onChange={this._onAvatarChanged} accept="image/*" />
<div className="mx_ProfileSettings_profile"> <div className="mx_ProfileSettings_profile">
<div className="mx_ProfileSettings_controls"> <div className="mx_ProfileSettings_controls">

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React from 'react'; import React, {createRef} from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import {_t} from "../../../../../languageHandler"; import {_t} from "../../../../../languageHandler";
import MatrixClientPeg from "../../../../../MatrixClientPeg"; import MatrixClientPeg from "../../../../../MatrixClientPeg";
@ -44,13 +44,15 @@ export default class NotificationsSettingsTab extends React.Component {
} }
this.setState({currentSound: soundData.name || soundData.url}); this.setState({currentSound: soundData.name || soundData.url});
}); });
this._soundUpload = createRef();
} }
async _triggerUploader(e) { async _triggerUploader(e) {
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
this.refs.soundUpload.click(); this._soundUpload.current.click();
} }
async _onSoundUploadChanged(e) { async _onSoundUploadChanged(e) {
@ -157,7 +159,7 @@ export default class NotificationsSettingsTab extends React.Component {
<div> <div>
<h3>{_t("Set a new custom sound")}</h3> <h3>{_t("Set a new custom sound")}</h3>
<form autoComplete="off" noValidate={true}> <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> </form>
{currentUploadedFile} {currentUploadedFile}

View file

@ -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 See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React from 'react'; import React, {createRef} from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import createReactClass from 'create-react-class'; import createReactClass from 'create-react-class';
import dis from '../../../dispatcher'; import dis from '../../../dispatcher';
@ -56,6 +56,10 @@ module.exports = createReactClass({
}; };
}, },
UNSAFE_componentWillMount: function() {
this._video = createRef();
},
componentDidMount: function() { componentDidMount: function() {
this.dispatcherRef = dis.register(this.onAction); this.dispatcherRef = dis.register(this.onAction);
this.showCall(); this.showCall();
@ -128,7 +132,7 @@ module.exports = createReactClass({
}, },
getVideoView: function() { getVideoView: function() {
return this.refs.video; return this._video.current;
}, },
render: function() { render: function() {
@ -147,7 +151,9 @@ module.exports = createReactClass({
return ( return (
<div> <div>
<VideoView ref="video" onClick={this.props.onClick} <VideoView
ref={this._video}
onClick={this.props.onClick}
onResize={this.props.onResize} onResize={this.props.onResize}
maxHeight={this.props.maxVideoHeight} maxHeight={this.props.maxVideoHeight}
/> />

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React from 'react'; import React, {createRef} from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import createReactClass from 'create-react-class'; import createReactClass from 'create-react-class';
@ -30,12 +30,16 @@ module.exports = createReactClass({
onResize: PropTypes.func, onResize: PropTypes.func,
}, },
UNSAFE_componentWillMount() {
this._vid = createRef();
},
componentDidMount() { componentDidMount() {
this.refs.vid.addEventListener('resize', this.onResize); this._vid.current.addEventListener('resize', this.onResize);
}, },
componentWillUnmount() { componentWillUnmount() {
this.refs.vid.removeEventListener('resize', this.onResize); this._vid.current.removeEventListener('resize', this.onResize);
}, },
onResize: function(e) { onResize: function(e) {
@ -46,7 +50,7 @@ module.exports = createReactClass({
render: function() { render: function() {
return ( return (
<video ref="vid" style={{maxHeight: this.props.maxHeight}}> <video ref={this._vid} style={{maxHeight: this.props.maxHeight}}>
</video> </video>
); );
}, },

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React from 'react'; import React, {createRef} from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import createReactClass from 'create-react-class'; import createReactClass from 'create-react-class';
@ -49,6 +49,11 @@ module.exports = createReactClass({
onResize: PropTypes.func, onResize: PropTypes.func,
}, },
UNSAFE_componentWillMount: function() {
this._local = createRef();
this._remote = createRef();
},
componentDidMount: function() { componentDidMount: function() {
this.dispatcherRef = dis.register(this.onAction); this.dispatcherRef = dis.register(this.onAction);
}, },
@ -58,7 +63,7 @@ module.exports = createReactClass({
}, },
getRemoteVideoElement: function() { getRemoteVideoElement: function() {
return ReactDOM.findDOMNode(this.refs.remote); return ReactDOM.findDOMNode(this._remote.current);
}, },
getRemoteAudioElement: function() { getRemoteAudioElement: function() {
@ -74,7 +79,7 @@ module.exports = createReactClass({
}, },
getLocalVideoElement: function() { getLocalVideoElement: function() {
return ReactDOM.findDOMNode(this.refs.local); return ReactDOM.findDOMNode(this._local.current);
}, },
setContainer: function(c) { setContainer: function(c) {
@ -125,11 +130,11 @@ module.exports = createReactClass({
return ( return (
<div className="mx_VideoView" ref={this.setContainer} onClick={this.props.onClick}> <div className="mx_VideoView" ref={this.setContainer} onClick={this.props.onClick}>
<div className="mx_VideoView_remoteVideoFeed"> <div className="mx_VideoView_remoteVideoFeed">
<VideoFeed ref="remote" onResize={this.props.onResize} <VideoFeed ref={this._remote} onResize={this.props.onResize}
maxHeight={maxVideoHeight} /> maxHeight={maxVideoHeight} />
</div> </div>
<div className={localVideoFeedClasses}> <div className={localVideoFeedClasses}>
<VideoFeed ref="local" /> <VideoFeed ref={this._local} />
</div> </div>
</div> </div>
); );