Merge branch 'develop' into kegan/indexeddb
This commit is contained in:
commit
53f3b5780e
20 changed files with 623 additions and 329 deletions
|
@ -13,6 +13,7 @@ module.exports = {
|
||||||
plugins: [
|
plugins: [
|
||||||
"react",
|
"react",
|
||||||
"flowtype",
|
"flowtype",
|
||||||
|
"babel"
|
||||||
],
|
],
|
||||||
env: {
|
env: {
|
||||||
es6: true,
|
es6: true,
|
||||||
|
@ -23,6 +24,11 @@ module.exports = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
|
// eslint's built in no-invalid-this rule breaks with class properties
|
||||||
|
"no-invalid-this": "off",
|
||||||
|
// so we replace it with a version that is class property aware
|
||||||
|
"babel/no-invalid-this": "error",
|
||||||
|
|
||||||
/** react **/
|
/** react **/
|
||||||
// This just uses the react plugin to help eslint known when
|
// This just uses the react plugin to help eslint known when
|
||||||
// variables have been used in JSX
|
// variables have been used in JSX
|
||||||
|
|
|
@ -90,6 +90,7 @@
|
||||||
"babel-preset-react": "^6.11.1",
|
"babel-preset-react": "^6.11.1",
|
||||||
"eslint": "^3.13.1",
|
"eslint": "^3.13.1",
|
||||||
"eslint-config-google": "^0.7.1",
|
"eslint-config-google": "^0.7.1",
|
||||||
|
"eslint-plugin-babel": "^4.0.1",
|
||||||
"eslint-plugin-flowtype": "^2.30.0",
|
"eslint-plugin-flowtype": "^2.30.0",
|
||||||
"eslint-plugin-react": "^6.9.0",
|
"eslint-plugin-react": "^6.9.0",
|
||||||
"expect": "^1.16.0",
|
"expect": "^1.16.0",
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015, 2016 OpenMarket Ltd
|
||||||
|
Copyright 2017 Vector Creations Ltd
|
||||||
|
|
||||||
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.
|
||||||
|
@ -24,6 +25,8 @@ import Presence from './Presence';
|
||||||
import dis from './dispatcher';
|
import dis from './dispatcher';
|
||||||
import DMRoomMap from './utils/DMRoomMap';
|
import DMRoomMap from './utils/DMRoomMap';
|
||||||
import RtsClient from './RtsClient';
|
import RtsClient from './RtsClient';
|
||||||
|
import Modal from './Modal';
|
||||||
|
import sdk from './index';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
|
@ -109,16 +112,17 @@ export function loadSession(opts) {
|
||||||
return q();
|
return q();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_restoreFromLocalStorage()) {
|
return _restoreFromLocalStorage().then((success) => {
|
||||||
return q();
|
if (success) {
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (enableGuest) {
|
if (enableGuest) {
|
||||||
return _registerAsGuest(guestHsUrl, guestIsUrl, defaultDeviceDisplayName);
|
return _registerAsGuest(guestHsUrl, guestIsUrl, defaultDeviceDisplayName);
|
||||||
}
|
}
|
||||||
|
|
||||||
// fall back to login screen
|
// fall back to login screen
|
||||||
return q();
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function _loginWithToken(queryParams, defaultDeviceDisplayName) {
|
function _loginWithToken(queryParams, defaultDeviceDisplayName) {
|
||||||
|
@ -178,10 +182,11 @@ function _registerAsGuest(hsUrl, isUrl, defaultDeviceDisplayName) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns true if a session is found in localstorage
|
// returns a promise which resolves to true if a session is found in
|
||||||
|
// localstorage
|
||||||
function _restoreFromLocalStorage() {
|
function _restoreFromLocalStorage() {
|
||||||
if (!localStorage) {
|
if (!localStorage) {
|
||||||
return false;
|
return q(false);
|
||||||
}
|
}
|
||||||
const hs_url = localStorage.getItem("mx_hs_url");
|
const hs_url = localStorage.getItem("mx_hs_url");
|
||||||
const is_url = localStorage.getItem("mx_is_url") || 'https://matrix.org';
|
const is_url = localStorage.getItem("mx_is_url") || 'https://matrix.org';
|
||||||
|
@ -208,28 +213,55 @@ function _restoreFromLocalStorage() {
|
||||||
identityServerUrl: is_url,
|
identityServerUrl: is_url,
|
||||||
guest: is_guest,
|
guest: is_guest,
|
||||||
});
|
});
|
||||||
return true;
|
return q(true);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log("Unable to restore session", e);
|
return _handleRestoreFailure(e);
|
||||||
|
|
||||||
var msg = e.message;
|
|
||||||
if (msg == "OLM.BAD_LEGACY_ACCOUNT_PICKLE") {
|
|
||||||
msg = "You need to log back in to generate end-to-end encryption keys "
|
|
||||||
+ "for this device and submit the public key to your homeserver. "
|
|
||||||
+ "This is a once off; sorry for the inconvenience.";
|
|
||||||
}
|
|
||||||
|
|
||||||
// don't leak things into the new session
|
|
||||||
_clearLocalStorage();
|
|
||||||
|
|
||||||
throw new Error("Unable to restore previous session: " + msg);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log("No previous session found.");
|
console.log("No previous session found.");
|
||||||
return false;
|
return q(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _handleRestoreFailure(e) {
|
||||||
|
console.log("Unable to restore session", e);
|
||||||
|
|
||||||
|
let msg = e.message;
|
||||||
|
if (msg == "OLM.BAD_LEGACY_ACCOUNT_PICKLE") {
|
||||||
|
msg = "You need to log back in to generate end-to-end encryption keys "
|
||||||
|
+ "for this device and submit the public key to your homeserver. "
|
||||||
|
+ "This is a once off; sorry for the inconvenience.";
|
||||||
|
|
||||||
|
_clearLocalStorage();
|
||||||
|
|
||||||
|
return q.reject(new Error(
|
||||||
|
"Unable to restore previous session: " + msg,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
const def = q.defer();
|
||||||
|
const SessionRestoreErrorDialog =
|
||||||
|
sdk.getComponent('views.dialogs.SessionRestoreErrorDialog');
|
||||||
|
|
||||||
|
Modal.createDialog(SessionRestoreErrorDialog, {
|
||||||
|
error: msg,
|
||||||
|
onFinished: (success) => {
|
||||||
|
def.resolve(success);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return def.promise.then((success) => {
|
||||||
|
if (success) {
|
||||||
|
// user clicked continue.
|
||||||
|
_clearLocalStorage();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// try, try again
|
||||||
|
return _restoreFromLocalStorage();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let rtsClient = null;
|
let rtsClient = null;
|
||||||
export function initRtsClient(url) {
|
export function initRtsClient(url) {
|
||||||
rtsClient = new RtsClient(url);
|
rtsClient = new RtsClient(url);
|
||||||
|
|
|
@ -50,18 +50,18 @@ export default class RtsClient {
|
||||||
* Track a referral with the Riot Team Server. This should be called once a referred
|
* Track a referral with the Riot Team Server. This should be called once a referred
|
||||||
* user has been successfully registered.
|
* user has been successfully registered.
|
||||||
* @param {string} referrer the user ID of one who referred the user to Riot.
|
* @param {string} referrer the user ID of one who referred the user to Riot.
|
||||||
* @param {string} userId the user ID of the user being referred.
|
* @param {string} sid the sign-up identity server session ID .
|
||||||
* @param {string} userEmail the email address linked to `userId`.
|
* @param {string} clientSecret the sign-up client secret.
|
||||||
* @returns {Promise} a promise that resolves to { team_token: 'sometoken' } upon
|
* @returns {Promise} a promise that resolves to { team_token: 'sometoken' } upon
|
||||||
* success.
|
* success.
|
||||||
*/
|
*/
|
||||||
trackReferral(referrer, userId, userEmail) {
|
trackReferral(referrer, sid, clientSecret) {
|
||||||
return request(this._url + '/register',
|
return request(this._url + '/register',
|
||||||
{
|
{
|
||||||
body: {
|
body: {
|
||||||
referrer: referrer,
|
referrer: referrer,
|
||||||
user_id: userId,
|
session_id: sid,
|
||||||
user_email: userEmail,
|
client_secret: clientSecret,
|
||||||
},
|
},
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
}
|
}
|
||||||
|
|
|
@ -149,6 +149,7 @@ class EmailIdentityStage extends Stage {
|
||||||
nextLink
|
nextLink
|
||||||
).then(function(response) {
|
).then(function(response) {
|
||||||
self.sid = response.sid;
|
self.sid = response.sid;
|
||||||
|
self.signupInstance.setIdSid(self.sid);
|
||||||
return self._completeVerify();
|
return self._completeVerify();
|
||||||
}).then(function(request) {
|
}).then(function(request) {
|
||||||
request.poll_for_success = true;
|
request.poll_for_success = true;
|
||||||
|
|
|
@ -26,7 +26,7 @@ var Notifier = require("./Notifier");
|
||||||
module.exports = {
|
module.exports = {
|
||||||
LABS_FEATURES: [
|
LABS_FEATURES: [
|
||||||
{
|
{
|
||||||
name: 'Rich Text Editor',
|
name: 'New Composer & Autocomplete',
|
||||||
id: 'rich_text_editor',
|
id: 'rich_text_editor',
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
|
|
@ -31,6 +31,8 @@ import structures$CreateRoom from './components/structures/CreateRoom';
|
||||||
structures$CreateRoom && (module.exports.components['structures.CreateRoom'] = structures$CreateRoom);
|
structures$CreateRoom && (module.exports.components['structures.CreateRoom'] = structures$CreateRoom);
|
||||||
import structures$FilePanel from './components/structures/FilePanel';
|
import structures$FilePanel from './components/structures/FilePanel';
|
||||||
structures$FilePanel && (module.exports.components['structures.FilePanel'] = structures$FilePanel);
|
structures$FilePanel && (module.exports.components['structures.FilePanel'] = structures$FilePanel);
|
||||||
|
import structures$InteractiveAuth from './components/structures/InteractiveAuth';
|
||||||
|
structures$InteractiveAuth && (module.exports.components['structures.InteractiveAuth'] = structures$InteractiveAuth);
|
||||||
import structures$LoggedInView from './components/structures/LoggedInView';
|
import structures$LoggedInView from './components/structures/LoggedInView';
|
||||||
structures$LoggedInView && (module.exports.components['structures.LoggedInView'] = structures$LoggedInView);
|
structures$LoggedInView && (module.exports.components['structures.LoggedInView'] = structures$LoggedInView);
|
||||||
import structures$MatrixChat from './components/structures/MatrixChat';
|
import structures$MatrixChat from './components/structures/MatrixChat';
|
||||||
|
@ -75,6 +77,8 @@ import views$dialogs$BaseDialog from './components/views/dialogs/BaseDialog';
|
||||||
views$dialogs$BaseDialog && (module.exports.components['views.dialogs.BaseDialog'] = views$dialogs$BaseDialog);
|
views$dialogs$BaseDialog && (module.exports.components['views.dialogs.BaseDialog'] = views$dialogs$BaseDialog);
|
||||||
import views$dialogs$ChatInviteDialog from './components/views/dialogs/ChatInviteDialog';
|
import views$dialogs$ChatInviteDialog from './components/views/dialogs/ChatInviteDialog';
|
||||||
views$dialogs$ChatInviteDialog && (module.exports.components['views.dialogs.ChatInviteDialog'] = views$dialogs$ChatInviteDialog);
|
views$dialogs$ChatInviteDialog && (module.exports.components['views.dialogs.ChatInviteDialog'] = views$dialogs$ChatInviteDialog);
|
||||||
|
import views$dialogs$ConfirmUserActionDialog from './components/views/dialogs/ConfirmUserActionDialog';
|
||||||
|
views$dialogs$ConfirmUserActionDialog && (module.exports.components['views.dialogs.ConfirmUserActionDialog'] = views$dialogs$ConfirmUserActionDialog);
|
||||||
import views$dialogs$DeactivateAccountDialog from './components/views/dialogs/DeactivateAccountDialog';
|
import views$dialogs$DeactivateAccountDialog from './components/views/dialogs/DeactivateAccountDialog';
|
||||||
views$dialogs$DeactivateAccountDialog && (module.exports.components['views.dialogs.DeactivateAccountDialog'] = views$dialogs$DeactivateAccountDialog);
|
views$dialogs$DeactivateAccountDialog && (module.exports.components['views.dialogs.DeactivateAccountDialog'] = views$dialogs$DeactivateAccountDialog);
|
||||||
import views$dialogs$ErrorDialog from './components/views/dialogs/ErrorDialog';
|
import views$dialogs$ErrorDialog from './components/views/dialogs/ErrorDialog';
|
||||||
|
@ -85,6 +89,8 @@ import views$dialogs$NeedToRegisterDialog from './components/views/dialogs/NeedT
|
||||||
views$dialogs$NeedToRegisterDialog && (module.exports.components['views.dialogs.NeedToRegisterDialog'] = views$dialogs$NeedToRegisterDialog);
|
views$dialogs$NeedToRegisterDialog && (module.exports.components['views.dialogs.NeedToRegisterDialog'] = views$dialogs$NeedToRegisterDialog);
|
||||||
import views$dialogs$QuestionDialog from './components/views/dialogs/QuestionDialog';
|
import views$dialogs$QuestionDialog from './components/views/dialogs/QuestionDialog';
|
||||||
views$dialogs$QuestionDialog && (module.exports.components['views.dialogs.QuestionDialog'] = views$dialogs$QuestionDialog);
|
views$dialogs$QuestionDialog && (module.exports.components['views.dialogs.QuestionDialog'] = views$dialogs$QuestionDialog);
|
||||||
|
import views$dialogs$SessionRestoreErrorDialog from './components/views/dialogs/SessionRestoreErrorDialog';
|
||||||
|
views$dialogs$SessionRestoreErrorDialog && (module.exports.components['views.dialogs.SessionRestoreErrorDialog'] = views$dialogs$SessionRestoreErrorDialog);
|
||||||
import views$dialogs$SetDisplayNameDialog from './components/views/dialogs/SetDisplayNameDialog';
|
import views$dialogs$SetDisplayNameDialog from './components/views/dialogs/SetDisplayNameDialog';
|
||||||
views$dialogs$SetDisplayNameDialog && (module.exports.components['views.dialogs.SetDisplayNameDialog'] = views$dialogs$SetDisplayNameDialog);
|
views$dialogs$SetDisplayNameDialog && (module.exports.components['views.dialogs.SetDisplayNameDialog'] = views$dialogs$SetDisplayNameDialog);
|
||||||
import views$dialogs$TextInputDialog from './components/views/dialogs/TextInputDialog';
|
import views$dialogs$TextInputDialog from './components/views/dialogs/TextInputDialog';
|
||||||
|
|
152
src/components/structures/InteractiveAuth.js
Normal file
152
src/components/structures/InteractiveAuth.js
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
/*
|
||||||
|
Copyright 2017 Vector Creations Ltd.
|
||||||
|
|
||||||
|
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 Matrix from 'matrix-js-sdk';
|
||||||
|
const InteractiveAuth = Matrix.InteractiveAuth;
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import sdk from '../../index';
|
||||||
|
|
||||||
|
import {getEntryComponentForLoginType} from '../views/login/InteractiveAuthEntryComponents';
|
||||||
|
|
||||||
|
export default React.createClass({
|
||||||
|
displayName: 'InteractiveAuth',
|
||||||
|
|
||||||
|
propTypes: {
|
||||||
|
// response from initial request. If not supplied, will do a request on
|
||||||
|
// mount.
|
||||||
|
authData: React.PropTypes.shape({
|
||||||
|
flows: React.PropTypes.array,
|
||||||
|
params: React.PropTypes.object,
|
||||||
|
session: React.PropTypes.string,
|
||||||
|
}),
|
||||||
|
|
||||||
|
// callback
|
||||||
|
makeRequest: React.PropTypes.func.isRequired,
|
||||||
|
|
||||||
|
// callback called when the auth process has finished
|
||||||
|
// @param {bool} status True if the operation requiring
|
||||||
|
// auth was completed sucessfully, false if canceled.
|
||||||
|
// @param result The result of the authenticated call
|
||||||
|
onFinished: React.PropTypes.func.isRequired,
|
||||||
|
},
|
||||||
|
|
||||||
|
getInitialState: function() {
|
||||||
|
return {
|
||||||
|
authStage: null,
|
||||||
|
busy: false,
|
||||||
|
errorText: null,
|
||||||
|
stageErrorText: null,
|
||||||
|
submitButtonEnabled: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillMount: function() {
|
||||||
|
this._unmounted = false;
|
||||||
|
this._authLogic = new InteractiveAuth({
|
||||||
|
authData: this.props.authData,
|
||||||
|
doRequest: this._requestCallback,
|
||||||
|
startAuthStage: this._startAuthStage,
|
||||||
|
});
|
||||||
|
|
||||||
|
this._authLogic.attemptAuth().then((result) => {
|
||||||
|
this.props.onFinished(true, result);
|
||||||
|
}).catch((error) => {
|
||||||
|
console.error("Error during user-interactive auth:", error);
|
||||||
|
if (this._unmounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const msg = error.message || error.toString();
|
||||||
|
this.setState({
|
||||||
|
errorText: msg
|
||||||
|
});
|
||||||
|
}).done();
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillUnmount: function() {
|
||||||
|
this._unmounted = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
_startAuthStage: function(stageType, error) {
|
||||||
|
this.setState({
|
||||||
|
authStage: stageType,
|
||||||
|
errorText: error ? error.error : null,
|
||||||
|
}, this._setFocus);
|
||||||
|
},
|
||||||
|
|
||||||
|
_requestCallback: function(auth) {
|
||||||
|
this.setState({
|
||||||
|
busy: true,
|
||||||
|
errorText: null,
|
||||||
|
stageErrorText: null,
|
||||||
|
});
|
||||||
|
return this.props.makeRequest(auth).finally(() => {
|
||||||
|
if (this._unmounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
busy: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_setFocus: function() {
|
||||||
|
if (this.refs.stageComponent && this.refs.stageComponent.focus) {
|
||||||
|
this.refs.stageComponent.focus();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_submitAuthDict: function(authData) {
|
||||||
|
this._authLogic.submitAuthDict(authData);
|
||||||
|
},
|
||||||
|
|
||||||
|
_renderCurrentStage: function() {
|
||||||
|
const stage = this.state.authStage;
|
||||||
|
var StageComponent = getEntryComponentForLoginType(stage);
|
||||||
|
return (
|
||||||
|
<StageComponent ref="stageComponent"
|
||||||
|
loginType={stage}
|
||||||
|
authSessionId={this._authLogic.getSessionId()}
|
||||||
|
stageParams={this._authLogic.getStageParams(stage)}
|
||||||
|
submitAuthDict={this._submitAuthDict}
|
||||||
|
errorText={this.state.stageErrorText}
|
||||||
|
busy={this.state.busy}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
let error = null;
|
||||||
|
if (this.state.errorText) {
|
||||||
|
error = (
|
||||||
|
<div className="error">
|
||||||
|
{this.state.errorText}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
{this._renderCurrentStage()}
|
||||||
|
{error}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
|
@ -191,6 +191,17 @@ module.exports = React.createClass({
|
||||||
MatrixClientPeg.opts.initialSyncLimit = this.props.config.sync_timeline_limit;
|
MatrixClientPeg.opts.initialSyncLimit = this.props.config.sync_timeline_limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// To enable things like riot.im/geektime in a nicer way than rewriting the URL
|
||||||
|
// and appending a team token query parameter, use the first path segment to
|
||||||
|
// indicate a team, with "public" team tokens stored in the config teamTokenMap.
|
||||||
|
let routedTeamToken = null;
|
||||||
|
if (this.props.config.teamTokenMap) {
|
||||||
|
const teamName = window.location.pathname.split('/')[1];
|
||||||
|
if (teamName && this.props.config.teamTokenMap.hasOwnProperty(teamName)) {
|
||||||
|
routedTeamToken = this.props.config.teamTokenMap[teamName];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Persist the team token across refreshes using sessionStorage. A new window or
|
// Persist the team token across refreshes using sessionStorage. A new window or
|
||||||
// tab will not persist sessionStorage, but refreshes will.
|
// tab will not persist sessionStorage, but refreshes will.
|
||||||
if (this.props.startingFragmentQueryParams.team_token) {
|
if (this.props.startingFragmentQueryParams.team_token) {
|
||||||
|
@ -202,8 +213,19 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
// Use the locally-stored team token first, then as a fall-back, check to see if
|
// Use the locally-stored team token first, then as a fall-back, check to see if
|
||||||
// a referral link was used, which will contain a query parameter `team_token`.
|
// a referral link was used, which will contain a query parameter `team_token`.
|
||||||
this._teamToken = window.localStorage.getItem('mx_team_token') ||
|
this._teamToken = routedTeamToken ||
|
||||||
|
window.localStorage.getItem('mx_team_token') ||
|
||||||
window.sessionStorage.getItem('mx_team_token');
|
window.sessionStorage.getItem('mx_team_token');
|
||||||
|
|
||||||
|
// Some users have ended up with "undefined" as their local storage team token,
|
||||||
|
// treat that as undefined.
|
||||||
|
if (this._teamToken === "undefined") {
|
||||||
|
this._teamToken = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._teamToken) {
|
||||||
|
console.info(`Team token set to ${this._teamToken}`);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount: function() {
|
||||||
|
@ -888,14 +910,6 @@ module.exports = React.createClass({
|
||||||
onUserClick: function(event, userId) {
|
onUserClick: function(event, userId) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
// var MemberInfo = sdk.getComponent('rooms.MemberInfo');
|
|
||||||
// var member = new Matrix.RoomMember(null, userId);
|
|
||||||
// ContextualMenu.createMenu(MemberInfo, {
|
|
||||||
// member: member,
|
|
||||||
// right: window.innerWidth - event.pageX,
|
|
||||||
// top: event.pageY
|
|
||||||
// });
|
|
||||||
|
|
||||||
var member = new Matrix.RoomMember(null, userId);
|
var member = new Matrix.RoomMember(null, userId);
|
||||||
if (!member) { return; }
|
if (!member) { return; }
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
|
@ -975,6 +989,11 @@ module.exports = React.createClass({
|
||||||
this._setPage(PageTypes.UserSettings);
|
this._setPage(PageTypes.UserSettings);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onTeamMemberRegistered: function(teamToken) {
|
||||||
|
this._teamToken = teamToken;
|
||||||
|
this._setPage(PageTypes.HomePage);
|
||||||
|
},
|
||||||
|
|
||||||
onFinishPostRegistration: function() {
|
onFinishPostRegistration: function() {
|
||||||
// Don't confuse this with "PageType" which is the middle window to show
|
// Don't confuse this with "PageType" which is the middle window to show
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -1103,6 +1122,7 @@ module.exports = React.createClass({
|
||||||
customIsUrl={this.getCurrentIsUrl()}
|
customIsUrl={this.getCurrentIsUrl()}
|
||||||
registrationUrl={this.props.registrationUrl}
|
registrationUrl={this.props.registrationUrl}
|
||||||
defaultDeviceDisplayName={this.props.defaultDeviceDisplayName}
|
defaultDeviceDisplayName={this.props.defaultDeviceDisplayName}
|
||||||
|
onTeamMemberRegistered={this.onTeamMemberRegistered}
|
||||||
onLoggedIn={this.onRegistered}
|
onLoggedIn={this.onRegistered}
|
||||||
onLoginClick={this.onLoginClick}
|
onLoginClick={this.onLoginClick}
|
||||||
onRegisterClick={this.onRegisterClick}
|
onRegisterClick={this.onRegisterClick}
|
||||||
|
|
|
@ -223,8 +223,7 @@ module.exports = React.createClass({
|
||||||
users = users.slice(0, limit - 1);
|
users = users.slice(0, limit - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
let avatars = users.map((u, index) => {
|
const avatars = users.map((u) => {
|
||||||
let showInitial = othersCount === 0 && index === users.length - 1;
|
|
||||||
return (
|
return (
|
||||||
<MemberAvatar
|
<MemberAvatar
|
||||||
key={u.userId}
|
key={u.userId}
|
||||||
|
@ -232,7 +231,6 @@ module.exports = React.createClass({
|
||||||
width={24}
|
width={24}
|
||||||
height={24}
|
height={24}
|
||||||
resizeMethod="crop"
|
resizeMethod="crop"
|
||||||
defaultToInitialLetter={showInitial}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -58,6 +58,7 @@ module.exports = React.createClass({
|
||||||
teamServerURL: React.PropTypes.string.isRequired,
|
teamServerURL: React.PropTypes.string.isRequired,
|
||||||
}),
|
}),
|
||||||
teamSelected: React.PropTypes.object,
|
teamSelected: React.PropTypes.object,
|
||||||
|
onTeamMemberRegistered: React.PropTypes.func.isRequired,
|
||||||
|
|
||||||
defaultDeviceDisplayName: React.PropTypes.string,
|
defaultDeviceDisplayName: React.PropTypes.string,
|
||||||
|
|
||||||
|
@ -213,20 +214,22 @@ module.exports = React.createClass({
|
||||||
accessToken: response.access_token
|
accessToken: response.access_token
|
||||||
});
|
});
|
||||||
|
|
||||||
if (
|
// Done regardless of `teamSelected`. People registering with non-team emails
|
||||||
self._rtsClient &&
|
// will just nop. The point of this being we might not have the email address
|
||||||
self.props.referrer &&
|
// that the user registered with at this stage (depending on whether this
|
||||||
self.state.teamSelected
|
// is the client they initiated registration).
|
||||||
) {
|
if (self._rtsClient) {
|
||||||
// Track referral, get team_token in order to retrieve team config
|
// Track referral if self.props.referrer set, get team_token in order to
|
||||||
|
// retrieve team config and see welcome page etc.
|
||||||
self._rtsClient.trackReferral(
|
self._rtsClient.trackReferral(
|
||||||
self.props.referrer,
|
self.props.referrer || '', // Default to empty string = not referred
|
||||||
response.user_id,
|
self.registerLogic.params.idSid,
|
||||||
self.state.formVals.email
|
self.registerLogic.params.clientSecret
|
||||||
).then((data) => {
|
).then((data) => {
|
||||||
const teamToken = data.team_token;
|
const teamToken = data.team_token;
|
||||||
// Store for use /w welcome pages
|
// Store for use /w welcome pages
|
||||||
window.localStorage.setItem('mx_team_token', teamToken);
|
window.localStorage.setItem('mx_team_token', teamToken);
|
||||||
|
self.props.onTeamMemberRegistered(teamToken);
|
||||||
|
|
||||||
self._rtsClient.getTeam(teamToken).then((team) => {
|
self._rtsClient.getTeam(teamToken).then((team) => {
|
||||||
console.log(
|
console.log(
|
||||||
|
@ -426,7 +429,12 @@ module.exports = React.createClass({
|
||||||
return (
|
return (
|
||||||
<div className="mx_Login">
|
<div className="mx_Login">
|
||||||
<div className="mx_Login_box">
|
<div className="mx_Login_box">
|
||||||
<LoginHeader icon={this.state.teamSelected ? this.state.teamSelected.icon : null}/>
|
<LoginHeader
|
||||||
|
icon={this.state.teamSelected ?
|
||||||
|
this.props.teamServerConfig.teamServerURL + "/static/common/" +
|
||||||
|
this.state.teamSelected.domain + "/icon.png" :
|
||||||
|
null}
|
||||||
|
/>
|
||||||
{this._getRegisterContentJsx()}
|
{this._getRegisterContentJsx()}
|
||||||
<LoginFooter />
|
<LoginFooter />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import * as KeyCode from '../../../KeyCode';
|
import * as KeyCode from '../../../KeyCode';
|
||||||
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Basic container for modal dialogs.
|
* Basic container for modal dialogs.
|
||||||
|
@ -59,9 +60,21 @@ export default React.createClass({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_onCancelClick: function(e) {
|
||||||
|
this.props.onFinished();
|
||||||
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
return (
|
return (
|
||||||
<div onKeyDown={this._onKeyDown} className={this.props.className}>
|
<div onKeyDown={this._onKeyDown} className={this.props.className}>
|
||||||
|
<AccessibleButton onClick={this._onCancelClick}
|
||||||
|
className="mx_Dialog_cancelButton"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src="img/cancel.svg" width="18" height="18"
|
||||||
|
alt="Cancel" title="Cancel"
|
||||||
|
/>
|
||||||
|
</AccessibleButton>
|
||||||
<div className='mx_Dialog_title'>
|
<div className='mx_Dialog_title'>
|
||||||
{ this.props.title }
|
{ this.props.title }
|
||||||
</div>
|
</div>
|
||||||
|
|
83
src/components/views/dialogs/ConfirmUserActionDialog.js
Normal file
83
src/components/views/dialogs/ConfirmUserActionDialog.js
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
Copyright 2017 Vector Creations Ltd
|
||||||
|
|
||||||
|
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 React from 'react';
|
||||||
|
import sdk from '../../../index';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A dialog for confirming an operation on another user.
|
||||||
|
* Takes a user ID and a verb, displays the target user prominently
|
||||||
|
* such that it should be easy to confirm that the operation is being
|
||||||
|
* performed on the right person, and displays the operation prominently
|
||||||
|
* to make it obvious what is going to happen.
|
||||||
|
* Also tweaks the style for 'dangerous' actions (albeit only with colour)
|
||||||
|
*/
|
||||||
|
export default React.createClass({
|
||||||
|
displayName: 'ConfirmUserActionDialog',
|
||||||
|
propTypes: {
|
||||||
|
member: React.PropTypes.object.isRequired, // matrix-js-sdk member object
|
||||||
|
action: React.PropTypes.string.isRequired, // eg. 'Ban'
|
||||||
|
danger: React.PropTypes.bool,
|
||||||
|
onFinished: React.PropTypes.func.isRequired,
|
||||||
|
},
|
||||||
|
|
||||||
|
defaultProps: {
|
||||||
|
danger: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
onOk: function() {
|
||||||
|
this.props.onFinished(true);
|
||||||
|
},
|
||||||
|
|
||||||
|
onCancel: function() {
|
||||||
|
this.props.onFinished(false);
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||||
|
const MemberAvatar = sdk.getComponent("views.avatars.MemberAvatar");
|
||||||
|
|
||||||
|
const title = this.props.action + " this person?";
|
||||||
|
const confirmButtonClass = classnames({
|
||||||
|
'mx_Dialog_primary': true,
|
||||||
|
'danger': this.props.danger,
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<BaseDialog className="mx_UserActionConfirmDialog" onFinished={this.props.onFinished}
|
||||||
|
onEnterPressed={ this.onOk }
|
||||||
|
title={title}
|
||||||
|
>
|
||||||
|
<div className="mx_Dialog_content">
|
||||||
|
<div className="mx_ConfirmUserActionDialog_avatar">
|
||||||
|
<MemberAvatar member={this.props.member} width={72} height={72} />
|
||||||
|
</div>
|
||||||
|
<div className="mx_ConfirmUserActionDialog_name">{this.props.member.name}</div>
|
||||||
|
<div className="mx_ConfirmUserActionDialog_userId">{this.props.member.userId}</div>
|
||||||
|
</div>
|
||||||
|
<div className="mx_Dialog_buttons">
|
||||||
|
<button className={confirmButtonClass} onClick={this.onOk} autoFocus={true}>
|
||||||
|
{this.props.action}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button onClick={this.onCancel}>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</BaseDialog>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2016 OpenMarket Ltd
|
Copyright 2016 OpenMarket Ltd
|
||||||
|
Copyright 2017 Vector Creations Ltd
|
||||||
|
|
||||||
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,13 +16,12 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Matrix from 'matrix-js-sdk';
|
import Matrix from 'matrix-js-sdk';
|
||||||
const InteractiveAuth = Matrix.InteractiveAuth;
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
|
|
||||||
import {getEntryComponentForLoginType} from '../login/InteractiveAuthEntryComponents';
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
|
|
||||||
export default React.createClass({
|
export default React.createClass({
|
||||||
displayName: 'InteractiveAuthDialog',
|
displayName: 'InteractiveAuthDialog',
|
||||||
|
@ -41,168 +41,29 @@ export default React.createClass({
|
||||||
onFinished: React.PropTypes.func.isRequired,
|
onFinished: React.PropTypes.func.isRequired,
|
||||||
|
|
||||||
title: React.PropTypes.string,
|
title: React.PropTypes.string,
|
||||||
submitButtonLabel: React.PropTypes.string,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getDefaultProps: function() {
|
getDefaultProps: function() {
|
||||||
return {
|
return {
|
||||||
title: "Authentication",
|
title: "Authentication",
|
||||||
submitButtonLabel: "Submit",
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
|
||||||
return {
|
|
||||||
authStage: null,
|
|
||||||
busy: false,
|
|
||||||
errorText: null,
|
|
||||||
stageErrorText: null,
|
|
||||||
submitButtonEnabled: false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
componentWillMount: function() {
|
|
||||||
this._unmounted = false;
|
|
||||||
this._authLogic = new InteractiveAuth({
|
|
||||||
authData: this.props.authData,
|
|
||||||
doRequest: this._requestCallback,
|
|
||||||
startAuthStage: this._startAuthStage,
|
|
||||||
});
|
|
||||||
|
|
||||||
this._authLogic.attemptAuth().then((result) => {
|
|
||||||
this.props.onFinished(true, result);
|
|
||||||
}).catch((error) => {
|
|
||||||
console.error("Error during user-interactive auth:", error);
|
|
||||||
if (this._unmounted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const msg = error.message || error.toString();
|
|
||||||
this.setState({
|
|
||||||
errorText: msg
|
|
||||||
});
|
|
||||||
}).done();
|
|
||||||
},
|
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
|
||||||
this._unmounted = true;
|
|
||||||
},
|
|
||||||
|
|
||||||
_startAuthStage: function(stageType, error) {
|
|
||||||
this.setState({
|
|
||||||
authStage: stageType,
|
|
||||||
errorText: error ? error.error : null,
|
|
||||||
}, this._setFocus);
|
|
||||||
},
|
|
||||||
|
|
||||||
_requestCallback: function(auth) {
|
|
||||||
this.setState({
|
|
||||||
busy: true,
|
|
||||||
errorText: null,
|
|
||||||
stageErrorText: null,
|
|
||||||
});
|
|
||||||
return this.props.makeRequest(auth).finally(() => {
|
|
||||||
if (this._unmounted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.setState({
|
|
||||||
busy: false,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_onEnterPressed: function(e) {
|
|
||||||
if (this.state.submitButtonEnabled && !this.state.busy) {
|
|
||||||
this._onSubmit();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_onSubmit: function() {
|
|
||||||
if (this.refs.stageComponent && this.refs.stageComponent.onSubmitClick) {
|
|
||||||
this.refs.stageComponent.onSubmitClick();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_setFocus: function() {
|
|
||||||
if (this.refs.stageComponent && this.refs.stageComponent.focus) {
|
|
||||||
this.refs.stageComponent.focus();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_onCancel: function() {
|
|
||||||
this.props.onFinished(false);
|
|
||||||
},
|
|
||||||
|
|
||||||
_setSubmitButtonEnabled: function(enabled) {
|
|
||||||
this.setState({
|
|
||||||
submitButtonEnabled: enabled,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_submitAuthDict: function(authData) {
|
|
||||||
this._authLogic.submitAuthDict(authData);
|
|
||||||
},
|
|
||||||
|
|
||||||
_renderCurrentStage: function() {
|
|
||||||
const stage = this.state.authStage;
|
|
||||||
var StageComponent = getEntryComponentForLoginType(stage);
|
|
||||||
return (
|
|
||||||
<StageComponent ref="stageComponent"
|
|
||||||
loginType={stage}
|
|
||||||
authSessionId={this._authLogic.getSessionId()}
|
|
||||||
stageParams={this._authLogic.getStageParams(stage)}
|
|
||||||
submitAuthDict={this._submitAuthDict}
|
|
||||||
setSubmitButtonEnabled={this._setSubmitButtonEnabled}
|
|
||||||
errorText={this.state.stageErrorText}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
const Loader = sdk.getComponent("elements.Spinner");
|
const InteractiveAuth = sdk.getComponent("structures.InteractiveAuth");
|
||||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||||
|
|
||||||
let error = null;
|
|
||||||
if (this.state.errorText) {
|
|
||||||
error = (
|
|
||||||
<div className="error">
|
|
||||||
{this.state.errorText}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const submitLabel = this.state.busy ? <Loader /> : this.props.submitButtonLabel;
|
|
||||||
const submitEnabled = this.state.submitButtonEnabled && !this.state.busy;
|
|
||||||
|
|
||||||
const submitButton = (
|
|
||||||
<button className="mx_Dialog_primary"
|
|
||||||
onClick={this._onSubmit}
|
|
||||||
disabled={!submitEnabled}
|
|
||||||
>
|
|
||||||
{submitLabel}
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
|
|
||||||
const cancelButton = (
|
|
||||||
<button onClick={this._onCancel}>
|
|
||||||
Cancel
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseDialog className="mx_InteractiveAuthDialog"
|
<BaseDialog className="mx_InteractiveAuthDialog"
|
||||||
onEnterPressed={this._onEnterPressed}
|
|
||||||
onFinished={this.props.onFinished}
|
onFinished={this.props.onFinished}
|
||||||
title={this.props.title}
|
title={this.props.title}
|
||||||
>
|
>
|
||||||
<div className="mx_Dialog_content">
|
<div>
|
||||||
<p>This operation requires additional authentication.</p>
|
<InteractiveAuth ref={this._collectInteractiveAuth}
|
||||||
{this._renderCurrentStage()}
|
authData={this.props.authData}
|
||||||
{error}
|
makeRequest={this.props.makeRequest}
|
||||||
</div>
|
onFinished={this.props.onFinished}
|
||||||
<div className="mx_Dialog_buttons">
|
/>
|
||||||
{submitButton}
|
|
||||||
{cancelButton}
|
|
||||||
</div>
|
</div>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
);
|
);
|
||||||
|
|
74
src/components/views/dialogs/SessionRestoreErrorDialog.js
Normal file
74
src/components/views/dialogs/SessionRestoreErrorDialog.js
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
Copyright 2017 Vector Creations Ltd
|
||||||
|
|
||||||
|
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 React from 'react';
|
||||||
|
import sdk from '../../../index';
|
||||||
|
import SdkConfig from '../../../SdkConfig';
|
||||||
|
import Modal from '../../../Modal';
|
||||||
|
|
||||||
|
|
||||||
|
export default React.createClass({
|
||||||
|
displayName: 'SessionRestoreErrorDialog',
|
||||||
|
|
||||||
|
propTypes: {
|
||||||
|
error: React.PropTypes.string.isRequired,
|
||||||
|
onFinished: React.PropTypes.func.isRequired,
|
||||||
|
},
|
||||||
|
|
||||||
|
_sendBugReport: function() {
|
||||||
|
const BugReportDialog = sdk.getComponent("dialogs.BugReportDialog");
|
||||||
|
Modal.createDialog(BugReportDialog, {});
|
||||||
|
},
|
||||||
|
|
||||||
|
_continueClicked: function() {
|
||||||
|
this.props.onFinished(true);
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||||
|
let bugreport;
|
||||||
|
|
||||||
|
if (SdkConfig.get().bug_report_endpoint_url) {
|
||||||
|
bugreport = (
|
||||||
|
<p>Otherwise, <a onClick={this._sendBugReport} href='#'>
|
||||||
|
click here</a> to send a bug report.
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BaseDialog className="mx_ErrorDialog" onFinished={this.props.onFinished}
|
||||||
|
title='Unable to restore session'>
|
||||||
|
<div className="mx_Dialog_content">
|
||||||
|
<p>We encountered an error trying to restore your previous session. If
|
||||||
|
you continue, you will need to log in again, and encrypted chat
|
||||||
|
history will be unreadable.</p>
|
||||||
|
|
||||||
|
<p>If you have previously used a more recent version of Riot, your session
|
||||||
|
may be incompatible with this version. Close this window and return
|
||||||
|
to the more recent version.</p>
|
||||||
|
|
||||||
|
{bugreport}
|
||||||
|
</div>
|
||||||
|
<div className="mx_Dialog_buttons">
|
||||||
|
<button className="mx_Dialog_primary" onClick={this._continueClicked}>
|
||||||
|
Continue anyway
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</BaseDialog>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2016 OpenMarket Ltd
|
Copyright 2016 OpenMarket Ltd
|
||||||
|
Copyright 2017 Vector Creations Ltd
|
||||||
|
|
||||||
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.
|
||||||
|
@ -20,7 +21,7 @@ import sdk from '../../../index';
|
||||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||||
|
|
||||||
/* This file contains a collection of components which are used by the
|
/* This file contains a collection of components which are used by the
|
||||||
* InteractiveAuthDialog to prompt the user to enter the information needed
|
* InteractiveAuth to prompt the user to enter the information needed
|
||||||
* for an auth stage. (The intention is that they could also be used for other
|
* for an auth stage. (The intention is that they could also be used for other
|
||||||
* components, such as the registration flow).
|
* components, such as the registration flow).
|
||||||
*
|
*
|
||||||
|
@ -32,10 +33,10 @@ import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||||
* stageParams: params from the server for the stage being attempted
|
* stageParams: params from the server for the stage being attempted
|
||||||
* errorText: error message from a previous attempt to authenticate
|
* errorText: error message from a previous attempt to authenticate
|
||||||
* submitAuthDict: a function which will be called with the new auth dict
|
* submitAuthDict: a function which will be called with the new auth dict
|
||||||
* setSubmitButtonEnabled: a function which will enable/disable the 'submit' button
|
* busy: a boolean indicating whether the auth logic is doing something
|
||||||
|
* the user needs to wait for.
|
||||||
*
|
*
|
||||||
* Each component may also provide the following functions (beyond the standard React ones):
|
* Each component may also provide the following functions (beyond the standard React ones):
|
||||||
* onSubmitClick: handle a 'submit' button click
|
|
||||||
* focus: set the input focus appropriately in the form.
|
* focus: set the input focus appropriately in the form.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -48,12 +49,16 @@ export const PasswordAuthEntry = React.createClass({
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
submitAuthDict: React.PropTypes.func.isRequired,
|
submitAuthDict: React.PropTypes.func.isRequired,
|
||||||
setSubmitButtonEnabled: React.PropTypes.func.isRequired,
|
|
||||||
errorText: React.PropTypes.string,
|
errorText: React.PropTypes.string,
|
||||||
|
// is the auth logic currently waiting for something to
|
||||||
|
// happen?
|
||||||
|
busy: React.PropTypes.bool,
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillMount: function() {
|
getInitialState: function() {
|
||||||
this.props.setSubmitButtonEnabled(false);
|
return {
|
||||||
|
passwordValid: false,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
focus: function() {
|
focus: function() {
|
||||||
|
@ -62,7 +67,10 @@ export const PasswordAuthEntry = React.createClass({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onSubmitClick: function() {
|
_onSubmit: function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (this.props.busy) return;
|
||||||
|
|
||||||
this.props.submitAuthDict({
|
this.props.submitAuthDict({
|
||||||
type: PasswordAuthEntry.LOGIN_TYPE,
|
type: PasswordAuthEntry.LOGIN_TYPE,
|
||||||
user: MatrixClientPeg.get().credentials.userId,
|
user: MatrixClientPeg.get().credentials.userId,
|
||||||
|
@ -72,7 +80,9 @@ export const PasswordAuthEntry = React.createClass({
|
||||||
|
|
||||||
_onPasswordFieldChange: function(ev) {
|
_onPasswordFieldChange: function(ev) {
|
||||||
// enable the submit button iff the password is non-empty
|
// enable the submit button iff the password is non-empty
|
||||||
this.props.setSubmitButtonEnabled(Boolean(ev.target.value));
|
this.setState({
|
||||||
|
passwordValid: Boolean(this.refs.passwordField.value),
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
|
@ -82,16 +92,34 @@ export const PasswordAuthEntry = React.createClass({
|
||||||
passwordBoxClass = 'error';
|
passwordBoxClass = 'error';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let submitButtonOrSpinner;
|
||||||
|
if (this.props.busy) {
|
||||||
|
const Loader = sdk.getComponent("elements.Spinner");
|
||||||
|
submitButtonOrSpinner = <Loader />;
|
||||||
|
} else {
|
||||||
|
submitButtonOrSpinner = (
|
||||||
|
<input type="submit"
|
||||||
|
className="mx_Dialog_primary"
|
||||||
|
disabled={!this.state.passwordValid}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<p>To continue, please enter your password.</p>
|
<p>To continue, please enter your password.</p>
|
||||||
<p>Password:</p>
|
<p>Password:</p>
|
||||||
<input
|
<form onSubmit={this._onSubmit}>
|
||||||
ref="passwordField"
|
<input
|
||||||
className={passwordBoxClass}
|
ref="passwordField"
|
||||||
onChange={this._onPasswordFieldChange}
|
className={passwordBoxClass}
|
||||||
type="password"
|
onChange={this._onPasswordFieldChange}
|
||||||
/>
|
type="password"
|
||||||
|
/>
|
||||||
|
<div className="mx_button_row">
|
||||||
|
{submitButtonOrSpinner}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
<div className="error">
|
<div className="error">
|
||||||
{this.props.errorText}
|
{this.props.errorText}
|
||||||
</div>
|
</div>
|
||||||
|
@ -110,14 +138,9 @@ export const RecaptchaAuthEntry = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
submitAuthDict: React.PropTypes.func.isRequired,
|
submitAuthDict: React.PropTypes.func.isRequired,
|
||||||
stageParams: React.PropTypes.object.isRequired,
|
stageParams: React.PropTypes.object.isRequired,
|
||||||
setSubmitButtonEnabled: React.PropTypes.func.isRequired,
|
|
||||||
errorText: React.PropTypes.string,
|
errorText: React.PropTypes.string,
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillMount: function() {
|
|
||||||
this.props.setSubmitButtonEnabled(false);
|
|
||||||
},
|
|
||||||
|
|
||||||
_onCaptchaResponse: function(response) {
|
_onCaptchaResponse: function(response) {
|
||||||
this.props.submitAuthDict({
|
this.props.submitAuthDict({
|
||||||
type: RecaptchaAuthEntry.LOGIN_TYPE,
|
type: RecaptchaAuthEntry.LOGIN_TYPE,
|
||||||
|
@ -148,7 +171,6 @@ export const FallbackAuthEntry = React.createClass({
|
||||||
authSessionId: React.PropTypes.string.isRequired,
|
authSessionId: React.PropTypes.string.isRequired,
|
||||||
loginType: React.PropTypes.string.isRequired,
|
loginType: React.PropTypes.string.isRequired,
|
||||||
submitAuthDict: React.PropTypes.func.isRequired,
|
submitAuthDict: React.PropTypes.func.isRequired,
|
||||||
setSubmitButtonEnabled: React.PropTypes.func.isRequired,
|
|
||||||
errorText: React.PropTypes.string,
|
errorText: React.PropTypes.string,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -156,7 +178,6 @@ export const FallbackAuthEntry = React.createClass({
|
||||||
// we have to make the user click a button, as browsers will block
|
// we have to make the user click a button, as browsers will block
|
||||||
// the popup if we open it immediately.
|
// the popup if we open it immediately.
|
||||||
this._popupWindow = null;
|
this._popupWindow = null;
|
||||||
this.props.setSubmitButtonEnabled(true);
|
|
||||||
window.addEventListener("message", this._onReceiveMessage);
|
window.addEventListener("message", this._onReceiveMessage);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -167,13 +188,12 @@ export const FallbackAuthEntry = React.createClass({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onSubmitClick: function() {
|
_onShowFallbackClick: function() {
|
||||||
var url = MatrixClientPeg.get().getFallbackAuthUrl(
|
var url = MatrixClientPeg.get().getFallbackAuthUrl(
|
||||||
this.props.loginType,
|
this.props.loginType,
|
||||||
this.props.authSessionId
|
this.props.authSessionId
|
||||||
);
|
);
|
||||||
this._popupWindow = window.open(url);
|
this._popupWindow = window.open(url);
|
||||||
this.props.setSubmitButtonEnabled(false);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onReceiveMessage: function(event) {
|
_onReceiveMessage: function(event) {
|
||||||
|
@ -188,7 +208,7 @@ export const FallbackAuthEntry = React.createClass({
|
||||||
render: function() {
|
render: function() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
Click "Submit" to authenticate
|
<a onClick={this._onShowFallbackClick}>Start authentication</a>
|
||||||
<div className="error">
|
<div className="error">
|
||||||
{this.props.errorText}
|
{this.props.errorText}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -222,7 +222,8 @@ module.exports = React.createClass({
|
||||||
title: "Add an Integration",
|
title: "Add an Integration",
|
||||||
description:
|
description:
|
||||||
<div>
|
<div>
|
||||||
You are about to taken to a third-party site so you can authenticate your account for use with {integrationsUrl}.<br/>
|
You are about to be taken to a third-party site so you can
|
||||||
|
authenticate your account for use with {integrationsUrl}.<br/>
|
||||||
Do you wish to continue?
|
Do you wish to continue?
|
||||||
</div>,
|
</div>,
|
||||||
button: "Continue",
|
button: "Continue",
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015, 2016 OpenMarket Ltd
|
||||||
|
Copyright 2017 Vector Creations Ltd
|
||||||
|
|
||||||
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.
|
||||||
|
@ -25,16 +26,16 @@ limitations under the License.
|
||||||
* 'muted': boolean,
|
* 'muted': boolean,
|
||||||
* 'isTargetMod': boolean
|
* 'isTargetMod': boolean
|
||||||
*/
|
*/
|
||||||
var React = require('react');
|
import React from 'react';
|
||||||
var classNames = require('classnames');
|
import classNames from 'classnames';
|
||||||
var dis = require("../../../dispatcher");
|
import dis from '../../../dispatcher';
|
||||||
var Modal = require("../../../Modal");
|
import Modal from '../../../Modal';
|
||||||
var sdk = require('../../../index');
|
import sdk from '../../../index';
|
||||||
var createRoom = require('../../../createRoom');
|
import createRoom from '../../../createRoom';
|
||||||
var DMRoomMap = require('../../../utils/DMRoomMap');
|
import DMRoomMap from '../../../utils/DMRoomMap';
|
||||||
var Unread = require('../../../Unread');
|
import Unread from '../../../Unread';
|
||||||
var Receipt = require('../../../utils/Receipt');
|
import { findReadReceiptFromUserId } from '../../../utils/Receipt';
|
||||||
var WithMatrixClient = require('../../../wrappers/WithMatrixClient');
|
import WithMatrixClient from '../../../wrappers/WithMatrixClient';
|
||||||
import AccessibleButton from '../elements/AccessibleButton';
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
|
|
||||||
module.exports = WithMatrixClient(React.createClass({
|
module.exports = WithMatrixClient(React.createClass({
|
||||||
|
@ -43,13 +44,6 @@ module.exports = WithMatrixClient(React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
matrixClient: React.PropTypes.object.isRequired,
|
matrixClient: React.PropTypes.object.isRequired,
|
||||||
member: React.PropTypes.object.isRequired,
|
member: React.PropTypes.object.isRequired,
|
||||||
onFinished: React.PropTypes.func,
|
|
||||||
},
|
|
||||||
|
|
||||||
getDefaultProps: function() {
|
|
||||||
return {
|
|
||||||
onFinished: function() {}
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
|
@ -164,7 +158,7 @@ module.exports = WithMatrixClient(React.createClass({
|
||||||
onRoomReceipt: function(receiptEvent, room) {
|
onRoomReceipt: function(receiptEvent, room) {
|
||||||
// because if we read a notification, it will affect notification count
|
// because if we read a notification, it will affect notification count
|
||||||
// only bother updating if there's a receipt from us
|
// only bother updating if there's a receipt from us
|
||||||
if (Receipt.findReadReceiptFromUserId(receiptEvent, this.props.matrixClient.credentials.userId)) {
|
if (findReadReceiptFromUserId(receiptEvent, this.props.matrixClient.credentials.userId)) {
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -224,46 +218,72 @@ module.exports = WithMatrixClient(React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
onKick: function() {
|
onKick: function() {
|
||||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
const ConfirmUserActionDialog = sdk.getComponent("dialogs.ConfirmUserActionDialog");
|
||||||
var roomId = this.props.member.roomId;
|
Modal.createDialog(ConfirmUserActionDialog, {
|
||||||
var target = this.props.member.userId;
|
member: this.props.member,
|
||||||
this.setState({ updating: this.state.updating + 1 });
|
action: 'Kick',
|
||||||
this.props.matrixClient.kick(roomId, target).then(function() {
|
danger: true,
|
||||||
// NO-OP; rely on the m.room.member event coming down else we could
|
onFinished: (proceed) => {
|
||||||
// get out of sync if we force setState here!
|
if (!proceed) return;
|
||||||
console.log("Kick success");
|
|
||||||
}, function(err) {
|
this.setState({ updating: this.state.updating + 1 });
|
||||||
Modal.createDialog(ErrorDialog, {
|
this.props.matrixClient.kick(
|
||||||
title: "Kick error",
|
this.props.member.roomId, this.props.member.userId,
|
||||||
description: err.message
|
).then(function() {
|
||||||
|
// NO-OP; rely on the m.room.member event coming down else we could
|
||||||
|
// get out of sync if we force setState here!
|
||||||
|
console.log("Kick success");
|
||||||
|
}, function(err) {
|
||||||
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||||
|
Modal.createDialog(ErrorDialog, {
|
||||||
|
title: "Kick error",
|
||||||
|
description: err.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
).finally(()=>{
|
||||||
|
this.setState({ updating: this.state.updating - 1 });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
).finally(()=>{
|
|
||||||
this.setState({ updating: this.state.updating - 1 });
|
|
||||||
});
|
});
|
||||||
this.props.onFinished();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
onBan: function() {
|
onBanOrUnban: function() {
|
||||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
const ConfirmUserActionDialog = sdk.getComponent("dialogs.ConfirmUserActionDialog");
|
||||||
var roomId = this.props.member.roomId;
|
Modal.createDialog(ConfirmUserActionDialog, {
|
||||||
var target = this.props.member.userId;
|
member: this.props.member,
|
||||||
this.setState({ updating: this.state.updating + 1 });
|
action: this.props.member.membership == 'ban' ? 'Unban' : 'Ban',
|
||||||
this.props.matrixClient.ban(roomId, target).then(
|
danger: this.props.member.membership != 'ban',
|
||||||
function() {
|
onFinished: (proceed) => {
|
||||||
// NO-OP; rely on the m.room.member event coming down else we could
|
if (!proceed) return;
|
||||||
// get out of sync if we force setState here!
|
|
||||||
console.log("Ban success");
|
this.setState({ updating: this.state.updating + 1 });
|
||||||
}, function(err) {
|
let promise;
|
||||||
Modal.createDialog(ErrorDialog, {
|
if (this.props.member.membership == 'ban') {
|
||||||
title: "Ban error",
|
promise = this.props.matrixClient.unban(
|
||||||
description: err.message
|
this.props.member.roomId, this.props.member.userId,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
promise = this.props.matrixClient.ban(
|
||||||
|
this.props.member.roomId, this.props.member.userId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
promise.then(
|
||||||
|
function() {
|
||||||
|
// NO-OP; rely on the m.room.member event coming down else we could
|
||||||
|
// get out of sync if we force setState here!
|
||||||
|
console.log("Ban success");
|
||||||
|
}, function(err) {
|
||||||
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||||
|
Modal.createDialog(ErrorDialog, {
|
||||||
|
title: "Ban error",
|
||||||
|
description: err.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
).finally(()=>{
|
||||||
|
this.setState({ updating: this.state.updating - 1 });
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
).finally(()=>{
|
|
||||||
this.setState({ updating: this.state.updating - 1 });
|
|
||||||
});
|
});
|
||||||
this.props.onFinished();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
onMuteToggle: function() {
|
onMuteToggle: function() {
|
||||||
|
@ -272,14 +292,12 @@ module.exports = WithMatrixClient(React.createClass({
|
||||||
var target = this.props.member.userId;
|
var target = this.props.member.userId;
|
||||||
var room = this.props.matrixClient.getRoom(roomId);
|
var room = this.props.matrixClient.getRoom(roomId);
|
||||||
if (!room) {
|
if (!room) {
|
||||||
this.props.onFinished();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var powerLevelEvent = room.currentState.getStateEvents(
|
var powerLevelEvent = room.currentState.getStateEvents(
|
||||||
"m.room.power_levels", ""
|
"m.room.power_levels", ""
|
||||||
);
|
);
|
||||||
if (!powerLevelEvent) {
|
if (!powerLevelEvent) {
|
||||||
this.props.onFinished();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var isMuted = this.state.muted;
|
var isMuted = this.state.muted;
|
||||||
|
@ -314,7 +332,6 @@ module.exports = WithMatrixClient(React.createClass({
|
||||||
this.setState({ updating: this.state.updating - 1 });
|
this.setState({ updating: this.state.updating - 1 });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.props.onFinished();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
onModToggle: function() {
|
onModToggle: function() {
|
||||||
|
@ -323,19 +340,16 @@ module.exports = WithMatrixClient(React.createClass({
|
||||||
var target = this.props.member.userId;
|
var target = this.props.member.userId;
|
||||||
var room = this.props.matrixClient.getRoom(roomId);
|
var room = this.props.matrixClient.getRoom(roomId);
|
||||||
if (!room) {
|
if (!room) {
|
||||||
this.props.onFinished();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var powerLevelEvent = room.currentState.getStateEvents(
|
var powerLevelEvent = room.currentState.getStateEvents(
|
||||||
"m.room.power_levels", ""
|
"m.room.power_levels", ""
|
||||||
);
|
);
|
||||||
if (!powerLevelEvent) {
|
if (!powerLevelEvent) {
|
||||||
this.props.onFinished();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var me = room.getMember(this.props.matrixClient.credentials.userId);
|
var me = room.getMember(this.props.matrixClient.credentials.userId);
|
||||||
if (!me) {
|
if (!me) {
|
||||||
this.props.onFinished();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var defaultLevel = powerLevelEvent.getContent().users_default;
|
var defaultLevel = powerLevelEvent.getContent().users_default;
|
||||||
|
@ -366,7 +380,6 @@ module.exports = WithMatrixClient(React.createClass({
|
||||||
).finally(()=>{
|
).finally(()=>{
|
||||||
this.setState({ updating: this.state.updating - 1 });
|
this.setState({ updating: this.state.updating - 1 });
|
||||||
});
|
});
|
||||||
this.props.onFinished();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_applyPowerChange: function(roomId, target, powerLevel, powerLevelEvent) {
|
_applyPowerChange: function(roomId, target, powerLevel, powerLevelEvent) {
|
||||||
|
@ -386,7 +399,6 @@ module.exports = WithMatrixClient(React.createClass({
|
||||||
).finally(()=>{
|
).finally(()=>{
|
||||||
this.setState({ updating: this.state.updating - 1 });
|
this.setState({ updating: this.state.updating - 1 });
|
||||||
}).done();
|
}).done();
|
||||||
this.props.onFinished();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
onPowerChange: function(powerLevel) {
|
onPowerChange: function(powerLevel) {
|
||||||
|
@ -396,14 +408,12 @@ module.exports = WithMatrixClient(React.createClass({
|
||||||
var room = this.props.matrixClient.getRoom(roomId);
|
var room = this.props.matrixClient.getRoom(roomId);
|
||||||
var self = this;
|
var self = this;
|
||||||
if (!room) {
|
if (!room) {
|
||||||
this.props.onFinished();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var powerLevelEvent = room.currentState.getStateEvents(
|
var powerLevelEvent = room.currentState.getStateEvents(
|
||||||
"m.room.power_levels", ""
|
"m.room.power_levels", ""
|
||||||
);
|
);
|
||||||
if (!powerLevelEvent) {
|
if (!powerLevelEvent) {
|
||||||
this.props.onFinished();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (powerLevelEvent.getContent().users) {
|
if (powerLevelEvent.getContent().users) {
|
||||||
|
@ -422,9 +432,6 @@ module.exports = WithMatrixClient(React.createClass({
|
||||||
if (confirmed) {
|
if (confirmed) {
|
||||||
self._applyPowerChange(roomId, target, powerLevel, powerLevelEvent);
|
self._applyPowerChange(roomId, target, powerLevel, powerLevelEvent);
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
self.props.onFinished();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -440,7 +447,6 @@ module.exports = WithMatrixClient(React.createClass({
|
||||||
onNewDMClick: function() {
|
onNewDMClick: function() {
|
||||||
this.setState({ updating: this.state.updating + 1 });
|
this.setState({ updating: this.state.updating + 1 });
|
||||||
createRoom({dmUserId: this.props.member.userId}).finally(() => {
|
createRoom({dmUserId: this.props.member.userId}).finally(() => {
|
||||||
this.props.onFinished();
|
|
||||||
this.setState({ updating: this.state.updating - 1 });
|
this.setState({ updating: this.state.updating - 1 });
|
||||||
}).done();
|
}).done();
|
||||||
},
|
},
|
||||||
|
@ -450,30 +456,29 @@ module.exports = WithMatrixClient(React.createClass({
|
||||||
action: 'leave_room',
|
action: 'leave_room',
|
||||||
room_id: this.props.member.roomId,
|
room_id: this.props.member.roomId,
|
||||||
});
|
});
|
||||||
this.props.onFinished();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_calculateOpsPermissions: function(member) {
|
_calculateOpsPermissions: function(member) {
|
||||||
var defaultPerms = {
|
const defaultPerms = {
|
||||||
can: {},
|
can: {},
|
||||||
muted: false,
|
muted: false,
|
||||||
modifyLevel: false
|
modifyLevel: false
|
||||||
};
|
};
|
||||||
var room = this.props.matrixClient.getRoom(member.roomId);
|
const room = this.props.matrixClient.getRoom(member.roomId);
|
||||||
if (!room) {
|
if (!room) {
|
||||||
return defaultPerms;
|
return defaultPerms;
|
||||||
}
|
}
|
||||||
var powerLevels = room.currentState.getStateEvents(
|
const powerLevels = room.currentState.getStateEvents(
|
||||||
"m.room.power_levels", ""
|
"m.room.power_levels", ""
|
||||||
);
|
);
|
||||||
if (!powerLevels) {
|
if (!powerLevels) {
|
||||||
return defaultPerms;
|
return defaultPerms;
|
||||||
}
|
}
|
||||||
var me = room.getMember(this.props.matrixClient.credentials.userId);
|
const me = room.getMember(this.props.matrixClient.credentials.userId);
|
||||||
if (!me) {
|
if (!me) {
|
||||||
return defaultPerms;
|
return defaultPerms;
|
||||||
}
|
}
|
||||||
var them = member;
|
const them = member;
|
||||||
return {
|
return {
|
||||||
can: this._calculateCanPermissions(
|
can: this._calculateCanPermissions(
|
||||||
me, them, powerLevels.getContent()
|
me, them, powerLevels.getContent()
|
||||||
|
@ -484,22 +489,22 @@ module.exports = WithMatrixClient(React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
_calculateCanPermissions: function(me, them, powerLevels) {
|
_calculateCanPermissions: function(me, them, powerLevels) {
|
||||||
var can = {
|
const can = {
|
||||||
kick: false,
|
kick: false,
|
||||||
ban: false,
|
ban: false,
|
||||||
mute: false,
|
mute: false,
|
||||||
modifyLevel: false
|
modifyLevel: false
|
||||||
};
|
};
|
||||||
var canAffectUser = them.powerLevel < me.powerLevel;
|
const canAffectUser = them.powerLevel < me.powerLevel;
|
||||||
if (!canAffectUser) {
|
if (!canAffectUser) {
|
||||||
//console.log("Cannot affect user: %s >= %s", them.powerLevel, me.powerLevel);
|
//console.log("Cannot affect user: %s >= %s", them.powerLevel, me.powerLevel);
|
||||||
return can;
|
return can;
|
||||||
}
|
}
|
||||||
var editPowerLevel = (
|
const editPowerLevel = (
|
||||||
(powerLevels.events ? powerLevels.events["m.room.power_levels"] : null) ||
|
(powerLevels.events ? powerLevels.events["m.room.power_levels"] : null) ||
|
||||||
powerLevels.state_default
|
powerLevels.state_default
|
||||||
);
|
);
|
||||||
var levelToSend = (
|
const levelToSend = (
|
||||||
(powerLevels.events ? powerLevels.events["m.room.message"] : null) ||
|
(powerLevels.events ? powerLevels.events["m.room.message"] : null) ||
|
||||||
powerLevels.events_default
|
powerLevels.events_default
|
||||||
);
|
);
|
||||||
|
@ -646,10 +651,14 @@ module.exports = WithMatrixClient(React.createClass({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (this.state.can.ban) {
|
if (this.state.can.ban) {
|
||||||
|
let label = 'Ban';
|
||||||
|
if (this.props.member.membership == 'ban') {
|
||||||
|
label = 'Unban';
|
||||||
|
}
|
||||||
banButton = (
|
banButton = (
|
||||||
<AccessibleButton className="mx_MemberInfo_field"
|
<AccessibleButton className="mx_MemberInfo_field"
|
||||||
onClick={this.onBan}>
|
onClick={this.onBanOrUnban}>
|
||||||
Ban
|
{label}
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,17 +14,18 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var q = require("q");
|
import q from 'q';
|
||||||
var React = require('react');
|
import React from 'react';
|
||||||
var MatrixClientPeg = require('../../../MatrixClientPeg');
|
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||||
var SdkConfig = require('../../../SdkConfig');
|
import SdkConfig from '../../../SdkConfig';
|
||||||
var sdk = require('../../../index');
|
import sdk from '../../../index';
|
||||||
var Modal = require('../../../Modal');
|
import Modal from '../../../Modal';
|
||||||
var ObjectUtils = require("../../../ObjectUtils");
|
import ObjectUtils from '../../../ObjectUtils';
|
||||||
var dis = require("../../../dispatcher");
|
import dis from '../../../dispatcher';
|
||||||
var ScalarAuthClient = require("../../../ScalarAuthClient");
|
import ScalarAuthClient from '../../../ScalarAuthClient';
|
||||||
var ScalarMessaging = require('../../../ScalarMessaging');
|
import ScalarMessaging from '../../../ScalarMessaging';
|
||||||
var UserSettingsStore = require('../../../UserSettingsStore');
|
import UserSettingsStore from '../../../UserSettingsStore';
|
||||||
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
|
|
||||||
|
|
||||||
// parse a string as an integer; if the input is undefined, or cannot be parsed
|
// parse a string as an integer; if the input is undefined, or cannot be parsed
|
||||||
|
@ -635,16 +636,16 @@ module.exports = React.createClass({
|
||||||
if (myMember) {
|
if (myMember) {
|
||||||
if (myMember.membership === "join") {
|
if (myMember.membership === "join") {
|
||||||
leaveButton = (
|
leaveButton = (
|
||||||
<div className="mx_RoomSettings_leaveButton" onClick={ this.onLeaveClick }>
|
<AccessibleButton className="mx_RoomSettings_leaveButton" onClick={ this.onLeaveClick }>
|
||||||
Leave room
|
Leave room
|
||||||
</div>
|
</AccessibleButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else if (myMember.membership === "leave") {
|
else if (myMember.membership === "leave") {
|
||||||
leaveButton = (
|
leaveButton = (
|
||||||
<div className="mx_RoomSettings_leaveButton" onClick={ this.onForgetClick }>
|
<AccessibleButton className="mx_RoomSettings_leaveButton" onClick={ this.onForgetClick }>
|
||||||
Forget room
|
Forget room
|
||||||
</div>
|
</AccessibleButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,16 +67,24 @@ describe('InteractiveAuthDialog', function () {
|
||||||
onFinished={onFinished}
|
onFinished={onFinished}
|
||||||
/>, parentDiv);
|
/>, parentDiv);
|
||||||
|
|
||||||
// at this point there should be a password box
|
// at this point there should be a password box and a submit button
|
||||||
const passwordNode = ReactTestUtils.findRenderedDOMComponentWithTag(
|
const formNode = ReactTestUtils.findRenderedDOMComponentWithTag(dlg, "form");
|
||||||
|
const inputNodes = ReactTestUtils.scryRenderedDOMComponentsWithTag(
|
||||||
dlg, "input"
|
dlg, "input"
|
||||||
);
|
);
|
||||||
expect(passwordNode.type).toEqual("password");
|
let passwordNode;
|
||||||
|
let submitNode;
|
||||||
|
for (const node of inputNodes) {
|
||||||
|
if (node.type == 'password') {
|
||||||
|
passwordNode = node;
|
||||||
|
} else if (node.type == 'submit') {
|
||||||
|
submitNode = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect(passwordNode).toExist();
|
||||||
|
expect(submitNode).toExist();
|
||||||
|
|
||||||
// submit should be disabled
|
// submit should be disabled
|
||||||
const submitNode = ReactTestUtils.findRenderedDOMComponentWithClass(
|
|
||||||
dlg, "mx_Dialog_primary"
|
|
||||||
);
|
|
||||||
expect(submitNode.disabled).toBe(true);
|
expect(submitNode.disabled).toBe(true);
|
||||||
|
|
||||||
// put something in the password box, and hit enter; that should
|
// put something in the password box, and hit enter; that should
|
||||||
|
@ -84,9 +92,7 @@ describe('InteractiveAuthDialog', function () {
|
||||||
passwordNode.value = "s3kr3t";
|
passwordNode.value = "s3kr3t";
|
||||||
ReactTestUtils.Simulate.change(passwordNode);
|
ReactTestUtils.Simulate.change(passwordNode);
|
||||||
expect(submitNode.disabled).toBe(false);
|
expect(submitNode.disabled).toBe(false);
|
||||||
ReactTestUtils.Simulate.keyDown(passwordNode, {
|
ReactTestUtils.Simulate.submit(formNode, {});
|
||||||
key: "Enter", keyCode: 13, which: 13,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(doRequest.callCount).toEqual(1);
|
expect(doRequest.callCount).toEqual(1);
|
||||||
expect(doRequest.calledWithExactly({
|
expect(doRequest.calledWithExactly({
|
||||||
|
@ -96,8 +102,10 @@ describe('InteractiveAuthDialog', function () {
|
||||||
user: "@user:id",
|
user: "@user:id",
|
||||||
})).toBe(true);
|
})).toBe(true);
|
||||||
|
|
||||||
// the submit button should now be disabled (and be a spinner)
|
// there should now be a spinner
|
||||||
expect(submitNode.disabled).toBe(true);
|
ReactTestUtils.findRenderedComponentWithType(
|
||||||
|
dlg, sdk.getComponent('elements.Spinner'),
|
||||||
|
);
|
||||||
|
|
||||||
// let the request complete
|
// let the request complete
|
||||||
q.delay(1).then(() => {
|
q.delay(1).then(() => {
|
||||||
|
|
Loading…
Reference in a new issue