diff --git a/package.json b/package.json
index 21add8ccb7..444d1c5369 100644
--- a/package.json
+++ b/package.json
@@ -65,12 +65,11 @@
"lodash": "^4.13.1",
"matrix-js-sdk": "matrix-org/matrix-js-sdk#develop",
"optimist": "^0.6.1",
- "prop-types": "^15.5.8",
"q": "^1.4.1",
"react": "^15.4.0",
"react-addons-css-transition-group": "15.3.2",
"react-dom": "^15.4.0",
- "react-gemini-scrollbar": "matrix-org/react-gemini-scrollbar#39d858c",
+ "react-gemini-scrollbar": "matrix-org/react-gemini-scrollbar#5e97aef",
"sanitize-html": "^1.11.1",
"text-encoding-utf-8": "^1.0.1",
"velocity-vector": "vector-im/velocity#059e3b2",
diff --git a/src/ConstantTimeDispatcher.js b/src/ConstantTimeDispatcher.js
deleted file mode 100644
index 6c2c3266aa..0000000000
--- a/src/ConstantTimeDispatcher.js
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
-Copyright 2017 Vector Creations Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-// singleton which dispatches invocations of a given type & argument
-// rather than just a type (as per EventEmitter and Flux's dispatcher etc)
-//
-// This means you can have a single point which listens for an EventEmitter event
-// and then dispatches out to one of thousands of RoomTiles (for instance) rather than
-// having each RoomTile register for the EventEmitter event and having to
-// iterate over all of them.
-class ConstantTimeDispatcher {
- constructor() {
- // type -> arg -> [ listener(arg, params) ]
- this.listeners = {};
- }
-
- register(type, arg, listener) {
- if (!this.listeners[type]) this.listeners[type] = {};
- if (!this.listeners[type][arg]) this.listeners[type][arg] = [];
- this.listeners[type][arg].push(listener);
- }
-
- unregister(type, arg, listener) {
- if (this.listeners[type] && this.listeners[type][arg]) {
- var i = this.listeners[type][arg].indexOf(listener);
- if (i > -1) {
- this.listeners[type][arg].splice(i, 1);
- }
- }
- else {
- console.warn("Unregistering unrecognised listener (type=" + type + ", arg=" + arg + ")");
- }
- }
-
- dispatch(type, arg, params) {
- if (!this.listeners[type] || !this.listeners[type][arg]) {
- //console.warn("No registered listeners for dispatch (type=" + type + ", arg=" + arg + ")");
- return;
- }
- this.listeners[type][arg].forEach(listener=>{
- listener.call(arg, params);
- });
- }
-}
-
-if (!global.constantTimeDispatcher) {
- global.constantTimeDispatcher = new ConstantTimeDispatcher();
-}
-module.exports = global.constantTimeDispatcher;
diff --git a/src/KeyCode.js b/src/KeyCode.js
index f164dbc15c..c9cac01239 100644
--- a/src/KeyCode.js
+++ b/src/KeyCode.js
@@ -32,5 +32,4 @@ module.exports = {
DELETE: 46,
KEY_D: 68,
KEY_E: 69,
- KEY_K: 75,
};
diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js
index c4eeb03d5f..25ca025a23 100644
--- a/src/components/structures/LoggedInView.js
+++ b/src/components/structures/LoggedInView.js
@@ -107,18 +107,6 @@ export default React.createClass({
var handled = false;
switch (ev.keyCode) {
- case KeyCode.ESCAPE:
-
- // Implemented this way so possible handling for other pages is neater
- switch (this.props.page_type) {
- case PageTypes.UserSettings:
- this.props.onUserSettingsClose();
- handled = true;
- break;
- }
-
- break;
-
case KeyCode.UP:
case KeyCode.DOWN:
if (ev.altKey && !ev.shiftKey && !ev.ctrlKey && !ev.metaKey) {
diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js
index de43bd1c19..8794713501 100644
--- a/src/components/structures/TimelinePanel.js
+++ b/src/components/structures/TimelinePanel.js
@@ -590,7 +590,6 @@ var TimelinePanel = React.createClass({
this.props.timelineSet.room.setUnreadNotificationCount('highlight', 0);
dis.dispatch({
action: 'on_room_read',
- room: this.props.timelineSet.room,
});
}
}
diff --git a/src/components/views/dialogs/BaseDialog.js b/src/components/views/dialogs/BaseDialog.js
index 02460148b3..0b2ca5225d 100644
--- a/src/components/views/dialogs/BaseDialog.js
+++ b/src/components/views/dialogs/BaseDialog.js
@@ -47,35 +47,18 @@ export default React.createClass({
children: React.PropTypes.node,
},
- componentWillMount: function() {
- this.priorActiveElement = document.activeElement;
- },
-
- componentWillUnmount: function() {
- if (this.priorActiveElement !== null) {
- this.priorActiveElement.focus();
- }
- },
-
- // Don't let key{down,press} events escape the modal. Consume them all.
- _eatKeyEvent: function(e) {
- e.stopPropagation();
- },
-
- // Must be when the key is released (and not pressed) otherwise componentWillUnmount
- // will focus another element which will receive future key events
- _onKeyUp: function(e) {
+ _onKeyDown: function(e) {
if (e.keyCode === KeyCode.ESCAPE) {
+ e.stopPropagation();
e.preventDefault();
this.props.onFinished();
} else if (e.keyCode === KeyCode.ENTER) {
if (this.props.onEnterPressed) {
+ e.stopPropagation();
e.preventDefault();
this.props.onEnterPressed(e);
}
}
- // Consume all keyup events while Modal is open
- e.stopPropagation();
},
_onCancelClick: function(e) {
@@ -84,13 +67,9 @@ export default React.createClass({
render: function() {
const TintableSvg = sdk.getComponent("elements.TintableSvg");
-
+
return (
-
+
diff --git a/src/components/views/dialogs/QuestionDialog.js b/src/components/views/dialogs/QuestionDialog.js
index 8e20b0d2bc..6012541b94 100644
--- a/src/components/views/dialogs/QuestionDialog.js
+++ b/src/components/views/dialogs/QuestionDialog.js
@@ -47,12 +47,6 @@ export default React.createClass({
this.props.onFinished(false);
},
- componentDidMount: function() {
- if (this.props.focus) {
- this.refs.button.focus();
- }
- },
-
render: function() {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
const cancelButton = this.props.hasCancelButton ? (
@@ -69,7 +63,7 @@ export default React.createClass({
{this.props.description}
-
+
{this.props.button}
{this.props.extraButtons}
diff --git a/src/components/views/elements/ActionButton.js b/src/components/views/elements/ActionButton.js
deleted file mode 100644
index 267388daf6..0000000000
--- a/src/components/views/elements/ActionButton.js
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
-Copyright 2017 Vector Creations Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-import React from 'react';
-import PropTypes from 'prop-types';
-import AccessibleButton from './AccessibleButton';
-import dis from '../../../dispatcher';
-import sdk from '../../../index';
-
-export default React.createClass({
- displayName: 'RoleButton',
-
- propTypes: {
- size: PropTypes.string,
- tooltip: PropTypes.bool,
- action: PropTypes.string.isRequired,
- label: PropTypes.string.isRequired,
- iconPath: PropTypes.string.isRequired,
- },
-
- getDefaultProps: function() {
- return {
- size: "25",
- tooltip: false,
- };
- },
-
- getInitialState: function() {
- return {
- showTooltip: false,
- };
- },
-
- _onClick: function(ev) {
- ev.stopPropagation();
- dis.dispatch({action: this.props.action});
- },
-
- _onMouseEnter: function() {
- if (this.props.tooltip) this.setState({showTooltip: true});
- },
-
- _onMouseLeave: function() {
- this.setState({showTooltip: false});
- },
-
- render: function() {
- const TintableSvg = sdk.getComponent("elements.TintableSvg");
-
- let tooltip;
- if (this.state.showTooltip) {
- const RoomTooltip = sdk.getComponent("rooms.RoomTooltip");
- tooltip = ;
- }
-
- return (
-
-
- {tooltip}
-
- );
- }
-});
diff --git a/src/components/views/elements/CreateRoomButton.js b/src/components/views/elements/CreateRoomButton.js
deleted file mode 100644
index 73c984a860..0000000000
--- a/src/components/views/elements/CreateRoomButton.js
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
-Copyright 2017 Vector Creations Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-import React from 'react';
-import sdk from '../../../index';
-import PropTypes from 'prop-types';
-
-const CreateRoomButton = function(props) {
- const ActionButton = sdk.getComponent('elements.ActionButton');
- return (
-
- );
-};
-
-CreateRoomButton.propTypes = {
- size: PropTypes.string,
- tooltip: PropTypes.bool,
-};
-
-export default CreateRoomButton;
diff --git a/src/components/views/elements/HomeButton.js b/src/components/views/elements/HomeButton.js
deleted file mode 100644
index 5c446f24c9..0000000000
--- a/src/components/views/elements/HomeButton.js
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
-Copyright 2017 Vector Creations Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-import React from 'react';
-import sdk from '../../../index';
-import PropTypes from 'prop-types';
-
-const HomeButton = function(props) {
- const ActionButton = sdk.getComponent('elements.ActionButton');
- return (
-
- );
-};
-
-HomeButton.propTypes = {
- size: PropTypes.string,
- tooltip: PropTypes.bool,
-};
-
-export default HomeButton;
diff --git a/src/components/views/elements/RoomDirectoryButton.js b/src/components/views/elements/RoomDirectoryButton.js
deleted file mode 100644
index 5e68776a15..0000000000
--- a/src/components/views/elements/RoomDirectoryButton.js
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
-Copyright 2017 Vector Creations Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-import React from 'react';
-import sdk from '../../../index';
-import PropTypes from 'prop-types';
-
-const RoomDirectoryButton = function(props) {
- const ActionButton = sdk.getComponent('elements.ActionButton');
- return (
-
- );
-};
-
-RoomDirectoryButton.propTypes = {
- size: PropTypes.string,
- tooltip: PropTypes.bool,
-};
-
-export default RoomDirectoryButton;
diff --git a/src/components/views/elements/SettingsButton.js b/src/components/views/elements/SettingsButton.js
deleted file mode 100644
index c6438da277..0000000000
--- a/src/components/views/elements/SettingsButton.js
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
-Copyright 2017 Vector Creations Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-import React from 'react';
-import sdk from '../../../index';
-import PropTypes from 'prop-types';
-
-const SettingsButton = function(props) {
- const ActionButton = sdk.getComponent('elements.ActionButton');
- return (
-
- );
-};
-
-SettingsButton.propTypes = {
- size: PropTypes.string,
- tooltip: PropTypes.bool,
-};
-
-export default SettingsButton;
diff --git a/src/components/views/elements/StartChatButton.js b/src/components/views/elements/StartChatButton.js
deleted file mode 100644
index 747f75d1b3..0000000000
--- a/src/components/views/elements/StartChatButton.js
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
-Copyright 2017 Vector Creations Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-import React from 'react';
-import sdk from '../../../index';
-import PropTypes from 'prop-types';
-
-const StartChatButton = function(props) {
- const ActionButton = sdk.getComponent('elements.ActionButton');
- return (
-
- );
-};
-
-StartChatButton.propTypes = {
- size: PropTypes.string,
- tooltip: PropTypes.bool,
-};
-
-export default StartChatButton;
diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js
index a595a91ba9..611dd10780 100644
--- a/src/components/views/rooms/RoomList.js
+++ b/src/components/views/rooms/RoomList.js
@@ -1,6 +1,5 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
-Copyright 2017 Vector Creations Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -22,23 +21,15 @@ var GeminiScrollbar = require('react-gemini-scrollbar');
var MatrixClientPeg = require("../../../MatrixClientPeg");
var CallHandler = require('../../../CallHandler');
var RoomListSorter = require("../../../RoomListSorter");
+var Unread = require('../../../Unread');
var dis = require("../../../dispatcher");
var sdk = require('../../../index');
var rate_limited_func = require('../../../ratelimitedfunc');
var Rooms = require('../../../Rooms');
import DMRoomMap from '../../../utils/DMRoomMap';
var Receipt = require('../../../utils/Receipt');
-var constantTimeDispatcher = require('../../../ConstantTimeDispatcher');
-import AccessibleButton from '../elements/AccessibleButton';
-const HIDE_CONFERENCE_CHANS = true;
-
-const VERBS = {
- 'm.favourite': 'favourite',
- 'im.vector.fake.direct': 'tag direct chat',
- 'im.vector.fake.recent': 'restore',
- 'm.lowpriority': 'demote',
-};
+var HIDE_CONFERENCE_CHANS = true;
module.exports = React.createClass({
displayName: 'RoomList',
@@ -46,23 +37,13 @@ module.exports = React.createClass({
propTypes: {
ConferenceHandler: React.PropTypes.any,
collapsed: React.PropTypes.bool.isRequired,
- selectedRoom: React.PropTypes.string,
+ currentRoom: React.PropTypes.string,
searchFilter: React.PropTypes.string,
},
- shouldComponentUpdate: function(nextProps, nextState) {
- if (nextProps.collapsed !== this.props.collapsed) return true;
- if (nextProps.searchFilter !== this.props.searchFilter) return true;
- if (nextState.lists !== this.state.lists ||
- nextState.isLoadingLeftRooms !== this.state.isLoadingLeftRooms ||
- nextState.incomingCall !== this.state.incomingCall) return true;
- return false;
- },
-
getInitialState: function() {
return {
isLoadingLeftRooms: false,
- totalRoomCount: null,
lists: {},
incomingCall: null,
};
@@ -76,21 +57,12 @@ module.exports = React.createClass({
cli.on("Room.name", this.onRoomName);
cli.on("Room.tags", this.onRoomTags);
cli.on("Room.receipt", this.onRoomReceipt);
- cli.on("RoomState.members", this.onRoomStateMember);
+ cli.on("RoomState.events", this.onRoomStateEvents);
cli.on("RoomMember.name", this.onRoomMemberName);
cli.on("accountData", this.onAccountData);
- // lookup for which lists a given roomId is currently in.
- this.listsForRoomId = {};
-
- this.refreshRoomList();
-
- // order of the sublists
- //this.listOrder = [];
-
- // loop count to stop a stack overflow if the user keeps waggling the
- // mouse for >30s in a row, or if running under mocha
- this._delayedRefreshRoomListLoopCount = 0
+ var s = this.getRoomLists();
+ this.setState(s);
},
componentDidMount: function() {
@@ -99,22 +71,7 @@ module.exports = React.createClass({
this._updateStickyHeaders(true);
},
- componentWillReceiveProps: function(nextProps) {
- // short-circuit react when the room changes
- // to avoid rerendering all the sublists everywhere
- if (nextProps.selectedRoom !== this.props.selectedRoom) {
- if (this.props.selectedRoom) {
- constantTimeDispatcher.dispatch(
- "RoomTile.select", this.props.selectedRoom, {}
- );
- }
- constantTimeDispatcher.dispatch(
- "RoomTile.select", nextProps.selectedRoom, { selected: true }
- );
- }
- },
-
- componentDidUpdate: function(prevProps, prevState) {
+ componentDidUpdate: function() {
// Reinitialise the stickyHeaders when the component is updated
this._updateStickyHeaders(true);
this._repositionIncomingCallBox(undefined, false);
@@ -140,24 +97,10 @@ module.exports = React.createClass({
}
break;
case 'on_room_read':
- // poke the right RoomTile to refresh, using the constantTimeDispatcher
- // to avoid each and every RoomTile registering to the 'on_room_read' event
- // XXX: if we like the constantTimeDispatcher we might want to dispatch
- // directly from TimelinePanel rather than needlessly bouncing via here.
- constantTimeDispatcher.dispatch(
- "RoomTile.refresh", payload.room.roomId, {}
- );
-
- // also have to poke the right list(s)
- var lists = this.listsForRoomId[payload.room.roomId];
- if (lists) {
- lists.forEach(list=>{
- constantTimeDispatcher.dispatch(
- "RoomSubList.refreshHeader", list, { room: payload.room }
- );
- });
- }
-
+ // Force an update because the notif count state is too deep to cause
+ // an update. This forces the local echo of reading notifs to be
+ // reflected by the RoomTiles.
+ this.forceUpdate();
break;
}
},
@@ -171,7 +114,7 @@ module.exports = React.createClass({
MatrixClientPeg.get().removeListener("Room.name", this.onRoomName);
MatrixClientPeg.get().removeListener("Room.tags", this.onRoomTags);
MatrixClientPeg.get().removeListener("Room.receipt", this.onRoomReceipt);
- MatrixClientPeg.get().removeListener("RoomState.members", this.onRoomStateMember);
+ MatrixClientPeg.get().removeListener("RoomState.events", this.onRoomStateEvents);
MatrixClientPeg.get().removeListener("RoomMember.name", this.onRoomMemberName);
MatrixClientPeg.get().removeListener("accountData", this.onAccountData);
}
@@ -180,14 +123,10 @@ module.exports = React.createClass({
},
onRoom: function(room) {
- // XXX: this happens rarely; ideally we should only update the correct
- // sublists when it does (e.g. via a constantTimeDispatch to the right sublist)
this._delayedRefreshRoomList();
},
onDeleteRoom: function(roomId) {
- // XXX: this happens rarely; ideally we should only update the correct
- // sublists when it does (e.g. via a constantTimeDispatch to the right sublist)
this._delayedRefreshRoomList();
},
@@ -210,10 +149,6 @@ module.exports = React.createClass({
}
},
- _onMouseOver: function(ev) {
- this._lastMouseOverTs = Date.now();
- },
-
onSubListHeaderClick: function(isHidden, scrollToPosition) {
// The scroll area has expanded or contracted, so re-calculate sticky headers positions
this._updateStickyHeaders(true, scrollToPosition);
@@ -223,98 +158,41 @@ module.exports = React.createClass({
if (toStartOfTimeline) return;
if (!room) return;
if (data.timeline.getTimelineSet() !== room.getUnfilteredTimelineSet()) return;
-
- // rather than regenerate our full roomlists, which is very heavy, we poke the
- // correct sublists to just re-sort themselves. This isn't enormously reacty,
- // but is much faster than the default react reconciler, or having to do voodoo
- // with shouldComponentUpdate and a pleaseRefresh property or similar.
- var lists = this.listsForRoomId[room.roomId];
- if (lists) {
- lists.forEach(list=>{
- constantTimeDispatcher.dispatch("RoomSubList.sort", list, { room: room });
- });
- }
-
- // we have to explicitly hit the roomtile which just changed
- constantTimeDispatcher.dispatch(
- "RoomTile.refresh", room.roomId, {}
- );
+ this._delayedRefreshRoomList();
},
onRoomReceipt: function(receiptEvent, room) {
// because if we read a notification, it will affect notification count
// only bother updating if there's a receipt from us
if (Receipt.findReadReceiptFromUserId(receiptEvent, MatrixClientPeg.get().credentials.userId)) {
- var lists = this.listsForRoomId[room.roomId];
- if (lists) {
- lists.forEach(list=>{
- constantTimeDispatcher.dispatch(
- "RoomSubList.refreshHeader", list, { room: room }
- );
- });
- }
-
- // we have to explicitly hit the roomtile which just changed
- constantTimeDispatcher.dispatch(
- "RoomTile.refresh", room.roomId, {}
- );
+ this._delayedRefreshRoomList();
}
},
onRoomName: function(room) {
- constantTimeDispatcher.dispatch(
- "RoomTile.refresh", room.roomId, {}
- );
- },
-
- onRoomTags: function(event, room) {
- // XXX: this happens rarely; ideally we should only update the correct
- // sublists when it does (e.g. via a constantTimeDispatch to the right sublist)
this._delayedRefreshRoomList();
},
- onRoomStateMember: function(ev, state, member) {
- if (ev.getStateKey() === MatrixClientPeg.get().credentials.userId &&
- ev.getPrevContent() && ev.getPrevContent().membership === "invite")
- {
- this._delayedRefreshRoomList();
- }
- else {
- constantTimeDispatcher.dispatch(
- "RoomTile.refresh", member.roomId, {}
- );
- }
+ onRoomTags: function(event, room) {
+ this._delayedRefreshRoomList();
+ },
+
+ onRoomStateEvents: function(ev, state) {
+ this._delayedRefreshRoomList();
},
onRoomMemberName: function(ev, member) {
- constantTimeDispatcher.dispatch(
- "RoomTile.refresh", member.roomId, {}
- );
+ this._delayedRefreshRoomList();
},
onAccountData: function(ev) {
if (ev.getType() == 'm.direct') {
- // XXX: this happens rarely; ideally we should only update the correct
- // sublists when it does (e.g. via a constantTimeDispatch to the right sublist)
- this._delayedRefreshRoomList();
- }
- else if (ev.getType() == 'm.push_rules') {
this._delayedRefreshRoomList();
}
},
_delayedRefreshRoomList: new rate_limited_func(function() {
- // if the mouse has been moving over the RoomList in the last 500ms
- // then delay the refresh further to avoid bouncing around under the
- // cursor
- if (Date.now() - this._lastMouseOverTs > 500 || this._delayedRefreshRoomListLoopCount > 60) {
- this.refreshRoomList();
- this._delayedRefreshRoomListLoopCount = 0;
- }
- else {
- this._delayedRefreshRoomListLoopCount++;
- this._delayedRefreshRoomList();
- }
+ this.refreshRoomList();
}, 500),
refreshRoomList: function() {
@@ -322,36 +200,27 @@ module.exports = React.createClass({
// (!this._lastRefreshRoomListTs ? "-" : (Date.now() - this._lastRefreshRoomListTs))
// );
- // TODO: ideally we'd calculate this once at start, and then maintain
- // any changes to it incrementally, updating the appropriate sublists
- // as needed.
- // Alternatively we'd do something magical with Immutable.js or similar.
- const lists = this.getRoomLists();
- let totalRooms = 0;
- for (const l of Object.values(lists)) {
- totalRooms += l.length;
- }
- this.setState({
- lists: this.getRoomLists(),
- totalRoomCount: totalRooms,
- });
-
+ // TODO: rather than bluntly regenerating and re-sorting everything
+ // every time we see any kind of room change from the JS SDK
+ // we could do incremental updates on our copy of the state
+ // based on the room which has actually changed. This would stop
+ // us re-rendering all the sublists every time anything changes anywhere
+ // in the state of the client.
+ this.setState(this.getRoomLists());
+
// this._lastRefreshRoomListTs = Date.now();
},
getRoomLists: function() {
var self = this;
- const lists = {};
+ var s = { lists: {} };
- lists["im.vector.fake.invite"] = [];
- lists["m.favourite"] = [];
- lists["im.vector.fake.recent"] = [];
- lists["im.vector.fake.direct"] = [];
- lists["m.lowpriority"] = [];
- lists["im.vector.fake.archived"] = [];
-
- this.listsForRoomId = {};
- var otherTagNames = {};
+ s.lists["im.vector.fake.invite"] = [];
+ s.lists["m.favourite"] = [];
+ s.lists["im.vector.fake.recent"] = [];
+ s.lists["im.vector.fake.direct"] = [];
+ s.lists["m.lowpriority"] = [];
+ s.lists["im.vector.fake.archived"] = [];
const dmRoomMap = new DMRoomMap(MatrixClientPeg.get());
@@ -364,13 +233,8 @@ module.exports = React.createClass({
// ", target = " + me.events.member.getStateKey() +
// ", prevMembership = " + me.events.member.getPrevContent().membership);
- if (!self.listsForRoomId[room.roomId]) {
- self.listsForRoomId[room.roomId] = [];
- }
-
if (me.membership == "invite") {
- self.listsForRoomId[room.roomId].push("im.vector.fake.invite");
- lists["im.vector.fake.invite"].push(room);
+ s.lists["im.vector.fake.invite"].push(room);
}
else if (HIDE_CONFERENCE_CHANS && Rooms.isConfCallRoom(room, me, self.props.ConferenceHandler)) {
// skip past this room & don't put it in any lists
@@ -380,52 +244,70 @@ module.exports = React.createClass({
{
// Used to split rooms via tags
var tagNames = Object.keys(room.tags);
+
if (tagNames.length) {
for (var i = 0; i < tagNames.length; i++) {
var tagName = tagNames[i];
- lists[tagName] = lists[tagName] || [];
- lists[tagName].push(room);
- self.listsForRoomId[room.roomId].push(tagName);
- otherTagNames[tagName] = 1;
+ s.lists[tagName] = s.lists[tagName] || [];
+ s.lists[tagNames[i]].push(room);
}
}
else if (dmRoomMap.getUserIdForRoomId(room.roomId)) {
// "Direct Message" rooms (that we're still in and that aren't otherwise tagged)
- self.listsForRoomId[room.roomId].push("im.vector.fake.direct");
- lists["im.vector.fake.direct"].push(room);
+ s.lists["im.vector.fake.direct"].push(room);
}
else {
- self.listsForRoomId[room.roomId].push("im.vector.fake.recent");
- lists["im.vector.fake.recent"].push(room);
+ s.lists["im.vector.fake.recent"].push(room);
}
}
else if (me.membership === "leave") {
- self.listsForRoomId[room.roomId].push("im.vector.fake.archived");
- lists["im.vector.fake.archived"].push(room);
+ s.lists["im.vector.fake.archived"].push(room);
}
else {
console.error("unrecognised membership: " + me.membership + " - this should never happen");
}
});
+ if (s.lists["im.vector.fake.direct"].length == 0 &&
+ MatrixClientPeg.get().getAccountData('m.direct') === undefined &&
+ !MatrixClientPeg.get().isGuest())
+ {
+ // scan through the 'recents' list for any rooms which look like DM rooms
+ // and make them DM rooms
+ const oldRecents = s.lists["im.vector.fake.recent"];
+ s.lists["im.vector.fake.recent"] = [];
+
+ for (const room of oldRecents) {
+ const me = room.getMember(MatrixClientPeg.get().credentials.userId);
+
+ if (me && Rooms.looksLikeDirectMessageRoom(room, me)) {
+ s.lists["im.vector.fake.direct"].push(room);
+ } else {
+ s.lists["im.vector.fake.recent"].push(room);
+ }
+ }
+
+ // save these new guessed DM rooms into the account data
+ const newMDirectEvent = {};
+ for (const room of s.lists["im.vector.fake.direct"]) {
+ const me = room.getMember(MatrixClientPeg.get().credentials.userId);
+ const otherPerson = Rooms.getOnlyOtherMember(room, me);
+ if (!otherPerson) continue;
+
+ const roomList = newMDirectEvent[otherPerson.userId] || [];
+ roomList.push(room.roomId);
+ newMDirectEvent[otherPerson.userId] = roomList;
+ }
+
+ // if this fails, fine, we'll just do the same thing next time we get the room lists
+ MatrixClientPeg.get().setAccountData('m.direct', newMDirectEvent).done();
+ }
+
+ //console.log("calculated new roomLists; im.vector.fake.recent = " + s.lists["im.vector.fake.recent"]);
+
// we actually apply the sorting to this when receiving the prop in RoomSubLists.
- // we'll need this when we get to iterating through lists programatically - e.g. ctrl-shift-up/down
-/*
- this.listOrder = [
- "im.vector.fake.invite",
- "m.favourite",
- "im.vector.fake.recent",
- "im.vector.fake.direct",
- Object.keys(otherTagNames).filter(tagName=>{
- return (!tagName.match(/^m\.(favourite|lowpriority)$/));
- }).sort(),
- "m.lowpriority",
- "im.vector.fake.archived"
- ];
-*/
-
- return lists;
+ return s;
},
_getScrollNode: function() {
@@ -455,7 +337,6 @@ module.exports = React.createClass({
var incomingCallBox = document.getElementById("incomingCallBox");
if (incomingCallBox && incomingCallBox.parentElement) {
var scrollArea = this._getScrollNode();
- if (!scrollArea) return;
// Use the offset of the top of the scroll area from the window
// as this is used to calculate the CSS fixed top position for the stickies
var scrollAreaOffset = scrollArea.getBoundingClientRect().top + window.pageYOffset;
@@ -479,7 +360,6 @@ module.exports = React.createClass({
// properly through React
_initAndPositionStickyHeaders: function(initialise, scrollToPosition) {
var scrollArea = this._getScrollNode();
- if (!scrollArea) return;
// Use the offset of the top of the scroll area from the window
// as this is used to calculate the CSS fixed top position for the stickies
var scrollAreaOffset = scrollArea.getBoundingClientRect().top + window.pageYOffset;
@@ -577,74 +457,21 @@ module.exports = React.createClass({
this.refs.gemscroll.forceUpdate();
},
- _getEmptyContent: function(section) {
- const RoomDropTarget = sdk.getComponent('rooms.RoomDropTarget');
-
- if (this.props.collapsed) {
- return ;
- }
-
- const StartChatButton = sdk.getComponent('elements.StartChatButton');
- const RoomDirectoryButton = sdk.getComponent('elements.RoomDirectoryButton');
- const CreateRoomButton = sdk.getComponent('elements.CreateRoomButton');
- if (this.state.totalRoomCount === 0) {
- const TintableSvg = sdk.getComponent('elements.TintableSvg');
- switch (section) {
- case 'im.vector.fake.direct':
- return
- Press
-
- to start a chat with someone
-
;
- case 'im.vector.fake.recent':
- return
- You're not in any rooms yet! Press
-
- to make a room or
-
- to browse the directory
-
;
- }
- }
-
- const labelText = 'Drop here to ' + (VERBS[section] || 'tag ' + section);
-
- return ;
- },
-
- _getHeaderItems: function(section) {
- const StartChatButton = sdk.getComponent('elements.StartChatButton');
- const RoomDirectoryButton = sdk.getComponent('elements.RoomDirectoryButton');
- const CreateRoomButton = sdk.getComponent('elements.CreateRoomButton');
- switch (section) {
- case 'im.vector.fake.direct':
- return
-
- ;
- case 'im.vector.fake.recent':
- return
-
-
- ;
- }
- },
-
render: function() {
var RoomSubList = sdk.getComponent('structures.RoomSubList');
var self = this;
return (
-
+ autoshow={true} onScroll={ self._whenScrolling } ref="gemscroll">
+
@@ -652,12 +479,12 @@ module.exports = React.createClass({
@@ -665,13 +492,12 @@ module.exports = React.createClass({
- { Object.keys(self.state.lists).sort().map(function(tagName) {
+ { Object.keys(self.state.lists).map(function(tagName) {
if (!tagName.match(/^(m\.(favourite|lowpriority)|im\.vector\.fake\.(invite|recent|direct|archived))$/)) {
return
;
@@ -713,23 +537,22 @@ module.exports = React.createClass({
0 || this.props.isInvite,
- });
- },
-
- onSelect: function(params) {
- this.setState({
- selected: params.selected,
- });
+ var cli = MatrixClientPeg.get();
+ if (cli) {
+ MatrixClientPeg.get().removeListener("accountData", this.onAccountData);
+ }
},
onClick: function(ev) {
@@ -179,13 +169,13 @@ module.exports = React.createClass({
// var highlightCount = this.props.room.getUnreadNotificationCount("highlight");
const notifBadges = notificationCount > 0 && this._shouldShowNotifBadge();
- const mentionBadges = this.state.highlight && this._shouldShowMentionBadge();
+ const mentionBadges = this.props.highlight && this._shouldShowMentionBadge();
const badges = notifBadges || mentionBadges;
var classes = classNames({
'mx_RoomTile': true,
- 'mx_RoomTile_selected': this.state.selected,
- 'mx_RoomTile_unread': this.state.unread,
+ 'mx_RoomTile_selected': this.props.selected,
+ 'mx_RoomTile_unread': this.props.unread,
'mx_RoomTile_unreadNotify': notifBadges,
'mx_RoomTile_highlight': mentionBadges,
'mx_RoomTile_invited': (me && me.membership == 'invite'),
@@ -231,7 +221,7 @@ module.exports = React.createClass({
'mx_RoomTile_badgeShown': badges || this.state.badgeHover || this.state.menuDisplayed,
});
- if (this.state.selected) {
+ if (this.props.selected) {
let nameSelected = {name} ;
label = { nameSelected }
;
@@ -265,8 +255,7 @@ module.exports = React.createClass({
let ret = (
{ /* Only native elements can be wrapped in a DnD object. */}
-
+