Merge remote-tracking branch 'origin/develop' into rav/handle_received_room_key_requests

This commit is contained in:
Richard van der Hoff 2017-06-07 15:44:33 +01:00
commit 27e5098e00
71 changed files with 2580 additions and 732 deletions

View file

@ -34,6 +34,9 @@ import sdk from '../../index';
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';
import RoomViewStore from '../../stores/RoomViewStore';
import PageTypes from '../../PageTypes';
import createRoom from "../../createRoom";
@ -103,9 +106,6 @@ module.exports = React.createClass({
// What the LoggedInView would be showing if visible
page_type: null,
// If we are viewing a room by alias, this contains the alias
currentRoomAlias: null,
// The ID of the room we're viewing. This is either populated directly
// in the case where we view a room by ID or by RoomView when it resolves
// what ID an alias points at.
@ -192,6 +192,9 @@ module.exports = React.createClass({
componentWillMount: function() {
SdkConfig.put(this.props.config);
RoomViewStore.addListener(this._onRoomViewStoreUpdated);
this._onRoomViewStoreUpdated();
if (!UserSettingsStore.getLocalSetting('analyticsOptOut', false)) Analytics.enable();
// Used by _viewRoom before getting state from sync
@ -293,7 +296,7 @@ module.exports = React.createClass({
},
componentWillUnmount: function() {
Lifecycle.stopMatrixClient(false);
Lifecycle.stopMatrixClient();
dis.unregister(this.dispatcherRef);
UDEHandler.stopListening();
window.removeEventListener("focus", this.onFocus);
@ -323,7 +326,6 @@ module.exports = React.createClass({
onAction: function(payload) {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
const TextInputDialog = sdk.getComponent("dialogs.TextInputDialog");
switch (payload.action) {
case 'logout':
@ -365,17 +367,21 @@ module.exports = React.createClass({
// 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(false);
Lifecycle.stopMatrixClient();
this.notifyNewScreen('register');
break;
case 'start_password_recovery':
if (this.state.loggedIn) return;
this.setStateForNewScreen({
screen: 'forgot_password',
});
this.notifyNewScreen('forgot_password');
break;
case 'start_chat':
createRoom({
dmUserId: payload.user_id,
});
break;
case 'leave_room':
this._leaveRoom(payload.room_id);
break;
@ -436,37 +442,36 @@ module.exports = React.createClass({
this._viewIndexedRoom(payload.roomIndex);
break;
case 'view_user_settings':
if (MatrixClientPeg.get().isGuest()) {
dis.dispatch({
action: 'do_after_sync_prepared',
deferred_action: {
action: 'view_user_settings',
},
});
dis.dispatch({action: 'view_set_mxid'});
break;
}
this._setPage(PageTypes.UserSettings);
this.notifyNewScreen('settings');
break;
case 'view_create_room':
//this._setPage(PageTypes.CreateRoom);
//this.notifyNewScreen('new');
Modal.createDialog(TextInputDialog, {
title: _t('Create Room'),
description: _t('Room name (optional)'),
button: _t('Create Room'),
onFinished: (shouldCreate, name) => {
if (shouldCreate) {
const createOpts = {};
if (name) createOpts.name = name;
createRoom({createOpts}).done();
}
},
});
this._createRoom();
break;
case 'view_room_directory':
this._setPage(PageTypes.RoomDirectory);
this.notifyNewScreen('directory');
break;
case 'view_home_page':
if (!this._teamToken) {
dis.dispatch({action: 'view_room_directory'});
return;
}
this._setPage(PageTypes.HomePage);
this.notifyNewScreen('home');
break;
case 'view_set_mxid':
this._setMxId();
break;
case 'view_start_chat_or_reuse':
this._chatCreateOrReuse(payload.user_id);
break;
case 'view_create_chat':
this._createChat();
break;
@ -508,7 +513,11 @@ module.exports = React.createClass({
this._onSetTheme(payload.value);
break;
case 'on_logging_in':
this.setState({loggingIn: true});
// 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});
break;
case 'on_logged_in':
this._onLoggedIn(payload.teamToken);
@ -531,6 +540,10 @@ module.exports = React.createClass({
}
},
_onRoomViewStoreUpdated: function() {
this.setState({ currentRoomId: RoomViewStore.getRoomId() });
},
_setPage: function(pageType) {
this.setState({
page_type: pageType,
@ -553,6 +566,7 @@ module.exports = React.createClass({
this.notifyNewScreen('register');
},
// TODO: Move to RoomViewStore
_viewNextRoom: function(roomIndexDelta) {
const allRooms = RoomListSorter.mostRecentActivityFirst(
MatrixClientPeg.get().getRooms(),
@ -566,15 +580,22 @@ module.exports = React.createClass({
}
roomIndex = (roomIndex + roomIndexDelta) % allRooms.length;
if (roomIndex < 0) roomIndex = allRooms.length - 1;
this._viewRoom({ room_id: allRooms[roomIndex].roomId });
dis.dispatch({
action: 'view_room',
room_id: allRooms[roomIndex].roomId,
});
},
// TODO: Move to RoomViewStore
_viewIndexedRoom: function(roomIndex) {
const allRooms = RoomListSorter.mostRecentActivityFirst(
MatrixClientPeg.get().getRooms(),
);
if (allRooms[roomIndex]) {
this._viewRoom({ room_id: allRooms[roomIndex].roomId });
dis.dispatch({
action: 'view_room',
room_id: allRooms[roomIndex].roomId,
});
}
},
@ -658,7 +679,41 @@ module.exports = React.createClass({
});
},
_setMxId: function() {
const SetMxIdDialog = sdk.getComponent('views.dialogs.SetMxIdDialog');
const close = Modal.createDialog(SetMxIdDialog, {
homeserverUrl: MatrixClientPeg.get().getHomeserverUrl(),
onFinished: (submitted, credentials) => {
if (!submitted) {
dis.dispatch({
action: 'cancel_after_sync_prepared',
});
return;
}
this.onRegistered(credentials);
},
onDifferentServerClicked: (ev) => {
dis.dispatch({action: 'start_registration'});
close();
},
onLoginClick: (ev) => {
dis.dispatch({action: 'start_login'});
close();
},
}).close;
},
_createChat: function() {
if (MatrixClientPeg.get().isGuest()) {
dis.dispatch({
action: 'do_after_sync_prepared',
deferred_action: {
action: 'view_create_chat',
},
});
dis.dispatch({action: 'view_set_mxid'});
return;
}
const ChatInviteDialog = sdk.getComponent("dialogs.ChatInviteDialog");
Modal.createDialog(ChatInviteDialog, {
title: _t('Start a chat'),
@ -668,6 +723,81 @@ module.exports = React.createClass({
});
},
_createRoom: function() {
if (MatrixClientPeg.get().isGuest()) {
dis.dispatch({
action: 'do_after_sync_prepared',
deferred_action: {
action: 'view_create_room',
},
});
dis.dispatch({action: 'view_set_mxid'});
return;
}
const TextInputDialog = sdk.getComponent("dialogs.TextInputDialog");
Modal.createDialog(TextInputDialog, {
title: _t('Create Room'),
description: _t('Room name (optional)'),
button: _t('Create Room'),
onFinished: (should_create, name) => {
if (should_create) {
const createOpts = {};
if (name) createOpts.name = name;
createRoom({createOpts}).done();
}
},
});
},
_chatCreateOrReuse: function(userId) {
const ChatCreateOrReuseDialog = sdk.getComponent(
'views.dialogs.ChatCreateOrReuseDialog',
);
// Use a deferred action to reshow the dialog once the user has registered
if (MatrixClientPeg.get().isGuest()) {
// No point in making 2 DMs with welcome bot. This assumes view_set_mxid will
// result in a new DM with the welcome user.
if (userId !== this.props.config.welcomeUserId) {
dis.dispatch({
action: 'do_after_sync_prepared',
deferred_action: {
action: 'view_start_chat_or_reuse',
user_id: userId,
},
});
}
dis.dispatch({
action: 'view_set_mxid',
});
return;
}
const close = Modal.createDialog(ChatCreateOrReuseDialog, {
userId: userId,
onFinished: (success) => {
if (!success) {
// Dialog cancelled, default to home
dis.dispatch({ action: 'view_home_page' });
}
},
onNewDMClick: () => {
dis.dispatch({
action: 'start_chat',
user_id: userId,
});
// Close the dialog, indicate success (calls onFinished(true))
close(true);
},
onExistingRoomSelected: (roomId) => {
dis.dispatch({
action: 'view_room',
room_id: roomId,
});
close(true);
},
}).close;
},
_invite: function(roomId) {
const ChatInviteDialog = sdk.getComponent("dialogs.ChatInviteDialog");
Modal.createDialog(ChatInviteDialog, {
@ -701,7 +831,7 @@ module.exports = React.createClass({
d.then(() => {
modal.close();
if (this.currentRoomId === roomId) {
if (this.state.currentRoomId === roomId) {
dis.dispatch({action: 'view_next_room'});
}
}, (err) => {
@ -796,12 +926,27 @@ module.exports = React.createClass({
this._teamToken = teamToken;
dis.dispatch({action: 'view_home_page'});
} else if (this._is_registered) {
this._is_registered = false;
// reset the 'have completed first sync' flag,
// since we've just logged in and will be about to sync
this.firstSyncComplete = false;
this.firstSyncPromise = q.defer();
// Set the display name = user ID localpart
MatrixClientPeg.get().setDisplayName(
MatrixClientPeg.get().getUserIdLocalpart(),
);
if (this.props.config.welcomeUserId && getCurrentLanguage().startsWith("en")) {
createRoom({dmUserId: this.props.config.welcomeUserId});
createRoom({
dmUserId: this.props.config.welcomeUserId,
// Only view the welcome user if we're NOT looking at a room
andView: !this.state.currentRoomId,
});
return;
}
// The user has just logged in after registering
dis.dispatch({action: 'view_room_directory'});
dis.dispatch({action: 'view_home_page'});
} else {
this._showScreenAfterLogin();
}
@ -823,12 +968,8 @@ module.exports = React.createClass({
action: 'view_room',
room_id: localStorage.getItem('mx_last_room_id'),
});
} else if (this._teamToken) {
// Team token might be set if we're a guest.
// Guests do not call _onLoggedIn with a teamToken
dis.dispatch({action: 'view_home_page'});
} else {
dis.dispatch({action: 'view_room_directory'});
dis.dispatch({action: 'view_home_page'});
}
},
@ -842,7 +983,6 @@ module.exports = React.createClass({
ready: false,
collapse_lhs: false,
collapse_rhs: false,
currentRoomAlias: null,
currentRoomId: null,
page_type: PageTypes.RoomDirectory,
});
@ -880,6 +1020,12 @@ module.exports = React.createClass({
});
cli.on('sync', function(state, prevState) {
// LifecycleStore and others cannot directly subscribe to matrix client for
// events because flux only allows store state changes during flux dispatches.
// So dispatch directly from here. Ideally we'd use a SyncStateStore that
// would do this dispatch and expose the sync state itself (by listening to
// its own dispatch).
dis.dispatch({action: 'sync_state', prevState, state});
self.updateStatusIndicator(state, prevState);
if (state === "SYNCING" && prevState === "SYNCING") {
return;
@ -957,6 +1103,11 @@ module.exports = React.createClass({
dis.dispatch({
action: 'view_home_page',
});
} else if (screen == 'start') {
this.showScreen('home');
dis.dispatch({
action: 'view_set_mxid',
});
} else if (screen == 'directory') {
dis.dispatch({
action: 'view_room_directory',
@ -1000,6 +1151,12 @@ module.exports = React.createClass({
}
} else if (screen.indexOf('user/') == 0) {
const userId = screen.substring(5);
if (params.action === 'chat') {
this._chatCreateOrReuse(userId);
return;
}
this.setState({ viewUserId: userId });
this._setPage(PageTypes.UserView);
this.notifyNewScreen('user/' + userId);
@ -1096,6 +1253,8 @@ module.exports = React.createClass({
},
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;
@ -1139,7 +1298,15 @@ module.exports = React.createClass({
PlatformPeg.get().setNotificationCount(notifCount);
}
document.title = `Riot ${state === "ERROR" ? " [offline]" : ""}${notifCount > 0 ? ` [${notifCount}]` : ""}`;
let title = "Riot ";
if (state === "ERROR") {
title += `[${_t("Offline")}] `;
}
if (notifCount > 0) {
title += `[${notifCount}]`;
}
document.title = title;
},
onUserSettingsClose: function() {
@ -1157,13 +1324,6 @@ module.exports = React.createClass({
}
},
onRoomIdResolved: function(roomId) {
// It's the RoomView's resposibility to look up room aliases, but we need the
// ID to pass into things like the Member List, so the Room View tells us when
// its done that resolution so we can display things that take a room ID.
this.setState({currentRoomId: roomId});
},
_makeRegistrationUrl: function(params) {
if (this.props.startingFragmentQueryParams.referrer) {
params.referrer = this.props.startingFragmentQueryParams.referrer;
@ -1205,9 +1365,10 @@ module.exports = React.createClass({
const LoggedInView = sdk.getComponent('structures.LoggedInView');
return (
<LoggedInView ref="loggedInView" matrixClient={MatrixClientPeg.get()}
onRoomIdResolved={this.onRoomIdResolved}
onRoomCreated={this.onRoomCreated}
onUserSettingsClose={this.onUserSettingsClose}
onRegistered={this.onRegistered}
currentRoomId={this.state.currentRoomId}
teamToken={this._teamToken}
{...this.props}
{...this.state}