Merge remote-tracking branch 'origin/develop' into t3chguy/updating_stuff
This commit is contained in:
commit
a2dc17256a
58 changed files with 4934 additions and 1050 deletions
|
@ -240,7 +240,6 @@ export default React.createClass({
|
|||
page_element = <UserSettings
|
||||
onClose={this.props.onUserSettingsClose}
|
||||
brand={this.props.config.brand}
|
||||
collapsedRhs={this.props.collapse_rhs}
|
||||
enableLabs={this.props.config.enableLabs}
|
||||
referralBaseUrl={this.props.config.referralBaseUrl}
|
||||
teamToken={this.props.teamToken}
|
||||
|
@ -271,7 +270,6 @@ export default React.createClass({
|
|||
this.props.config.teamServerConfig.teamServerURL : null;
|
||||
|
||||
page_element = <HomePage
|
||||
collapsedRhs={this.props.collapse_rhs}
|
||||
teamServerUrl={teamServerUrl}
|
||||
teamToken={this.props.teamToken}
|
||||
homePageUrl={this.props.config.welcomePageUrl}
|
||||
|
|
|
@ -35,15 +35,50 @@ import * as Rooms from '../../Rooms';
|
|||
import linkifyMatrix from "../../linkify-matrix";
|
||||
import * as Lifecycle from '../../Lifecycle';
|
||||
// LifecycleStore is not used but does listen to and dispatch actions
|
||||
import LifecycleStore from '../../stores/LifecycleStore';
|
||||
require('../../stores/LifecycleStore');
|
||||
import RoomViewStore from '../../stores/RoomViewStore';
|
||||
import PageTypes from '../../PageTypes';
|
||||
|
||||
import createRoom from "../../createRoom";
|
||||
import * as UDEHandler from '../../UnknownDeviceErrorHandler';
|
||||
import KeyRequestHandler from '../../KeyRequestHandler';
|
||||
import { _t, getCurrentLanguage } from '../../languageHandler';
|
||||
|
||||
/** constants for MatrixChat.state.view */
|
||||
const VIEWS = {
|
||||
// a special initial state which is only used at startup, while we are
|
||||
// trying to re-animate a matrix client or register as a guest.
|
||||
LOADING: 0,
|
||||
|
||||
// we are showing the login view
|
||||
LOGIN: 1,
|
||||
|
||||
// we are showing the registration view
|
||||
REGISTER: 2,
|
||||
|
||||
// completeing the registration flow
|
||||
POST_REGISTRATION: 3,
|
||||
|
||||
// showing the 'forgot password' view
|
||||
FORGOT_PASSWORD: 4,
|
||||
|
||||
// we have valid matrix credentials (either via an explicit login, via the
|
||||
// initial re-animation/guest registration, or via a registration), and are
|
||||
// now setting up a matrixclient to talk to it. This isn't an instant
|
||||
// process because (a) we need to clear out indexeddb, and (b) we need to
|
||||
// talk to the team server; while it is going on we show a big spinner.
|
||||
LOGGING_IN: 5,
|
||||
|
||||
// we are logged in with an active matrix client.
|
||||
LOGGED_IN: 6,
|
||||
};
|
||||
|
||||
module.exports = React.createClass({
|
||||
// we export this so that the integration tests can use it :-S
|
||||
statics: {
|
||||
VIEWS: VIEWS,
|
||||
},
|
||||
|
||||
displayName: 'MatrixChat',
|
||||
|
||||
propTypes: {
|
||||
|
@ -59,8 +94,8 @@ module.exports = React.createClass({
|
|||
// the initial queryParams extracted from the hash-fragment of the URI
|
||||
startingFragmentQueryParams: React.PropTypes.object,
|
||||
|
||||
// called when the session load completes
|
||||
onLoadCompleted: React.PropTypes.func,
|
||||
// called when we have completed a token login
|
||||
onTokenLoginCompleted: React.PropTypes.func,
|
||||
|
||||
// Represents the screen to display as a result of parsing the initial
|
||||
// window.location
|
||||
|
@ -93,14 +128,11 @@ module.exports = React.createClass({
|
|||
|
||||
getInitialState: function() {
|
||||
const s = {
|
||||
loading: true,
|
||||
screen: undefined,
|
||||
screenAfterLogin: this.props.initialScreenAfterLogin,
|
||||
// the master view we are showing.
|
||||
view: VIEWS.LOADING,
|
||||
|
||||
// Stashed guest credentials if the user logs out
|
||||
// whilst logged in as a guest user (so they can change
|
||||
// their mind & log back in)
|
||||
guestCreds: null,
|
||||
// a thing to call showScreen with once login completes.
|
||||
screenAfterLogin: this.props.initialScreenAfterLogin,
|
||||
|
||||
// What the LoggedInView would be showing if visible
|
||||
page_type: null,
|
||||
|
@ -113,8 +145,6 @@ module.exports = React.createClass({
|
|||
// If we're trying to just view a user ID (i.e. /user URL), this is it
|
||||
viewUserId: null,
|
||||
|
||||
loggedIn: false,
|
||||
loggingIn: false,
|
||||
collapse_lhs: false,
|
||||
collapse_rhs: false,
|
||||
ready: false,
|
||||
|
@ -129,11 +159,6 @@ module.exports = React.createClass({
|
|||
newVersionReleaseNotes: null,
|
||||
checkingForUpdate: null,
|
||||
|
||||
// The username to default to when upgrading an account from a guest
|
||||
upgradeUsername: null,
|
||||
// The access token we had for our guest account, used when upgrading to a normal account
|
||||
guestAccessToken: null,
|
||||
|
||||
// Parameters used in the registration dance with the IS
|
||||
register_client_secret: null,
|
||||
register_session_id: null,
|
||||
|
@ -149,7 +174,7 @@ module.exports = React.createClass({
|
|||
realQueryParams: {},
|
||||
startingFragmentQueryParams: {},
|
||||
config: {},
|
||||
onLoadCompleted: () => {},
|
||||
onTokenLoginCompleted: () => {},
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -192,7 +217,7 @@ module.exports = React.createClass({
|
|||
componentWillMount: function() {
|
||||
SdkConfig.put(this.props.config);
|
||||
|
||||
RoomViewStore.addListener(this._onRoomViewStoreUpdated);
|
||||
this._roomViewStoreToken = RoomViewStore.addListener(this._onRoomViewStoreUpdated);
|
||||
this._onRoomViewStoreUpdated();
|
||||
|
||||
if (!UserSettingsStore.getLocalSetting('analyticsOptOut', false)) Analytics.enable();
|
||||
|
@ -269,30 +294,52 @@ module.exports = React.createClass({
|
|||
window.addEventListener('resize', this.handleResize);
|
||||
this.handleResize();
|
||||
|
||||
if (this.props.config.teamServerConfig &&
|
||||
this.props.config.teamServerConfig.teamServerURL
|
||||
) {
|
||||
Lifecycle.initRtsClient(this.props.config.teamServerConfig.teamServerURL);
|
||||
}
|
||||
const teamServerConfig = this.props.config.teamServerConfig || {};
|
||||
Lifecycle.initRtsClient(teamServerConfig.teamServerURL);
|
||||
|
||||
// the extra q() ensures that synchronous exceptions hit the same codepath as
|
||||
// asynchronous ones.
|
||||
q().then(() => {
|
||||
return Lifecycle.loadSession({
|
||||
realQueryParams: this.props.realQueryParams,
|
||||
fragmentQueryParams: this.props.startingFragmentQueryParams,
|
||||
enableGuest: this.props.enableGuest,
|
||||
guestHsUrl: this.getCurrentHsUrl(),
|
||||
guestIsUrl: this.getCurrentIsUrl(),
|
||||
defaultDeviceDisplayName: this.props.defaultDeviceDisplayName,
|
||||
// the first thing to do is to try the token params in the query-string
|
||||
Lifecycle.attemptTokenLogin(this.props.realQueryParams).then((loggedIn) => {
|
||||
if(loggedIn) {
|
||||
this.props.onTokenLoginCompleted();
|
||||
|
||||
// don't do anything else until the page reloads - just stay in
|
||||
// the 'loading' state.
|
||||
return;
|
||||
}
|
||||
|
||||
// if the user has followed a login or register link, don't reanimate
|
||||
// the old creds, but rather go straight to the relevant page
|
||||
const firstScreen = this.state.screenAfterLogin ?
|
||||
this.state.screenAfterLogin.screen : null;
|
||||
|
||||
if (firstScreen === 'login' ||
|
||||
firstScreen === 'register' ||
|
||||
firstScreen === 'forgot_password') {
|
||||
this.setState({loading: false});
|
||||
this._showScreenAfterLogin();
|
||||
return;
|
||||
}
|
||||
|
||||
// the extra q() ensures that synchronous exceptions hit the same codepath as
|
||||
// asynchronous ones.
|
||||
return q().then(() => {
|
||||
return Lifecycle.loadSession({
|
||||
fragmentQueryParams: this.props.startingFragmentQueryParams,
|
||||
enableGuest: this.props.enableGuest,
|
||||
guestHsUrl: this.getCurrentHsUrl(),
|
||||
guestIsUrl: this.getCurrentIsUrl(),
|
||||
defaultDeviceDisplayName: this.props.defaultDeviceDisplayName,
|
||||
});
|
||||
}).catch((e) => {
|
||||
console.error("Unable to load session", e);
|
||||
return false;
|
||||
}).then((loadedSession) => {
|
||||
if (!loadedSession) {
|
||||
// fall back to showing the login screen
|
||||
dis.dispatch({action: "start_login"});
|
||||
}
|
||||
});
|
||||
}).catch((e) => {
|
||||
console.error("Unable to load session", e);
|
||||
}).done(()=>{
|
||||
// stuff this through the dispatcher so that it happens
|
||||
// after the on_logged_in action.
|
||||
dis.dispatch({action: 'load_completed'});
|
||||
});
|
||||
}).done();
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
|
@ -301,6 +348,7 @@ module.exports = React.createClass({
|
|||
UDEHandler.stopListening();
|
||||
window.removeEventListener("focus", this.onFocus);
|
||||
window.removeEventListener('resize', this.handleResize);
|
||||
this._roomViewStoreToken.remove();
|
||||
},
|
||||
|
||||
componentDidUpdate: function() {
|
||||
|
@ -310,20 +358,19 @@ module.exports = React.createClass({
|
|||
}
|
||||
},
|
||||
|
||||
setStateForNewScreen: function(state) {
|
||||
setStateForNewView: function(state) {
|
||||
if (state.view === undefined) {
|
||||
throw new Error("setStateForNewView with no view!");
|
||||
}
|
||||
const newState = {
|
||||
screen: undefined,
|
||||
viewUserId: null,
|
||||
loggedIn: false,
|
||||
ready: false,
|
||||
upgradeUsername: null,
|
||||
guestAccessToken: null,
|
||||
};
|
||||
Object.assign(newState, state);
|
||||
this.setState(newState);
|
||||
},
|
||||
|
||||
onAction: function(payload) {
|
||||
// console.log(`MatrixClientPeg.onAction: ${payload.action}`);
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
|
||||
|
@ -335,45 +382,19 @@ module.exports = React.createClass({
|
|||
this._startRegistration(payload.params || {});
|
||||
break;
|
||||
case 'start_login':
|
||||
if (MatrixClientPeg.get() &&
|
||||
MatrixClientPeg.get().isGuest()
|
||||
) {
|
||||
this.setState({
|
||||
guestCreds: MatrixClientPeg.getCredentials(),
|
||||
});
|
||||
}
|
||||
this.setStateForNewScreen({
|
||||
screen: 'login',
|
||||
this.setStateForNewView({
|
||||
view: VIEWS.LOGIN,
|
||||
});
|
||||
this.notifyNewScreen('login');
|
||||
break;
|
||||
case 'start_post_registration':
|
||||
this.setState({ // don't clobber loggedIn status
|
||||
screen: 'post_registration',
|
||||
this.setState({
|
||||
view: VIEWS.POST_REGISTRATION,
|
||||
});
|
||||
break;
|
||||
case 'start_upgrade_registration':
|
||||
// also stash our credentials, then if we restore the session,
|
||||
// we can just do it the same way whether we started upgrade
|
||||
// registration or explicitly logged out
|
||||
this.setStateForNewScreen({
|
||||
guestCreds: MatrixClientPeg.getCredentials(),
|
||||
screen: "register",
|
||||
upgradeUsername: MatrixClientPeg.get().getUserIdLocalpart(),
|
||||
guestAccessToken: MatrixClientPeg.get().getAccessToken(),
|
||||
});
|
||||
|
||||
// stop the client: if we are syncing whilst the registration
|
||||
// is completed in another browser, we'll be 401ed for using
|
||||
// a guest access token for a non-guest account.
|
||||
// It will be restarted in onReturnToGuestClick
|
||||
Lifecycle.stopMatrixClient();
|
||||
|
||||
this.notifyNewScreen('register');
|
||||
break;
|
||||
case 'start_password_recovery':
|
||||
this.setStateForNewScreen({
|
||||
screen: 'forgot_password',
|
||||
this.setStateForNewView({
|
||||
view: VIEWS.FORGOT_PASSWORD,
|
||||
});
|
||||
this.notifyNewScreen('forgot_password');
|
||||
break;
|
||||
|
@ -397,7 +418,7 @@ module.exports = React.createClass({
|
|||
|
||||
MatrixClientPeg.get().leave(payload.room_id).done(() => {
|
||||
modal.close();
|
||||
if (this.currentRoomId === payload.room_id) {
|
||||
if (this.state.currentRoomId === payload.room_id) {
|
||||
dis.dispatch({action: 'view_next_room'});
|
||||
}
|
||||
}, (err) => {
|
||||
|
@ -514,10 +535,11 @@ module.exports = React.createClass({
|
|||
break;
|
||||
case 'on_logging_in':
|
||||
// We are now logging in, so set the state to reflect that
|
||||
// and also that we're not ready (we'll be marked as logged
|
||||
// in once the login completes, then ready once the sync
|
||||
// completes).
|
||||
this.setState({loggingIn: true, ready: false});
|
||||
// NB. This does not touch 'ready' since if our dispatches
|
||||
// are delayed, the sync could already have completed
|
||||
this.setStateForNewView({
|
||||
view: VIEWS.LOGGING_IN,
|
||||
});
|
||||
break;
|
||||
case 'on_logged_in':
|
||||
this._onLoggedIn(payload.teamToken);
|
||||
|
@ -528,9 +550,6 @@ module.exports = React.createClass({
|
|||
case 'will_start_client':
|
||||
this._onWillStartClient();
|
||||
break;
|
||||
case 'load_completed':
|
||||
this._onLoadCompleted();
|
||||
break;
|
||||
case 'new_version':
|
||||
this.onVersion(
|
||||
payload.currentVersion, payload.newVersion,
|
||||
|
@ -540,6 +559,9 @@ module.exports = React.createClass({
|
|||
case 'check_updates':
|
||||
this.setState({ checkingForUpdate: payload.value });
|
||||
break;
|
||||
case 'send_event':
|
||||
this.onSendEvent(payload.room_id, payload.event);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -554,8 +576,8 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
_startRegistration: function(params) {
|
||||
this.setStateForNewScreen({
|
||||
screen: 'register',
|
||||
this.setStateForNewView({
|
||||
view: VIEWS.REGISTER,
|
||||
// these params may be undefined, but if they are,
|
||||
// unset them from our state: we don't want to
|
||||
// resume a previous registration session if the
|
||||
|
@ -749,8 +771,8 @@ module.exports = React.createClass({
|
|||
title: _t('Create Room'),
|
||||
description: _t('Room name (optional)'),
|
||||
button: _t('Create Room'),
|
||||
onFinished: (should_create, name) => {
|
||||
if (should_create) {
|
||||
onFinished: (shouldCreate, name) => {
|
||||
if (shouldCreate) {
|
||||
const createOpts = {};
|
||||
if (name) createOpts.name = name;
|
||||
createRoom({createOpts}).done();
|
||||
|
@ -863,22 +885,6 @@ module.exports = React.createClass({
|
|||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when the sessionloader has finished
|
||||
*/
|
||||
_onLoadCompleted: function() {
|
||||
this.props.onLoadCompleted();
|
||||
this.setState({loading: false});
|
||||
|
||||
// Show screens (like 'register') that need to be shown without _onLoggedIn
|
||||
// being called. 'register' needs to be routed here when the email confirmation
|
||||
// link is clicked on.
|
||||
if (this.state.screenAfterLogin &&
|
||||
['register'].indexOf(this.state.screenAfterLogin.screen) !== -1) {
|
||||
this._showScreenAfterLogin();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Called whenever someone changes the theme
|
||||
*
|
||||
|
@ -931,9 +937,7 @@ module.exports = React.createClass({
|
|||
*/
|
||||
_onLoggedIn: function(teamToken) {
|
||||
this.setState({
|
||||
guestCreds: null,
|
||||
loggedIn: true,
|
||||
loggingIn: false,
|
||||
view: VIEWS.LOGGED_IN,
|
||||
});
|
||||
|
||||
if (teamToken) {
|
||||
|
@ -975,6 +979,7 @@ module.exports = React.createClass({
|
|||
this.state.screenAfterLogin.screen,
|
||||
this.state.screenAfterLogin.params,
|
||||
);
|
||||
// XXX: is this necessary? `showScreen` should do it for us.
|
||||
this.notifyNewScreen(this.state.screenAfterLogin.screen);
|
||||
this.setState({screenAfterLogin: null});
|
||||
} else if (localStorage && localStorage.getItem('mx_last_room_id')) {
|
||||
|
@ -993,8 +998,8 @@ module.exports = React.createClass({
|
|||
*/
|
||||
_onLoggedOut: function() {
|
||||
this.notifyNewScreen('login');
|
||||
this.setStateForNewScreen({
|
||||
loggedIn: false,
|
||||
this.setStateForNewView({
|
||||
view: VIEWS.LOGIN,
|
||||
ready: false,
|
||||
collapse_lhs: false,
|
||||
collapse_rhs: false,
|
||||
|
@ -1010,6 +1015,10 @@ module.exports = React.createClass({
|
|||
*/
|
||||
_onWillStartClient() {
|
||||
const self = this;
|
||||
// if the client is about to start, we are, by definition, not ready.
|
||||
// Set ready to false now, then it'll be set to true when the sync
|
||||
// listener we set below fires.
|
||||
this.setState({ready: false});
|
||||
const cli = MatrixClientPeg.get();
|
||||
|
||||
// Allow the JS SDK to reap timeline events. This reduces the amount of
|
||||
|
@ -1080,6 +1089,14 @@ module.exports = React.createClass({
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
const krh = new KeyRequestHandler(cli);
|
||||
cli.on("crypto.roomKeyRequest", (req) => {
|
||||
krh.handleKeyRequest(req);
|
||||
});
|
||||
cli.on("crypto.roomKeyRequestCancellation", (req) => {
|
||||
krh.handleKeyRequestCancellation(req);
|
||||
});
|
||||
},
|
||||
|
||||
showScreen: function(screen, params) {
|
||||
|
@ -1157,7 +1174,7 @@ module.exports = React.createClass({
|
|||
|
||||
// we can't view a room unless we're logged in
|
||||
// (a guest account is fine)
|
||||
if (this.state.loggedIn) {
|
||||
if (this.state.view === VIEWS.LOGGED_IN) {
|
||||
dis.dispatch(payload);
|
||||
}
|
||||
} else if (screen.indexOf('user/') == 0) {
|
||||
|
@ -1255,27 +1272,25 @@ module.exports = React.createClass({
|
|||
this.showScreen("forgot_password");
|
||||
},
|
||||
|
||||
onReturnToGuestClick: function() {
|
||||
// reanimate our guest login
|
||||
if (this.state.guestCreds) {
|
||||
Lifecycle.setLoggedIn(this.state.guestCreds);
|
||||
this.setState({guestCreds: null});
|
||||
}
|
||||
onReturnToAppClick: function() {
|
||||
// treat it the same as if the user had completed the login
|
||||
this._onLoggedIn(null);
|
||||
},
|
||||
|
||||
// returns a promise which resolves to the new MatrixClient
|
||||
onRegistered: function(credentials, teamToken) {
|
||||
// XXX: These both should be in state or ideally store(s) because we risk not
|
||||
// rendering the most up-to-date view of state otherwise.
|
||||
// teamToken may not be truthy
|
||||
this._teamToken = teamToken;
|
||||
this._is_registered = true;
|
||||
Lifecycle.setLoggedIn(credentials);
|
||||
return Lifecycle.setLoggedIn(credentials);
|
||||
},
|
||||
|
||||
onFinishPostRegistration: function() {
|
||||
// Don't confuse this with "PageType" which is the middle window to show
|
||||
this.setState({
|
||||
screen: undefined,
|
||||
view: VIEWS.LOGGED_IN,
|
||||
});
|
||||
this.showScreen("settings");
|
||||
},
|
||||
|
@ -1290,6 +1305,27 @@ module.exports = React.createClass({
|
|||
});
|
||||
},
|
||||
|
||||
onSendEvent: function(roomId, event) {
|
||||
const cli = MatrixClientPeg.get();
|
||||
if (!cli) {
|
||||
dis.dispatch({action: 'message_send_failed'});
|
||||
return;
|
||||
}
|
||||
|
||||
cli.sendEvent(roomId, event.getType(), event.getContent()).done(() => {
|
||||
dis.dispatch({action: 'message_sent'});
|
||||
}, (err) => {
|
||||
if (err.name === 'UnknownDeviceError') {
|
||||
dis.dispatch({
|
||||
action: 'unknown_device_error',
|
||||
err: err,
|
||||
room: cli.getRoom(roomId),
|
||||
});
|
||||
}
|
||||
dis.dispatch({action: 'message_send_failed'});
|
||||
});
|
||||
},
|
||||
|
||||
updateStatusIndicator: function(state, prevState) {
|
||||
let notifCount = 0;
|
||||
|
||||
|
@ -1344,11 +1380,9 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
render: function() {
|
||||
// `loading` might be set to false before `loggedIn = true`, causing the default
|
||||
// (`<Login>`) to be visible for a few MS (say, whilst a request is in-flight to
|
||||
// the RTS). So in the meantime, use `loggingIn`, which is true between
|
||||
// actions `on_logging_in` and `on_logged_in`.
|
||||
if (this.state.loading || this.state.loggingIn) {
|
||||
// console.log(`Rendering MatrixChat with view ${this.state.view}`);
|
||||
|
||||
if (this.state.view === VIEWS.LOADING || this.state.view === VIEWS.LOGGING_IN) {
|
||||
const Spinner = sdk.getComponent('elements.Spinner');
|
||||
return (
|
||||
<div className="mx_MatrixChat_splash">
|
||||
|
@ -1358,7 +1392,7 @@ module.exports = React.createClass({
|
|||
}
|
||||
|
||||
// needs to be before normal PageTypes as you are logged in technically
|
||||
if (this.state.screen == 'post_registration') {
|
||||
if (this.state.view === VIEWS.POST_REGISTRATION) {
|
||||
const PostRegistration = sdk.getComponent('structures.login.PostRegistration');
|
||||
return (
|
||||
<PostRegistration
|
||||
|
@ -1366,38 +1400,42 @@ module.exports = React.createClass({
|
|||
);
|
||||
}
|
||||
|
||||
// `ready` and `loggedIn` may be set before `page_type` (because the
|
||||
// latter is set via the dispatcher). If we don't yet have a `page_type`,
|
||||
// keep showing the spinner for now.
|
||||
if (this.state.loggedIn && this.state.ready && this.state.page_type) {
|
||||
/* for now, we stuff the entirety of our props and state into the LoggedInView.
|
||||
* we should go through and figure out what we actually need to pass down, as well
|
||||
* as using something like redux to avoid having a billion bits of state kicking around.
|
||||
*/
|
||||
const LoggedInView = sdk.getComponent('structures.LoggedInView');
|
||||
return (
|
||||
<LoggedInView ref="loggedInView" matrixClient={MatrixClientPeg.get()}
|
||||
onRoomCreated={this.onRoomCreated}
|
||||
onUserSettingsClose={this.onUserSettingsClose}
|
||||
onRegistered={this.onRegistered}
|
||||
currentRoomId={this.state.currentRoomId}
|
||||
teamToken={this._teamToken}
|
||||
{...this.props}
|
||||
{...this.state}
|
||||
/>
|
||||
);
|
||||
} else if (this.state.loggedIn) {
|
||||
// we think we are logged in, but are still waiting for the /sync to complete
|
||||
const Spinner = sdk.getComponent('elements.Spinner');
|
||||
return (
|
||||
<div className="mx_MatrixChat_splash">
|
||||
<Spinner />
|
||||
<a href="#" className="mx_MatrixChat_splashButtons" onClick={ this.onLogoutClick }>
|
||||
{ _t('Logout') }
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
} else if (this.state.screen == 'register') {
|
||||
if (this.state.view === VIEWS.LOGGED_IN) {
|
||||
// `ready` and `view==LOGGED_IN` may be set before `page_type` (because the
|
||||
// latter is set via the dispatcher). If we don't yet have a `page_type`,
|
||||
// keep showing the spinner for now.
|
||||
if (this.state.ready && this.state.page_type) {
|
||||
/* for now, we stuff the entirety of our props and state into the LoggedInView.
|
||||
* we should go through and figure out what we actually need to pass down, as well
|
||||
* as using something like redux to avoid having a billion bits of state kicking around.
|
||||
*/
|
||||
const LoggedInView = sdk.getComponent('structures.LoggedInView');
|
||||
return (
|
||||
<LoggedInView ref="loggedInView" matrixClient={MatrixClientPeg.get()}
|
||||
onRoomCreated={this.onRoomCreated}
|
||||
onUserSettingsClose={this.onUserSettingsClose}
|
||||
onRegistered={this.onRegistered}
|
||||
currentRoomId={this.state.currentRoomId}
|
||||
teamToken={this._teamToken}
|
||||
{...this.props}
|
||||
{...this.state}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
// we think we are logged in, but are still waiting for the /sync to complete
|
||||
const Spinner = sdk.getComponent('elements.Spinner');
|
||||
return (
|
||||
<div className="mx_MatrixChat_splash">
|
||||
<Spinner />
|
||||
<a href="#" className="mx_MatrixChat_splashButtons" onClick={ this.onLogoutClick }>
|
||||
{ _t('Logout') }
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.state.view === VIEWS.REGISTER) {
|
||||
const Registration = sdk.getComponent('structures.login.Registration');
|
||||
return (
|
||||
<Registration
|
||||
|
@ -1406,8 +1444,6 @@ module.exports = React.createClass({
|
|||
idSid={this.state.register_id_sid}
|
||||
email={this.props.startingFragmentQueryParams.email}
|
||||
referrer={this.props.startingFragmentQueryParams.referrer}
|
||||
username={this.state.upgradeUsername}
|
||||
guestAccessToken={this.state.guestAccessToken}
|
||||
defaultHsUrl={this.getDefaultHsUrl()}
|
||||
defaultIsUrl={this.getDefaultIsUrl()}
|
||||
brand={this.props.config.brand}
|
||||
|
@ -1419,10 +1455,13 @@ module.exports = React.createClass({
|
|||
onLoggedIn={this.onRegistered}
|
||||
onLoginClick={this.onLoginClick}
|
||||
onRegisterClick={this.onRegisterClick}
|
||||
onCancelClick={this.state.guestCreds ? this.onReturnToGuestClick : null}
|
||||
onCancelClick={MatrixClientPeg.get() ? this.onReturnToAppClick : null}
|
||||
/>
|
||||
);
|
||||
} else if (this.state.screen == 'forgot_password') {
|
||||
}
|
||||
|
||||
|
||||
if (this.state.view === VIEWS.FORGOT_PASSWORD) {
|
||||
const ForgotPassword = sdk.getComponent('structures.login.ForgotPassword');
|
||||
return (
|
||||
<ForgotPassword
|
||||
|
@ -1434,7 +1473,9 @@ module.exports = React.createClass({
|
|||
onRegisterClick={this.onRegisterClick}
|
||||
onLoginClick={this.onLoginClick} />
|
||||
);
|
||||
} else {
|
||||
}
|
||||
|
||||
if (this.state.view === VIEWS.LOGIN) {
|
||||
const Login = sdk.getComponent('structures.login.Login');
|
||||
return (
|
||||
<Login
|
||||
|
@ -1448,9 +1489,11 @@ module.exports = React.createClass({
|
|||
defaultDeviceDisplayName={this.props.defaultDeviceDisplayName}
|
||||
onForgotPasswordClick={this.onForgotPasswordClick}
|
||||
enableGuest={this.props.enableGuest}
|
||||
onCancelClick={this.state.guestCreds ? this.onReturnToGuestClick : null}
|
||||
onCancelClick={MatrixClientPeg.get() ? this.onReturnToAppClick : null}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
console.error(`Unknown view ${this.state.view}`);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -93,6 +93,7 @@ module.exports = React.createClass({
|
|||
roomId: null,
|
||||
roomLoading: true,
|
||||
peekLoading: false,
|
||||
shouldPeek: true,
|
||||
|
||||
// The event to be scrolled to initially
|
||||
initialEventId: null,
|
||||
|
@ -168,8 +169,32 @@ module.exports = React.createClass({
|
|||
initialEventId: RoomViewStore.getInitialEventId(),
|
||||
initialEventPixelOffset: RoomViewStore.getInitialEventPixelOffset(),
|
||||
isInitialEventHighlighted: RoomViewStore.isInitialEventHighlighted(),
|
||||
forwardingEvent: RoomViewStore.getForwardingEvent(),
|
||||
shouldPeek: RoomViewStore.shouldPeek(),
|
||||
};
|
||||
|
||||
// finished joining, start waiting for a room and show a spinner. See onRoom.
|
||||
newState.waitingForRoom = this.state.joining && !newState.joining &&
|
||||
!RoomViewStore.getJoinError();
|
||||
|
||||
// Temporary logging to diagnose https://github.com/vector-im/riot-web/issues/4307
|
||||
console.log(
|
||||
'RVS update:',
|
||||
newState.roomId,
|
||||
newState.roomAlias,
|
||||
'loading?', newState.roomLoading,
|
||||
'joining?', newState.joining,
|
||||
'initial?', initial,
|
||||
'waiting?', newState.waitingForRoom,
|
||||
'shouldPeek?', newState.shouldPeek,
|
||||
);
|
||||
|
||||
// NB: This does assume that the roomID will not change for the lifetime of
|
||||
// the RoomView instance
|
||||
if (initial) {
|
||||
newState.room = MatrixClientPeg.get().getRoom(newState.roomId);
|
||||
}
|
||||
|
||||
// Clear the search results when clicking a search result (which changes the
|
||||
// currently scrolled to event, this.state.initialEventId).
|
||||
if (this.state.initialEventId !== newState.initialEventId) {
|
||||
|
@ -185,8 +210,9 @@ module.exports = React.createClass({
|
|||
this.setState(newState, () => {
|
||||
// At this point, this.state.roomId could be null (e.g. the alias might not
|
||||
// have been resolved yet) so anything called here must handle this case.
|
||||
this._onHaveRoom();
|
||||
this.onRoom(MatrixClientPeg.get().getRoom(this.state.roomId));
|
||||
if (initial) {
|
||||
this._onHaveRoom();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -202,25 +228,24 @@ module.exports = React.createClass({
|
|||
// which must be by alias or invite wherever possible (peeking currently does
|
||||
// not work over federation).
|
||||
|
||||
// NB. We peek if we are not in the room, although if we try to peek into
|
||||
// a room in which we have a member event (ie. we've left) synapse will just
|
||||
// send us the same data as we get in the sync (ie. the last events we saw).
|
||||
const room = MatrixClientPeg.get().getRoom(this.state.roomId);
|
||||
let isUserJoined = null;
|
||||
// NB. We peek if we have never seen the room before (i.e. js-sdk does not know
|
||||
// about it). We don't peek in the historical case where we were joined but are
|
||||
// now not joined because the js-sdk peeking API will clobber our historical room,
|
||||
// making it impossible to indicate a newly joined room.
|
||||
const room = this.state.room;
|
||||
if (room) {
|
||||
isUserJoined = room.hasMembershipState(
|
||||
MatrixClientPeg.get().credentials.userId, 'join',
|
||||
);
|
||||
|
||||
this._updateAutoComplete(room);
|
||||
this.tabComplete.loadEntries(room);
|
||||
this.setState({
|
||||
unsentMessageError: this._getUnsentMessageError(room),
|
||||
});
|
||||
this._onRoomLoaded(room);
|
||||
}
|
||||
if (!isUserJoined && !this.state.joining && this.state.roomId) {
|
||||
if (!this.state.joining && this.state.roomId) {
|
||||
if (this.props.autoJoin) {
|
||||
this.onJoinButtonClicked();
|
||||
} else if (this.state.roomId) {
|
||||
} else if (!room && this.state.shouldPeek) {
|
||||
console.log("Attempting to peek into room %s", this.state.roomId);
|
||||
|
||||
this.setState({
|
||||
peekLoading: true,
|
||||
});
|
||||
|
@ -244,12 +269,9 @@ module.exports = React.createClass({
|
|||
}
|
||||
}).done();
|
||||
}
|
||||
} else if (isUserJoined) {
|
||||
} else if (room) {
|
||||
// Stop peeking because we have joined this room previously
|
||||
MatrixClientPeg.get().stopPeeking();
|
||||
this.setState({
|
||||
unsentMessageError: this._getUnsentMessageError(room),
|
||||
});
|
||||
this._onRoomLoaded(room);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -436,11 +458,6 @@ module.exports = React.createClass({
|
|||
callState: callState
|
||||
});
|
||||
|
||||
break;
|
||||
case 'forward_event':
|
||||
this.setState({
|
||||
forwardingEvent: payload.content,
|
||||
});
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
@ -607,6 +624,7 @@ module.exports = React.createClass({
|
|||
}
|
||||
this.setState({
|
||||
room: room,
|
||||
waitingForRoom: false,
|
||||
}, () => {
|
||||
this._onRoomLoaded(room);
|
||||
});
|
||||
|
@ -662,7 +680,14 @@ module.exports = React.createClass({
|
|||
|
||||
onRoomMemberMembership: function(ev, member, oldMembership) {
|
||||
if (member.userId == MatrixClientPeg.get().credentials.userId) {
|
||||
this.forceUpdate();
|
||||
|
||||
if (member.membership === 'join') {
|
||||
this.setState({
|
||||
waitingForRoom: false,
|
||||
});
|
||||
} else {
|
||||
this.forceUpdate();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1140,8 +1165,13 @@ module.exports = React.createClass({
|
|||
this.updateTint();
|
||||
this.setState({
|
||||
editingRoomSettings: false,
|
||||
forwardingEvent: null,
|
||||
});
|
||||
if (this.state.forwardingEvent) {
|
||||
dis.dispatch({
|
||||
action: 'forward_event',
|
||||
event: null,
|
||||
});
|
||||
}
|
||||
dis.dispatch({action: 'focus_composer'});
|
||||
},
|
||||
|
||||
|
@ -1416,6 +1446,10 @@ module.exports = React.createClass({
|
|||
const Loader = sdk.getComponent("elements.Spinner");
|
||||
const TimelinePanel = sdk.getComponent("structures.TimelinePanel");
|
||||
|
||||
// Whether the preview bar spinner should be shown. We do this when joining or
|
||||
// when waiting for a room to be returned by js-sdk when joining
|
||||
const previewBarSpinner = this.state.joining || this.state.waitingForRoom;
|
||||
|
||||
if (!this.state.room) {
|
||||
if (this.state.roomLoading || this.state.peekLoading) {
|
||||
return (
|
||||
|
@ -1435,7 +1469,7 @@ module.exports = React.createClass({
|
|||
|
||||
// We have no room object for this room, only the ID.
|
||||
// We've got to this room by following a link, possibly a third party invite.
|
||||
var room_alias = this.state.room_alias;
|
||||
const roomAlias = this.state.roomAlias;
|
||||
return (
|
||||
<div className="mx_RoomView">
|
||||
<RoomHeader ref="header"
|
||||
|
@ -1448,8 +1482,8 @@ module.exports = React.createClass({
|
|||
onForgetClick={ this.onForgetClick }
|
||||
onRejectClick={ this.onRejectThreepidInviteButtonClicked }
|
||||
canPreview={ false } error={ this.state.roomLoadError }
|
||||
roomAlias={room_alias}
|
||||
spinner={this.state.joining}
|
||||
roomAlias={roomAlias}
|
||||
spinner={previewBarSpinner}
|
||||
inviterName={inviterName}
|
||||
invitedEmail={invitedEmail}
|
||||
room={this.state.room}
|
||||
|
@ -1492,7 +1526,7 @@ module.exports = React.createClass({
|
|||
onRejectClick={ this.onRejectButtonClicked }
|
||||
inviterName={ inviterName }
|
||||
canPreview={ false }
|
||||
spinner={this.state.joining}
|
||||
spinner={previewBarSpinner}
|
||||
room={this.state.room}
|
||||
/>
|
||||
</div>
|
||||
|
@ -1548,7 +1582,7 @@ module.exports = React.createClass({
|
|||
} else if (this.state.uploadingRoomSettings) {
|
||||
aux = <Loader/>;
|
||||
} else if (this.state.forwardingEvent !== null) {
|
||||
aux = <ForwardMessage onCancelClick={this.onCancelClick} currentRoomId={this.state.room.roomId} mxEvent={this.state.forwardingEvent} />;
|
||||
aux = <ForwardMessage onCancelClick={this.onCancelClick} />;
|
||||
} else if (this.state.searching) {
|
||||
hideCancel = true; // has own cancel
|
||||
aux = <SearchBar ref="search_bar" searchInProgress={this.state.searchInProgress } onCancelClick={this.onCancelSearchClick} onSearch={this.onSearch}/>;
|
||||
|
@ -1568,7 +1602,7 @@ module.exports = React.createClass({
|
|||
<RoomPreviewBar onJoinClick={this.onJoinButtonClicked}
|
||||
onForgetClick={ this.onForgetClick }
|
||||
onRejectClick={this.onRejectThreepidInviteButtonClicked}
|
||||
spinner={this.state.joining}
|
||||
spinner={previewBarSpinner}
|
||||
inviterName={inviterName}
|
||||
invitedEmail={invitedEmail}
|
||||
canPreview={this.state.canPeek}
|
||||
|
|
|
@ -352,13 +352,14 @@ module.exports = React.createClass({
|
|||
const tile = tiles[backwards ? i : tiles.length - 1 - i];
|
||||
// Subtract height of tile as if it were unpaginated
|
||||
excessHeight -= tile.clientHeight;
|
||||
//If removing the tile would lead to future pagination, break before setting scroll token
|
||||
if (tile.clientHeight > excessHeight) {
|
||||
break;
|
||||
}
|
||||
// The tile may not have a scroll token, so guard it
|
||||
if (tile.dataset.scrollTokens) {
|
||||
markerScrollToken = tile.dataset.scrollTokens.split(',')[0];
|
||||
}
|
||||
if (tile.clientHeight > excessHeight) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (markerScrollToken) {
|
||||
|
|
|
@ -110,6 +110,13 @@ const ANALYTICS_SETTINGS_LABELS = [
|
|||
},
|
||||
];
|
||||
|
||||
const WEBRTC_SETTINGS_LABELS = [
|
||||
{
|
||||
id: 'webRtcForceTURN',
|
||||
label: 'Disable Peer-to-Peer for 1:1 calls',
|
||||
},
|
||||
];
|
||||
|
||||
// Warning: Each "label" string below must be added to i18n/strings/en_EN.json,
|
||||
// since they will be translated when rendered.
|
||||
const CRYPTO_SETTINGS_LABELS = [
|
||||
|
@ -162,9 +169,6 @@ module.exports = React.createClass({
|
|||
// The base URL to use in the referral link. Defaults to window.location.origin.
|
||||
referralBaseUrl: React.PropTypes.string,
|
||||
|
||||
// true if RightPanel is collapsed
|
||||
collapsedRhs: React.PropTypes.bool,
|
||||
|
||||
// Team token for the referral link. If falsy, the referral section will
|
||||
// not appear
|
||||
teamToken: React.PropTypes.string,
|
||||
|
@ -310,11 +314,6 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
onAvatarPickerClick: function(ev) {
|
||||
if (MatrixClientPeg.get().isGuest()) {
|
||||
dis.dispatch({action: 'view_set_mxid'});
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.refs.file_label) {
|
||||
this.refs.file_label.click();
|
||||
}
|
||||
|
@ -389,17 +388,14 @@ module.exports = React.createClass({
|
|||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: _t("Success"),
|
||||
description: _t("Your password was successfully changed. You will not receive push notifications on other devices until you log back in to them") + ".",
|
||||
description: _t(
|
||||
"Your password was successfully changed. You will not receive " +
|
||||
"push notifications on other devices until you log back in to them",
|
||||
) + ".",
|
||||
});
|
||||
dis.dispatch({action: 'password_changed'});
|
||||
},
|
||||
|
||||
onUpgradeClicked: function() {
|
||||
dis.dispatch({
|
||||
action: "start_upgrade_registration",
|
||||
});
|
||||
},
|
||||
|
||||
onEnableNotificationsChange: function(event) {
|
||||
UserSettingsStore.setEnableNotifications(event.target.checked);
|
||||
},
|
||||
|
@ -427,7 +423,10 @@ module.exports = React.createClass({
|
|||
this._addThreepid.addEmailAddress(emailAddress, true).done(() => {
|
||||
Modal.createDialog(QuestionDialog, {
|
||||
title: _t("Verification Pending"),
|
||||
description: _t("Please check your email and click on the link it contains. Once this is done, click continue."),
|
||||
description: _t(
|
||||
"Please check your email and click on the link it contains. Once this " +
|
||||
"is done, click continue.",
|
||||
),
|
||||
button: _t('Continue'),
|
||||
onFinished: this.onEmailDialogFinished,
|
||||
});
|
||||
|
@ -447,7 +446,7 @@ module.exports = React.createClass({
|
|||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
Modal.createDialog(QuestionDialog, {
|
||||
title: _t("Remove Contact Information?"),
|
||||
description: _t("Remove %(threePid)s?", { threePid : threepid.address }),
|
||||
description: _t("Remove %(threePid)s?", { threePid: threepid.address }),
|
||||
button: _t('Remove'),
|
||||
onFinished: (submit) => {
|
||||
if (submit) {
|
||||
|
@ -489,8 +488,8 @@ module.exports = React.createClass({
|
|||
this.setState({email_add_pending: false});
|
||||
if (err.errcode == 'M_THREEPID_AUTH_FAILED') {
|
||||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
let message = _t("Unable to verify email address.") + " " +
|
||||
_t("Please check your email and click on the link it contains. Once this is done, click continue.");
|
||||
const message = _t("Unable to verify email address.") + " " +
|
||||
_t("Please check your email and click on the link it contains. Once this is done, click continue.");
|
||||
Modal.createDialog(QuestionDialog, {
|
||||
title: _t("Verification Pending"),
|
||||
description: message,
|
||||
|
@ -608,7 +607,7 @@ module.exports = React.createClass({
|
|||
}
|
||||
},
|
||||
|
||||
_renderLanguageSetting: function () {
|
||||
_renderLanguageSetting: function() {
|
||||
const LanguageDropdown = sdk.getComponent('views.elements.LanguageDropdown');
|
||||
return <div>
|
||||
<label htmlFor="languageSelector">{_t('Interface Language')}</label>
|
||||
|
@ -639,7 +638,7 @@ module.exports = React.createClass({
|
|||
<input id="urlPreviewsDisabled"
|
||||
type="checkbox"
|
||||
defaultChecked={ UserSettingsStore.getUrlPreviewsDisabled() }
|
||||
onChange={ (e) => UserSettingsStore.setUrlPreviewsDisabled(e.target.checked) }
|
||||
onChange={ this._onPreviewsDisabledChanged }
|
||||
/>
|
||||
<label htmlFor="urlPreviewsDisabled">
|
||||
{ _t("Disable inline URL previews by default") }
|
||||
|
@ -647,17 +646,24 @@ module.exports = React.createClass({
|
|||
</div>;
|
||||
},
|
||||
|
||||
_onPreviewsDisabledChanged: function(e) {
|
||||
UserSettingsStore.setUrlPreviewsDisabled(e.target.checked);
|
||||
},
|
||||
|
||||
_renderSyncedSetting: function(setting) {
|
||||
// TODO: this ought to be a separate component so that we don't need
|
||||
// to rebind the onChange each time we render
|
||||
|
||||
const onChange = (e) => {
|
||||
UserSettingsStore.setSyncedSetting(setting.id, e.target.checked);
|
||||
if (setting.fn) setting.fn(e.target.checked);
|
||||
};
|
||||
|
||||
return <div className="mx_UserSettings_toggle" key={ setting.id }>
|
||||
<input id={ setting.id }
|
||||
type="checkbox"
|
||||
defaultChecked={ this._syncedSettings[setting.id] }
|
||||
onChange={
|
||||
(e) => {
|
||||
UserSettingsStore.setSyncedSetting(setting.id, e.target.checked);
|
||||
if (setting.fn) setting.fn(e.target.checked);
|
||||
}
|
||||
}
|
||||
onChange={ onChange }
|
||||
/>
|
||||
<label htmlFor={ setting.id }>
|
||||
{ _t(setting.label) }
|
||||
|
@ -666,22 +672,24 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
_renderThemeSelector: function(setting) {
|
||||
// TODO: this ought to be a separate component so that we don't need
|
||||
// to rebind the onChange each time we render
|
||||
const onChange = (e) => {
|
||||
if (e.target.checked) {
|
||||
UserSettingsStore.setSyncedSetting(setting.id, setting.value);
|
||||
}
|
||||
dis.dispatch({
|
||||
action: 'set_theme',
|
||||
value: setting.value,
|
||||
});
|
||||
};
|
||||
return <div className="mx_UserSettings_toggle" key={ setting.id + "_" + setting.value }>
|
||||
<input id={ setting.id + "_" + setting.value }
|
||||
type="radio"
|
||||
name={ setting.id }
|
||||
value={ setting.value }
|
||||
defaultChecked={ this._syncedSettings[setting.id] === setting.value }
|
||||
onChange={ (e) => {
|
||||
if (e.target.checked) {
|
||||
UserSettingsStore.setSyncedSetting(setting.id, setting.value);
|
||||
}
|
||||
dis.dispatch({
|
||||
action: 'set_theme',
|
||||
value: setting.value,
|
||||
});
|
||||
}
|
||||
}
|
||||
onChange={ onChange }
|
||||
/>
|
||||
<label htmlFor={ setting.id + "_" + setting.value }>
|
||||
{ setting.label }
|
||||
|
@ -720,8 +728,10 @@ module.exports = React.createClass({
|
|||
<h3>{ _t("Cryptography") }</h3>
|
||||
<div className="mx_UserSettings_section mx_UserSettings_cryptoSection">
|
||||
<ul>
|
||||
<li><label>{_t("Device ID:")}</label> <span><code>{deviceId}</code></span></li>
|
||||
<li><label>{_t("Device key:")}</label> <span><code><b>{identityKey}</b></code></span></li>
|
||||
<li><label>{_t("Device ID:")}</label>
|
||||
<span><code>{deviceId}</code></span></li>
|
||||
<li><label>{_t("Device key:")}</label>
|
||||
<span><code><b>{identityKey}</b></code></span></li>
|
||||
</ul>
|
||||
{ importExportButtons }
|
||||
</div>
|
||||
|
@ -733,16 +743,18 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
_renderLocalSetting: function(setting) {
|
||||
// TODO: this ought to be a separate component so that we don't need
|
||||
// to rebind the onChange each time we render
|
||||
const onChange = (e) => {
|
||||
UserSettingsStore.setLocalSetting(setting.id, e.target.checked);
|
||||
if (setting.fn) setting.fn(e.target.checked);
|
||||
};
|
||||
|
||||
return <div className="mx_UserSettings_toggle" key={ setting.id }>
|
||||
<input id={ setting.id }
|
||||
type="checkbox"
|
||||
defaultChecked={ this._localSettings[setting.id] }
|
||||
onChange={
|
||||
(e) => {
|
||||
UserSettingsStore.setLocalSetting(setting.id, e.target.checked);
|
||||
if (setting.fn) setting.fn(e.target.checked);
|
||||
}
|
||||
}
|
||||
onChange={ onChange }
|
||||
/>
|
||||
<label htmlFor={ setting.id }>
|
||||
{ _t(setting.label) }
|
||||
|
@ -794,26 +806,27 @@ module.exports = React.createClass({
|
|||
if (this.props.enableLabs === false) return null;
|
||||
UserSettingsStore.doTranslations();
|
||||
|
||||
const features = UserSettingsStore.LABS_FEATURES.map((feature) => (
|
||||
<div key={feature.id} className="mx_UserSettings_toggle">
|
||||
<input
|
||||
type="checkbox"
|
||||
id={feature.id}
|
||||
name={feature.id}
|
||||
defaultChecked={ UserSettingsStore.isFeatureEnabled(feature.id) }
|
||||
onChange={(e) => {
|
||||
if (MatrixClientPeg.get().isGuest()) {
|
||||
e.target.checked = false;
|
||||
dis.dispatch({action: 'view_set_mxid'});
|
||||
return;
|
||||
}
|
||||
const features = UserSettingsStore.LABS_FEATURES.map((feature) => {
|
||||
// TODO: this ought to be a separate component so that we don't need
|
||||
// to rebind the onChange each time we render
|
||||
const onChange = (e) => {
|
||||
UserSettingsStore.setFeatureEnabled(feature.id, e.target.checked);
|
||||
this.forceUpdate();
|
||||
};
|
||||
|
||||
UserSettingsStore.setFeatureEnabled(feature.id, e.target.checked);
|
||||
this.forceUpdate();
|
||||
}}/>
|
||||
<label htmlFor={feature.id}>{feature.name}</label>
|
||||
</div>
|
||||
));
|
||||
return (
|
||||
<div key={feature.id} className="mx_UserSettings_toggle">
|
||||
<input
|
||||
type="checkbox"
|
||||
id={feature.id}
|
||||
name={feature.id}
|
||||
defaultChecked={ UserSettingsStore.isFeatureEnabled(feature.id) }
|
||||
onChange={ onChange }
|
||||
/>
|
||||
<label htmlFor={feature.id}>{feature.name}</label>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
return (
|
||||
<div>
|
||||
<h3>{ _t("Labs") }</h3>
|
||||
|
@ -826,9 +839,6 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
_renderDeactivateAccount: function() {
|
||||
// We can't deactivate a guest account.
|
||||
if (MatrixClientPeg.get().isGuest()) return null;
|
||||
|
||||
return <div>
|
||||
<h3>{ _t("Deactivate Account") }</h3>
|
||||
<div className="mx_UserSettings_section">
|
||||
|
@ -880,9 +890,10 @@ module.exports = React.createClass({
|
|||
if (!this.state.rejectingInvites) {
|
||||
// bind() the invited rooms so any new invites that may come in as this button is clicked
|
||||
// don't inadvertently get rejected as well.
|
||||
const onClick = this._onRejectAllInvitesClicked.bind(this, invitedRooms);
|
||||
reject = (
|
||||
<AccessibleButton className="mx_UserSettings_button danger"
|
||||
onClick={this._onRejectAllInvitesClicked.bind(this, invitedRooms)}>
|
||||
onClick={onClick}>
|
||||
{_t("Reject all %(invitedRooms)s invites", {invitedRooms: invitedRooms.length})}
|
||||
</AccessibleButton>
|
||||
);
|
||||
|
@ -900,8 +911,6 @@ module.exports = React.createClass({
|
|||
const settings = this.state.electron_settings;
|
||||
if (!settings) return;
|
||||
|
||||
const {ipcRenderer} = require('electron');
|
||||
|
||||
return <div>
|
||||
<h3>{ _t('Desktop specific') }</h3>
|
||||
<div className="mx_UserSettings_section">
|
||||
|
@ -909,9 +918,7 @@ module.exports = React.createClass({
|
|||
<input type="checkbox"
|
||||
name="auto-launch"
|
||||
defaultChecked={settings['auto-launch']}
|
||||
onChange={(e) => {
|
||||
ipcRenderer.send('settings_set', 'auto-launch', e.target.checked);
|
||||
}}
|
||||
onChange={this._onAutoLaunchChanged}
|
||||
/>
|
||||
<label htmlFor="auto-launch">{_t('Start automatically after system login')}</label>
|
||||
</div>
|
||||
|
@ -919,6 +926,11 @@ module.exports = React.createClass({
|
|||
</div>;
|
||||
},
|
||||
|
||||
_onAutoLaunchChanged: function(e) {
|
||||
const {ipcRenderer} = require('electron');
|
||||
ipcRenderer.send('settings_set', 'auto-launch', e.target.checked);
|
||||
},
|
||||
|
||||
_mapWebRtcDevicesToSpans: function(devices) {
|
||||
return devices.map((device) => <span key={device.deviceId}>{device.label}</span>);
|
||||
},
|
||||
|
@ -952,16 +964,13 @@ module.exports = React.createClass({
|
|||
}
|
||||
},
|
||||
|
||||
_renderWebRtcSettings: function() {
|
||||
_renderWebRtcDeviceSettings: function() {
|
||||
if (this.state.mediaDevices === false) {
|
||||
return <div>
|
||||
<h3>{_t('VoIP')}</h3>
|
||||
<div className="mx_UserSettings_section">
|
||||
<p className="mx_UserSettings_link" onClick={this._requestMediaPermissions}>
|
||||
{_t('Missing Media Permissions, click here to request.')}
|
||||
</p>
|
||||
</div>
|
||||
</div>;
|
||||
return (
|
||||
<p className="mx_UserSettings_link" onClick={this._requestMediaPermissions}>
|
||||
{_t('Missing Media Permissions, click here to request.')}
|
||||
</p>
|
||||
);
|
||||
} else if (!this.state.mediaDevices) return;
|
||||
|
||||
const Dropdown = sdk.getComponent('elements.Dropdown');
|
||||
|
@ -1015,10 +1024,17 @@ module.exports = React.createClass({
|
|||
}
|
||||
|
||||
return <div>
|
||||
<h3>{_t('VoIP')}</h3>
|
||||
<div className="mx_UserSettings_section">
|
||||
{microphoneDropdown}
|
||||
{webcamDropdown}
|
||||
</div>;
|
||||
},
|
||||
|
||||
_renderWebRtcSettings: function() {
|
||||
return <div>
|
||||
<h3>{_t('VoIP')}</h3>
|
||||
<div className="mx_UserSettings_section">
|
||||
{ WEBRTC_SETTINGS_LABELS.map(this._renderLocalSetting) }
|
||||
{ this._renderWebRtcDeviceSettings() }
|
||||
</div>
|
||||
</div>;
|
||||
},
|
||||
|
@ -1075,6 +1091,9 @@ module.exports = React.createClass({
|
|||
|
||||
const threepidsSection = this.state.threepids.map((val, pidIndex) => {
|
||||
const id = "3pid-" + val.address;
|
||||
// TODO; make a separate component to avoid having to rebind onClick
|
||||
// each time we render
|
||||
const onRemoveClick = (e) => this.onRemoveThreepidClicked(val);
|
||||
return (
|
||||
<div className="mx_UserSettings_profileTableRow" key={pidIndex}>
|
||||
<div className="mx_UserSettings_profileLabelCell">
|
||||
|
@ -1086,7 +1105,8 @@ module.exports = React.createClass({
|
|||
/>
|
||||
</div>
|
||||
<div className="mx_UserSettings_threepidButton mx_filterFlipColor">
|
||||
<img src="img/cancel-small.svg" width="14" height="14" alt={ _t("Remove") } onClick={this.onRemoveThreepidClicked.bind(this, val)} />
|
||||
<img src="img/cancel-small.svg" width="14" height="14" alt={ _t("Remove") }
|
||||
onClick={onRemoveClick} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -1094,7 +1114,7 @@ module.exports = React.createClass({
|
|||
let addEmailSection;
|
||||
if (this.state.email_add_pending) {
|
||||
addEmailSection = <Loader key="_email_add_spinner" />;
|
||||
} else if (!MatrixClientPeg.get().isGuest()) {
|
||||
} else {
|
||||
addEmailSection = (
|
||||
<div className="mx_UserSettings_profileTableRow" key="_newEmail">
|
||||
<div className="mx_UserSettings_profileLabelCell">
|
||||
|
@ -1122,16 +1142,7 @@ module.exports = React.createClass({
|
|||
threepidsSection.push(addEmailSection);
|
||||
threepidsSection.push(addMsisdnSection);
|
||||
|
||||
let accountJsx;
|
||||
|
||||
if (MatrixClientPeg.get().isGuest()) {
|
||||
accountJsx = (
|
||||
<div className="mx_UserSettings_button" onClick={this.onUpgradeClicked}>
|
||||
{ _t("Create an account") }
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
accountJsx = (
|
||||
const accountJsx = (
|
||||
<ChangePassword
|
||||
className="mx_UserSettings_accountTable"
|
||||
rowClassName="mx_UserSettings_profileTableRow"
|
||||
|
@ -1140,10 +1151,10 @@ module.exports = React.createClass({
|
|||
buttonClassName="mx_UserSettings_button mx_UserSettings_changePasswordButton"
|
||||
onError={this.onPasswordChangeError}
|
||||
onFinished={this.onPasswordChanged} />
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
let notificationArea;
|
||||
if (!MatrixClientPeg.get().isGuest() && this.state.threepids !== undefined) {
|
||||
if (this.state.threepids !== undefined) {
|
||||
notificationArea = (<div>
|
||||
<h3>{ _t("Notifications") }</h3>
|
||||
|
||||
|
@ -1165,7 +1176,6 @@ module.exports = React.createClass({
|
|||
<div className="mx_UserSettings">
|
||||
<SimpleRoomHeader
|
||||
title={ _t("Settings") }
|
||||
collapsedRhs={ this.props.collapsedRhs }
|
||||
onCancelClick={ this.props.onClose }
|
||||
/>
|
||||
|
||||
|
@ -1237,7 +1247,12 @@ module.exports = React.createClass({
|
|||
{ _t("Logged in as:") } {this._me}
|
||||
</div>
|
||||
<div className="mx_UserSettings_advanced">
|
||||
{_t('Access Token:')} <span className="mx_UserSettings_advanced_spoiler" onClick={this._showSpoiler} data-spoiler={ MatrixClientPeg.get().getAccessToken() }><{ _t("click to reveal") }></span>
|
||||
{_t('Access Token:')}
|
||||
<span className="mx_UserSettings_advanced_spoiler"
|
||||
onClick={this._showSpoiler}
|
||||
data-spoiler={ MatrixClientPeg.get().getAccessToken() }>
|
||||
<{ _t("click to reveal") }>
|
||||
</span>
|
||||
</div>
|
||||
<div className="mx_UserSettings_advanced">
|
||||
{ _t("Homeserver is") } { MatrixClientPeg.get().getHomeserverUrl() }
|
||||
|
|
|
@ -45,8 +45,6 @@ module.exports = React.createClass({
|
|||
brand: React.PropTypes.string,
|
||||
email: React.PropTypes.string,
|
||||
referrer: React.PropTypes.string,
|
||||
username: React.PropTypes.string,
|
||||
guestAccessToken: React.PropTypes.string,
|
||||
teamServerConfig: React.PropTypes.shape({
|
||||
// Email address to request new teams
|
||||
supportEmail: React.PropTypes.string.isRequired,
|
||||
|
@ -220,29 +218,29 @@ module.exports = React.createClass({
|
|||
}
|
||||
|
||||
trackPromise.then((teamToken) => {
|
||||
this.props.onLoggedIn({
|
||||
return this.props.onLoggedIn({
|
||||
userId: response.user_id,
|
||||
deviceId: response.device_id,
|
||||
homeserverUrl: this._matrixClient.getHomeserverUrl(),
|
||||
identityServerUrl: this._matrixClient.getIdentityServerUrl(),
|
||||
accessToken: response.access_token
|
||||
}, teamToken);
|
||||
}).then(() => {
|
||||
return this._setupPushers();
|
||||
}).then((cli) => {
|
||||
return this._setupPushers(cli);
|
||||
});
|
||||
},
|
||||
|
||||
_setupPushers: function() {
|
||||
_setupPushers: function(matrixClient) {
|
||||
if (!this.props.brand) {
|
||||
return q();
|
||||
}
|
||||
return MatrixClientPeg.get().getPushers().then((resp)=>{
|
||||
return matrixClient.getPushers().then((resp)=>{
|
||||
const pushers = resp.pushers;
|
||||
for (let i = 0; i < pushers.length; ++i) {
|
||||
if (pushers[i].kind == 'email') {
|
||||
const emailPusher = pushers[i];
|
||||
emailPusher.data = { brand: this.props.brand };
|
||||
MatrixClientPeg.get().setPusher(emailPusher).done(() => {
|
||||
matrixClient.setPusher(emailPusher).done(() => {
|
||||
console.log("Set email branding to " + this.props.brand);
|
||||
}, (error) => {
|
||||
console.error("Couldn't set email branding: " + error);
|
||||
|
@ -295,17 +293,6 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
_makeRegisterRequest: function(auth) {
|
||||
let guestAccessToken = this.props.guestAccessToken;
|
||||
|
||||
if (
|
||||
this.state.formVals.username !== this.props.username ||
|
||||
this.state.hsUrl != this.props.defaultHsUrl
|
||||
) {
|
||||
// don't try to upgrade if we changed our username
|
||||
// or are registering on a different HS
|
||||
guestAccessToken = null;
|
||||
}
|
||||
|
||||
// Only send the bind params if we're sending username / pw params
|
||||
// (Since we need to send no params at all to use the ones saved in the
|
||||
// session).
|
||||
|
@ -320,7 +307,7 @@ module.exports = React.createClass({
|
|||
undefined, // session id: included in the auth dict already
|
||||
auth,
|
||||
bindThreepids,
|
||||
guestAccessToken,
|
||||
null,
|
||||
);
|
||||
},
|
||||
|
||||
|
@ -357,10 +344,6 @@ module.exports = React.createClass({
|
|||
} else if (this.state.busy || this.state.teamServerBusy) {
|
||||
registerBody = <Spinner />;
|
||||
} else {
|
||||
let guestUsername = this.props.username;
|
||||
if (this.state.hsUrl != this.props.defaultHsUrl) {
|
||||
guestUsername = null;
|
||||
}
|
||||
let errorSection;
|
||||
if (this.state.errorText) {
|
||||
errorSection = <div className="mx_Login_error">{this.state.errorText}</div>;
|
||||
|
@ -374,7 +357,6 @@ module.exports = React.createClass({
|
|||
defaultPhoneNumber={this.state.formVals.phoneNumber}
|
||||
defaultPassword={this.state.formVals.password}
|
||||
teamsConfig={this.state.teamsConfig}
|
||||
guestUsername={guestUsername}
|
||||
minPasswordLength={MIN_PASSWORD_LENGTH}
|
||||
onError={this.onFormValidationFailed}
|
||||
onRegisterClick={this.onFormSubmit}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue