From 447aa1e5a0fd9e3e4862ff25354f1a83728b63df Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 14 Aug 2017 17:38:59 +0100 Subject: [PATCH 1/9] Refactor ChatInviteDialog to be UserPickerDialog Now it's just a means of choosing users and all the actual inviting functionality is moved out to Invite.js. This will allow us to reuse it for inviting to groups. Adds the ability to restrict what types of addresses may be chosen, although this isn;t used yet, it will be necessary for groups because groups don't support 3pid invites. --- .../views/dialogs/{ChatInviteDialog.js => UserPickerDialog.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/components/views/dialogs/{ChatInviteDialog.js => UserPickerDialog.js} (100%) diff --git a/src/components/views/dialogs/ChatInviteDialog.js b/src/components/views/dialogs/UserPickerDialog.js similarity index 100% rename from src/components/views/dialogs/ChatInviteDialog.js rename to src/components/views/dialogs/UserPickerDialog.js From 1b66e88b6ef1affbeaf5c32d347b64be1dd08be7 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 14 Aug 2017 17:43:00 +0100 Subject: [PATCH 2/9] ChatInviteDialog -> UserPickerDialog pt 2 The other changes I forgot to add --- src/Invite.js | 129 ++++++++++ src/components/structures/MatrixChat.js | 23 +- .../views/dialogs/UserPickerDialog.js | 221 +++--------------- .../views/elements/AddressSelector.js | 2 +- src/components/views/elements/AddressTile.js | 22 +- 5 files changed, 164 insertions(+), 233 deletions(-) diff --git a/src/Invite.js b/src/Invite.js index 0e8aca2cb5..d825dac6f5 100644 --- a/src/Invite.js +++ b/src/Invite.js @@ -1,5 +1,6 @@ /* Copyright 2016 OpenMarket Ltd +Copyright 2017 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. @@ -16,11 +17,35 @@ limitations under the License. import MatrixClientPeg from './MatrixClientPeg'; import MultiInviter from './utils/MultiInviter'; +import Modal from "./Modal"; +import createRoom from './createRoom'; +import sdk from "./"; +import { _t } from './languageHandler'; const emailRegex = /^\S+@\S+\.\S+$/; const mxidRegex = /^@\S+:\S+$/ +export const addressTypes = [ + 'mx', 'email', +]; + +// React PropType definition for an object describing +// an address that can be invited to a room (which +// could be a third party identifier or a matrix ID) +// along with some additional information about the +// address / target. +export const InviteAddressType = React.PropTypes.shape({ + addressType: React.PropTypes.oneOf(addressTypes).isRequired, + address: React.PropTypes.string.isRequired, + displayName: React.PropTypes.string, + avatarMxc: React.PropTypes.string, + // true if the address is known to be a valid address (eg. is a real + // user we've seen) or false otherwise (eg. is just an address the + // user has entered) + isKnown: React.PropTypes.bool, +}); + export function getAddressType(inputText) { const isEmailAddress = emailRegex.test(inputText); const isMatrixId = mxidRegex.test(inputText); @@ -61,3 +86,107 @@ export function inviteMultipleToRoom(roomId, addrs) { return inviter.invite(addrs); } +export function showStartChatInviteDialog() { + const UserPickerDialog = sdk.getComponent("dialogs.UserPickerDialog"); + Modal.createTrackedDialog('Start a chat', '', UserPickerDialog, { + title: _t('Start a chat'), + description: _t("Who would you like to communicate with?"), + placeholder: _t("Email, name or matrix ID"), + button: _t("Start Chat"), + onFinished: _onStartChatFinished, + }); +} + +export function showRoomInviteDialog(roomId) { + const UserPickerDialog = sdk.getComponent("dialogs.UserPickerDialog"); + Modal.createTrackedDialog('Chat Invite', '', UserPickerDialog, { + title: _t('Invite new room members'), + description: _t('Who would you like to add to this room?'), + button: _t('Send Invites'), + placeholder: _t("Email, name or matrix ID"), + onFinished: (shouldInvite, addrs) => { + _onRoomInviteFinished(roomId, shouldInvite, addrs); + }, + }); +} + +function _onStartChatFinished(shouldInvite, addrs) { + if (!shouldInvite) return; + + const addrTexts = addrs.map(addr => addr.address); + + if (_isDmChat(addrTexts)) { + // Start a new DM chat + createRoom({dmUserId: addrTexts[0]}).catch((err) => { + console.error(err.stack); + const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + Modal.createTrackedDialog('Failed to invite user', '', ErrorDialog, { + title: _t("Failed to invite user"), + description: ((err && err.message) ? err.message : _t("Operation failed")), + }); + }); + } else { + // Start multi user chat + let room; + createRoom().then((roomId) => { + room = MatrixClientPeg.get().getRoom(roomId); + return inviteMultipleToRoom(roomId, addrTexts); + }).then((addrs) => { + return _showAnyInviteErrors(addrs, room); + }).catch((err) => { + console.error(err.stack); + const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + Modal.createTrackedDialog('Failed to invite', '', ErrorDialog, { + title: _t("Failed to invite"), + description: ((err && err.message) ? err.message : _t("Operation failed")), + }); + }); + } +} + +function _onRoomInviteFinished(roomId, shouldInvite, addrs) { + if (!shouldInvite) return; + + const addrTexts = addrs.map(addr => addr.address); + + // Invite new users to a room + inviteMultipleToRoom(roomId, addrTexts).then((addrs) => { + const room = MatrixClientPeg.get().getRoom(roomId); + return _showAnyInviteErrors(addrs, room); + }).catch((err) => { + console.error(err.stack); + const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + Modal.createTrackedDialog('Failed to invite', '', ErrorDialog, { + title: _t("Failed to invite"), + description: ((err && err.message) ? err.message : _t("Operation failed")), + }); + }); +} + +function _isDmChat(addrTexts) { + if (addrTexts.length === 1 && getAddressType(addrTexts[0])) { + return true; + } else { + return false; + } +} + +function _showAnyInviteErrors(addrs, room) { + // Show user any errors + let errorList = []; + for (let addr of Object.keys(addrs)) { + if (addrs[addr] === "error") { + errorList.push(addr); + } + } + + if (errorList.length > 0) { + const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + Modal.createTrackedDialog('Failed to invite the following users to the room', '', ErrorDialog, { + title: _t("Failed to invite the following users to the %(roomName)s room:", {roomName: room.name}), + description: errorList.join(", "), + }); + } + return addrs; +} + diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 6fdec80f38..fcd5ac2bd0 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -1,6 +1,7 @@ /* Copyright 2015, 2016 OpenMarket Ltd Copyright 2017 Vector Creations Ltd +Copyright 2017 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. @@ -31,6 +32,7 @@ import dis from "../../dispatcher"; import Modal from "../../Modal"; import Tinter from "../../Tinter"; import sdk from '../../index'; +import { showStartChatInviteDialog, showRoomInviteDialog } from '../../Invite'; import * as Rooms from '../../Rooms'; import linkifyMatrix from "../../linkify-matrix"; import * as Lifecycle from '../../Lifecycle'; @@ -512,7 +514,7 @@ module.exports = React.createClass({ this._createChat(); break; case 'view_invite': - this._invite(payload.roomId); + showRoomInviteDialog(payload.roomId); break; case 'notifier_enabled': this.forceUpdate(); @@ -766,13 +768,7 @@ module.exports = React.createClass({ dis.dispatch({action: 'view_set_mxid'}); return; } - const ChatInviteDialog = sdk.getComponent("dialogs.ChatInviteDialog"); - Modal.createTrackedDialog('Start a chat', '', ChatInviteDialog, { - title: _t('Start a chat'), - description: _t("Who would you like to communicate with?"), - placeholder: _t("Email, name or matrix ID"), - button: _t("Start Chat"), - }); + showStartChatInviteDialog(); }, _createRoom: function() { @@ -857,17 +853,6 @@ module.exports = React.createClass({ }).close; }, - _invite: function(roomId) { - const ChatInviteDialog = sdk.getComponent("dialogs.ChatInviteDialog"); - Modal.createTrackedDialog('Chat Invite', '', ChatInviteDialog, { - title: _t('Invite new room members'), - description: _t('Who would you like to add to this room?'), - button: _t('Send Invites'), - placeholder: _t("Email, name or matrix ID"), - roomId: roomId, - }); - }, - _leaveRoom: function(roomId) { const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); diff --git a/src/components/views/dialogs/UserPickerDialog.js b/src/components/views/dialogs/UserPickerDialog.js index 728860edec..5627a814c0 100644 --- a/src/components/views/dialogs/UserPickerDialog.js +++ b/src/components/views/dialogs/UserPickerDialog.js @@ -1,5 +1,6 @@ /* Copyright 2015, 2016 OpenMarket Ltd +Copyright 2017 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. @@ -15,9 +16,10 @@ limitations under the License. */ import React from 'react'; +import PropTypes from 'prop-types'; import { _t } from '../../../languageHandler'; import sdk from '../../../index'; -import { getAddressType, inviteMultipleToRoom } from '../../../Invite'; +import { getAddressType } from '../../../Invite'; import createRoom from '../../../createRoom'; import MatrixClientPeg from '../../../MatrixClientPeg'; import DMRoomMap from '../../../utils/DMRoomMap'; @@ -25,30 +27,34 @@ import Modal from '../../../Modal'; import AccessibleButton from '../elements/AccessibleButton'; import Promise from 'bluebird'; import dis from '../../../dispatcher'; +import { addressTypes, InviteAddressType } from '../../../Invite.js'; const TRUNCATE_QUERY_LIST = 40; const QUERY_USER_DIRECTORY_DEBOUNCE_MS = 200; module.exports = React.createClass({ - displayName: "ChatInviteDialog", + displayName: "UserPickerDialog", + propTypes: { - title: React.PropTypes.string.isRequired, - description: React.PropTypes.oneOfType([ - React.PropTypes.element, - React.PropTypes.string, + title: PropTypes.string.isRequired, + description: PropTypes.oneOfType([ + PropTypes.element, + PropTypes.string, ]), - value: React.PropTypes.string, - placeholder: React.PropTypes.string, - roomId: React.PropTypes.string, - button: React.PropTypes.string, - focus: React.PropTypes.bool, - onFinished: React.PropTypes.func.isRequired, + value: PropTypes.string, + placeholder: PropTypes.string, + roomId: PropTypes.string, + button: PropTypes.string, + focus: PropTypes.bool, + validAddressTypes: PropTypes.arrayOf(PropTypes.oneOfType(addressTypes)), + onFinished: PropTypes.func.isRequired, }, getDefaultProps: function() { return { value: "", focus: true, + validAddressTypes: addressTypes, }; }, @@ -56,7 +62,7 @@ module.exports = React.createClass({ return { error: false, - // List of AddressTile.InviteAddressType objects representing + // List of InviteAddressType objects representing // the list of addresses we're going to invite inviteList: [], @@ -90,50 +96,7 @@ module.exports = React.createClass({ inviteList = this._addInputToList(); if (inviteList === null) return; } - - const addrTexts = inviteList.map(addr => addr.address); - if (inviteList.length > 0) { - if (this._isDmChat(addrTexts)) { - const userId = inviteList[0].address; - // Direct Message chat - const rooms = this._getDirectMessageRooms(userId); - 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: userId, - onFinished: (success) => { - this.props.onFinished(success); - }, - onNewDMClick: () => { - dis.dispatch({ - action: 'start_chat', - user_id: userId, - }); - close(true); - }, - onExistingRoomSelected: (roomId) => { - dis.dispatch({ - action: 'view_room', - room_id: roomId, - }); - close(true); - }, - }).close; - } else { - this._startChat(inviteList); - } - } else { - // Multi invite chat - this._startChat(inviteList); - } - } else { - // No addresses supplied - this.setState({ error: true }); - } + this.props.onFinished(true, inviteList); }, onCancel: function() { @@ -201,11 +164,10 @@ module.exports = React.createClass({ }, onDismissed: function(index) { - var self = this; return () => { - var inviteList = self.state.inviteList.slice(); + const inviteList = this.state.inviteList.slice(); inviteList.splice(index, 1); - self.setState({ + this.setState({ inviteList: inviteList, queryList: [], query: "", @@ -215,14 +177,13 @@ module.exports = React.createClass({ }, onClick: function(index) { - var self = this; - return function() { - self.onSelected(index); + return () => { + this.onSelected(index); }; }, onSelected: function(index) { - var inviteList = this.state.inviteList.slice(); + const inviteList = this.state.inviteList.slice(); inviteList.push(this.state.queryList[index]); this.setState({ inviteList: inviteList, @@ -311,7 +272,7 @@ module.exports = React.createClass({ // This is important, otherwise there's no way to invite // a perfectly valid address if there are close matches. const addrType = getAddressType(query); - if (addrType !== null) { + if (this.props.validAddressTypes.indexOf(addrType) !== -1) { queryList.unshift({ addressType: addrType, address: query, @@ -330,132 +291,6 @@ module.exports = React.createClass({ }); }, - _getDirectMessageRooms: function(addr) { - const dmRoomMap = new DMRoomMap(MatrixClientPeg.get()); - const dmRooms = dmRoomMap.getDMRoomsForUserId(addr); - const rooms = []; - dmRooms.forEach(dmRoom => { - let room = MatrixClientPeg.get().getRoom(dmRoom); - if (room) { - const me = room.getMember(MatrixClientPeg.get().credentials.userId); - if (me.membership == 'join') { - rooms.push(room); - } - } - }); - return rooms; - }, - - _startChat: function(addrs) { - if (MatrixClientPeg.get().isGuest()) { - dis.dispatch({action: 'view_set_mxid'}); - return; - } - - const addrTexts = addrs.map((addr) => { - return addr.address; - }); - - if (this.props.roomId) { - // Invite new user to a room - var self = this; - inviteMultipleToRoom(this.props.roomId, addrTexts) - .then(function(addrs) { - var room = MatrixClientPeg.get().getRoom(self.props.roomId); - return self._showAnyInviteErrors(addrs, room); - }) - .catch(function(err) { - console.error(err.stack); - var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); - Modal.createTrackedDialog('Failed to invite', '', ErrorDialog, { - title: _t("Failed to invite"), - description: ((err && err.message) ? err.message : _t("Operation failed")), - }); - return null; - }) - .done(); - } else if (this._isDmChat(addrTexts)) { - // Start the DM chat - createRoom({dmUserId: addrTexts[0]}) - .catch(function(err) { - console.error(err.stack); - var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); - Modal.createTrackedDialog('Failed to invite user', '', ErrorDialog, { - title: _t("Failed to invite user"), - description: ((err && err.message) ? err.message : _t("Operation failed")), - }); - return null; - }) - .done(); - } else { - // Start multi user chat - var self = this; - var room; - createRoom().then(function(roomId) { - room = MatrixClientPeg.get().getRoom(roomId); - return inviteMultipleToRoom(roomId, addrTexts); - }) - .then(function(addrs) { - return self._showAnyInviteErrors(addrs, room); - }) - .catch(function(err) { - console.error(err.stack); - var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); - Modal.createTrackedDialog('Failed to invite', '', ErrorDialog, { - title: _t("Failed to invite"), - description: ((err && err.message) ? err.message : _t("Operation failed")), - }); - return null; - }) - .done(); - } - - // Close - this will happen before the above, as that is async - this.props.onFinished(true, addrTexts); - }, - - _isOnInviteList: function(uid) { - for (let i = 0; i < this.state.inviteList.length; i++) { - if ( - this.state.inviteList[i].addressType == 'mx' && - this.state.inviteList[i].address.toLowerCase() === uid - ) { - return true; - } - } - return false; - }, - - _isDmChat: function(addrTexts) { - if (addrTexts.length === 1 && - getAddressType(addrTexts[0]) === "mx" && - !this.props.roomId - ) { - return true; - } else { - return false; - } - }, - - _showAnyInviteErrors: function(addrs, room) { - // Show user any errors - var errorList = []; - for (var addr in addrs) { - if (addrs.hasOwnProperty(addr) && addrs[addr] === "error") { - errorList.push(addr); - } - } - - if (errorList.length > 0) { - var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); - Modal.createTrackedDialog('Failed to invite the following users to the room', '', ErrorDialog, { - title: _t("Failed to invite the following users to the %(roomName)s room:", {roomName: room.name}), - description: errorList.join(", "), - }); - } - return addrs; - }, - _addInputToList: function() { const addressText = this.refs.textinput.value.trim(); const addrType = getAddressType(addressText); @@ -527,10 +362,10 @@ module.exports = React.createClass({ const AddressSelector = sdk.getComponent("elements.AddressSelector"); this.scrollElement = null; - var query = []; + let query = []; // create the invite list if (this.state.inviteList.length > 0) { - var AddressTile = sdk.getComponent("elements.AddressTile"); + const AddressTile = sdk.getComponent("elements.AddressTile"); for (let i = 0; i < this.state.inviteList.length; i++) { query.push( diff --git a/src/components/views/elements/AddressSelector.js b/src/components/views/elements/AddressSelector.js index 5329994037..003fd534bb 100644 --- a/src/components/views/elements/AddressSelector.js +++ b/src/components/views/elements/AddressSelector.js @@ -20,7 +20,7 @@ limitations under the License. import React from 'react'; import sdk from '../../../index'; import classNames from 'classnames'; -import { InviteAddressType } from './AddressTile'; +import { InviteAddressType } from '../../../Invite'; export default React.createClass({ displayName: 'AddressSelector', diff --git a/src/components/views/elements/AddressTile.js b/src/components/views/elements/AddressTile.js index 78fd942a46..fb35dafa5c 100644 --- a/src/components/views/elements/AddressTile.js +++ b/src/components/views/elements/AddressTile.js @@ -1,5 +1,6 @@ /* Copyright 2015, 2016 OpenMarket Ltd +Copyright 2017 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. @@ -14,31 +15,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -'use strict'; - import React from 'react'; import classNames from 'classnames'; import sdk from "../../../index"; import MatrixClientPeg from "../../../MatrixClientPeg"; import { _t } from '../../../languageHandler'; - -// React PropType definition for an object describing -// an address that can be invited to a room (which -// could be a third party identifier or a matrix ID) -// along with some additional information about the -// address / target. -export const InviteAddressType = React.PropTypes.shape({ - addressType: React.PropTypes.oneOf([ - 'mx', 'email' - ]).isRequired, - address: React.PropTypes.string.isRequired, - displayName: React.PropTypes.string, - avatarMxc: React.PropTypes.string, - // true if the address is known to be a valid address (eg. is a real - // user we've seen) or false otherwise (eg. is just an address the - // user has entered) - isKnown: React.PropTypes.bool, -}); +import { InviteAddressType } from '../../../Invite.js'; export default React.createClass({ From d1c54e1224d2c8c46e91fe480527046cac26164d Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 15 Aug 2017 08:58:08 +0100 Subject: [PATCH 3/9] Switch to prop-types --- src/Invite.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Invite.js b/src/Invite.js index d825dac6f5..e7e7264d11 100644 --- a/src/Invite.js +++ b/src/Invite.js @@ -15,6 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. */ +import PropTypes from 'prop-types'; import MatrixClientPeg from './MatrixClientPeg'; import MultiInviter from './utils/MultiInviter'; import Modal from "./Modal"; @@ -30,20 +31,20 @@ export const addressTypes = [ 'mx', 'email', ]; -// React PropType definition for an object describing +// PropType definition for an object describing // an address that can be invited to a room (which // could be a third party identifier or a matrix ID) // along with some additional information about the // address / target. -export const InviteAddressType = React.PropTypes.shape({ - addressType: React.PropTypes.oneOf(addressTypes).isRequired, - address: React.PropTypes.string.isRequired, - displayName: React.PropTypes.string, - avatarMxc: React.PropTypes.string, +export const InviteAddressType = PropTypes.shape({ + addressType: PropTypes.oneOf(addressTypes).isRequired, + address: PropTypes.string.isRequired, + displayName: PropTypes.string, + avatarMxc: PropTypes.string, // true if the address is known to be a valid address (eg. is a real // user we've seen) or false otherwise (eg. is just an address the // user has entered) - isKnown: React.PropTypes.bool, + isKnown: PropTypes.bool, }); export function getAddressType(inputText) { From b7b449434dd5859ac8a66a240683766101044ab6 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 15 Aug 2017 09:10:13 +0100 Subject: [PATCH 4/9] Lint --- .eslintignore.errorfiles | 1 - .../views/dialogs/UserPickerDialog.js | 18 ++++++++---------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/.eslintignore.errorfiles b/.eslintignore.errorfiles index 55eaf75e4b..2018961854 100644 --- a/.eslintignore.errorfiles +++ b/.eslintignore.errorfiles @@ -33,7 +33,6 @@ src/components/views/create_room/CreateRoomButton.js src/components/views/create_room/Presets.js src/components/views/create_room/RoomAlias.js src/components/views/dialogs/ChatCreateOrReuseDialog.js -src/components/views/dialogs/ChatInviteDialog.js src/components/views/dialogs/DeactivateAccountDialog.js src/components/views/dialogs/InteractiveAuthDialog.js src/components/views/dialogs/SetMxIdDialog.js diff --git a/src/components/views/dialogs/UserPickerDialog.js b/src/components/views/dialogs/UserPickerDialog.js index 5627a814c0..7dc1be7d25 100644 --- a/src/components/views/dialogs/UserPickerDialog.js +++ b/src/components/views/dialogs/UserPickerDialog.js @@ -20,14 +20,10 @@ import PropTypes from 'prop-types'; import { _t } from '../../../languageHandler'; import sdk from '../../../index'; import { getAddressType } from '../../../Invite'; -import createRoom from '../../../createRoom'; import MatrixClientPeg from '../../../MatrixClientPeg'; -import DMRoomMap from '../../../utils/DMRoomMap'; -import Modal from '../../../Modal'; import AccessibleButton from '../elements/AccessibleButton'; import Promise from 'bluebird'; -import dis from '../../../dispatcher'; -import { addressTypes, InviteAddressType } from '../../../Invite.js'; +import { addressTypes } from '../../../Invite.js'; const TRUNCATE_QUERY_LIST = 40; const QUERY_USER_DIRECTORY_DEBOUNCE_MS = 200; @@ -330,7 +326,7 @@ module.exports = React.createClass({ // not like they leak. this._cancelThreepidLookup = function() { cancelled = true; - } + }; // wait a bit to let the user finish typing return Promise.delay(500).then(() => { @@ -362,13 +358,13 @@ module.exports = React.createClass({ const AddressSelector = sdk.getComponent("elements.AddressSelector"); this.scrollElement = null; - let query = []; + const query = []; // create the invite list if (this.state.inviteList.length > 0) { const AddressTile = sdk.getComponent("elements.AddressTile"); for (let i = 0; i < this.state.inviteList.length; i++) { query.push( - + , ); } } @@ -390,7 +386,9 @@ module.exports = React.createClass({ let error; let addressSelector; if (this.state.error) { - error =
{_t("You have entered an invalid contact. Try using their Matrix ID or email address.")}
; + error =
+ {_t("You have entered an invalid contact. Try using their Matrix ID or email address.")} +
; } else if (this.state.searchError) { error =
{this.state.searchError}
; } else if ( @@ -433,5 +431,5 @@ module.exports = React.createClass({ ); - } + }, }); From fa660c8211bf834781af5872bae59d2b3387f99d Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 15 Aug 2017 10:57:24 +0100 Subject: [PATCH 5/9] PR feedback --- src/Invite.js | 6 +++--- src/components/views/dialogs/UserPickerDialog.js | 15 ++++++--------- src/components/views/elements/AddressSelector.js | 4 ++-- src/components/views/elements/AddressTile.js | 4 ++-- 4 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/Invite.js b/src/Invite.js index e7e7264d11..b79a245549 100644 --- a/src/Invite.js +++ b/src/Invite.js @@ -18,9 +18,9 @@ limitations under the License. import PropTypes from 'prop-types'; import MatrixClientPeg from './MatrixClientPeg'; import MultiInviter from './utils/MultiInviter'; -import Modal from "./Modal"; +import Modal from './Modal'; import createRoom from './createRoom'; -import sdk from "./"; +import sdk from './'; import { _t } from './languageHandler'; const emailRegex = /^\S+@\S+\.\S+$/; @@ -36,7 +36,7 @@ export const addressTypes = [ // could be a third party identifier or a matrix ID) // along with some additional information about the // address / target. -export const InviteAddressType = PropTypes.shape({ +export const UserAddressType = PropTypes.shape({ addressType: PropTypes.oneOf(addressTypes).isRequired, address: PropTypes.string.isRequired, displayName: PropTypes.string, diff --git a/src/components/views/dialogs/UserPickerDialog.js b/src/components/views/dialogs/UserPickerDialog.js index 7dc1be7d25..1405bc754b 100644 --- a/src/components/views/dialogs/UserPickerDialog.js +++ b/src/components/views/dialogs/UserPickerDialog.js @@ -33,10 +33,7 @@ module.exports = React.createClass({ propTypes: { title: PropTypes.string.isRequired, - description: PropTypes.oneOfType([ - PropTypes.element, - PropTypes.string, - ]), + description: PropTypes.node, value: PropTypes.string, placeholder: PropTypes.string, roomId: PropTypes.string, @@ -58,7 +55,7 @@ module.exports = React.createClass({ return { error: false, - // List of InviteAddressType objects representing + // List of UserAddressType objects representing // the list of addresses we're going to invite inviteList: [], @@ -70,7 +67,7 @@ module.exports = React.createClass({ serverSupportsUserDirectory: true, // The query being searched for query: "", - // List of AddressTile.InviteAddressType objects representing + // List of UserAddressType objects representing // the set of auto-completion results for the current search // query. queryList: [], @@ -254,7 +251,7 @@ module.exports = React.createClass({ return; } // Return objects, structure of which is defined - // by InviteAddressType + // by UserAddressType queryList.push({ addressType: 'mx', address: user.user_id, @@ -268,7 +265,7 @@ module.exports = React.createClass({ // This is important, otherwise there's no way to invite // a perfectly valid address if there are close matches. const addrType = getAddressType(query); - if (this.props.validAddressTypes.indexOf(addrType) !== -1) { + if (this.props.validAddressTypes.includes(addrType)) { queryList.unshift({ addressType: addrType, address: query, @@ -342,7 +339,7 @@ module.exports = React.createClass({ if (cancelled) return null; this.setState({ queryList: [{ - // an InviteAddressType + // a UserAddressType addressType: medium, address: address, displayName: res.displayname, diff --git a/src/components/views/elements/AddressSelector.js b/src/components/views/elements/AddressSelector.js index 003fd534bb..1aae10737e 100644 --- a/src/components/views/elements/AddressSelector.js +++ b/src/components/views/elements/AddressSelector.js @@ -20,7 +20,7 @@ limitations under the License. import React from 'react'; import sdk from '../../../index'; import classNames from 'classnames'; -import { InviteAddressType } from '../../../Invite'; +import { UserAddressType } from '../../../Invite'; export default React.createClass({ displayName: 'AddressSelector', @@ -29,7 +29,7 @@ export default React.createClass({ onSelected: React.PropTypes.func.isRequired, // List of the addresses to display - addressList: React.PropTypes.arrayOf(InviteAddressType).isRequired, + addressList: React.PropTypes.arrayOf(UserAddressType).isRequired, truncateAt: React.PropTypes.number.isRequired, selected: React.PropTypes.number, diff --git a/src/components/views/elements/AddressTile.js b/src/components/views/elements/AddressTile.js index fb35dafa5c..ba7d79d987 100644 --- a/src/components/views/elements/AddressTile.js +++ b/src/components/views/elements/AddressTile.js @@ -20,14 +20,14 @@ import classNames from 'classnames'; import sdk from "../../../index"; import MatrixClientPeg from "../../../MatrixClientPeg"; import { _t } from '../../../languageHandler'; -import { InviteAddressType } from '../../../Invite.js'; +import { UserAddressType } from '../../../Invite.js'; export default React.createClass({ displayName: 'AddressTile', propTypes: { - address: InviteAddressType.isRequired, + address: UserAddressType.isRequired, canDismiss: React.PropTypes.bool, onDismissed: React.PropTypes.func, justified: React.PropTypes.bool, From bbcf7e1d9bd6f0de315f4c251510b2de95bb4cbe Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 15 Aug 2017 13:30:13 +0100 Subject: [PATCH 6/9] s/inviteList/userList/ --- .../views/dialogs/UserPickerDialog.js | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/components/views/dialogs/UserPickerDialog.js b/src/components/views/dialogs/UserPickerDialog.js index 1405bc754b..f4ea0c6a24 100644 --- a/src/components/views/dialogs/UserPickerDialog.js +++ b/src/components/views/dialogs/UserPickerDialog.js @@ -57,7 +57,7 @@ module.exports = React.createClass({ // List of UserAddressType objects representing // the list of addresses we're going to invite - inviteList: [], + userList: [], // Whether a search is ongoing busy: false, @@ -82,14 +82,14 @@ module.exports = React.createClass({ }, onButtonClick: function() { - let inviteList = this.state.inviteList.slice(); + let userList = this.state.userList.slice(); // Check the text input field to see if user has an unconverted address - // If there is and it's valid add it to the local inviteList + // If there is and it's valid add it to the local userList if (this.refs.textinput.value !== '') { - inviteList = this._addInputToList(); - if (inviteList === null) return; + userList = this._addInputToList(); + if (userList === null) return; } - this.props.onFinished(true, inviteList); + this.props.onFinished(true, userList); }, onCancel: function() { @@ -113,10 +113,10 @@ module.exports = React.createClass({ e.stopPropagation(); e.preventDefault(); if (this.addressSelector) this.addressSelector.chooseSelection(); - } else if (this.refs.textinput.value.length === 0 && this.state.inviteList.length && e.keyCode === 8) { // backspace + } else if (this.refs.textinput.value.length === 0 && this.state.userList.length && e.keyCode === 8) { // backspace e.stopPropagation(); e.preventDefault(); - this.onDismissed(this.state.inviteList.length - 1)(); + this.onDismissed(this.state.userList.length - 1)(); } else if (e.keyCode === 13) { // enter e.stopPropagation(); e.preventDefault(); @@ -158,10 +158,10 @@ module.exports = React.createClass({ onDismissed: function(index) { return () => { - const inviteList = this.state.inviteList.slice(); - inviteList.splice(index, 1); + const userList = this.state.userList.slice(); + userList.splice(index, 1); this.setState({ - inviteList: inviteList, + userList: userList, queryList: [], query: "", }); @@ -176,10 +176,10 @@ module.exports = React.createClass({ }, onSelected: function(index) { - const inviteList = this.state.inviteList.slice(); - inviteList.push(this.state.queryList[index]); + const userList = this.state.userList.slice(); + userList.push(this.state.queryList[index]); this.setState({ - inviteList: inviteList, + userList: userList, queryList: [], query: "", }); @@ -304,15 +304,15 @@ module.exports = React.createClass({ } } - const inviteList = this.state.inviteList.slice(); - inviteList.push(addrObj); + const userList = this.state.userList.slice(); + userList.push(addrObj); this.setState({ - inviteList: inviteList, + userList: userList, queryList: [], query: "", }); if (this._cancelThreepidLookup) this._cancelThreepidLookup(); - return inviteList; + return userList; }, _lookupThreepid: function(medium, address) { @@ -357,18 +357,18 @@ module.exports = React.createClass({ const query = []; // create the invite list - if (this.state.inviteList.length > 0) { + if (this.state.userList.length > 0) { const AddressTile = sdk.getComponent("elements.AddressTile"); - for (let i = 0; i < this.state.inviteList.length; i++) { + for (let i = 0; i < this.state.userList.length; i++) { query.push( - , + , ); } } // Add the query at the end query.push( -