Merge pull request #2612 from matrix-org/bwindels/roomlistlag
Fix: roomlist reordering lags
This commit is contained in:
commit
240dc3c1cc
6 changed files with 51 additions and 64 deletions
|
@ -32,7 +32,7 @@ module.exports = {
|
||||||
return false;
|
return false;
|
||||||
} else if (ev.getType() == 'm.call.answer' || ev.getType() == 'm.call.hangup') {
|
} else if (ev.getType() == 'm.call.answer' || ev.getType() == 'm.call.hangup') {
|
||||||
return false;
|
return false;
|
||||||
} else if (ev.getType == 'm.room.message' && ev.getContent().msgtype == 'm.notify') {
|
} else if (ev.getType() == 'm.room.message' && ev.getContent().msgtype == 'm.notify') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const EventTile = sdk.getComponent('rooms.EventTile');
|
const EventTile = sdk.getComponent('rooms.EventTile');
|
||||||
|
|
|
@ -145,6 +145,7 @@ const RoomSubList = React.createClass({
|
||||||
collapsed={this.props.collapsed || false}
|
collapsed={this.props.collapsed || false}
|
||||||
unread={Unread.doesRoomHaveUnreadMessages(room)}
|
unread={Unread.doesRoomHaveUnreadMessages(room)}
|
||||||
highlight={room.getUnreadNotificationCount('highlight') > 0 || this.props.isInvite}
|
highlight={room.getUnreadNotificationCount('highlight') > 0 || this.props.isInvite}
|
||||||
|
notificationCount={room.getUnreadNotificationCount()}
|
||||||
isInvite={this.props.isInvite}
|
isInvite={this.props.isInvite}
|
||||||
refreshSubList={this._updateSubListCount}
|
refreshSubList={this._updateSubListCount}
|
||||||
incomingCall={null}
|
incomingCall={null}
|
||||||
|
|
|
@ -21,7 +21,7 @@ import { _t } from '../../languageHandler';
|
||||||
import { KeyCode } from '../../Keyboard';
|
import { KeyCode } from '../../Keyboard';
|
||||||
import sdk from '../../index';
|
import sdk from '../../index';
|
||||||
import dis from '../../dispatcher';
|
import dis from '../../dispatcher';
|
||||||
import rate_limited_func from '../../ratelimitedfunc';
|
import { debounce } from 'lodash';
|
||||||
import AccessibleButton from '../../components/views/elements/AccessibleButton';
|
import AccessibleButton from '../../components/views/elements/AccessibleButton';
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
|
@ -67,12 +67,9 @@ module.exports = React.createClass({
|
||||||
this.onSearch();
|
this.onSearch();
|
||||||
},
|
},
|
||||||
|
|
||||||
onSearch: new rate_limited_func(
|
onSearch: debounce(function() {
|
||||||
function() {
|
this.props.onSearch(this.refs.search.value);
|
||||||
this.props.onSearch(this.refs.search.value);
|
}, 200, {trailing: true}),
|
||||||
},
|
|
||||||
500,
|
|
||||||
),
|
|
||||||
|
|
||||||
_onKeyDown: function(ev) {
|
_onKeyDown: function(ev) {
|
||||||
switch (ev.keyCode) {
|
switch (ev.keyCode) {
|
||||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
import SettingsStore from "../../../settings/SettingsStore";
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
|
import Timer from "../../../utils/Timer";
|
||||||
|
|
||||||
const React = require("react");
|
const React = require("react");
|
||||||
const ReactDOM = require("react-dom");
|
const ReactDOM = require("react-dom");
|
||||||
|
@ -41,6 +42,7 @@ import {Resizer} from '../../../resizer';
|
||||||
import {Layout, Distributor} from '../../../resizer/distributors/roomsublist2';
|
import {Layout, Distributor} from '../../../resizer/distributors/roomsublist2';
|
||||||
const HIDE_CONFERENCE_CHANS = true;
|
const HIDE_CONFERENCE_CHANS = true;
|
||||||
const STANDARD_TAGS_REGEX = /^(m\.(favourite|lowpriority|server_notice)|im\.vector\.fake\.(invite|recent|direct|archived))$/;
|
const STANDARD_TAGS_REGEX = /^(m\.(favourite|lowpriority|server_notice)|im\.vector\.fake\.(invite|recent|direct|archived))$/;
|
||||||
|
const HOVER_MOVE_TIMEOUT = 1000;
|
||||||
|
|
||||||
function labelForTagName(tagName) {
|
function labelForTagName(tagName) {
|
||||||
if (tagName.startsWith('u.')) return tagName.slice(2);
|
if (tagName.startsWith('u.')) return tagName.slice(2);
|
||||||
|
@ -73,6 +75,7 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
|
|
||||||
|
this._hoverClearTimer = null;
|
||||||
this._subListRefs = {
|
this._subListRefs = {
|
||||||
// key => RoomSubList ref
|
// key => RoomSubList ref
|
||||||
};
|
};
|
||||||
|
@ -95,7 +98,7 @@ module.exports = React.createClass({
|
||||||
// update overflow indicators
|
// update overflow indicators
|
||||||
this._checkSubListsOverflow();
|
this._checkSubListsOverflow();
|
||||||
// don't store height for collapsed sublists
|
// don't store height for collapsed sublists
|
||||||
if(!this.collapsedState[key]) {
|
if (!this.collapsedState[key]) {
|
||||||
this.subListSizes[key] = size;
|
this.subListSizes[key] = size;
|
||||||
window.localStorage.setItem("mx_roomlist_sizes",
|
window.localStorage.setItem("mx_roomlist_sizes",
|
||||||
JSON.stringify(this.subListSizes));
|
JSON.stringify(this.subListSizes));
|
||||||
|
@ -357,11 +360,32 @@ module.exports = React.createClass({
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
},
|
},
|
||||||
|
|
||||||
onMouseEnter: function(ev) {
|
onMouseMove: async function(ev) {
|
||||||
this.setState({hover: true});
|
if (!this._hoverClearTimer) {
|
||||||
|
this.setState({hover: true});
|
||||||
|
this._hoverClearTimer = new Timer(HOVER_MOVE_TIMEOUT);
|
||||||
|
this._hoverClearTimer.start();
|
||||||
|
let finished = true;
|
||||||
|
try {
|
||||||
|
await this._hoverClearTimer.finished();
|
||||||
|
} catch (err) {
|
||||||
|
finished = false;
|
||||||
|
}
|
||||||
|
this._hoverClearTimer = null;
|
||||||
|
if (finished) {
|
||||||
|
this.setState({hover: false});
|
||||||
|
this._delayedRefreshRoomList();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this._hoverClearTimer.restart();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onMouseLeave: function(ev) {
|
onMouseLeave: function(ev) {
|
||||||
|
if (this._hoverClearTimer) {
|
||||||
|
this._hoverClearTimer.abort();
|
||||||
|
this._hoverClearTimer = null;
|
||||||
|
}
|
||||||
this.setState({hover: false});
|
this.setState({hover: false});
|
||||||
|
|
||||||
// Refresh the room list just in case the user missed something.
|
// Refresh the room list just in case the user missed something.
|
||||||
|
@ -774,7 +798,7 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={this._collectResizeContainer} className="mx_RoomList"
|
<div ref={this._collectResizeContainer} className="mx_RoomList"
|
||||||
onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
|
onMouseMove={this.onMouseMove} onMouseLeave={this.onMouseLeave}>
|
||||||
{ subListComponents }
|
{ subListComponents }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -108,13 +108,6 @@ module.exports = React.createClass({
|
||||||
return statusUser._unstable_statusMessage;
|
return statusUser._unstable_statusMessage;
|
||||||
},
|
},
|
||||||
|
|
||||||
onRoomTimeline: function(ev, room) {
|
|
||||||
if (room !== this.props.room) return;
|
|
||||||
this.setState({
|
|
||||||
notificationCount: this.props.room.getUnreadNotificationCount(),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
onRoomName: function(room) {
|
onRoomName: function(room) {
|
||||||
if (room !== this.props.room) return;
|
if (room !== this.props.room) return;
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -159,7 +152,6 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
componentWillMount: function() {
|
componentWillMount: function() {
|
||||||
MatrixClientPeg.get().on("accountData", this.onAccountData);
|
MatrixClientPeg.get().on("accountData", this.onAccountData);
|
||||||
MatrixClientPeg.get().on("Room.timeline", this.onRoomTimeline);
|
|
||||||
MatrixClientPeg.get().on("Room.name", this.onRoomName);
|
MatrixClientPeg.get().on("Room.name", this.onRoomName);
|
||||||
ActiveRoomObserver.addListener(this.props.room.roomId, this._onActiveRoomChange);
|
ActiveRoomObserver.addListener(this.props.room.roomId, this._onActiveRoomChange);
|
||||||
this.dispatcherRef = dis.register(this.onAction);
|
this.dispatcherRef = dis.register(this.onAction);
|
||||||
|
@ -179,7 +171,6 @@ module.exports = React.createClass({
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
if (cli) {
|
if (cli) {
|
||||||
MatrixClientPeg.get().removeListener("accountData", this.onAccountData);
|
MatrixClientPeg.get().removeListener("accountData", this.onAccountData);
|
||||||
MatrixClientPeg.get().removeListener("Room.timeline", this.onRoomTimeline);
|
|
||||||
MatrixClientPeg.get().removeListener("Room.name", this.onRoomName);
|
MatrixClientPeg.get().removeListener("Room.name", this.onRoomName);
|
||||||
}
|
}
|
||||||
ActiveRoomObserver.removeListener(this.props.room.roomId, this._onActiveRoomChange);
|
ActiveRoomObserver.removeListener(this.props.room.roomId, this._onActiveRoomChange);
|
||||||
|
@ -306,7 +297,7 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
const isInvite = this.props.room.getMyMembership() === "invite";
|
const isInvite = this.props.room.getMyMembership() === "invite";
|
||||||
const notificationCount = this.state.notificationCount;
|
const notificationCount = this.props.notificationCount;
|
||||||
// var highlightCount = this.props.room.getUnreadNotificationCount("highlight");
|
// var highlightCount = this.props.room.getUnreadNotificationCount("highlight");
|
||||||
|
|
||||||
const notifBadges = notificationCount > 0 && this._shouldShowNotifBadge();
|
const notifBadges = notificationCount > 0 && this._shouldShowNotifBadge();
|
||||||
|
|
|
@ -20,54 +20,28 @@ limitations under the License.
|
||||||
* to update the interface once for all of them.
|
* to update the interface once for all of them.
|
||||||
*
|
*
|
||||||
* Note that the function must not take arguments, since the args
|
* Note that the function must not take arguments, since the args
|
||||||
* could be different for each invocarion of the function.
|
* could be different for each invocation of the function.
|
||||||
*
|
*
|
||||||
* The returned function has a 'cancelPendingCall' property which can be called
|
* The returned function has a 'cancelPendingCall' property which can be called
|
||||||
* on unmount or similar to cancel any pending update.
|
* on unmount or similar to cancel any pending update.
|
||||||
*/
|
*/
|
||||||
module.exports = function(f, minIntervalMs) {
|
|
||||||
this.lastCall = 0;
|
|
||||||
this.scheduledCall = undefined;
|
|
||||||
|
|
||||||
const self = this;
|
import { throttle } from "lodash";
|
||||||
const wrapper = function() {
|
|
||||||
const now = Date.now();
|
|
||||||
|
|
||||||
if (self.lastCall < now - minIntervalMs) {
|
export default function ratelimitedfunc(fn, time) {
|
||||||
f.apply(this);
|
const throttledFn = throttle(fn, time, {
|
||||||
// get the time again now the function has finished, so if it
|
leading: true,
|
||||||
// took longer than the delay time to execute, it doesn't
|
trailing: true,
|
||||||
// immediately become eligible to run again.
|
});
|
||||||
self.lastCall = Date.now();
|
const _bind = throttledFn.bind;
|
||||||
} else if (self.scheduledCall === undefined) {
|
throttledFn.bind = function() {
|
||||||
self.scheduledCall = setTimeout(
|
const boundFn = _bind.apply(throttledFn, arguments);
|
||||||
() => {
|
boundFn.cancelPendingCall = throttledFn.cancelPendingCall;
|
||||||
self.scheduledCall = undefined;
|
return boundFn;
|
||||||
f.apply(this);
|
|
||||||
// get time again as per above
|
|
||||||
self.lastCall = Date.now();
|
|
||||||
},
|
|
||||||
(self.lastCall + minIntervalMs) - now,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// add the cancelPendingCall property
|
throttledFn.cancelPendingCall = function() {
|
||||||
wrapper.cancelPendingCall = function() {
|
throttledFn.cancel();
|
||||||
if (self.scheduledCall) {
|
|
||||||
clearTimeout(self.scheduledCall);
|
|
||||||
self.scheduledCall = undefined;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
return throttledFn;
|
||||||
// make sure that cancelPendingCall is copied when react rebinds the
|
}
|
||||||
// wrapper
|
|
||||||
const _bind = wrapper.bind;
|
|
||||||
wrapper.bind = function() {
|
|
||||||
const rebound = _bind.apply(this, arguments);
|
|
||||||
rebound.cancelPendingCall = wrapper.cancelPendingCall;
|
|
||||||
return rebound;
|
|
||||||
};
|
|
||||||
|
|
||||||
return wrapper;
|
|
||||||
};
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue