Convert AddressPickerDialog to TS
Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com>
This commit is contained in:
parent
156901ce62
commit
a747bbbae7
1 changed files with 112 additions and 103 deletions
|
@ -18,7 +18,6 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { createRef } from 'react';
|
import React, { createRef } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { sleep } from "matrix-js-sdk/src/utils";
|
import { sleep } from "matrix-js-sdk/src/utils";
|
||||||
|
|
||||||
import { _t, _td } from '../../../languageHandler';
|
import { _t, _td } from '../../../languageHandler';
|
||||||
|
@ -34,6 +33,7 @@ import { abbreviateUrl } from '../../../utils/UrlUtils';
|
||||||
import { Key } from "../../../Keyboard";
|
import { Key } from "../../../Keyboard";
|
||||||
import { Action } from "../../../dispatcher/actions";
|
import { Action } from "../../../dispatcher/actions";
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
|
import AddressSelector from '../elements/AddressSelector';
|
||||||
|
|
||||||
const TRUNCATE_QUERY_LIST = 40;
|
const TRUNCATE_QUERY_LIST = 40;
|
||||||
const QUERY_USER_DIRECTORY_DEBOUNCE_MS = 200;
|
const QUERY_USER_DIRECTORY_DEBOUNCE_MS = 200;
|
||||||
|
@ -44,27 +44,54 @@ const addressTypeName = {
|
||||||
'email': _td("email address"),
|
'email': _td("email address"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
title: string;
|
||||||
|
description?: JSX.Element;
|
||||||
|
// Extra node inserted after picker input, dropdown and errors
|
||||||
|
extraNode?: JSX.Element;
|
||||||
|
value?: string;
|
||||||
|
placeholder?: ((validAddressTypes: any) => string) | string;
|
||||||
|
roomId?: string;
|
||||||
|
button?: string;
|
||||||
|
focus?: boolean;
|
||||||
|
validAddressTypes?; // FIXME: UserAddressType should be an interface
|
||||||
|
onFinished: (success: boolean, list?) => void; // FIXME: UserAddressType should be an interface
|
||||||
|
groupId?: string;
|
||||||
|
// The type of entity to search for. Default: 'user'.
|
||||||
|
pickerType?: 'user' | 'room';
|
||||||
|
// Whether the current user should be included in the addresses returned. Only
|
||||||
|
// applicable when pickerType is `user`. Default: false.
|
||||||
|
includeSelf?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IState {
|
||||||
|
// Whether to show an error message because of an invalid address
|
||||||
|
invalidAddressError: boolean;
|
||||||
|
// List of UserAddressType objects representing
|
||||||
|
// the list of addresses we're going to invite
|
||||||
|
selectedList; // FIXME: UserAddressType should be an interface
|
||||||
|
// Whether a search is ongoing
|
||||||
|
busy: boolean;
|
||||||
|
// An error message generated during the user directory search
|
||||||
|
searchError: string;
|
||||||
|
// Whether the server supports the user_directory API
|
||||||
|
serverSupportsUserDirectory: boolean;
|
||||||
|
// The query being searched for
|
||||||
|
query: string;
|
||||||
|
// List of UserAddressType objects representing the set of
|
||||||
|
// auto-completion results for the current search query.
|
||||||
|
suggestedList; // FIXME: UserAddressType should be an interface
|
||||||
|
// List of address types initialised from props, but may change while the
|
||||||
|
// dialog is open and represents the supported list of address types at this time.
|
||||||
|
validAddressTypes;
|
||||||
|
}
|
||||||
|
|
||||||
@replaceableComponent("views.dialogs.AddressPickerDialog")
|
@replaceableComponent("views.dialogs.AddressPickerDialog")
|
||||||
export default class AddressPickerDialog extends React.Component {
|
export default class AddressPickerDialog extends React.Component<IProps, IState> {
|
||||||
static propTypes = {
|
private textinput = createRef<HTMLTextAreaElement>();
|
||||||
title: PropTypes.string.isRequired,
|
private addressSelector = createRef<AddressSelector>();
|
||||||
description: PropTypes.node,
|
private queryChangedDebouncer: NodeJS.Timeout;
|
||||||
// Extra node inserted after picker input, dropdown and errors
|
private cancelThreepidLookup: () => void;
|
||||||
extraNode: PropTypes.node,
|
|
||||||
value: PropTypes.string,
|
|
||||||
placeholder: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
|
|
||||||
roomId: PropTypes.string,
|
|
||||||
button: PropTypes.string,
|
|
||||||
focus: PropTypes.bool,
|
|
||||||
validAddressTypes: PropTypes.arrayOf(PropTypes.oneOf(addressTypes)),
|
|
||||||
onFinished: PropTypes.func.isRequired,
|
|
||||||
groupId: PropTypes.string,
|
|
||||||
// The type of entity to search for. Default: 'user'.
|
|
||||||
pickerType: PropTypes.oneOf(['user', 'room']),
|
|
||||||
// Whether the current user should be included in the addresses returned. Only
|
|
||||||
// applicable when pickerType is `user`. Default: false.
|
|
||||||
includeSelf: PropTypes.bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
value: "",
|
value: "",
|
||||||
|
@ -74,11 +101,9 @@ export default class AddressPickerDialog extends React.Component {
|
||||||
includeSelf: false,
|
includeSelf: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props: IProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this._textinput = createRef();
|
|
||||||
|
|
||||||
let validAddressTypes = this.props.validAddressTypes;
|
let validAddressTypes = this.props.validAddressTypes;
|
||||||
// Remove email from validAddressTypes if no IS is configured. It may be added at a later stage by the user
|
// Remove email from validAddressTypes if no IS is configured. It may be added at a later stage by the user
|
||||||
if (!MatrixClientPeg.get().getIdentityServerUrl() && validAddressTypes.includes("email")) {
|
if (!MatrixClientPeg.get().getIdentityServerUrl() && validAddressTypes.includes("email")) {
|
||||||
|
@ -86,24 +111,13 @@ export default class AddressPickerDialog extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
// Whether to show an error message because of an invalid address
|
|
||||||
invalidAddressError: false,
|
invalidAddressError: false,
|
||||||
// List of UserAddressType objects representing
|
|
||||||
// the list of addresses we're going to invite
|
|
||||||
selectedList: [],
|
selectedList: [],
|
||||||
// Whether a search is ongoing
|
|
||||||
busy: false,
|
busy: false,
|
||||||
// An error message generated during the user directory search
|
|
||||||
searchError: null,
|
searchError: null,
|
||||||
// Whether the server supports the user_directory API
|
|
||||||
serverSupportsUserDirectory: true,
|
serverSupportsUserDirectory: true,
|
||||||
// The query being searched for
|
|
||||||
query: "",
|
query: "",
|
||||||
// List of UserAddressType objects representing the set of
|
|
||||||
// auto-completion results for the current search query.
|
|
||||||
suggestedList: [],
|
suggestedList: [],
|
||||||
// List of address types initialised from props, but may change while the
|
|
||||||
// dialog is open and represents the supported list of address types at this time.
|
|
||||||
validAddressTypes,
|
validAddressTypes,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -111,11 +125,11 @@ export default class AddressPickerDialog extends React.Component {
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
if (this.props.focus) {
|
if (this.props.focus) {
|
||||||
// Set the cursor at the end of the text input
|
// Set the cursor at the end of the text input
|
||||||
this._textinput.current.value = this.props.value;
|
this.textinput.current.value = this.props.value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getPlaceholder() {
|
private getPlaceholder(): string {
|
||||||
const { placeholder } = this.props;
|
const { placeholder } = this.props;
|
||||||
if (typeof placeholder === "string") {
|
if (typeof placeholder === "string") {
|
||||||
return placeholder;
|
return placeholder;
|
||||||
|
@ -124,23 +138,23 @@ export default class AddressPickerDialog extends React.Component {
|
||||||
return placeholder(this.state.validAddressTypes);
|
return placeholder(this.state.validAddressTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
onButtonClick = () => {
|
private onButtonClick = (): void => {
|
||||||
let selectedList = this.state.selectedList.slice();
|
let selectedList = this.state.selectedList.slice();
|
||||||
// Check the text input field to see if user has an unconverted address
|
// 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 selectedList
|
// If there is and it's valid add it to the local selectedList
|
||||||
if (this._textinput.current.value !== '') {
|
if (this.textinput.current.value !== '') {
|
||||||
selectedList = this._addAddressesToList([this._textinput.current.value]);
|
selectedList = this.addAddressesToList([this.textinput.current.value]);
|
||||||
if (selectedList === null) return;
|
if (selectedList === null) return;
|
||||||
}
|
}
|
||||||
this.props.onFinished(true, selectedList);
|
this.props.onFinished(true, selectedList);
|
||||||
};
|
};
|
||||||
|
|
||||||
onCancel = () => {
|
private onCancel = (): void => {
|
||||||
this.props.onFinished(false);
|
this.props.onFinished(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
onKeyDown = e => {
|
private onKeyDown = (e: React.KeyboardEvent): void => {
|
||||||
const textInput = this._textinput.current ? this._textinput.current.value : undefined;
|
const textInput = this.textinput.current ? this.textinput.current.value : undefined;
|
||||||
|
|
||||||
if (e.key === Key.ESCAPE) {
|
if (e.key === Key.ESCAPE) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
@ -149,15 +163,15 @@ export default class AddressPickerDialog extends React.Component {
|
||||||
} else if (e.key === Key.ARROW_UP) {
|
} else if (e.key === Key.ARROW_UP) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (this.addressSelector) this.addressSelector.moveSelectionUp();
|
if (this.addressSelector.current) this.addressSelector.current.moveSelectionUp();
|
||||||
} else if (e.key === Key.ARROW_DOWN) {
|
} else if (e.key === Key.ARROW_DOWN) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (this.addressSelector) this.addressSelector.moveSelectionDown();
|
if (this.addressSelector.current) this.addressSelector.current.moveSelectionDown();
|
||||||
} else if (this.state.suggestedList.length > 0 && [Key.COMMA, Key.ENTER, Key.TAB].includes(e.key)) {
|
} else if (this.state.suggestedList.length > 0 && [Key.COMMA, Key.ENTER, Key.TAB].includes(e.key)) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (this.addressSelector) this.addressSelector.chooseSelection();
|
if (this.addressSelector.current) this.addressSelector.current.chooseSelection();
|
||||||
} else if (textInput.length === 0 && this.state.selectedList.length && e.key === Key.BACKSPACE) {
|
} else if (textInput.length === 0 && this.state.selectedList.length && e.key === Key.BACKSPACE) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
@ -169,17 +183,17 @@ export default class AddressPickerDialog extends React.Component {
|
||||||
// if there's nothing in the input box, submit the form
|
// if there's nothing in the input box, submit the form
|
||||||
this.onButtonClick();
|
this.onButtonClick();
|
||||||
} else {
|
} else {
|
||||||
this._addAddressesToList([textInput]);
|
this.addAddressesToList([textInput]);
|
||||||
}
|
}
|
||||||
} else if (textInput && (e.key === Key.COMMA || e.key === Key.TAB)) {
|
} else if (textInput && (e.key === Key.COMMA || e.key === Key.TAB)) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this._addAddressesToList([textInput]);
|
this.addAddressesToList([textInput]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onQueryChanged = ev => {
|
private onQueryChanged = (ev: React.ChangeEvent): void => {
|
||||||
const query = ev.target.value;
|
const query = (ev.target as HTMLTextAreaElement).value;
|
||||||
if (this.queryChangedDebouncer) {
|
if (this.queryChangedDebouncer) {
|
||||||
clearTimeout(this.queryChangedDebouncer);
|
clearTimeout(this.queryChangedDebouncer);
|
||||||
}
|
}
|
||||||
|
@ -188,17 +202,17 @@ export default class AddressPickerDialog extends React.Component {
|
||||||
this.queryChangedDebouncer = setTimeout(() => {
|
this.queryChangedDebouncer = setTimeout(() => {
|
||||||
if (this.props.pickerType === 'user') {
|
if (this.props.pickerType === 'user') {
|
||||||
if (this.props.groupId) {
|
if (this.props.groupId) {
|
||||||
this._doNaiveGroupSearch(query);
|
this.doNaiveGroupSearch(query);
|
||||||
} else if (this.state.serverSupportsUserDirectory) {
|
} else if (this.state.serverSupportsUserDirectory) {
|
||||||
this._doUserDirectorySearch(query);
|
this.doUserDirectorySearch(query);
|
||||||
} else {
|
} else {
|
||||||
this._doLocalSearch(query);
|
this.doLocalSearch(query);
|
||||||
}
|
}
|
||||||
} else if (this.props.pickerType === 'room') {
|
} else if (this.props.pickerType === 'room') {
|
||||||
if (this.props.groupId) {
|
if (this.props.groupId) {
|
||||||
this._doNaiveGroupRoomSearch(query);
|
this.doNaiveGroupRoomSearch(query);
|
||||||
} else {
|
} else {
|
||||||
this._doRoomSearch(query);
|
this.doRoomSearch(query);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.error('Unknown pickerType', this.props.pickerType);
|
console.error('Unknown pickerType', this.props.pickerType);
|
||||||
|
@ -213,7 +227,7 @@ export default class AddressPickerDialog extends React.Component {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onDismissed = index => () => {
|
private onDismissed = (index: number) => () => {
|
||||||
const selectedList = this.state.selectedList.slice();
|
const selectedList = this.state.selectedList.slice();
|
||||||
selectedList.splice(index, 1);
|
selectedList.splice(index, 1);
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -221,25 +235,21 @@ export default class AddressPickerDialog extends React.Component {
|
||||||
suggestedList: [],
|
suggestedList: [],
|
||||||
query: "",
|
query: "",
|
||||||
});
|
});
|
||||||
if (this._cancelThreepidLookup) this._cancelThreepidLookup();
|
if (this.cancelThreepidLookup) this.cancelThreepidLookup();
|
||||||
};
|
};
|
||||||
|
|
||||||
onClick = index => () => {
|
private onSelected = (index: number): void => {
|
||||||
this.onSelected(index);
|
|
||||||
};
|
|
||||||
|
|
||||||
onSelected = index => {
|
|
||||||
const selectedList = this.state.selectedList.slice();
|
const selectedList = this.state.selectedList.slice();
|
||||||
selectedList.push(this._getFilteredSuggestions()[index]);
|
selectedList.push(this.getFilteredSuggestions()[index]);
|
||||||
this.setState({
|
this.setState({
|
||||||
selectedList,
|
selectedList,
|
||||||
suggestedList: [],
|
suggestedList: [],
|
||||||
query: "",
|
query: "",
|
||||||
});
|
});
|
||||||
if (this._cancelThreepidLookup) this._cancelThreepidLookup();
|
if (this.cancelThreepidLookup) this.cancelThreepidLookup();
|
||||||
};
|
};
|
||||||
|
|
||||||
_doNaiveGroupSearch(query) {
|
private doNaiveGroupSearch(query: string): void {
|
||||||
const lowerCaseQuery = query.toLowerCase();
|
const lowerCaseQuery = query.toLowerCase();
|
||||||
this.setState({
|
this.setState({
|
||||||
busy: true,
|
busy: true,
|
||||||
|
@ -260,7 +270,7 @@ export default class AddressPickerDialog extends React.Component {
|
||||||
display_name: u.displayname,
|
display_name: u.displayname,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this._processResults(results, query);
|
this.processResults(results, query);
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
console.error('Error whilst searching group rooms: ', err);
|
console.error('Error whilst searching group rooms: ', err);
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -273,7 +283,7 @@ export default class AddressPickerDialog extends React.Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_doNaiveGroupRoomSearch(query) {
|
private doNaiveGroupRoomSearch(query: string): void {
|
||||||
const lowerCaseQuery = query.toLowerCase();
|
const lowerCaseQuery = query.toLowerCase();
|
||||||
const results = [];
|
const results = [];
|
||||||
GroupStore.getGroupRooms(this.props.groupId).forEach((r) => {
|
GroupStore.getGroupRooms(this.props.groupId).forEach((r) => {
|
||||||
|
@ -289,13 +299,13 @@ export default class AddressPickerDialog extends React.Component {
|
||||||
name: r.name || r.canonical_alias,
|
name: r.name || r.canonical_alias,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this._processResults(results, query);
|
this.processResults(results, query);
|
||||||
this.setState({
|
this.setState({
|
||||||
busy: false,
|
busy: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_doRoomSearch(query) {
|
private doRoomSearch(query: string): void {
|
||||||
const lowerCaseQuery = query.toLowerCase();
|
const lowerCaseQuery = query.toLowerCase();
|
||||||
const rooms = MatrixClientPeg.get().getRooms();
|
const rooms = MatrixClientPeg.get().getRooms();
|
||||||
const results = [];
|
const results = [];
|
||||||
|
@ -346,13 +356,13 @@ export default class AddressPickerDialog extends React.Component {
|
||||||
return a.rank - b.rank;
|
return a.rank - b.rank;
|
||||||
});
|
});
|
||||||
|
|
||||||
this._processResults(sortedResults, query);
|
this.processResults(sortedResults, query);
|
||||||
this.setState({
|
this.setState({
|
||||||
busy: false,
|
busy: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_doUserDirectorySearch(query) {
|
private doUserDirectorySearch(query: string): void {
|
||||||
this.setState({
|
this.setState({
|
||||||
busy: true,
|
busy: true,
|
||||||
query,
|
query,
|
||||||
|
@ -366,7 +376,7 @@ export default class AddressPickerDialog extends React.Component {
|
||||||
if (this.state.query !== query) {
|
if (this.state.query !== query) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._processResults(resp.results, query);
|
this.processResults(resp.results, query);
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
console.error('Error whilst searching user directory: ', err);
|
console.error('Error whilst searching user directory: ', err);
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -377,7 +387,7 @@ export default class AddressPickerDialog extends React.Component {
|
||||||
serverSupportsUserDirectory: false,
|
serverSupportsUserDirectory: false,
|
||||||
});
|
});
|
||||||
// Do a local search immediately
|
// Do a local search immediately
|
||||||
this._doLocalSearch(query);
|
this.doLocalSearch(query);
|
||||||
}
|
}
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -386,7 +396,7 @@ export default class AddressPickerDialog extends React.Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_doLocalSearch(query) {
|
private doLocalSearch(query: string): void {
|
||||||
this.setState({
|
this.setState({
|
||||||
query,
|
query,
|
||||||
searchError: null,
|
searchError: null,
|
||||||
|
@ -407,10 +417,10 @@ export default class AddressPickerDialog extends React.Component {
|
||||||
avatar_url: user.avatarUrl,
|
avatar_url: user.avatarUrl,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this._processResults(results, query);
|
this.processResults(results, query);
|
||||||
}
|
}
|
||||||
|
|
||||||
_processResults(results, query) {
|
private processResults(results, query: string): void {
|
||||||
const suggestedList = [];
|
const suggestedList = [];
|
||||||
results.forEach((result) => {
|
results.forEach((result) => {
|
||||||
if (result.room_id) {
|
if (result.room_id) {
|
||||||
|
@ -465,20 +475,20 @@ export default class AddressPickerDialog extends React.Component {
|
||||||
address: query,
|
address: query,
|
||||||
isKnown: false,
|
isKnown: false,
|
||||||
});
|
});
|
||||||
if (this._cancelThreepidLookup) this._cancelThreepidLookup();
|
if (this.cancelThreepidLookup) this.cancelThreepidLookup();
|
||||||
if (addrType === 'email') {
|
if (addrType === 'email') {
|
||||||
this._lookupThreepid(addrType, query);
|
this.lookupThreepid(addrType, query);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
suggestedList,
|
suggestedList,
|
||||||
invalidAddressError: false,
|
invalidAddressError: false,
|
||||||
}, () => {
|
}, () => {
|
||||||
if (this.addressSelector) this.addressSelector.moveSelectionTop();
|
if (this.addressSelector.current) this.addressSelector.current.moveSelectionTop();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_addAddressesToList(addressTexts) {
|
private addAddressesToList(addressTexts): void {
|
||||||
const selectedList = this.state.selectedList.slice();
|
const selectedList = this.state.selectedList.slice();
|
||||||
|
|
||||||
let hasError = false;
|
let hasError = false;
|
||||||
|
@ -518,17 +528,17 @@ export default class AddressPickerDialog extends React.Component {
|
||||||
query: "",
|
query: "",
|
||||||
invalidAddressError: hasError ? true : this.state.invalidAddressError,
|
invalidAddressError: hasError ? true : this.state.invalidAddressError,
|
||||||
});
|
});
|
||||||
if (this._cancelThreepidLookup) this._cancelThreepidLookup();
|
if (this.cancelThreepidLookup) this.cancelThreepidLookup();
|
||||||
return hasError ? null : selectedList;
|
return hasError ? null : selectedList;
|
||||||
}
|
}
|
||||||
|
|
||||||
async _lookupThreepid(medium, address) {
|
private async lookupThreepid(medium, address): Promise<string> {
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
// Note that we can't safely remove this after we're done
|
// Note that we can't safely remove this after we're done
|
||||||
// because we don't know that it's the same one, so we just
|
// because we don't know that it's the same one, so we just
|
||||||
// leave it: it's replacing the old one each time so it's
|
// leave it: it's replacing the old one each time so it's
|
||||||
// not like they leak.
|
// not like they leak.
|
||||||
this._cancelThreepidLookup = function() {
|
this.cancelThreepidLookup = function() {
|
||||||
cancelled = true;
|
cancelled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -570,7 +580,7 @@ export default class AddressPickerDialog extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_getFilteredSuggestions() {
|
private getFilteredSuggestions() { // FIXME: UserAddressType should be an interface
|
||||||
// map addressType => set of addresses to avoid O(n*m) operation
|
// map addressType => set of addresses to avoid O(n*m) operation
|
||||||
const selectedAddresses = {};
|
const selectedAddresses = {};
|
||||||
this.state.selectedList.forEach(({ address, addressType }) => {
|
this.state.selectedList.forEach(({ address, addressType }) => {
|
||||||
|
@ -584,15 +594,15 @@ export default class AddressPickerDialog extends React.Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_onPaste = e => {
|
private onPaste = (e: React.ClipboardEvent): void => {
|
||||||
// Prevent the text being pasted into the textarea
|
// Prevent the text being pasted into the textarea
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const text = e.clipboardData.getData("text");
|
const text = e.clipboardData.getData("text");
|
||||||
// Process it as a list of addresses to add instead
|
// Process it as a list of addresses to add instead
|
||||||
this._addAddressesToList(text.split(/[\s,]+/));
|
this.addAddressesToList(text.split(/[\s,]+/));
|
||||||
};
|
};
|
||||||
|
|
||||||
onUseDefaultIdentityServerClick = e => {
|
private onUseDefaultIdentityServerClick = (e: React.MouseEvent): void => {
|
||||||
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.
|
||||||
|
@ -605,7 +615,7 @@ export default class AddressPickerDialog extends React.Component {
|
||||||
this.setState({ validAddressTypes });
|
this.setState({ validAddressTypes });
|
||||||
};
|
};
|
||||||
|
|
||||||
onManageSettingsClick = e => {
|
private onManageSettingsClick = (e: React.MouseEvent): void => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
dis.fire(Action.ViewUserSettings);
|
dis.fire(Action.ViewUserSettings);
|
||||||
this.onCancel();
|
this.onCancel();
|
||||||
|
@ -615,12 +625,11 @@ export default class AddressPickerDialog extends React.Component {
|
||||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||||
const AddressSelector = sdk.getComponent("elements.AddressSelector");
|
const AddressSelector = sdk.getComponent("elements.AddressSelector");
|
||||||
this.scrollElement = null;
|
|
||||||
|
|
||||||
let inputLabel;
|
let inputLabel;
|
||||||
if (this.props.description) {
|
if (this.props.description) {
|
||||||
inputLabel = <div className="mx_AddressPickerDialog_label">
|
inputLabel = <div className="mx_AddressPickerDialog_label">
|
||||||
<label htmlFor="textinput">{this.props.description}</label>
|
<label htmlFor="textinput">{ this.props.description }</label>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -644,10 +653,10 @@ export default class AddressPickerDialog extends React.Component {
|
||||||
query.push(
|
query.push(
|
||||||
<textarea
|
<textarea
|
||||||
key={this.state.selectedList.length}
|
key={this.state.selectedList.length}
|
||||||
onPaste={this._onPaste}
|
onPaste={this.onPaste}
|
||||||
rows="1"
|
rows={1}
|
||||||
id="textinput"
|
id="textinput"
|
||||||
ref={this._textinput}
|
ref={this.textinput}
|
||||||
className="mx_AddressPickerDialog_input"
|
className="mx_AddressPickerDialog_input"
|
||||||
onChange={this.onQueryChanged}
|
onChange={this.onQueryChanged}
|
||||||
placeholder={this.getPlaceholder()}
|
placeholder={this.getPlaceholder()}
|
||||||
|
@ -656,7 +665,7 @@ export default class AddressPickerDialog extends React.Component {
|
||||||
</textarea>,
|
</textarea>,
|
||||||
);
|
);
|
||||||
|
|
||||||
const filteredSuggestedList = this._getFilteredSuggestions();
|
const filteredSuggestedList = this.getFilteredSuggestions();
|
||||||
|
|
||||||
let error;
|
let error;
|
||||||
let addressSelector;
|
let addressSelector;
|
||||||
|
@ -675,7 +684,7 @@ export default class AddressPickerDialog extends React.Component {
|
||||||
error = <div className="mx_AddressPickerDialog_error">{ _t("No results") }</div>;
|
error = <div className="mx_AddressPickerDialog_error">{ _t("No results") }</div>;
|
||||||
} else {
|
} else {
|
||||||
addressSelector = (
|
addressSelector = (
|
||||||
<AddressSelector ref={(ref) => {this.addressSelector = ref;}}
|
<AddressSelector ref={this.addressSelector}
|
||||||
addressList={filteredSuggestedList}
|
addressList={filteredSuggestedList}
|
||||||
showAddress={this.props.pickerType === 'user'}
|
showAddress={this.props.pickerType === 'user'}
|
||||||
onSelected={this.onSelected}
|
onSelected={this.onSelected}
|
||||||
|
@ -690,7 +699,7 @@ export default class AddressPickerDialog extends React.Component {
|
||||||
&& this.props.validAddressTypes.includes('email')) {
|
&& this.props.validAddressTypes.includes('email')) {
|
||||||
const defaultIdentityServerUrl = getDefaultIdentityServerUrl();
|
const defaultIdentityServerUrl = getDefaultIdentityServerUrl();
|
||||||
if (defaultIdentityServerUrl) {
|
if (defaultIdentityServerUrl) {
|
||||||
identityServer = <div className="mx_AddressPickerDialog_identityServer">{_t(
|
identityServer = <div className="mx_AddressPickerDialog_identityServer">{ _t(
|
||||||
"Use an identity server to invite by email. " +
|
"Use an identity server to invite by email. " +
|
||||||
"<default>Use the default (%(defaultIdentityServerName)s)</default> " +
|
"<default>Use the default (%(defaultIdentityServerName)s)</default> " +
|
||||||
"or manage in <settings>Settings</settings>.",
|
"or manage in <settings>Settings</settings>.",
|
||||||
|
@ -698,25 +707,25 @@ export default class AddressPickerDialog extends React.Component {
|
||||||
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>;
|
||||||
} else {
|
} else {
|
||||||
identityServer = <div className="mx_AddressPickerDialog_identityServer">{_t(
|
identityServer = <div className="mx_AddressPickerDialog_identityServer">{ _t(
|
||||||
"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>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseDialog className="mx_AddressPickerDialog" onKeyDown={this.onKeyDown}
|
<BaseDialog className="mx_AddressPickerDialog" onKeyDown={this.onKeyDown}
|
||||||
onFinished={this.props.onFinished} title={this.props.title}>
|
onFinished={this.props.onFinished} title={this.props.title}>
|
||||||
{inputLabel}
|
{ inputLabel }
|
||||||
<div className="mx_Dialog_content">
|
<div className="mx_Dialog_content">
|
||||||
<div className="mx_AddressPickerDialog_inputContainer">{ query }</div>
|
<div className="mx_AddressPickerDialog_inputContainer">{ query }</div>
|
||||||
{ error }
|
{ error }
|
Loading…
Add table
Add a link
Reference in a new issue