diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index 96ff65498f..39d3406b73 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -29,7 +29,14 @@ import DMRoomMap from '../../../utils/DMRoomMap'; var Receipt = require('../../../utils/Receipt'); var constantTimeDispatcher = require('../../../ConstantTimeDispatcher'); -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 +60,7 @@ module.exports = React.createClass({ getInitialState: function() { return { isLoadingLeftRooms: false, + totalRoomCount: null, lists: {}, incomingCall: null, }; @@ -73,8 +81,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 +324,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 +368,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 +381,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,46 +390,46 @@ 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"); } }); - if (s.lists["im.vector.fake.direct"].length == 0 && + if (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"] = []; + const oldRecents = lists["im.vector.fake.recent"]; + lists["im.vector.fake.recent"] = []; for (const room of oldRecents) { const me = room.getMember(MatrixClientPeg.get().credentials.userId); if (me && Rooms.looksLikeDirectMessageRoom(room, me)) { 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); } } // save these new guessed DM rooms into the account data const newMDirectEvent = {}; - for (const room of s.lists["im.vector.fake.direct"]) { + for (const room of lists["im.vector.fake.direct"]) { const me = room.getMember(MatrixClientPeg.get().credentials.userId); const otherPerson = Rooms.getOnlyOtherMember(room, me); if (!otherPerson) continue; @@ -449,7 +464,7 @@ module.exports = React.createClass({ ]; */ - return s; + return lists; }, _getScrollNode: function() { @@ -479,6 +494,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; @@ -502,6 +518,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; @@ -599,6 +616,49 @@ module.exports = React.createClass({ this.refs.gemscroll.forceUpdate(); }, + _getEmptyContent: function(section) { + let greyed = false; + if (this.state.totalRoomCount === 0) { + const TintableSvg = sdk.getComponent('elements.TintableSvg'); + switch (section) { + case 'm.favourite': + case 'm.lowpriority': + greyed = true; + break; + case 'im.vector.fake.direct': + return <div className="mx_RoomList_emptySubListTip"> + <div className="mx_RoomList_butonPreview"> + <TintableSvg src="img/icons-people.svg" width="25" height="25" /> + </div> + Use the button below to chat with someone! + </div>; + case 'im.vector.fake.recent': + return <div className="mx_RoomList_emptySubListTip"> + <div className="mx_RoomList_butonPreview"> + <TintableSvg src="img/icons-directory.svg" width="25" height="25" /> + </div> + Use the button below to browse the room directory + <br /><br /> + <div className="mx_RoomList_butonPreview"> + <TintableSvg src="img/icons-create-room.svg" width="25" height="25" /> + </div> + or this button to start a new one! + </div>; + } + } + const RoomDropTarget = sdk.getComponent('rooms.RoomDropTarget'); + + const labelText = 'Drop here to ' + (VERBS[section] || 'tag ' + section); + + let label; + if (greyed) { + label = <span className="mx_RoomList_greyedSubListLabel">{labelText}</span>; + } else { + label = labelText; + } + return <RoomDropTarget label={label} />; + }, + render: function() { var RoomSubList = sdk.getComponent('structures.RoomSubList'); var self = this; @@ -622,7 +682,7 @@ module.exports = React.createClass({ <RoomSubList list={ self.state.lists['m.favourite'] } label="Favourites" tagName="m.favourite" - verb="favourite" + emptyContent={this._getEmptyContent('m.favourite')} editable={ true } order="manual" incomingCall={ self.state.incomingCall } @@ -635,7 +695,7 @@ module.exports = React.createClass({ <RoomSubList list={ self.state.lists['im.vector.fake.direct'] } label="People" tagName="im.vector.fake.direct" - verb="tag direct chat" + emptyContent={this._getEmptyContent('im.vector.fake.direct')} editable={ true } order="recent" incomingCall={ self.state.incomingCall } @@ -650,7 +710,7 @@ module.exports = React.createClass({ label="Rooms" tagName="im.vector.fake.recent" editable={ true } - verb="restore" + emptyContent={this._getEmptyContent('im.vector.fake.recent')} order="recent" incomingCall={ self.state.incomingCall } collapsed={ self.props.collapsed } @@ -665,7 +725,7 @@ module.exports = React.createClass({ key={ tagName } label={ tagName } tagName={ tagName } - verb={ "tag as " + tagName } + emptyContent={this._getEmptyContent(tagName)} editable={ true } order="manual" incomingCall={ self.state.incomingCall } @@ -681,7 +741,7 @@ module.exports = React.createClass({ <RoomSubList list={ self.state.lists['m.lowpriority'] } label="Low priority" tagName="m.lowpriority" - verb="demote" + emptyContent={this._getEmptyContent('m.lowpriority')} editable={ true } order="recent" incomingCall={ self.state.incomingCall }