Skip to content

Commit

Permalink
Allow connecting to an IS from address picker
Browse files Browse the repository at this point in the history
This allows those who previously disconnected from an IS to either choose the
default IS or a custom one from Settings via the address picker dialog.

Part of element-hq/element-web#10619
  • Loading branch information
jryans committed Aug 29, 2019
1 parent 752eb17 commit 166fb69
Show file tree
Hide file tree
Showing 10 changed files with 139 additions and 14 deletions.
6 changes: 6 additions & 0 deletions res/css/_common.scss
Expand Up @@ -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 {
Expand Down
3 changes: 1 addition & 2 deletions res/css/views/auth/_AuthBody.scss
Expand Up @@ -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],
Expand Down
3 changes: 3 additions & 0 deletions res/css/views/dialogs/_AddressPickerDialog.scss
Expand Up @@ -67,3 +67,6 @@ limitations under the License.
pointer-events: none;
}

.mx_AddressPickerDialog_identityServer {
margin-top: 1em;
}
5 changes: 5 additions & 0 deletions res/themes/dark/css/_dark.scss
Expand Up @@ -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.
Expand Down
5 changes: 5 additions & 0 deletions res/themes/light/css/_light.scss
Expand Up @@ -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;
}
18 changes: 16 additions & 2 deletions src/RoomInvite.js
Expand Up @@ -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,
Expand All @@ -69,7 +76,14 @@ export function showRoomInviteDialog(roomId) {
Modal.createTrackedDialog('Chat Invite', '', AddressPickerDialog, {
title: _t('Invite new room members'),
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);
Expand Down
71 changes: 66 additions & 5 deletions src/components/views/dialogs/AddressPickerDialog.js
Expand Up @@ -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;
Expand All @@ -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,
Expand Down Expand Up @@ -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,
};
},

Expand All @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -571,6 +586,24 @@ 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');
Expand Down Expand Up @@ -610,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>,
Expand All @@ -621,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 />
Expand All @@ -644,6 +677,33 @@ 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}>
Expand All @@ -653,6 +713,7 @@ module.exports = createReactClass({
{ error }
{ addressSelector }
{ this.props.extraNode }
{ identityServer }
</div>
<DialogButtons primaryButton={this.props.button}
onPrimaryButtonClick={this.onButtonClick}
Expand Down
10 changes: 5 additions & 5 deletions src/components/views/settings/SetIdServer.js
Expand Up @@ -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
Expand Down Expand Up @@ -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 = {
Expand Down Expand Up @@ -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({
Expand Down
2 changes: 2 additions & 0 deletions src/i18n/strings/en_EN.json
Expand Up @@ -1161,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",
Expand Down
30 changes: 30 additions & 0 deletions src/utils/IdentityServerUtils.js
@@ -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,
});
}

0 comments on commit 166fb69

Please sign in to comment.