@@ -25,7 +25,12 @@ import { Icon as InfoIcon } from "../../../../res/img/element-icons/info.svg";
2525import { Icon as EmailPillAvatarIcon } from "../../../../res/img/icon-email-pill-avatar.svg" ;
2626import { _t , _td } from "../../../languageHandler" ;
2727import { MatrixClientPeg } from "../../../MatrixClientPeg" ;
28- import { makeRoomPermalink , makeUserPermalink } from "../../../utils/permalinks/Permalinks" ;
28+ import {
29+ getHostnameFromMatrixServerName ,
30+ getServerName ,
31+ makeRoomPermalink ,
32+ makeUserPermalink ,
33+ } from "../../../utils/permalinks/Permalinks" ;
2934import DMRoomMap from "../../../utils/DMRoomMap" ;
3035import SdkConfig from "../../../SdkConfig" ;
3136import * as Email from "../../../email" ;
@@ -69,7 +74,7 @@ import {
6974 startDmOnFirstMessage ,
7075 ThreepidMember ,
7176} from "../../../utils/direct-messages" ;
72- import { AnyInviteKind , KIND_CALL_TRANSFER , KIND_DM , KIND_INVITE } from './InviteDialogTypes' ;
77+ import { KIND_CALL_TRANSFER , KIND_DM , KIND_INVITE } from './InviteDialogTypes' ;
7378import Modal from '../../../Modal' ;
7479import dis from "../../../dispatcher/dispatcher" ;
7580
@@ -243,26 +248,37 @@ class DMRoomTile extends React.PureComponent<IDMRoomTileProps> {
243248 }
244249}
245250
246- interface IInviteDialogProps {
251+ interface BaseProps {
247252 // Takes a boolean which is true if a user / users were invited /
248253 // a call transfer was initiated or false if the dialog was cancelled
249254 // with no action taken.
250255 onFinished : ( success : boolean ) => void ;
251256
252- // The kind of invite being performed. Assumed to be KIND_DM if
253- // not provided.
254- kind : AnyInviteKind ;
257+ // Initial value to populate the filter with
258+ initialText ?: string ;
259+ }
260+
261+ interface InviteDMProps extends BaseProps {
262+ // The kind of invite being performed. Assumed to be KIND_DM if not provided.
263+ kind ?: typeof KIND_DM ;
264+ }
265+
266+ interface InviteRoomProps extends BaseProps {
267+ kind : typeof KIND_INVITE ;
255268
256269 // The room ID this dialog is for. Only required for KIND_INVITE.
257270 roomId : string ;
271+ }
272+
273+ interface InviteCallProps extends BaseProps {
274+ kind : typeof KIND_CALL_TRANSFER ;
258275
259276 // The call to transfer. Only required for KIND_CALL_TRANSFER.
260277 call : MatrixCall ;
261-
262- // Initial value to populate the filter with
263- initialText : string ;
264278}
265279
280+ type Props = InviteDMProps | InviteRoomProps | InviteCallProps ;
281+
266282interface IInviteDialogState {
267283 targets : Member [ ] ; // array of Member objects (see interface above)
268284 filterText : string ;
@@ -283,14 +299,13 @@ interface IInviteDialogState {
283299 errorText : string ;
284300}
285301
286- export default class InviteDialog extends React . PureComponent < IInviteDialogProps , IInviteDialogState > {
302+ export default class InviteDialog extends React . PureComponent < Props , IInviteDialogState > {
287303 static defaultProps = {
288304 kind : KIND_DM ,
289305 initialText : "" ,
290306 } ;
291307
292- private closeCopiedTooltip : ( ) => void ;
293- private debounceTimer : number = null ; // actually number because we're in the browser
308+ private debounceTimer : number | null = null ; // actually number because we're in the browser
294309 private editorRef = createRef < HTMLInputElement > ( ) ;
295310 private numberEntryFieldRef : React . RefObject < Field > = createRef ( ) ;
296311 private unmounted = false ;
@@ -316,7 +331,7 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
316331
317332 this . state = {
318333 targets : [ ] , // array of Member objects (see interface above)
319- filterText : this . props . initialText ,
334+ filterText : this . props . initialText || "" ,
320335 recents : InviteDialog . buildRecents ( alreadyInvited ) ,
321336 numRecentsShown : INITIAL_ROOMS_SHOWN ,
322337 suggestions : this . buildSuggestions ( alreadyInvited ) ,
@@ -343,9 +358,6 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
343358
344359 componentWillUnmount ( ) {
345360 this . unmounted = true ;
346- // if the Copied tooltip is open then get rid of it, there are ways to close the modal which wouldn't close
347- // the tooltip otherwise, such as pressing Escape or clicking X really quickly
348- if ( this . closeCopiedTooltip ) this . closeCopiedTooltip ( ) ;
349361 }
350362
351363 private onConsultFirstChange = ( ev ) => {
@@ -466,6 +478,7 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
466478 } ;
467479
468480 private inviteUsers = async ( ) => {
481+ if ( this . props . kind !== KIND_INVITE ) return ;
469482 this . setState ( { busy : true } ) ;
470483 this . convertFilter ( ) ;
471484 const targets = this . convertFilter ( ) ;
@@ -499,6 +512,7 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
499512 } ;
500513
501514 private transferCall = async ( ) => {
515+ if ( this . props . kind !== KIND_CALL_TRANSFER ) return ;
502516 if ( this . state . currentTabId == TabId . UserDirectory ) {
503517 this . convertFilter ( ) ;
504518 const targets = this . convertFilter ( ) ;
@@ -565,7 +579,7 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
565579 this . props . onFinished ( false ) ;
566580 } ;
567581
568- private updateSuggestions = async ( term ) => {
582+ private updateSuggestions = async ( term : string ) => {
569583 MatrixClientPeg . get ( ) . searchUserDirectory ( { term } ) . then ( async r => {
570584 if ( term !== this . state . filterText ) {
571585 // Discard the results - we were probably too slow on the server-side to make
@@ -595,13 +609,17 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
595609 logger . warn ( "Non-fatal error trying to make an invite for a user ID" ) ;
596610 logger . warn ( e ) ;
597611
598- // Add a result anyways, just without a profile. We stick it at the
599- // top so it is most obviously presented to the user.
600- r . results . splice ( 0 , 0 , {
601- user_id : term ,
602- display_name : term ,
603- avatar_url : null ,
604- } ) ;
612+ // Reuse logic from Permalinks as a basic MXID validity check
613+ const serverName = getServerName ( term ) ;
614+ const domain = getHostnameFromMatrixServerName ( serverName ) ;
615+ if ( domain ) {
616+ // Add a result anyways, just without a profile. We stick it at the
617+ // top so it is most obviously presented to the user.
618+ r . results . splice ( 0 , 0 , {
619+ user_id : term ,
620+ display_name : term ,
621+ } ) ;
622+ }
605623 }
606624 }
607625
@@ -940,7 +958,7 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
940958 disabled = { this . state . busy || ( this . props . kind == KIND_CALL_TRANSFER && this . state . targets . length > 0 ) }
941959 autoComplete = "off"
942960 placeholder = { hasPlaceholder ? _t ( "Search" ) : null }
943- data-test-id = "invite-dialog-input"
961+ data-testid = "invite-dialog-input"
944962 />
945963 ) ;
946964 return (
@@ -1107,14 +1125,15 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
11071125 </ CopyableText >
11081126 </ div > ;
11091127 } else if ( this . props . kind === KIND_INVITE ) {
1110- const room = MatrixClientPeg . get ( ) ?. getRoom ( this . props . roomId ) ;
1128+ const roomId = this . props . roomId ;
1129+ const room = MatrixClientPeg . get ( ) ?. getRoom ( roomId ) ;
11111130 const isSpace = room ?. isSpaceRoom ( ) ;
11121131 title = isSpace
11131132 ? _t ( "Invite to %(spaceName)s" , {
1114- spaceName : room . name || _t ( "Unnamed Space" ) ,
1133+ spaceName : room ? .name || _t ( "Unnamed Space" ) ,
11151134 } )
11161135 : _t ( "Invite to %(roomName)s" , {
1117- roomName : room . name || _t ( "Unnamed Room" ) ,
1136+ roomName : room ? .name || _t ( "Unnamed Room" ) ,
11181137 } ) ;
11191138
11201139 let helpTextUntranslated ;
@@ -1140,14 +1159,14 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
11401159 userId : ( ) =>
11411160 < a href = { makeUserPermalink ( userId ) } rel = "noreferrer noopener" target = "_blank" > { userId } </ a > ,
11421161 a : ( sub ) =>
1143- < a href = { makeRoomPermalink ( this . props . roomId ) } rel = "noreferrer noopener" target = "_blank" > { sub } </ a > ,
1162+ < a href = { makeRoomPermalink ( roomId ) } rel = "noreferrer noopener" target = "_blank" > { sub } </ a > ,
11441163 } ) ;
11451164
11461165 buttonText = _t ( "Invite" ) ;
11471166 goButtonFn = this . inviteUsers ;
11481167
11491168 if ( cli . isRoomEncrypted ( this . props . roomId ) ) {
1150- const room = cli . getRoom ( this . props . roomId ) ;
1169+ const room = cli . getRoom ( this . props . roomId ) ! ;
11511170 const visibilityEvent = room . currentState . getStateEvents (
11521171 "m.room.history_visibility" , "" ,
11531172 ) ;
@@ -1185,8 +1204,6 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
11851204 { _t ( "Transfer" ) }
11861205 </ AccessibleButton >
11871206 </ div > ;
1188- } else {
1189- logger . error ( "Unknown kind of InviteDialog: " + this . props . kind ) ;
11901207 }
11911208
11921209 const goButton = this . props . kind == KIND_CALL_TRANSFER ? null : < AccessibleButton
0 commit comments