Merge branch 'develop' into matthew/notif-panel
This commit is contained in:
commit
e75148e799
50 changed files with 1182 additions and 92 deletions
|
@ -17,48 +17,88 @@ limitations under the License.
|
|||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactDOM = require('react-dom');
|
||||
var sdk = require('matrix-react-sdk')
|
||||
var dis = require('matrix-react-sdk/lib/dispatcher');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'BottomLeftMenu',
|
||||
|
||||
propTypes: {
|
||||
collapsed: React.PropTypes.bool.isRequired,
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return({
|
||||
roomsHover : false,
|
||||
peopleHover : false,
|
||||
settingsHover : false,
|
||||
});
|
||||
},
|
||||
|
||||
// Room events
|
||||
onRoomsClick: function() {
|
||||
dis.dispatch({action: 'view_create_room'});
|
||||
},
|
||||
|
||||
onRoomsMouseEnter: function() {
|
||||
this.setState({ roomsHover: true });
|
||||
},
|
||||
|
||||
onRoomsMouseLeave: function() {
|
||||
this.setState({ roomsHover: 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'});
|
||||
},
|
||||
|
||||
onRoomDirectoryClick: function() {
|
||||
dis.dispatch({action: 'view_room_directory'});
|
||||
onSettingsMouseEnter: function() {
|
||||
this.setState({ settingsHover: true });
|
||||
},
|
||||
|
||||
onCreateRoomClick: function() {
|
||||
dis.dispatch({action: 'view_create_room'});
|
||||
onSettingsMouseLeave: function() {
|
||||
this.setState({ settingsHover: false });
|
||||
},
|
||||
|
||||
getLabel: function(name) {
|
||||
if (!this.props.collapsed) {
|
||||
return <div className="mx_RoomTile_name">{name}</div>
|
||||
}
|
||||
else if (this.state.hover) {
|
||||
// Get the label/tooltip to show
|
||||
getLabel: function(label, show) {
|
||||
if (show) {
|
||||
var RoomTooltip = sdk.getComponent("rooms.RoomTooltip");
|
||||
return <RoomTooltip name={name}/>;
|
||||
return <RoomTooltip className="mx_BottomLeftMenu_tooltip" label={label} />;
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var BottomLeftMenuTile = sdk.getComponent('rooms.BottomLeftMenuTile');
|
||||
var TintableSvg = sdk.getComponent('elements.TintableSvg');
|
||||
return (
|
||||
<div className="mx_BottomLeftMenu">
|
||||
<div className="mx_BottomLeftMenu_options">
|
||||
<div className="mx_BottomLeftMenu_createRoom" title="Start chat" onClick={ this.onCreateRoomClick }>
|
||||
<TintableSvg src="img/icons-create-room.svg" width="25" height="25"/>
|
||||
<div 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("Rooms", this.state.roomsHover) }
|
||||
</div>
|
||||
<div className="mx_BottomLeftMenu_directory" title="Room directory" onClick={ this.onRoomDirectoryClick }>
|
||||
<TintableSvg src="img/icons-directory.svg" width="25" height="25"/>
|
||||
<div className="mx_BottomLeftMenu_people" onClick={ this.onPeopleClick } onMouseEnter={ this.onPeopleMouseEnter } onMouseLeave={ this.onPeopleMouseLeave } >
|
||||
<TintableSvg src="img/icons-people.svg" width="25" height="25" />
|
||||
{ this.getLabel("New direct message", this.state.peopleHover) }
|
||||
</div>
|
||||
<div className="mx_BottomLeftMenu_settings" title="Settings" onClick={ this.onSettingsClick }>
|
||||
<TintableSvg src="img/icons-settings.svg" width="25" height="25"/>
|
||||
<div 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) }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -418,8 +418,18 @@ var RoomSubList = React.createClass({
|
|||
badge = <div className={badgeClasses}>{subListNotifCount > 99 ? "99+" : 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 + "]";
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx_RoomSubList_labelContainer" ref="header">
|
||||
<div className="mx_RoomSubList_labelContainer" title={ title } ref="header">
|
||||
<div onClick={ this.onClick } className="mx_RoomSubList_label">
|
||||
{ this.props.collapsed ? '' : this.props.label }
|
||||
<div className="mx_RoomSubList_roomCount">{roomCount}</div>
|
||||
|
|
|
@ -84,6 +84,14 @@ module.exports = React.createClass({
|
|||
if (this.props.onFinished) this.props.onFinished();
|
||||
},
|
||||
|
||||
onQuoteClick: function () {
|
||||
console.log(this.props.mxEvent);
|
||||
dis.dispatch({
|
||||
action: 'quote',
|
||||
event: this.props.mxEvent,
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var eventStatus = this.props.mxEvent.status;
|
||||
var resendButton;
|
||||
|
@ -141,6 +149,12 @@ module.exports = React.createClass({
|
|||
</div>
|
||||
);
|
||||
|
||||
const quoteButton = (
|
||||
<div className="mx_MessageContextMenu_field" onClick={this.onQuoteClick}>
|
||||
Quote
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{resendButton}
|
||||
|
@ -149,6 +163,7 @@ module.exports = React.createClass({
|
|||
{viewSourceButton}
|
||||
{unhidePreviewButton}
|
||||
{permalinkButton}
|
||||
{quoteButton}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@ var React = require('react');
|
|||
var classNames = require('classnames');
|
||||
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
||||
var dis = require('matrix-react-sdk/lib/dispatcher');
|
||||
var DMRoomMap = require('matrix-react-sdk/lib/utils/DMRoomMap');
|
||||
var Rooms = require('matrix-react-sdk/lib/Rooms');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'RoomTagContextMenu',
|
||||
|
@ -32,9 +34,11 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
getInitialState: function() {
|
||||
const dmRoomMap = new DMRoomMap(MatrixClientPeg.get());
|
||||
return {
|
||||
isFavourite: this.props.room.tags.hasOwnProperty("m.favourite"),
|
||||
isLowPriority: this.props.room.tags.hasOwnProperty("m.lowpriority"),
|
||||
isDirectMessage: Boolean(dmRoomMap.getUserIdForRoomId(this.props.room.roomId)),
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -113,6 +117,43 @@ module.exports = React.createClass({
|
|||
}
|
||||
},
|
||||
|
||||
_onClickDM: function() {
|
||||
const newIsDirectMessage = !this.state.isDirectMessage;
|
||||
this.setState({
|
||||
isDirectMessage: newIsDirectMessage,
|
||||
});
|
||||
|
||||
if (MatrixClientPeg.get().isGuest()) return;
|
||||
|
||||
let newTarget;
|
||||
if (newIsDirectMessage) {
|
||||
const guessedTarget = Rooms.guessDMRoomTarget(
|
||||
this.props.room,
|
||||
this.props.room.getMember(MatrixClientPeg.get().credentials.userId),
|
||||
);
|
||||
newTarget = guessedTarget.userId;
|
||||
} else {
|
||||
newTarget = null;
|
||||
}
|
||||
|
||||
// give some time for the user to see the icon change first, since
|
||||
// this will hide the context menu once it completes
|
||||
q.delay(500).done(() => {
|
||||
return Rooms.setDMRoom(this.props.room.roomId, newTarget).finally(() => {
|
||||
// Close the context menu
|
||||
if (this.props.onFinished) {
|
||||
this.props.onFinished();
|
||||
};
|
||||
}, (err) => {
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Failed to set Direct Message status of room",
|
||||
description: err.toString()
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
_onClickLeave: function() {
|
||||
// Leave room
|
||||
dis.dispatch({
|
||||
|
@ -146,27 +187,33 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
render: function() {
|
||||
var myUserId = MatrixClientPeg.get().credentials.userId;
|
||||
var myMember = this.props.room.getMember(myUserId);
|
||||
const myUserId = MatrixClientPeg.get().credentials.userId;
|
||||
const myMember = this.props.room.getMember(myUserId);
|
||||
|
||||
var favouriteClasses = classNames({
|
||||
const favouriteClasses = classNames({
|
||||
'mx_RoomTagContextMenu_field': true,
|
||||
'mx_RoomTagContextMenu_fieldSet': this.state.isFavourite,
|
||||
'mx_RoomTagContextMenu_fieldDisabled': false,
|
||||
});
|
||||
|
||||
var lowPriorityClasses = classNames({
|
||||
const lowPriorityClasses = classNames({
|
||||
'mx_RoomTagContextMenu_field': true,
|
||||
'mx_RoomTagContextMenu_fieldSet': this.state.isLowPriority,
|
||||
'mx_RoomTagContextMenu_fieldDisabled': false,
|
||||
});
|
||||
|
||||
var leaveClasses = classNames({
|
||||
const leaveClasses = classNames({
|
||||
'mx_RoomTagContextMenu_field': true,
|
||||
'mx_RoomTagContextMenu_fieldSet': false,
|
||||
'mx_RoomTagContextMenu_fieldDisabled': false,
|
||||
});
|
||||
|
||||
const dmClasses = classNames({
|
||||
'mx_RoomTagContextMenu_field': true,
|
||||
'mx_RoomTagContextMenu_fieldSet': this.state.isDirectMessage,
|
||||
'mx_RoomTagContextMenu_fieldDisabled': false,
|
||||
});
|
||||
|
||||
if (myMember && myMember.membership === "leave") {
|
||||
return (
|
||||
<div>
|
||||
|
@ -190,6 +237,11 @@ module.exports = React.createClass({
|
|||
<img className="mx_RoomTagContextMenu_icon_set" src="img/icon_context_low_on.svg" width="15" height="15" />
|
||||
Low Priority
|
||||
</div>
|
||||
<div className={ dmClasses } onClick={this._onClickDM} >
|
||||
<img className="mx_RoomTagContextMenu_icon" src="img/icon_context_person.svg" width="15" height="15" />
|
||||
<img className="mx_RoomTagContextMenu_icon_set" src="img/icon_context_person_on.svg" width="15" height="15" />
|
||||
Direct Message
|
||||
</div>
|
||||
<hr className="mx_RoomTagContextMenu_separator" />
|
||||
<div className={ leaveClasses } onClick={(myMember && myMember.membership === "join") ? this._onClickLeave : null} >
|
||||
<img className="mx_RoomTagContextMenu_icon" src="img/icon_context_delete.svg" width="15" height="15" />
|
||||
|
|
|
@ -18,42 +18,79 @@ limitations under the License.
|
|||
|
||||
var React = require('react');
|
||||
var ReactDOM = require('react-dom');
|
||||
|
||||
var dis = require('matrix-react-sdk/lib/dispatcher');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'RoomTooltip',
|
||||
|
||||
componentDidMount: function() {
|
||||
var tooltip = ReactDOM.findDOMNode(this);
|
||||
if (!this.props.bottom) {
|
||||
// tell the roomlist about us so it can position us
|
||||
dis.dispatch({
|
||||
action: 'view_tooltip',
|
||||
tooltip: tooltip,
|
||||
});
|
||||
}
|
||||
else {
|
||||
tooltip.style.top = (70 + tooltip.parentElement.getBoundingClientRect().top) + "px";
|
||||
tooltip.style.display = "block";
|
||||
}
|
||||
propTypes: {
|
||||
// Alllow the tooltip to be styled by the parent element
|
||||
className: React.PropTypes.string.isRequired,
|
||||
// The tooltip is derived from either the room name or a label
|
||||
room: React.PropTypes.object,
|
||||
label: React.PropTypes.string,
|
||||
},
|
||||
|
||||
// Create a wrapper for the tooltip outside the parent and attach it to the body element
|
||||
componentDidMount: function() {
|
||||
this.tooltipContainer = document.createElement("div");
|
||||
this.tooltipContainer.className = "mx_RoomTileTooltip_wrapper";
|
||||
document.body.appendChild(this.tooltipContainer);
|
||||
|
||||
this._renderTooltip();
|
||||
},
|
||||
|
||||
componentDidUpdate: function() {
|
||||
this._renderTooltip();
|
||||
},
|
||||
|
||||
// Remove the wrapper element, as the tooltip has finished using it
|
||||
componentWillUnmount: function() {
|
||||
if (!this.props.bottom) {
|
||||
dis.dispatch({
|
||||
action: 'view_tooltip',
|
||||
tooltip: null,
|
||||
});
|
||||
}
|
||||
dis.dispatch({
|
||||
action: 'view_tooltip',
|
||||
tooltip: null,
|
||||
parent: null,
|
||||
});
|
||||
|
||||
ReactDOM.unmountComponentAtNode(this.tooltipContainer);
|
||||
document.body.removeChild(this.tooltipContainer);
|
||||
},
|
||||
|
||||
_renderTooltip: function() {
|
||||
var label = this.props.room ? this.props.room.name : this.props.label;
|
||||
|
||||
// Add the parent's position to the tooltips, so it's correctly
|
||||
// positioned, also taking into account any window zoom
|
||||
// NOTE: The additional 6 pixels for the left position, is to take account of the
|
||||
// tooltips chevron
|
||||
var parent = ReactDOM.findDOMNode(this);
|
||||
var style = {};
|
||||
style.top = parent.getBoundingClientRect().top + window.pageYOffset;
|
||||
style.left = 6 + parent.getBoundingClientRect().right + window.pageXOffset;
|
||||
style.display = "block";
|
||||
|
||||
var tooltip = (
|
||||
<div className="mx_RoomTooltip" style={style} >
|
||||
<div className="mx_RoomTooltip_chevron"></div>
|
||||
{ label }
|
||||
</div>
|
||||
);
|
||||
|
||||
// Render the tooltip manually, as we wish it not to be rendered within the parent
|
||||
this.tooltip = ReactDOM.render(tooltip, this.tooltipContainer);
|
||||
|
||||
// Tell the roomlist about us so it can manipulate us if it wishes
|
||||
dis.dispatch({
|
||||
action: 'view_tooltip',
|
||||
tooltip: this.tooltip,
|
||||
parent: parent,
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var label = this.props.room ? this.props.room.name : this.props.label;
|
||||
// Render a placeholder
|
||||
return (
|
||||
<div className="mx_RoomTooltip">
|
||||
<img className="mx_RoomTooltip_chevron" src="img/chevron-left.png" width="9" height="16"/>
|
||||
{ label }
|
||||
<div className={ this.props.className } >
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue