Make the left panel more friendly to new users

https://github.com/vector-im/riot-web/issues/3609
This commit is contained in:
David Baker 2017-04-26 18:59:16 +01:00
parent 19482d751d
commit f5f35e3294

View file

@ -29,7 +29,14 @@ import DMRoomMap from '../../../utils/DMRoomMap';
var Receipt = require('../../../utils/Receipt'); var Receipt = require('../../../utils/Receipt');
var constantTimeDispatcher = require('../../../ConstantTimeDispatcher'); 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({ module.exports = React.createClass({
displayName: 'RoomList', displayName: 'RoomList',
@ -53,6 +60,7 @@ module.exports = React.createClass({
getInitialState: function() { getInitialState: function() {
return { return {
isLoadingLeftRooms: false, isLoadingLeftRooms: false,
totalRoomCount: null,
lists: {}, lists: {},
incomingCall: null, incomingCall: null,
}; };
@ -73,8 +81,7 @@ module.exports = React.createClass({
// lookup for which lists a given roomId is currently in. // lookup for which lists a given roomId is currently in.
this.listsForRoomId = {}; this.listsForRoomId = {};
var s = this.getRoomLists(); this.refreshRoomList();
this.setState(s);
// order of the sublists // order of the sublists
//this.listOrder = []; //this.listOrder = [];
@ -317,21 +324,29 @@ module.exports = React.createClass({
// any changes to it incrementally, updating the appropriate sublists // any changes to it incrementally, updating the appropriate sublists
// as needed. // as needed.
// Alternatively we'd do something magical with Immutable.js or similar. // 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(); // this._lastRefreshRoomListTs = Date.now();
}, },
getRoomLists: function() { getRoomLists: function() {
var self = this; var self = this;
var s = { lists: {} }; const lists = {};
s.lists["im.vector.fake.invite"] = []; lists["im.vector.fake.invite"] = [];
s.lists["m.favourite"] = []; lists["m.favourite"] = [];
s.lists["im.vector.fake.recent"] = []; lists["im.vector.fake.recent"] = [];
s.lists["im.vector.fake.direct"] = []; lists["im.vector.fake.direct"] = [];
s.lists["m.lowpriority"] = []; lists["m.lowpriority"] = [];
s.lists["im.vector.fake.archived"] = []; lists["im.vector.fake.archived"] = [];
this.listsForRoomId = {}; this.listsForRoomId = {};
var otherTagNames = {}; var otherTagNames = {};
@ -353,7 +368,7 @@ module.exports = React.createClass({
if (me.membership == "invite") { if (me.membership == "invite") {
self.listsForRoomId[room.roomId].push("im.vector.fake.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)) { else if (HIDE_CONFERENCE_CHANS && Rooms.isConfCallRoom(room, me, self.props.ConferenceHandler)) {
// skip past this room & don't put it in any lists // skip past this room & don't put it in any lists
@ -366,8 +381,8 @@ module.exports = React.createClass({
if (tagNames.length) { if (tagNames.length) {
for (var i = 0; i < tagNames.length; i++) { for (var i = 0; i < tagNames.length; i++) {
var tagName = tagNames[i]; var tagName = tagNames[i];
s.lists[tagName] = s.lists[tagName] || []; lists[tagName] = lists[tagName] || [];
s.lists[tagName].push(room); lists[tagName].push(room);
self.listsForRoomId[room.roomId].push(tagName); self.listsForRoomId[room.roomId].push(tagName);
otherTagNames[tagName] = 1; otherTagNames[tagName] = 1;
} }
@ -375,46 +390,46 @@ module.exports = React.createClass({
else if (dmRoomMap.getUserIdForRoomId(room.roomId)) { else if (dmRoomMap.getUserIdForRoomId(room.roomId)) {
// "Direct Message" rooms (that we're still in and that aren't otherwise tagged) // "Direct Message" rooms (that we're still in and that aren't otherwise tagged)
self.listsForRoomId[room.roomId].push("im.vector.fake.direct"); 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 { else {
self.listsForRoomId[room.roomId].push("im.vector.fake.recent"); 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") { else if (me.membership === "leave") {
self.listsForRoomId[room.roomId].push("im.vector.fake.archived"); 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 { else {
console.error("unrecognised membership: " + me.membership + " - this should never happen"); 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().getAccountData('m.direct') === undefined &&
!MatrixClientPeg.get().isGuest()) !MatrixClientPeg.get().isGuest())
{ {
// scan through the 'recents' list for any rooms which look like DM rooms // scan through the 'recents' list for any rooms which look like DM rooms
// and make them DM rooms // and make them DM rooms
const oldRecents = s.lists["im.vector.fake.recent"]; const oldRecents = lists["im.vector.fake.recent"];
s.lists["im.vector.fake.recent"] = []; lists["im.vector.fake.recent"] = [];
for (const room of oldRecents) { for (const room of oldRecents) {
const me = room.getMember(MatrixClientPeg.get().credentials.userId); const me = room.getMember(MatrixClientPeg.get().credentials.userId);
if (me && Rooms.looksLikeDirectMessageRoom(room, me)) { if (me && Rooms.looksLikeDirectMessageRoom(room, me)) {
self.listsForRoomId[room.roomId].push("im.vector.fake.direct"); 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 { } else {
self.listsForRoomId[room.roomId].push("im.vector.fake.recent"); 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 // save these new guessed DM rooms into the account data
const newMDirectEvent = {}; 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 me = room.getMember(MatrixClientPeg.get().credentials.userId);
const otherPerson = Rooms.getOnlyOtherMember(room, me); const otherPerson = Rooms.getOnlyOtherMember(room, me);
if (!otherPerson) continue; if (!otherPerson) continue;
@ -449,7 +464,7 @@ module.exports = React.createClass({
]; ];
*/ */
return s; return lists;
}, },
_getScrollNode: function() { _getScrollNode: function() {
@ -479,6 +494,7 @@ module.exports = React.createClass({
var incomingCallBox = document.getElementById("incomingCallBox"); var incomingCallBox = document.getElementById("incomingCallBox");
if (incomingCallBox && incomingCallBox.parentElement) { if (incomingCallBox && incomingCallBox.parentElement) {
var scrollArea = this._getScrollNode(); var scrollArea = this._getScrollNode();
if (!scrollArea) return;
// Use the offset of the top of the scroll area from the window // 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 // as this is used to calculate the CSS fixed top position for the stickies
var scrollAreaOffset = scrollArea.getBoundingClientRect().top + window.pageYOffset; var scrollAreaOffset = scrollArea.getBoundingClientRect().top + window.pageYOffset;
@ -502,6 +518,7 @@ module.exports = React.createClass({
// properly through React // properly through React
_initAndPositionStickyHeaders: function(initialise, scrollToPosition) { _initAndPositionStickyHeaders: function(initialise, scrollToPosition) {
var scrollArea = this._getScrollNode(); var scrollArea = this._getScrollNode();
if (!scrollArea) return;
// Use the offset of the top of the scroll area from the window // 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 // as this is used to calculate the CSS fixed top position for the stickies
var scrollAreaOffset = scrollArea.getBoundingClientRect().top + window.pageYOffset; var scrollAreaOffset = scrollArea.getBoundingClientRect().top + window.pageYOffset;
@ -599,6 +616,49 @@ module.exports = React.createClass({
this.refs.gemscroll.forceUpdate(); 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() { render: function() {
var RoomSubList = sdk.getComponent('structures.RoomSubList'); var RoomSubList = sdk.getComponent('structures.RoomSubList');
var self = this; var self = this;
@ -622,7 +682,7 @@ module.exports = React.createClass({
<RoomSubList list={ self.state.lists['m.favourite'] } <RoomSubList list={ self.state.lists['m.favourite'] }
label="Favourites" label="Favourites"
tagName="m.favourite" tagName="m.favourite"
verb="favourite" emptyContent={this._getEmptyContent('m.favourite')}
editable={ true } editable={ true }
order="manual" order="manual"
incomingCall={ self.state.incomingCall } incomingCall={ self.state.incomingCall }
@ -635,7 +695,7 @@ module.exports = React.createClass({
<RoomSubList list={ self.state.lists['im.vector.fake.direct'] } <RoomSubList list={ self.state.lists['im.vector.fake.direct'] }
label="People" label="People"
tagName="im.vector.fake.direct" tagName="im.vector.fake.direct"
verb="tag direct chat" emptyContent={this._getEmptyContent('im.vector.fake.direct')}
editable={ true } editable={ true }
order="recent" order="recent"
incomingCall={ self.state.incomingCall } incomingCall={ self.state.incomingCall }
@ -650,7 +710,7 @@ module.exports = React.createClass({
label="Rooms" label="Rooms"
tagName="im.vector.fake.recent" tagName="im.vector.fake.recent"
editable={ true } editable={ true }
verb="restore" emptyContent={this._getEmptyContent('im.vector.fake.recent')}
order="recent" order="recent"
incomingCall={ self.state.incomingCall } incomingCall={ self.state.incomingCall }
collapsed={ self.props.collapsed } collapsed={ self.props.collapsed }
@ -665,7 +725,7 @@ module.exports = React.createClass({
key={ tagName } key={ tagName }
label={ tagName } label={ tagName }
tagName={ tagName } tagName={ tagName }
verb={ "tag as " + tagName } emptyContent={this._getEmptyContent(tagName)}
editable={ true } editable={ true }
order="manual" order="manual"
incomingCall={ self.state.incomingCall } incomingCall={ self.state.incomingCall }
@ -681,7 +741,7 @@ module.exports = React.createClass({
<RoomSubList list={ self.state.lists['m.lowpriority'] } <RoomSubList list={ self.state.lists['m.lowpriority'] }
label="Low priority" label="Low priority"
tagName="m.lowpriority" tagName="m.lowpriority"
verb="demote" emptyContent={this._getEmptyContent('m.lowpriority')}
editable={ true } editable={ true }
order="recent" order="recent"
incomingCall={ self.state.incomingCall } incomingCall={ self.state.incomingCall }