Merge branch 'develop' into matthew/notif-panel

This commit is contained in:
Matthew Hodgson 2016-08-30 23:39:00 +01:00
commit a047f81b84
8 changed files with 265 additions and 56 deletions

View File

@ -241,3 +241,4 @@ encrypted.
Note that historical encrypted messages cannot currently be decoded - history Note that historical encrypted messages cannot currently be decoded - history
is therefore lost when the page is reloaded. is therefore lost when the page is reloaded.

View File

@ -17,7 +17,7 @@
"build:css": "catw \"src/skins/vector/css/**/*.css\" -o vector/components.css --no-watch", "build:css": "catw \"src/skins/vector/css/**/*.css\" -o vector/components.css --no-watch",
"build:compile": "babel --source-maps -d lib src", "build:compile": "babel --source-maps -d lib src",
"build:bundle": "NODE_ENV=production webpack -p lib/vector/index.js vector/bundle.js", "build:bundle": "NODE_ENV=production webpack -p lib/vector/index.js vector/bundle.js",
"build:bundle:dev": "NODE_ENV=production webpack --optimize-occurence-order lib/vector/index.js vector/bundle.js", "build:bundle:dev": "webpack --optimize-occurence-order lib/vector/index.js vector/bundle.js",
"build:staticfiles": "cpx -v node_modules/olm/olm.js vector/", "build:staticfiles": "cpx -v node_modules/olm/olm.js vector/",
"build": "npm run build:staticfiles && npm run build:emojione && npm run build:css && npm run build:compile && npm run build:bundle", "build": "npm run build:staticfiles && npm run build:emojione && npm run build:css && npm run build:compile && npm run build:bundle",
"build:dev": "npm run build:staticfiles && npm run build:emojione && npm run build:css && npm run build:compile && npm run build:bundle:dev", "build:dev": "npm run build:staticfiles && npm run build:emojione && npm run build:css && npm run build:compile && npm run build:bundle:dev",
@ -47,8 +47,8 @@
"gfm.css": "^1.1.1", "gfm.css": "^1.1.1",
"highlight.js": "^9.0.0", "highlight.js": "^9.0.0",
"linkifyjs": "2.0.0-beta.4", "linkifyjs": "2.0.0-beta.4",
"matrix-js-sdk": "0.5.6", "matrix-js-sdk": "matrix-org/matrix-js-sdk#develop",
"matrix-react-sdk": "0.6.5", "matrix-react-sdk": "matrix-org/matrix-react-sdk#develop",
"modernizr": "^3.1.0", "modernizr": "^3.1.0",
"q": "^1.4.1", "q": "^1.4.1",
"react": "^15.2.1", "react": "^15.2.1",

View File

@ -17,15 +17,20 @@ limitations under the License.
'use strict'; 'use strict';
var React = require('react'); var React = require('react');
var ReactDOM = require('react-dom');
var classNames = require('classnames');
var DropTarget = require('react-dnd').DropTarget; var DropTarget = require('react-dnd').DropTarget;
var sdk = require('matrix-react-sdk') var sdk = require('matrix-react-sdk')
var dis = require('matrix-react-sdk/lib/dispatcher'); var dis = require('matrix-react-sdk/lib/dispatcher');
var Unread = require('matrix-react-sdk/lib/Unread'); var Unread = require('matrix-react-sdk/lib/Unread');
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
var RoomNotifs = require('matrix-react-sdk/lib/RoomNotifs');
// turn this on for drop & drag console debugging galore // turn this on for drop & drag console debugging galore
var debug = false; var debug = false;
const TRUNCATE_AT = 10;
var roomListTarget = { var roomListTarget = {
canDrop: function() { canDrop: function() {
return true; return true;
@ -80,7 +85,8 @@ var RoomSubList = React.createClass({
getInitialState: function() { getInitialState: function() {
return { return {
hidden: this.props.startAsHidden || false, hidden: this.props.startAsHidden || false,
truncateAt: 20, capTruncate: this.props.list.length > TRUNCATE_AT,
truncateAt: this.props.list.length > TRUNCATE_AT ? TRUNCATE_AT : -1,
sortedList: [], sortedList: [],
}; };
}, },
@ -109,17 +115,62 @@ var RoomSubList = React.createClass({
}); });
}, },
onClick: function(ev) { // The header is collapsable if it is hidden or not stuck
var isHidden = !this.state.hidden; // The dataset elements are added in the RoomList _initAndPositionStickyHeaders method
this.setState({ hidden : isHidden }); isCollapsableOnClick: function() {
var stuck = this.refs.header.dataset.stuck;
if (isHidden) { if (this.state.hidden || stuck === undefined || stuck === "none") {
// as good a way as any to reset the truncate state return true;
this.setState({ truncateAt : 20 }); } else {
this.props.onShowMoreRooms(); return false;
} }
},
this.props.onHeaderClick(isHidden); onClick: function(ev) {
if (this.isCollapsableOnClick()) {
// The header iscCollapsable, so the click is to be interpreted as collapse and truncation logic
var isHidden = false;
var isTruncatable = this.props.list.length > TRUNCATE_AT;
if (this.state.hidden && (this.state.capTruncate && isTruncatable)) {
isHidden = false;
this.setState({
hidden : isHidden,
capTruncate : true,
truncateAt : TRUNCATE_AT
});
} else if ((!this.state.hidden && this.state.capTruncate)
|| (this.state.hidden && (this.state.capTruncate && !isTruncatable)))
{
isHidden = false;
this.setState({
hidden : isHidden,
capTruncate : false,
truncateAt : -1
});
} else if (!this.state.hidden && !this.state.capTruncate) {
isHidden = true;
this.setState({
hidden : isHidden,
capTruncate : true,
truncateAt : TRUNCATE_AT
});
} else {
// Catch any weird states the system gets into
isHidden = false;
this.setState({
hidden : isHidden,
capTruncate : true,
truncateAt : TRUNCATE_AT
});
}
this.props.onShowMoreRooms();
this.props.onHeaderClick(isHidden);
} else {
// The header is stuck, so the click is to be interpreted as a scroll to the header
this.props.onHeaderClick(this.state.hidden, this.refs.header.dataset.originalPosition);
}
}, },
tsOfNewestEvent: function(room) { tsOfNewestEvent: function(room) {
@ -186,6 +237,44 @@ var RoomSubList = React.createClass({
this.setState({ sortedList: list.sort(comparator) }); this.setState({ sortedList: list.sort(comparator) });
}, },
_shouldShowNotifBadge: function(roomNotifState) {
const showBadgeInStates = [RoomNotifs.ALL_MESSAGES, RoomNotifs.ALL_MESSAGES_LOUD];
return showBadgeInStates.indexOf(roomNotifState) > -1;
},
_shouldShowMentionBadge: function(roomNotifState) {
return roomNotifState != RoomNotifs.MUTE;
},
roomNotificationCount: function() {
var self = this;
return this.props.list.reduce(function(result, room) {
var roomNotifState = RoomNotifs.getRoomNotifsState(room.roomId);
var highlight = room.getUnreadNotificationCount('highlight') > 0 || self.props.label === 'Invites';
var notificationCount = room.getUnreadNotificationCount();
const notifBadges = notificationCount > 0 && self._shouldShowNotifBadge(roomNotifState);
const mentionBadges = highlight && self._shouldShowMentionBadge(roomNotifState);
const badges = notifBadges || mentionBadges;
if (badges) {
result[0] += notificationCount;
if (highlight) {
result[1] = true;
}
}
return result;
}, [0, false]);
},
_updateSubListCount: function() {
// Force an update by setting the state to the current state
// Doing it this way rather than using forceUpdate(), so that the shouldComponentUpdate()
// method is honoured
this.setState(this.state);
},
moveRoomTile: function(room, atIndex) { moveRoomTile: function(room, atIndex) {
if (debug) console.log("moveRoomTile: id " + room.roomId + ", atIndex " + atIndex); if (debug) console.log("moveRoomTile: id " + room.roomId + ", atIndex " + atIndex);
//console.log("moveRoomTile before: " + JSON.stringify(this.state.rooms)); //console.log("moveRoomTile before: " + JSON.stringify(this.state.rooms));
@ -293,6 +382,7 @@ var RoomSubList = React.createClass({
unread={ Unread.doesRoomHaveUnreadMessages(room) } unread={ Unread.doesRoomHaveUnreadMessages(room) }
highlight={ room.getUnreadNotificationCount('highlight') > 0 || self.props.label === 'Invites' } highlight={ room.getUnreadNotificationCount('highlight') > 0 || self.props.label === 'Invites' }
isInvite={ self.props.label === 'Invites' } isInvite={ self.props.label === 'Invites' }
refreshSubList={ self._updateSubListCount }
incomingCall={ self.props.incomingCall && (self.props.incomingCall.roomId === room.roomId) ? self.props.incomingCall : null } /> incomingCall={ self.props.incomingCall && (self.props.incomingCall.roomId === room.roomId) ? self.props.incomingCall : null } />
); );
}); });
@ -300,35 +390,47 @@ var RoomSubList = React.createClass({
_getHeaderJsx: function() { _getHeaderJsx: function() {
var TintableSvg = sdk.getComponent("elements.TintableSvg"); var TintableSvg = sdk.getComponent("elements.TintableSvg");
return (
<h2 onClick={ this.onClick } className="mx_RoomSubList_label">
{ this.props.collapsed ? '' : this.props.label }
<img className="mx_RoomSubList_chevron"
src={ this.state.hidden ? "img/list-close.svg" : "img/list-open.svg" }
width="10" height="10" />
</h2>
);
},
_createOverflowTile: function(overflowCount, totalCount) { var subListNotifications = this.roomNotificationCount();
var BaseAvatar = sdk.getComponent('avatars.BaseAvatar'); var subListNotifCount = subListNotifications[0];
// XXX: this is duplicated from RoomTile - factor it out var subListNotifHighlight = subListNotifications[1];
var roomCount = this.props.list.length > 0 ? this.props.list.length : '';
var isTruncatable = this.props.list.length > TRUNCATE_AT;
if (!this.state.hidden && this.state.capTruncate && isTruncatable) {
roomCount = TRUNCATE_AT + " of " + roomCount;
}
var chevronClasses = classNames({
'mx_RoomSubList_chevron': true,
'mx_RoomSubList_chevronUp': this.state.hidden,
'mx_RoomSubList_chevronRight': !this.state.hidden && this.state.capTruncate,
'mx_RoomSubList_chevronDown': !this.state.hidden && !this.state.capTruncate,
});
var badgeClasses = classNames({
'mx_RoomSubList_badge': true,
'mx_RoomSubList_badgeHighlight': subListNotifHighlight,
});
var badge;
if (subListNotifCount > 0) {
badge = <div className={badgeClasses}>{subListNotifCount > 99 ? "99+" : subListNotifCount}</div>;
}
return ( return (
<div className="mx_RoomTile mx_RoomTile_ellipsis" onClick={this._showFullMemberList}> <div className="mx_RoomSubList_labelContainer" ref="header">
<div className="mx_RoomTile_avatar"> <div onClick={ this.onClick } className="mx_RoomSubList_label">
<BaseAvatar url="img/ellipsis.svg" name="..." width={24} height={24} /> { this.props.collapsed ? '' : this.props.label }
<div className="mx_RoomSubList_roomCount">{roomCount}</div>
<div className={chevronClasses}></div>
{badge}
</div> </div>
<div className="mx_RoomTile_name">and { overflowCount } others...</div>
</div> </div>
); );
}, },
_showFullMemberList: function() { _createOverflowTile: function() {}, // NOP
this.setState({
truncateAt: -1
});
this.props.onShowMoreRooms();
},
// Fix any undefined order elements of a room in a manual ordered list // Fix any undefined order elements of a room in a manual ordered list
// room.tag[tagname].order // room.tag[tagname].order

View File

@ -20,7 +20,6 @@ limitations under the License.
margin-right: 16px; margin-right: 16px;
padding-top: 24px; padding-top: 24px;
padding-bottom: 22px; padding-bottom: 22px;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
display: flex; display: flex;
display: -webkit-flex; display: -webkit-flex;

View File

@ -15,7 +15,6 @@ limitations under the License.
*/ */
.mx_RoomList { .mx_RoomList {
padding-top: 8px;
padding-bottom: 12px; padding-bottom: 12px;
min-height: 400px; min-height: 400px;
} }
@ -33,3 +32,8 @@ limitations under the License.
overflow-x: hidden ! important; overflow-x: hidden ! important;
overflow-y: scroll ! important; overflow-y: scroll ! important;
} }
/* Make sure the scrollbar is above the sticky headers from RoomList */
.mx_RoomList_scrollbar .gm-scrollbar.-vertical {
z-index: 5;
}

View File

@ -109,9 +109,8 @@ limitations under the License.
} }
.collapsed .mx_RoomTile_badge { .collapsed .mx_RoomTile_badge {
top: -2px; top: 0px;
min-width: 12px; min-width: 12px;
height: 16px;
border-radius: 16px; border-radius: 16px;
padding: 0px 4px 0px 4px; padding: 0px 4px 0px 4px;
z-index: 200; z-index: 200;
@ -129,22 +128,22 @@ limitations under the License.
display: block; display: block;
width: 0; width: 0;
height: 0; height: 0;
margin-left: 6px; margin-left: 5px;
border-top: 8px solid #ff0064; border-top: 5px solid #ff0064;
border-right: 10px solid transparent; border-right: 7px solid transparent;
} }
.mx_RoomTile_badge { .mx_RoomTile_badge {
display: inline-block; display: inline-block;
min-width: 19px; min-width: 15px;
height: 17px; height: 15px;
position: absolute; position: absolute;
right: 8px; /*gutter */ right: 8px; /*gutter */
top: 9px; top: 9px;
border-radius: 14px; border-radius: 8px;
color: #fff; color: #fff;
font-weight: 600; font-weight: 600;
font-size: 11px; font-size: 10px;
text-align: center; text-align: center;
padding-top: 1px; padding-top: 1px;
padding-left: 4px; padding-left: 4px;
@ -175,7 +174,7 @@ limitations under the License.
} }
.mx_RoomTile_selected { .mx_RoomTile_selected {
background-color: rgba(118,207,166,0.2); background-color: rgba(255, 255, 255, 0.8);
} }
.mx_RoomTile .mx_RoomTile_name.mx_RoomTile_badgeShown { .mx_RoomTile .mx_RoomTile_name.mx_RoomTile_badgeShown {
@ -187,5 +186,3 @@ limitations under the License.
right: 0px; right: 0px;
} }
.mx_RoomTile:hover {
}

View File

@ -49,6 +49,7 @@ limitations under the License.
flex: 1 1 0; flex: 1 1 0;
overflow-y: auto; overflow-y: auto;
z-index: 5;
} }
.mx_LeftPanel.collapsed .mx_BottomLeftMenu { .mx_LeftPanel.collapsed .mx_BottomLeftMenu {
@ -62,7 +63,7 @@ limitations under the License.
-webkit-order: 3; -webkit-order: 3;
order: 3; order: 3;
border-top: 1px solid rgba(0, 0, 0, 0.1); border-top: 1px solid rgba(118, 207, 166, 0.2);
margin-left: 16px; /* gutter */ margin-left: 16px; /* gutter */
margin-right: 16px; /* gutter */ margin-right: 16px; /* gutter */
-webkit-flex: 0 0 60px; -webkit-flex: 0 0 60px;

View File

@ -20,23 +20,128 @@ limitations under the License.
width: 100%; width: 100%;
} }
.mx_RoomSubList_labelContainer {
height: 31px; /* mx_RoomSubList_label height including border */
width: 235px; /* LHS Panel width */
position: relative;
}
.mx_RoomSubList_label { .mx_RoomSubList_label {
position: relative;
text-transform: uppercase; text-transform: uppercase;
color: #3d3b39; color: #3d3b39;
font-weight: 600; font-weight: 600;
font-size: 13px; font-size: 12px;
width: 203px; /* padding + width = LHS Panel width */
height: 17px; /* padding + height = 29px, same as mx_RoomSubList_stickyContainer */
padding-left: 16px; /* gutter */ padding-left: 16px; /* gutter */
padding-right: 16px; /* gutter */ padding-right: 16px; /* gutter */
margin-top: 8px; padding-top: 6px;
margin-bottom: 4px; padding-bottom: 6px;
cursor: pointer; cursor: pointer;
background-color: rgba(118, 207, 166, 0.2);
border-top: solid 2px #eaf5f0;
}
.mx_RoomSubList_label.mx_RoomSubList_fixed {
position: fixed;
top: 0;
z-index: 4;
/* pointer-events: none; */
}
.collapsed .mx_RoomSubList_label {
height: 17px;
width: 28px; /* collapsed LHS Panel width */
}
.collapsed .mx_RoomSubList_labelContainer {
width: 28px; /* collapsed LHS Panel width */
}
.mx_RoomSubList_roomCount {
display: inline-block;
font-size: 12px;
font-weight: normal;
color: #76cfa6;
padding-left: 5px;
text-transform: none;
}
.collapsed .mx_RoomSubList_roomCount {
display: none;
}
.mx_RoomSubList_badge {
display: inline-block;
min-width: 15px;
height: 15px;
position: absolute;
right: 8px; /*gutter */
top: 7px;
border-radius: 8px;
color: #fff;
font-weight: 600;
font-size: 10px;
text-align: center;
padding-top: 1px;
padding-left: 4px;
padding-right: 4px;
background-color: #76cfa6;
}
.collapsed .mx_RoomSubList_badge {
display: none;
}
.mx_RoomSubList_badgeHighlight {
background-color: #ff0064;
}
/* This is the bottom of the speech bubble */
.mx_RoomSubList_badgeHighlight:after {
content: "";
position: absolute;
display: block;
width: 0;
height: 0;
margin-left: 5px;
border-top: 5px solid #ff0064;
border-right: 7px solid transparent;
}
/* Hide the bottom of speech bubble */
.collapsed .mx_RoomSubList_badgeHighlight:after {
display: none;
} }
.mx_RoomSubList_chevron { .mx_RoomSubList_chevron {
padding-left: 4px;
pointer-events: none; pointer-events: none;
position: absolute;
right: 41px;
top: 11px;
} }
.collapsed .mx_RoomSubList_chevron { .mx_RoomSubList_chevronDown {
padding-left: 12px; width: 0;
height: 0;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-top: 6px solid #76cfa6;
}
.mx_RoomSubList_chevronUp {
width: 0;
height: 0;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-bottom: 6px solid #76cfa6;
}
.mx_RoomSubList_chevronRight {
width: 0;
height: 0;
border-top: 5px solid transparent;
border-left: 6px solid #76cfa6;
border-bottom: 5px solid transparent;
} }