Avoid visual glitch when terms appear for IM
This avoids a visual glitch where the Integration Manager portal would briefly appear, but then be replaced by a smaller Terms dialog when there's something to agree to. To resolve this minimal code churn, this cheats a bit and customises the size of the terms dialog to match the IM portal modal when terms are shown for IM purposes. Fixes https://github.com/vector-im/riot-web/issues/10386pull/21833/head
parent
2eb8a8879b
commit
39d5aa7cf4
|
@ -14,6 +14,17 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
.mx_TermsDialog_forIntegrationsManager .mx_Dialog {
|
||||||
|
width: 60%;
|
||||||
|
height: 70%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_TermsDialog_termsTableHeader {
|
.mx_TermsDialog_termsTableHeader {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
@ -21,6 +32,7 @@ limitations under the License.
|
||||||
|
|
||||||
.mx_TermsDialog_termsTable {
|
.mx_TermsDialog_termsTable {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_TermsDialog_service, .mx_TermsDialog_summary {
|
.mx_TermsDialog_service, .mx_TermsDialog_summary {
|
||||||
|
|
|
@ -18,7 +18,7 @@ limitations under the License.
|
||||||
import url from 'url';
|
import url from 'url';
|
||||||
import Promise from 'bluebird';
|
import Promise from 'bluebird';
|
||||||
import SettingsStore from "./settings/SettingsStore";
|
import SettingsStore from "./settings/SettingsStore";
|
||||||
import { Service, presentTermsForServices, TermsNotSignedError } from './Terms';
|
import { Service, startTermsFlow, TermsNotSignedError } from './Terms';
|
||||||
const request = require('browser-request');
|
const request = require('browser-request');
|
||||||
|
|
||||||
const SdkConfig = require('./SdkConfig');
|
const SdkConfig = require('./SdkConfig');
|
||||||
|
@ -32,6 +32,9 @@ const imApiVersion = "1.1";
|
||||||
class ScalarAuthClient {
|
class ScalarAuthClient {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.scalarToken = null;
|
this.scalarToken = null;
|
||||||
|
// `undefined` to allow `startTermsFlow` to fallback to a default
|
||||||
|
// callback if this is unset.
|
||||||
|
this.termsInteractionCallback = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -42,6 +45,10 @@ class ScalarAuthClient {
|
||||||
return SdkConfig.get()['integrations_rest_url'] && SdkConfig.get()['integrations_ui_url'];
|
return SdkConfig.get()['integrations_rest_url'] && SdkConfig.get()['integrations_ui_url'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setTermsInteractionCallback(callback) {
|
||||||
|
this.termsInteractionCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
connect() {
|
connect() {
|
||||||
return this.getScalarToken().then((tok) => {
|
return this.getScalarToken().then((tok) => {
|
||||||
this.scalarToken = tok;
|
this.scalarToken = tok;
|
||||||
|
@ -122,11 +129,11 @@ class ScalarAuthClient {
|
||||||
const parsedImRestUrl = url.parse(SdkConfig.get().integrations_rest_url);
|
const parsedImRestUrl = url.parse(SdkConfig.get().integrations_rest_url);
|
||||||
parsedImRestUrl.path = '';
|
parsedImRestUrl.path = '';
|
||||||
parsedImRestUrl.pathname = '';
|
parsedImRestUrl.pathname = '';
|
||||||
return presentTermsForServices([new Service(
|
return startTermsFlow([new Service(
|
||||||
Matrix.SERVICE_TYPES.IM,
|
Matrix.SERVICE_TYPES.IM,
|
||||||
parsedImRestUrl.format(),
|
parsedImRestUrl.format(),
|
||||||
token,
|
token,
|
||||||
)]).then(() => {
|
)], this.termsInteractionCallback).then(() => {
|
||||||
return token;
|
return token;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
25
src/Terms.js
25
src/Terms.js
|
@ -15,6 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Promise from 'bluebird';
|
import Promise from 'bluebird';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import MatrixClientPeg from './MatrixClientPeg';
|
import MatrixClientPeg from './MatrixClientPeg';
|
||||||
import sdk from './';
|
import sdk from './';
|
||||||
|
@ -39,17 +40,6 @@ export class Service {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Present a popup to the user prompting them to agree to terms and conditions
|
|
||||||
*
|
|
||||||
* @param {Service[]} services Object with keys 'serviceType', 'baseUrl', 'accessToken'
|
|
||||||
* @returns {Promise} resolves when the user agreed to all necessary terms or rejects
|
|
||||||
* if they cancel.
|
|
||||||
*/
|
|
||||||
export function presentTermsForServices(services) {
|
|
||||||
return startTermsFlow(services, dialogTermsInteractionCallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start a flow where the user is presented with terms & conditions for some services
|
* Start a flow where the user is presented with terms & conditions for some services
|
||||||
*
|
*
|
||||||
|
@ -61,7 +51,10 @@ export function presentTermsForServices(services) {
|
||||||
* @returns {Promise} resolves when the user agreed to all necessary terms or rejects
|
* @returns {Promise} resolves when the user agreed to all necessary terms or rejects
|
||||||
* if they cancel.
|
* if they cancel.
|
||||||
*/
|
*/
|
||||||
export async function startTermsFlow(services, interactionCallback) {
|
export async function startTermsFlow(
|
||||||
|
services,
|
||||||
|
interactionCallback = dialogTermsInteractionCallback,
|
||||||
|
) {
|
||||||
const termsPromises = services.map(
|
const termsPromises = services.map(
|
||||||
(s) => MatrixClientPeg.get().getTerms(s.serviceType, s.baseUrl),
|
(s) => MatrixClientPeg.get().getTerms(s.serviceType, s.baseUrl),
|
||||||
);
|
);
|
||||||
|
@ -160,7 +153,11 @@ export async function startTermsFlow(services, interactionCallback) {
|
||||||
return Promise.all(agreePromises);
|
return Promise.all(agreePromises);
|
||||||
}
|
}
|
||||||
|
|
||||||
function dialogTermsInteractionCallback(policiesAndServicePairs, agreedUrls) {
|
export function dialogTermsInteractionCallback(
|
||||||
|
policiesAndServicePairs,
|
||||||
|
agreedUrls,
|
||||||
|
extraClassNames,
|
||||||
|
) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
console.log("Terms that need agreement", policiesAndServicePairs);
|
console.log("Terms that need agreement", policiesAndServicePairs);
|
||||||
const TermsDialog = sdk.getComponent("views.dialogs.TermsDialog");
|
const TermsDialog = sdk.getComponent("views.dialogs.TermsDialog");
|
||||||
|
@ -175,6 +172,6 @@ function dialogTermsInteractionCallback(policiesAndServicePairs, agreedUrls) {
|
||||||
}
|
}
|
||||||
resolve(agreedUrls);
|
resolve(agreedUrls);
|
||||||
},
|
},
|
||||||
});
|
}, classNames("mx_TermsDialog", extraClassNames));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -176,7 +176,7 @@ export default class TermsDialog extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseDialog className='mx_TermsDialog'
|
<BaseDialog
|
||||||
fixedWidth={false}
|
fixedWidth={false}
|
||||||
onFinished={this._onCancelClick}
|
onFinished={this._onCancelClick}
|
||||||
title={_t("Terms of Service")}
|
title={_t("Terms of Service")}
|
||||||
|
|
|
@ -17,7 +17,7 @@ limitations under the License.
|
||||||
import sdk from "../index";
|
import sdk from "../index";
|
||||||
import ScalarAuthClient from '../ScalarAuthClient';
|
import ScalarAuthClient from '../ScalarAuthClient';
|
||||||
import Modal from '../Modal';
|
import Modal from '../Modal';
|
||||||
import { TermsNotSignedError } from '../Terms';
|
import { TermsNotSignedError, dialogTermsInteractionCallback } from '../Terms';
|
||||||
|
|
||||||
export async function showIntegrationsManager(opts) {
|
export async function showIntegrationsManager(opts) {
|
||||||
const IntegrationsManager = sdk.getComponent("views.settings.IntegrationsManager");
|
const IntegrationsManager = sdk.getComponent("views.settings.IntegrationsManager");
|
||||||
|
@ -38,6 +38,7 @@ export async function showIntegrationsManager(opts) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const scalarClient = new ScalarAuthClient();
|
const scalarClient = new ScalarAuthClient();
|
||||||
|
scalarClient.setTermsInteractionCallback(integrationsTermsInteractionCallback);
|
||||||
try {
|
try {
|
||||||
await scalarClient.connect();
|
await scalarClient.connect();
|
||||||
if (!scalarClient.hasCredentials()) {
|
if (!scalarClient.hasCredentials()) {
|
||||||
|
@ -63,3 +64,16 @@ export async function showIntegrationsManager(opts) {
|
||||||
close();
|
close();
|
||||||
Modal.createTrackedDialog('Integrations Manager', '', IntegrationsManager, props, "mx_IntegrationsManager");
|
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",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue