diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index ff864379fa..5791ffe49a 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -378,6 +378,11 @@ module.exports = React.createClass({ }); this.notifyNewScreen('forgot_password'); break; + case 'start_chat': + createRoom({ + dmUserId: payload.user_id, + }); + break; case 'leave_room': this._leaveRoom(payload.room_id); break; @@ -474,6 +479,9 @@ module.exports = React.createClass({ case 'view_set_mxid': this._setMxId(); break; + case 'view_start_chat_or_reuse': + this._chatCreateOrReuse(payload.user_id); + break; case 'view_create_chat': this._createChat(); break; @@ -707,6 +715,51 @@ module.exports = React.createClass({ }); }, + _chatCreateOrReuse: function(userId) { + const ChatCreateOrReuseDialog = sdk.getComponent( + 'views.dialogs.ChatCreateOrReuseDialog', + ); + // Use a deferred action to reshow the dialog once the user has registered + if (MatrixClientPeg.get().isGuest()) { + dis.dispatch({ + action: 'do_after_sync_prepared', + deferred_action: { + action: 'view_start_chat_or_reuse', + user_id: userId, + }, + }); + dis.dispatch({ + action: 'view_set_mxid', + }); + return; + } + + const close = Modal.createDialog(ChatCreateOrReuseDialog, { + userId: userId, + onFinished: (success) => { + if (!success) { + // 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; + }, + _invite: function(roomId) { const ChatInviteDialog = sdk.getComponent("dialogs.ChatInviteDialog"); Modal.createDialog(ChatInviteDialog, { @@ -1043,6 +1096,12 @@ module.exports = React.createClass({ } } else if (screen.indexOf('user/') == 0) { const userId = screen.substring(5); + + if (params.action === 'chat') { + this._chatCreateOrReuse(userId); + return; + } + this.setState({ viewUserId: userId }); this._setPage(PageTypes.UserView); this.notifyNewScreen('user/' + userId); diff --git a/src/components/views/avatars/BaseAvatar.js b/src/components/views/avatars/BaseAvatar.js index 65730be40b..a4443430f4 100644 --- a/src/components/views/avatars/BaseAvatar.js +++ b/src/components/views/avatars/BaseAvatar.js @@ -32,6 +32,7 @@ module.exports = React.createClass({ urls: React.PropTypes.array, // [highest_priority, ... , lowest_priority] width: React.PropTypes.number, height: React.PropTypes.number, + // XXX resizeMethod not actually used. resizeMethod: React.PropTypes.string, defaultToInitialLetter: React.PropTypes.bool // true to add default url }, diff --git a/src/components/views/dialogs/ChatCreateOrReuseDialog.js b/src/components/views/dialogs/ChatCreateOrReuseDialog.js index f563af6691..fdb4e994ae 100644 --- a/src/components/views/dialogs/ChatCreateOrReuseDialog.js +++ b/src/components/views/dialogs/ChatCreateOrReuseDialog.js @@ -18,34 +18,31 @@ import React from 'react'; import sdk from '../../../index'; import dis from '../../../dispatcher'; import MatrixClientPeg from '../../../MatrixClientPeg'; +import { _t } from '../../../languageHandler'; import DMRoomMap from '../../../utils/DMRoomMap'; import AccessibleButton from '../elements/AccessibleButton'; import Unread from '../../../Unread'; import classNames from 'classnames'; import createRoom from '../../../createRoom'; +import { RoomMember } from "matrix-js-sdk"; export default class ChatCreateOrReuseDialog extends React.Component { constructor(props) { super(props); - this.onNewDMClick = this.onNewDMClick.bind(this); this.onRoomTileClick = this.onRoomTileClick.bind(this); + + this.state = { + tiles: [], + profile: { + displayName: null, + avatarUrl: null, + }, + profileError: null, + }; } - onNewDMClick() { - createRoom({dmUserId: this.props.userId}); - this.props.onFinished(true); - } - - onRoomTileClick(roomId) { - dis.dispatch({ - action: 'view_room', - room_id: roomId, - }); - this.props.onFinished(true); - } - - render() { + componentWillMount() { const client = MatrixClientPeg.get(); const dmRoomMap = new DMRoomMap(client); @@ -70,40 +67,123 @@ export default class ChatCreateOrReuseDialog extends React.Component { highlight={highlight} isInvite={me.membership == "invite"} onClick={this.onRoomTileClick} - /> + />, ); } } - const labelClasses = classNames({ - mx_MemberInfo_createRoom_label: true, - mx_RoomTile_name: true, + this.setState({ + tiles: tiles, }); - const startNewChat = -
- -
-
{_("Start new chat")}
-
; + + 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); + } + + 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 = +
+ +
+
{ _t("Start new chat") }
+
; + content =
+ { _t('You already have existing direct chats with this user:') } +
+ { this.state.tiles } + { startNewChat } +
+
; + } else { + // Show the avatar, name and a button to confirm that a new chat is requested + const BaseAvatar = sdk.getComponent('avatars.BaseAvatar'); + const Spinner = sdk.getComponent('elements.Spinner'); + title = _t('Start chatting'); + + let profile = null; + if (this.state.busyProfile) { + profile = ; + } else if (this.state.profileError) { + profile =
+ Unable to load profile information for { this.props.userId } +
; + } else { + profile =
+ +
+ {this.state.profile.displayName || this.props.userId} +
+
; + } + content =
+
+

