Merge remote-tracking branch 'origin/develop' into develop
This commit is contained in:
commit
a2764a0c1f
12 changed files with 297 additions and 210 deletions
107
src/Lifecycle.js
107
src/Lifecycle.js
|
@ -35,26 +35,20 @@ import { _t } from './languageHandler';
|
||||||
* Called at startup, to attempt to build a logged-in Matrix session. It tries
|
* Called at startup, to attempt to build a logged-in Matrix session. It tries
|
||||||
* a number of things:
|
* a number of things:
|
||||||
*
|
*
|
||||||
* 1. if we have a loginToken in the (real) query params, it uses that to log
|
|
||||||
* in.
|
|
||||||
*
|
*
|
||||||
* 2. if we have a guest access token in the fragment query params, it uses
|
* 1. if we have a guest access token in the fragment query params, it uses
|
||||||
* that.
|
* that.
|
||||||
*
|
*
|
||||||
* 3. if an access token is stored in local storage (from a previous session),
|
* 2. if an access token is stored in local storage (from a previous session),
|
||||||
* it uses that.
|
* it uses that.
|
||||||
*
|
*
|
||||||
* 4. it attempts to auto-register as a guest user.
|
* 3. it attempts to auto-register as a guest user.
|
||||||
*
|
*
|
||||||
* If any of steps 1-4 are successful, it will call {_doSetLoggedIn}, which in
|
* If any of steps 1-4 are successful, it will call {_doSetLoggedIn}, which in
|
||||||
* turn will raise on_logged_in and will_start_client events.
|
* turn will raise on_logged_in and will_start_client events.
|
||||||
*
|
*
|
||||||
* @param {object} opts
|
* @param {object} opts
|
||||||
*
|
*
|
||||||
* @param {object} opts.realQueryParams: string->string map of the
|
|
||||||
* query-parameters extracted from the real query-string of the starting
|
|
||||||
* URI.
|
|
||||||
*
|
|
||||||
* @param {object} opts.fragmentQueryParams: string->string map of the
|
* @param {object} opts.fragmentQueryParams: string->string map of the
|
||||||
* query-parameters extracted from the #-fragment of the starting URI.
|
* query-parameters extracted from the #-fragment of the starting URI.
|
||||||
*
|
*
|
||||||
|
@ -68,9 +62,10 @@ import { _t } from './languageHandler';
|
||||||
* true; defines the IS to use.
|
* true; defines the IS to use.
|
||||||
*
|
*
|
||||||
* @returns {Promise} a promise which resolves when the above process completes.
|
* @returns {Promise} a promise which resolves when the above process completes.
|
||||||
|
* Resolves to `true` if we ended up starting a session, or `false` if we
|
||||||
|
* failed.
|
||||||
*/
|
*/
|
||||||
export function loadSession(opts) {
|
export function loadSession(opts) {
|
||||||
const realQueryParams = opts.realQueryParams || {};
|
|
||||||
const fragmentQueryParams = opts.fragmentQueryParams || {};
|
const fragmentQueryParams = opts.fragmentQueryParams || {};
|
||||||
let enableGuest = opts.enableGuest || false;
|
let enableGuest = opts.enableGuest || false;
|
||||||
const guestHsUrl = opts.guestHsUrl;
|
const guestHsUrl = opts.guestHsUrl;
|
||||||
|
@ -82,14 +77,6 @@ export function loadSession(opts) {
|
||||||
enableGuest = false;
|
enableGuest = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (realQueryParams.loginToken) {
|
|
||||||
if (!realQueryParams.homeserver) {
|
|
||||||
console.warn("Cannot log in with token: can't determine HS URL to use");
|
|
||||||
} else {
|
|
||||||
return _loginWithToken(realQueryParams, defaultDeviceDisplayName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (enableGuest &&
|
if (enableGuest &&
|
||||||
fragmentQueryParams.guest_user_id &&
|
fragmentQueryParams.guest_user_id &&
|
||||||
fragmentQueryParams.guest_access_token
|
fragmentQueryParams.guest_access_token
|
||||||
|
@ -101,12 +88,12 @@ export function loadSession(opts) {
|
||||||
homeserverUrl: guestHsUrl,
|
homeserverUrl: guestHsUrl,
|
||||||
identityServerUrl: guestIsUrl,
|
identityServerUrl: guestIsUrl,
|
||||||
guest: true,
|
guest: true,
|
||||||
}, true);
|
}, true).then(() => true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return _restoreFromLocalStorage().then((success) => {
|
return _restoreFromLocalStorage().then((success) => {
|
||||||
if (success) {
|
if (success) {
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enableGuest) {
|
if (enableGuest) {
|
||||||
|
@ -114,10 +101,30 @@ export function loadSession(opts) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// fall back to login screen
|
// fall back to login screen
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function _loginWithToken(queryParams, defaultDeviceDisplayName) {
|
/**
|
||||||
|
* @param {Object} queryParams string->string map of the
|
||||||
|
* query-parameters extracted from the real query-string of the starting
|
||||||
|
* URI.
|
||||||
|
*
|
||||||
|
* @param {String} defaultDeviceDisplayName
|
||||||
|
*
|
||||||
|
* @returns {Promise} promise which resolves to true if we completed the token
|
||||||
|
* login, else false
|
||||||
|
*/
|
||||||
|
export function attemptTokenLogin(queryParams, defaultDeviceDisplayName) {
|
||||||
|
if (!queryParams.loginToken) {
|
||||||
|
return q(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!queryParams.homeserver) {
|
||||||
|
console.warn("Cannot log in with token: can't determine HS URL to use");
|
||||||
|
return q(false);
|
||||||
|
}
|
||||||
|
|
||||||
// create a temporary MatrixClient to do the login
|
// create a temporary MatrixClient to do the login
|
||||||
const client = Matrix.createClient({
|
const client = Matrix.createClient({
|
||||||
baseUrl: queryParams.homeserver,
|
baseUrl: queryParams.homeserver,
|
||||||
|
@ -130,17 +137,21 @@ function _loginWithToken(queryParams, defaultDeviceDisplayName) {
|
||||||
},
|
},
|
||||||
).then(function(data) {
|
).then(function(data) {
|
||||||
console.log("Logged in with token");
|
console.log("Logged in with token");
|
||||||
return _doSetLoggedIn({
|
return _clearStorage().then(() => {
|
||||||
|
_persistCredentialsToLocalStorage({
|
||||||
userId: data.user_id,
|
userId: data.user_id,
|
||||||
deviceId: data.device_id,
|
deviceId: data.device_id,
|
||||||
accessToken: data.access_token,
|
accessToken: data.access_token,
|
||||||
homeserverUrl: queryParams.homeserver,
|
homeserverUrl: queryParams.homeserver,
|
||||||
identityServerUrl: queryParams.identityServer,
|
identityServerUrl: queryParams.identityServer,
|
||||||
guest: false,
|
guest: false,
|
||||||
}, true);
|
});
|
||||||
}, (err) => {
|
return true;
|
||||||
|
});
|
||||||
|
}).catch((err) => {
|
||||||
console.error("Failed to log in with login token: " + err + " " +
|
console.error("Failed to log in with login token: " + err + " " +
|
||||||
err.data);
|
err.data);
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,9 +179,10 @@ function _registerAsGuest(hsUrl, isUrl, defaultDeviceDisplayName) {
|
||||||
homeserverUrl: hsUrl,
|
homeserverUrl: hsUrl,
|
||||||
identityServerUrl: isUrl,
|
identityServerUrl: isUrl,
|
||||||
guest: true,
|
guest: true,
|
||||||
}, true);
|
}, true).then(() => true);
|
||||||
}, (err) => {
|
}, (err) => {
|
||||||
console.error("Failed to register as guest: " + err + " " + err.data);
|
console.error("Failed to register as guest: " + err + " " + err.data);
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,10 +294,12 @@ export function initRtsClient(url) {
|
||||||
* storage before starting the new client.
|
* storage before starting the new client.
|
||||||
*
|
*
|
||||||
* @param {MatrixClientCreds} credentials The credentials to use
|
* @param {MatrixClientCreds} credentials The credentials to use
|
||||||
|
*
|
||||||
|
* @returns {Promise} promise which resolves to the new MatrixClient once it has been started
|
||||||
*/
|
*/
|
||||||
export function setLoggedIn(credentials) {
|
export function setLoggedIn(credentials) {
|
||||||
stopMatrixClient();
|
stopMatrixClient();
|
||||||
_doSetLoggedIn(credentials, true);
|
return _doSetLoggedIn(credentials, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -295,7 +309,7 @@ export function setLoggedIn(credentials) {
|
||||||
* @param {MatrixClientCreds} credentials
|
* @param {MatrixClientCreds} credentials
|
||||||
* @param {Boolean} clearStorage
|
* @param {Boolean} clearStorage
|
||||||
*
|
*
|
||||||
* returns a Promise which resolves once the client has been started
|
* @returns {Promise} promise which resolves to the new MatrixClient once it has been started
|
||||||
*/
|
*/
|
||||||
async function _doSetLoggedIn(credentials, clearStorage) {
|
async function _doSetLoggedIn(credentials, clearStorage) {
|
||||||
credentials.guest = Boolean(credentials.guest);
|
credentials.guest = Boolean(credentials.guest);
|
||||||
|
@ -322,23 +336,10 @@ async function _doSetLoggedIn(credentials, clearStorage) {
|
||||||
// Resolves by default
|
// Resolves by default
|
||||||
let teamPromise = Promise.resolve(null);
|
let teamPromise = Promise.resolve(null);
|
||||||
|
|
||||||
// persist the session
|
|
||||||
if (localStorage) {
|
if (localStorage) {
|
||||||
try {
|
try {
|
||||||
localStorage.setItem("mx_hs_url", credentials.homeserverUrl);
|
_persistCredentialsToLocalStorage(credentials);
|
||||||
localStorage.setItem("mx_is_url", credentials.identityServerUrl);
|
|
||||||
localStorage.setItem("mx_user_id", credentials.userId);
|
|
||||||
localStorage.setItem("mx_access_token", credentials.accessToken);
|
|
||||||
localStorage.setItem("mx_is_guest", JSON.stringify(credentials.guest));
|
|
||||||
|
|
||||||
// if we didn't get a deviceId from the login, leave mx_device_id unset,
|
|
||||||
// rather than setting it to "undefined".
|
|
||||||
//
|
|
||||||
// (in this case MatrixClient doesn't bother with the crypto stuff
|
|
||||||
// - that's fine for us).
|
|
||||||
if (credentials.deviceId) {
|
|
||||||
localStorage.setItem("mx_device_id", credentials.deviceId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// The user registered as a PWLU (PassWord-Less User), the generated password
|
// The user registered as a PWLU (PassWord-Less User), the generated password
|
||||||
// is cached here such that the user can change it at a later time.
|
// is cached here such that the user can change it at a later time.
|
||||||
|
@ -349,8 +350,6 @@ async function _doSetLoggedIn(credentials, clearStorage) {
|
||||||
cachedPassword: credentials.password,
|
cachedPassword: credentials.password,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Session persisted for %s", credentials.userId);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn("Error using local storage: can't persist session!", e);
|
console.warn("Error using local storage: can't persist session!", e);
|
||||||
}
|
}
|
||||||
|
@ -377,6 +376,26 @@ async function _doSetLoggedIn(credentials, clearStorage) {
|
||||||
});
|
});
|
||||||
|
|
||||||
startMatrixClient();
|
startMatrixClient();
|
||||||
|
return MatrixClientPeg.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
function _persistCredentialsToLocalStorage(credentials) {
|
||||||
|
localStorage.setItem("mx_hs_url", credentials.homeserverUrl);
|
||||||
|
localStorage.setItem("mx_is_url", credentials.identityServerUrl);
|
||||||
|
localStorage.setItem("mx_user_id", credentials.userId);
|
||||||
|
localStorage.setItem("mx_access_token", credentials.accessToken);
|
||||||
|
localStorage.setItem("mx_is_guest", JSON.stringify(credentials.guest));
|
||||||
|
|
||||||
|
// if we didn't get a deviceId from the login, leave mx_device_id unset,
|
||||||
|
// rather than setting it to "undefined".
|
||||||
|
//
|
||||||
|
// (in this case MatrixClient doesn't bother with the crypto stuff
|
||||||
|
// - that's fine for us).
|
||||||
|
if (credentials.deviceId) {
|
||||||
|
localStorage.setItem("mx_device_id", credentials.deviceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Session persisted for %s", credentials.userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -43,7 +43,41 @@ import createRoom from "../../createRoom";
|
||||||
import * as UDEHandler from '../../UnknownDeviceErrorHandler';
|
import * as UDEHandler from '../../UnknownDeviceErrorHandler';
|
||||||
import { _t, getCurrentLanguage } from '../../languageHandler';
|
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({
|
module.exports = React.createClass({
|
||||||
|
// we export this so that the integration tests can use it :-S
|
||||||
|
statics: {
|
||||||
|
VIEWS: VIEWS,
|
||||||
|
},
|
||||||
|
|
||||||
displayName: 'MatrixChat',
|
displayName: 'MatrixChat',
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
|
@ -59,8 +93,8 @@ module.exports = React.createClass({
|
||||||
// the initial queryParams extracted from the hash-fragment of the URI
|
// the initial queryParams extracted from the hash-fragment of the URI
|
||||||
startingFragmentQueryParams: React.PropTypes.object,
|
startingFragmentQueryParams: React.PropTypes.object,
|
||||||
|
|
||||||
// called when the session load completes
|
// called when we have completed a token login
|
||||||
onLoadCompleted: React.PropTypes.func,
|
onTokenLoginCompleted: React.PropTypes.func,
|
||||||
|
|
||||||
// Represents the screen to display as a result of parsing the initial
|
// Represents the screen to display as a result of parsing the initial
|
||||||
// window.location
|
// window.location
|
||||||
|
@ -93,8 +127,10 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
const s = {
|
const s = {
|
||||||
loading: true,
|
// the master view we are showing.
|
||||||
screen: undefined,
|
view: VIEWS.LOADING,
|
||||||
|
|
||||||
|
// a thing to call showScreen with once login completes.
|
||||||
screenAfterLogin: this.props.initialScreenAfterLogin,
|
screenAfterLogin: this.props.initialScreenAfterLogin,
|
||||||
|
|
||||||
// Stashed guest credentials if the user logs out
|
// Stashed guest credentials if the user logs out
|
||||||
|
@ -113,8 +149,6 @@ module.exports = React.createClass({
|
||||||
// If we're trying to just view a user ID (i.e. /user URL), this is it
|
// If we're trying to just view a user ID (i.e. /user URL), this is it
|
||||||
viewUserId: null,
|
viewUserId: null,
|
||||||
|
|
||||||
loggedIn: false,
|
|
||||||
loggingIn: false,
|
|
||||||
collapse_lhs: false,
|
collapse_lhs: false,
|
||||||
collapse_rhs: false,
|
collapse_rhs: false,
|
||||||
ready: false,
|
ready: false,
|
||||||
|
@ -143,7 +177,7 @@ module.exports = React.createClass({
|
||||||
realQueryParams: {},
|
realQueryParams: {},
|
||||||
startingFragmentQueryParams: {},
|
startingFragmentQueryParams: {},
|
||||||
config: {},
|
config: {},
|
||||||
onLoadCompleted: () => {},
|
onTokenLoginCompleted: () => {},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -266,16 +300,24 @@ module.exports = React.createClass({
|
||||||
const teamServerConfig = this.props.config.teamServerConfig || {};
|
const teamServerConfig = this.props.config.teamServerConfig || {};
|
||||||
Lifecycle.initRtsClient(teamServerConfig.teamServerURL);
|
Lifecycle.initRtsClient(teamServerConfig.teamServerURL);
|
||||||
|
|
||||||
|
// 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
|
// if the user has followed a login or register link, don't reanimate
|
||||||
// the old creds, but rather go straight to the relevant page
|
// the old creds, but rather go straight to the relevant page
|
||||||
|
|
||||||
const firstScreen = this.state.screenAfterLogin ?
|
const firstScreen = this.state.screenAfterLogin ?
|
||||||
this.state.screenAfterLogin.screen : null;
|
this.state.screenAfterLogin.screen : null;
|
||||||
|
|
||||||
if (firstScreen === 'login' ||
|
if (firstScreen === 'login' ||
|
||||||
firstScreen === 'register' ||
|
firstScreen === 'register' ||
|
||||||
firstScreen === 'forgot_password') {
|
firstScreen === 'forgot_password') {
|
||||||
this.props.onLoadCompleted();
|
|
||||||
this.setState({loading: false});
|
this.setState({loading: false});
|
||||||
this._showScreenAfterLogin();
|
this._showScreenAfterLogin();
|
||||||
return;
|
return;
|
||||||
|
@ -283,9 +325,8 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
// the extra q() ensures that synchronous exceptions hit the same codepath as
|
// the extra q() ensures that synchronous exceptions hit the same codepath as
|
||||||
// asynchronous ones.
|
// asynchronous ones.
|
||||||
q().then(() => {
|
return q().then(() => {
|
||||||
return Lifecycle.loadSession({
|
return Lifecycle.loadSession({
|
||||||
realQueryParams: this.props.realQueryParams,
|
|
||||||
fragmentQueryParams: this.props.startingFragmentQueryParams,
|
fragmentQueryParams: this.props.startingFragmentQueryParams,
|
||||||
enableGuest: this.props.enableGuest,
|
enableGuest: this.props.enableGuest,
|
||||||
guestHsUrl: this.getCurrentHsUrl(),
|
guestHsUrl: this.getCurrentHsUrl(),
|
||||||
|
@ -294,11 +335,14 @@ module.exports = React.createClass({
|
||||||
});
|
});
|
||||||
}).catch((e) => {
|
}).catch((e) => {
|
||||||
console.error("Unable to load session", e);
|
console.error("Unable to load session", e);
|
||||||
}).done(()=>{
|
return false;
|
||||||
// stuff this through the dispatcher so that it happens
|
}).then((loadedSession) => {
|
||||||
// after the on_logged_in action.
|
if (!loadedSession) {
|
||||||
dis.dispatch({action: 'load_completed'});
|
// fall back to showing the login screen
|
||||||
|
dis.dispatch({action: "start_login"});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
}).done();
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount: function() {
|
||||||
|
@ -317,18 +361,19 @@ module.exports = React.createClass({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
setStateForNewScreen: function(state) {
|
setStateForNewView: function(state) {
|
||||||
|
if (state.view === undefined) {
|
||||||
|
throw new Error("setStateForNewView with no view!");
|
||||||
|
}
|
||||||
const newState = {
|
const newState = {
|
||||||
screen: undefined,
|
|
||||||
viewUserId: null,
|
viewUserId: null,
|
||||||
loggedIn: false,
|
|
||||||
ready: false,
|
|
||||||
};
|
};
|
||||||
Object.assign(newState, state);
|
Object.assign(newState, state);
|
||||||
this.setState(newState);
|
this.setState(newState);
|
||||||
},
|
},
|
||||||
|
|
||||||
onAction: function(payload) {
|
onAction: function(payload) {
|
||||||
|
// console.log(`MatrixClientPeg.onAction: ${payload.action}`);
|
||||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||||
|
|
||||||
|
@ -347,19 +392,19 @@ module.exports = React.createClass({
|
||||||
guestCreds: MatrixClientPeg.getCredentials(),
|
guestCreds: MatrixClientPeg.getCredentials(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.setStateForNewScreen({
|
this.setStateForNewView({
|
||||||
screen: 'login',
|
view: VIEWS.LOGIN,
|
||||||
});
|
});
|
||||||
this.notifyNewScreen('login');
|
this.notifyNewScreen('login');
|
||||||
break;
|
break;
|
||||||
case 'start_post_registration':
|
case 'start_post_registration':
|
||||||
this.setState({ // don't clobber loggedIn status
|
this.setState({
|
||||||
screen: 'post_registration',
|
view: VIEWS.POST_REGISTRATION,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'start_password_recovery':
|
case 'start_password_recovery':
|
||||||
this.setStateForNewScreen({
|
this.setStateForNewView({
|
||||||
screen: 'forgot_password',
|
view: VIEWS.FORGOT_PASSWORD,
|
||||||
});
|
});
|
||||||
this.notifyNewScreen('forgot_password');
|
this.notifyNewScreen('forgot_password');
|
||||||
break;
|
break;
|
||||||
|
@ -503,7 +548,10 @@ module.exports = React.createClass({
|
||||||
// and also that we're not ready (we'll be marked as logged
|
// and also that we're not ready (we'll be marked as logged
|
||||||
// in once the login completes, then ready once the sync
|
// in once the login completes, then ready once the sync
|
||||||
// completes).
|
// completes).
|
||||||
this.setState({loggingIn: true, ready: false});
|
this.setStateForNewView({
|
||||||
|
view: VIEWS.LOGGING_IN,
|
||||||
|
ready: false,
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
case 'on_logged_in':
|
case 'on_logged_in':
|
||||||
this._onLoggedIn(payload.teamToken);
|
this._onLoggedIn(payload.teamToken);
|
||||||
|
@ -514,15 +562,15 @@ module.exports = React.createClass({
|
||||||
case 'will_start_client':
|
case 'will_start_client':
|
||||||
this._onWillStartClient();
|
this._onWillStartClient();
|
||||||
break;
|
break;
|
||||||
case 'load_completed':
|
|
||||||
this._onLoadCompleted();
|
|
||||||
break;
|
|
||||||
case 'new_version':
|
case 'new_version':
|
||||||
this.onVersion(
|
this.onVersion(
|
||||||
payload.currentVersion, payload.newVersion,
|
payload.currentVersion, payload.newVersion,
|
||||||
payload.releaseNotes,
|
payload.releaseNotes,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
case 'send_event':
|
||||||
|
this.onSendEvent(payload.room_id, payload.event);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -537,8 +585,8 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
_startRegistration: function(params) {
|
_startRegistration: function(params) {
|
||||||
this.setStateForNewScreen({
|
this.setStateForNewView({
|
||||||
screen: 'register',
|
view: VIEWS.REGISTER,
|
||||||
// these params may be undefined, but if they are,
|
// these params may be undefined, but if they are,
|
||||||
// unset them from our state: we don't want to
|
// unset them from our state: we don't want to
|
||||||
// resume a previous registration session if the
|
// resume a previous registration session if the
|
||||||
|
@ -846,14 +894,6 @@ module.exports = React.createClass({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the sessionloader has finished
|
|
||||||
*/
|
|
||||||
_onLoadCompleted: function() {
|
|
||||||
this.props.onLoadCompleted();
|
|
||||||
this.setState({loading: false});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called whenever someone changes the theme
|
* Called whenever someone changes the theme
|
||||||
*
|
*
|
||||||
|
@ -906,9 +946,8 @@ module.exports = React.createClass({
|
||||||
*/
|
*/
|
||||||
_onLoggedIn: function(teamToken) {
|
_onLoggedIn: function(teamToken) {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
view: VIEWS.LOGGED_IN,
|
||||||
guestCreds: null,
|
guestCreds: null,
|
||||||
loggedIn: true,
|
|
||||||
loggingIn: false,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (teamToken) {
|
if (teamToken) {
|
||||||
|
@ -969,8 +1008,8 @@ module.exports = React.createClass({
|
||||||
*/
|
*/
|
||||||
_onLoggedOut: function() {
|
_onLoggedOut: function() {
|
||||||
this.notifyNewScreen('login');
|
this.notifyNewScreen('login');
|
||||||
this.setStateForNewScreen({
|
this.setStateForNewView({
|
||||||
loggedIn: false,
|
view: VIEWS.LOGIN,
|
||||||
ready: false,
|
ready: false,
|
||||||
collapse_lhs: false,
|
collapse_lhs: false,
|
||||||
collapse_rhs: false,
|
collapse_rhs: false,
|
||||||
|
@ -1133,7 +1172,7 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
// we can't view a room unless we're logged in
|
// we can't view a room unless we're logged in
|
||||||
// (a guest account is fine)
|
// (a guest account is fine)
|
||||||
if (this.state.loggedIn) {
|
if (this.state.view === VIEWS.LOGGED_IN) {
|
||||||
dis.dispatch(payload);
|
dis.dispatch(payload);
|
||||||
}
|
}
|
||||||
} else if (screen.indexOf('user/') == 0) {
|
} else if (screen.indexOf('user/') == 0) {
|
||||||
|
@ -1241,19 +1280,20 @@ module.exports = React.createClass({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// returns a promise which resolves to the new MatrixClient
|
||||||
onRegistered: function(credentials, teamToken) {
|
onRegistered: function(credentials, teamToken) {
|
||||||
// XXX: These both should be in state or ideally store(s) because we risk not
|
// 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.
|
// rendering the most up-to-date view of state otherwise.
|
||||||
// teamToken may not be truthy
|
// teamToken may not be truthy
|
||||||
this._teamToken = teamToken;
|
this._teamToken = teamToken;
|
||||||
this._is_registered = true;
|
this._is_registered = true;
|
||||||
Lifecycle.setLoggedIn(credentials);
|
return Lifecycle.setLoggedIn(credentials);
|
||||||
},
|
},
|
||||||
|
|
||||||
onFinishPostRegistration: function() {
|
onFinishPostRegistration: function() {
|
||||||
// Don't confuse this with "PageType" which is the middle window to show
|
// Don't confuse this with "PageType" which is the middle window to show
|
||||||
this.setState({
|
this.setState({
|
||||||
screen: undefined,
|
view: VIEWS.LOGGED_IN,
|
||||||
});
|
});
|
||||||
this.showScreen("settings");
|
this.showScreen("settings");
|
||||||
},
|
},
|
||||||
|
@ -1267,6 +1307,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) {
|
updateStatusIndicator: function(state, prevState) {
|
||||||
let notifCount = 0;
|
let notifCount = 0;
|
||||||
|
|
||||||
|
@ -1321,11 +1382,9 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
// `loading` might be set to false before `loggedIn = true`, causing the default
|
// console.log(`Rendering MatrixChat with view ${this.state.view}`);
|
||||||
// (`<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
|
if (this.state.view === VIEWS.LOADING || this.state.view === VIEWS.LOGGING_IN) {
|
||||||
// actions `on_logging_in` and `on_logged_in`.
|
|
||||||
if (this.state.loading || this.state.loggingIn) {
|
|
||||||
const Spinner = sdk.getComponent('elements.Spinner');
|
const Spinner = sdk.getComponent('elements.Spinner');
|
||||||
return (
|
return (
|
||||||
<div className="mx_MatrixChat_splash">
|
<div className="mx_MatrixChat_splash">
|
||||||
|
@ -1335,7 +1394,7 @@ module.exports = React.createClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
// needs to be before normal PageTypes as you are logged in technically
|
// 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');
|
const PostRegistration = sdk.getComponent('structures.login.PostRegistration');
|
||||||
return (
|
return (
|
||||||
<PostRegistration
|
<PostRegistration
|
||||||
|
@ -1343,10 +1402,11 @@ module.exports = React.createClass({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// `ready` and `loggedIn` may be set before `page_type` (because the
|
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`,
|
// latter is set via the dispatcher). If we don't yet have a `page_type`,
|
||||||
// keep showing the spinner for now.
|
// keep showing the spinner for now.
|
||||||
if (this.state.loggedIn && this.state.ready && this.state.page_type) {
|
if (this.state.ready && this.state.page_type) {
|
||||||
/* for now, we stuff the entirety of our props and state into the LoggedInView.
|
/* 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
|
* 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.
|
* as using something like redux to avoid having a billion bits of state kicking around.
|
||||||
|
@ -1363,7 +1423,7 @@ module.exports = React.createClass({
|
||||||
{...this.state}
|
{...this.state}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (this.state.loggedIn) {
|
} else {
|
||||||
// we think we are logged in, but are still waiting for the /sync to complete
|
// we think we are logged in, but are still waiting for the /sync to complete
|
||||||
const Spinner = sdk.getComponent('elements.Spinner');
|
const Spinner = sdk.getComponent('elements.Spinner');
|
||||||
return (
|
return (
|
||||||
|
@ -1374,7 +1434,10 @@ module.exports = React.createClass({
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else if (this.state.screen == 'register') {
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.state.view === VIEWS.REGISTER) {
|
||||||
const Registration = sdk.getComponent('structures.login.Registration');
|
const Registration = sdk.getComponent('structures.login.Registration');
|
||||||
return (
|
return (
|
||||||
<Registration
|
<Registration
|
||||||
|
@ -1397,7 +1460,10 @@ module.exports = React.createClass({
|
||||||
onCancelClick={this.state.guestCreds ? this.onReturnToGuestClick : null}
|
onCancelClick={this.state.guestCreds ? this.onReturnToGuestClick : null}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (this.state.screen == 'forgot_password') {
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (this.state.view === VIEWS.FORGOT_PASSWORD) {
|
||||||
const ForgotPassword = sdk.getComponent('structures.login.ForgotPassword');
|
const ForgotPassword = sdk.getComponent('structures.login.ForgotPassword');
|
||||||
return (
|
return (
|
||||||
<ForgotPassword
|
<ForgotPassword
|
||||||
|
@ -1409,7 +1475,9 @@ module.exports = React.createClass({
|
||||||
onRegisterClick={this.onRegisterClick}
|
onRegisterClick={this.onRegisterClick}
|
||||||
onLoginClick={this.onLoginClick} />
|
onLoginClick={this.onLoginClick} />
|
||||||
);
|
);
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
if (this.state.view === VIEWS.LOGIN) {
|
||||||
const Login = sdk.getComponent('structures.login.Login');
|
const Login = sdk.getComponent('structures.login.Login');
|
||||||
return (
|
return (
|
||||||
<Login
|
<Login
|
||||||
|
@ -1427,5 +1495,7 @@ module.exports = React.createClass({
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.error(`Unknown view ${this.state.view}`);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -169,6 +169,7 @@ module.exports = React.createClass({
|
||||||
initialEventId: RoomViewStore.getInitialEventId(),
|
initialEventId: RoomViewStore.getInitialEventId(),
|
||||||
initialEventPixelOffset: RoomViewStore.getInitialEventPixelOffset(),
|
initialEventPixelOffset: RoomViewStore.getInitialEventPixelOffset(),
|
||||||
isInitialEventHighlighted: RoomViewStore.isInitialEventHighlighted(),
|
isInitialEventHighlighted: RoomViewStore.isInitialEventHighlighted(),
|
||||||
|
forwardingEvent: RoomViewStore.getForwardingEvent(),
|
||||||
shouldPeek: RoomViewStore.shouldPeek(),
|
shouldPeek: RoomViewStore.shouldPeek(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -457,11 +458,6 @@ module.exports = React.createClass({
|
||||||
callState: callState
|
callState: callState
|
||||||
});
|
});
|
||||||
|
|
||||||
break;
|
|
||||||
case 'forward_event':
|
|
||||||
this.setState({
|
|
||||||
forwardingEvent: payload.content,
|
|
||||||
});
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1169,8 +1165,13 @@ module.exports = React.createClass({
|
||||||
this.updateTint();
|
this.updateTint();
|
||||||
this.setState({
|
this.setState({
|
||||||
editingRoomSettings: false,
|
editingRoomSettings: false,
|
||||||
forwardingEvent: null,
|
|
||||||
});
|
});
|
||||||
|
if (this.state.forwardingEvent) {
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'forward_event',
|
||||||
|
event: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
dis.dispatch({action: 'focus_composer'});
|
dis.dispatch({action: 'focus_composer'});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1581,7 +1582,7 @@ module.exports = React.createClass({
|
||||||
} else if (this.state.uploadingRoomSettings) {
|
} else if (this.state.uploadingRoomSettings) {
|
||||||
aux = <Loader/>;
|
aux = <Loader/>;
|
||||||
} else if (this.state.forwardingEvent !== null) {
|
} 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) {
|
} else if (this.state.searching) {
|
||||||
hideCancel = true; // has own cancel
|
hideCancel = true; // has own cancel
|
||||||
aux = <SearchBar ref="search_bar" searchInProgress={this.state.searchInProgress } onCancelClick={this.onCancelSearchClick} onSearch={this.onSearch}/>;
|
aux = <SearchBar ref="search_bar" searchInProgress={this.state.searchInProgress } onCancelClick={this.onCancelSearchClick} onSearch={this.onSearch}/>;
|
||||||
|
|
|
@ -160,6 +160,10 @@ module.exports = React.createClass({
|
||||||
this.checkFillState();
|
this.checkFillState();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
componentWillUpdate: function(nextProps, nextState) {
|
||||||
|
this._saveScrollState();
|
||||||
|
},
|
||||||
|
|
||||||
componentDidUpdate: function() {
|
componentDidUpdate: function() {
|
||||||
// after adding event tiles, we may need to tweak the scroll (either to
|
// after adding event tiles, we may need to tweak the scroll (either to
|
||||||
// keep at the bottom of the timeline, or to maintain the view after
|
// keep at the bottom of the timeline, or to maintain the view after
|
||||||
|
|
|
@ -218,29 +218,29 @@ module.exports = React.createClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
trackPromise.then((teamToken) => {
|
trackPromise.then((teamToken) => {
|
||||||
this.props.onLoggedIn({
|
return this.props.onLoggedIn({
|
||||||
userId: response.user_id,
|
userId: response.user_id,
|
||||||
deviceId: response.device_id,
|
deviceId: response.device_id,
|
||||||
homeserverUrl: this._matrixClient.getHomeserverUrl(),
|
homeserverUrl: this._matrixClient.getHomeserverUrl(),
|
||||||
identityServerUrl: this._matrixClient.getIdentityServerUrl(),
|
identityServerUrl: this._matrixClient.getIdentityServerUrl(),
|
||||||
accessToken: response.access_token
|
accessToken: response.access_token
|
||||||
}, teamToken);
|
}, teamToken);
|
||||||
}).then(() => {
|
}).then((cli) => {
|
||||||
return this._setupPushers();
|
return this._setupPushers(cli);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
_setupPushers: function() {
|
_setupPushers: function(matrixClient) {
|
||||||
if (!this.props.brand) {
|
if (!this.props.brand) {
|
||||||
return q();
|
return q();
|
||||||
}
|
}
|
||||||
return MatrixClientPeg.get().getPushers().then((resp)=>{
|
return matrixClient.getPushers().then((resp)=>{
|
||||||
const pushers = resp.pushers;
|
const pushers = resp.pushers;
|
||||||
for (let i = 0; i < pushers.length; ++i) {
|
for (let i = 0; i < pushers.length; ++i) {
|
||||||
if (pushers[i].kind == 'email') {
|
if (pushers[i].kind == 'email') {
|
||||||
const emailPusher = pushers[i];
|
const emailPusher = pushers[i];
|
||||||
emailPusher.data = { brand: this.props.brand };
|
emailPusher.data = { brand: this.props.brand };
|
||||||
MatrixClientPeg.get().setPusher(emailPusher).done(() => {
|
matrixClient.setPusher(emailPusher).done(() => {
|
||||||
console.log("Set email branding to " + this.props.brand);
|
console.log("Set email branding to " + this.props.brand);
|
||||||
}, (error) => {
|
}, (error) => {
|
||||||
console.error("Couldn't set email branding: " + error);
|
console.error("Couldn't set email branding: " + error);
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
|
||||||
import dis from '../../../dispatcher';
|
import dis from '../../../dispatcher';
|
||||||
import KeyCode from '../../../KeyCode';
|
import KeyCode from '../../../KeyCode';
|
||||||
|
|
||||||
|
@ -26,11 +25,6 @@ module.exports = React.createClass({
|
||||||
displayName: 'ForwardMessage',
|
displayName: 'ForwardMessage',
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
currentRoomId: React.PropTypes.string.isRequired,
|
|
||||||
|
|
||||||
/* the MatrixEvent to be forwarded */
|
|
||||||
mxEvent: React.PropTypes.object.isRequired,
|
|
||||||
|
|
||||||
onCancelClick: React.PropTypes.func.isRequired,
|
onCancelClick: React.PropTypes.func.isRequired,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -44,7 +38,6 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount: function() {
|
||||||
this.dispatcherRef = dis.register(this.onAction);
|
|
||||||
document.addEventListener('keydown', this._onKeyDown);
|
document.addEventListener('keydown', this._onKeyDown);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -54,30 +47,9 @@ module.exports = React.createClass({
|
||||||
sideOpacity: 1.0,
|
sideOpacity: 1.0,
|
||||||
middleOpacity: 1.0,
|
middleOpacity: 1.0,
|
||||||
});
|
});
|
||||||
dis.unregister(this.dispatcherRef);
|
|
||||||
document.removeEventListener('keydown', this._onKeyDown);
|
document.removeEventListener('keydown', this._onKeyDown);
|
||||||
},
|
},
|
||||||
|
|
||||||
onAction: function(payload) {
|
|
||||||
if (payload.action === 'view_room') {
|
|
||||||
const event = this.props.mxEvent;
|
|
||||||
const Client = MatrixClientPeg.get();
|
|
||||||
Client.sendEvent(payload.room_id, event.getType(), event.getContent()).done(() => {
|
|
||||||
dis.dispatch({action: 'message_sent'});
|
|
||||||
}, (err) => {
|
|
||||||
if (err.name === "UnknownDeviceError") {
|
|
||||||
dis.dispatch({
|
|
||||||
action: 'unknown_device_error',
|
|
||||||
err: err,
|
|
||||||
room: Client.getRoom(payload.room_id),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
dis.dispatch({action: 'message_send_failed'});
|
|
||||||
});
|
|
||||||
if (this.props.currentRoomId === payload.room_id) this.props.onCancelClick();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_onKeyDown: function(ev) {
|
_onKeyDown: function(ev) {
|
||||||
switch (ev.keyCode) {
|
switch (ev.keyCode) {
|
||||||
case KeyCode.ESCAPE:
|
case KeyCode.ESCAPE:
|
||||||
|
|
|
@ -839,13 +839,13 @@
|
||||||
"Share message history with new users": "Διαμοιρασμός ιστορικού μηνυμάτων με τους νέους χρήστες",
|
"Share message history with new users": "Διαμοιρασμός ιστορικού μηνυμάτων με τους νέους χρήστες",
|
||||||
"numbullet": "απαρίθμηση",
|
"numbullet": "απαρίθμηση",
|
||||||
"%(severalUsers)sleft and rejoined %(repeats)s times": "%(severalUsers)s έφυγαν και ξανασυνδέθηκαν %(repeats)s φορές",
|
"%(severalUsers)sleft and rejoined %(repeats)s times": "%(severalUsers)s έφυγαν και ξανασυνδέθηκαν %(repeats)s φορές",
|
||||||
"%(oneUser)sleft and rejoined %(repeats)s times": "%(severalUsers)s έφυγε και ξανασυνδέθηκε %(repeats)s φορές",
|
"%(oneUser)sleft and rejoined %(repeats)s times": "%(oneUser)s έφυγε και ξανασυνδέθηκε %(repeats)s φορές",
|
||||||
"%(severalUsers)sleft and rejoined": "%(severalUsers)s έφυγαν και ξανασυνδέθηκαν",
|
"%(severalUsers)sleft and rejoined": "%(severalUsers)s έφυγαν και ξανασυνδέθηκαν",
|
||||||
"%(oneUser)sleft and rejoined": "%(severalUsers)s έφυγε και ξανασυνδέθηκε",
|
"%(oneUser)sleft and rejoined": "%(oneUser)s έφυγε και ξανασυνδέθηκε",
|
||||||
"%(severalUsers)shad their invitations withdrawn %(repeats)s times": "Οι %(severalUsers)s απέσυραν τις προσκλήσεις τους %(repeats)s φορές",
|
"%(severalUsers)shad their invitations withdrawn %(repeats)s times": "Οι %(severalUsers)s απέσυραν τις προσκλήσεις τους %(repeats)s φορές",
|
||||||
"%(oneUser)shad their invitation withdrawn %(repeats)s times": "Ο %(severalUsers)s απέσυρε την πρόσκληση του %(repeats)s φορές",
|
"%(oneUser)shad their invitation withdrawn %(repeats)s times": "Ο %(oneUser)s απέσυρε την πρόσκληση του %(repeats)s φορές",
|
||||||
"%(severalUsers)shad their invitations withdrawn": "Οι %(severalUsers)s απέσυραν τις προσκλήσεις τους",
|
"%(severalUsers)shad their invitations withdrawn": "Οι %(severalUsers)s απέσυραν τις προσκλήσεις τους",
|
||||||
"%(oneUser)shad their invitation withdrawn": "Ο %(severalUsers)s απέσυρε την πρόσκληση του",
|
"%(oneUser)shad their invitation withdrawn": "Ο %(oneUser)s απέσυρε την πρόσκληση του",
|
||||||
"You must join the room to see its files": "Πρέπει να συνδεθείτε στο δωμάτιο για να δείτε τα αρχεία του",
|
"You must join the room to see its files": "Πρέπει να συνδεθείτε στο δωμάτιο για να δείτε τα αρχεία του",
|
||||||
"Reject all %(invitedRooms)s invites": "Απόρριψη όλων των προσκλήσεων %(invitedRooms)s",
|
"Reject all %(invitedRooms)s invites": "Απόρριψη όλων των προσκλήσεων %(invitedRooms)s",
|
||||||
"Failed to invite the following users to the %(roomName)s room:": "Δεν ήταν δυνατή η πρόσκληση των χρηστών στο δωμάτιο %(roomName)s:",
|
"Failed to invite the following users to the %(roomName)s room:": "Δεν ήταν δυνατή η πρόσκληση των χρηστών στο δωμάτιο %(roomName)s:",
|
||||||
|
|
|
@ -204,7 +204,7 @@
|
||||||
"Anyone who knows the room's link, apart from guests": "A vendégeken kívül bárki aki ismeri a szoba link-jét",
|
"Anyone who knows the room's link, apart from guests": "A vendégeken kívül bárki aki ismeri a szoba link-jét",
|
||||||
"Anyone who knows the room's link, including guests": "Bárki aki tudja a szoba link-jét, még a vendégek is",
|
"Anyone who knows the room's link, including guests": "Bárki aki tudja a szoba link-jét, még a vendégek is",
|
||||||
"Are you sure?": "Biztos?",
|
"Are you sure?": "Biztos?",
|
||||||
"Are you sure you want to leave the room '%(roomName)s'?": "Biztos elhagyod a szobát?",
|
"Are you sure you want to leave the room '%(roomName)s'?": "Biztos elhagyod a szobát '%(roomName)s'?",
|
||||||
"Are you sure you want to reject the invitation?": "Biztos elutasítod a meghívást?",
|
"Are you sure you want to reject the invitation?": "Biztos elutasítod a meghívást?",
|
||||||
"Are you sure you want to upload the following files?": "Biztos feltöltöd ezeket a fájlokat?",
|
"Are you sure you want to upload the following files?": "Biztos feltöltöd ezeket a fájlokat?",
|
||||||
"Attachment": "Csatolmány",
|
"Attachment": "Csatolmány",
|
||||||
|
|
|
@ -333,7 +333,7 @@
|
||||||
"Create an account": "Open een account",
|
"Create an account": "Open een account",
|
||||||
"Cryptography": "Cryptografie",
|
"Cryptography": "Cryptografie",
|
||||||
"Current password": "Huidig wachtwoord",
|
"Current password": "Huidig wachtwoord",
|
||||||
"%(senderDisplayName)s removed the room name.": "%(senderDisplayName) heeft de naam van de kamer verwijderd.",
|
"%(senderDisplayName)s removed the room name.": "%(senderDisplayName)s heeft de naam van de kamer verwijderd.",
|
||||||
"Create a new chat or reuse an existing one": "Maak een nieuwe chat aan of gebruik een reeds bestaande",
|
"Create a new chat or reuse an existing one": "Maak een nieuwe chat aan of gebruik een reeds bestaande",
|
||||||
"Create Room": "Maak een kamer",
|
"Create Room": "Maak een kamer",
|
||||||
"Curve25519 identity key": "Curve25519 identiteitssleutel",
|
"Curve25519 identity key": "Curve25519 identiteitssleutel",
|
||||||
|
|
|
@ -246,7 +246,7 @@
|
||||||
"Failed to set up conference call": "Не удалось установить конференц-вызов",
|
"Failed to set up conference call": "Не удалось установить конференц-вызов",
|
||||||
"Failed to verify email address: make sure you clicked the link in the email": "Не удалось подтвердить email-адрес: убедитесь что вы щелкнули по ссылке электронной почты",
|
"Failed to verify email address: make sure you clicked the link in the email": "Не удалось подтвердить email-адрес: убедитесь что вы щелкнули по ссылке электронной почты",
|
||||||
"Failure to create room": "Не удалось создать комнату",
|
"Failure to create room": "Не удалось создать комнату",
|
||||||
"%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId) изменил %(fromPowerLevel) на %(toPowerLevel)",
|
"%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s изменил %(fromPowerLevel)s на %(toPowerLevel)s",
|
||||||
"Guest users can't create new rooms. Please register to create room and start a chat.": "Гостевые пользователи не могут создавать новые комнаты. Зарегистрируйтесь для создания комнаты и чата.",
|
"Guest users can't create new rooms. Please register to create room and start a chat.": "Гостевые пользователи не могут создавать новые комнаты. Зарегистрируйтесь для создания комнаты и чата.",
|
||||||
"click to reveal": "нажать для открытия",
|
"click to reveal": "нажать для открытия",
|
||||||
"%(senderName)s invited %(targetName)s.": "%(senderName)s приглашает %(targetName)s.",
|
"%(senderName)s invited %(targetName)s.": "%(senderName)s приглашает %(targetName)s.",
|
||||||
|
@ -355,7 +355,7 @@
|
||||||
"Friday": "Пятница",
|
"Friday": "Пятница",
|
||||||
"Saturday": "Суббота",
|
"Saturday": "Суббота",
|
||||||
"Sunday": "Воскресенье",
|
"Sunday": "Воскресенье",
|
||||||
"%(weekDayName)s %(time)s": "%(weekDayName) %(time)",
|
"%(weekDayName)s %(time)s": "%(weekDayName)s %(time)s",
|
||||||
"Upload an avatar:": "Загрузите аватар:",
|
"Upload an avatar:": "Загрузите аватар:",
|
||||||
"You need to be logged in.": "Вы должны быть авторизованы.",
|
"You need to be logged in.": "Вы должны быть авторизованы.",
|
||||||
"You need to be able to invite users to do that.": "Вам необходимо пригласить пользователей чтобы сделать это.",
|
"You need to be able to invite users to do that.": "Вам необходимо пригласить пользователей чтобы сделать это.",
|
||||||
|
@ -524,7 +524,7 @@
|
||||||
"OK": "ОК",
|
"OK": "ОК",
|
||||||
"Only people who have been invited": "Только приглашённые люди",
|
"Only people who have been invited": "Только приглашённые люди",
|
||||||
"Passwords can't be empty": "Поля паролей не могут быть пустыми",
|
"Passwords can't be empty": "Поля паролей не могут быть пустыми",
|
||||||
"%(senderName)s placed a %(callType)s call.": "%(senderName) выполнил %(callType) вызов.",
|
"%(senderName)s placed a %(callType)s call.": "%(senderName)s выполнил %(callType)s вызов.",
|
||||||
"Please check your email and click on the link it contains. Once this is done, click continue.": "Пожалуйста, проверьте вашу электронную почту и нажмите в ней ссылку. По завершении нажмите продолжить.",
|
"Please check your email and click on the link it contains. Once this is done, click continue.": "Пожалуйста, проверьте вашу электронную почту и нажмите в ней ссылку. По завершении нажмите продолжить.",
|
||||||
"Power level must be positive integer.": "Уровень силы должен быть положительным числом.",
|
"Power level must be positive integer.": "Уровень силы должен быть положительным числом.",
|
||||||
"Press": "Нажать",
|
"Press": "Нажать",
|
||||||
|
@ -946,8 +946,8 @@
|
||||||
"Would you like to <acceptText>accept</acceptText> or <declineText>decline</declineText> this invitation?": "Хотели бы вы <acceptText>подтвердить</acceptText> это приглашение или <declineText>отклонить</declineText>?",
|
"Would you like to <acceptText>accept</acceptText> or <declineText>decline</declineText> this invitation?": "Хотели бы вы <acceptText>подтвердить</acceptText> это приглашение или <declineText>отклонить</declineText>?",
|
||||||
"(~%(count)s results).one": "(~%(count)s Результат)",
|
"(~%(count)s results).one": "(~%(count)s Результат)",
|
||||||
"Can't connect to homeserver - please check your connectivity, ensure your <a>homeserver's SSL certificate</a> is trusted, and that a browser extension is not blocking requests.": "Не удается подключиться к домашнему серверу - проверьте подключение, убедитесь, что ваш сертификат SSL <a>homeserver's SSL certificate</a> действителен, и расширение браузера не блокирует запросы.",
|
"Can't connect to homeserver - please check your connectivity, ensure your <a>homeserver's SSL certificate</a> is trusted, and that a browser extension is not blocking requests.": "Не удается подключиться к домашнему серверу - проверьте подключение, убедитесь, что ваш сертификат SSL <a>homeserver's SSL certificate</a> действителен, и расширение браузера не блокирует запросы.",
|
||||||
"You have been banned from %(roomName)s by %(userName)s.": "%(userName) забанил Вас в % (roomName).",
|
"You have been banned from %(roomName)s by %(userName)s.": "%(userName)s забанил Вас в %(roomName)s.",
|
||||||
"You have been kicked from %(roomName)s by %(userName)s.": "%(userName) выгнал Вас из %(roomName).",
|
"You have been kicked from %(roomName)s by %(userName)s.": "%(userName)s выгнал Вас из %(roomName)s.",
|
||||||
"You may wish to login with a different account, or add this email to this account.": "Вы можете войти в систему с другой учетной записью или добавить этот адрес email в эту учетную запись.",
|
"You may wish to login with a different account, or add this email to this account.": "Вы можете войти в систему с другой учетной записью или добавить этот адрес email в эту учетную запись.",
|
||||||
"Your home server does not support device management.": "Ваш домашний сервер не поддерживает управление устройствами.",
|
"Your home server does not support device management.": "Ваш домашний сервер не поддерживает управление устройствами.",
|
||||||
"(could not connect media)": "(не удается подключиться к медиа)",
|
"(could not connect media)": "(не удается подключиться к медиа)",
|
||||||
|
|
|
@ -296,7 +296,7 @@
|
||||||
"Active call (%(roomName)s)": "Aktiv samtal (%(roomName)s)",
|
"Active call (%(roomName)s)": "Aktiv samtal (%(roomName)s)",
|
||||||
"Add": "Lägg till",
|
"Add": "Lägg till",
|
||||||
"Admin tools": "Admin verktyg",
|
"Admin tools": "Admin verktyg",
|
||||||
"And %(count)s more...": "Och %(count) till...",
|
"And %(count)s more...": "Och %(count)s till...",
|
||||||
"Alias (optional)": "Alias (valfri)",
|
"Alias (optional)": "Alias (valfri)",
|
||||||
"Can't connect to homeserver - please check your connectivity, ensure your <a>homeserver's SSL certificate</a> is trusted, and that a browser extension is not blocking requests.": "Det gick inte att ansluta till servern - kontrollera anslutningen, försäkra att din <a>hemservers TLS-certifikat</a> är betrott, och att inget webbläsartillägg blockerar förfrågningar.",
|
"Can't connect to homeserver - please check your connectivity, ensure your <a>homeserver's SSL certificate</a> is trusted, and that a browser extension is not blocking requests.": "Det gick inte att ansluta till servern - kontrollera anslutningen, försäkra att din <a>hemservers TLS-certifikat</a> är betrott, och att inget webbläsartillägg blockerar förfrågningar.",
|
||||||
"%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s ändrade maktnivån av %(powerLevelDiffText)s.",
|
"%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s ändrade maktnivån av %(powerLevelDiffText)s.",
|
||||||
|
|
|
@ -55,6 +55,8 @@ const INITIAL_STATE = {
|
||||||
// pixelOffset: the number of pixels the window is scrolled down
|
// pixelOffset: the number of pixels the window is scrolled down
|
||||||
// from the focussedEvent.
|
// from the focussedEvent.
|
||||||
scrollStateMap: {},
|
scrollStateMap: {},
|
||||||
|
|
||||||
|
forwardingEvent: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -116,6 +118,11 @@ class RoomViewStore extends Store {
|
||||||
case 'update_scroll_state':
|
case 'update_scroll_state':
|
||||||
this._updateScrollState(payload);
|
this._updateScrollState(payload);
|
||||||
break;
|
break;
|
||||||
|
case 'forward_event':
|
||||||
|
this._setState({
|
||||||
|
forwardingEvent: payload.event,
|
||||||
|
});
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,6 +134,7 @@ class RoomViewStore extends Store {
|
||||||
initialEventId: payload.event_id,
|
initialEventId: payload.event_id,
|
||||||
initialEventPixelOffset: undefined,
|
initialEventPixelOffset: undefined,
|
||||||
isInitialEventHighlighted: payload.highlighted,
|
isInitialEventHighlighted: payload.highlighted,
|
||||||
|
forwardingEvent: null,
|
||||||
roomLoading: false,
|
roomLoading: false,
|
||||||
roomLoadError: null,
|
roomLoadError: null,
|
||||||
// should peek by default
|
// should peek by default
|
||||||
|
@ -143,6 +151,14 @@ class RoomViewStore extends Store {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this._state.forwardingEvent) {
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'send_event',
|
||||||
|
room_id: newState.roomId,
|
||||||
|
event: this._state.forwardingEvent,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this._setState(newState);
|
this._setState(newState);
|
||||||
} else if (payload.room_alias) {
|
} else if (payload.room_alias) {
|
||||||
// Resolve the alias and then do a second dispatch with the room ID acquired
|
// Resolve the alias and then do a second dispatch with the room ID acquired
|
||||||
|
@ -279,6 +295,11 @@ class RoomViewStore extends Store {
|
||||||
return this._state.joinError;
|
return this._state.joinError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The mxEvent if one is about to be forwarded
|
||||||
|
getForwardingEvent() {
|
||||||
|
return this._state.forwardingEvent;
|
||||||
|
}
|
||||||
|
|
||||||
shouldPeek() {
|
shouldPeek() {
|
||||||
return this._state.shouldPeek;
|
return this._state.shouldPeek;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue