List common rooms in MemberInfo
This commit is contained in:
parent
c9475e2ae5
commit
aa0f15c46e
3 changed files with 88 additions and 113 deletions
|
@ -26,12 +26,15 @@ limitations under the License.
|
||||||
* 'isTargetMod': boolean
|
* 'isTargetMod': boolean
|
||||||
*/
|
*/
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
|
var classNames = require('classnames');
|
||||||
var MatrixClientPeg = require("../../../MatrixClientPeg");
|
var MatrixClientPeg = require("../../../MatrixClientPeg");
|
||||||
var dis = require("../../../dispatcher");
|
var dis = require("../../../dispatcher");
|
||||||
var Modal = require("../../../Modal");
|
var Modal = require("../../../Modal");
|
||||||
var sdk = require('../../../index');
|
var sdk = require('../../../index');
|
||||||
var UserSettingsStore = require('../../../UserSettingsStore');
|
var UserSettingsStore = require('../../../UserSettingsStore');
|
||||||
var createRoom = require('../../../createRoom');
|
var createRoom = require('../../../createRoom');
|
||||||
|
var DMRoomMap = require('../../../utils/DMRoomMap');
|
||||||
|
var Unread = require('../../../Unread');
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'MemberInfo',
|
displayName: 'MemberInfo',
|
||||||
|
@ -60,7 +63,6 @@ module.exports = React.createClass({
|
||||||
updating: 0,
|
updating: 0,
|
||||||
devicesLoading: true,
|
devicesLoading: true,
|
||||||
devices: null,
|
devices: null,
|
||||||
existingOneToOneRoomId: null,
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -71,10 +73,6 @@ module.exports = React.createClass({
|
||||||
// feature is enabled in the user settings
|
// feature is enabled in the user settings
|
||||||
this._enableDevices = MatrixClientPeg.get().isCryptoEnabled() &&
|
this._enableDevices = MatrixClientPeg.get().isCryptoEnabled() &&
|
||||||
UserSettingsStore.isFeatureEnabled("e2e_encryption");
|
UserSettingsStore.isFeatureEnabled("e2e_encryption");
|
||||||
|
|
||||||
this.setState({
|
|
||||||
existingOneToOneRoomId: this.getExistingOneToOneRoomId()
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount: function() {
|
||||||
|
@ -98,59 +96,6 @@ module.exports = React.createClass({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
getExistingOneToOneRoomId: function() {
|
|
||||||
const rooms = MatrixClientPeg.get().getRooms();
|
|
||||||
const userIds = [
|
|
||||||
this.props.member.userId,
|
|
||||||
MatrixClientPeg.get().credentials.userId
|
|
||||||
];
|
|
||||||
let existingRoomId = null;
|
|
||||||
let invitedRoomId = null;
|
|
||||||
|
|
||||||
// roomId can be null here because of a hack in MatrixChat.onUserClick where we
|
|
||||||
// abuse this to view users rather than room members.
|
|
||||||
let currentMembers;
|
|
||||||
if (this.props.member.roomId) {
|
|
||||||
const currentRoom = MatrixClientPeg.get().getRoom(this.props.member.roomId);
|
|
||||||
currentMembers = currentRoom.getJoinedMembers();
|
|
||||||
}
|
|
||||||
|
|
||||||
// reuse the first private 1:1 we find
|
|
||||||
existingRoomId = null;
|
|
||||||
|
|
||||||
for (let i = 0; i < rooms.length; i++) {
|
|
||||||
// don't try to reuse public 1:1 rooms
|
|
||||||
const join_rules = rooms[i].currentState.getStateEvents("m.room.join_rules", '');
|
|
||||||
if (join_rules && join_rules.getContent().join_rule === 'public') continue;
|
|
||||||
|
|
||||||
const members = rooms[i].getJoinedMembers();
|
|
||||||
if (members.length === 2 &&
|
|
||||||
userIds.indexOf(members[0].userId) !== -1 &&
|
|
||||||
userIds.indexOf(members[1].userId) !== -1)
|
|
||||||
{
|
|
||||||
existingRoomId = rooms[i].roomId;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const invited = rooms[i].getMembersWithMembership('invite');
|
|
||||||
if (members.length === 1 &&
|
|
||||||
invited.length === 1 &&
|
|
||||||
userIds.indexOf(members[0].userId) !== -1 &&
|
|
||||||
userIds.indexOf(invited[0].userId) !== -1 &&
|
|
||||||
invitedRoomId === null)
|
|
||||||
{
|
|
||||||
invitedRoomId = rooms[i].roomId;
|
|
||||||
// keep looking: we'll use this one if there's nothing better
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (existingRoomId === null) {
|
|
||||||
existingRoomId = invitedRoomId;
|
|
||||||
}
|
|
||||||
|
|
||||||
return existingRoomId;
|
|
||||||
},
|
|
||||||
|
|
||||||
onDeviceVerificationChanged: function(userId, device) {
|
onDeviceVerificationChanged: function(userId, device) {
|
||||||
if (!this._enableDevices) {
|
if (!this._enableDevices) {
|
||||||
return;
|
return;
|
||||||
|
@ -416,23 +361,7 @@ module.exports = React.createClass({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onChatClick: function() {
|
onNewDMClick: function() {
|
||||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
|
||||||
|
|
||||||
// TODO: keep existingOneToOneRoomId updated if we see any room member changes anywhere
|
|
||||||
|
|
||||||
const useExistingOneToOneRoom = this.state.existingOneToOneRoomId && (this.state.existingOneToOneRoomId !== this.props.member.roomId);
|
|
||||||
|
|
||||||
// check if there are any existing rooms with just us and them (1:1)
|
|
||||||
// If so, just view that room. If not, create a private room with them.
|
|
||||||
if (useExistingOneToOneRoom) {
|
|
||||||
dis.dispatch({
|
|
||||||
action: 'view_room',
|
|
||||||
room_id: this.state.existingOneToOneRoomId,
|
|
||||||
});
|
|
||||||
this.props.onFinished();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.setState({ updating: this.state.updating + 1 });
|
this.setState({ updating: this.state.updating + 1 });
|
||||||
createRoom({
|
createRoom({
|
||||||
createOpts: {
|
createOpts: {
|
||||||
|
@ -442,7 +371,6 @@ module.exports = React.createClass({
|
||||||
this.props.onFinished();
|
this.props.onFinished();
|
||||||
this.setState({ updating: this.state.updating - 1 });
|
this.setState({ updating: this.state.updating - 1 });
|
||||||
}).done();
|
}).done();
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
onLeaveClick: function() {
|
onLeaveClick: function() {
|
||||||
|
@ -583,24 +511,50 @@ module.exports = React.createClass({
|
||||||
render: function() {
|
render: function() {
|
||||||
var startChat, kickButton, banButton, muteButton, giveModButton, spinner;
|
var startChat, kickButton, banButton, muteButton, giveModButton, spinner;
|
||||||
if (this.props.member.userId !== MatrixClientPeg.get().credentials.userId) {
|
if (this.props.member.userId !== MatrixClientPeg.get().credentials.userId) {
|
||||||
// FIXME: we're referring to a vector component from react-sdk
|
const dmRoomMap = new DMRoomMap(MatrixClientPeg.get());
|
||||||
var BottomLeftMenuTile = sdk.getComponent('rooms.BottomLeftMenuTile');
|
const dmRooms = dmRoomMap.getDMRoomsForUserId(this.props.member.userId);
|
||||||
|
|
||||||
var label;
|
const RoomTile = sdk.getComponent("rooms.RoomTile");
|
||||||
if (this.state.existingOneToOneRoomId) {
|
|
||||||
if (this.state.existingOneToOneRoomId == this.props.member.roomId) {
|
const tiles = [];
|
||||||
label = "Start new direct chat";
|
for (const roomId of dmRooms) {
|
||||||
|
const room = MatrixClientPeg.get().getRoom(roomId);
|
||||||
|
if (room) {
|
||||||
|
const me = room.getMember(MatrixClientPeg.get().credentials.userId);
|
||||||
|
const highlight = (
|
||||||
|
room.getUnreadNotificationCount('highlight') > 0 ||
|
||||||
|
me.membership == "invite"
|
||||||
|
);
|
||||||
|
tiles.push(
|
||||||
|
<RoomTile key={room.roomId} room={room}
|
||||||
|
collapsed={false}
|
||||||
|
selected={false}
|
||||||
|
unread={Unread.doesRoomHaveUnreadMessages(room)}
|
||||||
|
highlight={highlight}
|
||||||
|
isInvite={me.membership == "invite"}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
label = "Go to direct chat";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
label = "Start direct chat";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
startChat = <BottomLeftMenuTile collapsed={ false } img="img/create-big.svg"
|
const labelClasses = classNames({
|
||||||
label={ label } onClick={ this.onChatClick }/>
|
mx_MemberInfo_createRoom_label: true,
|
||||||
|
mx_RoomTile_name: true,
|
||||||
|
});
|
||||||
|
const startNewChat = <div
|
||||||
|
className="mx_MemberInfo_createRoom"
|
||||||
|
onClick={this.onNewDMClick}
|
||||||
|
>
|
||||||
|
<div className="mx_RoomTile_avatar">
|
||||||
|
<img src="img/create-big.svg" width="26" height="26" />
|
||||||
|
</div>
|
||||||
|
<div className={labelClasses}><i>Start new direct chat</i></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
startChat = <div>
|
||||||
|
{tiles}
|
||||||
|
{startNewChat}
|
||||||
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.updating) {
|
if (this.state.updating) {
|
||||||
|
|
|
@ -28,10 +28,9 @@ module.exports = React.createClass({
|
||||||
displayName: 'RoomTile',
|
displayName: 'RoomTile',
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
// TODO: We should *optionally* support DND stuff and ideally be impl agnostic about it
|
connectDragSource: React.PropTypes.func,
|
||||||
connectDragSource: React.PropTypes.func.isRequired,
|
connectDropTarget: React.PropTypes.func,
|
||||||
connectDropTarget: React.PropTypes.func.isRequired,
|
isDragging: React.PropTypes.bool,
|
||||||
isDragging: React.PropTypes.bool.isRequired,
|
|
||||||
|
|
||||||
room: React.PropTypes.object.isRequired,
|
room: React.PropTypes.object.isRequired,
|
||||||
collapsed: React.PropTypes.bool.isRequired,
|
collapsed: React.PropTypes.bool.isRequired,
|
||||||
|
@ -39,11 +38,15 @@ module.exports = React.createClass({
|
||||||
unread: React.PropTypes.bool.isRequired,
|
unread: React.PropTypes.bool.isRequired,
|
||||||
highlight: React.PropTypes.bool.isRequired,
|
highlight: React.PropTypes.bool.isRequired,
|
||||||
isInvite: React.PropTypes.bool.isRequired,
|
isInvite: React.PropTypes.bool.isRequired,
|
||||||
roomSubList: React.PropTypes.object.isRequired,
|
|
||||||
refreshSubList: React.PropTypes.func.isRequired,
|
|
||||||
incomingCall: React.PropTypes.object,
|
incomingCall: React.PropTypes.object,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getDefaultProps: function() {
|
||||||
|
return {
|
||||||
|
isDragging: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
return({
|
return({
|
||||||
hover : false,
|
hover : false,
|
||||||
|
@ -265,7 +268,7 @@ module.exports = React.createClass({
|
||||||
var connectDragSource = this.props.connectDragSource;
|
var connectDragSource = this.props.connectDragSource;
|
||||||
var connectDropTarget = this.props.connectDropTarget;
|
var connectDropTarget = this.props.connectDropTarget;
|
||||||
|
|
||||||
return connectDragSource(connectDropTarget(
|
let ret = (
|
||||||
<div className={classes} onClick={this.onClick} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
|
<div className={classes} onClick={this.onClick} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
|
||||||
<div className={avatarClasses}>
|
<div className={avatarClasses}>
|
||||||
<div className="mx_RoomTile_avatar_menu" onClick={this.onAvatarClicked}>
|
<div className="mx_RoomTile_avatar_menu" onClick={this.onAvatarClicked}>
|
||||||
|
@ -281,6 +284,11 @@ module.exports = React.createClass({
|
||||||
{ incomingCallBox }
|
{ incomingCallBox }
|
||||||
{ tooltip }
|
{ tooltip }
|
||||||
</div>
|
</div>
|
||||||
));
|
);
|
||||||
|
|
||||||
|
if (connectDropTarget) ret = connectDropTarget(ret);
|
||||||
|
if (connectDragSource) ret = connectDragSource(ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -21,18 +21,13 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
export default class DMRoomMap {
|
export default class DMRoomMap {
|
||||||
constructor(matrixClient) {
|
constructor(matrixClient) {
|
||||||
|
this.roomToUser = null;
|
||||||
|
|
||||||
const mDirectEvent = matrixClient.getAccountData('m.direct');
|
const mDirectEvent = matrixClient.getAccountData('m.direct');
|
||||||
if (!mDirectEvent) {
|
if (!mDirectEvent) {
|
||||||
this.userToRooms = {};
|
this.userToRooms = {};
|
||||||
this.roomToUser = {};
|
|
||||||
} else {
|
} else {
|
||||||
this.userToRooms = mDirectEvent.getContent();
|
this.userToRooms = mDirectEvent.getContent();
|
||||||
this.roomToUser = {};
|
|
||||||
for (const user of Object.keys(this.userToRooms)) {
|
|
||||||
for (const roomId of this.userToRooms[user]) {
|
|
||||||
this.roomToUser[roomId] = user;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,6 +36,24 @@ export default class DMRoomMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
getUserIdForRoomId(roomId) {
|
getUserIdForRoomId(roomId) {
|
||||||
|
if (this.roomToUser == null) {
|
||||||
|
// we lazily populate roomToUser so you can use
|
||||||
|
// this class just to call getDMRoomsForUserId
|
||||||
|
// which doesn't do very much, but is a fairly
|
||||||
|
// convenient wrapper and there's no point
|
||||||
|
// iterating through the map if getUserIdForRoomId()
|
||||||
|
// is never called.
|
||||||
|
this._populateRoomToUser();
|
||||||
|
}
|
||||||
return this.roomToUser[roomId];
|
return this.roomToUser[roomId];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_populateRoomToUser() {
|
||||||
|
this.roomToUser = {};
|
||||||
|
for (const user of Object.keys(this.userToRooms)) {
|
||||||
|
for (const roomId of this.userToRooms[user]) {
|
||||||
|
this.roomToUser[roomId] = user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue