Merge branch 'develop' into matthew/e2e_backups

This commit is contained in:
Matthew Hodgson 2018-05-28 00:46:05 +01:00
commit 25216d4660
824 changed files with 43369 additions and 3899 deletions

View file

@ -1,7 +1,7 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Copyright 2017 New Vector Ltd
Copyright 2017, 2018 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.
@ -165,12 +165,18 @@ export default React.createClass({
newVersionReleaseNotes: null,
checkingForUpdate: null,
showCookieBar: false,
// Parameters used in the registration dance with the IS
register_client_secret: null,
register_session_id: null,
register_hs_url: null,
register_is_url: null,
register_id_sid: null,
// When showing Modal dialogs we need to set aria-hidden on the root app element
// and disable it when there are no dialogs
hideToSRUsers: false,
};
return s;
},
@ -223,8 +229,6 @@ export default React.createClass({
componentWillMount: function() {
SdkConfig.put(this.props.config);
if (!SettingsStore.getValue("analyticsOptOut")) Analytics.enable();
// Used by _viewRoom before getting state from sync
this.firstSyncComplete = false;
this.firstSyncPromise = Promise.defer();
@ -287,6 +291,8 @@ export default React.createClass({
this.handleResize();
window.addEventListener('resize', this.handleResize);
this._pageChanging = false;
// check we have the right tint applied for this theme.
// N.B. we don't call the whole of setTheme() here as we may be
// racing with the theme CSS download finishing from index.js
@ -345,16 +351,26 @@ export default React.createClass({
guestIsUrl: this.getCurrentIsUrl(),
defaultDeviceDisplayName: this.props.defaultDeviceDisplayName,
});
}).catch((e) => {
console.error(`Error attempting to load session: ${e}`);
return false;
}).then((loadedSession) => {
if (!loadedSession) {
// fall back to showing the login screen
dis.dispatch({action: "start_login"});
}
});
}).done();
// Note we don't catch errors from this: we catch everything within
// loadSession as there's logic there to ask the user if they want
// to try logging out.
});
if (SettingsStore.getValue("showCookieBar")) {
this.setState({
showCookieBar: true,
});
}
if (SettingsStore.getValue("analyticsOptIn")) {
Analytics.enable();
}
},
componentWillUnmount: function() {
@ -364,13 +380,62 @@ export default React.createClass({
window.removeEventListener('resize', this.handleResize);
},
componentDidUpdate: function() {
componentWillUpdate: function(props, state) {
if (this.shouldTrackPageChange(this.state, state)) {
this.startPageChangeTimer();
}
},
componentDidUpdate: function(prevProps, prevState) {
if (this.shouldTrackPageChange(prevState, this.state)) {
const durationMs = this.stopPageChangeTimer();
Analytics.trackPageChange(durationMs);
}
if (this.focusComposer) {
dis.dispatch({action: 'focus_composer'});
this.focusComposer = false;
}
},
startPageChangeTimer() {
// This shouldn't happen because componentWillUpdate and componentDidUpdate
// are used.
if (this._pageChanging) {
console.warn('MatrixChat.startPageChangeTimer: timer already started');
return;
}
this._pageChanging = true;
performance.mark('riot_MatrixChat_page_change_start');
},
stopPageChangeTimer() {
if (!this._pageChanging) {
console.warn('MatrixChat.stopPageChangeTimer: timer not started');
return;
}
this._pageChanging = false;
performance.mark('riot_MatrixChat_page_change_stop');
performance.measure(
'riot_MatrixChat_page_change_delta',
'riot_MatrixChat_page_change_start',
'riot_MatrixChat_page_change_stop',
);
performance.clearMarks('riot_MatrixChat_page_change_start');
performance.clearMarks('riot_MatrixChat_page_change_stop');
const measurement = performance.getEntriesByName('riot_MatrixChat_page_change_delta').pop();
// In practice, sometimes the entries list is empty, so we get no measurement
if (!measurement) return null;
return measurement.duration;
},
shouldTrackPageChange(prevState, state) {
return prevState.currentRoomId !== state.currentRoomId ||
prevState.view !== state.view ||
prevState.page_type !== state.page_type;
},
setStateForNewView: function(state) {
if (state.view === undefined) {
throw new Error("setStateForNewView with no view!");
@ -608,6 +673,33 @@ export default React.createClass({
case 'send_event':
this.onSendEvent(payload.room_id, payload.event);
break;
case 'aria_hide_main_app':
this.setState({
hideToSRUsers: true,
});
break;
case 'aria_unhide_main_app':
this.setState({
hideToSRUsers: false,
});
break;
case 'accept_cookies':
SettingsStore.setValue("analyticsOptIn", null, SettingLevel.DEVICE, true);
SettingsStore.setValue("showCookieBar", null, SettingLevel.DEVICE, false);
this.setState({
showCookieBar: false,
});
Analytics.enable();
break;
case 'reject_cookies':
SettingsStore.setValue("analyticsOptIn", null, SettingLevel.DEVICE, false);
SettingsStore.setValue("showCookieBar", null, SettingLevel.DEVICE, false);
this.setState({
showCookieBar: false,
});
break;
}
},
@ -618,18 +710,26 @@ export default React.createClass({
},
_startRegistration: function(params) {
this.setStateForNewView({
const newState = {
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
// user just clicked 'register'
register_client_secret: params.client_secret,
register_session_id: params.session_id,
register_hs_url: params.hs_url,
register_is_url: params.is_url,
register_id_sid: params.sid,
});
};
// Only honour params if they are all present, otherwise we reset
// HS and IS URLs when switching to registration.
if (params.client_secret &&
params.session_id &&
params.hs_url &&
params.is_url &&
params.sid
) {
newState.register_client_secret = params.client_secret;
newState.register_session_id = params.session_id;
newState.register_hs_url = params.hs_url;
newState.register_is_url = params.is_url;
newState.register_id_sid = params.sid;
}
this.setStateForNewView(newState);
this.notifyNewScreen('register');
},
@ -847,16 +947,37 @@ export default React.createClass({
}).close;
},
_leaveRoomWarnings: function(roomId) {
const roomToLeave = MatrixClientPeg.get().getRoom(roomId);
// Show a warning if there are additional complications.
const joinRules = roomToLeave.currentState.getStateEvents('m.room.join_rules', '');
const warnings = [];
if (joinRules) {
const rule = joinRules.getContent().join_rule;
if (rule !== "public") {
warnings.push((
<span className="warning" key="non_public_warning">
{' '/* Whitespace, otherwise the sentences get smashed together */ }
{ _t("This room is not public. You will not be able to rejoin without an invite.") }
</span>
));
}
}
return warnings;
},
_leaveRoom: function(roomId) {
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
const roomToLeave = MatrixClientPeg.get().getRoom(roomId);
const warnings = this._leaveRoomWarnings(roomId);
Modal.createTrackedDialog('Leave room', '', QuestionDialog, {
title: _t("Leave room"),
description: (
<span>
{ _t("Are you sure you want to leave the room '%(roomName)s'?", {roomName: roomToLeave.name}) }
{ warnings }
</span>
),
onFinished: (shouldLeave) => {
@ -1066,10 +1187,10 @@ export default React.createClass({
// this if we are not scrolled up in the view. To find out, delegate to
// the timeline panel. If the timeline panel doesn't exist, then we assume
// it is safe to reset the timeline.
if (!self._loggedInView) {
if (!self._loggedInView || !self._loggedInView.child) {
return true;
}
return self._loggedInView.getDecoratedComponentInstance().canResetTimelineInRoom(roomId);
return self._loggedInView.child.canResetTimelineInRoom(roomId);
});
cli.on('sync', function(state, prevState) {
@ -1111,6 +1232,28 @@ export default React.createClass({
action: 'logout',
});
});
cli.on('no_consent', function(message, consentUri) {
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createTrackedDialog('No Consent Dialog', '', QuestionDialog, {
title: _t('Terms and Conditions'),
description: <div>
<p> { _t(
'To continue using the %(homeserverDomain)s homeserver ' +
'you must review and agree to our terms and conditions.',
{ homeserverDomain: cli.getDomain() },
) }
</p>
</div>,
button: _t('Review terms and conditions'),
cancelButton: _t('Dismiss'),
onFinished: (confirmed) => {
if (confirmed) {
window.open(consentUri, '_blank');
}
},
}, null, true);
});
cli.on("accountData", function(ev) {
if (ev.getType() === 'im.vector.web.settings') {
if (ev.getContent() && ev.getContent().theme) {
@ -1149,18 +1292,6 @@ export default React.createClass({
cli.on("crypto.warning", (type) => {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
switch (type) {
case 'CRYPTO_WARNING_ACCOUNT_MIGRATED':
Modal.createTrackedDialog('Crypto migrated', '', ErrorDialog, {
title: _t('Cryptography data migrated'),
description: _t(
"A one-off migration of cryptography data has been performed. "+
"End-to-end encryption will not work if you go back to an older "+
"version of Riot. If you need to use end-to-end cryptography on "+
"an older version, log out of Riot first. To retain message history, "+
"export and re-import your keys.",
),
});
break;
case 'CRYPTO_WARNING_OLD_VERSION_DETECTED':
Modal.createTrackedDialog('Crypto migrated', '', ErrorDialog, {
title: _t('Old cryptography data detected'),
@ -1317,7 +1448,6 @@ export default React.createClass({
if (this.props.onNewScreen) {
this.props.onNewScreen(screen);
}
Analytics.trackPageChange();
},
onAliasClick: function(event, alias) {
@ -1487,6 +1617,17 @@ export default React.createClass({
}
},
onServerConfigChange(config) {
const newState = {};
if (config.hsUrl) {
newState.register_hs_url = config.hsUrl;
}
if (config.isUrl) {
newState.register_is_url = config.isUrl;
}
this.setState(newState);
},
_makeRegistrationUrl: function(params) {
if (this.props.startingFragmentQueryParams.referrer) {
params.referrer = this.props.startingFragmentQueryParams.referrer;
@ -1536,6 +1677,7 @@ export default React.createClass({
onRegistered={this.onRegistered}
currentRoomId={this.state.currentRoomId}
teamToken={this._teamToken}
showCookieBar={this.state.showCookieBar}
{...this.props}
{...this.state}
/>
@ -1575,6 +1717,7 @@ export default React.createClass({
onLoginClick={this.onLoginClick}
onRegisterClick={this.onRegisterClick}
onCancelClick={MatrixClientPeg.get() ? this.onReturnToAppClick : null}
onServerConfigChange={this.onServerConfigChange}
/>
);
}
@ -1609,6 +1752,7 @@ export default React.createClass({
onForgotPasswordClick={this.onForgotPasswordClick}
enableGuest={this.props.enableGuest}
onCancelClick={MatrixClientPeg.get() ? this.onReturnToAppClick : null}
onServerConfigChange={this.onServerConfigChange}
/>
);
}