Merge pull request #3359 from matrix-org/jryans/features-without-is
Allow connecting to an IS from address picker
This commit is contained in:
commit
27fdef557d
10 changed files with 147 additions and 19 deletions
|
@ -281,6 +281,12 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus {
|
|||
box-shadow: 2px 15px 30px 0 $dialog-shadow-color;
|
||||
border-radius: 4px;
|
||||
overflow-y: auto;
|
||||
|
||||
a:link,
|
||||
a:hover,
|
||||
a:visited {
|
||||
@mixin mx_Dialog_link;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_Dialog_fixedWidth {
|
||||
|
|
|
@ -39,8 +39,7 @@ limitations under the License.
|
|||
a:link,
|
||||
a:hover,
|
||||
a:visited {
|
||||
color: $accent-color;
|
||||
text-decoration: none;
|
||||
@mixin mx_Dialog_link;
|
||||
}
|
||||
|
||||
input[type=text],
|
||||
|
|
|
@ -67,3 +67,6 @@ limitations under the License.
|
|||
pointer-events: none;
|
||||
}
|
||||
|
||||
.mx_AddressPickerDialog_identityServer {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
|
|
@ -202,6 +202,11 @@ $interactive-tooltip-fg-color: #ffffff;
|
|||
background-color: $button-secondary-bg-color;
|
||||
}
|
||||
|
||||
@define-mixin mx_Dialog_link {
|
||||
color: $accent-color;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
// Nasty hacks to apply a filter to arbitrary monochrome artwork to make it
|
||||
// better match the theme. Typically applied to dark grey 'off' buttons or
|
||||
// light grey 'on' buttons.
|
||||
|
|
|
@ -328,3 +328,8 @@ $interactive-tooltip-fg-color: #ffffff;
|
|||
color: $accent-color;
|
||||
background-color: $button-secondary-bg-color;
|
||||
}
|
||||
|
||||
@define-mixin mx_Dialog_link {
|
||||
color: $accent-color;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
|
|
@ -51,7 +51,14 @@ export function showStartChatInviteDialog() {
|
|||
Modal.createTrackedDialog('Start a chat', '', AddressPickerDialog, {
|
||||
title: _t('Start a chat'),
|
||||
description: _t("Who would you like to communicate with?"),
|
||||
placeholder: _t("Email, name or Matrix ID"),
|
||||
placeholder: (validAddressTypes) => {
|
||||
// The set of valid address type can be mutated inside the dialog
|
||||
// when you first have no IS but agree to use one in the dialog.
|
||||
if (validAddressTypes.includes('email')) {
|
||||
return _t("Email, name or Matrix ID");
|
||||
}
|
||||
return _t("Name or Matrix ID");
|
||||
},
|
||||
validAddressTypes,
|
||||
button: _t("Start Chat"),
|
||||
onFinished: _onStartDmFinished,
|
||||
|
@ -68,9 +75,15 @@ export function showRoomInviteDialog(roomId) {
|
|||
|
||||
Modal.createTrackedDialog('Chat Invite', '', AddressPickerDialog, {
|
||||
title: _t('Invite new room members'),
|
||||
description: _t('Who would you like to add to this room?'),
|
||||
button: _t('Send Invites'),
|
||||
placeholder: _t("Email, name or Matrix ID"),
|
||||
placeholder: (validAddressTypes) => {
|
||||
// The set of valid address type can be mutated inside the dialog
|
||||
// when you first have no IS but agree to use one in the dialog.
|
||||
if (validAddressTypes.includes('email')) {
|
||||
return _t("Email, name or Matrix ID");
|
||||
}
|
||||
return _t("Name or Matrix ID");
|
||||
},
|
||||
validAddressTypes,
|
||||
onFinished: (shouldInvite, addrs) => {
|
||||
_onRoomInviteFinished(roomId, shouldInvite, addrs);
|
||||
|
|
|
@ -24,11 +24,14 @@ import createReactClass from 'create-react-class';
|
|||
import { _t, _td } from '../../../languageHandler';
|
||||
import sdk from '../../../index';
|
||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||
import dis from '../../../dispatcher';
|
||||
import Promise from 'bluebird';
|
||||
import { addressTypes, getAddressType } from '../../../UserAddress.js';
|
||||
import GroupStore from '../../../stores/GroupStore';
|
||||
import * as Email from '../../../email';
|
||||
import IdentityAuthClient from '../../../IdentityAuthClient';
|
||||
import { getDefaultIdentityServerUrl, useDefaultIdentityServer } from '../../../utils/IdentityServerUtils';
|
||||
import { abbreviateUrl } from '../../../utils/UrlUtils';
|
||||
|
||||
const TRUNCATE_QUERY_LIST = 40;
|
||||
const QUERY_USER_DIRECTORY_DEBOUNCE_MS = 200;
|
||||
|
@ -49,7 +52,7 @@ module.exports = createReactClass({
|
|||
// Extra node inserted after picker input, dropdown and errors
|
||||
extraNode: PropTypes.node,
|
||||
value: PropTypes.string,
|
||||
placeholder: PropTypes.string,
|
||||
placeholder: PropTypes.oneOfType(PropTypes.string, PropTypes.func),
|
||||
roomId: PropTypes.string,
|
||||
button: PropTypes.string,
|
||||
focus: PropTypes.bool,
|
||||
|
@ -91,6 +94,9 @@ module.exports = createReactClass({
|
|||
// List of UserAddressType objects representing the set of
|
||||
// auto-completion results for the current search query.
|
||||
suggestedList: [],
|
||||
// List of address types initialised from props, but may change while the
|
||||
// dialog is open.
|
||||
validAddressTypes: this.props.validAddressTypes,
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -101,6 +107,15 @@ module.exports = createReactClass({
|
|||
}
|
||||
},
|
||||
|
||||
getPlaceholder() {
|
||||
const { placeholder } = this.props;
|
||||
if (typeof placeholder === "string") {
|
||||
return placeholder;
|
||||
}
|
||||
// Otherwise it's a function, as checked by prop types.
|
||||
return placeholder(this.state.validAddressTypes);
|
||||
},
|
||||
|
||||
onButtonClick: function() {
|
||||
let selectedList = this.state.selectedList.slice();
|
||||
// Check the text input field to see if user has an unconverted address
|
||||
|
@ -434,7 +449,7 @@ module.exports = createReactClass({
|
|||
// This is important, otherwise there's no way to invite
|
||||
// a perfectly valid address if there are close matches.
|
||||
const addrType = getAddressType(query);
|
||||
if (this.props.validAddressTypes.includes(addrType)) {
|
||||
if (this.state.validAddressTypes.includes(addrType)) {
|
||||
if (addrType === 'email' && !Email.looksValid(query)) {
|
||||
this.setState({searchError: _t("That doesn't look like a valid email address")});
|
||||
return;
|
||||
|
@ -470,7 +485,7 @@ module.exports = createReactClass({
|
|||
isKnown: false,
|
||||
};
|
||||
|
||||
if (!this.props.validAddressTypes.includes(addrType)) {
|
||||
if (!this.state.validAddressTypes.includes(addrType)) {
|
||||
hasError = true;
|
||||
} else if (addrType === 'mx-user-id') {
|
||||
const user = MatrixClientPeg.get().getUser(addrObj.address);
|
||||
|
@ -571,12 +586,37 @@ module.exports = createReactClass({
|
|||
this._addAddressesToList(text.split(/[\s,]+/));
|
||||
},
|
||||
|
||||
onUseDefaultIdentityServerClick(e) {
|
||||
e.preventDefault();
|
||||
|
||||
// Update the IS in account data. Actually using it may trigger terms.
|
||||
useDefaultIdentityServer();
|
||||
|
||||
// Add email as a valid address type.
|
||||
const { validAddressTypes } = this.state;
|
||||
validAddressTypes.push('email');
|
||||
this.setState({ validAddressTypes });
|
||||
},
|
||||
|
||||
onManageSettingsClick(e) {
|
||||
e.preventDefault();
|
||||
dis.dispatch({ action: 'view_user_settings' });
|
||||
this.onCancel();
|
||||
},
|
||||
|
||||
render: function() {
|
||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
const AddressSelector = sdk.getComponent("elements.AddressSelector");
|
||||
this.scrollElement = null;
|
||||
|
||||
let inputLabel;
|
||||
if (this.props.description) {
|
||||
inputLabel = <div className="mx_AddressPickerDialog_label">
|
||||
<label htmlFor="textinput">{this.props.description}</label>
|
||||
</div>;
|
||||
}
|
||||
|
||||
const query = [];
|
||||
// create the invite list
|
||||
if (this.state.selectedList.length > 0) {
|
||||
|
@ -603,7 +643,7 @@ module.exports = createReactClass({
|
|||
ref="textinput"
|
||||
className="mx_AddressPickerDialog_input"
|
||||
onChange={this.onQueryChanged}
|
||||
placeholder={this.props.placeholder}
|
||||
placeholder={this.getPlaceholder()}
|
||||
defaultValue={this.props.value}
|
||||
autoFocus={this.props.focus}>
|
||||
</textarea>,
|
||||
|
@ -614,7 +654,7 @@ module.exports = createReactClass({
|
|||
let error;
|
||||
let addressSelector;
|
||||
if (this.state.invalidAddressError) {
|
||||
const validTypeDescriptions = this.props.validAddressTypes.map((t) => _t(addressTypeName[t]));
|
||||
const validTypeDescriptions = this.state.validAddressTypes.map((t) => _t(addressTypeName[t]));
|
||||
error = <div className="mx_AddressPickerDialog_error">
|
||||
{ _t("You have entered an invalid address.") }
|
||||
<br />
|
||||
|
@ -637,17 +677,43 @@ module.exports = createReactClass({
|
|||
);
|
||||
}
|
||||
|
||||
let identityServer;
|
||||
if (this.props.pickerType === 'user' && !this.state.validAddressTypes.includes('email')) {
|
||||
const defaultIdentityServerUrl = getDefaultIdentityServerUrl();
|
||||
if (defaultIdentityServerUrl) {
|
||||
identityServer = <div className="mx_AddressPickerDialog_identityServer">{_t(
|
||||
"Use an identity server to invite by email. " +
|
||||
"<default>Use the default (%(defaultIdentityServerName)s)</default> " +
|
||||
"or manage in <settings>Settings</settings>.",
|
||||
{
|
||||
defaultIdentityServerName: abbreviateUrl(defaultIdentityServerUrl),
|
||||
},
|
||||
{
|
||||
default: sub => <a href="#" onClick={this.onUseDefaultIdentityServerClick}>{sub}</a>,
|
||||
settings: sub => <a href="#" onClick={this.onManageSettingsClick}>{sub}</a>,
|
||||
},
|
||||
)}</div>;
|
||||
} else {
|
||||
identityServer = <div className="mx_AddressPickerDialog_identityServer">{_t(
|
||||
"Use an identity server to invite by email. " +
|
||||
"Manage in <settings>Settings</settings>.",
|
||||
{}, {
|
||||
settings: sub => <a href="#" onClick={this.onManageSettingsClick}>{sub}</a>,
|
||||
},
|
||||
)}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<BaseDialog className="mx_AddressPickerDialog" onKeyDown={this.onKeyDown}
|
||||
onFinished={this.props.onFinished} title={this.props.title}>
|
||||
<div className="mx_AddressPickerDialog_label">
|
||||
<label htmlFor="textinput">{ this.props.description }</label>
|
||||
</div>
|
||||
{inputLabel}
|
||||
<div className="mx_Dialog_content">
|
||||
<div className="mx_AddressPickerDialog_inputContainer">{ query }</div>
|
||||
{ error }
|
||||
{ addressSelector }
|
||||
{ this.props.extraNode }
|
||||
{ identityServer }
|
||||
</div>
|
||||
<DialogButtons primaryButton={this.props.button}
|
||||
onPrimaryButtonClick={this.onButtonClick}
|
||||
|
|
|
@ -20,13 +20,13 @@ import PropTypes from 'prop-types';
|
|||
import {_t} from "../../../languageHandler";
|
||||
import sdk from '../../../index';
|
||||
import MatrixClientPeg from "../../../MatrixClientPeg";
|
||||
import SdkConfig from "../../../SdkConfig";
|
||||
import Modal from '../../../Modal';
|
||||
import dis from "../../../dispatcher";
|
||||
import { getThreepidBindStatus } from '../../../boundThreepids';
|
||||
import IdentityAuthClient from "../../../IdentityAuthClient";
|
||||
import {SERVICE_TYPES} from "matrix-js-sdk";
|
||||
import {abbreviateUrl, unabbreviateUrl} from "../../../utils/UrlUtils";
|
||||
import { getDefaultIdentityServerUrl } from '../../../utils/IdentityServerUtils';
|
||||
|
||||
/**
|
||||
* Check an IS URL is valid, including liveness check
|
||||
|
@ -66,10 +66,10 @@ export default class SetIdServer extends React.Component {
|
|||
super();
|
||||
|
||||
let defaultIdServer = '';
|
||||
if (!MatrixClientPeg.get().getIdentityServerUrl() && SdkConfig.get()['validated_server_config']['isUrl']) {
|
||||
if (!MatrixClientPeg.get().getIdentityServerUrl() && getDefaultIdentityServerUrl()) {
|
||||
// If no ID server is configured but there's one in the config, prepopulate
|
||||
// the field to help the user.
|
||||
defaultIdServer = abbreviateUrl(SdkConfig.get()['validated_server_config']['isUrl']);
|
||||
defaultIdServer = abbreviateUrl(getDefaultIdentityServerUrl());
|
||||
}
|
||||
|
||||
this.state = {
|
||||
|
@ -253,10 +253,10 @@ export default class SetIdServer extends React.Component {
|
|||
});
|
||||
|
||||
let newFieldVal = '';
|
||||
if (SdkConfig.get()['validated_server_config']['isUrl']) {
|
||||
if (getDefaultIdentityServerUrl()) {
|
||||
// Prepopulate the client's default so the user at least has some idea of
|
||||
// a valid value they might enter
|
||||
newFieldVal = abbreviateUrl(SdkConfig.get()['validated_server_config']['isUrl']);
|
||||
newFieldVal = abbreviateUrl(getDefaultIdentityServerUrl());
|
||||
}
|
||||
|
||||
this.setState({
|
||||
|
|
|
@ -117,7 +117,6 @@
|
|||
"Email, name or Matrix ID": "Email, name or Matrix ID",
|
||||
"Start Chat": "Start Chat",
|
||||
"Invite new room members": "Invite new room members",
|
||||
"Who would you like to add to this room?": "Who would you like to add to this room?",
|
||||
"Send Invites": "Send Invites",
|
||||
"Failed to start chat": "Failed to start chat",
|
||||
"Operation failed": "Operation failed",
|
||||
|
@ -1162,6 +1161,8 @@
|
|||
"That doesn't look like a valid email address": "That doesn't look like a valid email address",
|
||||
"You have entered an invalid address.": "You have entered an invalid address.",
|
||||
"Try using one of the following valid address types: %(validTypesList)s.": "Try using one of the following valid address types: %(validTypesList)s.",
|
||||
"Use an identity server to invite by email. <default>Use the default (%(defaultIdentityServerName)s)</default> or manage in <settings>Settings</settings>.": "Use an identity server to invite by email. <default>Use the default (%(defaultIdentityServerName)s)</default> or manage in <settings>Settings</settings>.",
|
||||
"Use an identity server to invite by email. Manage in <settings>Settings</settings>.": "Use an identity server to invite by email. Manage in <settings>Settings</settings>.",
|
||||
"The following users may not exist": "The following users may not exist",
|
||||
"Unable to find profiles for the Matrix IDs listed below - would you like to invite them anyway?": "Unable to find profiles for the Matrix IDs listed below - would you like to invite them anyway?",
|
||||
"Invite anyway and never warn me again": "Invite anyway and never warn me again",
|
||||
|
|
30
src/utils/IdentityServerUtils.js
Normal file
30
src/utils/IdentityServerUtils.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import SdkConfig from '../SdkConfig';
|
||||
import MatrixClientPeg from '../MatrixClientPeg';
|
||||
|
||||
export function getDefaultIdentityServerUrl() {
|
||||
return SdkConfig.get()['validated_server_config']['isUrl'];
|
||||
}
|
||||
|
||||
export function useDefaultIdentityServer() {
|
||||
const url = getDefaultIdentityServerUrl();
|
||||
// Account data change will update localstorage, client, etc through dispatcher
|
||||
MatrixClientPeg.get().setAccountData("m.identity_server", {
|
||||
base_url: url,
|
||||
});
|
||||
}
|
Loading…
Reference in a new issue