diff --git a/package.json b/package.json
index 60d02c4d46..2001f0d4ad 100644
--- a/package.json
+++ b/package.json
@@ -63,6 +63,7 @@
"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",
diff --git a/src/component-index.js b/src/component-index.js
index d6873c6dfd..090a27d5ed 100644
--- a/src/component-index.js
+++ b/src/component-index.js
@@ -103,10 +103,14 @@ import views$dialogs$UnknownDeviceDialog from './components/views/dialogs/Unknow
views$dialogs$UnknownDeviceDialog && (module.exports.components['views.dialogs.UnknownDeviceDialog'] = views$dialogs$UnknownDeviceDialog);
import views$elements$AccessibleButton from './components/views/elements/AccessibleButton';
views$elements$AccessibleButton && (module.exports.components['views.elements.AccessibleButton'] = views$elements$AccessibleButton);
+import views$elements$ActionButton from './components/views/elements/ActionButton';
+views$elements$ActionButton && (module.exports.components['views.elements.ActionButton'] = views$elements$ActionButton);
import views$elements$AddressSelector from './components/views/elements/AddressSelector';
views$elements$AddressSelector && (module.exports.components['views.elements.AddressSelector'] = views$elements$AddressSelector);
import views$elements$AddressTile from './components/views/elements/AddressTile';
views$elements$AddressTile && (module.exports.components['views.elements.AddressTile'] = views$elements$AddressTile);
+import views$elements$CreateRoomButton from './components/views/elements/CreateRoomButton';
+views$elements$CreateRoomButton && (module.exports.components['views.elements.CreateRoomButton'] = views$elements$CreateRoomButton);
import views$elements$DeviceVerifyButtons from './components/views/elements/DeviceVerifyButtons';
views$elements$DeviceVerifyButtons && (module.exports.components['views.elements.DeviceVerifyButtons'] = views$elements$DeviceVerifyButtons);
import views$elements$DirectorySearchBox from './components/views/elements/DirectorySearchBox';
@@ -119,12 +123,20 @@ import views$elements$EditableTextContainer from './components/views/elements/Ed
views$elements$EditableTextContainer && (module.exports.components['views.elements.EditableTextContainer'] = views$elements$EditableTextContainer);
import views$elements$EmojiText from './components/views/elements/EmojiText';
views$elements$EmojiText && (module.exports.components['views.elements.EmojiText'] = views$elements$EmojiText);
+import views$elements$HomeButton from './components/views/elements/HomeButton';
+views$elements$HomeButton && (module.exports.components['views.elements.HomeButton'] = views$elements$HomeButton);
import views$elements$MemberEventListSummary from './components/views/elements/MemberEventListSummary';
views$elements$MemberEventListSummary && (module.exports.components['views.elements.MemberEventListSummary'] = views$elements$MemberEventListSummary);
import views$elements$PowerSelector from './components/views/elements/PowerSelector';
views$elements$PowerSelector && (module.exports.components['views.elements.PowerSelector'] = views$elements$PowerSelector);
import views$elements$ProgressBar from './components/views/elements/ProgressBar';
views$elements$ProgressBar && (module.exports.components['views.elements.ProgressBar'] = views$elements$ProgressBar);
+import views$elements$RoomDirectoryButton from './components/views/elements/RoomDirectoryButton';
+views$elements$RoomDirectoryButton && (module.exports.components['views.elements.RoomDirectoryButton'] = views$elements$RoomDirectoryButton);
+import views$elements$SettingsButton from './components/views/elements/SettingsButton';
+views$elements$SettingsButton && (module.exports.components['views.elements.SettingsButton'] = views$elements$SettingsButton);
+import views$elements$StartChatButton from './components/views/elements/StartChatButton';
+views$elements$StartChatButton && (module.exports.components['views.elements.StartChatButton'] = views$elements$StartChatButton);
import views$elements$TintableSvg from './components/views/elements/TintableSvg';
views$elements$TintableSvg && (module.exports.components['views.elements.TintableSvg'] = views$elements$TintableSvg);
import views$elements$TruncatedList from './components/views/elements/TruncatedList';
diff --git a/src/components/views/elements/ActionButton.js b/src/components/views/elements/ActionButton.js
new file mode 100644
index 0000000000..267388daf6
--- /dev/null
+++ b/src/components/views/elements/ActionButton.js
@@ -0,0 +1,80 @@
+/*
+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
new file mode 100644
index 0000000000..e7e526d36b
--- /dev/null
+++ b/src/components/views/elements/CreateRoomButton.js
@@ -0,0 +1,38 @@
+/*
+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
new file mode 100644
index 0000000000..5c446f24c9
--- /dev/null
+++ b/src/components/views/elements/HomeButton.js
@@ -0,0 +1,38 @@
+/*
+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
new file mode 100644
index 0000000000..5e68776a15
--- /dev/null
+++ b/src/components/views/elements/RoomDirectoryButton.js
@@ -0,0 +1,38 @@
+/*
+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
new file mode 100644
index 0000000000..c6438da277
--- /dev/null
+++ b/src/components/views/elements/SettingsButton.js
@@ -0,0 +1,38 @@
+/*
+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
new file mode 100644
index 0000000000..02d5677a7c
--- /dev/null
+++ b/src/components/views/elements/StartChatButton.js
@@ -0,0 +1,38 @@
+/*
+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 49af1560f1..8d396b5536 100644
--- a/src/components/views/rooms/RoomList.js
+++ b/src/components/views/rooms/RoomList.js
@@ -1,5 +1,6 @@
/*
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.
@@ -28,8 +29,16 @@ var Rooms = require('../../../Rooms');
import DMRoomMap from '../../../utils/DMRoomMap';
var Receipt = require('../../../utils/Receipt');
var constantTimeDispatcher = require('../../../ConstantTimeDispatcher');
+import AccessibleButton from '../elements/AccessibleButton';
-var HIDE_CONFERENCE_CHANS = true;
+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',
+};
module.exports = React.createClass({
displayName: 'RoomList',
@@ -53,6 +62,7 @@ module.exports = React.createClass({
getInitialState: function() {
return {
isLoadingLeftRooms: false,
+ totalRoomCount: null,
lists: {},
incomingCall: null,
};
@@ -73,8 +83,7 @@ module.exports = React.createClass({
// lookup for which lists a given roomId is currently in.
this.listsForRoomId = {};
- var s = this.getRoomLists();
- this.setState(s);
+ this.refreshRoomList();
// order of the sublists
//this.listOrder = [];
@@ -317,21 +326,29 @@ module.exports = React.createClass({
// any changes to it incrementally, updating the appropriate sublists
// as needed.
// Alternatively we'd do something magical with Immutable.js or similar.
- this.setState(this.getRoomLists());
-
+ const lists = this.getRoomLists();
+ let totalRooms = 0;
+ for (const l of Object.values(lists)) {
+ totalRooms += l.length;
+ }
+ this.setState({
+ lists: this.getRoomLists(),
+ totalRoomCount: totalRooms,
+ });
+
// this._lastRefreshRoomListTs = Date.now();
},
getRoomLists: function() {
var self = this;
- var s = { lists: {} };
+ const lists = {};
- 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"] = [];
+ 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 = {};
@@ -353,7 +370,7 @@ module.exports = React.createClass({
if (me.membership == "invite") {
self.listsForRoomId[room.roomId].push("im.vector.fake.invite");
- s.lists["im.vector.fake.invite"].push(room);
+ 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
@@ -366,8 +383,8 @@ module.exports = React.createClass({
if (tagNames.length) {
for (var i = 0; i < tagNames.length; i++) {
var tagName = tagNames[i];
- s.lists[tagName] = s.lists[tagName] || [];
- s.lists[tagName].push(room);
+ lists[tagName] = lists[tagName] || [];
+ lists[tagName].push(room);
self.listsForRoomId[room.roomId].push(tagName);
otherTagNames[tagName] = 1;
}
@@ -375,16 +392,16 @@ module.exports = React.createClass({
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");
- s.lists["im.vector.fake.direct"].push(room);
+ lists["im.vector.fake.direct"].push(room);
}
else {
self.listsForRoomId[room.roomId].push("im.vector.fake.recent");
- s.lists["im.vector.fake.recent"].push(room);
+ lists["im.vector.fake.recent"].push(room);
}
}
else if (me.membership === "leave") {
self.listsForRoomId[room.roomId].push("im.vector.fake.archived");
- s.lists["im.vector.fake.archived"].push(room);
+ lists["im.vector.fake.archived"].push(room);
}
else {
console.error("unrecognised membership: " + me.membership + " - this should never happen");
@@ -408,7 +425,7 @@ module.exports = React.createClass({
];
*/
- return s;
+ return lists;
},
_getScrollNode: function() {
@@ -438,6 +455,7 @@ 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;
@@ -461,6 +479,7 @@ 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;
@@ -558,6 +577,58 @@ 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;
@@ -581,7 +652,7 @@ module.exports = React.createClass({