Merge remote-tracking branch 'origin/develop' into dbkr/disco_is
This commit is contained in:
commit
735c6d73d8
35 changed files with 493 additions and 333 deletions
|
@ -15,6 +15,9 @@ module.exports = {
|
||||||
"number-leading-zero": null,
|
"number-leading-zero": null,
|
||||||
"selector-list-comma-newline-after": null,
|
"selector-list-comma-newline-after": null,
|
||||||
"at-rule-no-unknown": 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"],
|
||||||
|
}],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -148,7 +148,7 @@
|
||||||
"karma-summary-reporter": "^1.5.1",
|
"karma-summary-reporter": "^1.5.1",
|
||||||
"karma-webpack": "^4.0.0-beta.0",
|
"karma-webpack": "^4.0.0-beta.0",
|
||||||
"matrix-mock-request": "^1.2.3",
|
"matrix-mock-request": "^1.2.3",
|
||||||
"matrix-react-test-utils": "^0.1.1",
|
"matrix-react-test-utils": "^0.2.2",
|
||||||
"mocha": "^5.0.5",
|
"mocha": "^5.0.5",
|
||||||
"react-addons-test-utils": "^15.4.0",
|
"react-addons-test-utils": "^15.4.0",
|
||||||
"require-json": "0.0.1",
|
"require-json": "0.0.1",
|
||||||
|
|
|
@ -559,3 +559,7 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus {
|
||||||
.mx_Username_color8 {
|
.mx_Username_color8 {
|
||||||
color: $username-variant8-color;
|
color: $username-variant8-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@define-mixin mx_Settings_fullWidthField {
|
||||||
|
margin-right: 200px;
|
||||||
|
}
|
||||||
|
|
|
@ -26,6 +26,10 @@ limitations under the License.
|
||||||
height: 4em;
|
height: 4em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_ProfileSettings_controls .mx_Field {
|
||||||
|
margin-right: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_ProfileSettings_controls .mx_Field:first-child {
|
.mx_ProfileSettings_controls .mx_Field:first-child {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with 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 {
|
.mx_SetIdServer .mx_Field_input {
|
||||||
width: 300px;
|
@mixin mx_Settings_fullWidthField;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,15 +16,15 @@ limitations under the License.
|
||||||
|
|
||||||
.mx_GeneralUserSettingsTab_changePassword .mx_Field,
|
.mx_GeneralUserSettingsTab_changePassword .mx_Field,
|
||||||
.mx_GeneralUserSettingsTab_themeSection .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 {
|
.mx_GeneralUserSettingsTab_changePassword .mx_Field:first-child {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_GeneralUserSettingsTab_accountSection > .mx_EmailAddresses,
|
.mx_GeneralUserSettingsTab_accountSection .mx_EmailAddresses,
|
||||||
.mx_GeneralUserSettingsTab_accountSection > .mx_PhoneNumbers,
|
.mx_GeneralUserSettingsTab_accountSection .mx_PhoneNumbers,
|
||||||
.mx_GeneralUserSettingsTab_languageInput {
|
.mx_GeneralUserSettingsTab_languageInput {
|
||||||
margin-right: 100px; // Align with the other fields on the page
|
@mixin mx_Settings_fullWidthField;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,5 +15,5 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.mx_PreferencesUserSettingsTab .mx_Field {
|
.mx_PreferencesUserSettingsTab .mx_Field {
|
||||||
margin-right: 100px; // Align with the rest of the controls
|
@mixin mx_Settings_fullWidthField;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.mx_VoiceUserSettingsTab .mx_Field {
|
.mx_VoiceUserSettingsTab .mx_Field {
|
||||||
margin-right: 100px; // align with the rest of the fields
|
@mixin mx_Settings_fullWidthField;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_VoiceUserSettingsTab_missingMediaPermissions {
|
.mx_VoiceUserSettingsTab_missingMediaPermissions {
|
||||||
|
|
|
@ -63,7 +63,7 @@ import SdkConfig from './SdkConfig';
|
||||||
import { showUnknownDeviceDialogForCalls } from './cryptodevices';
|
import { showUnknownDeviceDialogForCalls } from './cryptodevices';
|
||||||
import WidgetUtils from './utils/WidgetUtils';
|
import WidgetUtils from './utils/WidgetUtils';
|
||||||
import WidgetEchoStore from './stores/WidgetEchoStore';
|
import WidgetEchoStore from './stores/WidgetEchoStore';
|
||||||
import ScalarAuthClient from './ScalarAuthClient';
|
import {IntegrationManagers} from "./integrations/IntegrationManagers";
|
||||||
|
|
||||||
global.mxCalls = {
|
global.mxCalls = {
|
||||||
//room_id: MatrixCall
|
//room_id: MatrixCall
|
||||||
|
@ -348,14 +348,20 @@ async function _startCallApp(roomId, type) {
|
||||||
// the state event in anyway, but the resulting widget would then not
|
// 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
|
// work for us. Better that the user knows before everyone else in the
|
||||||
// room sees it.
|
// room sees it.
|
||||||
const scalarClient = new ScalarAuthClient();
|
const managers = IntegrationManagers.sharedInstance();
|
||||||
let haveScalar = false;
|
let haveScalar = true;
|
||||||
try {
|
if (managers.hasManager()) {
|
||||||
await scalarClient.connect();
|
try {
|
||||||
haveScalar = scalarClient.hasCredentials();
|
const scalarClient = managers.getPrimaryManager().getScalarClient();
|
||||||
} catch (e) {
|
await scalarClient.connect();
|
||||||
// fall through
|
haveScalar = scalarClient.hasCredentials();
|
||||||
|
} catch (e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
haveScalar = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!haveScalar) {
|
if (!haveScalar) {
|
||||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
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.
|
// URL, but this will at least allow the integration manager to not be hardcoded.
|
||||||
widgetUrl = SdkConfig.get().integrations_jitsi_widget_url + '?' + queryString;
|
widgetUrl = SdkConfig.get().integrations_jitsi_widget_url + '?' + queryString;
|
||||||
} else {
|
} 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 };
|
const widgetData = { widgetSessionId };
|
||||||
|
|
|
@ -22,7 +22,7 @@ import WidgetMessagingEndpoint from './WidgetMessagingEndpoint';
|
||||||
import ActiveWidgetStore from './stores/ActiveWidgetStore';
|
import ActiveWidgetStore from './stores/ActiveWidgetStore';
|
||||||
import MatrixClientPeg from "./MatrixClientPeg";
|
import MatrixClientPeg from "./MatrixClientPeg";
|
||||||
import RoomViewStore from "./stores/RoomViewStore";
|
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 WIDGET_API_VERSION = '0.0.2'; // Current API version
|
||||||
const SUPPORTED_WIDGET_API_VERSIONS = [
|
const SUPPORTED_WIDGET_API_VERSIONS = [
|
||||||
|
@ -193,11 +193,12 @@ export default class FromWidgetPostMessageApi {
|
||||||
const integType = (data && data.integType) ? data.integType : null;
|
const integType = (data && data.integType) ? data.integType : null;
|
||||||
const integId = (data && data.integId) ? data.integId : null;
|
const integId = (data && data.integId) ? data.integId : null;
|
||||||
|
|
||||||
showIntegrationsManager({
|
// TODO: Open the right integration manager for the widget
|
||||||
room: MatrixClientPeg.get().getRoom(RoomViewStore.getRoomId()),
|
IntegrationManagers.sharedInstance().getPrimaryManager().open(
|
||||||
screen: 'type_' + integType,
|
MatrixClientPeg.get().getRoom(RoomViewStore.getRoomId()),
|
||||||
integrationId: integId,
|
`type_${integType}`,
|
||||||
});
|
integId,
|
||||||
|
);
|
||||||
} else if (action === 'set_always_on_screen') {
|
} else if (action === 'set_always_on_screen') {
|
||||||
// This is a new message: there is no reason to support the deprecated widgetData here
|
// This is a new message: there is no reason to support the deprecated widgetData here
|
||||||
const data = event.data.data;
|
const data = event.data.data;
|
||||||
|
|
|
@ -35,6 +35,7 @@ import { sendLoginRequest } from "./Login";
|
||||||
import * as StorageManager from './utils/StorageManager';
|
import * as StorageManager from './utils/StorageManager';
|
||||||
import SettingsStore from "./settings/SettingsStore";
|
import SettingsStore from "./settings/SettingsStore";
|
||||||
import TypingStore from "./stores/TypingStore";
|
import TypingStore from "./stores/TypingStore";
|
||||||
|
import {IntegrationManagers} from "./integrations/IntegrationManagers";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called at startup, to attempt to build a logged-in Matrix session. It tries
|
* 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();
|
Presence.start();
|
||||||
}
|
}
|
||||||
DMRoomMap.makeShared().start();
|
DMRoomMap.makeShared().start();
|
||||||
|
IntegrationManagers.sharedInstance().startWatching();
|
||||||
ActiveWidgetStore.start();
|
ActiveWidgetStore.start();
|
||||||
|
|
||||||
if (startSyncing) {
|
if (startSyncing) {
|
||||||
|
@ -638,6 +640,7 @@ export function stopMatrixClient(unsetClient=true) {
|
||||||
TypingStore.sharedInstance().reset();
|
TypingStore.sharedInstance().reset();
|
||||||
Presence.stop();
|
Presence.stop();
|
||||||
ActiveWidgetStore.stop();
|
ActiveWidgetStore.stop();
|
||||||
|
IntegrationManagers.sharedInstance().stopWatching();
|
||||||
if (DMRoomMap.shared()) DMRoomMap.shared().stop();
|
if (DMRoomMap.shared()) DMRoomMap.shared().stop();
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
if (cli) {
|
if (cli) {
|
||||||
|
|
|
@ -274,7 +274,7 @@ class ModalManager {
|
||||||
this._reRender();
|
this._reRender();
|
||||||
return {
|
return {
|
||||||
close: closeDialog,
|
close: closeDialog,
|
||||||
then: (resolve, reject) => onFinishedProm.then(resolve, reject),
|
finished: onFinishedProm,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,7 +285,7 @@ class ModalManager {
|
||||||
this._reRender();
|
this._reRender();
|
||||||
return {
|
return {
|
||||||
close: closeDialog,
|
close: closeDialog,
|
||||||
then: (resolve, reject) => onFinishedProm.then(resolve, reject),
|
finished: onFinishedProm,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,20 +29,43 @@ import * as Matrix from 'matrix-js-sdk';
|
||||||
// The version of the integration manager API we're intending to work with
|
// The version of the integration manager API we're intending to work with
|
||||||
const imApiVersion = "1.1";
|
const imApiVersion = "1.1";
|
||||||
|
|
||||||
class ScalarAuthClient {
|
export default class ScalarAuthClient {
|
||||||
constructor() {
|
constructor(apiUrl, uiUrl) {
|
||||||
|
this.apiUrl = apiUrl;
|
||||||
|
this.uiUrl = uiUrl;
|
||||||
this.scalarToken = null;
|
this.scalarToken = null;
|
||||||
// `undefined` to allow `startTermsFlow` to fallback to a default
|
// `undefined` to allow `startTermsFlow` to fallback to a default
|
||||||
// callback if this is unset.
|
// callback if this is unset.
|
||||||
this.termsInteractionCallback = undefined;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
_writeTokenToStore() {
|
||||||
* Determines if setting up a ScalarAuthClient is even possible
|
window.localStorage.setItem("mx_scalar_token_at_" + this.apiUrl, this.scalarToken);
|
||||||
* @returns {boolean} true if possible, false otherwise.
|
if (this.isDefaultManager) {
|
||||||
*/
|
// We remove the old token from storage to migrate upwards. This is safe
|
||||||
static isPossible() {
|
// to do because even if the user switches to /app when this is on /develop
|
||||||
return SdkConfig.get()['integrations_rest_url'] && SdkConfig.get()['integrations_ui_url'];
|
// 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) {
|
setTermsInteractionCallback(callback) {
|
||||||
|
@ -61,8 +84,7 @@ class ScalarAuthClient {
|
||||||
|
|
||||||
// Returns a promise that resolves to a scalar_token string
|
// Returns a promise that resolves to a scalar_token string
|
||||||
getScalarToken() {
|
getScalarToken() {
|
||||||
let token = this.scalarToken;
|
const token = this._readToken();
|
||||||
if (!token) token = window.localStorage.getItem("mx_scalar_token");
|
|
||||||
|
|
||||||
if (!token) {
|
if (!token) {
|
||||||
return this.registerForToken();
|
return this.registerForToken();
|
||||||
|
@ -78,7 +100,7 @@ class ScalarAuthClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
_getAccountName(token) {
|
_getAccountName(token) {
|
||||||
const url = SdkConfig.get().integrations_rest_url + "/account";
|
const url = this.apiUrl + "/account";
|
||||||
|
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
request({
|
request({
|
||||||
|
@ -111,7 +133,7 @@ class ScalarAuthClient {
|
||||||
return token;
|
return token;
|
||||||
}).catch((e) => {
|
}).catch((e) => {
|
||||||
if (e instanceof TermsNotSignedError) {
|
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,
|
// The terms endpoints are new and so live on standard _matrix prefixes,
|
||||||
// but IM rest urls are currently configured with paths, so remove the
|
// but IM rest urls are currently configured with paths, so remove the
|
||||||
// path from the base URL before passing it to the js-sdk
|
// 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
|
// 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 grace period to update their configs, then use the rest url as
|
||||||
// a regular base url.
|
// a regular base url.
|
||||||
const parsedImRestUrl = url.parse(SdkConfig.get().integrations_rest_url);
|
const parsedImRestUrl = url.parse(this.apiUrl);
|
||||||
parsedImRestUrl.path = '';
|
parsedImRestUrl.path = '';
|
||||||
parsedImRestUrl.pathname = '';
|
parsedImRestUrl.pathname = '';
|
||||||
return startTermsFlow([new Service(
|
return startTermsFlow([new Service(
|
||||||
|
@ -147,17 +169,18 @@ class ScalarAuthClient {
|
||||||
return MatrixClientPeg.get().getOpenIdToken().then((tokenObject) => {
|
return MatrixClientPeg.get().getOpenIdToken().then((tokenObject) => {
|
||||||
// Now we can send that to scalar and exchange it for a scalar token
|
// Now we can send that to scalar and exchange it for a scalar token
|
||||||
return this.exchangeForScalarToken(tokenObject);
|
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)
|
// Validate it (this mostly checks to see if the IM needs us to agree to some terms)
|
||||||
return this._checkToken(tokenObject);
|
return this._checkToken(token);
|
||||||
}).then((tokenObject) => {
|
}).then((token) => {
|
||||||
window.localStorage.setItem("mx_scalar_token", tokenObject);
|
this.scalarToken = token;
|
||||||
return tokenObject;
|
this._writeTokenToStore();
|
||||||
|
return token;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
exchangeForScalarToken(openidTokenObject) {
|
exchangeForScalarToken(openidTokenObject) {
|
||||||
const scalarRestUrl = SdkConfig.get().integrations_rest_url;
|
const scalarRestUrl = this.apiUrl;
|
||||||
|
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
request({
|
request({
|
||||||
|
@ -181,7 +204,7 @@ class ScalarAuthClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
getScalarPageTitle(url) {
|
getScalarPageTitle(url) {
|
||||||
let scalarPageLookupUrl = SdkConfig.get().integrations_rest_url + '/widgets/title_lookup';
|
let scalarPageLookupUrl = this.apiUrl + '/widgets/title_lookup';
|
||||||
scalarPageLookupUrl = this.getStarterLink(scalarPageLookupUrl);
|
scalarPageLookupUrl = this.getStarterLink(scalarPageLookupUrl);
|
||||||
scalarPageLookupUrl += '&curl=' + encodeURIComponent(url);
|
scalarPageLookupUrl += '&curl=' + encodeURIComponent(url);
|
||||||
|
|
||||||
|
@ -217,7 +240,7 @@ class ScalarAuthClient {
|
||||||
* @return {Promise} Resolves on completion
|
* @return {Promise} Resolves on completion
|
||||||
*/
|
*/
|
||||||
disableWidgetAssets(widgetType, widgetId) {
|
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);
|
url = this.getStarterLink(url);
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
request({
|
request({
|
||||||
|
@ -246,7 +269,7 @@ class ScalarAuthClient {
|
||||||
getScalarInterfaceUrlForRoom(room, screen, id) {
|
getScalarInterfaceUrlForRoom(room, screen, id) {
|
||||||
const roomId = room.roomId;
|
const roomId = room.roomId;
|
||||||
const roomName = room.name;
|
const roomName = room.name;
|
||||||
let url = SdkConfig.get().integrations_ui_url;
|
let url = this.uiUrl;
|
||||||
url += "?scalar_token=" + encodeURIComponent(this.scalarToken);
|
url += "?scalar_token=" + encodeURIComponent(this.scalarToken);
|
||||||
url += "&room_id=" + encodeURIComponent(roomId);
|
url += "&room_id=" + encodeURIComponent(roomId);
|
||||||
url += "&room_name=" + encodeURIComponent(roomName);
|
url += "&room_name=" + encodeURIComponent(roomName);
|
||||||
|
@ -264,5 +287,3 @@ class ScalarAuthClient {
|
||||||
return starterLinkUrl + "?scalar_token=" + encodeURIComponent(this.scalarToken);
|
return starterLinkUrl + "?scalar_token=" + encodeURIComponent(this.scalarToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = ScalarAuthClient;
|
|
||||||
|
|
|
@ -232,13 +232,13 @@ Example:
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import SdkConfig from './SdkConfig';
|
|
||||||
import MatrixClientPeg from './MatrixClientPeg';
|
import MatrixClientPeg from './MatrixClientPeg';
|
||||||
import { MatrixEvent } from 'matrix-js-sdk';
|
import { MatrixEvent } from 'matrix-js-sdk';
|
||||||
import dis from './dispatcher';
|
import dis from './dispatcher';
|
||||||
import WidgetUtils from './utils/WidgetUtils';
|
import WidgetUtils from './utils/WidgetUtils';
|
||||||
import RoomViewStore from './stores/RoomViewStore';
|
import RoomViewStore from './stores/RoomViewStore';
|
||||||
import { _t } from './languageHandler';
|
import { _t } from './languageHandler';
|
||||||
|
import {IntegrationManagers} from "./integrations/IntegrationManagers";
|
||||||
|
|
||||||
function sendResponse(event, res) {
|
function sendResponse(event, res) {
|
||||||
const data = JSON.parse(JSON.stringify(event.data));
|
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)
|
// (See https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage)
|
||||||
let configUrl;
|
let configUrl;
|
||||||
try {
|
try {
|
||||||
configUrl = new URL(SdkConfig.get().integrations_ui_url);
|
// TODO: Support multiple integration managers
|
||||||
|
configUrl = new URL(IntegrationManagers.sharedInstance().getPrimaryManager().uiUrl);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// No integrations UI URL, ignore silently.
|
// No integrations UI URL, ignore silently.
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -935,7 +935,7 @@ export default React.createClass({
|
||||||
const CreateRoomDialog = sdk.getComponent('dialogs.CreateRoomDialog');
|
const CreateRoomDialog = sdk.getComponent('dialogs.CreateRoomDialog');
|
||||||
const modal = Modal.createTrackedDialog('Create Room', '', CreateRoomDialog);
|
const modal = Modal.createTrackedDialog('Create Room', '', CreateRoomDialog);
|
||||||
|
|
||||||
const [shouldCreate, name, noFederate] = await modal;
|
const [shouldCreate, name, noFederate] = await modal.finished;
|
||||||
if (shouldCreate) {
|
if (shouldCreate) {
|
||||||
const createOpts = {};
|
const createOpts = {};
|
||||||
if (name) createOpts.name = name;
|
if (name) createOpts.name = name;
|
||||||
|
|
|
@ -15,13 +15,13 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils";
|
import {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils";
|
||||||
import SdkConfig from "../../../SdkConfig";
|
import SdkConfig from "../../../SdkConfig";
|
||||||
import AutoDiscoveryUtils from "../../../utils/AutoDiscoveryUtils";
|
import AutoDiscoveryUtils from "../../../utils/AutoDiscoveryUtils";
|
||||||
import * as ServerType from '../../views/auth/ServerTypeSelector';
|
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';
|
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
|
* This is a variant of ServerConfig with only the HS field and different body
|
||||||
* text that is specific to the Modular case.
|
* text that is specific to the Modular case.
|
||||||
*/
|
*/
|
||||||
export default class ModularServerConfig extends React.PureComponent {
|
export default class ModularServerConfig extends ServerConfig {
|
||||||
static propTypes = {
|
static propTypes = ServerConfig.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);
|
|
||||||
}
|
|
||||||
|
|
||||||
async validateAndApplyServer(hsUrl, isUrl) {
|
async validateAndApplyServer(hsUrl, isUrl) {
|
||||||
// Always try and use the defaults first
|
// 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);
|
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() {
|
render() {
|
||||||
const Field = sdk.getComponent('elements.Field');
|
const Field = sdk.getComponent('elements.Field');
|
||||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015, 2016 OpenMarket Ltd
|
||||||
Copyright 2017 Vector Creations Ltd
|
Copyright 2017 Vector Creations Ltd
|
||||||
Copyright 2018, 2019 New Vector Ltd
|
Copyright 2018, 2019 New Vector Ltd
|
||||||
|
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -69,10 +70,10 @@ module.exports = React.createClass({
|
||||||
fieldValid: {},
|
fieldValid: {},
|
||||||
// The ISO2 country code selected in the phone number entry
|
// The ISO2 country code selected in the phone number entry
|
||||||
phoneCountry: this.props.defaultPhoneCountry,
|
phoneCountry: this.props.defaultPhoneCountry,
|
||||||
username: "",
|
username: this.props.defaultUsername || "",
|
||||||
email: "",
|
email: this.props.defaultEmail || "",
|
||||||
phoneNumber: "",
|
phoneNumber: this.props.defaultPhoneNumber || "",
|
||||||
password: "",
|
password: this.props.defaultPassword || "",
|
||||||
passwordConfirm: "",
|
passwordConfirm: "",
|
||||||
passwordComplexity: null,
|
passwordComplexity: null,
|
||||||
passwordSafe: false,
|
passwordSafe: false,
|
||||||
|
@ -90,7 +91,7 @@ module.exports = React.createClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
const self = this;
|
const self = this;
|
||||||
if (this.state.email == '') {
|
if (this.state.email === '') {
|
||||||
const haveIs = Boolean(this.props.serverConfig.isUrl);
|
const haveIs = Boolean(this.props.serverConfig.isUrl);
|
||||||
|
|
||||||
let desc;
|
let desc;
|
||||||
|
@ -455,7 +456,6 @@ module.exports = React.createClass({
|
||||||
ref={field => this[FIELD_EMAIL] = field}
|
ref={field => this[FIELD_EMAIL] = field}
|
||||||
type="text"
|
type="text"
|
||||||
label={emailPlaceholder}
|
label={emailPlaceholder}
|
||||||
defaultValue={this.props.defaultEmail}
|
|
||||||
value={this.state.email}
|
value={this.state.email}
|
||||||
onChange={this.onEmailChange}
|
onChange={this.onEmailChange}
|
||||||
onValidate={this.onEmailValidate}
|
onValidate={this.onEmailValidate}
|
||||||
|
@ -469,7 +469,6 @@ module.exports = React.createClass({
|
||||||
ref={field => this[FIELD_PASSWORD] = field}
|
ref={field => this[FIELD_PASSWORD] = field}
|
||||||
type="password"
|
type="password"
|
||||||
label={_t("Password")}
|
label={_t("Password")}
|
||||||
defaultValue={this.props.defaultPassword}
|
|
||||||
value={this.state.password}
|
value={this.state.password}
|
||||||
onChange={this.onPasswordChange}
|
onChange={this.onPasswordChange}
|
||||||
onValidate={this.onPasswordValidate}
|
onValidate={this.onPasswordValidate}
|
||||||
|
@ -483,7 +482,6 @@ module.exports = React.createClass({
|
||||||
ref={field => this[FIELD_PASSWORD_CONFIRM] = field}
|
ref={field => this[FIELD_PASSWORD_CONFIRM] = field}
|
||||||
type="password"
|
type="password"
|
||||||
label={_t("Confirm")}
|
label={_t("Confirm")}
|
||||||
defaultValue={this.props.defaultPassword}
|
|
||||||
value={this.state.passwordConfirm}
|
value={this.state.passwordConfirm}
|
||||||
onChange={this.onPasswordConfirmChange}
|
onChange={this.onPasswordConfirmChange}
|
||||||
onValidate={this.onPasswordConfirmValidate}
|
onValidate={this.onPasswordConfirmValidate}
|
||||||
|
@ -512,7 +510,6 @@ module.exports = React.createClass({
|
||||||
ref={field => this[FIELD_PHONE_NUMBER] = field}
|
ref={field => this[FIELD_PHONE_NUMBER] = field}
|
||||||
type="text"
|
type="text"
|
||||||
label={phoneLabel}
|
label={phoneLabel}
|
||||||
defaultValue={this.props.defaultPhoneNumber}
|
|
||||||
value={this.state.phoneNumber}
|
value={this.state.phoneNumber}
|
||||||
prefix={phoneCountry}
|
prefix={phoneCountry}
|
||||||
onChange={this.onPhoneNumberChange}
|
onChange={this.onPhoneNumberChange}
|
||||||
|
@ -528,7 +525,6 @@ module.exports = React.createClass({
|
||||||
type="text"
|
type="text"
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
label={_t("Username")}
|
label={_t("Username")}
|
||||||
defaultValue={this.props.defaultUsername}
|
|
||||||
value={this.state.username}
|
value={this.state.username}
|
||||||
onChange={this.onUsernameChange}
|
onChange={this.onUsernameChange}
|
||||||
onValidate={this.onUsernameValidate}
|
onValidate={this.onUsernameValidate}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015, 2016 OpenMarket Ltd
|
||||||
Copyright 2018 New Vector Ltd
|
Copyright 2018 New Vector Ltd
|
||||||
|
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with 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 PropTypes from 'prop-types';
|
||||||
import { MatrixClient } from 'matrix-js-sdk';
|
import { MatrixClient } from 'matrix-js-sdk';
|
||||||
import AvatarLogic from '../../../Avatar';
|
import AvatarLogic from '../../../Avatar';
|
||||||
import sdk from '../../../index';
|
|
||||||
import SettingsStore from "../../../settings/SettingsStore";
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
import AccessibleButton from '../elements/AccessibleButton';
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
|
|
||||||
|
@ -121,6 +121,10 @@ module.exports = React.createClass({
|
||||||
);
|
);
|
||||||
urls.push(defaultImageUrl); // lowest priority
|
urls.push(defaultImageUrl); // lowest priority
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// deduplicate URLs
|
||||||
|
urls = Array.from(new Set(urls));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
imageUrls: urls,
|
imageUrls: urls,
|
||||||
defaultImageUrl: defaultImageUrl,
|
defaultImageUrl: defaultImageUrl,
|
||||||
|
|
|
@ -22,7 +22,6 @@ import qs from 'querystring';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||||
import ScalarAuthClient from '../../../ScalarAuthClient';
|
|
||||||
import WidgetMessaging from '../../../WidgetMessaging';
|
import WidgetMessaging from '../../../WidgetMessaging';
|
||||||
import AccessibleButton from './AccessibleButton';
|
import AccessibleButton from './AccessibleButton';
|
||||||
import Modal from '../../../Modal';
|
import Modal from '../../../Modal';
|
||||||
|
@ -35,7 +34,7 @@ import WidgetUtils from '../../../utils/WidgetUtils';
|
||||||
import dis from '../../../dispatcher';
|
import dis from '../../../dispatcher';
|
||||||
import ActiveWidgetStore from '../../../stores/ActiveWidgetStore';
|
import ActiveWidgetStore from '../../../stores/ActiveWidgetStore';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { showIntegrationsManager } from '../../../integrations/integrations';
|
import {IntegrationManagers} from "../../../integrations/IntegrationManagers";
|
||||||
|
|
||||||
const ALLOWED_APP_URL_SCHEMES = ['https:', 'http:'];
|
const ALLOWED_APP_URL_SCHEMES = ['https:', 'http:'];
|
||||||
const ENABLE_REACT_PERF = false;
|
const ENABLE_REACT_PERF = false;
|
||||||
|
@ -178,9 +177,22 @@ export default class AppTile extends React.Component {
|
||||||
return;
|
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
|
// Fetch the token before loading the iframe as we need it to mangle the URL
|
||||||
if (!this._scalarClient) {
|
if (!this._scalarClient) {
|
||||||
this._scalarClient = new ScalarAuthClient();
|
this._scalarClient = managers.getPrimaryManager().getScalarClient();
|
||||||
}
|
}
|
||||||
this._scalarClient.getScalarToken().done((token) => {
|
this._scalarClient.getScalarToken().done((token) => {
|
||||||
// Append scalar_token as a query param if not already present
|
// 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);
|
const params = qs.parse(u.query);
|
||||||
if (!params.scalar_token) {
|
if (!params.scalar_token) {
|
||||||
params.scalar_token = encodeURIComponent(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.search = undefined;
|
||||||
u.query = params;
|
u.query = params;
|
||||||
}
|
}
|
||||||
|
@ -251,11 +263,12 @@ export default class AppTile extends React.Component {
|
||||||
if (this.props.onEditClick) {
|
if (this.props.onEditClick) {
|
||||||
this.props.onEditClick();
|
this.props.onEditClick();
|
||||||
} else {
|
} else {
|
||||||
showIntegrationsManager({
|
// TODO: Open the right manager for the widget
|
||||||
room: this.props.room,
|
IntegrationManagers.sharedInstance().getPrimaryManager().open(
|
||||||
screen: 'type_' + this.props.type,
|
this.props.room,
|
||||||
integrationId: this.props.id,
|
this.props.type,
|
||||||
});
|
this.props.id,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,7 @@ export default class Field extends React.PureComponent {
|
||||||
onValidate: PropTypes.func,
|
onValidate: PropTypes.func,
|
||||||
// If specified, contents will appear as a tooltip on the element and
|
// If specified, contents will appear as a tooltip on the element and
|
||||||
// validation feedback tooltips will be suppressed.
|
// validation feedback tooltips will be suppressed.
|
||||||
tooltip: PropTypes.node,
|
tooltipContent: PropTypes.node,
|
||||||
// All other props pass through to the <input>.
|
// All other props pass through to the <input>.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -137,8 +137,7 @@ export default class Field extends React.PureComponent {
|
||||||
}, VALIDATION_THROTTLE_MS);
|
}, VALIDATION_THROTTLE_MS);
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { element, prefix, onValidate, children, ...inputProps } = this.props;
|
const { element, prefix, onValidate, children, tooltipContent, ...inputProps } = this.props;
|
||||||
delete inputProps.tooltip; // needs to be removed from props but we don't need it here
|
|
||||||
|
|
||||||
const inputElement = element || "input";
|
const inputElement = element || "input";
|
||||||
|
|
||||||
|
@ -170,11 +169,11 @@ export default class Field extends React.PureComponent {
|
||||||
// Handle displaying feedback on validity
|
// Handle displaying feedback on validity
|
||||||
const Tooltip = sdk.getComponent("elements.Tooltip");
|
const Tooltip = sdk.getComponent("elements.Tooltip");
|
||||||
let fieldTooltip;
|
let fieldTooltip;
|
||||||
if (this.props.tooltip || this.state.feedback) {
|
if (tooltipContent || this.state.feedback) {
|
||||||
fieldTooltip = <Tooltip
|
fieldTooltip = <Tooltip
|
||||||
tooltipClassName="mx_Field_tooltip"
|
tooltipClassName="mx_Field_tooltip"
|
||||||
visible={this.state.feedbackVisible}
|
visible={this.state.feedbackVisible}
|
||||||
label={this.props.tooltip || this.state.feedback}
|
label={tooltipContent || this.state.feedback}
|
||||||
/>;
|
/>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,9 +18,8 @@ limitations under the License.
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
import ScalarAuthClient from '../../../ScalarAuthClient';
|
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import { showIntegrationsManager } from '../../../integrations/integrations';
|
import {IntegrationManagers} from "../../../integrations/IntegrationManagers";
|
||||||
|
|
||||||
export default class ManageIntegsButton extends React.Component {
|
export default class ManageIntegsButton extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -30,12 +29,17 @@ export default class ManageIntegsButton extends React.Component {
|
||||||
onManageIntegrations = (ev) => {
|
onManageIntegrations = (ev) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|
||||||
showIntegrationsManager({ room: this.props.room });
|
const managers = IntegrationManagers.sharedInstance();
|
||||||
|
if (!managers.hasManager()) {
|
||||||
|
managers.openNoManagerDialog();
|
||||||
|
} else {
|
||||||
|
managers.getPrimaryManager().open(this.props.room);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let integrationsButton = <div />;
|
let integrationsButton = <div />;
|
||||||
if (ScalarAuthClient.isPossible()) {
|
if (IntegrationManagers.sharedInstance().hasManager()) {
|
||||||
const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
|
const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
|
||||||
integrationsButton = (
|
integrationsButton = (
|
||||||
<AccessibleButton
|
<AccessibleButton
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2017 Vector Creations Ltd
|
Copyright 2017 Vector Creations Ltd
|
||||||
|
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -17,7 +18,6 @@ limitations under the License.
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||||
import { ContentRepo } from 'matrix-js-sdk';
|
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
import Modal from '../../../Modal';
|
import Modal from '../../../Modal';
|
||||||
|
@ -31,12 +31,21 @@ module.exports = React.createClass({
|
||||||
mxEvent: PropTypes.object.isRequired,
|
mxEvent: PropTypes.object.isRequired,
|
||||||
},
|
},
|
||||||
|
|
||||||
onAvatarClick: function(name) {
|
onAvatarClick: function() {
|
||||||
const httpUrl = MatrixClientPeg.get().mxcUrlToHttp(this.props.mxEvent.getContent().url);
|
const cli = MatrixClientPeg.get();
|
||||||
|
const ev = this.props.mxEvent;
|
||||||
|
const httpUrl = cli.mxcUrlToHttp(ev.getContent().url);
|
||||||
|
|
||||||
|
const room = cli.getRoom(this.props.mxEvent.getRoomId());
|
||||||
|
const text = _t('%(senderDisplayName)s changed the avatar for %(roomName)s', {
|
||||||
|
senderDisplayName: ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(),
|
||||||
|
roomName: room ? room.name : '',
|
||||||
|
});
|
||||||
|
|
||||||
const ImageView = sdk.getComponent("elements.ImageView");
|
const ImageView = sdk.getComponent("elements.ImageView");
|
||||||
const params = {
|
const params = {
|
||||||
src: httpUrl,
|
src: httpUrl,
|
||||||
name: name,
|
name: text,
|
||||||
};
|
};
|
||||||
Modal.createDialog(ImageView, params, "mx_Dialog_lightbox");
|
Modal.createDialog(ImageView, params, "mx_Dialog_lightbox");
|
||||||
},
|
},
|
||||||
|
@ -44,29 +53,22 @@ module.exports = React.createClass({
|
||||||
render: function() {
|
render: function() {
|
||||||
const ev = this.props.mxEvent;
|
const ev = this.props.mxEvent;
|
||||||
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
|
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
|
||||||
const BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
|
const RoomAvatar = sdk.getComponent("avatars.RoomAvatar");
|
||||||
|
|
||||||
const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId());
|
|
||||||
const name = _t('%(senderDisplayName)s changed the avatar for %(roomName)s', {
|
|
||||||
senderDisplayName: senderDisplayName,
|
|
||||||
roomName: room ? room.name : '',
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!ev.getContent().url || ev.getContent().url.trim().length === 0) {
|
if (!ev.getContent().url || ev.getContent().url.trim().length === 0) {
|
||||||
return (
|
return (
|
||||||
<div className="mx_TextualEvent">
|
<div className="mx_TextualEvent">
|
||||||
{ _t('%(senderDisplayName)s removed the room avatar.', {senderDisplayName: senderDisplayName}) }
|
{ _t('%(senderDisplayName)s removed the room avatar.', {senderDisplayName}) }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = ContentRepo.getHttpUriForMxc(
|
const room = MatrixClientPeg.get().getRoom(ev.getRoomId());
|
||||||
MatrixClientPeg.get().getHomeserverUrl(),
|
// Provide all arguments to RoomAvatar via oobData because the avatar is historic
|
||||||
ev.getContent().url,
|
const oobData = {
|
||||||
Math.ceil(14 * window.devicePixelRatio),
|
avatarUrl: ev.getContent().url,
|
||||||
Math.ceil(14 * window.devicePixelRatio),
|
name: room ? room.name : "",
|
||||||
'crop',
|
};
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx_RoomAvatarEvent">
|
<div className="mx_RoomAvatarEvent">
|
||||||
|
@ -75,8 +77,8 @@ module.exports = React.createClass({
|
||||||
{
|
{
|
||||||
'img': () =>
|
'img': () =>
|
||||||
<AccessibleButton key="avatar" className="mx_RoomAvatarEvent_avatar"
|
<AccessibleButton key="avatar" className="mx_RoomAvatarEvent_avatar"
|
||||||
onClick={this.onAvatarClick.bind(this, name)}>
|
onClick={this.onAvatarClick}>
|
||||||
<BaseAvatar width={14} height={14} url={url} name={name} />
|
<RoomAvatar width={14} height={14} oobData={oobData} />
|
||||||
</AccessibleButton>,
|
</AccessibleButton>,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,6 @@ import highlight from 'highlight.js';
|
||||||
import * as HtmlUtils from '../../../HtmlUtils';
|
import * as HtmlUtils from '../../../HtmlUtils';
|
||||||
import {formatDate} from '../../../DateUtils';
|
import {formatDate} from '../../../DateUtils';
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
import ScalarAuthClient from '../../../ScalarAuthClient';
|
|
||||||
import Modal from '../../../Modal';
|
import Modal from '../../../Modal';
|
||||||
import SdkConfig from '../../../SdkConfig';
|
import SdkConfig from '../../../SdkConfig';
|
||||||
import dis from '../../../dispatcher';
|
import dis from '../../../dispatcher';
|
||||||
|
@ -35,6 +34,7 @@ import SettingsStore from "../../../settings/SettingsStore";
|
||||||
import ReplyThread from "../elements/ReplyThread";
|
import ReplyThread from "../elements/ReplyThread";
|
||||||
import {host as matrixtoHost} from '../../../matrix-to';
|
import {host as matrixtoHost} from '../../../matrix-to';
|
||||||
import {pillifyLinks} from '../../../utils/pillify';
|
import {pillifyLinks} from '../../../utils/pillify';
|
||||||
|
import {IntegrationManagers} from "../../../integrations/IntegrationManagers";
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'TextualBody',
|
displayName: 'TextualBody',
|
||||||
|
@ -318,12 +318,19 @@ module.exports = React.createClass({
|
||||||
// which requires the user to click through and THEN we can open the link in a new tab because
|
// which requires the user to click through and THEN we can open the link in a new tab because
|
||||||
// the window.open command occurs in the same stack frame as the onClick callback.
|
// the window.open command occurs in the same stack frame as the onClick callback.
|
||||||
|
|
||||||
|
const managers = IntegrationManagers.sharedInstance();
|
||||||
|
if (!managers.hasManager()) {
|
||||||
|
managers.openNoManagerDialog();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Go fetch a scalar token
|
// Go fetch a scalar token
|
||||||
const scalarClient = new ScalarAuthClient();
|
const integrationManager = managers.getPrimaryManager();
|
||||||
|
const scalarClient = integrationManager.getScalarClient();
|
||||||
scalarClient.connect().then(() => {
|
scalarClient.connect().then(() => {
|
||||||
const completeUrl = scalarClient.getStarterLink(starterLink);
|
const completeUrl = scalarClient.getStarterLink(starterLink);
|
||||||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||||
const integrationsUrl = SdkConfig.get().integrations_ui_url;
|
const integrationsUrl = integrationManager.uiUrl;
|
||||||
Modal.createTrackedDialog('Add an integration', '', QuestionDialog, {
|
Modal.createTrackedDialog('Add an integration', '', QuestionDialog, {
|
||||||
title: _t("Add an Integration"),
|
title: _t("Add an Integration"),
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -29,7 +29,7 @@ import { _t } from '../../../languageHandler';
|
||||||
import WidgetUtils from '../../../utils/WidgetUtils';
|
import WidgetUtils from '../../../utils/WidgetUtils';
|
||||||
import WidgetEchoStore from "../../../stores/WidgetEchoStore";
|
import WidgetEchoStore from "../../../stores/WidgetEchoStore";
|
||||||
import AccessibleButton from '../elements/AccessibleButton';
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
import { showIntegrationsManager } from '../../../integrations/integrations';
|
import {IntegrationManagers} from "../../../integrations/IntegrationManagers";
|
||||||
|
|
||||||
// The maximum number of widgets that can be added in a room
|
// The maximum number of widgets that can be added in a room
|
||||||
const MAX_WIDGETS = 2;
|
const MAX_WIDGETS = 2;
|
||||||
|
@ -128,10 +128,7 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
_launchManageIntegrations: function() {
|
_launchManageIntegrations: function() {
|
||||||
showIntegrationsManager({
|
IntegrationManagers.sharedInstance().getPrimaryManager().open(this.props.room, 'add_integ');
|
||||||
room: this.props.room,
|
|
||||||
screen: 'add_integ',
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
onClickAddWidget: function(e) {
|
onClickAddWidget: function(e) {
|
||||||
|
|
|
@ -18,13 +18,12 @@ import {_t, _td} from '../../../languageHandler';
|
||||||
import AppTile from '../elements/AppTile';
|
import AppTile from '../elements/AppTile';
|
||||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
import ScalarAuthClient from '../../../ScalarAuthClient';
|
|
||||||
import dis from '../../../dispatcher';
|
import dis from '../../../dispatcher';
|
||||||
import AccessibleButton from '../elements/AccessibleButton';
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
import WidgetUtils from '../../../utils/WidgetUtils';
|
import WidgetUtils from '../../../utils/WidgetUtils';
|
||||||
import ActiveWidgetStore from '../../../stores/ActiveWidgetStore';
|
import ActiveWidgetStore from '../../../stores/ActiveWidgetStore';
|
||||||
import PersistedElement from "../elements/PersistedElement";
|
import PersistedElement from "../elements/PersistedElement";
|
||||||
import { showIntegrationsManager } from '../../../integrations/integrations';
|
import {IntegrationManagers} from "../../../integrations/IntegrationManagers";
|
||||||
|
|
||||||
const widgetType = 'm.stickerpicker';
|
const widgetType = 'm.stickerpicker';
|
||||||
|
|
||||||
|
@ -67,8 +66,9 @@ export default class Stickerpicker extends React.Component {
|
||||||
|
|
||||||
_acquireScalarClient() {
|
_acquireScalarClient() {
|
||||||
if (this.scalarClient) return Promise.resolve(this.scalarClient);
|
if (this.scalarClient) return Promise.resolve(this.scalarClient);
|
||||||
if (ScalarAuthClient.isPossible()) {
|
// TODO: Pick the right manager for the widget
|
||||||
this.scalarClient = new ScalarAuthClient();
|
if (IntegrationManagers.sharedInstance().hasManager()) {
|
||||||
|
this.scalarClient = IntegrationManagers.sharedInstance().getPrimaryManager().getScalarClient();
|
||||||
return this.scalarClient.connect().then(() => {
|
return this.scalarClient.connect().then(() => {
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
return this.scalarClient;
|
return this.scalarClient;
|
||||||
|
@ -348,11 +348,12 @@ export default class Stickerpicker extends React.Component {
|
||||||
* Launch the integrations manager on the stickers integration page
|
* Launch the integrations manager on the stickers integration page
|
||||||
*/
|
*/
|
||||||
_launchManageIntegrations() {
|
_launchManageIntegrations() {
|
||||||
showIntegrationsManager({
|
// TODO: Open the right integration manager for the widget
|
||||||
room: this.props.room,
|
IntegrationManagers.sharedInstance().getPrimaryManager().open(
|
||||||
screen: `type_${widgetType}`,
|
this.props.room,
|
||||||
integrationId: this.state.widgetId,
|
`type_${widgetType}`,
|
||||||
});
|
this.state.widgetId,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
|
@ -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");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -14,15 +14,14 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import request from 'browser-request';
|
|
||||||
import url from 'url';
|
import url from 'url';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {_t} from "../../../languageHandler";
|
import {_t} from "../../../languageHandler";
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
import MatrixClientPeg from "../../../MatrixClientPeg";
|
import MatrixClientPeg from "../../../MatrixClientPeg";
|
||||||
import SdkConfig from "../../../SdkConfig";
|
import SdkConfig from "../../../SdkConfig";
|
||||||
import Field from "../elements/Field";
|
|
||||||
import Modal from '../../../Modal';
|
import Modal from '../../../Modal';
|
||||||
|
import dis from "../../../dispatcher";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If a url has no path component, etc. abbreviate it to just the hostname
|
* If a url has no path component, etc. abbreviate it to just the hostname
|
||||||
|
@ -59,41 +58,39 @@ function unabbreviateUrl(u) {
|
||||||
/**
|
/**
|
||||||
* Check an IS URL is valid, including liveness check
|
* Check an IS URL is valid, including liveness check
|
||||||
*
|
*
|
||||||
* @param {string} isUrl The url to check
|
* @param {string} u The url to check
|
||||||
* @returns {string} null if url passes all checks, otherwise i18ned error string
|
* @returns {string} null if url passes all checks, otherwise i18ned error string
|
||||||
*/
|
*/
|
||||||
async function checkIsUrl(isUrl) {
|
async function checkIdentityServerUrl(u) {
|
||||||
const parsedUrl = url.parse(isUrl);
|
const parsedUrl = url.parse(u);
|
||||||
|
|
||||||
if (parsedUrl.protocol !== 'https:') return _t("Identity Server URL must be HTTPS");
|
if (parsedUrl.protocol !== 'https:') return _t("Identity Server URL must be HTTPS");
|
||||||
|
|
||||||
// XXX: duplicated logic from js-sdk but it's quite tied up in the validation logic in the
|
// XXX: duplicated logic from js-sdk but it's quite tied up in the validation logic in the
|
||||||
// js-sdk so probably as easy to duplicate it than to separate it out so we can reuse it
|
// js-sdk so probably as easy to duplicate it than to separate it out so we can reuse it
|
||||||
return new Promise((resolve) => {
|
try {
|
||||||
request(
|
const response = await fetch(u + '/_matrix/identity/api/v1');
|
||||||
// also XXX: we don't really know whether to hit /v1 or /v2 for this: we
|
if (response.ok) {
|
||||||
// probably want a /versions endpoint like the C/S API.
|
return null;
|
||||||
{ method: "GET", url: isUrl + '/_matrix/identity/api/v1' },
|
} else if (response.status < 200 || response.status >= 300) {
|
||||||
(err, response, body) => {
|
return _t("Not a valid Identity Server (status code %(code)s)", {code: response.status});
|
||||||
if (err) {
|
} else {
|
||||||
resolve(_t("Could not connect to ID Server"));
|
return _t("Could not connect to Identity Server");
|
||||||
} else if (response.status < 200 || response.status >= 300) {
|
}
|
||||||
resolve(_t("Not a valid ID Server (status code %(code)s)", {code: response.status}));
|
} catch (e) {
|
||||||
} else {
|
return _t("Could not connect to Identity Server");
|
||||||
resolve(null);
|
}
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class SetIdServer extends React.Component {
|
export default class SetIdServer extends React.Component {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
let defaultIdServer = abbreviateUrl(MatrixClientPeg.get().getIdentityServerUrl());
|
let defaultIdServer = '';
|
||||||
if (!defaultIdServer) {
|
if (!MatrixClientPeg.get().getIdentityServerUrl() && SdkConfig.get()['validated_server_config']['isUrl']) {
|
||||||
defaultIdServer = abbreviateUrl(SdkConfig.get()['validated_server_config']['idServer']) || '';
|
// 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']);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
|
@ -115,7 +112,7 @@ export default class SetIdServer extends React.Component {
|
||||||
const InlineSpinner = sdk.getComponent('views.elements.InlineSpinner');
|
const InlineSpinner = sdk.getComponent('views.elements.InlineSpinner');
|
||||||
return <div>
|
return <div>
|
||||||
<InlineSpinner />
|
<InlineSpinner />
|
||||||
{ _t("Checking Server") }
|
{ _t("Checking server") }
|
||||||
</div>;
|
</div>;
|
||||||
} else if (this.state.error) {
|
} else if (this.state.error) {
|
||||||
return this.state.error;
|
return this.state.error;
|
||||||
|
@ -128,18 +125,21 @@ export default class SetIdServer extends React.Component {
|
||||||
return !!this.state.idServer && !this.state.busy;
|
return !!this.state.idServer && !this.state.busy;
|
||||||
};
|
};
|
||||||
|
|
||||||
_saveIdServer = async () => {
|
_saveIdServer = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
this.setState({busy: true});
|
this.setState({busy: true});
|
||||||
|
|
||||||
const fullUrl = unabbreviateUrl(this.state.idServer);
|
const fullUrl = unabbreviateUrl(this.state.idServer);
|
||||||
|
|
||||||
const errStr = await checkIsUrl(fullUrl);
|
const errStr = await checkIdentityServerUrl(fullUrl);
|
||||||
|
|
||||||
let newFormValue = this.state.idServer;
|
let newFormValue = this.state.idServer;
|
||||||
if (!errStr) {
|
if (!errStr) {
|
||||||
MatrixClientPeg.get().setIdentityServerUrl(fullUrl);
|
MatrixClientPeg.get().setIdentityServerUrl(fullUrl);
|
||||||
localStorage.removeItem("mx_is_access_token");
|
localStorage.removeItem("mx_is_access_token");
|
||||||
localStorage.setItem("mx_is_url", fullUrl);
|
localStorage.setItem("mx_is_url", fullUrl);
|
||||||
|
dis.dispatch({action: 'id_server_changed'});
|
||||||
newFormValue = '';
|
newFormValue = '';
|
||||||
}
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -184,6 +184,7 @@ export default class SetIdServer extends React.Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const AccessibleButton = sdk.getComponent('views.elements.AccessibleButton');
|
const AccessibleButton = sdk.getComponent('views.elements.AccessibleButton');
|
||||||
|
const Field = sdk.getComponent('elements.Field');
|
||||||
const idServerUrl = this.state.currentClientIdServer;
|
const idServerUrl = this.state.currentClientIdServer;
|
||||||
let sectionTitle;
|
let sectionTitle;
|
||||||
let bodyText;
|
let bodyText;
|
||||||
|
@ -198,9 +199,9 @@ export default class SetIdServer extends React.Component {
|
||||||
} else {
|
} else {
|
||||||
sectionTitle = _t("Identity Server");
|
sectionTitle = _t("Identity Server");
|
||||||
bodyText = _t(
|
bodyText = _t(
|
||||||
"You are not currently using an Identity Server. " +
|
"You are not currently using an identity server. " +
|
||||||
"To discover and be discoverable by existing contacts you know, " +
|
"To discover and be discoverable by existing contacts you know, " +
|
||||||
"add one below",
|
"add one below.",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,12 +231,12 @@ export default class SetIdServer extends React.Component {
|
||||||
id="mx_SetIdServer_idServer"
|
id="mx_SetIdServer_idServer"
|
||||||
type="text" value={this.state.idServer} autoComplete="off"
|
type="text" value={this.state.idServer} autoComplete="off"
|
||||||
onChange={this._onIdentityServerChanged}
|
onChange={this._onIdentityServerChanged}
|
||||||
tooltip={this._getTooltip()}
|
tooltipContent={this._getTooltip()}
|
||||||
/>
|
/>
|
||||||
<input className="mx_Dialog_primary"
|
<AccessibleButton type="submit" kind="primary_sm"
|
||||||
type="submit" value={_t("Change")}
|
onClick={this._saveIdServer}
|
||||||
disabled={!this._idServerChangeEnabled()}
|
disabled={!this._idServerChangeEnabled()}
|
||||||
/>
|
>{_t("Change")}</AccessibleButton>
|
||||||
{discoSection}
|
{discoSection}
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2019 New Vector Ltd
|
Copyright 2019 New Vector Ltd
|
||||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -26,6 +27,7 @@ import LanguageDropdown from "../../../elements/LanguageDropdown";
|
||||||
import AccessibleButton from "../../../elements/AccessibleButton";
|
import AccessibleButton from "../../../elements/AccessibleButton";
|
||||||
import DeactivateAccountDialog from "../../../dialogs/DeactivateAccountDialog";
|
import DeactivateAccountDialog from "../../../dialogs/DeactivateAccountDialog";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
|
import {THEMES} from "../../../../../themes";
|
||||||
import PlatformPeg from "../../../../../PlatformPeg";
|
import PlatformPeg from "../../../../../PlatformPeg";
|
||||||
import MatrixClientPeg from "../../../../../MatrixClientPeg";
|
import MatrixClientPeg from "../../../../../MatrixClientPeg";
|
||||||
import sdk from "../../../../..";
|
import sdk from "../../../../..";
|
||||||
|
@ -43,9 +45,22 @@ export default class GeneralUserSettingsTab extends React.Component {
|
||||||
this.state = {
|
this.state = {
|
||||||
language: languageHandler.getCurrentLanguage(),
|
language: languageHandler.getCurrentLanguage(),
|
||||||
theme: SettingsStore.getValueAt(SettingLevel.ACCOUNT, "theme"),
|
theme: SettingsStore.getValueAt(SettingLevel.ACCOUNT, "theme"),
|
||||||
|
haveIdServer: Boolean(MatrixClientPeg.get().getIdentityServerUrl()),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.dispatcherRef = dis.register(this._onAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
dis.unregister(this.dispatcherRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
_onAction = (payload) => {
|
||||||
|
if (payload.action === 'id_server_changed') {
|
||||||
|
this.setState({haveIdServer: Boolean(MatrixClientPeg.get().getIdentityServerUrl())});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
_onLanguageChange = (newLanguage) => {
|
_onLanguageChange = (newLanguage) => {
|
||||||
if (this.state.language === newLanguage) return;
|
if (this.state.language === newLanguage) return;
|
||||||
|
|
||||||
|
@ -122,7 +137,7 @@ export default class GeneralUserSettingsTab extends React.Component {
|
||||||
onFinished={this._onPasswordChanged} />
|
onFinished={this._onPasswordChanged} />
|
||||||
);
|
);
|
||||||
|
|
||||||
const threepidSection = MatrixClientPeg.get().getIdentityServerUrl() ? <div>
|
const threepidSection = this.state.haveIdServer ? <div>
|
||||||
<span className="mx_SettingsTab_subheading">{_t("Email addresses")}</span>
|
<span className="mx_SettingsTab_subheading">{_t("Email addresses")}</span>
|
||||||
<EmailAddresses />
|
<EmailAddresses />
|
||||||
|
|
||||||
|
@ -160,8 +175,9 @@ export default class GeneralUserSettingsTab extends React.Component {
|
||||||
<span className="mx_SettingsTab_subheading">{_t("Theme")}</span>
|
<span className="mx_SettingsTab_subheading">{_t("Theme")}</span>
|
||||||
<Field id="theme" label={_t("Theme")} element="select"
|
<Field id="theme" label={_t("Theme")} element="select"
|
||||||
value={this.state.theme} onChange={this._onThemeChange}>
|
value={this.state.theme} onChange={this._onThemeChange}>
|
||||||
<option value="light">{_t("Light theme")}</option>
|
{Object.entries(THEMES).map(([theme, text]) => {
|
||||||
<option value="dark">{_t("Dark theme")}</option>
|
return <option key={theme} value={theme}>{_t(text)}</option>;
|
||||||
|
})}
|
||||||
</Field>
|
</Field>
|
||||||
<SettingsFlag name="useCompactLayout" level={SettingLevel.ACCOUNT} />
|
<SettingsFlag name="useCompactLayout" level={SettingLevel.ACCOUNT} />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -251,6 +251,8 @@
|
||||||
"%(widgetName)s widget modified by %(senderName)s": "%(widgetName)s widget modified by %(senderName)s",
|
"%(widgetName)s widget modified by %(senderName)s": "%(widgetName)s widget modified by %(senderName)s",
|
||||||
"%(widgetName)s widget added by %(senderName)s": "%(widgetName)s widget added by %(senderName)s",
|
"%(widgetName)s widget added by %(senderName)s": "%(widgetName)s widget added by %(senderName)s",
|
||||||
"%(widgetName)s widget removed by %(senderName)s": "%(widgetName)s widget removed by %(senderName)s",
|
"%(widgetName)s widget removed by %(senderName)s": "%(widgetName)s widget removed by %(senderName)s",
|
||||||
|
"Light theme": "Light theme",
|
||||||
|
"Dark theme": "Dark theme",
|
||||||
"%(displayName)s is typing …": "%(displayName)s is typing …",
|
"%(displayName)s is typing …": "%(displayName)s is typing …",
|
||||||
"%(names)s and %(count)s others are typing …|other": "%(names)s and %(count)s others are typing …",
|
"%(names)s and %(count)s others are typing …|other": "%(names)s and %(count)s others are typing …",
|
||||||
"%(names)s and %(count)s others are typing …|one": "%(names)s and one other is typing …",
|
"%(names)s and %(count)s others are typing …|one": "%(names)s and one other is typing …",
|
||||||
|
@ -538,16 +540,16 @@
|
||||||
"Display Name": "Display Name",
|
"Display Name": "Display Name",
|
||||||
"Save": "Save",
|
"Save": "Save",
|
||||||
"Identity Server URL must be HTTPS": "Identity Server URL must be HTTPS",
|
"Identity Server URL must be HTTPS": "Identity Server URL must be HTTPS",
|
||||||
"Could not connect to ID Server": "Could not connect to ID Server",
|
"Not a valid Identity Server (status code %(code)s)": "Not a valid Identity Server (status code %(code)s)",
|
||||||
"Not a valid ID Server (status code %(code)s)": "Not a valid ID Server (status code %(code)s)",
|
"Could not connect to Identity Server": "Could not connect to Identity Server",
|
||||||
"Checking Server": "Checking Server",
|
"Checking server": "Checking server",
|
||||||
"Disconnect ID Server": "Disconnect ID Server",
|
"Disconnect ID Server": "Disconnect ID Server",
|
||||||
"Disconnect from the ID Server <idserver></idserver>?": "Disconnect from the ID Server <idserver></idserver>?",
|
"Disconnect from the ID Server <idserver></idserver>?": "Disconnect from the ID Server <idserver></idserver>?",
|
||||||
"Disconnect": "Disconnect",
|
"Disconnect": "Disconnect",
|
||||||
"Identity Server (%(server)s)": "Identity Server (%(server)s)",
|
"Identity Server (%(server)s)": "Identity Server (%(server)s)",
|
||||||
"You are currently using <server></server> to discover and be discoverable by existing contacts you know. You can change your identity server below.": "You are currently using <server></server> to discover and be discoverable by existing contacts you know. You can change your identity server below.",
|
"You are currently using <server></server> to discover and be discoverable by existing contacts you know. You can change your identity server below.": "You are currently using <server></server> to discover and be discoverable by existing contacts you know. You can change your identity server below.",
|
||||||
"Identity Server": "Identity Server",
|
"Identity Server": "Identity Server",
|
||||||
"You are not currently using an Identity Server. To discover and be discoverable by existing contacts you know, add one below": "You are not currently using an Identity Server. To discover and be discoverable by existing contacts you know, add one below",
|
"You are not currently using an identity server. To discover and be discoverable by existing contacts you know, add one below.": "You are not currently using an identity server. To discover and be discoverable by existing contacts you know, add one below.",
|
||||||
"Disconnecting from your identity server will mean you won’t be discoverable by other users and you won’t be able to invite others by email or phone.": "Disconnecting from your identity server will mean you won’t be discoverable by other users and you won’t be able to invite others by email or phone.",
|
"Disconnecting from your identity server will mean you won’t be discoverable by other users and you won’t be able to invite others by email or phone.": "Disconnecting from your identity server will mean you won’t be discoverable by other users and you won’t be able to invite others by email or phone.",
|
||||||
"Change": "Change",
|
"Change": "Change",
|
||||||
"Flair": "Flair",
|
"Flair": "Flair",
|
||||||
|
@ -561,8 +563,6 @@
|
||||||
"Set a new account password...": "Set a new account password...",
|
"Set a new account password...": "Set a new account password...",
|
||||||
"Language and region": "Language and region",
|
"Language and region": "Language and region",
|
||||||
"Theme": "Theme",
|
"Theme": "Theme",
|
||||||
"Light theme": "Light theme",
|
|
||||||
"Dark theme": "Dark theme",
|
|
||||||
"Account management": "Account management",
|
"Account management": "Account management",
|
||||||
"Deactivating your account is a permanent action - be careful!": "Deactivating your account is a permanent action - be careful!",
|
"Deactivating your account is a permanent action - be careful!": "Deactivating your account is a permanent action - be careful!",
|
||||||
"Deactivate Account": "Deactivate Account",
|
"Deactivate Account": "Deactivate Account",
|
||||||
|
|
81
src/integrations/IntegrationManagerInstance.js
Normal file
81
src/integrations/IntegrationManagerInstance.js
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
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 ScalarAuthClient from "../ScalarAuthClient";
|
||||||
|
import sdk from "../index";
|
||||||
|
import {dialogTermsInteractionCallback, TermsNotSignedError} from "../Terms";
|
||||||
|
import type {Room} from "matrix-js-sdk";
|
||||||
|
import Modal from '../Modal';
|
||||||
|
|
||||||
|
export class IntegrationManagerInstance {
|
||||||
|
apiUrl: string;
|
||||||
|
uiUrl: string;
|
||||||
|
|
||||||
|
constructor(apiUrl: string, uiUrl: string) {
|
||||||
|
this.apiUrl = apiUrl;
|
||||||
|
this.uiUrl = uiUrl;
|
||||||
|
|
||||||
|
// Per the spec: UI URL is optional.
|
||||||
|
if (!this.uiUrl) this.uiUrl = this.apiUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
getScalarClient(): ScalarAuthClient {
|
||||||
|
return new ScalarAuthClient(this.apiUrl, this.uiUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
async open(room: Room = null, screen: string = null, integrationId: string = null): void {
|
||||||
|
const IntegrationsManager = sdk.getComponent("views.settings.IntegrationsManager");
|
||||||
|
const dialog = Modal.createTrackedDialog(
|
||||||
|
'Integration Manager', '', IntegrationsManager,
|
||||||
|
{loading: true}, 'mx_IntegrationsManager',
|
||||||
|
);
|
||||||
|
|
||||||
|
const client = this.getScalarClient();
|
||||||
|
client.setTermsInteractionCallback((policyInfo, agreedUrls) => {
|
||||||
|
// To avoid visual glitching of two modals stacking briefly, we customise the
|
||||||
|
// terms dialog sizing when it will appear for the integrations manager so that
|
||||||
|
// it gets the same basic size as the IM's own modal.
|
||||||
|
return dialogTermsInteractionCallback(
|
||||||
|
policyInfo, agreedUrls, 'mx_TermsDialog_forIntegrationsManager',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const newProps = {};
|
||||||
|
try {
|
||||||
|
await client.connect();
|
||||||
|
if (!client.hasCredentials()) {
|
||||||
|
newProps["connected"] = false;
|
||||||
|
} else {
|
||||||
|
newProps["url"] = client.getScalarInterfaceUrlForRoom(room, screen, integrationId);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof TermsNotSignedError) {
|
||||||
|
dialog.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.error(e);
|
||||||
|
newProps["connected"] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the old dialog and open a new one
|
||||||
|
dialog.close();
|
||||||
|
Modal.createTrackedDialog(
|
||||||
|
'Integration Manager', '', IntegrationsManager,
|
||||||
|
newProps, 'mx_IntegrationsManager',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
113
src/integrations/IntegrationManagers.js
Normal file
113
src/integrations/IntegrationManagers.js
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
/*
|
||||||
|
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 sdk from "../index";
|
||||||
|
import Modal from '../Modal';
|
||||||
|
import {IntegrationManagerInstance} from "./IntegrationManagerInstance";
|
||||||
|
import type {MatrixClient, MatrixEvent} from "matrix-js-sdk";
|
||||||
|
import WidgetUtils from "../utils/WidgetUtils";
|
||||||
|
import MatrixClientPeg from "../MatrixClientPeg";
|
||||||
|
|
||||||
|
export class IntegrationManagers {
|
||||||
|
static _instance;
|
||||||
|
|
||||||
|
static sharedInstance(): IntegrationManagers {
|
||||||
|
if (!IntegrationManagers._instance) {
|
||||||
|
IntegrationManagers._instance = new IntegrationManagers();
|
||||||
|
}
|
||||||
|
return IntegrationManagers._instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
_managers: IntegrationManagerInstance[] = [];
|
||||||
|
_client: MatrixClient;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this._compileManagers();
|
||||||
|
}
|
||||||
|
|
||||||
|
startWatching(): void {
|
||||||
|
this.stopWatching();
|
||||||
|
this._client = MatrixClientPeg.get();
|
||||||
|
this._client.on("accountData", this._onAccountData.bind(this));
|
||||||
|
this._compileManagers();
|
||||||
|
}
|
||||||
|
|
||||||
|
stopWatching(): void {
|
||||||
|
if (!this._client) return;
|
||||||
|
this._client.removeListener("accountData", this._onAccountData.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
_compileManagers() {
|
||||||
|
this._managers = [];
|
||||||
|
this._setupConfiguredManager();
|
||||||
|
this._setupAccountManagers();
|
||||||
|
}
|
||||||
|
|
||||||
|
_setupConfiguredManager() {
|
||||||
|
const apiUrl = SdkConfig.get()['integrations_rest_url'];
|
||||||
|
const uiUrl = SdkConfig.get()['integrations_ui_url'];
|
||||||
|
|
||||||
|
if (apiUrl && uiUrl) {
|
||||||
|
this._managers.push(new IntegrationManagerInstance(apiUrl, uiUrl));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_setupAccountManagers() {
|
||||||
|
if (!this._client || !this._client.getUserId()) return; // not logged in
|
||||||
|
const widgets = WidgetUtils.getIntegrationManagerWidgets();
|
||||||
|
widgets.forEach(w => {
|
||||||
|
const data = w.content['data'];
|
||||||
|
if (!data) return;
|
||||||
|
|
||||||
|
const uiUrl = w.content['url'];
|
||||||
|
const apiUrl = data['api_url'];
|
||||||
|
if (!apiUrl || !uiUrl) return;
|
||||||
|
|
||||||
|
this._managers.push(new IntegrationManagerInstance(apiUrl, uiUrl));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_onAccountData(ev: MatrixEvent): void {
|
||||||
|
if (ev.getType() === 'm.widgets') {
|
||||||
|
this._compileManagers();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hasManager(): boolean {
|
||||||
|
return this._managers.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPrimaryManager(): IntegrationManagerInstance {
|
||||||
|
if (this.hasManager()) {
|
||||||
|
return this._managers[this._managers.length - 1];
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
openNoManagerDialog(): void {
|
||||||
|
// TODO: Is it Integrations (plural) or Integration (singular). Singular is easier spoken.
|
||||||
|
const IntegrationsManager = sdk.getComponent("views.settings.IntegrationsManager");
|
||||||
|
Modal.createTrackedDialog(
|
||||||
|
"Integration Manager", "None", IntegrationsManager,
|
||||||
|
{configured: false}, 'mx_IntegrationsManager',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For debugging
|
||||||
|
global.mxIntegrationManagers = IntegrationManagers;
|
|
@ -1,79 +0,0 @@
|
||||||
/*
|
|
||||||
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 sdk from "../index";
|
|
||||||
import ScalarAuthClient from '../ScalarAuthClient';
|
|
||||||
import Modal from '../Modal';
|
|
||||||
import { TermsNotSignedError, dialogTermsInteractionCallback } from '../Terms';
|
|
||||||
|
|
||||||
export async function showIntegrationsManager(opts) {
|
|
||||||
const IntegrationsManager = sdk.getComponent("views.settings.IntegrationsManager");
|
|
||||||
|
|
||||||
let props = {};
|
|
||||||
if (ScalarAuthClient.isPossible()) {
|
|
||||||
props.loading = true;
|
|
||||||
} else {
|
|
||||||
props.configured = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const close = Modal.createTrackedDialog(
|
|
||||||
'Integrations Manager', '', IntegrationsManager, props, "mx_IntegrationsManager",
|
|
||||||
).close;
|
|
||||||
|
|
||||||
if (!ScalarAuthClient.isPossible()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const scalarClient = new ScalarAuthClient();
|
|
||||||
scalarClient.setTermsInteractionCallback(integrationsTermsInteractionCallback);
|
|
||||||
try {
|
|
||||||
await scalarClient.connect();
|
|
||||||
if (!scalarClient.hasCredentials()) {
|
|
||||||
props = { connected: false };
|
|
||||||
} else {
|
|
||||||
props = {
|
|
||||||
url: scalarClient.getScalarInterfaceUrlForRoom(
|
|
||||||
opts.room,
|
|
||||||
opts.screen,
|
|
||||||
opts.integrationId,
|
|
||||||
),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
if (err instanceof TermsNotSignedError) {
|
|
||||||
// user canceled terms dialog, so just cancel the action
|
|
||||||
close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.error(err);
|
|
||||||
props = { connected: false };
|
|
||||||
}
|
|
||||||
close();
|
|
||||||
Modal.createTrackedDialog('Integrations Manager', '', IntegrationsManager, props, "mx_IntegrationsManager");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* To avoid visual glitching of two modals stacking briefly, we customise the
|
|
||||||
* terms dialog sizing when it will appear for the integrations manager so that
|
|
||||||
* it gets the same basic size as the IM's own modal.
|
|
||||||
*/
|
|
||||||
function integrationsTermsInteractionCallback(policiesAndServicePairs, agreedUrls) {
|
|
||||||
return dialogTermsInteractionCallback(
|
|
||||||
policiesAndServicePairs,
|
|
||||||
agreedUrls,
|
|
||||||
"mx_TermsDialog_forIntegrationsManager",
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2019 New Vector Ltd
|
Copyright 2019 New Vector Ltd
|
||||||
|
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -15,17 +16,13 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import SettingController from "./SettingController";
|
import SettingController from "./SettingController";
|
||||||
|
import {DEFAULT_THEME, THEMES} from "../../themes";
|
||||||
const SUPPORTED_THEMES = [
|
|
||||||
"light",
|
|
||||||
"dark",
|
|
||||||
];
|
|
||||||
|
|
||||||
export default class ThemeController extends SettingController {
|
export default class ThemeController extends SettingController {
|
||||||
getValueOverride(level, roomId, calculatedValue, calculatedAtLevel) {
|
getValueOverride(level, roomId, calculatedValue, calculatedAtLevel) {
|
||||||
// Override in case some no longer supported theme is stored here
|
// Override in case some no longer supported theme is stored here
|
||||||
if (!SUPPORTED_THEMES.includes(calculatedValue)) {
|
if (!THEMES[calculatedValue]) {
|
||||||
return "light";
|
return DEFAULT_THEME;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null; // no override
|
return null; // no override
|
||||||
|
|
24
src/themes.js
Normal file
24
src/themes.js
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
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 {_td} from "./languageHandler";
|
||||||
|
|
||||||
|
export const DEFAULT_THEME = "light";
|
||||||
|
|
||||||
|
export const THEMES = {
|
||||||
|
"light": _td("Light theme"),
|
||||||
|
"dark": _td("Dark theme"),
|
||||||
|
};
|
|
@ -27,6 +27,7 @@ import WidgetEchoStore from '../stores/WidgetEchoStore';
|
||||||
const WIDGET_WAIT_TIME = 20000;
|
const WIDGET_WAIT_TIME = 20000;
|
||||||
import SettingsStore from "../settings/SettingsStore";
|
import SettingsStore from "../settings/SettingsStore";
|
||||||
import ActiveWidgetStore from "../stores/ActiveWidgetStore";
|
import ActiveWidgetStore from "../stores/ActiveWidgetStore";
|
||||||
|
import {IntegrationManagers} from "../integrations/IntegrationManagers";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encodes a URI according to a set of template variables. Variables will be
|
* Encodes a URI according to a set of template variables. Variables will be
|
||||||
|
@ -102,7 +103,8 @@ export default class WidgetUtils {
|
||||||
|
|
||||||
let scalarUrls = SdkConfig.get().integrations_widgets_urls;
|
let scalarUrls = SdkConfig.get().integrations_widgets_urls;
|
||||||
if (!scalarUrls || scalarUrls.length === 0) {
|
if (!scalarUrls || scalarUrls.length === 0) {
|
||||||
scalarUrls = [SdkConfig.get().integrations_rest_url];
|
const defaultManager = IntegrationManagers.sharedInstance().getPrimaryManager();
|
||||||
|
if (defaultManager) scalarUrls = [defaultManager.apiUrl];
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < scalarUrls.length; i++) {
|
for (let i = 0; i < scalarUrls.length; i++) {
|
||||||
|
@ -338,6 +340,17 @@ export default class WidgetUtils {
|
||||||
return widgets.filter((widget) => widget.content && widget.content.type === "m.stickerpicker");
|
return widgets.filter((widget) => widget.content && widget.content.type === "m.stickerpicker");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all integration manager widgets for this user.
|
||||||
|
* @returns {Object[]} An array of integration manager user widgets.
|
||||||
|
*/
|
||||||
|
static getIntegrationManagerWidgets() {
|
||||||
|
const widgets = WidgetUtils.getUserWidgetsArray();
|
||||||
|
// We'll be using im.vector.integration_manager until MSC1957 or similar is accepted.
|
||||||
|
const imTypes = ["m.integration_manager", "im.vector.integration_manager"];
|
||||||
|
return widgets.filter(w => w.content && imTypes.includes(w.content.type));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove all stickerpicker widgets (stickerpickers are user widgets by nature)
|
* Remove all stickerpicker widgets (stickerpickers are user widgets by nature)
|
||||||
* @return {Promise} Resolves on account data updated
|
* @return {Promise} Resolves on account data updated
|
||||||
|
|
15
yarn.lock
15
yarn.lock
|
@ -5061,13 +5061,10 @@ matrix-mock-request@^1.2.3:
|
||||||
bluebird "^3.5.0"
|
bluebird "^3.5.0"
|
||||||
expect "^1.20.2"
|
expect "^1.20.2"
|
||||||
|
|
||||||
matrix-react-test-utils@^0.1.1:
|
matrix-react-test-utils@^0.2.2:
|
||||||
version "0.1.1"
|
version "0.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/matrix-react-test-utils/-/matrix-react-test-utils-0.1.1.tgz#b548844d0ebe338ea1b9c8f16474c30d17c3bdf4"
|
resolved "https://registry.yarnpkg.com/matrix-react-test-utils/-/matrix-react-test-utils-0.2.2.tgz#c87144d3b910c7edc544a6699d13c7c2bf02f853"
|
||||||
integrity sha1-tUiETQ6+M46hucjxZHTDDRfDvfQ=
|
integrity sha512-49+7gfV6smvBIVbeloql+37IeWMTD+fiywalwCqk8Dnz53zAFjKSltB3rmWHso1uecLtQEcPtCijfhzcLXAxTQ==
|
||||||
dependencies:
|
|
||||||
react "^15.6.1"
|
|
||||||
react-dom "^15.6.1"
|
|
||||||
|
|
||||||
md5.js@^1.3.4:
|
md5.js@^1.3.4:
|
||||||
version "1.3.5"
|
version "1.3.5"
|
||||||
|
@ -6373,7 +6370,7 @@ react-beautiful-dnd@^4.0.1:
|
||||||
redux-thunk "^2.2.0"
|
redux-thunk "^2.2.0"
|
||||||
reselect "^3.0.1"
|
reselect "^3.0.1"
|
||||||
|
|
||||||
react-dom@^15.6.0, react-dom@^15.6.1:
|
react-dom@^15.6.0:
|
||||||
version "15.6.2"
|
version "15.6.2"
|
||||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-15.6.2.tgz#41cfadf693b757faf2708443a1d1fd5a02bef730"
|
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-15.6.2.tgz#41cfadf693b757faf2708443a1d1fd5a02bef730"
|
||||||
integrity sha1-Qc+t9pO3V/rycIRDodH9WgK+9zA=
|
integrity sha1-Qc+t9pO3V/rycIRDodH9WgK+9zA=
|
||||||
|
@ -6436,7 +6433,7 @@ react-redux@^5.0.6:
|
||||||
react-is "^16.6.0"
|
react-is "^16.6.0"
|
||||||
react-lifecycles-compat "^3.0.0"
|
react-lifecycles-compat "^3.0.0"
|
||||||
|
|
||||||
react@^15.6.0, react@^15.6.1:
|
react@^15.6.0:
|
||||||
version "15.6.2"
|
version "15.6.2"
|
||||||
resolved "https://registry.yarnpkg.com/react/-/react-15.6.2.tgz#dba0434ab439cfe82f108f0f511663908179aa72"
|
resolved "https://registry.yarnpkg.com/react/-/react-15.6.2.tgz#dba0434ab439cfe82f108f0f511663908179aa72"
|
||||||
integrity sha1-26BDSrQ5z+gvEI8PURZjkIF5qnI=
|
integrity sha1-26BDSrQ5z+gvEI8PURZjkIF5qnI=
|
||||||
|
|
Loading…
Reference in a new issue