Merge pull request #448 from matrix-org/wmwragg/one-to-one-chat
Wmwragg/one to one chat
This commit is contained in:
commit
dce2dd36ec
6 changed files with 485 additions and 13 deletions
|
@ -45,6 +45,7 @@ module.exports.components['views.avatars.RoomAvatar'] = require('./components/vi
|
||||||
module.exports.components['views.create_room.CreateRoomButton'] = require('./components/views/create_room/CreateRoomButton');
|
module.exports.components['views.create_room.CreateRoomButton'] = require('./components/views/create_room/CreateRoomButton');
|
||||||
module.exports.components['views.create_room.Presets'] = require('./components/views/create_room/Presets');
|
module.exports.components['views.create_room.Presets'] = require('./components/views/create_room/Presets');
|
||||||
module.exports.components['views.create_room.RoomAlias'] = require('./components/views/create_room/RoomAlias');
|
module.exports.components['views.create_room.RoomAlias'] = require('./components/views/create_room/RoomAlias');
|
||||||
|
module.exports.components['views.dialogs.ChatInviteDialog'] = require('./components/views/dialogs/ChatInviteDialog');
|
||||||
module.exports.components['views.dialogs.DeactivateAccountDialog'] = require('./components/views/dialogs/DeactivateAccountDialog');
|
module.exports.components['views.dialogs.DeactivateAccountDialog'] = require('./components/views/dialogs/DeactivateAccountDialog');
|
||||||
module.exports.components['views.dialogs.ErrorDialog'] = require('./components/views/dialogs/ErrorDialog');
|
module.exports.components['views.dialogs.ErrorDialog'] = require('./components/views/dialogs/ErrorDialog');
|
||||||
module.exports.components['views.dialogs.LogoutPrompt'] = require('./components/views/dialogs/LogoutPrompt');
|
module.exports.components['views.dialogs.LogoutPrompt'] = require('./components/views/dialogs/LogoutPrompt');
|
||||||
|
@ -53,6 +54,7 @@ module.exports.components['views.dialogs.NeedToRegisterDialog'] = require('./com
|
||||||
module.exports.components['views.dialogs.QuestionDialog'] = require('./components/views/dialogs/QuestionDialog');
|
module.exports.components['views.dialogs.QuestionDialog'] = require('./components/views/dialogs/QuestionDialog');
|
||||||
module.exports.components['views.dialogs.SetDisplayNameDialog'] = require('./components/views/dialogs/SetDisplayNameDialog');
|
module.exports.components['views.dialogs.SetDisplayNameDialog'] = require('./components/views/dialogs/SetDisplayNameDialog');
|
||||||
module.exports.components['views.dialogs.TextInputDialog'] = require('./components/views/dialogs/TextInputDialog');
|
module.exports.components['views.dialogs.TextInputDialog'] = require('./components/views/dialogs/TextInputDialog');
|
||||||
|
module.exports.components['views.elements.AddressTile'] = require('./components/views/elements/AddressTile');
|
||||||
module.exports.components['views.elements.EditableText'] = require('./components/views/elements/EditableText');
|
module.exports.components['views.elements.EditableText'] = require('./components/views/elements/EditableText');
|
||||||
module.exports.components['views.elements.EditableTextContainer'] = require('./components/views/elements/EditableTextContainer');
|
module.exports.components['views.elements.EditableTextContainer'] = require('./components/views/elements/EditableTextContainer');
|
||||||
module.exports.components['views.elements.EmojiText'] = require('./components/views/elements/EmojiText');
|
module.exports.components['views.elements.EmojiText'] = require('./components/views/elements/EmojiText');
|
||||||
|
|
|
@ -370,6 +370,9 @@ module.exports = React.createClass({
|
||||||
this._setPage(this.PageTypes.RoomDirectory);
|
this._setPage(this.PageTypes.RoomDirectory);
|
||||||
this.notifyNewScreen('directory');
|
this.notifyNewScreen('directory');
|
||||||
break;
|
break;
|
||||||
|
case 'view_create_chat':
|
||||||
|
this._createChat();
|
||||||
|
break;
|
||||||
case 'notifier_enabled':
|
case 'notifier_enabled':
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
break;
|
break;
|
||||||
|
@ -506,6 +509,13 @@ module.exports = React.createClass({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_createChat: function() {
|
||||||
|
var ChatInviteDialog = sdk.getComponent("dialogs.ChatInviteDialog");
|
||||||
|
Modal.createDialog(ChatInviteDialog, {
|
||||||
|
title: "Start a one to one chat",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
// update scrollStateMap according to the current scroll state of the
|
// update scrollStateMap according to the current scroll state of the
|
||||||
// room view.
|
// room view.
|
||||||
_updateScrollMap: function() {
|
_updateScrollMap: function() {
|
||||||
|
|
371
src/components/views/dialogs/ChatInviteDialog.js
Normal file
371
src/components/views/dialogs/ChatInviteDialog.js
Normal file
|
@ -0,0 +1,371 @@
|
||||||
|
/*
|
||||||
|
Copyright 2015, 2016 OpenMarket 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var React = require("react");
|
||||||
|
var classNames = require('classnames');
|
||||||
|
var sdk = require("../../../index");
|
||||||
|
var Invite = require("../../../Invite");
|
||||||
|
var createRoom = require("../../../createRoom");
|
||||||
|
var MatrixClientPeg = require("../../../MatrixClientPeg");
|
||||||
|
var DMRoomMap = require('../../../utils/DMRoomMap');
|
||||||
|
var rate_limited_func = require("../../../ratelimitedfunc");
|
||||||
|
var dis = require("../../../dispatcher");
|
||||||
|
var Modal = require('../../../Modal');
|
||||||
|
|
||||||
|
const TRUNCATE_QUERY_LIST = 40;
|
||||||
|
|
||||||
|
module.exports = React.createClass({
|
||||||
|
displayName: "ChatInviteDialog",
|
||||||
|
propTypes: {
|
||||||
|
title: React.PropTypes.string,
|
||||||
|
description: React.PropTypes.oneOfType([
|
||||||
|
React.PropTypes.element,
|
||||||
|
React.PropTypes.string,
|
||||||
|
]),
|
||||||
|
value: React.PropTypes.string,
|
||||||
|
placeholder: React.PropTypes.string,
|
||||||
|
button: React.PropTypes.string,
|
||||||
|
focus: React.PropTypes.bool,
|
||||||
|
onFinished: React.PropTypes.func.isRequired
|
||||||
|
},
|
||||||
|
|
||||||
|
getDefaultProps: function() {
|
||||||
|
return {
|
||||||
|
title: "Start a chat",
|
||||||
|
description: "Who would you like to communicate with?",
|
||||||
|
value: "",
|
||||||
|
placeholder: "User ID, Name or email",
|
||||||
|
button: "Start Chat",
|
||||||
|
focus: true
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
getInitialState: function() {
|
||||||
|
return {
|
||||||
|
user: null,
|
||||||
|
queryList: [],
|
||||||
|
addressSelected: false,
|
||||||
|
selected: 0,
|
||||||
|
hover: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidMount: function() {
|
||||||
|
if (this.props.focus) {
|
||||||
|
// Set the cursor at the end of the text input
|
||||||
|
this.refs.textinput.value = this.props.value;
|
||||||
|
}
|
||||||
|
this._updateUserList();
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidUpdate: function() {
|
||||||
|
// As the user scrolls with the arrow keys keep the selected item
|
||||||
|
// at the top of the window.
|
||||||
|
if (this.scrollElement && !this.state.hover) {
|
||||||
|
var elementHeight = this.queryListElement.getBoundingClientRect().height;
|
||||||
|
this.scrollElement.scrollTop = (this.state.selected * elementHeight) - elementHeight;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onStartChat: function() {
|
||||||
|
var addr;
|
||||||
|
|
||||||
|
// Either an address tile was created, or text input is being used
|
||||||
|
if (this.state.user) {
|
||||||
|
addr = this.state.user.userId;
|
||||||
|
} else {
|
||||||
|
addr = this.refs.textinput.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the addr is a valid type
|
||||||
|
if (Invite.getAddressType(addr) === "mx") {
|
||||||
|
var room = this._getDirectMessageRoom(addr);
|
||||||
|
if (room) {
|
||||||
|
// A Direct Message room already exists for this user and you
|
||||||
|
// so go straight to that room
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'view_room',
|
||||||
|
room_id: room.roomId,
|
||||||
|
});
|
||||||
|
this.props.onFinished(true, addr);
|
||||||
|
} else {
|
||||||
|
this._startChat(addr);
|
||||||
|
}
|
||||||
|
} else if (Invite.getAddressType(addr) === "email") {
|
||||||
|
this._startChat(addr);
|
||||||
|
} else {
|
||||||
|
// Nothing to do, so focus back on the textinput
|
||||||
|
this.refs.textinput.focus();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onCancel: function() {
|
||||||
|
this.props.onFinished(false);
|
||||||
|
},
|
||||||
|
|
||||||
|
onKeyDown: function(e) {
|
||||||
|
if (e.keyCode === 27) { // escape
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
this.props.onFinished(false);
|
||||||
|
} else if (e.keyCode === 38) { // up arrow
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
if (this.state.selected > 0) {
|
||||||
|
this.setState({
|
||||||
|
selected: this.state.selected - 1,
|
||||||
|
hover : false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (e.keyCode === 40) { // down arrow
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
if (this.state.selected < this._maxSelected(this.state.queryList)) {
|
||||||
|
this.setState({
|
||||||
|
selected: this.state.selected + 1,
|
||||||
|
hover : false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (e.keyCode === 13) { // enter
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
if (this.state.queryList.length > 0) {
|
||||||
|
this.setState({
|
||||||
|
user: this.state.queryList[this.state.selected],
|
||||||
|
addressSelected: true,
|
||||||
|
queryList: [],
|
||||||
|
hover : false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onQueryChanged: function(ev) {
|
||||||
|
var query = ev.target.value;
|
||||||
|
var queryList = [];
|
||||||
|
|
||||||
|
// Only do search if there is something to search
|
||||||
|
if (query.length > 0) {
|
||||||
|
queryList = this._userList.filter((user) => {
|
||||||
|
return this._matches(query, user);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the selected item isn't outside the list bounds
|
||||||
|
var selected = this.state.selected;
|
||||||
|
var maxSelected = this._maxSelected(queryList);
|
||||||
|
if (selected > maxSelected) {
|
||||||
|
selected = maxSelected;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
queryList: queryList,
|
||||||
|
selected: selected,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onDismissed: function() {
|
||||||
|
this.setState({
|
||||||
|
user: null,
|
||||||
|
addressSelected: false,
|
||||||
|
selected: 0,
|
||||||
|
queryList: [],
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onClick: function(index) {
|
||||||
|
var self = this;
|
||||||
|
return function() {
|
||||||
|
self.setState({
|
||||||
|
user: self.state.queryList[index],
|
||||||
|
addressSelected: true,
|
||||||
|
queryList: [],
|
||||||
|
hover: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
onMouseEnter: function(index) {
|
||||||
|
var self = this;
|
||||||
|
return function() {
|
||||||
|
self.setState({
|
||||||
|
selected: index,
|
||||||
|
hover: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
onMouseLeave: function() {
|
||||||
|
this.setState({ hover : false });
|
||||||
|
},
|
||||||
|
|
||||||
|
createQueryListTiles: function() {
|
||||||
|
var self = this;
|
||||||
|
var TintableSvg = sdk.getComponent("elements.TintableSvg");
|
||||||
|
var AddressTile = sdk.getComponent("elements.AddressTile");
|
||||||
|
var maxSelected = this._maxSelected(this.state.queryList);
|
||||||
|
var queryList = [];
|
||||||
|
|
||||||
|
// Only create the query elements if there are queries
|
||||||
|
if (this.state.queryList.length > 0) {
|
||||||
|
for (var i = 0; i <= maxSelected; i++) {
|
||||||
|
var classes = classNames({
|
||||||
|
"mx_ChatInviteDialog_queryListElement": true,
|
||||||
|
"mx_ChatInviteDialog_selected": this.state.selected === i,
|
||||||
|
});
|
||||||
|
|
||||||
|
// NOTE: Defaulting to "vector" as the network, until the network backend stuff is done.
|
||||||
|
// Saving the queryListElement so we can use it to work out, in the componentDidUpdate
|
||||||
|
// method, how far to scroll when using the arrow keys
|
||||||
|
queryList.push(
|
||||||
|
<div className={classes} onClick={this.onClick(i)} onMouseEnter={this.onMouseEnter(i)} onMouseLeave={this.onMouseLeave} key={i} ref={(ref) => { this.queryListElement = ref; }} >
|
||||||
|
<AddressTile user={this.state.queryList[i]} justified={true} networkName="vector" networkUrl="img/search-icon-vector.svg" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return queryList;
|
||||||
|
},
|
||||||
|
|
||||||
|
_getDirectMessageRoom: function(addr) {
|
||||||
|
const dmRoomMap = new DMRoomMap(MatrixClientPeg.get());
|
||||||
|
var dmRooms = dmRoomMap.getDMRoomsForUserId(addr);
|
||||||
|
if (dmRooms.length > 0) {
|
||||||
|
// Cycle through all the DM rooms and find the first non forgotten or parted room
|
||||||
|
for (let i = 0; i < dmRooms.length; i++) {
|
||||||
|
let room = MatrixClientPeg.get().getRoom(dmRooms[i]);
|
||||||
|
if (room) {
|
||||||
|
return room;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
_startChat: function(addr) {
|
||||||
|
// Start the chat
|
||||||
|
createRoom().then(function(roomId) {
|
||||||
|
return Invite.inviteToRoom(roomId, addr);
|
||||||
|
})
|
||||||
|
.catch(function(err) {
|
||||||
|
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||||
|
Modal.createDialog(ErrorDialog, {
|
||||||
|
title: "Failure to invite user",
|
||||||
|
description: err.toString()
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
.done();
|
||||||
|
|
||||||
|
// Close - this will happen before the above, as that is async
|
||||||
|
this.props.onFinished(true, addr);
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateUserList: new rate_limited_func(function() {
|
||||||
|
// Get all the users
|
||||||
|
this._userList = MatrixClientPeg.get().getUsers();
|
||||||
|
}, 500),
|
||||||
|
|
||||||
|
_maxSelected: function(list) {
|
||||||
|
var listSize = list.length === 0 ? 0 : list.length - 1;
|
||||||
|
var maxSelected = listSize > (TRUNCATE_QUERY_LIST - 1) ? (TRUNCATE_QUERY_LIST - 1) : listSize
|
||||||
|
return maxSelected;
|
||||||
|
},
|
||||||
|
|
||||||
|
// This is the search algorithm for matching users
|
||||||
|
_matches: function(query, user) {
|
||||||
|
var name = user.displayName.toLowerCase();
|
||||||
|
var uid = user.userId.toLowerCase();
|
||||||
|
query = query.toLowerCase();
|
||||||
|
|
||||||
|
// direct prefix matches
|
||||||
|
if (name.indexOf(query) === 0 || uid.indexOf(query) === 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// strip @ on uid and try matching again
|
||||||
|
if (uid.length > 1 && uid[0] === "@" && uid.substring(1).indexOf(query) === 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// split spaces in name and try matching constituent parts
|
||||||
|
var parts = name.split(" ");
|
||||||
|
for (var i = 0; i < parts.length; i++) {
|
||||||
|
if (parts[i].indexOf(query) === 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
var TintableSvg = sdk.getComponent("elements.TintableSvg");
|
||||||
|
this.scrollElement = null;
|
||||||
|
|
||||||
|
var query;
|
||||||
|
if (this.state.addressSelected) {
|
||||||
|
var AddressTile = sdk.getComponent("elements.AddressTile");
|
||||||
|
query = (
|
||||||
|
<AddressTile user={this.state.user} canDismiss={true} onDismissed={this.onDismissed} />
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
query = (
|
||||||
|
<textarea rows="1"
|
||||||
|
id="textinput"
|
||||||
|
ref="textinput"
|
||||||
|
className="mx_ChatInviteDialog_input"
|
||||||
|
onChange={this.onQueryChanged}
|
||||||
|
placeholder={this.props.placeholder}
|
||||||
|
defaultValue={this.props.value}
|
||||||
|
autoFocus={this.props.focus}>
|
||||||
|
</textarea>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var queryList;
|
||||||
|
var queryListElements = this.createQueryListTiles();
|
||||||
|
if (queryListElements.length > 0) {
|
||||||
|
queryList = (
|
||||||
|
<div className="mx_ChatInviteDialog_queryList" ref={(ref) => {this.scrollElement = ref}}>
|
||||||
|
{ queryListElements }
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mx_ChatInviteDialog" onKeyDown={this.onKeyDown}>
|
||||||
|
<div className="mx_Dialog_title">
|
||||||
|
{this.props.title}
|
||||||
|
</div>
|
||||||
|
<div className="mx_ChatInviteDialog_cancel" onClick={this.onCancel} >
|
||||||
|
<TintableSvg src="img/icons-close-button.svg" width="35" height="35" />
|
||||||
|
</div>
|
||||||
|
<div className="mx_ChatInviteDialog_label">
|
||||||
|
<label htmlFor="textinput">{ this.props.description }</label>
|
||||||
|
</div>
|
||||||
|
<div className="mx_Dialog_content">
|
||||||
|
<div className="mx_ChatInviteDialog_inputContainer">{ query }</div>
|
||||||
|
{ queryList }
|
||||||
|
</div>
|
||||||
|
<div className="mx_Dialog_buttons">
|
||||||
|
<button className="mx_Dialog_primary" onClick={this.onStartChat}>
|
||||||
|
{this.props.button}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
93
src/components/views/elements/AddressTile.js
Normal file
93
src/components/views/elements/AddressTile.js
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
/*
|
||||||
|
Copyright 2015, 2016 OpenMarket 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var React = require('react');
|
||||||
|
var classNames = require('classnames');
|
||||||
|
var sdk = require("../../../index");
|
||||||
|
var Avatar = require('../../../Avatar');
|
||||||
|
|
||||||
|
module.exports = React.createClass({
|
||||||
|
displayName: 'AddressTile',
|
||||||
|
|
||||||
|
propTypes: {
|
||||||
|
user: React.PropTypes.object.isRequired,
|
||||||
|
canDismiss: React.PropTypes.bool,
|
||||||
|
onDismissed: React.PropTypes.func,
|
||||||
|
justified: React.PropTypes.bool,
|
||||||
|
networkName: React.PropTypes.string,
|
||||||
|
networkUrl: React.PropTypes.string,
|
||||||
|
},
|
||||||
|
|
||||||
|
getDefaultProps: function() {
|
||||||
|
return {
|
||||||
|
canDismiss: false,
|
||||||
|
onDismissed: function() {}, // NOP
|
||||||
|
justified: false,
|
||||||
|
networkName: "",
|
||||||
|
networkUrl: "",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
var BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
|
||||||
|
var TintableSvg = sdk.getComponent("elements.TintableSvg");
|
||||||
|
var userId = this.props.user.userId;
|
||||||
|
var name = this.props.user.displayName || userId;
|
||||||
|
var imgUrl = Avatar.avatarUrlForUser(this.props.user, 25, 25, "crop");
|
||||||
|
|
||||||
|
var network;
|
||||||
|
if (this.props.networkUrl !== "") {
|
||||||
|
network = (
|
||||||
|
<div className="mx_AddressTile_network">
|
||||||
|
<BaseAvatar width={25} height={25} name={this.props.networkName} title="vector" url={this.props.networkUrl} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var dismiss;
|
||||||
|
if (this.props.canDismiss) {
|
||||||
|
dismiss = (
|
||||||
|
<div className="mx_AddressTile_dismiss" onClick={this.props.onDismissed} >
|
||||||
|
<TintableSvg src="img/icon-address-delete.svg" width="9" height="9" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var nameClasses = classNames({
|
||||||
|
"mx_AddressTile_name": true,
|
||||||
|
"mx_AddressTile_justified": this.props.justified,
|
||||||
|
});
|
||||||
|
|
||||||
|
var idClasses = classNames({
|
||||||
|
"mx_AddressTile_id": true,
|
||||||
|
"mx_AddressTile_justified": this.props.justified,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mx_AddressTile">
|
||||||
|
{ network }
|
||||||
|
<div className="mx_AddressTile_avatar">
|
||||||
|
<BaseAvatar width={25} height={25} name={name} title={name} url={imgUrl} />
|
||||||
|
</div>
|
||||||
|
<div className={nameClasses}>{ name }</div>
|
||||||
|
<div className={idClasses}>{ userId }</div>
|
||||||
|
{ dismiss }
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
|
@ -79,8 +79,6 @@ module.exports = React.createClass({
|
||||||
switch (payload.action) {
|
switch (payload.action) {
|
||||||
case 'view_tooltip':
|
case 'view_tooltip':
|
||||||
this.tooltip = payload.tooltip;
|
this.tooltip = payload.tooltip;
|
||||||
this._repositionTooltip();
|
|
||||||
if (this.tooltip) this.tooltip.style.display = 'block';
|
|
||||||
break;
|
break;
|
||||||
case 'call_state':
|
case 'call_state':
|
||||||
var call = CallHandler.getCall(payload.room_id);
|
var call = CallHandler.getCall(payload.room_id);
|
||||||
|
@ -315,17 +313,15 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
_whenScrolling: function(e) {
|
_whenScrolling: function(e) {
|
||||||
this._repositionTooltip(e);
|
this._hideTooltip(e);
|
||||||
this._repositionIncomingCallBox(e, false);
|
this._repositionIncomingCallBox(e, false);
|
||||||
this._updateStickyHeaders(false);
|
this._updateStickyHeaders(false);
|
||||||
},
|
},
|
||||||
|
|
||||||
_repositionTooltip: function(e) {
|
_hideTooltip: function(e) {
|
||||||
// We access the parent of the parent, as the tooltip is inside a container
|
// Hide tooltip when scrolling, as we'll no longer be over the one we were on
|
||||||
// Needs refactoring into a better multipurpose tooltip
|
if (this.tooltip && this.tooltip.style.display !== "none") {
|
||||||
if (this.tooltip && this.tooltip.parentElement && this.tooltip.parentElement.parentElement) {
|
this.tooltip.style.display = "none";
|
||||||
var scroll = ReactDOM.findDOMNode(this);
|
|
||||||
this.tooltip.style.top = (3 + scroll.parentElement.offsetTop + this.tooltip.parentElement.parentElement.offsetTop - this._getScrollNode().scrollTop) + "px";
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -374,7 +370,7 @@ module.exports = React.createClass({
|
||||||
var scrollArea = this._getScrollNode();
|
var scrollArea = this._getScrollNode();
|
||||||
// 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;
|
var scrollAreaOffset = scrollArea.getBoundingClientRect().top + window.pageYOffset;
|
||||||
// Use the offset of the top of the componet from the window
|
// Use the offset of the top of the componet 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 scrollAreaHeight = ReactDOM.findDOMNode(this).getBoundingClientRect().height;
|
var scrollAreaHeight = ReactDOM.findDOMNode(this).getBoundingClientRect().height;
|
||||||
|
|
|
@ -17,6 +17,7 @@ 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 classNames = require('classnames');
|
||||||
var dis = require("../../../dispatcher");
|
var dis = require("../../../dispatcher");
|
||||||
var MatrixClientPeg = require('../../../MatrixClientPeg');
|
var MatrixClientPeg = require('../../../MatrixClientPeg');
|
||||||
|
@ -245,10 +246,9 @@ module.exports = React.createClass({
|
||||||
} else {
|
} else {
|
||||||
label = <EmojiText element="div" title={ name } className={ nameClasses }>{name}</EmojiText>;
|
label = <EmojiText element="div" title={ name } className={ nameClasses }>{name}</EmojiText>;
|
||||||
}
|
}
|
||||||
}
|
} else if (this.state.hover) {
|
||||||
else if (this.state.hover) {
|
|
||||||
var RoomTooltip = sdk.getComponent("rooms.RoomTooltip");
|
var RoomTooltip = sdk.getComponent("rooms.RoomTooltip");
|
||||||
label = <RoomTooltip room={this.props.room}/>;
|
tooltip = <RoomTooltip className="mx_RoomTile_tooltip" room={this.props.room} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
var incomingCallBox;
|
var incomingCallBox;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue