Apply some actual typescripting to this file

pull/21833/head
Michael Telatynski 2021-05-26 16:47:46 +01:00
parent 3f10279e15
commit 60d161caf5
1 changed files with 96 additions and 99 deletions

View File

@ -47,6 +47,8 @@ import { MatrixCall } from 'matrix-js-sdk/src/webrtc/call';
import {replaceableComponent} from "../../../utils/replaceableComponent"; import {replaceableComponent} from "../../../utils/replaceableComponent";
import {mediaFromMxc} from "../../../customisations/Media"; import {mediaFromMxc} from "../../../customisations/Media";
import {getAddressType} from "../../../UserAddress"; import {getAddressType} from "../../../UserAddress";
import BaseAvatar from '../avatars/BaseAvatar';
import AccessibleButton from '../elements/AccessibleButton';
// we have a number of types defined from the Matrix spec which can't reasonably be altered here. // we have a number of types defined from the Matrix spec which can't reasonably be altered here.
/* eslint-disable camelcase */ /* eslint-disable camelcase */
@ -61,43 +63,41 @@ const INCREMENT_ROOMS_SHOWN = 5; // Number of rooms to add when 'show more' is c
// This is the interface that is expected by various components in this file. It is a bit // 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 // awkward because it also matches the RoomMember class from the js-sdk with some extra support
// for 3PIDs/email addresses. // for 3PIDs/email addresses.
// abstract class Member {
// XXX: We should use TypeScript interfaces instead of this weird "abstract" class.
class Member {
/** /**
* The display name of this Member. For users this should be their profile's display * 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). * 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"); } public abstract get name(): string;
/** /**
* The ID of this Member. For users this should be their user ID. For 3PIDs this should * The ID of this Member. For users this should be their user ID. For 3PIDs this should
* be the 3PID address (email). * be the 3PID address (email).
*/ */
get userId(): string { throw new Error("Member class not implemented"); } public abstract get userId(): string;
/** /**
* Gets the MXC URL of this Member's avatar. For users this should be their profile's * 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. * avatar MXC URL or null if none set. For 3PIDs this should always be null.
*/ */
getMxcAvatarUrl(): string { throw new Error("Member class not implemented"); } public abstract getMxcAvatarUrl(): string;
} }
class DirectoryMember extends Member { class DirectoryMember extends Member {
_userId: string; private readonly _userId: string;
_displayName: string; private readonly displayName: string;
_avatarUrl: string; private readonly avatarUrl: string;
constructor(userDirResult: {user_id: string, display_name: string, avatar_url: string}) { constructor(userDirResult: {user_id: string, display_name: string, avatar_url: string}) {
super(); super();
this._userId = userDirResult.user_id; this._userId = userDirResult.user_id;
this._displayName = userDirResult.display_name; this.displayName = userDirResult.display_name;
this._avatarUrl = userDirResult.avatar_url; this.avatarUrl = userDirResult.avatar_url;
} }
// These next class members are for the Member interface // These next class members are for the Member interface
get name(): string { get name(): string {
return this._displayName || this._userId; return this.displayName || this._userId;
} }
get userId(): string { get userId(): string {
@ -105,32 +105,32 @@ class DirectoryMember extends Member {
} }
getMxcAvatarUrl(): string { getMxcAvatarUrl(): string {
return this._avatarUrl; return this.avatarUrl;
} }
} }
class ThreepidMember extends Member { class ThreepidMember extends Member {
_id: string; private readonly id: string;
constructor(id: string) { constructor(id: string) {
super(); super();
this._id = id; this.id = id;
} }
// This is a getter that would be falsey on all other implementations. Until we have // This is a getter that would be falsey on all other implementations. Until we have
// better type support in the react-sdk we can use this trick to determine the kind // better type support in the react-sdk we can use this trick to determine the kind
// of 3PID we're dealing with, if any. // of 3PID we're dealing with, if any.
get isEmail(): boolean { get isEmail(): boolean {
return this._id.includes('@'); return this.id.includes('@');
} }
// These next class members are for the Member interface // These next class members are for the Member interface
get name(): string { get name(): string {
return this._id; return this.id;
} }
get userId(): string { get userId(): string {
return this._id; return this.id;
} }
getMxcAvatarUrl(): string { getMxcAvatarUrl(): string {
@ -140,11 +140,11 @@ class ThreepidMember extends Member {
interface IDMUserTileProps { interface IDMUserTileProps {
member: RoomMember; member: RoomMember;
onRemove: (RoomMember) => any; onRemove(member: RoomMember): void;
} }
class DMUserTile extends React.PureComponent<IDMUserTileProps> { class DMUserTile extends React.PureComponent<IDMUserTileProps> {
_onRemove = (e) => { private onRemove = (e) => {
// Stop the browser from highlighting text // Stop the browser from highlighting text
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
@ -153,9 +153,6 @@ class DMUserTile extends React.PureComponent<IDMUserTileProps> {
}; };
render() { render() {
const BaseAvatar = sdk.getComponent("views.avatars.BaseAvatar");
const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
const avatarSize = 20; const avatarSize = 20;
const avatar = this.props.member.isEmail const avatar = this.props.member.isEmail
? <img ? <img
@ -177,7 +174,7 @@ class DMUserTile extends React.PureComponent<IDMUserTileProps> {
closeButton = ( closeButton = (
<AccessibleButton <AccessibleButton
className='mx_InviteDialog_userTile_remove' className='mx_InviteDialog_userTile_remove'
onClick={this._onRemove} onClick={this.onRemove}
> >
<img src={require("../../../../res/img/icon-pill-remove.svg")} <img src={require("../../../../res/img/icon-pill-remove.svg")}
alt={_t('Remove')} width={8} height={8} alt={_t('Remove')} width={8} height={8}
@ -201,13 +198,13 @@ class DMUserTile extends React.PureComponent<IDMUserTileProps> {
interface IDMRoomTileProps { interface IDMRoomTileProps {
member: RoomMember; member: RoomMember;
lastActiveTs: number; lastActiveTs: number;
onToggle: (RoomMember) => any; onToggle(member: RoomMember): void;
highlightWord: string; highlightWord: string;
isSelected: boolean; isSelected: boolean;
} }
class DMRoomTile extends React.PureComponent<IDMRoomTileProps> { class DMRoomTile extends React.PureComponent<IDMRoomTileProps> {
_onClick = (e) => { private onClick = (e) => {
// Stop the browser from highlighting text // Stop the browser from highlighting text
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
@ -215,7 +212,7 @@ class DMRoomTile extends React.PureComponent<IDMRoomTileProps> {
this.props.onToggle(this.props.member); this.props.onToggle(this.props.member);
}; };
_highlightName(str: string) { private highlightName(str: string) {
if (!this.props.highlightWord) return str; if (!this.props.highlightWord) return str;
// We convert things to lowercase for index searching, but pull substrings from // We convert things to lowercase for index searching, but pull substrings from
@ -252,8 +249,6 @@ class DMRoomTile extends React.PureComponent<IDMRoomTileProps> {
} }
render() { render() {
const BaseAvatar = sdk.getComponent("views.avatars.BaseAvatar");
let timestamp = null; let timestamp = null;
if (this.props.lastActiveTs) { if (this.props.lastActiveTs) {
const humanTs = humanizeTime(this.props.lastActiveTs); const humanTs = humanizeTime(this.props.lastActiveTs);
@ -291,13 +286,13 @@ class DMRoomTile extends React.PureComponent<IDMRoomTileProps> {
const caption = this.props.member.isEmail const caption = this.props.member.isEmail
? _t("Invite by email") ? _t("Invite by email")
: this._highlightName(this.props.member.userId); : this.highlightName(this.props.member.userId);
return ( return (
<div className='mx_InviteDialog_roomTile' onClick={this._onClick}> <div className='mx_InviteDialog_roomTile' onClick={this.onClick}>
{stackedAvatar} {stackedAvatar}
<span className="mx_InviteDialog_roomTile_nameStack"> <span className="mx_InviteDialog_roomTile_nameStack">
<div className='mx_InviteDialog_roomTile_name'>{this._highlightName(this.props.member.name)}</div> <div className='mx_InviteDialog_roomTile_name'>{this.highlightName(this.props.member.name)}</div>
<div className='mx_InviteDialog_roomTile_userId'>{caption}</div> <div className='mx_InviteDialog_roomTile_userId'>{caption}</div>
</span> </span>
{timestamp} {timestamp}
@ -308,7 +303,7 @@ class DMRoomTile extends React.PureComponent<IDMRoomTileProps> {
interface IInviteDialogProps { interface IInviteDialogProps {
// Takes an array of user IDs/emails to invite. // Takes an array of user IDs/emails to invite.
onFinished: (toInvite?: string[]) => any; onFinished: (toInvite?: string[]) => void;
// The kind of invite being performed. Assumed to be KIND_DM if // The kind of invite being performed. Assumed to be KIND_DM if
// not provided. // not provided.
@ -349,8 +344,8 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
initialText: "", initialText: "",
}; };
_debounceTimer: NodeJS.Timeout = null; // actually number because we're in the browser private debounceTimer: NodeJS.Timeout = null; // actually number because we're in the browser
_editorRef: any = null; private editorRef = createRef<HTMLInputElement>();
private unmounted = false; private unmounted = false;
constructor(props) { constructor(props) {
@ -379,7 +374,7 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
filterText: this.props.initialText, filterText: this.props.initialText,
recents: InviteDialog.buildRecents(alreadyInvited), recents: InviteDialog.buildRecents(alreadyInvited),
numRecentsShown: INITIAL_ROOMS_SHOWN, numRecentsShown: INITIAL_ROOMS_SHOWN,
suggestions: this._buildSuggestions(alreadyInvited), suggestions: this.buildSuggestions(alreadyInvited),
numSuggestionsShown: INITIAL_ROOMS_SHOWN, numSuggestionsShown: INITIAL_ROOMS_SHOWN,
serverResultsMixin: [], serverResultsMixin: [],
threepidResultsMixin: [], threepidResultsMixin: [],
@ -391,13 +386,11 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
busy: false, busy: false,
errorText: null, errorText: null,
}; };
this._editorRef = createRef();
} }
componentDidMount() { componentDidMount() {
if (this.props.initialText) { if (this.props.initialText) {
this._updateSuggestions(this.props.initialText); this.updateSuggestions(this.props.initialText);
} }
} }
@ -409,7 +402,11 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
this.setState({consultFirst: ev.target.checked}); this.setState({consultFirst: ev.target.checked});
} }
static buildRecents(excludedTargetIds: Set<string>): {userId: string, user: RoomMember, lastActive: number}[] { public static buildRecents(excludedTargetIds: Set<string>): {
userId: string,
user: RoomMember,
lastActive: number,
}[] {
const rooms = DMRoomMap.shared().getUniqueRoomsWithIndividuals(); // map of userId => js-sdk Room const rooms = DMRoomMap.shared().getUniqueRoomsWithIndividuals(); // map of userId => js-sdk Room
// Also pull in all the rooms tagged as DefaultTagID.DM so we don't miss anything. Sometimes the // Also pull in all the rooms tagged as DefaultTagID.DM so we don't miss anything. Sometimes the
@ -472,7 +469,7 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
return recents; return recents;
} }
_buildSuggestions(excludedTargetIds: Set<string>): {userId: string, user: RoomMember}[] { private buildSuggestions(excludedTargetIds: Set<string>): {userId: string, user: RoomMember}[] {
const maxConsideredMembers = 200; const maxConsideredMembers = 200;
const joinedRooms = MatrixClientPeg.get().getRooms() const joinedRooms = MatrixClientPeg.get().getRooms()
.filter(r => r.getMyMembership() === 'join' && r.getJoinedMemberCount() <= maxConsideredMembers); .filter(r => r.getMyMembership() === 'join' && r.getJoinedMemberCount() <= maxConsideredMembers);
@ -590,7 +587,7 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
return members.map(m => ({userId: m.member.userId, user: m.member})); return members.map(m => ({userId: m.member.userId, user: m.member}));
} }
_shouldAbortAfterInviteError(result): boolean { private shouldAbortAfterInviteError(result): boolean {
const failedUsers = Object.keys(result.states).filter(a => result.states[a] === 'error'); const failedUsers = Object.keys(result.states).filter(a => result.states[a] === 'error');
if (failedUsers.length > 0) { if (failedUsers.length > 0) {
console.log("Failed to invite users: ", result); console.log("Failed to invite users: ", result);
@ -605,7 +602,7 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
return false; return false;
} }
_convertFilter(): Member[] { private convertFilter(): Member[] {
// Check to see if there's anything to convert first // Check to see if there's anything to convert first
if (!this.state.filterText || !this.state.filterText.includes('@')) return this.state.targets || []; if (!this.state.filterText || !this.state.filterText.includes('@')) return this.state.targets || [];
@ -622,10 +619,10 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
return newTargets; return newTargets;
} }
_startDm = async () => { private startDm = async () => {
this.setState({busy: true}); this.setState({busy: true});
const client = MatrixClientPeg.get(); const client = MatrixClientPeg.get();
const targets = this._convertFilter(); const targets = this.convertFilter();
const targetIds = targets.map(t => t.userId); const targetIds = targets.map(t => t.userId);
// Check if there is already a DM with these people and reuse it if possible. // Check if there is already a DM with these people and reuse it if possible.
@ -699,11 +696,11 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
} }
}; };
_inviteUsers = async () => { private inviteUsers = async () => {
const startTime = CountlyAnalytics.getTimestamp(); const startTime = CountlyAnalytics.getTimestamp();
this.setState({busy: true}); this.setState({busy: true});
this._convertFilter(); this.convertFilter();
const targets = this._convertFilter(); const targets = this.convertFilter();
const targetIds = targets.map(t => t.userId); const targetIds = targets.map(t => t.userId);
const cli = MatrixClientPeg.get(); const cli = MatrixClientPeg.get();
@ -720,7 +717,7 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
try { try {
const result = await inviteMultipleToRoom(this.props.roomId, targetIds) const result = await inviteMultipleToRoom(this.props.roomId, targetIds)
CountlyAnalytics.instance.trackSendInvite(startTime, this.props.roomId, targetIds.length); CountlyAnalytics.instance.trackSendInvite(startTime, this.props.roomId, targetIds.length);
if (!this._shouldAbortAfterInviteError(result)) { // handles setting error message too if (!this.shouldAbortAfterInviteError(result)) { // handles setting error message too
this.props.onFinished(); this.props.onFinished();
} }
@ -754,9 +751,9 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
} }
}; };
_transferCall = async () => { private transferCall = async () => {
this._convertFilter(); this.convertFilter();
const targets = this._convertFilter(); const targets = this.convertFilter();
const targetIds = targets.map(t => t.userId); const targetIds = targets.map(t => t.userId);
if (targetIds.length > 1) { if (targetIds.length > 1) {
this.setState({ this.setState({
@ -795,26 +792,26 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
} }
}; };
_onKeyDown = (e) => { private onKeyDown = (e) => {
if (this.state.busy) return; if (this.state.busy) return;
const value = e.target.value.trim(); const value = e.target.value.trim();
const hasModifiers = e.ctrlKey || e.shiftKey || e.metaKey; const hasModifiers = e.ctrlKey || e.shiftKey || e.metaKey;
if (!value && this.state.targets.length > 0 && e.key === Key.BACKSPACE && !hasModifiers) { if (!value && this.state.targets.length > 0 && e.key === Key.BACKSPACE && !hasModifiers) {
// when the field is empty and the user hits backspace remove the right-most target // when the field is empty and the user hits backspace remove the right-most target
e.preventDefault(); e.preventDefault();
this._removeMember(this.state.targets[this.state.targets.length - 1]); this.removeMember(this.state.targets[this.state.targets.length - 1]);
} else if (value && e.key === Key.ENTER && !hasModifiers) { } else if (value && e.key === Key.ENTER && !hasModifiers) {
// when the user hits enter with something in their field try to convert it // when the user hits enter with something in their field try to convert it
e.preventDefault(); e.preventDefault();
this._convertFilter(); this.convertFilter();
} else if (value && e.key === Key.SPACE && !hasModifiers && value.includes("@") && !value.includes(" ")) { } else if (value && e.key === Key.SPACE && !hasModifiers && value.includes("@") && !value.includes(" ")) {
// when the user hits space and their input looks like an e-mail/MXID then try to convert it // when the user hits space and their input looks like an e-mail/MXID then try to convert it
e.preventDefault(); e.preventDefault();
this._convertFilter(); this.convertFilter();
} }
}; };
_updateSuggestions = async (term) => { private updateSuggestions = async (term) => {
MatrixClientPeg.get().searchUserDirectory({term}).then(async r => { MatrixClientPeg.get().searchUserDirectory({term}).then(async r => {
if (term !== this.state.filterText) { if (term !== this.state.filterText) {
// Discard the results - we were probably too slow on the server-side to make // Discard the results - we were probably too slow on the server-side to make
@ -923,30 +920,30 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
} }
}; };
_updateFilter = (e) => { private updateFilter = (e) => {
const term = e.target.value; const term = e.target.value;
this.setState({filterText: term}); this.setState({filterText: term});
// Debounce server lookups to reduce spam. We don't clear the existing server // Debounce server lookups to reduce spam. We don't clear the existing server
// results because they might still be vaguely accurate, likewise for races which // results because they might still be vaguely accurate, likewise for races which
// could happen here. // could happen here.
if (this._debounceTimer) { if (this.debounceTimer) {
clearTimeout(this._debounceTimer); clearTimeout(this.debounceTimer);
} }
this._debounceTimer = setTimeout(() => { this.debounceTimer = setTimeout(() => {
this._updateSuggestions(term); this.updateSuggestions(term);
}, 150); // 150ms debounce (human reaction time + some) }, 150); // 150ms debounce (human reaction time + some)
}; };
_showMoreRecents = () => { private showMoreRecents = () => {
this.setState({numRecentsShown: this.state.numRecentsShown + INCREMENT_ROOMS_SHOWN}); this.setState({numRecentsShown: this.state.numRecentsShown + INCREMENT_ROOMS_SHOWN});
}; };
_showMoreSuggestions = () => { private showMoreSuggestions = () => {
this.setState({numSuggestionsShown: this.state.numSuggestionsShown + INCREMENT_ROOMS_SHOWN}); this.setState({numSuggestionsShown: this.state.numSuggestionsShown + INCREMENT_ROOMS_SHOWN});
}; };
_toggleMember = (member: Member) => { private toggleMember = (member: Member) => {
if (!this.state.busy) { if (!this.state.busy) {
let filterText = this.state.filterText; let filterText = this.state.filterText;
const targets = this.state.targets.map(t => t); // cheap clone for mutation const targets = this.state.targets.map(t => t); // cheap clone for mutation
@ -959,13 +956,13 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
} }
this.setState({targets, filterText}); this.setState({targets, filterText});
if (this._editorRef && this._editorRef.current) { if (this.editorRef && this.editorRef.current) {
this._editorRef.current.focus(); this.editorRef.current.focus();
} }
} }
}; };
_removeMember = (member: Member) => { private removeMember = (member: Member) => {
const targets = this.state.targets.map(t => t); // cheap clone for mutation const targets = this.state.targets.map(t => t); // cheap clone for mutation
const idx = targets.indexOf(member); const idx = targets.indexOf(member);
if (idx >= 0) { if (idx >= 0) {
@ -973,12 +970,12 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
this.setState({targets}); this.setState({targets});
} }
if (this._editorRef && this._editorRef.current) { if (this.editorRef && this.editorRef.current) {
this._editorRef.current.focus(); this.editorRef.current.focus();
} }
}; };
_onPaste = async (e) => { private onPaste = async (e) => {
if (this.state.filterText) { if (this.state.filterText) {
// if the user has already typed something, just let them // if the user has already typed something, just let them
// paste normally. // paste normally.
@ -1049,17 +1046,17 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
this.setState({targets: [...this.state.targets, ...toAdd]}); this.setState({targets: [...this.state.targets, ...toAdd]});
}; };
_onClickInputArea = (e) => { private onClickInputArea = (e) => {
// Stop the browser from highlighting text // Stop the browser from highlighting text
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
if (this._editorRef && this._editorRef.current) { if (this.editorRef && this.editorRef.current) {
this._editorRef.current.focus(); this.editorRef.current.focus();
} }
}; };
_onUseDefaultIdentityServerClick = (e) => { private onUseDefaultIdentityServerClick = (e) => {
e.preventDefault(); e.preventDefault();
// Update the IS in account data. Actually using it may trigger terms. // Update the IS in account data. Actually using it may trigger terms.
@ -1068,21 +1065,21 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
this.setState({canUseIdentityServer: true, tryingIdentityServer: false}); this.setState({canUseIdentityServer: true, tryingIdentityServer: false});
}; };
_onManageSettingsClick = (e) => { private onManageSettingsClick = (e) => {
e.preventDefault(); e.preventDefault();
dis.fire(Action.ViewUserSettings); dis.fire(Action.ViewUserSettings);
this.props.onFinished(); this.props.onFinished();
}; };
_onCommunityInviteClick = (e) => { private onCommunityInviteClick = (e) => {
this.props.onFinished(); this.props.onFinished();
showCommunityInviteDialog(CommunityPrototypeStore.instance.getSelectedCommunityId()); showCommunityInviteDialog(CommunityPrototypeStore.instance.getSelectedCommunityId());
}; };
_renderSection(kind: "recents"|"suggestions") { private renderSection(kind: "recents"|"suggestions") {
let sourceMembers = kind === 'recents' ? this.state.recents : this.state.suggestions; let sourceMembers = kind === 'recents' ? this.state.recents : this.state.suggestions;
let showNum = kind === 'recents' ? this.state.numRecentsShown : this.state.numSuggestionsShown; let showNum = kind === 'recents' ? this.state.numRecentsShown : this.state.numSuggestionsShown;
const showMoreFn = kind === 'recents' ? this._showMoreRecents.bind(this) : this._showMoreSuggestions.bind(this); const showMoreFn = kind === 'recents' ? this.showMoreRecents.bind(this) : this.showMoreSuggestions.bind(this);
const lastActive = (m) => kind === 'recents' ? m.lastActive : null; const lastActive = (m) => kind === 'recents' ? m.lastActive : null;
let sectionName = kind === 'recents' ? _t("Recent Conversations") : _t("Suggestions"); let sectionName = kind === 'recents' ? _t("Recent Conversations") : _t("Suggestions");
let sectionSubname = null; let sectionSubname = null;
@ -1162,7 +1159,7 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
member={r.user} member={r.user}
lastActiveTs={lastActive(r)} lastActiveTs={lastActive(r)}
key={r.userId} key={r.userId}
onToggle={this._toggleMember} onToggle={this.toggleMember}
highlightWord={this.state.filterText} highlightWord={this.state.filterText}
isSelected={this.state.targets.some(t => t.userId === r.userId)} isSelected={this.state.targets.some(t => t.userId === r.userId)}
/> />
@ -1177,32 +1174,32 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
); );
} }
_renderEditor() { private renderEditor() {
const targets = this.state.targets.map(t => ( const targets = this.state.targets.map(t => (
<DMUserTile member={t} onRemove={!this.state.busy && this._removeMember} key={t.userId} /> <DMUserTile member={t} onRemove={!this.state.busy && this.removeMember} key={t.userId} />
)); ));
const input = ( const input = (
<input <input
type="text" type="text"
onKeyDown={this._onKeyDown} onKeyDown={this.onKeyDown}
onChange={this._updateFilter} onChange={this.updateFilter}
value={this.state.filterText} value={this.state.filterText}
ref={this._editorRef} ref={this.editorRef}
onPaste={this._onPaste} onPaste={this.onPaste}
autoFocus={true} autoFocus={true}
disabled={this.state.busy} disabled={this.state.busy}
autoComplete="off" autoComplete="off"
/> />
); );
return ( return (
<div className='mx_InviteDialog_editor' onClick={this._onClickInputArea}> <div className='mx_InviteDialog_editor' onClick={this.onClickInputArea}>
{targets} {targets}
{input} {input}
</div> </div>
); );
} }
_renderIdentityServerWarning() { private renderIdentityServerWarning() {
if (!this.state.tryingIdentityServer || this.state.canUseIdentityServer || if (!this.state.tryingIdentityServer || this.state.canUseIdentityServer ||
!SettingsStore.getValue(UIFeature.IdentityServer) !SettingsStore.getValue(UIFeature.IdentityServer)
) { ) {
@ -1220,8 +1217,8 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
defaultIdentityServerName: abbreviateUrl(defaultIdentityServerUrl), defaultIdentityServerName: abbreviateUrl(defaultIdentityServerUrl),
}, },
{ {
default: sub => <a href="#" onClick={this._onUseDefaultIdentityServerClick}>{sub}</a>, default: sub => <a href="#" onClick={this.onUseDefaultIdentityServerClick}>{sub}</a>,
settings: sub => <a href="#" onClick={this._onManageSettingsClick}>{sub}</a>, settings: sub => <a href="#" onClick={this.onManageSettingsClick}>{sub}</a>,
}, },
)}</div> )}</div>
); );
@ -1231,7 +1228,7 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
"Use an identity server to invite by email. " + "Use an identity server to invite by email. " +
"Manage in <settings>Settings</settings>.", "Manage in <settings>Settings</settings>.",
{}, { {}, {
settings: sub => <a href="#" onClick={this._onManageSettingsClick}>{sub}</a>, settings: sub => <a href="#" onClick={this.onManageSettingsClick}>{sub}</a>,
}, },
)}</div> )}</div>
); );
@ -1304,7 +1301,7 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
return ( return (
<AccessibleButton <AccessibleButton
kind="link" kind="link"
onClick={this._onCommunityInviteClick} onClick={this.onCommunityInviteClick}
>{sub}</AccessibleButton> >{sub}</AccessibleButton>
); );
}, },
@ -1315,7 +1312,7 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
</React.Fragment>; </React.Fragment>;
} }
buttonText = _t("Go"); buttonText = _t("Go");
goButtonFn = this._startDm; goButtonFn = this.startDm;
} else if (this.props.kind === KIND_INVITE) { } else if (this.props.kind === KIND_INVITE) {
const room = MatrixClientPeg.get()?.getRoom(this.props.roomId); const room = MatrixClientPeg.get()?.getRoom(this.props.roomId);
const isSpace = SettingsStore.getValue("feature_spaces") && room?.isSpaceRoom(); const isSpace = SettingsStore.getValue("feature_spaces") && room?.isSpaceRoom();
@ -1354,7 +1351,7 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
}); });
buttonText = _t("Invite"); buttonText = _t("Invite");
goButtonFn = this._inviteUsers; goButtonFn = this.inviteUsers;
if (cli.isRoomEncrypted(this.props.roomId)) { if (cli.isRoomEncrypted(this.props.roomId)) {
const room = cli.getRoom(this.props.roomId); const room = cli.getRoom(this.props.roomId);
@ -1376,7 +1373,7 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
} else if (this.props.kind === KIND_CALL_TRANSFER) { } else if (this.props.kind === KIND_CALL_TRANSFER) {
title = _t("Transfer"); title = _t("Transfer");
buttonText = _t("Transfer"); buttonText = _t("Transfer");
goButtonFn = this._transferCall; goButtonFn = this.transferCall;
consultSection = <div> consultSection = <div>
<label> <label>
<input type="checkbox" checked={this.state.consultFirst} onChange={this.onConsultFirstChange} /> <input type="checkbox" checked={this.state.consultFirst} onChange={this.onConsultFirstChange} />
@ -1399,7 +1396,7 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
<div className='mx_InviteDialog_content'> <div className='mx_InviteDialog_content'>
<p className='mx_InviteDialog_helpText'>{helpText}</p> <p className='mx_InviteDialog_helpText'>{helpText}</p>
<div className='mx_InviteDialog_addressBar'> <div className='mx_InviteDialog_addressBar'>
{this._renderEditor()} {this.renderEditor()}
<div className='mx_InviteDialog_buttonAndSpinner'> <div className='mx_InviteDialog_buttonAndSpinner'>
<AccessibleButton <AccessibleButton
kind="primary" kind="primary"
@ -1413,11 +1410,11 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
</div> </div>
</div> </div>
{keySharingWarning} {keySharingWarning}
{this._renderIdentityServerWarning()} {this.renderIdentityServerWarning()}
<div className='error'>{this.state.errorText}</div> <div className='error'>{this.state.errorText}</div>
<div className='mx_InviteDialog_userSections'> <div className='mx_InviteDialog_userSections'>
{this._renderSection('recents')} {this.renderSection('recents')}
{this._renderSection('suggestions')} {this.renderSection('suggestions')}
</div> </div>
{consultSection} {consultSection}
</div> </div>