+ { _t('Click on the button below to start chatting!') } +

+ { profile } +
+
+ +
+
; + } const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); return ( { - this.props.onFinished(false) - }} - title='Create a new chat or reuse an existing one' + onFinished={ this.props.onFinished.bind(false) } + title={title} > -
- You already have existing direct chats with this user: -
- {tiles} - {startNewChat} -
-
+ { content }
); } @@ -111,5 +191,8 @@ export default class ChatCreateOrReuseDialog extends React.Component { ChatCreateOrReuseDialog.propTyps = { userId: React.PropTypes.string.isRequired, + // Called when clicking outside of the dialog onFinished: React.PropTypes.func.isRequired, + onNewDMClick: React.PropTypes.func.isRequired, + onExistingRoomSelected: React.PropTypes.func.isRequired, }; diff --git a/src/components/views/dialogs/ChatInviteDialog.js b/src/components/views/dialogs/ChatInviteDialog.js index b50a782073..f475635212 100644 --- a/src/components/views/dialogs/ChatInviteDialog.js +++ b/src/components/views/dialogs/ChatInviteDialog.js @@ -95,16 +95,25 @@ module.exports = React.createClass({ // 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" + "views.dialogs.ChatCreateOrReuseDialog", ); Modal.createDialog(ChatCreateOrReuseDialog, { userId: userId, onFinished: (success) => { - if (success) { - this.props.onFinished(true, inviteList[0]); - } - // else show this ChatInviteDialog again - } + this.props.onFinished(success); + }, + onNewDMClick: () => { + dis.dispatch({ + action: 'start_chat', + user_id: userId, + }); + }, + onExistingRoomSelected: (roomId) => { + dis.dispatch({ + action: 'view_room', + user_id: roomId, + }); + }, }); } else { this._startChat(inviteList); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 11727223e3..c8d9614961 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -771,5 +771,11 @@ "Idle": "Idle", "Offline": "Offline", "disabled": "disabled", - "enabled": "enabled" + "enabled": "enabled", + "Start chatting": "Start chatting", + "Start Chatting": "Start Chatting", + "Click on the button below to start chatting!": "Click on the button below to start chatting!", + "Create a new chat or reuse an existing one": "Create a new chat or reuse an existing one", + "You already have existing direct chats with this user:": "You already have existing direct chats with this user:", + "Start new chat": "Start new chat" } diff --git a/src/stores/LifecycleStore.js b/src/stores/LifecycleStore.js index d38138b3ef..b06c03b778 100644 --- a/src/stores/LifecycleStore.js +++ b/src/stores/LifecycleStore.js @@ -41,6 +41,7 @@ class LifecycleStore extends Store { __onDispatch(payload) { switch (payload.action) { case 'do_after_sync_prepared': + console.info('Will do after sync', payload.deferred_action); this._setState({ deferred_action: payload.deferred_action, }); @@ -49,6 +50,7 @@ class LifecycleStore extends Store { if (payload.state !== 'PREPARED') { break; } + console.info('Doing', payload.deferred_action); if (!this._state.deferred_action) break; const deferredAction = Object.assign({}, this._state.deferred_action); this._setState({