Merge branch 'new_release_process_stable' into develop

This reverts PRs on riot-web marked as notready, turning the develop
branch into a more stable version.
pull/3942/head
David Baker 2017-05-17 10:43:02 +01:00
commit 7ea0ecd125
11 changed files with 239 additions and 406 deletions

View File

@ -61,6 +61,7 @@
"favico.js": "^0.3.10", "favico.js": "^0.3.10",
"filesize": "3.5.6", "filesize": "3.5.6",
"flux": "~2.0.3", "flux": "~2.0.3",
"gemini-scrollbar": "matrix-org/gemini-scrollbar#b302279",
"gfm.css": "^1.1.1", "gfm.css": "^1.1.1",
"highlight.js": "^9.0.0", "highlight.js": "^9.0.0",
"linkifyjs": "^2.1.3", "linkifyjs": "^2.1.3",
@ -73,7 +74,7 @@
"react-dnd": "^2.1.4", "react-dnd": "^2.1.4",
"react-dnd-html5-backend": "^2.1.2", "react-dnd-html5-backend": "^2.1.2",
"react-dom": "^15.4.0", "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", "sanitize-html": "^1.11.1",
"ua-parser-js": "^0.7.10", "ua-parser-js": "^0.7.10",
"url": "^0.11.0" "url": "^0.11.0"

View File

@ -1,6 +1,5 @@
/* /*
Copyright 2015, 2016 OpenMarket Ltd Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -15,8 +14,13 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React from 'react'; 'use strict';
import sdk from 'matrix-react-sdk';
var React = require('react');
var ReactDOM = require('react-dom');
var sdk = require('matrix-react-sdk')
var dis = require('matrix-react-sdk/lib/dispatcher');
var AccessibleButton = require('matrix-react-sdk/lib/components/views/elements/AccessibleButton');
module.exports = React.createClass({ module.exports = React.createClass({
displayName: 'BottomLeftMenu', displayName: 'BottomLeftMenu',
@ -26,28 +30,121 @@ module.exports = React.createClass({
teamToken: React.PropTypes.string, teamToken: React.PropTypes.string,
}, },
getInitialState: function() {
return({
directoryHover : false,
roomsHover : false,
homeHover: false,
peopleHover : false,
settingsHover : false,
});
},
// Room events
onDirectoryClick: function() {
dis.dispatch({ action: 'view_room_directory' });
},
onDirectoryMouseEnter: function() {
this.setState({ directoryHover: true });
},
onDirectoryMouseLeave: function() {
this.setState({ directoryHover: false });
},
onRoomsClick: function() {
dis.dispatch({ action: 'view_create_room' });
},
onRoomsMouseEnter: function() {
this.setState({ roomsHover: true });
},
onRoomsMouseLeave: function() {
this.setState({ roomsHover: false });
},
// Home button events
onHomeClick: function() {
dis.dispatch({ action: 'view_home_page' });
},
onHomeMouseEnter: function() {
this.setState({ homeHover: true });
},
onHomeMouseLeave: function() {
this.setState({ homeHover: false });
},
// People events
onPeopleClick: function() {
dis.dispatch({ action: 'view_create_chat' });
},
onPeopleMouseEnter: function() {
this.setState({ peopleHover: true });
},
onPeopleMouseLeave: function() {
this.setState({ peopleHover: false });
},
// Settings events
onSettingsClick: function() {
dis.dispatch({ action: 'view_user_settings' });
},
onSettingsMouseEnter: function() {
this.setState({ settingsHover: true });
},
onSettingsMouseLeave: function() {
this.setState({ settingsHover: false });
},
// Get the label/tooltip to show
getLabel: function(label, show) {
if (show) {
var RoomTooltip = sdk.getComponent("rooms.RoomTooltip");
return <RoomTooltip className="mx_BottomLeftMenu_tooltip" label={label} />;
}
},
render: function() { render: function() {
const HomeButton = sdk.getComponent('elements.HomeButton'); var TintableSvg = sdk.getComponent('elements.TintableSvg');
const StartChatButton = sdk.getComponent('elements.StartChatButton');
const RoomDirectoryButton = sdk.getComponent('elements.RoomDirectoryButton');
const CreateRoomButton = sdk.getComponent('elements.CreateRoomButton');
const SettingsButton = sdk.getComponent('elements.SettingsButton');
var homeButton; var homeButton;
if (this.props.teamToken) { if (this.props.teamToken) {
homeButton = <HomeButton tooltip={true} />; homeButton = (
<AccessibleButton className="mx_BottomLeftMenu_homePage" onClick={ this.onHomeClick } onMouseEnter={ this.onHomeMouseEnter } onMouseLeave={ this.onHomeMouseLeave } >
<TintableSvg src="img/icons-home.svg" width="25" height="25" />
{ this.getLabel("Welcome page", this.state.homeHover) }
</AccessibleButton>
);
} }
return ( return (
<div className="mx_BottomLeftMenu"> <div className="mx_BottomLeftMenu">
<div className="mx_BottomLeftMenu_options"> <div className="mx_BottomLeftMenu_options">
{ homeButton } { homeButton }
<StartChatButton tooltip={true} /> <AccessibleButton className="mx_BottomLeftMenu_people" onClick={ this.onPeopleClick } onMouseEnter={ this.onPeopleMouseEnter } onMouseLeave={ this.onPeopleMouseLeave } >
<RoomDirectoryButton tooltip={true} /> <TintableSvg src="img/icons-people.svg" width="25" height="25" />
<CreateRoomButton tooltip={true} /> { this.getLabel("Start chat", this.state.peopleHover) }
<span className="mx_BottomLeftMenu_settings"> </AccessibleButton>
<SettingsButton tooltip={true} /> <AccessibleButton className="mx_BottomLeftMenu_directory" onClick={ this.onDirectoryClick } onMouseEnter={ this.onDirectoryMouseEnter } onMouseLeave={ this.onDirectoryMouseLeave } >
</span> <TintableSvg src="img/icons-directory.svg" width="25" height="25"/>
{ this.getLabel("Room directory", this.state.directoryHover) }
</AccessibleButton>
<AccessibleButton className="mx_BottomLeftMenu_createRoom" onClick={ this.onRoomsClick } onMouseEnter={ this.onRoomsMouseEnter } onMouseLeave={ this.onRoomsMouseLeave } >
<TintableSvg src="img/icons-create-room.svg" width="25" height="25" />
{ this.getLabel("Create new room", this.state.roomsHover) }
</AccessibleButton>
<AccessibleButton className="mx_BottomLeftMenu_settings" onClick={ this.onSettingsClick } onMouseEnter={ this.onSettingsMouseEnter } onMouseLeave={ this.onSettingsMouseLeave } >
<TintableSvg src="img/icons-settings.svg" width="25" height="25" />
{ this.getLabel("Settings", this.state.settingsHover) }
</AccessibleButton>
</div> </div>
</div> </div>
); );

View File

@ -19,11 +19,9 @@ limitations under the License.
var React = require('react'); var React = require('react');
var DragDropContext = require('react-dnd').DragDropContext; var DragDropContext = require('react-dnd').DragDropContext;
var HTML5Backend = require('react-dnd-html5-backend'); var HTML5Backend = require('react-dnd-html5-backend');
var KeyCode = require('matrix-react-sdk/lib/KeyCode');
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 VectorConferenceHandler = require('../../VectorConferenceHandler'); var VectorConferenceHandler = require('../../VectorConferenceHandler');
var CallHandler = require("matrix-react-sdk/lib/CallHandler"); var CallHandler = require("matrix-react-sdk/lib/CallHandler");
@ -42,10 +40,6 @@ var LeftPanel = React.createClass({
}; };
}, },
componentWillMount: function() {
this.focusedElement = null;
},
componentDidMount: function() { componentDidMount: function() {
this.dispatcherRef = dis.register(this.onAction); this.dispatcherRef = dis.register(this.onAction);
}, },
@ -68,91 +62,6 @@ var LeftPanel = React.createClass({
} }
}, },
_onFocus: function(ev) {
this.focusedElement = ev.target;
},
_onBlur: function(ev) {
this.focusedElement = null;
},
_onKeyDown: function(ev) {
if (!this.focusedElement) return;
let handled = false;
switch (ev.keyCode) {
case KeyCode.UP:
this._onMoveFocus(true);
handled = true;
break;
case KeyCode.DOWN:
this._onMoveFocus(false);
handled = true;
break;
}
if (handled) {
ev.stopPropagation();
ev.preventDefault();
}
},
_onMoveFocus: function(up) {
var element = this.focusedElement;
// unclear why this isn't needed
// var descending = (up == this.focusDirection) ? this.focusDescending : !this.focusDescending;
// this.focusDirection = up;
var descending = false; // are we currently descending or ascending through the DOM tree?
var classes;
do {
var child = up ? element.lastElementChild : element.firstElementChild;
var sibling = up ? element.previousElementSibling : element.nextElementSibling;
if (descending) {
if (child) {
element = child;
}
else if (sibling) {
element = sibling;
}
else {
descending = false;
element = element.parentElement;
}
}
else {
if (sibling) {
element = sibling;
descending = true;
}
else {
element = element.parentElement;
}
}
if (element) {
classes = element.classList;
if (classes.contains("mx_LeftPanel")) { // we hit the top
element = up ? element.lastElementChild : element.firstElementChild;
descending = true;
}
}
} while(element && !(
classes.contains("mx_RoomTile") ||
classes.contains("mx_SearchBox_search") ||
classes.contains("mx_RoomSubList_ellipsis")));
if (element) {
element.focus();
this.focusedElement = element;
this.focusedDescending = descending;
}
},
_recheckCallElement: function(selectedRoomId) { _recheckCallElement: function(selectedRoomId) {
// if we aren't viewing a room with an ongoing call, but there is an // if we aren't viewing a room with an ongoing call, but there is an
// active call, show the call element - we need to do this to make // active call, show the call element - we need to do this to make
@ -211,8 +120,7 @@ var LeftPanel = React.createClass({
} }
return ( return (
<aside className={classes} style={{ opacity: this.props.opacity }} <aside className={classes} style={{ opacity: this.props.opacity }}>
onKeyDown={ this._onKeyDown } onFocus={ this._onFocus } onBlur={ this._onBlur }>
<SearchBox collapsed={ this.props.collapsed } onSearch={ this.onSearch } /> <SearchBox collapsed={ this.props.collapsed } onSearch={ this.onSearch } />
{ collapseButton } { collapseButton }
{ callPreview } { callPreview }

View File

@ -1,5 +1,4 @@
/* /*
Copyright 2017 Vector Creations Ltd
Copyright 2015, 2016 OpenMarket Ltd Copyright 2015, 2016 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
@ -28,11 +27,9 @@ var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
var RoomNotifs = require('matrix-react-sdk/lib/RoomNotifs'); var RoomNotifs = require('matrix-react-sdk/lib/RoomNotifs');
var FormattingUtils = require('matrix-react-sdk/lib/utils/FormattingUtils'); var FormattingUtils = require('matrix-react-sdk/lib/utils/FormattingUtils');
var AccessibleButton = require('matrix-react-sdk/lib/components/views/elements/AccessibleButton'); var AccessibleButton = require('matrix-react-sdk/lib/components/views/elements/AccessibleButton');
var ConstantTimeDispatcher = require('matrix-react-sdk/lib/ConstantTimeDispatcher');
var RoomSubListHeader = require('./RoomSubListHeader.js');
import Modal from 'matrix-react-sdk/lib/Modal'; import Modal from 'matrix-react-sdk/lib/Modal';
// turn this on for drag & drop console debugging galore // turn this on for drop & drag console debugging galore
var debug = false; var debug = false;
const TRUNCATE_AT = 10; const TRUNCATE_AT = 10;
@ -75,17 +72,17 @@ var RoomSubList = React.createClass({
order: React.PropTypes.string.isRequired, order: React.PropTypes.string.isRequired,
// undefined if no room is selected (eg we are showing settings)
selectedRoom: React.PropTypes.string,
startAsHidden: React.PropTypes.bool, startAsHidden: React.PropTypes.bool,
showSpinner: React.PropTypes.bool, // true to show a spinner if 0 elements when expanded showSpinner: React.PropTypes.bool, // true to show a spinner if 0 elements when expanded
collapsed: React.PropTypes.bool.isRequired, // is LeftPanel collapsed? collapsed: React.PropTypes.bool.isRequired, // is LeftPanel collapsed?
onHeaderClick: React.PropTypes.func, onHeaderClick: React.PropTypes.func,
alwaysShowHeader: React.PropTypes.bool, alwaysShowHeader: React.PropTypes.bool,
selectedRoom: React.PropTypes.string,
incomingCall: React.PropTypes.object, incomingCall: React.PropTypes.object,
onShowMoreRooms: React.PropTypes.func, onShowMoreRooms: React.PropTypes.func,
searchFilter: React.PropTypes.string, searchFilter: React.PropTypes.string,
emptyContent: React.PropTypes.node, // content shown if the list is empty
headerItems: React.PropTypes.node, // content shown in the sublist header
}, },
getInitialState: function() { getInitialState: function() {
@ -104,31 +101,13 @@ var RoomSubList = React.createClass({
}, },
componentWillMount: function() { componentWillMount: function() {
constantTimeDispatcher.register("RoomSubList.sort", this.props.tagName, this.onSort);
constantTimeDispatcher.register("RoomSubList.refreshHeader", this.props.tagName, this.onRefresh);
this.sortList(this.applySearchFilter(this.props.list, this.props.searchFilter), this.props.order); this.sortList(this.applySearchFilter(this.props.list, this.props.searchFilter), this.props.order);
this._fixUndefinedOrder(this.props.list);
},
componentWillUnmount: function() {
constantTimeDispatcher.unregister("RoomSubList.sort", this.props.tagName, this.onSort);
constantTimeDispatcher.unregister("RoomSubList.refreshHeader", this.props.tagName, this.onRefresh);
}, },
componentWillReceiveProps: function(newProps) { componentWillReceiveProps: function(newProps) {
// order the room list appropriately before we re-render // order the room list appropriately before we re-render
//if (debug) console.log("received new props, list = " + newProps.list); //if (debug) console.log("received new props, list = " + newProps.list);
this.sortList(this.applySearchFilter(newProps.list, newProps.searchFilter), newProps.order); this.sortList(this.applySearchFilter(newProps.list, newProps.searchFilter), newProps.order);
this._fixUndefinedOrder(newProps.list);
},
onSort: function() {
this.sortList(this.applySearchFilter(this.props.list, this.props.searchFilter), this.props.order);
// we deliberately don't waste time trying to fix undefined ordering here
},
onRefresh: function() {
this.forceUpdate();
}, },
applySearchFilter: function(list, filter) { applySearchFilter: function(list, filter) {
@ -141,7 +120,7 @@ var RoomSubList = React.createClass({
// The header is collapsable if it is hidden or not stuck // The header is collapsable if it is hidden or not stuck
// The dataset elements are added in the RoomList _initAndPositionStickyHeaders method // The dataset elements are added in the RoomList _initAndPositionStickyHeaders method
isCollapsableOnClick: function() { isCollapsableOnClick: function() {
var stuck = this.refs.header.refs.header.dataset.stuck; var stuck = this.refs.header.dataset.stuck;
if (this.state.hidden || stuck === undefined || stuck === "none") { if (this.state.hidden || stuck === undefined || stuck === "none") {
return true; return true;
} else { } else {
@ -164,15 +143,14 @@ var RoomSubList = React.createClass({
this.props.onHeaderClick(isHidden); this.props.onHeaderClick(isHidden);
} else { } else {
// The header is stuck, so the click is to be interpreted as a scroll to the header // 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.refs.header.dataset.originalPosition); this.props.onHeaderClick(this.state.hidden, this.refs.header.dataset.originalPosition);
} }
}, },
onRoomTileClick(roomId, ev) { onRoomTileClick(roomId) {
dis.dispatch({ dis.dispatch({
action: 'view_room', action: 'view_room',
room_id: roomId, room_id: roomId,
clear_search: (ev && (ev.keyCode == 13 || ev.keyCode == 32)),
}); });
}, },
@ -234,6 +212,9 @@ var RoomSubList = React.createClass({
if (order === "manual") comparator = this.manualComparator; if (order === "manual") comparator = this.manualComparator;
if (order === "recent") comparator = this.recentsComparator; if (order === "recent") comparator = this.recentsComparator;
// Fix undefined orders here, and make sure the backend gets updated as well
this._fixUndefinedOrder(list);
//if (debug) console.log("sorting list for sublist " + this.props.label + " with length " + list.length + ", this.props.list = " + this.props.list); //if (debug) console.log("sorting list for sublist " + this.props.label + " with length " + list.length + ", this.props.list = " + this.props.list);
this.setState({ sortedList: list.sort(comparator) }); this.setState({ sortedList: list.sort(comparator) });
}, },
@ -268,9 +249,10 @@ var RoomSubList = React.createClass({
if (badges) { if (badges) {
result[0] += notificationCount; result[0] += notificationCount;
if (highlight) {
result[1] = true;
}
} }
result[1] |= highlight;
} }
return result; return result;
}, [0, false]); }, [0, false]);
@ -378,6 +360,7 @@ var RoomSubList = React.createClass({
var self = this; var self = this;
var DNDRoomTile = sdk.getComponent("rooms.DNDRoomTile"); var DNDRoomTile = sdk.getComponent("rooms.DNDRoomTile");
return this.state.sortedList.map(function(room) { return this.state.sortedList.map(function(room) {
var selected = room.roomId == self.props.selectedRoom;
// XXX: is it evil to pass in self as a prop to RoomTile? // XXX: is it evil to pass in self as a prop to RoomTile?
return ( return (
<DNDRoomTile <DNDRoomTile
@ -385,7 +368,9 @@ var RoomSubList = React.createClass({
roomSubList={ self } roomSubList={ self }
key={ room.roomId } key={ room.roomId }
collapsed={ self.props.collapsed || false} collapsed={ self.props.collapsed || false}
selectedRoom={ self.props.selectedRoom } selected={ selected }
unread={ Unread.doesRoomHaveUnreadMessages(room) }
highlight={ room.getUnreadNotificationCount('highlight') > 0 || self.props.label === 'Invites' }
isInvite={ self.props.label === 'Invites' } isInvite={ self.props.label === 'Invites' }
refreshSubList={ self._updateSubListCount } refreshSubList={ self._updateSubListCount }
incomingCall={ null } incomingCall={ null }
@ -395,6 +380,70 @@ var RoomSubList = React.createClass({
}); });
}, },
_getHeaderJsx: function() {
var TintableSvg = sdk.getComponent("elements.TintableSvg");
var subListNotifications = this.roomNotificationCount();
var subListNotifCount = subListNotifications[0];
var subListNotifHighlight = subListNotifications[1];
var roomCount = this.props.list.length > 0 ? this.props.list.length : '';
var chevronClasses = classNames({
'mx_RoomSubList_chevron': true,
'mx_RoomSubList_chevronRight': this.state.hidden,
'mx_RoomSubList_chevronDown': !this.state.hidden,
});
var badgeClasses = classNames({
'mx_RoomSubList_badge': true,
'mx_RoomSubList_badgeHighlight': subListNotifHighlight,
});
var badge;
if (subListNotifCount > 0) {
badge = <div className={badgeClasses}>{ FormattingUtils.formatCount(subListNotifCount) }</div>;
}
// When collapsed, allow a long hover on the header to show user
// the full tag name and room count
var title;
if (this.props.collapsed) {
title = this.props.label;
if (roomCount !== '') {
title += " [" + roomCount + "]";
}
}
var incomingCall;
if (this.props.incomingCall) {
var self = this;
// Check if the incoming call is for this section
var incomingCallRoom = this.props.list.filter(function(room) {
return self.props.incomingCall.roomId === room.roomId;
});
if (incomingCallRoom.length === 1) {
var IncomingCallBox = sdk.getComponent("voip.IncomingCallBox");
incomingCall = <IncomingCallBox className="mx_RoomSubList_incomingCall" incomingCall={ this.props.incomingCall }/>;
}
}
var tabindex = this.props.searchFilter === "" ? "0" : "-1";
return (
<div className="mx_RoomSubList_labelContainer" title={ title } ref="header">
<AccessibleButton onClick={ this.onClick } className="mx_RoomSubList_label" tabIndex={tabindex}>
{ this.props.collapsed ? '' : this.props.label }
<div className="mx_RoomSubList_roomCount">{ roomCount }</div>
<div className={chevronClasses}></div>
{ badge }
{ incomingCall }
</AccessibleButton>
</div>
);
},
_createOverflowTile: function(overflowCount, totalCount) { _createOverflowTile: function(overflowCount, totalCount) {
var content = <div className="mx_RoomSubList_chevronDown"></div>; var content = <div className="mx_RoomSubList_chevronDown"></div>;
@ -450,7 +499,7 @@ var RoomSubList = React.createClass({
// gets triggered and another list is passed in. Doing it one at a time means that // gets triggered and another list is passed in. Doing it one at a time means that
// we always correctly calculate the highest order for the list - stops multiple // we always correctly calculate the highest order for the list - stops multiple
// rooms getting the same order. This is only really relevant for the first time this // rooms getting the same order. This is only really relevant for the first time this
// is run with historical room tag data, after that there should only be one undefined // is run with historical room tag data, after that there should only be undefined
// in the list at a time anyway. // in the list at a time anyway.
for (let i = 0; i < list.length; i++) { for (let i = 0; i < list.length; i++) {
if (list[i].tags[self.props.tagName] && list[i].tags[self.props.tagName].order === undefined) { if (list[i].tags[self.props.tagName] && list[i].tags[self.props.tagName].order === undefined) {
@ -472,25 +521,16 @@ var RoomSubList = React.createClass({
render: function() { render: function() {
var connectDropTarget = this.props.connectDropTarget; var connectDropTarget = this.props.connectDropTarget;
var RoomDropTarget = sdk.getComponent('rooms.RoomDropTarget');
var TruncatedList = sdk.getComponent('elements.TruncatedList'); var TruncatedList = sdk.getComponent('elements.TruncatedList');
var label = this.props.collapsed ? null : this.props.label; var label = this.props.collapsed ? null : this.props.label;
let content; //console.log("render: " + JSON.stringify(this.state.sortedList));
if (this.state.sortedList.length == 0) {
content = this.props.emptyContent;
} else {
content = this.makeRoomTiles();
}
var roomCount = this.props.list.length > 0 ? this.props.list.length : ''; var target;
if (this.state.sortedList.length == 0 && this.props.editable) {
var isIncomingCallRoom; target = <RoomDropTarget label={ 'Drop here to ' + this.props.verb }/>;
if (this.props.incomingCall) {
// Check if the incoming call is for this section
isIncomingCallRoom = this.props.list.find(room=>{
return this.props.incomingCall.roomId === room.roomId;
}) ? true : false;
} }
if (this.state.sortedList.length > 0 || this.props.editable) { if (this.state.sortedList.length > 0 || this.props.editable) {
@ -500,7 +540,8 @@ var RoomSubList = React.createClass({
if (!this.state.hidden) { if (!this.state.hidden) {
subList = <TruncatedList className={ classes } truncateAt={this.state.truncateAt} subList = <TruncatedList className={ classes } truncateAt={this.state.truncateAt}
createOverflowElement={this._createOverflowTile} > createOverflowElement={this._createOverflowTile} >
{ content } { target }
{ this.makeRoomTiles() }
</TruncatedList>; </TruncatedList>;
} }
else { else {
@ -510,20 +551,7 @@ var RoomSubList = React.createClass({
return connectDropTarget( return connectDropTarget(
<div> <div>
<RoomSubListHeader { this._getHeaderJsx() }
ref='header'
label={ this.props.label }
tagName={ this.props.tagName }
roomCount={ roomCount }
collapsed={ this.props.collapsed }
hidden={ this.state.hidden }
incomingCall={ this.props.incomingCall }
isIncomingCallRoom={ isIncomingCallRoom }
roomNotificationCount={ this.roomNotificationCount() }
onClick={ this.onClick }
onHeaderClick={ this.props.onHeaderClick }
headerItems={this.props.headerItems}
/>
{ subList } { subList }
</div> </div>
); );
@ -532,21 +560,7 @@ var RoomSubList = React.createClass({
var Loader = sdk.getComponent("elements.Spinner"); var Loader = sdk.getComponent("elements.Spinner");
return ( return (
<div className="mx_RoomSubList"> <div className="mx_RoomSubList">
{ this.props.alwaysShowHeader ? { this.props.alwaysShowHeader ? this._getHeaderJsx() : undefined }
<RoomSubListHeader
ref='header'
label={ this.props.label }
tagName={ this.props.tagName }
roomCount={ roomCount }
collapsed={ this.props.collapsed }
hidden={ this.state.hidden }
isIncomingCallRoom={ isIncomingCallRoom }
roomNotificationCount={ this.roomNotificationCount() }
onClick={ this.onClick }
onHeaderClick={ this.props.onHeaderClick }
headerItems={this.props.headerItems}
/>
: undefined }
{ (this.props.showSpinner && !this.state.hidden) ? <Loader /> : undefined } { (this.props.showSpinner && !this.state.hidden) ? <Loader /> : undefined }
</div> </div>
); );

View File

@ -1,117 +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 classNames from 'classnames';
import sdk from 'matrix-react-sdk';
import { formatCount } from 'matrix-react-sdk/lib/utils/FormattingUtils';
import AccessibleButton from 'matrix-react-sdk/lib/components/views/elements/AccessibleButton';
module.exports = React.createClass({
displayName: 'RoomSubListHeader',
propTypes: {
label: React.PropTypes.string.isRequired,
tagName: React.PropTypes.string,
roomCount: React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.number
]),
collapsed: React.PropTypes.bool.isRequired, // is LeftPanel collapsed?
incomingCall: React.PropTypes.object,
isIncomingCallRoom: React.PropTypes.bool,
roomNotificationCount: React.PropTypes.array,
hidden: React.PropTypes.bool,
onClick: React.PropTypes.func,
onHeaderClick: React.PropTypes.func,
headerItems: React.PropTypes.node, // content shown in the sublist header
},
getDefaultProps: function() {
return {
onHeaderClick: function() {}, // NOP
};
},
componentWillMount: function() {
// constantTimeDispatcher.register("RoomSubList.refreshHeader", this.props.tagName, this.onRefresh);
},
componentWillUnmount: function() {
// constantTimeDispatcher.unregister("RoomSubList.refreshHeader", this.props.tagName, this.onRefresh);
},
// onRefresh: function() {
// this.forceUpdate();
// },
render: function() {
const TintableSvg = sdk.getComponent("elements.TintableSvg");
const subListNotifications = this.props.roomNotificationCount;
const subListNotifCount = subListNotifications[0];
const subListNotifHighlight = subListNotifications[1];
const chevronClasses = classNames({
'mx_RoomSubList_chevron': true,
'mx_RoomSubList_chevronRight': this.props.hidden,
'mx_RoomSubList_chevronDown': !this.props.hidden,
});
const badgeClasses = classNames({
'mx_RoomSubList_badge': true,
'mx_RoomSubList_badgeHighlight': subListNotifHighlight,
});
let badge;
if (subListNotifCount > 0) {
badge = <div className={badgeClasses}>{ formatCount(subListNotifCount) }</div>;
} else if (subListNotifHighlight) {
badge = <div className={badgeClasses}>!</div>;
}
// When collapsed, allow a long hover on the header to show user
// the full tag name and room count
let title;
const roomCount = this.props.roomCount;
if (this.props.collapsed) {
title = this.props.label;
if (roomCount !== '') {
title += " [" + roomCount + "]";
}
}
let incomingCall;
if (this.props.isIncomingCallRoom) {
const IncomingCallBox = sdk.getComponent("voip.IncomingCallBox");
incomingCall = <IncomingCallBox className="mx_RoomSubList_incomingCall" incomingCall={ this.props.incomingCall }/>;
}
return (
<div className="mx_RoomSubList_labelContainer" title={ title } ref="header">
<AccessibleButton onClick={ this.props.onClick } className="mx_RoomSubList_label" tabIndex="0">
{ this.props.collapsed ? '' : this.props.label }
{this.props.headerItems}
<div className="mx_RoomSubList_roomCount">{ roomCount }</div>
<div className={chevronClasses}></div>
{ badge }
</AccessibleButton>
{ incomingCall }
</div>
);
},
});

View File

@ -21,7 +21,6 @@ var sdk = require('matrix-react-sdk')
var dis = require('matrix-react-sdk/lib/dispatcher'); var dis = require('matrix-react-sdk/lib/dispatcher');
var rate_limited_func = require('matrix-react-sdk/lib/ratelimitedfunc'); var rate_limited_func = require('matrix-react-sdk/lib/ratelimitedfunc');
var AccessibleButton = require('matrix-react-sdk/lib/components/views/elements/AccessibleButton'); var AccessibleButton = require('matrix-react-sdk/lib/components/views/elements/AccessibleButton');
var KeyCode = require('matrix-react-sdk/lib/KeyCode');
module.exports = React.createClass({ module.exports = React.createClass({
displayName: 'SearchBox', displayName: 'SearchBox',
@ -39,23 +38,25 @@ module.exports = React.createClass({
componentDidMount: function() { componentDidMount: function() {
this.dispatcherRef = dis.register(this.onAction); this.dispatcherRef = dis.register(this.onAction);
document.addEventListener('keydown', this._onKeyDown);
}, },
componentWillUnmount: function() { componentWillUnmount: function() {
dis.unregister(this.dispatcherRef); dis.unregister(this.dispatcherRef);
document.removeEventListener('keydown', this._onKeyDown);
}, },
onAction: function(payload) { onAction: function(payload) {
// Disabling this as I find it really really annoying, and was used to the
// previous behaviour - see https://github.com/vector-im/riot-web/issues/3348
/*
switch (payload.action) { switch (payload.action) {
// Clear up the text field when a room is selected. // Clear up the text field when a room is selected.
case 'view_room': case 'view_room':
if (payload.clear_search && this.refs.search) { if (this.refs.search) {
this._clearSearch(); this._clearSearch();
} }
break; break;
} }
*/
}, },
onChange: function() { onChange: function() {
@ -89,38 +90,6 @@ module.exports = React.createClass({
this.onChange(); this.onChange();
}, },
_onKeyDown: function(ev) {
let handled = false;
const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
let ctrlCmdOnly;
if (isMac) {
ctrlCmdOnly = ev.metaKey && !ev.altKey && !ev.ctrlKey && !ev.shiftKey;
} else {
ctrlCmdOnly = ev.ctrlKey && !ev.altKey && !ev.metaKey && !ev.shiftKey;
}
switch (ev.keyCode) {
case KeyCode.ESCAPE:
this._clearSearch();
dis.dispatch({action: 'focus_composer'});
break;
case KeyCode.KEY_K:
if (ctrlCmdOnly) {
if (this.refs.search) {
this.refs.search.focus();
this.refs.search.select();
}
handled = true;
}
break;
}
if (handled) {
ev.stopPropagation();
ev.preventDefault();
}
},
render: function() { render: function() {
var TintableSvg = sdk.getComponent('elements.TintableSvg'); var TintableSvg = sdk.getComponent('elements.TintableSvg');

View File

@ -27,7 +27,6 @@
@import "./matrix-react-sdk/views/elements/_MemberEventListSummary.scss"; @import "./matrix-react-sdk/views/elements/_MemberEventListSummary.scss";
@import "./matrix-react-sdk/views/elements/_ProgressBar.scss"; @import "./matrix-react-sdk/views/elements/_ProgressBar.scss";
@import "./matrix-react-sdk/views/elements/_RichText.scss"; @import "./matrix-react-sdk/views/elements/_RichText.scss";
@import "./matrix-react-sdk/views/elements/_RoleButton.scss";
@import "./matrix-react-sdk/views/login/_InteractiveAuthEntryComponents.scss"; @import "./matrix-react-sdk/views/login/_InteractiveAuthEntryComponents.scss";
@import "./matrix-react-sdk/views/login/_ServerConfig.scss"; @import "./matrix-react-sdk/views/login/_ServerConfig.scss";
@import "./matrix-react-sdk/views/messages/_MEmoteBody.scss"; @import "./matrix-react-sdk/views/messages/_MEmoteBody.scss";

View File

@ -172,7 +172,7 @@ hr.mx_RoomView_myReadMarker {
max-height: 0px; max-height: 0px;
background-color: $primary-bg-color; background-color: $primary-bg-color;
z-index: 5; z-index: 1000;
overflow: hidden; overflow: hidden;
-webkit-transition: all .2s ease-out; -webkit-transition: all .2s ease-out;
@ -260,4 +260,4 @@ hr.mx_RoomView_myReadMarker {
.mx_RoomView_ongoingConfCallNotification a { .mx_RoomView_ongoingConfCallNotification a {
color: $accent-fg-color ! important; color: $accent-fg-color ! important;
} }

View File

@ -1,33 +0,0 @@
/*
Copyright 2107 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.
*/
.mx_RoleButton {
margin-left: 4px;
margin-right: 4px;
cursor: pointer;
display: inline-block;
}
.mx_RoleButton object {
pointer-events: none;
}
.mx_RoleButton_tooltip {
display: inline-block;
position: relative;
top: -25px;
left: 6px;
}

View File

@ -1,6 +1,5 @@
/* /*
Copyright 2015, 2016 OpenMarket Ltd Copyright 2015, 2016 OpenMarket Ltd
Copyright 2107 Vector Creations Ltd
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -38,25 +37,3 @@ limitations under the License.
.mx_RoomList_scrollbar .gm-scrollbar.-vertical { .mx_RoomList_scrollbar .gm-scrollbar.-vertical {
z-index: 6; z-index: 6;
} }
.mx_RoomList_emptySubListTip {
font-size: 13px;
margin-left: 18px;
margin-right: 18px;
margin-top: 8px;
margin-bottom: 7px;
padding: 5px;
border: 1px dashed $accent-color;
color: $primary-fg-color;
background-color: $droptarget-bg-color;
border-radius: 4px;
}
.mx_RoomList_emptySubListTip .mx_RoleButton {
vertical-align: -3px;
}
.mx_RoomList_headerButtons {
position: absolute;
right: 60px;
}

View File

@ -64,25 +64,43 @@ limitations under the License.
pointer-events: none; pointer-events: none;
} }
.collapsed .mx_RoleButton { .mx_LeftPanel .mx_BottomLeftMenu_homePage,
.mx_LeftPanel .mx_BottomLeftMenu_directory,
.mx_LeftPanel .mx_BottomLeftMenu_createRoom,
.mx_LeftPanel .mx_BottomLeftMenu_people,
.mx_LeftPanel .mx_BottomLeftMenu_settings {
display: inline-block;
cursor: pointer;
}
.collapsed .mx_BottomLeftMenu_homePage,
.collapsed .mx_BottomLeftMenu_directory,
.collapsed .mx_BottomLeftMenu_createRoom,
.collapsed .mx_BottomLeftMenu_people,
.collapsed .mx_BottomLeftMenu_settings {
margin-right: 0px ! important; margin-right: 0px ! important;
padding-top: 3px ! important; padding-top: 3px ! important;
padding-bottom: 3px ! important; padding-bottom: 3px ! important;
} }
.mx_BottomLeftMenu_options .mx_RoleButton { .mx_LeftPanel .mx_BottomLeftMenu_homePage,
margin-left: 0px; .mx_LeftPanel .mx_BottomLeftMenu_directory,
.mx_LeftPanel .mx_BottomLeftMenu_createRoom,
.mx_LeftPanel .mx_BottomLeftMenu_people {
margin-right: 10px; margin-right: 10px;
} }
.mx_BottomLeftMenu_options .mx_BottomLeftMenu_settings { .mx_LeftPanel .mx_BottomLeftMenu_settings {
float: right; float: right;
} }
.mx_BottomLeftMenu_options .mx_BottomLeftMenu_settings .mx_RoleButton {
margin-right: 0px;
}
.mx_LeftPanel.collapsed .mx_BottomLeftMenu_settings { .mx_LeftPanel.collapsed .mx_BottomLeftMenu_settings {
float: none; float: none;
} }
.mx_LeftPanel .mx_BottomLeftMenu_tooltip {
display: inline-block;
position: relative;
top: -25px;
left: 6px;
}