Merge pull request #3286 from matrix-org/travis/immutable-dms/basic

Reuse DMs whenever possible instead of asking to reuse them
pull/21833/head
Travis Ralston 2019-08-08 07:26:17 -06:00 committed by GitHub
commit 07d6fa7fdb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 39 additions and 269 deletions

View File

@ -54,7 +54,7 @@ export function showStartChatInviteDialog() {
placeholder: _t("Email, name or Matrix ID"),
validAddressTypes,
button: _t("Start Chat"),
onFinished: _onStartChatFinished,
onFinished: _onStartDmFinished,
});
}
@ -96,7 +96,8 @@ export function isValid3pidInvite(event) {
return true;
}
function _onStartChatFinished(shouldInvite, addrs) {
// TODO: Immutable DMs replaces this
function _onStartDmFinished(shouldInvite, addrs) {
if (!shouldInvite) return;
const addrTexts = addrs.map((addr) => addr.address);
@ -104,32 +105,19 @@ function _onStartChatFinished(shouldInvite, addrs) {
if (_isDmChat(addrTexts)) {
const rooms = _getDirectMessageRooms(addrTexts[0]);
if (rooms.length > 0) {
// A Direct Message room already exists for this user, so select a
// room from a list that is similar to the one in MemberInfo panel
const ChatCreateOrReuseDialog = sdk.getComponent("views.dialogs.ChatCreateOrReuseDialog");
const close = Modal.createTrackedDialog('Create or Reuse', '', ChatCreateOrReuseDialog, {
userId: addrTexts[0],
onNewDMClick: () => {
dis.dispatch({
action: 'start_chat',
user_id: addrTexts[0],
});
close(true);
},
onExistingRoomSelected: (roomId) => {
dis.dispatch({
action: 'view_room',
room_id: roomId,
});
close(true);
},
}).close;
// A Direct Message room already exists for this user, so reuse it
dis.dispatch({
action: 'view_room',
room_id: rooms[0],
should_peek: false,
joining: false,
});
} else {
// Start a new DM chat
createRoom({dmUserId: addrTexts[0]}).catch((err) => {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Failed to invite user', '', ErrorDialog, {
title: _t("Failed to invite user"),
Modal.createTrackedDialog('Failed to start chat', '', ErrorDialog, {
title: _t("Failed to start chat"),
description: ((err && err.message) ? err.message : _t("Operation failed")),
});
});
@ -138,8 +126,8 @@ function _onStartChatFinished(shouldInvite, addrs) {
// Start a new DM chat
createRoom({dmUserId: addrTexts[0]}).catch((err) => {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Failed to invite user', '', ErrorDialog, {
title: _t("Failed to invite user"),
Modal.createTrackedDialog('Failed to start chat', '', ErrorDialog, {
title: _t("Failed to start chat"),
description: ((err && err.message) ? err.message : _t("Operation failed")),
});
});
@ -181,6 +169,7 @@ function _onRoomInviteFinished(roomId, shouldInvite, addrs) {
});
}
// TODO: Immutable DMs replaces this
function _isDmChat(addrTexts) {
if (addrTexts.length === 1 && getAddressType(addrTexts[0]) === 'mx-user-id') {
return true;

View File

@ -343,7 +343,6 @@ const FeaturedUser = React.createClass({
dis.dispatch({
action: 'view_start_chat_or_reuse',
user_id: this.props.summaryInfo.user_id,
go_home_on_cancel: false,
});
},

View File

@ -584,7 +584,7 @@ export default React.createClass({
this._setMxId(payload);
break;
case 'view_start_chat_or_reuse':
this._chatCreateOrReuse(payload.user_id, payload.go_home_on_cancel);
this._chatCreateOrReuse(payload.user_id);
break;
case 'view_create_chat':
showStartChatInviteDialog();
@ -945,12 +945,7 @@ export default React.createClass({
});
},
_chatCreateOrReuse: function(userId, goHomeOnCancel) {
if (goHomeOnCancel === undefined) goHomeOnCancel = true;
const ChatCreateOrReuseDialog = sdk.getComponent(
'views.dialogs.ChatCreateOrReuseDialog',
);
_chatCreateOrReuse: function(userId) {
// Use a deferred action to reshow the dialog once the user has registered
if (MatrixClientPeg.get().isGuest()) {
// No point in making 2 DMs with welcome bot. This assumes view_set_mxid will
@ -975,30 +970,23 @@ export default React.createClass({
return;
}
const close = Modal.createTrackedDialog('Chat create or reuse', '', ChatCreateOrReuseDialog, {
userId: userId,
onFinished: (success) => {
if (!success && goHomeOnCancel) {
// Dialog cancelled, default to home
dis.dispatch({ action: 'view_home_page' });
}
},
onNewDMClick: () => {
dis.dispatch({
action: 'start_chat',
user_id: userId,
});
// Close the dialog, indicate success (calls onFinished(true))
close(true);
},
onExistingRoomSelected: (roomId) => {
dis.dispatch({
action: 'view_room',
room_id: roomId,
});
close(true);
},
}).close;
// TODO: Immutable DMs replaces this
const client = MatrixClientPeg.get();
const dmRoomMap = new DMRoomMap(client);
const dmRooms = dmRoomMap.getDMRoomsForUserId(userId);
if (dmRooms.length > 0) {
dis.dispatch({
action: 'view_room',
room_id: dmRooms[0],
});
} else {
dis.dispatch({
action: 'start_chat',
user_id: userId,
});
}
},
_leaveRoomWarnings: function(roomId) {

View File

@ -1,203 +0,0 @@
/*
Copyright 2017 Vector Creations Ltd
Copyright 2018 New Vector 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 PropTypes from 'prop-types';
import sdk from '../../../index';
import { _t } from '../../../languageHandler';
import MatrixClientPeg from '../../../MatrixClientPeg';
import DMRoomMap from '../../../utils/DMRoomMap';
import AccessibleButton from '../elements/AccessibleButton';
import Unread from '../../../Unread';
import classNames from 'classnames';
export default class ChatCreateOrReuseDialog extends React.Component {
constructor(props) {
super(props);
this.onFinished = this.onFinished.bind(this);
this.onRoomTileClick = this.onRoomTileClick.bind(this);
this.state = {
tiles: [],
profile: {
displayName: null,
avatarUrl: null,
},
profileError: null,
};
}
componentWillMount() {
const client = MatrixClientPeg.get();
const dmRoomMap = new DMRoomMap(client);
const dmRooms = dmRoomMap.getDMRoomsForUserId(this.props.userId);
const RoomTile = sdk.getComponent("rooms.RoomTile");
const tiles = [];
for (const roomId of dmRooms) {
const room = client.getRoom(roomId);
if (room && room.getMyMembership() === "join") {
const member = room.getMember(this.props.userId);
if (!member || member.membership !== "join") {
continue;
}
const isInvite = room.getMyMembership() === "invite";
const highlight = room.getUnreadNotificationCount('highlight') > 0 || isInvite;
tiles.push(
<RoomTile key={room.roomId} room={room}
transparent={true}
collapsed={false}
selected={false}
unread={Unread.doesRoomHaveUnreadMessages(room)}
highlight={highlight}
isInvite={isInvite}
onClick={this.onRoomTileClick}
/>,
);
}
}
this.setState({
tiles: tiles,
});
if (tiles.length === 0) {
this.setState({
busyProfile: true,
});
MatrixClientPeg.get().getProfileInfo(this.props.userId).done((resp) => {
const profile = {
displayName: resp.displayname,
avatarUrl: null,
};
if (resp.avatar_url) {
profile.avatarUrl = MatrixClientPeg.get().mxcUrlToHttp(
resp.avatar_url, 48, 48, "crop",
);
}
this.setState({
busyProfile: false,
profile: profile,
});
}, (err) => {
console.error(
'Unable to get profile for user ' + this.props.userId + ':',
err,
);
this.setState({
busyProfile: false,
profileError: err,
});
});
}
}
onRoomTileClick(roomId) {
this.props.onExistingRoomSelected(roomId);
}
onFinished() {
this.props.onFinished(false);
}
render() {
let title = '';
let content = null;
if (this.state.tiles.length > 0) {
// Show the existing rooms with a "+" to add a new dm
title = _t('Create a new chat or reuse an existing one');
const labelClasses = classNames({
mx_MemberInfo_createRoom_label: true,
mx_RoomTile_name: true,
});
const startNewChat = <AccessibleButton
className="mx_MemberInfo_createRoom"
onClick={this.props.onNewDMClick}
>
<div className="mx_RoomTile_avatar">
<img src={require("../../../../res/img/create-big.svg")} width="26" height="26" />
</div>
<div className={labelClasses}><i>{ _t("Start new chat") }</i></div>
</AccessibleButton>;
content = <div className="mx_Dialog_content" id='mx_Dialog_content'>
{ _t('You already have existing direct chats with this user:') }
<div className="mx_ChatCreateOrReuseDialog_tiles">
{ this.state.tiles }
{ startNewChat }
</div>
</div>;
} else {
// Show the avatar, name and a button to confirm that a new chat is requested
const BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
const Spinner = sdk.getComponent('elements.Spinner');
title = _t('Start chatting');
let profile = null;
if (this.state.busyProfile) {
profile = <Spinner />;
} else if (this.state.profileError) {
profile = <div className="error" role="alert">
Unable to load profile information for { this.props.userId }
</div>;
} else {
profile = <div className="mx_ChatCreateOrReuseDialog_profile">
<BaseAvatar
name={this.state.profile.displayName || this.props.userId}
url={this.state.profile.avatarUrl}
width={48} height={48}
/>
<div className="mx_ChatCreateOrReuseDialog_profile_name">
{ this.state.profile.displayName || this.props.userId }
</div>
</div>;
}
content = <div>
<div className="mx_Dialog_content" id='mx_Dialog_content'>
<p>
{ _t('Click on the button below to start chatting!') }
</p>
{ profile }
</div>
<DialogButtons primaryButton={_t('Start Chatting')}
onPrimaryButtonClick={this.props.onNewDMClick} focus={true} />
</div>;
}
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
return (
<BaseDialog className='mx_ChatCreateOrReuseDialog'
onFinished={this.onFinished}
title={title}
contentId='mx_Dialog_content'
>
{ content }
</BaseDialog>
);
}
}
ChatCreateOrReuseDialog.propTypes = {
userId: PropTypes.string.isRequired,
// Called when clicking outside of the dialog
onFinished: PropTypes.func.isRequired,
onNewDMClick: PropTypes.func.isRequired,
onExistingRoomSelected: PropTypes.func.isRequired,
};

View File

@ -789,6 +789,7 @@ module.exports = React.createClass({
let spinner;
if (this.props.member.userId !== this.context.matrixClient.credentials.userId) {
// TODO: Immutable DMs replaces a lot of this
const dmRoomMap = new DMRoomMap(this.context.matrixClient);
// dmRooms will not include dmRooms that we have been invited into but did not join.
// Because DMRoomMap runs off account_data[m.direct] which is only set on join of dm room.
@ -830,7 +831,7 @@ module.exports = React.createClass({
mx_MemberInfo_createRoom_label: true,
mx_RoomTile_name: true,
});
const startNewChat = <AccessibleButton
let startNewChat = <AccessibleButton
className="mx_MemberInfo_createRoom"
onClick={this.onNewDMClick}
>
@ -840,6 +841,8 @@ module.exports = React.createClass({
<div className={labelClasses}><i>{ _t("Start a chat") }</i></div>
</AccessibleButton>;
if (tiles.length > 0) startNewChat = null; // Don't offer a button for a new chat if we have one.
startChat = <div>
<h3>{ _t("Direct chats") }</h3>
{ tiles }

View File

@ -114,7 +114,7 @@
"Invite new room members": "Invite new room members",
"Who would you like to add to this room?": "Who would you like to add to this room?",
"Send Invites": "Send Invites",
"Failed to invite user": "Failed to invite user",
"Failed to start chat": "Failed to start chat",
"Operation failed": "Operation failed",
"Failed to invite": "Failed to invite",
"Failed to invite users to the room:": "Failed to invite users to the room:",
@ -1124,12 +1124,6 @@
"Unable to load commit detail: %(msg)s": "Unable to load commit detail: %(msg)s",
"Unavailable": "Unavailable",
"Changelog": "Changelog",
"Create a new chat or reuse an existing one": "Create a new chat or reuse an existing one",
"Start new chat": "Start new chat",
"You already have existing direct chats with this user:": "You already have existing direct chats with this user:",
"Start chatting": "Start chatting",
"Click on the button below to start chatting!": "Click on the button below to start chatting!",
"Start Chatting": "Start Chatting",
"You cannot delete this message. (%(code)s)": "You cannot delete this message. (%(code)s)",
"Removing…": "Removing…",
"Confirm Removal": "Confirm Removal",