Merge remote-tracking branch 'remotes/origin/develop' into set_default_federate_by_settings

# Conflicts:
#	src/components/structures/MatrixChat.js
This commit is contained in:
Michael Telatynski 2017-08-17 13:39:19 +01:00
commit bdb2d6b475
No known key found for this signature in database
GPG key ID: 3F879DA5AD802A5E
93 changed files with 5888 additions and 1285 deletions

View file

@ -266,7 +266,7 @@ export default React.createClass({
this.setState({uploadingAvatar: false});
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Failed to upload avatar image", e);
Modal.createDialog(ErrorDialog, {
Modal.createTrackedDialog('Failed to upload image', '', ErrorDialog, {
title: _t('Error'),
description: _t('Failed to upload image'),
});
@ -288,7 +288,7 @@ export default React.createClass({
});
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Failed to save group profile", e);
Modal.createDialog(ErrorDialog, {
Modal.createTrackedDialog('Failed to update group', '', ErrorDialog, {
title: _t('Error'),
description: _t('Failed to update group'),
});

View file

@ -301,13 +301,13 @@ export default React.createClass({
case PageTypes.UserView:
page_element = null; // deliberately null for now
right_panel = <RightPanel userId={this.props.viewUserId} opacity={this.props.rightOpacity} />;
right_panel = <RightPanel opacity={this.props.rightOpacity} />;
break;
case PageTypes.GroupView:
page_element = <GroupView
groupId={this.props.currentGroupId}
/>;
//right_panel = <RightPanel userId={this.props.viewUserId} opacity={this.props.rightOpacity} />;
//right_panel = <RightPanel opacity={this.props.rightOpacity} />;
break;
}

View file

@ -1,6 +1,7 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Copyright 2017 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -31,6 +32,7 @@ import dis from "../../dispatcher";
import Modal from "../../Modal";
import Tinter from "../../Tinter";
import sdk from '../../index';
import { showStartChatInviteDialog, showRoomInviteDialog } from '../../Invite';
import * as Rooms from '../../Rooms';
import linkifyMatrix from "../../linkify-matrix";
import * as Lifecycle from '../../Lifecycle';
@ -131,9 +133,6 @@ module.exports = React.createClass({
// the master view we are showing.
view: VIEWS.LOADING,
// a thing to call showScreen with once login completes.
screenAfterLogin: this.props.initialScreenAfterLogin,
// What the LoggedInView would be showing if visible
page_type: null,
@ -147,8 +146,6 @@ module.exports = React.createClass({
collapse_lhs: false,
collapse_rhs: false,
ready: false,
width: 10000,
leftOpacity: 1.0,
middleOpacity: 1.0,
rightOpacity: 1.0,
@ -274,6 +271,15 @@ module.exports = React.createClass({
register_hs_url: paramHs,
});
}
// a thing to call showScreen with once login completes. this is kept
// outside this.state because updating it should never trigger a
// rerender.
this._screenAfterLogin = this.props.initialScreenAfterLogin;
this._windowWidth = 10000;
this.handleResize();
window.addEventListener('resize', this.handleResize);
},
componentDidMount: function() {
@ -294,9 +300,6 @@ module.exports = React.createClass({
linkifyMatrix.onGroupClick = this.onGroupClick;
}
window.addEventListener('resize', this.handleResize);
this.handleResize();
const teamServerConfig = this.props.config.teamServerConfig || {};
Lifecycle.initRtsClient(teamServerConfig.teamServerURL);
@ -312,13 +315,12 @@ module.exports = React.createClass({
// 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;
const firstScreen = this._screenAfterLogin ?
this._screenAfterLogin.screen : null;
if (firstScreen === 'login' ||
firstScreen === 'register' ||
firstScreen === 'forgot_password') {
this.setState({loading: false});
this._showScreenAfterLogin();
return;
}
@ -367,9 +369,9 @@ module.exports = React.createClass({
}
const newState = {
viewUserId: null,
};
Object.assign(newState, state);
this.setState(newState);
};
Object.assign(newState, state);
this.setState(newState);
},
onAction: function(payload) {
@ -410,7 +412,7 @@ module.exports = React.createClass({
this._leaveRoom(payload.room_id);
break;
case 'reject_invite':
Modal.createDialog(QuestionDialog, {
Modal.createTrackedDialog('Reject invitation', '', QuestionDialog, {
title: _t('Reject invitation'),
description: _t('Are you sure you want to reject the invitation?'),
onFinished: (confirm) => {
@ -426,7 +428,7 @@ module.exports = React.createClass({
}
}, (err) => {
modal.close();
Modal.createDialog(ErrorDialog, {
Modal.createTrackedDialog('Failed to reject invitation', '', ErrorDialog, {
title: _t('Failed to reject invitation'),
description: err.toString(),
});
@ -512,7 +514,7 @@ module.exports = React.createClass({
this._createChat();
break;
case 'view_invite':
this._invite(payload.roomId);
showRoomInviteDialog(payload.roomId);
break;
case 'notifier_enabled':
this.forceUpdate();
@ -728,7 +730,7 @@ module.exports = React.createClass({
_setMxId: function(payload) {
const SetMxIdDialog = sdk.getComponent('views.dialogs.SetMxIdDialog');
const close = Modal.createDialog(SetMxIdDialog, {
const close = Modal.createTrackedDialog('Set MXID', '', SetMxIdDialog, {
homeserverUrl: MatrixClientPeg.get().getHomeserverUrl(),
onFinished: (submitted, credentials) => {
if (!submitted) {
@ -766,13 +768,7 @@ module.exports = React.createClass({
dis.dispatch({action: 'view_set_mxid'});
return;
}
const ChatInviteDialog = sdk.getComponent("dialogs.ChatInviteDialog");
Modal.createDialog(ChatInviteDialog, {
title: _t('Start a chat'),
description: _t("Who would you like to communicate with?"),
placeholder: _t("Email, name or matrix ID"),
button: _t("Start Chat"),
});
showStartChatInviteDialog();
},
_createRoom: function() {
@ -787,7 +783,7 @@ module.exports = React.createClass({
return;
}
const CreateRoomDialog = sdk.getComponent('dialogs.CreateRoomDialog');
Modal.createDialog(CreateRoomDialog, {
Modal.createTrackedDialog('Create Room', '', CreateRoomDialog, {
onFinished: (shouldCreate, name, noFederate) => {
if (shouldCreate) {
const createOpts = {};
@ -829,7 +825,7 @@ module.exports = React.createClass({
return;
}
const close = Modal.createDialog(ChatCreateOrReuseDialog, {
const close = Modal.createTrackedDialog('Chat create or reuse', '', ChatCreateOrReuseDialog, {
userId: userId,
onFinished: (success) => {
if (!success && goHomeOnCancel) {
@ -855,23 +851,12 @@ module.exports = React.createClass({
}).close;
},
_invite: function(roomId) {
const ChatInviteDialog = sdk.getComponent("dialogs.ChatInviteDialog");
Modal.createDialog(ChatInviteDialog, {
title: _t('Invite new room members'),
description: _t('Who would you like to add to this room?'),
button: _t('Send Invites'),
placeholder: _t("Email, name or matrix ID"),
roomId: roomId,
});
},
_leaveRoom: function(roomId) {
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
const roomToLeave = MatrixClientPeg.get().getRoom(roomId);
Modal.createDialog(QuestionDialog, {
Modal.createTrackedDialog('Leave room', '', QuestionDialog, {
title: _t("Leave room"),
description: (
<span>
@ -894,7 +879,7 @@ module.exports = React.createClass({
}, (err) => {
modal.close();
console.error("Failed to leave room " + roomId + " " + err);
Modal.createDialog(ErrorDialog, {
Modal.createTrackedDialog('Failed to leave room', '', ErrorDialog, {
title: _t("Failed to leave room"),
description: (err && err.message ? err.message :
_t("Server may be unavailable, overloaded, or you hit a bug.")),
@ -990,14 +975,12 @@ module.exports = React.createClass({
_showScreenAfterLogin: function() {
// If screenAfterLogin is set, use that, then null it so that a second login will
// result in view_home_page, _user_settings or _room_directory
if (this.state.screenAfterLogin && this.state.screenAfterLogin.screen) {
if (this._screenAfterLogin && this._screenAfterLogin.screen) {
this.showScreen(
this.state.screenAfterLogin.screen,
this.state.screenAfterLogin.params,
this._screenAfterLogin.screen,
this._screenAfterLogin.params,
);
// XXX: is this necessary? `showScreen` should do it for us.
this.notifyNewScreen(this.state.screenAfterLogin.screen);
this.setState({screenAfterLogin: null});
this._screenAfterLogin = null;
} else if (localStorage && localStorage.getItem('mx_last_room_id')) {
// Before defaulting to directory, show the last viewed room
dis.dispatch({
@ -1090,7 +1073,7 @@ module.exports = React.createClass({
});
cli.on('Session.logged_out', function(call) {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
Modal.createTrackedDialog('Signed out', '', ErrorDialog, {
title: _t('Signed Out'),
description: _t('For security, this session has been signed out. Please sign in again.'),
});
@ -1203,21 +1186,24 @@ module.exports = React.createClass({
} else if (screen.indexOf('user/') == 0) {
const userId = screen.substring(5);
if (params.action === 'chat') {
this._chatCreateOrReuse(userId);
return;
}
// Wait for the first sync so that `getRoom` gives us a room object if it's
// in the sync response
const waitFor = this.firstSyncPromise ?
this.firstSyncPromise.promise : Promise.resolve();
waitFor.then(() => {
if (params.action === 'chat') {
this._chatCreateOrReuse(userId);
return;
}
this.setState({ viewUserId: userId });
this._setPage(PageTypes.UserView);
this.notifyNewScreen('user/' + userId);
const member = new Matrix.RoomMember(null, userId);
if (member) {
this._setPage(PageTypes.UserView);
this.notifyNewScreen('user/' + userId);
const member = new Matrix.RoomMember(null, userId);
dis.dispatch({
action: 'view_user',
member: member,
});
}
});
} else if (screen.indexOf('group/') == 0) {
const groupId = screen.substring(6);
@ -1274,20 +1260,20 @@ module.exports = React.createClass({
const hideRhsThreshold = 820;
const showRhsThreshold = 820;
if (this.state.width > hideLhsThreshold && window.innerWidth <= hideLhsThreshold) {
if (this._windowWidth > hideLhsThreshold && window.innerWidth <= hideLhsThreshold) {
dis.dispatch({ action: 'hide_left_panel' });
}
if (this.state.width <= showLhsThreshold && window.innerWidth > showLhsThreshold) {
if (this._windowWidth <= showLhsThreshold && window.innerWidth > showLhsThreshold) {
dis.dispatch({ action: 'show_left_panel' });
}
if (this.state.width > hideRhsThreshold && window.innerWidth <= hideRhsThreshold) {
if (this._windowWidth > hideRhsThreshold && window.innerWidth <= hideRhsThreshold) {
dis.dispatch({ action: 'hide_right_panel' });
}
if (this.state.width <= showRhsThreshold && window.innerWidth > showRhsThreshold) {
if (this._windowWidth <= showRhsThreshold && window.innerWidth > showRhsThreshold) {
dis.dispatch({ action: 'show_right_panel' });
}
this.setState({width: window.innerWidth});
this._windowWidth = window.innerWidth;
},
onRoomCreated: function(roomId) {

View file

@ -307,13 +307,13 @@ module.exports = React.createClass({
for (i = 0; i < this.props.events.length; i++) {
let mxEv = this.props.events[i];
let eventId = mxEv.getId();
let readMarkerInMels = false;
let last = (mxEv === lastShownEvent);
const wantTile = this._shouldShowEvent(mxEv);
// Wrap consecutive member events in a ListSummary, ignore if redacted
if (isMembershipChange(mxEv) && wantTile) {
let readMarkerInMels = false;
let ts1 = mxEv.getTs();
// Ensure that the key of the MemberEventListSummary does not change with new
// member events. This will prevent it from being re-created unnecessarily, and
@ -330,6 +330,11 @@ module.exports = React.createClass({
ret.push(dateSeparator);
}
// If RM event is the first in the MELS, append the RM after MELS
if (mxEv.getId() === this.props.readMarkerEventId) {
readMarkerInMels = true;
}
let summarisedEvents = [mxEv];
for (;i + 1 < this.props.events.length; i++) {
const collapsedMxEv = this.props.events[i + 1];

View file

@ -63,7 +63,7 @@ export default withMatrixClient(React.createClass({
_onCreateGroupClick: function() {
const CreateGroupDialog = sdk.getComponent("dialogs.CreateGroupDialog");
Modal.createDialog(CreateGroupDialog);
Modal.createTrackedDialog('Create Group', '', CreateGroupDialog);
},
_fetch: function() {

View file

@ -544,7 +544,7 @@ module.exports = React.createClass({
}
if (!userHasUsedEncryption) {
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createDialog(QuestionDialog, {
Modal.createTrackedDialog('E2E Warning', '', QuestionDialog, {
title: _t("Warning!"),
hasCancelButton: false,
description: (
@ -820,7 +820,7 @@ module.exports = React.createClass({
});
const SetMxIdDialog = sdk.getComponent('views.dialogs.SetMxIdDialog');
const close = Modal.createDialog(SetMxIdDialog, {
const close = Modal.createTrackedDialog('Set MXID', '', SetMxIdDialog, {
homeserverUrl: cli.getHomeserverUrl(),
onFinished: (submitted, credentials) => {
if (submitted) {
@ -934,7 +934,7 @@ module.exports = React.createClass({
}
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Failed to upload file " + file + " " + error);
Modal.createDialog(ErrorDialog, {
Modal.createTrackedDialog('Failed to upload file', '', ErrorDialog, {
title: _t('Failed to upload file'),
description: ((error && error.message) ? error.message : _t("Server may be unavailable, overloaded, or the file too big")),
});
@ -1021,7 +1021,7 @@ module.exports = React.createClass({
}, function(error) {
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Search failed: " + error);
Modal.createDialog(ErrorDialog, {
Modal.createTrackedDialog('Search failed', '', ErrorDialog, {
title: _t("Search failed"),
description: ((error && error.message) ? error.message : _t("Server may be unavailable, overloaded, or search timed out :(")),
});
@ -1148,7 +1148,7 @@ module.exports = React.createClass({
console.error(result.reason);
});
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
Modal.createTrackedDialog('Failed to save room settings', '', ErrorDialog, {
title: _t("Failed to save settings"),
description: fails.map(function(result) { return result.reason; }).join("\n"),
});
@ -1195,7 +1195,7 @@ module.exports = React.createClass({
}, function(err) {
var errCode = err.errcode || _t("unknown error code");
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
Modal.createTrackedDialog('Failed to forget room', '', ErrorDialog, {
title: _t("Error"),
description: _t("Failed to forget room %(errCode)s", { errCode: errCode }),
});
@ -1217,7 +1217,7 @@ module.exports = React.createClass({
var msg = error.message ? error.message : JSON.stringify(error);
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
Modal.createTrackedDialog('Failed to reject invite', '', ErrorDialog, {
title: _t("Failed to reject invite"),
description: msg,
});

View file

@ -923,7 +923,7 @@ var TimelinePanel = React.createClass({
var message = (error.errcode == 'M_FORBIDDEN')
? _t("Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.")
: _t("Tried to load a specific point in this room's timeline, but was unable to find it.");
Modal.createDialog(ErrorDialog, {
Modal.createTrackedDialog('Failed to load timeline position', '', ErrorDialog, {
title: _t("Failed to load timeline position"),
description: message,
onFinished: onFinished,

View file

@ -85,6 +85,10 @@ const SETTINGS_LABELS = [
id: 'hideJoinLeaves',
label: 'Hide join/leave messages (invites/kicks/bans unaffected)',
},
{
id: 'hideAvatarDisplaynameChanges',
label: 'Hide avatar and display name changes',
},
{
id: 'useCompactLayout',
label: 'Use compact timeline layout',
@ -101,6 +105,10 @@ const SETTINGS_LABELS = [
id: 'MessageComposerInput.autoReplaceEmoji',
label: 'Automatically replace plain text Emoji',
},
{
id: 'Pill.shouldHidePillAvatar',
label: 'Hide avatars in user and room mentions',
},
/*
{
id: 'useFixedWidthFont',
@ -331,7 +339,7 @@ module.exports = React.createClass({
}, function(error) {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Failed to load user settings: " + error);
Modal.createDialog(ErrorDialog, {
Modal.createTrackedDialog('Can\'t load user settings', '', ErrorDialog, {
title: _t("Can't load user settings"),
description: ((error && error.message) ? error.message : _t("Server may be unavailable or overloaded")),
});
@ -364,7 +372,7 @@ module.exports = React.createClass({
// const errMsg = (typeof err === "string") ? err : (err.error || "");
console.error("Failed to set avatar: " + err);
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
Modal.createTrackedDialog('Failed to set avatar', '', ErrorDialog, {
title: _t("Failed to set avatar."),
description: ((err && err.message) ? err.message : _t("Operation failed")),
});
@ -373,7 +381,7 @@ module.exports = React.createClass({
onLogoutClicked: function(ev) {
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createDialog(QuestionDialog, {
Modal.createTrackedDialog('Logout E2E Export', '', QuestionDialog, {
title: _t("Sign out"),
description:
<div>
@ -409,7 +417,7 @@ module.exports = React.createClass({
}
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Failed to change password: " + errMsg);
Modal.createDialog(ErrorDialog, {
Modal.createTrackedDialog('Failed to change password', '', ErrorDialog, {
title: _t("Error"),
description: errMsg,
});
@ -417,7 +425,7 @@ module.exports = React.createClass({
onPasswordChanged: function() {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
Modal.createTrackedDialog('Password changed', '', ErrorDialog, {
title: _t("Success"),
description: _t(
"Your password was successfully changed. You will not receive " +
@ -442,7 +450,7 @@ module.exports = React.createClass({
const emailAddress = this.refs.add_email_input.value;
if (!Email.looksValid(emailAddress)) {
Modal.createDialog(ErrorDialog, {
Modal.createTrackedDialog('Invalid email address', '', ErrorDialog, {
title: _t("Invalid Email Address"),
description: _t("This doesn't appear to be a valid email address"),
});
@ -452,7 +460,7 @@ module.exports = React.createClass({
// we always bind emails when registering, so let's do the
// same here.
this._addThreepid.addEmailAddress(emailAddress, true).done(() => {
Modal.createDialog(QuestionDialog, {
Modal.createTrackedDialog('Verification Pending', '', QuestionDialog, {
title: _t("Verification Pending"),
description: _t(
"Please check your email and click on the link it contains. Once this " +
@ -464,7 +472,7 @@ module.exports = React.createClass({
}, (err) => {
this.setState({email_add_pending: false});
console.error("Unable to add email address " + emailAddress + " " + err);
Modal.createDialog(ErrorDialog, {
Modal.createTrackedDialog('Unable to add email address', '', ErrorDialog, {
title: _t("Unable to add email address"),
description: ((err && err.message) ? err.message : _t("Operation failed")),
});
@ -475,7 +483,7 @@ module.exports = React.createClass({
onRemoveThreepidClicked: function(threepid) {
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createDialog(QuestionDialog, {
Modal.createTrackedDialog('Remove 3pid', '', QuestionDialog, {
title: _t("Remove Contact Information?"),
description: _t("Remove %(threePid)s?", { threePid: threepid.address }),
button: _t('Remove'),
@ -489,7 +497,7 @@ module.exports = React.createClass({
}).catch((err) => {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Unable to remove contact information: " + err);
Modal.createDialog(ErrorDialog, {
Modal.createTrackedDialog('Remove 3pid failed', '', ErrorDialog, {
title: _t("Unable to remove contact information"),
description: ((err && err.message) ? err.message : _t("Operation failed")),
});
@ -521,7 +529,7 @@ module.exports = React.createClass({
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
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, {
Modal.createTrackedDialog('Verification Pending', '', QuestionDialog, {
title: _t("Verification Pending"),
description: message,
button: _t('Continue'),
@ -530,7 +538,7 @@ module.exports = React.createClass({
} else {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Unable to verify email address: " + err);
Modal.createDialog(ErrorDialog, {
Modal.createTrackedDialog('Unable to verify email address', '', ErrorDialog, {
title: _t("Unable to verify email address."),
description: ((err && err.message) ? err.message : _t("Operation failed")),
});
@ -540,7 +548,7 @@ module.exports = React.createClass({
_onDeactivateAccountClicked: function() {
const DeactivateAccountDialog = sdk.getComponent("dialogs.DeactivateAccountDialog");
Modal.createDialog(DeactivateAccountDialog, {});
Modal.createTrackedDialog('Deactivate Account', '', DeactivateAccountDialog, {});
},
_onBugReportClicked: function() {
@ -548,7 +556,7 @@ module.exports = React.createClass({
if (!BugReportDialog) {
return;
}
Modal.createDialog(BugReportDialog, {});
Modal.createTrackedDialog('Bug Report Dialog', '', BugReportDialog, {});
},
_onClearCacheClicked: function() {
@ -585,27 +593,23 @@ module.exports = React.createClass({
},
_onExportE2eKeysClicked: function() {
Modal.createDialogAsync(
(cb) => {
require.ensure(['../../async-components/views/dialogs/ExportE2eKeysDialog'], () => {
cb(require('../../async-components/views/dialogs/ExportE2eKeysDialog'));
}, "e2e-export");
}, {
matrixClient: MatrixClientPeg.get(),
},
);
Modal.createTrackedDialogAsync('Export E2E Keys', '', (cb) => {
require.ensure(['../../async-components/views/dialogs/ExportE2eKeysDialog'], () => {
cb(require('../../async-components/views/dialogs/ExportE2eKeysDialog'));
}, "e2e-export");
}, {
matrixClient: MatrixClientPeg.get(),
});
},
_onImportE2eKeysClicked: function() {
Modal.createDialogAsync(
(cb) => {
require.ensure(['../../async-components/views/dialogs/ImportE2eKeysDialog'], () => {
cb(require('../../async-components/views/dialogs/ImportE2eKeysDialog'));
}, "e2e-export");
}, {
matrixClient: MatrixClientPeg.get(),
},
);
Modal.createTrackedDialogAsync('Import E2E Keys', '', (cb) => {
require.ensure(['../../async-components/views/dialogs/ImportE2eKeysDialog'], () => {
cb(require('../../async-components/views/dialogs/ImportE2eKeysDialog'));
}, "e2e-export");
}, {
matrixClient: MatrixClientPeg.get(),
});
},
_renderReferral: function() {
@ -855,7 +859,13 @@ module.exports = React.createClass({
if (this.props.enableLabs === false) return null;
UserSettingsStore.doTranslations();
const features = UserSettingsStore.LABS_FEATURES.map((feature) => {
const features = [];
UserSettingsStore.LABS_FEATURES.forEach((feature) => {
// This feature has an override and will be set to the default, so do not
// show it here.
if (feature.override) {
return;
}
// 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) => {
@ -863,7 +873,7 @@ module.exports = React.createClass({
this.forceUpdate();
};
return (
features.push(
<div key={feature.id} className="mx_UserSettings_toggle">
<input
type="checkbox"
@ -873,9 +883,14 @@ module.exports = React.createClass({
onChange={ onChange }
/>
<label htmlFor={feature.id}>{feature.name}</label>
</div>
);
</div>);
});
// No labs section when there are no features in labs
if (features.length === 0) {
return null;
}
return (
<div>
<h3>{ _t("Labs") }</h3>
@ -1004,7 +1019,7 @@ module.exports = React.createClass({
this._refreshMediaDevices,
function() {
const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog');
Modal.createDialog(ErrorDialog, {
Modal.createTrackedDialog('No media permissions', '', ErrorDialog, {
title: _t('No media permissions'),
description: _t('You may need to manually permit Riot to access your microphone/webcam'),
});

View file

@ -89,14 +89,14 @@ module.exports = React.createClass({
}
else {
var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createDialog(QuestionDialog, {
Modal.createTrackedDialog('Forgot Password Warning', '', QuestionDialog, {
title: _t('Warning!'),
description:
<div>
{ _t(
'Resetting password will currently reset any ' +
'end-to-end encryption keys on all devices, ' +
'making encrypted chat history unreadable, ' +
'making encrypted chat history unreadable, ' +
'unless you first export your room keys and re-import ' +
'them afterwards. In future this will be improved.'
) }
@ -121,15 +121,13 @@ module.exports = React.createClass({
},
_onExportE2eKeysClicked: function() {
Modal.createDialogAsync(
(cb) => {
require.ensure(['../../../async-components/views/dialogs/ExportE2eKeysDialog'], () => {
cb(require('../../../async-components/views/dialogs/ExportE2eKeysDialog'));
}, "e2e-export");
}, {
matrixClient: MatrixClientPeg.get(),
}
);
Modal.createTrackedDialogAsync('Export E2E Keys', 'Forgot Password', (cb) => {
require.ensure(['../../../async-components/views/dialogs/ExportE2eKeysDialog'], () => {
cb(require('../../../async-components/views/dialogs/ExportE2eKeysDialog'));
}, "e2e-export");
}, {
matrixClient: MatrixClientPeg.get(),
});
},
onInputChanged: function(stateKey, ev) {
@ -152,7 +150,7 @@ module.exports = React.createClass({
showErrorDialog: function(body, title) {
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
Modal.createTrackedDialog('Forgot Password Error', '', ErrorDialog, {
title: title,
description: body,
});

View file

@ -19,8 +19,11 @@ limitations under the License.
import React from 'react';
import { _t, _tJsx } from '../../../languageHandler';
import * as languageHandler from '../../../languageHandler';
import sdk from '../../../index';
import Login from '../../../Login';
import UserSettingsStore from '../../../UserSettingsStore';
import PlatformPeg from '../../../PlatformPeg';
// For validating phone numbers without country codes
const PHONE_NUMBER_REGEX = /^[0-9\(\)\-\s]*$/;
@ -306,6 +309,23 @@ module.exports = React.createClass({
}
},
_onLanguageChange: function(newLang) {
if(languageHandler.getCurrentLanguage() !== newLang) {
UserSettingsStore.setLocalSetting('language', newLang);
PlatformPeg.get().reload();
}
},
_renderLanguageSetting: function() {
const LanguageDropdown = sdk.getComponent('views.elements.LanguageDropdown');
return <div className="mx_Login_language_div">
<LanguageDropdown onOptionChange={this._onLanguageChange}
className="mx_Login_language"
value={languageHandler.getCurrentLanguage()}
/>
</div>;
},
render: function() {
const Loader = sdk.getComponent("elements.Spinner");
const LoginHeader = sdk.getComponent("login.LoginHeader");
@ -354,6 +374,7 @@ module.exports = React.createClass({
</a>
{ loginAsGuestJsx }
{ returnToAppJsx }
{ this._renderLanguageSetting() }
<LoginFooter />
</div>
</div>