Merge branch 'travis/ftue/user-lists/4-composer' into travis/ftue/user-lists/4.5-paste-and-3pids
						commit
						67a67e3cb2
					
				|  | @ -21,15 +21,51 @@ limitations under the License. | |||
|     .mx_DMInviteDialog_editor { | ||||
|         flex: 1; | ||||
|         width: 100%; // Needed to make the Field inside grow | ||||
|     } | ||||
|         background-color: $user-tile-hover-bg-color; | ||||
|         border-radius: 4px; | ||||
|         min-height: 25px; | ||||
|         padding-left: 8px; | ||||
|         overflow-x: hidden; | ||||
|         overflow-y: auto; | ||||
| 
 | ||||
|     .mx_Field { | ||||
|         margin: 0; | ||||
|         .mx_DMInviteDialog_userTile { | ||||
|             display: inline-block; | ||||
|             float: left; | ||||
|             position: relative; | ||||
|             top: 7px; | ||||
|         } | ||||
| 
 | ||||
|         // Using a textarea for this element, to circumvent autofill | ||||
|         // Mostly copied from AddressPickerDialog | ||||
|         textarea, | ||||
|         textarea:focus { | ||||
|             height: 34px; | ||||
|             line-height: 34px; | ||||
|             font-size: 14px; | ||||
|             padding-left: 12px; | ||||
|             margin: 0 !important; | ||||
|             border: 0 !important; | ||||
|             outline: 0 !important; | ||||
|             resize: none; | ||||
|             overflow: hidden; | ||||
|             box-sizing: border-box; | ||||
|             word-wrap: nowrap; | ||||
| 
 | ||||
|             // Roughly fill about 2/5ths of the available space. This is to try and 'fill' the | ||||
|             // remaining space after a bunch of pills, but is a bit hacky. Ideally we'd have | ||||
|             // support for "fill remaining width", but traditional tricks don't work with what | ||||
|             // we're pushing into this "field". Flexbox just makes things worse. The theory is | ||||
|             // that users won't need more than about 2/5ths of the input to find the person | ||||
|             // they're looking for. | ||||
|             width: 40%; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     .mx_DMInviteDialog_goButton { | ||||
|         width: 48px; | ||||
|         margin-left: 10px; | ||||
|         height: 25px; | ||||
|         line-height: 25px; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -57,6 +93,43 @@ limitations under the License. | |||
|         vertical-align: middle; | ||||
|     } | ||||
| 
 | ||||
|     .mx_DMInviteDialog_roomTile_avatarStack { | ||||
|         display: inline-block; | ||||
|         position: relative; | ||||
|         width: 36px; | ||||
|         height: 36px; | ||||
| 
 | ||||
|         & > * { | ||||
|             position: absolute; | ||||
|             top: 0; | ||||
|             left: 0; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     .mx_DMInviteDialog_roomTile_selected { | ||||
|         width: 36px; | ||||
|         height: 36px; | ||||
|         border-radius: 36px; | ||||
|         background-color: $username-variant1-color; | ||||
|         display: inline-block; | ||||
|         position: relative; | ||||
| 
 | ||||
|         &::before { | ||||
|             content: ""; | ||||
|             width: 24px; | ||||
|             height: 24px; | ||||
|             grid-column: 1; | ||||
|             grid-row: 1; | ||||
|             mask-image: url('$(res)/img/feather-customised/check.svg'); | ||||
|             mask-size: 100%; | ||||
|             mask-repeat: no-repeat; | ||||
|             position: absolute; | ||||
|             top: 6px; // 50% | ||||
|             left: 6px; // 50% | ||||
|             background-color: #ffffff; // this is fine without a var because it's for both themes | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     .mx_DMInviteDialog_roomTile_name { | ||||
|         font-weight: 600; | ||||
|         font-size: 14px; | ||||
|  | @ -83,3 +156,38 @@ limitations under the License. | |||
|     } | ||||
| } | ||||
| 
 | ||||
| // Many of these styles are stolen from mx_UserPill, but adjusted for the invite dialog. | ||||
| .mx_DMInviteDialog_userTile { | ||||
|     margin-right: 8px; | ||||
| 
 | ||||
|     .mx_DMInviteDialog_userTile_pill { | ||||
|         background-color: $username-variant1-color; | ||||
|         border-radius: 12px; | ||||
|         display: inline-block; | ||||
|         height: 24px; | ||||
|         line-height: 24px; | ||||
|         padding-left: 8px; | ||||
|         padding-right: 8px; | ||||
|         color: #ffffff; // this is fine without a var because it's for both themes | ||||
| 
 | ||||
|         .mx_DMInviteDialog_userTile_avatar { | ||||
|             border-radius: 20px; | ||||
|             position: relative; | ||||
|             left: -5px; | ||||
|             top: 2px; | ||||
|         } | ||||
| 
 | ||||
|         img.mx_DMInviteDialog_userTile_avatar { | ||||
|             vertical-align: top; | ||||
|         } | ||||
| 
 | ||||
|         .mx_DMInviteDialog_userTile_name { | ||||
|             vertical-align: top; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     .mx_DMInviteDialog_userTile_remove { | ||||
|         display: inline-block; | ||||
|         margin-left: 4px; | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -0,0 +1,37 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <svg width="69px" height="68px" viewBox="0 0 69 68" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> | ||||
|     <!-- Generator: Sketch 58 (84663) - https://sketch.com --> | ||||
|     <title>at-sign</title> | ||||
|     <desc>Created with Sketch.</desc> | ||||
|     <defs> | ||||
|         <filter x="-5.9%" y="-7.9%" width="111.8%" height="115.8%" filterUnits="objectBoundingBox" id="filter-1"> | ||||
|             <feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset> | ||||
|             <feGaussianBlur stdDeviation="16" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur> | ||||
|             <feColorMatrix values="0 0 0 0 0   0 0 0 0 0.473684211   0 0 0 0 1  0 0 0 0.241258741 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix> | ||||
|             <feMerge> | ||||
|                 <feMergeNode in="shadowMatrixOuter1"></feMergeNode> | ||||
|                 <feMergeNode in="SourceGraphic"></feMergeNode> | ||||
|             </feMerge> | ||||
|         </filter> | ||||
|     </defs> | ||||
|     <g id="FTUE" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round"> | ||||
|         <g id="FTUE---User-list-suggestions" transform="translate(-5161.000000, -1379.000000)" stroke="#368BD6"> | ||||
|             <g id="Group-14-Copy-10" transform="translate(4748.000000, 1168.000000)"> | ||||
|                 <g id="Web-Copy-6" filter="url(#filter-1)"> | ||||
|                     <g id="modal-copy" transform="translate(253.000000, 118.000000)"> | ||||
|                         <g id="content" transform="translate(40.000000, 107.000000)"> | ||||
|                             <g id="Group-43"> | ||||
|                                 <g id="Group-15-Copy-6" transform="translate(143.000000, 6.000000)"> | ||||
|                                     <g id="at-sign" transform="translate(5.500000, 6.500000)"> | ||||
|                                         <circle id="Oval" cx="6.28571429" cy="5.71428571" r="2.28571429"></circle> | ||||
|                                         <path d="M8.57142857,3.42857143 L8.57142857,6.28571429 C8.57142857,7.23248814 9.33894043,8 10.2857143,8 C11.2324881,8 12,7.23248814 12,6.28571429 L12,5.71428571 C11.9998328,3.05880261 10.1703625,0.75337961 7.58436487,0.149884297 C4.9983672,-0.453611015 2.33741804,0.803881013 1.16186053,3.18498476 C-0.0136969889,5.5660885 0.605971794,8.4432307 2.65750183,10.1292957 C4.70903186,11.8153606 7.65171364,11.8659623 9.76,10.2514286" id="Path"></path> | ||||
|                                     </g> | ||||
|                                 </g> | ||||
|                             </g> | ||||
|                         </g> | ||||
|                     </g> | ||||
|                 </g> | ||||
|             </g> | ||||
|         </g> | ||||
|     </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 2.6 KiB | 
|  | @ -0,0 +1,36 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <svg width="58px" height="60px" viewBox="26 25 6 6" version="1.1" xmlns="http://www.w3.org/2000/svg"> | ||||
|     <!-- Generator: Sketch 58 (84663) - https://sketch.com --> | ||||
|     <title>x</title> | ||||
|     <desc>Created with Sketch.</desc> | ||||
|     <defs> | ||||
|         <filter x="-5.9%" y="-7.9%" width="111.8%" height="115.8%" filterUnits="objectBoundingBox" id="filter-1"> | ||||
|             <feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset> | ||||
|             <feGaussianBlur stdDeviation="16" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur> | ||||
|             <feColorMatrix values="0 0 0 0 0   0 0 0 0 0.473684211   0 0 0 0 1  0 0 0 0.241258741 0" type="matrix" | ||||
|                            in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix> | ||||
|             <feMerge> | ||||
|                 <feMergeNode in="shadowMatrixOuter1"></feMergeNode> | ||||
|                 <feMergeNode in="SourceGraphic"></feMergeNode> | ||||
|             </feMerge> | ||||
|         </filter> | ||||
|     </defs> | ||||
|     <g id="F..." transform="translate(-2802.000000, -392.000000)" stroke="#61708B"> | ||||
|         <g id="Group-14-Copy-8" transform="translate(2396.000000, 177.000000)"> | ||||
|             <g id="Web-Copy-6" filter="url(#filter-1)"> | ||||
|                 <g id="modal-copy" transform="translate(253.000000, 118.000000)"> | ||||
|                     <g id="content" transform="translate(40.000000, 107.000000)"> | ||||
|                         <g id="Group-43"> | ||||
|                             <g id="Group-15" transform="translate(7.000000, 6.000000)"> | ||||
|                                 <g id="x" transform="translate(132.000000, 9.000000)"> | ||||
|                                     <path d="M6,0 L0,6" id="Path"></path> | ||||
|                                     <path d="M0,0 L6,6" id="Path"></path> | ||||
|                                 </g> | ||||
|                             </g> | ||||
|                         </g> | ||||
|                     </g> | ||||
|                 </g> | ||||
|             </g> | ||||
|         </g> | ||||
|     </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 1.9 KiB | 
|  | @ -14,7 +14,7 @@ See the License for the specific language governing permissions and | |||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| import React from 'react'; | ||||
| import React, {createRef} from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import {_t} from "../../../languageHandler"; | ||||
| import sdk from "../../../index"; | ||||
|  | @ -31,18 +31,46 @@ import {getHttpUriForMxc} from "matrix-js-sdk/lib/content-repo"; | |||
| const INITIAL_ROOMS_SHOWN = 3; // Number of rooms to show at first
 | ||||
| const INCREMENT_ROOMS_SHOWN = 5; // Number of rooms to add when 'show more' is clicked
 | ||||
| 
 | ||||
| class DirectoryMember { | ||||
| // This is the interface that is expected by various components in this file. It is a bit
 | ||||
| // awkward because it also matches the RoomMember class from the js-sdk with some extra support
 | ||||
| // for 3PIDs/email addresses.
 | ||||
| //
 | ||||
| // Dev note: In order to allow us to compile the app correctly, this needs to be a class
 | ||||
| // even though FlowJS supports interfaces. It just means that we "extend" rather than "implement"
 | ||||
| // in the classes, at least until TypeScript saves us.
 | ||||
| class Member { | ||||
|     /** | ||||
|      * The display name of this Member. For users this should be their profile's display | ||||
|      * name or user ID if none set. For 3PIDs this should be the 3PID address (email). | ||||
|      */ | ||||
|     get name(): string { throw new Error("Member class not implemented"); } | ||||
| 
 | ||||
|     /** | ||||
|      * The ID of this Member. For users this should be their user ID. For 3PIDs this should | ||||
|      * be the 3PID address (email). | ||||
|      */ | ||||
|     get userId(): string { throw new Error("Member class not implemented"); } | ||||
| 
 | ||||
|     /** | ||||
|      * Gets the MXC URL of this Member's avatar. For users this should be their profile's | ||||
|      * avatar MXC URL or null if none set. For 3PIDs this should always be null. | ||||
|      */ | ||||
|     getMxcAvatarUrl(): string { throw new Error("Member class not implemented"); } | ||||
| } | ||||
| 
 | ||||
| class DirectoryMember extends Member { | ||||
|     _userId: string; | ||||
|     _displayName: string; | ||||
|     _avatarUrl: string; | ||||
| 
 | ||||
|     constructor(userDirResult: {user_id: string, display_name: string, avatar_url: string}) { | ||||
|         super(); | ||||
|         this._userId = userDirResult.user_id; | ||||
|         this._displayName = userDirResult.display_name; | ||||
|         this._avatarUrl = userDirResult.avatar_url; | ||||
|     } | ||||
| 
 | ||||
|     // These next members are to implement the contract expected by DMRoomTile
 | ||||
|     // These next class members are for the Member interface
 | ||||
|     get name(): string { | ||||
|         return this._displayName || this._userId; | ||||
|     } | ||||
|  | @ -56,13 +84,64 @@ class DirectoryMember { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| class DMUserTile extends React.PureComponent { | ||||
|     static propTypes = { | ||||
|         member: PropTypes.object.isRequired, // Should be a Member (see interface above)
 | ||||
|         onRemove: PropTypes.func.isRequired, // takes 1 argument, the member being removed
 | ||||
|     }; | ||||
| 
 | ||||
|     _onRemove = (e) => { | ||||
|         // Stop the browser from highlighting text
 | ||||
|         e.preventDefault(); | ||||
|         e.stopPropagation(); | ||||
| 
 | ||||
|         this.props.onRemove(this.props.member); | ||||
|     }; | ||||
| 
 | ||||
|     render() { | ||||
|         const BaseAvatar = sdk.getComponent("views.avatars.BaseAvatar"); | ||||
|         const AccessibleButton = sdk.getComponent("elements.AccessibleButton"); | ||||
| 
 | ||||
|         const avatarSize = 20; | ||||
|         const avatar = this.props.member.isEmail | ||||
|             ? <img | ||||
|                 className='mx_DMInviteDialog_userTile_avatar' | ||||
|                 src={require("../../../../res/img/icon-email-pill-avatar.svg")} | ||||
|                 width={avatarSize} height={avatarSize} /> | ||||
|             : <BaseAvatar | ||||
|                 className='mx_DMInviteDialog_userTile_avatar' | ||||
|                 url={getHttpUriForMxc( | ||||
|                     MatrixClientPeg.get().getHomeserverUrl(), this.props.member.getMxcAvatarUrl(), | ||||
|                     avatarSize, avatarSize, "crop")} | ||||
|                 name={this.props.member.name} | ||||
|                 idName={this.props.member.userId} | ||||
|                 width={avatarSize} | ||||
|                 height={avatarSize} />; | ||||
| 
 | ||||
|         return ( | ||||
|             <span className='mx_DMInviteDialog_userTile'> | ||||
|                 <span className='mx_DMInviteDialog_userTile_pill'> | ||||
|                     {avatar} | ||||
|                     <span className='mx_DMInviteDialog_userTile_name'>{this.props.member.name}</span> | ||||
|                 </span> | ||||
|                 <AccessibleButton | ||||
|                     className='mx_DMInviteDialog_userTile_remove' | ||||
|                     onClick={this._onRemove} | ||||
|                 > | ||||
|                     <img src={require("../../../../res/img/icon-pill-remove.svg")} alt={_t('Remove')} width={8} height={8} /> | ||||
|                 </AccessibleButton> | ||||
|             </span> | ||||
|         ); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| class DMRoomTile extends React.PureComponent { | ||||
|     static propTypes = { | ||||
|         // Has properties to match RoomMember: userId (str), name (str), getMxcAvatarUrl(): string
 | ||||
|         member: PropTypes.object.isRequired, | ||||
|         member: PropTypes.object.isRequired, // Should be a Member (see interface above)
 | ||||
|         lastActiveTs: PropTypes.number, | ||||
|         onToggle: PropTypes.func.isRequired, | ||||
|         onToggle: PropTypes.func.isRequired, // takes 1 argument, the member being toggled
 | ||||
|         highlightWord: PropTypes.string, | ||||
|         isSelected: PropTypes.bool, | ||||
|     }; | ||||
| 
 | ||||
|     _onClick = (e) => { | ||||
|  | @ -70,7 +149,7 @@ class DMRoomTile extends React.PureComponent { | |||
|         e.preventDefault(); | ||||
|         e.stopPropagation(); | ||||
| 
 | ||||
|         this.props.onToggle(this.props.member.userId); | ||||
|         this.props.onToggle(this.props.member); | ||||
|     }; | ||||
| 
 | ||||
|     _highlightName(str: string) { | ||||
|  | @ -121,19 +200,37 @@ class DMRoomTile extends React.PureComponent { | |||
|         } | ||||
| 
 | ||||
|         const avatarSize = 36; | ||||
|         const avatarUrl = getHttpUriForMxc( | ||||
|             MatrixClientPeg.get().getHomeserverUrl(), this.props.member.getMxcAvatarUrl(), | ||||
|             avatarSize, avatarSize, "crop"); | ||||
|         const avatar = this.props.member.isEmail | ||||
|             ? <img | ||||
|                 src={require("../../../../res/img/icon-email-pill-avatar.svg")} | ||||
|                 width={avatarSize} height={avatarSize} /> | ||||
|             : <BaseAvatar | ||||
|                 url={getHttpUriForMxc( | ||||
|                     MatrixClientPeg.get().getHomeserverUrl(), this.props.member.getMxcAvatarUrl(), | ||||
|                     avatarSize, avatarSize, "crop")} | ||||
|                 name={this.props.member.name} | ||||
|                 idName={this.props.member.userId} | ||||
|                 width={avatarSize} | ||||
|                 height={avatarSize} />; | ||||
| 
 | ||||
|         let checkmark = null; | ||||
|         if (this.props.isSelected) { | ||||
|             // To reduce flickering we put the 'selected' room tile above the real avatar
 | ||||
|             checkmark = <div className='mx_DMInviteDialog_roomTile_selected' />; | ||||
|         } | ||||
| 
 | ||||
|         // To reduce flickering we put the checkmark on top of the actual avatar (prevents
 | ||||
|         // the browser from reloading the image source when the avatar remounts).
 | ||||
|         const stackedAvatar = ( | ||||
|             <span className='mx_DMInviteDialog_roomTile_avatarStack'> | ||||
|                 {avatar} | ||||
|                 {checkmark} | ||||
|             </span> | ||||
|         ); | ||||
| 
 | ||||
|         return ( | ||||
|             <div className='mx_DMInviteDialog_roomTile' onClick={this._onClick}> | ||||
|                 <BaseAvatar | ||||
|                     url={avatarUrl} | ||||
|                     name={this.props.member.name} | ||||
|                     idName={this.props.member.userId} | ||||
|                     width={avatarSize} | ||||
|                     height={avatarSize} | ||||
|                 /> | ||||
|                 {stackedAvatar} | ||||
|                 <span className='mx_DMInviteDialog_roomTile_name'>{this._highlightName(this.props.member.name)}</span> | ||||
|                 <span className='mx_DMInviteDialog_roomTile_userId'>{this._highlightName(this.props.member.userId)}</span> | ||||
|                 {timestamp} | ||||
|  | @ -149,12 +246,13 @@ export default class DMInviteDialog extends React.PureComponent { | |||
|     }; | ||||
| 
 | ||||
|     _debounceTimer: number = null; | ||||
|     _editorRef: any = null; | ||||
| 
 | ||||
|     constructor() { | ||||
|         super(); | ||||
| 
 | ||||
|         this.state = { | ||||
|             targets: [], // string[] of mxids/email addresses
 | ||||
|             targets: [], // array of Member objects (see interface above)
 | ||||
|             filterText: "", | ||||
|             recents: this._buildRecents(), | ||||
|             numRecentsShown: INITIAL_ROOMS_SHOWN, | ||||
|  | @ -162,6 +260,8 @@ export default class DMInviteDialog extends React.PureComponent { | |||
|             numSuggestionsShown: INITIAL_ROOMS_SHOWN, | ||||
|             serverResultsMixin: [], // { user: DirectoryMember, userId: string }[], like recents and suggestions
 | ||||
|         }; | ||||
| 
 | ||||
|         this._editorRef = createRef(); | ||||
|     } | ||||
| 
 | ||||
|     _buildRecents(): {userId: string, user: RoomMember, lastActive: number} { | ||||
|  | @ -245,7 +345,7 @@ export default class DMInviteDialog extends React.PureComponent { | |||
|     } | ||||
| 
 | ||||
|     _startDm = () => { | ||||
|         this.props.onFinished(this.state.targets); | ||||
|         this.props.onFinished(this.state.targets.map(t => t.userId)); | ||||
|     }; | ||||
| 
 | ||||
|     _cancel = () => { | ||||
|  | @ -292,14 +392,33 @@ export default class DMInviteDialog extends React.PureComponent { | |||
|         this.setState({numSuggestionsShown: this.state.numSuggestionsShown + INCREMENT_ROOMS_SHOWN}); | ||||
|     }; | ||||
| 
 | ||||
|     _toggleMember = (userId) => { | ||||
|     _toggleMember = (member: Member) => { | ||||
|         const targets = this.state.targets.map(t => t); // cheap clone for mutation
 | ||||
|         const idx = targets.indexOf(userId); | ||||
|         const idx = targets.indexOf(member); | ||||
|         if (idx >= 0) targets.splice(idx, 1); | ||||
|         else targets.push(userId); | ||||
|         else targets.push(member); | ||||
|         this.setState({targets}); | ||||
|     }; | ||||
| 
 | ||||
|     _removeMember = (member: Member) => { | ||||
|         const targets = this.state.targets.map(t => t); // cheap clone for mutation
 | ||||
|         const idx = targets.indexOf(member); | ||||
|         if (idx >= 0) { | ||||
|             targets.splice(idx, 1); | ||||
|             this.setState({targets}); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     _onClickInputArea = (e) => { | ||||
|         // Stop the browser from highlighting text
 | ||||
|         e.preventDefault(); | ||||
|         e.stopPropagation(); | ||||
| 
 | ||||
|         if (this._editorRef && this._editorRef.current) { | ||||
|             this._editorRef.current.focus(); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     _renderSection(kind: "recents"|"suggestions") { | ||||
|         let sourceMembers = kind === 'recents' ? this.state.recents : this.state.suggestions; | ||||
|         let showNum = kind === 'recents' ? this.state.numRecentsShown : this.state.numSuggestionsShown; | ||||
|  | @ -360,6 +479,7 @@ export default class DMInviteDialog extends React.PureComponent { | |||
|                 key={r.userId} | ||||
|                 onToggle={this._toggleMember} | ||||
|                 highlightWord={this.state.filterText} | ||||
|                 isSelected={this.state.targets.some(t => t.userId === r.userId)} | ||||
|             /> | ||||
|         )); | ||||
|         return ( | ||||
|  | @ -371,23 +491,30 @@ export default class DMInviteDialog extends React.PureComponent { | |||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     render() { | ||||
|         const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); | ||||
|         const Field = sdk.getComponent("elements.Field"); | ||||
|         const AccessibleButton = sdk.getComponent("elements.AccessibleButton"); | ||||
| 
 | ||||
|         // Dev note: The use of Field is temporary/incomplete pending https://github.com/vector-im/riot-web/issues/11197
 | ||||
|         // For now, we just list who the targets are.
 | ||||
|         const editor = ( | ||||
|             <div className='mx_DMInviteDialog_editor'> | ||||
|                 <Field | ||||
|                     id="inviteTargets" | ||||
|                     value={this.state.filterText} | ||||
|                     onChange={this._updateFilter} | ||||
|                 /> | ||||
|     _renderEditor() { | ||||
|         const targets = this.state.targets.map(t => ( | ||||
|             <DMUserTile member={t} onRemove={this._removeMember} key={t.userId} /> | ||||
|         )); | ||||
|         const input = ( | ||||
|             <textarea | ||||
|                 key={"input"} | ||||
|                 rows={1} | ||||
|                 onChange={this._updateFilter} | ||||
|                 defaultValue={this.state.filterText} | ||||
|                 ref={this._editorRef} | ||||
|             /> | ||||
|         ); | ||||
|         return ( | ||||
|             <div className='mx_DMInviteDialog_editor' onClick={this._onClickInputArea}> | ||||
|                 {targets} | ||||
|                 {input} | ||||
|             </div> | ||||
|         ); | ||||
|         const targets = this.state.targets.map(t => <div key={t}>{t}</div>); | ||||
|     } | ||||
| 
 | ||||
|     render() { | ||||
|         const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); | ||||
|         const AccessibleButton = sdk.getComponent("elements.AccessibleButton"); | ||||
| 
 | ||||
|         const userId = MatrixClientPeg.get().getUserId(); | ||||
|         return ( | ||||
|  | @ -406,9 +533,8 @@ export default class DMInviteDialog extends React.PureComponent { | |||
|                             {a: (sub) => <a href={makeUserPermalink(userId)} rel="noopener" target="_blank">{sub}</a>}, | ||||
|                         )} | ||||
|                     </p> | ||||
|                     {targets} | ||||
|                     <div className='mx_DMInviteDialog_addressBar'> | ||||
|                         {editor} | ||||
|                         {this._renderEditor()} | ||||
|                         <AccessibleButton | ||||
|                             kind="primary" | ||||
|                             onClick={this._startDm} | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Travis Ralston
						Travis Ralston