diff --git a/.stylelintrc.js b/.stylelintrc.js
index 97e1ec8023..f028c76cc0 100644
--- a/.stylelintrc.js
+++ b/.stylelintrc.js
@@ -15,6 +15,9 @@ module.exports = {
"number-leading-zero": null,
"selector-list-comma-newline-after": null,
"at-rule-no-unknown": null,
- "scss/at-rule-no-unknown": true,
+ "scss/at-rule-no-unknown": [true, {
+ // https://github.com/vector-im/riot-web/issues/10544
+ "ignoreAtRules": ["define-mixin"],
+ }],
}
}
diff --git a/package.json b/package.json
index 8e1a1fa668..ffd701a233 100644
--- a/package.json
+++ b/package.json
@@ -148,7 +148,7 @@
"karma-summary-reporter": "^1.5.1",
"karma-webpack": "^4.0.0-beta.0",
"matrix-mock-request": "^1.2.3",
- "matrix-react-test-utils": "^0.1.1",
+ "matrix-react-test-utils": "^0.2.2",
"mocha": "^5.0.5",
"react-addons-test-utils": "^15.4.0",
"require-json": "0.0.1",
diff --git a/res/css/_common.scss b/res/css/_common.scss
index 517ced43fb..1b7c8ec938 100644
--- a/res/css/_common.scss
+++ b/res/css/_common.scss
@@ -559,3 +559,7 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus {
.mx_Username_color8 {
color: $username-variant8-color;
}
+
+@define-mixin mx_Settings_fullWidthField {
+ margin-right: 200px;
+}
diff --git a/res/css/views/settings/_ProfileSettings.scss b/res/css/views/settings/_ProfileSettings.scss
index 3e97a0ff6d..afac75986f 100644
--- a/res/css/views/settings/_ProfileSettings.scss
+++ b/res/css/views/settings/_ProfileSettings.scss
@@ -26,6 +26,10 @@ limitations under the License.
height: 4em;
}
+.mx_ProfileSettings_controls .mx_Field {
+ margin-right: 100px;
+}
+
.mx_ProfileSettings_controls .mx_Field:first-child {
margin-top: 0;
}
diff --git a/res/css/views/settings/_SetIdServer.scss b/res/css/views/settings/_SetIdServer.scss
index c6fcfc8af5..55ad6eef02 100644
--- a/res/css/views/settings/_SetIdServer.scss
+++ b/res/css/views/settings/_SetIdServer.scss
@@ -1,5 +1,5 @@
/*
-Copyright 2019 New Vector Ltd
+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.
@@ -15,5 +15,5 @@ limitations under the License.
*/
.mx_SetIdServer .mx_Field_input {
- width: 300px;
+ @mixin mx_Settings_fullWidthField;
}
diff --git a/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss b/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss
index 091c98ffb8..16467165cf 100644
--- a/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss
+++ b/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss
@@ -16,15 +16,15 @@ limitations under the License.
.mx_GeneralUserSettingsTab_changePassword .mx_Field,
.mx_GeneralUserSettingsTab_themeSection .mx_Field {
- margin-right: 100px; // Align with the other fields on the page
+ @mixin mx_Settings_fullWidthField;
}
.mx_GeneralUserSettingsTab_changePassword .mx_Field:first-child {
margin-top: 0;
}
-.mx_GeneralUserSettingsTab_accountSection > .mx_EmailAddresses,
-.mx_GeneralUserSettingsTab_accountSection > .mx_PhoneNumbers,
+.mx_GeneralUserSettingsTab_accountSection .mx_EmailAddresses,
+.mx_GeneralUserSettingsTab_accountSection .mx_PhoneNumbers,
.mx_GeneralUserSettingsTab_languageInput {
- margin-right: 100px; // Align with the other fields on the page
+ @mixin mx_Settings_fullWidthField;
}
diff --git a/res/css/views/settings/tabs/user/_PreferencesUserSettingsTab.scss b/res/css/views/settings/tabs/user/_PreferencesUserSettingsTab.scss
index b3430f47af..d003e175d9 100644
--- a/res/css/views/settings/tabs/user/_PreferencesUserSettingsTab.scss
+++ b/res/css/views/settings/tabs/user/_PreferencesUserSettingsTab.scss
@@ -15,5 +15,5 @@ limitations under the License.
*/
.mx_PreferencesUserSettingsTab .mx_Field {
- margin-right: 100px; // Align with the rest of the controls
+ @mixin mx_Settings_fullWidthField;
}
diff --git a/res/css/views/settings/tabs/user/_VoiceUserSettingsTab.scss b/res/css/views/settings/tabs/user/_VoiceUserSettingsTab.scss
index 36c8cfd896..69d57bdba1 100644
--- a/res/css/views/settings/tabs/user/_VoiceUserSettingsTab.scss
+++ b/res/css/views/settings/tabs/user/_VoiceUserSettingsTab.scss
@@ -15,7 +15,7 @@ limitations under the License.
*/
.mx_VoiceUserSettingsTab .mx_Field {
- margin-right: 100px; // align with the rest of the fields
+ @mixin mx_Settings_fullWidthField;
}
.mx_VoiceUserSettingsTab_missingMediaPermissions {
diff --git a/src/CallHandler.js b/src/CallHandler.js
index 5b58400ae6..40a8d426f8 100644
--- a/src/CallHandler.js
+++ b/src/CallHandler.js
@@ -63,7 +63,7 @@ import SdkConfig from './SdkConfig';
import { showUnknownDeviceDialogForCalls } from './cryptodevices';
import WidgetUtils from './utils/WidgetUtils';
import WidgetEchoStore from './stores/WidgetEchoStore';
-import ScalarAuthClient from './ScalarAuthClient';
+import {IntegrationManagers} from "./integrations/IntegrationManagers";
global.mxCalls = {
//room_id: MatrixCall
@@ -348,14 +348,20 @@ async function _startCallApp(roomId, type) {
// the state event in anyway, but the resulting widget would then not
// work for us. Better that the user knows before everyone else in the
// room sees it.
- const scalarClient = new ScalarAuthClient();
- let haveScalar = false;
- try {
- await scalarClient.connect();
- haveScalar = scalarClient.hasCredentials();
- } catch (e) {
- // fall through
+ const managers = IntegrationManagers.sharedInstance();
+ let haveScalar = true;
+ if (managers.hasManager()) {
+ try {
+ const scalarClient = managers.getPrimaryManager().getScalarClient();
+ await scalarClient.connect();
+ haveScalar = scalarClient.hasCredentials();
+ } catch (e) {
+ // ignore
+ }
+ } else {
+ haveScalar = false;
}
+
if (!haveScalar) {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
@@ -421,7 +427,8 @@ async function _startCallApp(roomId, type) {
// URL, but this will at least allow the integration manager to not be hardcoded.
widgetUrl = SdkConfig.get().integrations_jitsi_widget_url + '?' + queryString;
} else {
- widgetUrl = SdkConfig.get().integrations_rest_url + '/widgets/jitsi.html?' + queryString;
+ const apiUrl = IntegrationManagers.sharedInstance().getPrimaryManager().apiUrl;
+ widgetUrl = apiUrl + '/widgets/jitsi.html?' + queryString;
}
const widgetData = { widgetSessionId };
diff --git a/src/FromWidgetPostMessageApi.js b/src/FromWidgetPostMessageApi.js
index d34e3d8ed0..b2bd579b74 100644
--- a/src/FromWidgetPostMessageApi.js
+++ b/src/FromWidgetPostMessageApi.js
@@ -22,7 +22,7 @@ import WidgetMessagingEndpoint from './WidgetMessagingEndpoint';
import ActiveWidgetStore from './stores/ActiveWidgetStore';
import MatrixClientPeg from "./MatrixClientPeg";
import RoomViewStore from "./stores/RoomViewStore";
-import { showIntegrationsManager } from './integrations/integrations';
+import {IntegrationManagers} from "./integrations/IntegrationManagers";
const WIDGET_API_VERSION = '0.0.2'; // Current API version
const SUPPORTED_WIDGET_API_VERSIONS = [
@@ -193,11 +193,12 @@ export default class FromWidgetPostMessageApi {
const integType = (data && data.integType) ? data.integType : null;
const integId = (data && data.integId) ? data.integId : null;
- showIntegrationsManager({
- room: MatrixClientPeg.get().getRoom(RoomViewStore.getRoomId()),
- screen: 'type_' + integType,
- integrationId: integId,
- });
+ // TODO: Open the right integration manager for the widget
+ IntegrationManagers.sharedInstance().getPrimaryManager().open(
+ MatrixClientPeg.get().getRoom(RoomViewStore.getRoomId()),
+ `type_${integType}`,
+ integId,
+ );
} else if (action === 'set_always_on_screen') {
// This is a new message: there is no reason to support the deprecated widgetData here
const data = event.data.data;
diff --git a/src/Lifecycle.js b/src/Lifecycle.js
index 0ddb7e9aae..c03a958840 100644
--- a/src/Lifecycle.js
+++ b/src/Lifecycle.js
@@ -35,6 +35,7 @@ import { sendLoginRequest } from "./Login";
import * as StorageManager from './utils/StorageManager';
import SettingsStore from "./settings/SettingsStore";
import TypingStore from "./stores/TypingStore";
+import {IntegrationManagers} from "./integrations/IntegrationManagers";
/**
* Called at startup, to attempt to build a logged-in Matrix session. It tries
@@ -580,6 +581,7 @@ async function startMatrixClient(startSyncing=true) {
Presence.start();
}
DMRoomMap.makeShared().start();
+ IntegrationManagers.sharedInstance().startWatching();
ActiveWidgetStore.start();
if (startSyncing) {
@@ -638,6 +640,7 @@ export function stopMatrixClient(unsetClient=true) {
TypingStore.sharedInstance().reset();
Presence.stop();
ActiveWidgetStore.stop();
+ IntegrationManagers.sharedInstance().stopWatching();
if (DMRoomMap.shared()) DMRoomMap.shared().stop();
const cli = MatrixClientPeg.get();
if (cli) {
diff --git a/src/Modal.js b/src/Modal.js
index 96be445ab1..26c9da8bbb 100644
--- a/src/Modal.js
+++ b/src/Modal.js
@@ -274,7 +274,7 @@ class ModalManager {
this._reRender();
return {
close: closeDialog,
- then: (resolve, reject) => onFinishedProm.then(resolve, reject),
+ finished: onFinishedProm,
};
}
@@ -285,7 +285,7 @@ class ModalManager {
this._reRender();
return {
close: closeDialog,
- then: (resolve, reject) => onFinishedProm.then(resolve, reject),
+ finished: onFinishedProm,
};
}
diff --git a/src/ScalarAuthClient.js b/src/ScalarAuthClient.js
index c268fbe3fb..3623d47f8e 100644
--- a/src/ScalarAuthClient.js
+++ b/src/ScalarAuthClient.js
@@ -29,20 +29,43 @@ import * as Matrix from 'matrix-js-sdk';
// The version of the integration manager API we're intending to work with
const imApiVersion = "1.1";
-class ScalarAuthClient {
- constructor() {
+export default class ScalarAuthClient {
+ constructor(apiUrl, uiUrl) {
+ this.apiUrl = apiUrl;
+ this.uiUrl = uiUrl;
this.scalarToken = null;
// `undefined` to allow `startTermsFlow` to fallback to a default
// callback if this is unset.
this.termsInteractionCallback = undefined;
+
+ // We try and store the token on a per-manager basis, but need a fallback
+ // for the default manager.
+ const configApiUrl = SdkConfig.get()['integrations_rest_url'];
+ const configUiUrl = SdkConfig.get()['integrations_ui_url'];
+ this.isDefaultManager = apiUrl === configApiUrl && configUiUrl === uiUrl;
}
- /**
- * Determines if setting up a ScalarAuthClient is even possible
- * @returns {boolean} true if possible, false otherwise.
- */
- static isPossible() {
- return SdkConfig.get()['integrations_rest_url'] && SdkConfig.get()['integrations_ui_url'];
+ _writeTokenToStore() {
+ window.localStorage.setItem("mx_scalar_token_at_" + this.apiUrl, this.scalarToken);
+ if (this.isDefaultManager) {
+ // We remove the old token from storage to migrate upwards. This is safe
+ // to do because even if the user switches to /app when this is on /develop
+ // they'll at worst register for a new token.
+ window.localStorage.removeItem("mx_scalar_token"); // no-op when not present
+ }
+ }
+
+ _readTokenFromStore() {
+ let token = window.localStorage.getItem("mx_scalar_token_at_" + this.apiUrl);
+ if (!token && this.isDefaultManager) {
+ token = window.localStorage.getItem("mx_scalar_token");
+ }
+ return token;
+ }
+
+ _readToken() {
+ if (this.scalarToken) return this.scalarToken;
+ return this._readTokenFromStore();
}
setTermsInteractionCallback(callback) {
@@ -61,8 +84,7 @@ class ScalarAuthClient {
// Returns a promise that resolves to a scalar_token string
getScalarToken() {
- let token = this.scalarToken;
- if (!token) token = window.localStorage.getItem("mx_scalar_token");
+ const token = this._readToken();
if (!token) {
return this.registerForToken();
@@ -78,7 +100,7 @@ class ScalarAuthClient {
}
_getAccountName(token) {
- const url = SdkConfig.get().integrations_rest_url + "/account";
+ const url = this.apiUrl + "/account";
return new Promise(function(resolve, reject) {
request({
@@ -111,7 +133,7 @@ class ScalarAuthClient {
return token;
}).catch((e) => {
if (e instanceof TermsNotSignedError) {
- console.log("Integrations manager requires new terms to be agreed to");
+ console.log("Integration manager requires new terms to be agreed to");
// The terms endpoints are new and so live on standard _matrix prefixes,
// but IM rest urls are currently configured with paths, so remove the
// path from the base URL before passing it to the js-sdk
@@ -126,7 +148,7 @@ class ScalarAuthClient {
// Once we've fully transitioned to _matrix URLs, we can give people
// a grace period to update their configs, then use the rest url as
// a regular base url.
- const parsedImRestUrl = url.parse(SdkConfig.get().integrations_rest_url);
+ const parsedImRestUrl = url.parse(this.apiUrl);
parsedImRestUrl.path = '';
parsedImRestUrl.pathname = '';
return startTermsFlow([new Service(
@@ -147,17 +169,18 @@ class ScalarAuthClient {
return MatrixClientPeg.get().getOpenIdToken().then((tokenObject) => {
// Now we can send that to scalar and exchange it for a scalar token
return this.exchangeForScalarToken(tokenObject);
- }).then((tokenObject) => {
+ }).then((token) => {
// Validate it (this mostly checks to see if the IM needs us to agree to some terms)
- return this._checkToken(tokenObject);
- }).then((tokenObject) => {
- window.localStorage.setItem("mx_scalar_token", tokenObject);
- return tokenObject;
+ return this._checkToken(token);
+ }).then((token) => {
+ this.scalarToken = token;
+ this._writeTokenToStore();
+ return token;
});
}
exchangeForScalarToken(openidTokenObject) {
- const scalarRestUrl = SdkConfig.get().integrations_rest_url;
+ const scalarRestUrl = this.apiUrl;
return new Promise(function(resolve, reject) {
request({
@@ -181,7 +204,7 @@ class ScalarAuthClient {
}
getScalarPageTitle(url) {
- let scalarPageLookupUrl = SdkConfig.get().integrations_rest_url + '/widgets/title_lookup';
+ let scalarPageLookupUrl = this.apiUrl + '/widgets/title_lookup';
scalarPageLookupUrl = this.getStarterLink(scalarPageLookupUrl);
scalarPageLookupUrl += '&curl=' + encodeURIComponent(url);
@@ -217,7 +240,7 @@ class ScalarAuthClient {
* @return {Promise} Resolves on completion
*/
disableWidgetAssets(widgetType, widgetId) {
- let url = SdkConfig.get().integrations_rest_url + '/widgets/set_assets_state';
+ let url = this.apiUrl + '/widgets/set_assets_state';
url = this.getStarterLink(url);
return new Promise((resolve, reject) => {
request({
@@ -246,7 +269,7 @@ class ScalarAuthClient {
getScalarInterfaceUrlForRoom(room, screen, id) {
const roomId = room.roomId;
const roomName = room.name;
- let url = SdkConfig.get().integrations_ui_url;
+ let url = this.uiUrl;
url += "?scalar_token=" + encodeURIComponent(this.scalarToken);
url += "&room_id=" + encodeURIComponent(roomId);
url += "&room_name=" + encodeURIComponent(roomName);
@@ -264,5 +287,3 @@ class ScalarAuthClient {
return starterLinkUrl + "?scalar_token=" + encodeURIComponent(this.scalarToken);
}
}
-
-module.exports = ScalarAuthClient;
diff --git a/src/ScalarMessaging.js b/src/ScalarMessaging.js
index 8b87650929..0d61755519 100644
--- a/src/ScalarMessaging.js
+++ b/src/ScalarMessaging.js
@@ -232,13 +232,13 @@ Example:
}
*/
-import SdkConfig from './SdkConfig';
import MatrixClientPeg from './MatrixClientPeg';
import { MatrixEvent } from 'matrix-js-sdk';
import dis from './dispatcher';
import WidgetUtils from './utils/WidgetUtils';
import RoomViewStore from './stores/RoomViewStore';
import { _t } from './languageHandler';
+import {IntegrationManagers} from "./integrations/IntegrationManagers";
function sendResponse(event, res) {
const data = JSON.parse(JSON.stringify(event.data));
@@ -548,7 +548,8 @@ const onMessage = function(event) {
// (See https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage)
let configUrl;
try {
- configUrl = new URL(SdkConfig.get().integrations_ui_url);
+ // TODO: Support multiple integration managers
+ configUrl = new URL(IntegrationManagers.sharedInstance().getPrimaryManager().uiUrl);
} catch (e) {
// No integrations UI URL, ignore silently.
return;
diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index deef8488f4..b8903076c7 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -935,7 +935,7 @@ export default React.createClass({
const CreateRoomDialog = sdk.getComponent('dialogs.CreateRoomDialog');
const modal = Modal.createTrackedDialog('Create Room', '', CreateRoomDialog);
- const [shouldCreate, name, noFederate] = await modal;
+ const [shouldCreate, name, noFederate] = await modal.finished;
if (shouldCreate) {
const createOpts = {};
if (name) createOpts.name = name;
diff --git a/src/components/views/auth/ModularServerConfig.js b/src/components/views/auth/ModularServerConfig.js
index b5af58adf1..ff8d88f738 100644
--- a/src/components/views/auth/ModularServerConfig.js
+++ b/src/components/views/auth/ModularServerConfig.js
@@ -15,13 +15,13 @@ limitations under the License.
*/
import React from 'react';
-import PropTypes from 'prop-types';
import sdk from '../../../index';
import { _t } from '../../../languageHandler';
import {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils";
import SdkConfig from "../../../SdkConfig";
import AutoDiscoveryUtils from "../../../utils/AutoDiscoveryUtils";
import * as ServerType from '../../views/auth/ServerTypeSelector';
+import ServerConfig from "./ServerConfig";
const MODULAR_URL = 'https://modular.im/?utm_source=riot-web&utm_medium=web&utm_campaign=riot-web-authentication';
@@ -33,49 +33,8 @@ const MODULAR_URL = 'https://modular.im/?utm_source=riot-web&utm_medium=web&utm_
* This is a variant of ServerConfig with only the HS field and different body
* text that is specific to the Modular case.
*/
-export default class ModularServerConfig extends React.PureComponent {
- static propTypes = {
- onServerConfigChange: PropTypes.func,
-
- // The current configuration that the user is expecting to change.
- serverConfig: PropTypes.instanceOf(ValidatedServerConfig).isRequired,
-
- delayTimeMs: PropTypes.number, // time to wait before invoking onChanged
-
- // Called after the component calls onServerConfigChange
- onAfterSubmit: PropTypes.func,
-
- // Optional text for the submit button. If falsey, no button will be shown.
- submitText: PropTypes.string,
-
- // Optional class for the submit button. Only applies if the submit button
- // is to be rendered.
- submitClass: PropTypes.string,
- };
-
- static defaultProps = {
- onServerConfigChange: function() {},
- customHsUrl: "",
- delayTimeMs: 0,
- };
-
- constructor(props) {
- super(props);
-
- this.state = {
- busy: false,
- errorText: "",
- hsUrl: props.serverConfig.hsUrl,
- isUrl: props.serverConfig.isUrl,
- };
- }
-
- componentWillReceiveProps(newProps) {
- if (newProps.serverConfig.hsUrl === this.state.hsUrl &&
- newProps.serverConfig.isUrl === this.state.isUrl) return;
-
- this.validateAndApplyServer(newProps.serverConfig.hsUrl, newProps.serverConfig.isUrl);
- }
+export default class ModularServerConfig extends ServerConfig {
+ static propTypes = ServerConfig.propTypes;
async validateAndApplyServer(hsUrl, isUrl) {
// Always try and use the defaults first
@@ -120,35 +79,6 @@ export default class ModularServerConfig extends React.PureComponent {
return this.validateAndApplyServer(this.state.hsUrl, ServerType.TYPES.PREMIUM.identityServerUrl);
}
- onHomeserverBlur = (ev) => {
- this._hsTimeoutId = this._waitThenInvoke(this._hsTimeoutId, () => {
- this.validateServer();
- });
- };
-
- onHomeserverChange = (ev) => {
- const hsUrl = ev.target.value;
- this.setState({ hsUrl });
- };
-
- onSubmit = async (ev) => {
- ev.preventDefault();
- ev.stopPropagation();
- const result = await this.validateServer();
- if (!result) return; // Do not continue.
-
- if (this.props.onAfterSubmit) {
- this.props.onAfterSubmit();
- }
- };
-
- _waitThenInvoke(existingTimeoutId, fn) {
- if (existingTimeoutId) {
- clearTimeout(existingTimeoutId);
- }
- return setTimeout(fn.bind(this), this.props.delayTimeMs);
- }
-
render() {
const Field = sdk.getComponent('elements.Field');
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
diff --git a/src/components/views/auth/RegistrationForm.js b/src/components/views/auth/RegistrationForm.js
index cd3dab12ac..f3b9640e16 100644
--- a/src/components/views/auth/RegistrationForm.js
+++ b/src/components/views/auth/RegistrationForm.js
@@ -2,6 +2,7 @@
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Copyright 2018, 2019 New Vector Ltd
+Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -69,10 +70,10 @@ module.exports = React.createClass({
fieldValid: {},
// The ISO2 country code selected in the phone number entry
phoneCountry: this.props.defaultPhoneCountry,
- username: "",
- email: "",
- phoneNumber: "",
- password: "",
+ username: this.props.defaultUsername || "",
+ email: this.props.defaultEmail || "",
+ phoneNumber: this.props.defaultPhoneNumber || "",
+ password: this.props.defaultPassword || "",
passwordConfirm: "",
passwordComplexity: null,
passwordSafe: false,
@@ -90,7 +91,7 @@ module.exports = React.createClass({
}
const self = this;
- if (this.state.email == '') {
+ if (this.state.email === '') {
const haveIs = Boolean(this.props.serverConfig.isUrl);
let desc;
@@ -455,7 +456,6 @@ module.exports = React.createClass({
ref={field => this[FIELD_EMAIL] = field}
type="text"
label={emailPlaceholder}
- defaultValue={this.props.defaultEmail}
value={this.state.email}
onChange={this.onEmailChange}
onValidate={this.onEmailValidate}
@@ -469,7 +469,6 @@ module.exports = React.createClass({
ref={field => this[FIELD_PASSWORD] = field}
type="password"
label={_t("Password")}
- defaultValue={this.props.defaultPassword}
value={this.state.password}
onChange={this.onPasswordChange}
onValidate={this.onPasswordValidate}
@@ -483,7 +482,6 @@ module.exports = React.createClass({
ref={field => this[FIELD_PASSWORD_CONFIRM] = field}
type="password"
label={_t("Confirm")}
- defaultValue={this.props.defaultPassword}
value={this.state.passwordConfirm}
onChange={this.onPasswordConfirmChange}
onValidate={this.onPasswordConfirmValidate}
@@ -512,7 +510,6 @@ module.exports = React.createClass({
ref={field => this[FIELD_PHONE_NUMBER] = field}
type="text"
label={phoneLabel}
- defaultValue={this.props.defaultPhoneNumber}
value={this.state.phoneNumber}
prefix={phoneCountry}
onChange={this.onPhoneNumberChange}
@@ -528,7 +525,6 @@ module.exports = React.createClass({
type="text"
autoFocus={true}
label={_t("Username")}
- defaultValue={this.props.defaultUsername}
value={this.state.username}
onChange={this.onUsernameChange}
onValidate={this.onUsernameValidate}
diff --git a/src/components/views/avatars/BaseAvatar.js b/src/components/views/avatars/BaseAvatar.js
index 80f5c43d0c..afc6faa18d 100644
--- a/src/components/views/avatars/BaseAvatar.js
+++ b/src/components/views/avatars/BaseAvatar.js
@@ -1,6 +1,7 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2018 New Vector Ltd
+Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -19,7 +20,6 @@ import React from 'react';
import PropTypes from 'prop-types';
import { MatrixClient } from 'matrix-js-sdk';
import AvatarLogic from '../../../Avatar';
-import sdk from '../../../index';
import SettingsStore from "../../../settings/SettingsStore";
import AccessibleButton from '../elements/AccessibleButton';
@@ -121,6 +121,10 @@ module.exports = React.createClass({
);
urls.push(defaultImageUrl); // lowest priority
}
+
+ // deduplicate URLs
+ urls = Array.from(new Set(urls));
+
return {
imageUrls: urls,
defaultImageUrl: defaultImageUrl,
diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js
index 38830d78f2..82953cf52e 100644
--- a/src/components/views/elements/AppTile.js
+++ b/src/components/views/elements/AppTile.js
@@ -22,7 +22,6 @@ import qs from 'querystring';
import React from 'react';
import PropTypes from 'prop-types';
import MatrixClientPeg from '../../../MatrixClientPeg';
-import ScalarAuthClient from '../../../ScalarAuthClient';
import WidgetMessaging from '../../../WidgetMessaging';
import AccessibleButton from './AccessibleButton';
import Modal from '../../../Modal';
@@ -35,7 +34,7 @@ import WidgetUtils from '../../../utils/WidgetUtils';
import dis from '../../../dispatcher';
import ActiveWidgetStore from '../../../stores/ActiveWidgetStore';
import classNames from 'classnames';
-import { showIntegrationsManager } from '../../../integrations/integrations';
+import {IntegrationManagers} from "../../../integrations/IntegrationManagers";
const ALLOWED_APP_URL_SCHEMES = ['https:', 'http:'];
const ENABLE_REACT_PERF = false;
@@ -178,9 +177,22 @@ export default class AppTile extends React.Component {
return;
}
+ const managers = IntegrationManagers.sharedInstance();
+ if (!managers.hasManager()) {
+ console.warn("No integration manager - not setting scalar token", url);
+ this.setState({
+ error: null,
+ widgetUrl: this._addWurlParams(this.props.url),
+ initialising: false,
+ });
+ return;
+ }
+
+ // TODO: Pick the right manager for the widget
+
// Fetch the token before loading the iframe as we need it to mangle the URL
if (!this._scalarClient) {
- this._scalarClient = new ScalarAuthClient();
+ this._scalarClient = managers.getPrimaryManager().getScalarClient();
}
this._scalarClient.getScalarToken().done((token) => {
// Append scalar_token as a query param if not already present
@@ -189,7 +201,7 @@ export default class AppTile extends React.Component {
const params = qs.parse(u.query);
if (!params.scalar_token) {
params.scalar_token = encodeURIComponent(token);
- // u.search must be set to undefined, so that u.format() uses query paramerters - https://nodejs.org/docs/latest/api/url.html#url_url_format_url_options
+ // u.search must be set to undefined, so that u.format() uses query parameters - https://nodejs.org/docs/latest/api/url.html#url_url_format_url_options
u.search = undefined;
u.query = params;
}
@@ -251,11 +263,12 @@ export default class AppTile extends React.Component {
if (this.props.onEditClick) {
this.props.onEditClick();
} else {
- showIntegrationsManager({
- room: this.props.room,
- screen: 'type_' + this.props.type,
- integrationId: this.props.id,
- });
+ // TODO: Open the right manager for the widget
+ IntegrationManagers.sharedInstance().getPrimaryManager().open(
+ this.props.room,
+ this.props.type,
+ this.props.id,
+ );
}
}
diff --git a/src/components/views/elements/Field.js b/src/components/views/elements/Field.js
index 084ec1bd6a..8272b36639 100644
--- a/src/components/views/elements/Field.js
+++ b/src/components/views/elements/Field.js
@@ -48,7 +48,7 @@ export default class Field extends React.PureComponent {
onValidate: PropTypes.func,
// If specified, contents will appear as a tooltip on the element and
// validation feedback tooltips will be suppressed.
- tooltip: PropTypes.node,
+ tooltipContent: PropTypes.node,
// All other props pass through to the .
};
@@ -137,8 +137,7 @@ export default class Field extends React.PureComponent {
}, VALIDATION_THROTTLE_MS);
render() {
- const { element, prefix, onValidate, children, ...inputProps } = this.props;
- delete inputProps.tooltip; // needs to be removed from props but we don't need it here
+ const { element, prefix, onValidate, children, tooltipContent, ...inputProps } = this.props;
const inputElement = element || "input";
@@ -170,11 +169,11 @@ export default class Field extends React.PureComponent {
// Handle displaying feedback on validity
const Tooltip = sdk.getComponent("elements.Tooltip");
let fieldTooltip;
- if (this.props.tooltip || this.state.feedback) {
+ if (tooltipContent || this.state.feedback) {
fieldTooltip =