diff --git a/src/UnknownDeviceErrorHandler.js b/src/UnknownDeviceErrorHandler.js
index d842cc3a6e..2aa0573e22 100644
--- a/src/UnknownDeviceErrorHandler.js
+++ b/src/UnknownDeviceErrorHandler.js
@@ -18,13 +18,17 @@ import dis from './dispatcher';
import sdk from './index';
import Modal from './Modal';
+let isDialogOpen = false;
+
const onAction = function(payload) {
- if (payload.action === 'unknown_device_error') {
+ if (payload.action === 'unknown_device_error' && !isDialogOpen) {
var UnknownDeviceDialog = sdk.getComponent("dialogs.UnknownDeviceDialog");
+ isDialogOpen = true;
Modal.createDialog(UnknownDeviceDialog, {
devices: payload.err.devices,
room: payload.room,
onFinished: (r) => {
+ isDialogOpen = false;
// XXX: temporary logging to try to diagnose
// https://github.com/vector-im/riot-web/issues/3148
console.log('UnknownDeviceDialog closed with '+r);
diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index 2fa5e92608..2337d62fd8 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -63,6 +63,13 @@ module.exports = React.createClass({
// called when the session load completes
onLoadCompleted: React.PropTypes.func,
+ // Represents the screen to display as a result of parsing the initial
+ // window.location
+ initialScreenAfterLogin: React.PropTypes.shape({
+ screen: React.PropTypes.string.isRequired,
+ params: React.PropTypes.object,
+ }),
+
// displayname, if any, to set on the device when logging
// in/registering.
defaultDeviceDisplayName: React.PropTypes.string,
@@ -89,6 +96,12 @@ module.exports = React.createClass({
var s = {
loading: true,
screen: undefined,
+ screenAfterLogin: this.props.initialScreenAfterLogin,
+
+ // 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,
// What the LoggedInView would be showing if visible
page_type: null,
@@ -184,11 +197,6 @@ module.exports = React.createClass({
componentWillMount: function() {
SdkConfig.put(this.props.config);
- // Stashed guest credentials if the user logs out
- // whilst logged in as a guest user (so they can change
- // their mind & log back in)
- this.guestCreds = null;
-
// if the automatic session load failed, the error
this.sessionLoadError = null;
@@ -317,14 +325,13 @@ module.exports = React.createClass({
},
onAction: function(payload) {
+ const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
+ const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
var roomIndexDelta = 1;
var self = this;
switch (payload.action) {
case 'logout':
- if (MatrixClientPeg.get().isGuest()) {
- this.guestCreds = MatrixClientPeg.getCredentials();
- }
Lifecycle.logout();
break;
case 'start_registration':
@@ -344,7 +351,13 @@ module.exports = React.createClass({
this.notifyNewScreen('register');
break;
case 'start_login':
- if (this.state.logged_in) return;
+ if (MatrixClientPeg.get() &&
+ MatrixClientPeg.get().isGuest()
+ ) {
+ this.setState({
+ guestCreds: MatrixClientPeg.getCredentials(),
+ });
+ }
this.setStateForNewScreen({
screen: 'login',
});
@@ -359,8 +372,8 @@ module.exports = React.createClass({
// 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.guestCreds = MatrixClientPeg.getCredentials();
this.setStateForNewScreen({
+ guestCreds: MatrixClientPeg.getCredentials(),
screen: "register",
upgradeUsername: MatrixClientPeg.get().getUserIdLocalpart(),
guestAccessToken: MatrixClientPeg.get().getAccessToken(),
@@ -382,25 +395,23 @@ module.exports = React.createClass({
this.notifyNewScreen('forgot_password');
break;
case 'leave_room':
- var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
- var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
-
- var roomId = payload.room_id;
Modal.createDialog(QuestionDialog, {
title: "Leave room",
description: "Are you sure you want to leave the room?",
- onFinished: function(should_leave) {
+ onFinished: (should_leave) => {
if (should_leave) {
- var d = MatrixClientPeg.get().leave(roomId);
+ const d = MatrixClientPeg.get().leave(payload.room_id);
// FIXME: controller shouldn't be loading a view :(
- var Loader = sdk.getComponent("elements.Spinner");
- var modal = Modal.createDialog(Loader, null, 'mx_Dialog_spinner');
+ const Loader = sdk.getComponent("elements.Spinner");
+ const modal = Modal.createDialog(Loader, null, 'mx_Dialog_spinner');
- d.then(function() {
+ d.then(() => {
modal.close();
- dis.dispatch({action: 'view_next_room'});
- }, function(err) {
+ if (this.currentRoomId === payload.room_id) {
+ dis.dispatch({action: 'view_next_room'});
+ }
+ }, (err) => {
modal.close();
console.error("Failed to leave room " + payload.room_id + " " + err);
Modal.createDialog(ErrorDialog, {
@@ -412,6 +423,32 @@ module.exports = React.createClass({
}
});
break;
+ case 'reject_invite':
+ Modal.createDialog(QuestionDialog, {
+ title: "Reject invitation",
+ description: "Are you sure you want to reject the invitation?",
+ onFinished: (confirm) => {
+ if (confirm) {
+ // FIXME: controller shouldn't be loading a view :(
+ const Loader = sdk.getComponent("elements.Spinner");
+ const modal = Modal.createDialog(Loader, null, 'mx_Dialog_spinner');
+
+ MatrixClientPeg.get().leave(payload.room_id).done(() => {
+ modal.close();
+ if (this.currentRoomId === payload.room_id) {
+ dis.dispatch({action: 'view_next_room'});
+ }
+ }, (err) => {
+ modal.close();
+ Modal.createDialog(ErrorDialog, {
+ title: "Failed to reject invitation",
+ description: err.toString()
+ });
+ });
+ }
+ }
+ });
+ break;
case 'view_user':
// FIXME: ugly hack to expand the RightPanel and then re-dispatch.
if (this.state.collapse_rhs) {
@@ -659,6 +696,14 @@ module.exports = React.createClass({
_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();
+ }
},
/**
@@ -709,18 +754,33 @@ module.exports = React.createClass({
* Called when a new logged in session has started
*/
_onLoggedIn: function(teamToken) {
- this.guestCreds = null;
- this.notifyNewScreen('');
this.setState({
- screen: undefined,
+ guestCreds: null,
logged_in: true,
});
if (teamToken) {
this._teamToken = teamToken;
- this._setPage(PageTypes.HomePage);
+ dis.dispatch({action: 'view_home_page'});
} else if (this._is_registered) {
- this._setPage(PageTypes.UserSettings);
+ dis.dispatch({action: 'view_user_settings'});
+ } else {
+ this._showScreenAfterLogin();
+ }
+ },
+
+ _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) {
+ this.showScreen(
+ this.state.screenAfterLogin.screen,
+ this.state.screenAfterLogin.params
+ );
+ this.notifyNewScreen(this.state.screenAfterLogin.screen);
+ this.setState({screenAfterLogin: null});
+ } else {
+ dis.dispatch({action: 'view_room_directory'});
}
},
@@ -769,12 +829,6 @@ module.exports = React.createClass({
cli.getRooms()
)[0].roomId;
self.setState({ready: true, currentRoomId: firstRoom, page_type: PageTypes.RoomView});
- } else {
- if (self._teamToken) {
- self.setState({ready: true, page_type: PageTypes.HomePage});
- } else {
- self.setState({ready: true, page_type: PageTypes.RoomDirectory});
- }
}
} else {
self.setState({ready: true, page_type: PageTypes.RoomView});
@@ -791,16 +845,7 @@ module.exports = React.createClass({
if (presentedId != undefined) {
self.notifyNewScreen('room/'+presentedId);
- } else {
- // There is no information on presentedId
- // so point user to fallback like /directory
- if (self._teamToken) {
- self.notifyNewScreen('home');
- } else {
- self.notifyNewScreen('directory');
- }
}
-
dis.dispatch({action: 'focus_composer'});
} else {
self.setState({ready: true});
@@ -1003,9 +1048,9 @@ module.exports = React.createClass({
onReturnToGuestClick: function() {
// reanimate our guest login
- if (this.guestCreds) {
- Lifecycle.setLoggedIn(this.guestCreds);
- this.guestCreds = null;
+ if (this.state.guestCreds) {
+ Lifecycle.setLoggedIn(this.state.guestCreds);
+ this.setState({guestCreds: null});
}
},
@@ -1154,7 +1199,7 @@ module.exports = React.createClass({
onLoggedIn={this.onRegistered}
onLoginClick={this.onLoginClick}
onRegisterClick={this.onRegisterClick}
- onCancelClick={this.guestCreds ? this.onReturnToGuestClick : null}
+ onCancelClick={this.state.guestCreds ? this.onReturnToGuestClick : null}
/>
);
} else if (this.state.screen == 'forgot_password') {
@@ -1181,7 +1226,7 @@ module.exports = React.createClass({
defaultDeviceDisplayName={this.props.defaultDeviceDisplayName}
onForgotPasswordClick={this.onForgotPasswordClick}
enableGuest={this.props.enableGuest}
- onCancelClick={this.guestCreds ? this.onReturnToGuestClick : null}
+ onCancelClick={this.state.guestCreds ? this.onReturnToGuestClick : null}
initialErrorText={this.sessionLoadError}
/>
);
diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js
index ff507b6f90..0f8d35f525 100644
--- a/src/components/structures/MessagePanel.js
+++ b/src/components/structures/MessagePanel.js
@@ -413,7 +413,7 @@ module.exports = React.createClass({
var continuation = false;
if (prevEvent !== null
- && !prevEvent.isRedacted() && prevEvent.sender && mxEv.sender
+ && prevEvent.sender && mxEv.sender
&& mxEv.sender.userId === prevEvent.sender.userId
&& mxEv.getType() == prevEvent.getType()) {
continuation = true;
diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js
index 52161012aa..345d0f6b80 100644
--- a/src/components/structures/RoomView.js
+++ b/src/components/structures/RoomView.js
@@ -915,8 +915,6 @@ module.exports = React.createClass({
},
uploadFile: function(file) {
- var self = this;
-
if (MatrixClientPeg.get().isGuest()) {
var NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog");
Modal.createDialog(NeedToRegisterDialog, {
@@ -928,8 +926,16 @@ module.exports = React.createClass({
ContentMessages.sendContentToRoom(
file, this.state.room.roomId, MatrixClientPeg.get()
- ).done(undefined, function(error) {
- var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
+ ).done(undefined, (error) => {
+ if (error.name === "UnknownDeviceError") {
+ dis.dispatch({
+ action: 'unknown_device_error',
+ err: error,
+ room: this.state.room,
+ });
+ return;
+ }
+ const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Failed to upload file " + file + " " + error);
Modal.createDialog(ErrorDialog, {
title: "Failed to upload file",
diff --git a/src/components/structures/ScrollPanel.js b/src/components/structures/ScrollPanel.js
index 4a0faae9db..44176f73af 100644
--- a/src/components/structures/ScrollPanel.js
+++ b/src/components/structures/ScrollPanel.js
@@ -25,7 +25,7 @@ var DEBUG_SCROLL = false;
// The amount of extra scroll distance to allow prior to unfilling.
// See _getExcessHeight.
-const UNPAGINATION_PADDING = 3000;
+const UNPAGINATION_PADDING = 6000;
// The number of milliseconds to debounce calls to onUnfillRequest, to prevent
// many scroll events causing many unfilling requests.
const UNFILL_REQUEST_DEBOUNCE_MS = 200;
diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js
index 8b30d0f497..91ecc4fb0f 100644
--- a/src/components/structures/UserSettings.js
+++ b/src/components/structures/UserSettings.js
@@ -268,6 +268,12 @@ module.exports = React.createClass({
but for now be warned.
,
button: "Sign out",
+ extraButtons: [
+
+ ],
onFinished: (confirmed) => {
if (confirmed) {
dis.dispatch({action: 'logout'});
diff --git a/src/components/structures/login/Registration.js b/src/components/structures/login/Registration.js
index cbc8929158..5f62407e65 100644
--- a/src/components/structures/login/Registration.js
+++ b/src/components/structures/login/Registration.js
@@ -185,7 +185,6 @@ module.exports = React.createClass({
const teamToken = data.team_token;
// Store for use /w welcome pages
window.localStorage.setItem('mx_team_token', teamToken);
- this.props.onTeamMemberRegistered(teamToken);
this._rtsClient.getTeam(teamToken).then((team) => {
console.log(
diff --git a/src/components/views/dialogs/DeactivateAccountDialog.js b/src/components/views/dialogs/DeactivateAccountDialog.js
index 54a4e99424..b4879982bf 100644
--- a/src/components/views/dialogs/DeactivateAccountDialog.js
+++ b/src/components/views/dialogs/DeactivateAccountDialog.js
@@ -18,7 +18,7 @@ import React from 'react';
import sdk from '../../../index';
import MatrixClientPeg from '../../../MatrixClientPeg';
-import Lifecycle from '../../../Lifecycle';
+import * as Lifecycle from '../../../Lifecycle';
import Velocity from 'velocity-vector';
export default class DeactivateAccountDialog extends React.Component {
diff --git a/src/components/views/dialogs/QuestionDialog.js b/src/components/views/dialogs/QuestionDialog.js
index 0260fc29e2..6012541b94 100644
--- a/src/components/views/dialogs/QuestionDialog.js
+++ b/src/components/views/dialogs/QuestionDialog.js
@@ -21,10 +21,8 @@ export default React.createClass({
displayName: 'QuestionDialog',
propTypes: {
title: React.PropTypes.string,
- description: React.PropTypes.oneOfType([
- React.PropTypes.element,
- React.PropTypes.string,
- ]),
+ description: React.PropTypes.node,
+ extraButtons: React.PropTypes.node,
button: React.PropTypes.string,
focus: React.PropTypes.bool,
onFinished: React.PropTypes.func.isRequired,
@@ -34,6 +32,7 @@ export default React.createClass({
return {
title: "",
description: "",
+ extraButtons: null,
button: "OK",
focus: true,
hasCancelButton: true,
@@ -67,6 +66,7 @@ export default React.createClass({
+ {this.props.extraButtons}
{cancelButton}
diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js
index 48f0f282c1..b451d1c046 100644
--- a/src/components/views/rooms/EventTile.js
+++ b/src/components/views/rooms/EventTile.js
@@ -435,10 +435,7 @@ module.exports = WithMatrixClient(React.createClass({
let avatarSize;
let needsSenderProfile;
- if (isRedacted) {
- avatarSize = 0;
- needsSenderProfile = false;
- } else if (this.props.tileShape === "notif") {
+ if (this.props.tileShape === "notif") {
avatarSize = 24;
needsSenderProfile = true;
} else if (isInfoMessage) {
@@ -503,8 +500,8 @@ module.exports = WithMatrixClient(React.createClass({
else if (e2eEnabled) {
e2e = ;
}
- const timestamp = this.props.mxEvent.isRedacted() ?
- null :