Beyond this point you're going to need to pick a username - your
- unique identifire in Riot.
+ unique identifier in Riot.
From 6f4eb9d8b1809c6419945f472d0455978274751f Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Fri, 5 May 2017 16:31:33 +0100
Subject: [PATCH 0023/1016] Show password nag bar when user is PWLU
---
src/Lifecycle.js | 6 ++++--
src/components/structures/LoggedInView.js | 13 +++++++++----
src/components/structures/MatrixChat.js | 12 ++++++++++--
3 files changed, 23 insertions(+), 8 deletions(-)
diff --git a/src/Lifecycle.js b/src/Lifecycle.js
index 7fba0a0298..43a46e2d7d 100644
--- a/src/Lifecycle.js
+++ b/src/Lifecycle.js
@@ -284,6 +284,7 @@ export function setLoggedIn(credentials) {
// Resolves by default
let teamPromise = Promise.resolve(null);
+ let isPasswordStored = false;
// persist the session
if (localStorage) {
@@ -307,6 +308,7 @@ export function setLoggedIn(credentials) {
// is cached here such that the user can change it at a later time.
if (credentials.password) {
localStorage.setItem("mx_pass", credentials.password);
+ isPasswordStored = true;
}
console.log("Session persisted for %s", credentials.userId);
@@ -332,10 +334,10 @@ export function setLoggedIn(credentials) {
MatrixClientPeg.replaceUsingCreds(credentials);
teamPromise.then((teamToken) => {
- dis.dispatch({action: 'on_logged_in', teamToken: teamToken});
+ dis.dispatch({action: 'on_logged_in', teamToken: teamToken, isPasswordStored});
}, (err) => {
console.warn("Failed to get team token on login", err);
- dis.dispatch({action: 'on_logged_in', teamToken: null});
+ dis.dispatch({action: 'on_logged_in', teamToken: null, isPasswordStored});
});
startMatrixClient();
diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js
index 6514b32f79..4001227355 100644
--- a/src/components/structures/LoggedInView.js
+++ b/src/components/structures/LoggedInView.js
@@ -49,6 +49,10 @@ export default React.createClass({
teamToken: React.PropTypes.string,
+ // Has the user generated a password that is stored in local storage?
+ // (are they a PWLU?)
+ userHasGeneratedPassword: React.PropTypes.boolean,
+
// and lots and lots of other stuff.
},
@@ -177,6 +181,7 @@ export default React.createClass({
const MatrixToolbar = sdk.getComponent('globals.MatrixToolbar');
const GuestWarningBar = sdk.getComponent('globals.GuestWarningBar');
const NewVersionBar = sdk.getComponent('globals.NewVersionBar');
+ const PasswordNagBar = sdk.getComponent('globals.PasswordNagBar');
let page_element;
let right_panel = '';
@@ -250,11 +255,11 @@ export default React.createClass({
topBar = ;
- }
- else if (this.props.matrixClient.isGuest()) {
+ } else if (this.props.matrixClient.isGuest()) {
topBar = ;
- }
- else if (Notifier.supportsDesktopNotifications() && !Notifier.isEnabled() && !Notifier.isToolbarHidden()) {
+ } else if (this.props.userHasGeneratedPassword) {
+ topBar = ;
+ } else if (Notifier.supportsDesktopNotifications() && !Notifier.isEnabled() && !Notifier.isToolbarHidden()) {
topBar = ;
}
diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index 0fe2d6a52f..26f89079f7 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -138,6 +138,9 @@ module.exports = React.createClass({
register_hs_url: null,
register_is_url: null,
register_id_sid: null,
+
+ // Initially, use localStorage as source of truth
+ userHasGeneratedPassword: localStorage && localStorage.getItem('mx_pass'),
};
return s;
},
@@ -569,7 +572,7 @@ module.exports = React.createClass({
this.setState({loggingIn: true});
break;
case 'on_logged_in':
- this._onLoggedIn(payload.teamToken);
+ this._onLoggedIn(payload.teamToken, payload.isPasswordStored);
break;
case 'on_logged_out':
this._onLoggedOut();
@@ -755,11 +758,15 @@ module.exports = React.createClass({
/**
* Called when a new logged in session has started
*/
- _onLoggedIn: function(teamToken) {
+ _onLoggedIn: function(teamToken, isPasswordStored) {
this.setState({
guestCreds: null,
loggedIn: true,
loggingIn: false,
+ // isPasswordStored only true when ROU sets a username and becomes PWLU.
+ // (the password was randomly generated and stored in localStorage).
+ userHasGeneratedPassword:
+ this.state.userHasGeneratedPassword || isPasswordStored,
});
if (teamToken) {
@@ -1168,6 +1175,7 @@ module.exports = React.createClass({
onUserSettingsClose={this.onUserSettingsClose}
onRegistered={this.onRegistered}
teamToken={this._teamToken}
+ userHasGeneratedPassword={this.state.userHasGeneratedPassword}
{...this.props}
{...this.state}
/>
From b944fff5c5f97193c55dc1255545179be820fcef Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@googlemail.com>
Date: Fri, 5 May 2017 20:57:18 +0100
Subject: [PATCH 0024/1016] unscrew merge
---
src/components/structures/UserSettings.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js
index 06103eae55..9d53bfda31 100644
--- a/src/components/structures/UserSettings.js
+++ b/src/components/structures/UserSettings.js
@@ -24,6 +24,7 @@ const dis = require("../../dispatcher");
const q = require('q');
const packageJson = require('../../../package.json');
const UserSettingsStore = require('../../UserSettingsStore');
+const CallMediaHandler = require('../../CallMediaHandler');
const GeminiScrollbar = require('react-gemini-scrollbar');
const Email = require('../../email');
const AddThreepid = require('../../AddThreepid');
From 78e72723440780814fced82cc8a2406c47992464 Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@googlemail.com>
Date: Sun, 7 May 2017 20:43:42 +0100
Subject: [PATCH 0025/1016] Fixes 2 issues with Dialog closing
+ Upload Confirmation dialog would just change focus on ESC and not close
+ Keywords Dialog in UserSettings would also close UserSettings because event bubbled up
---
src/components/views/dialogs/BaseDialog.js | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/src/components/views/dialogs/BaseDialog.js b/src/components/views/dialogs/BaseDialog.js
index 279dedbd43..d567a0ba9a 100644
--- a/src/components/views/dialogs/BaseDialog.js
+++ b/src/components/views/dialogs/BaseDialog.js
@@ -57,6 +57,12 @@ export default React.createClass({
}
},
+ // Don't let key down events get any further, so they only trigger this and nothing more
+ _onKeyDown: function(e) {
+ e.stopPropagation();
+ e.preventDefault();
+ },
+
// Must be when the key is released (and not pressed) otherwise componentWillUnmount
// will focus another element which will receive future key events
_onKeyUp: function(e) {
@@ -81,7 +87,7 @@ export default React.createClass({
const TintableSvg = sdk.getComponent("elements.TintableSvg");
return (
-
+
From 360f1cd250378a54d0305464111b2af57fb79731 Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@googlemail.com>
Date: Sun, 7 May 2017 20:57:54 +0100
Subject: [PATCH 0026/1016] completely missed the ESC check
I need sleep
---
src/components/views/dialogs/BaseDialog.js | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/components/views/dialogs/BaseDialog.js b/src/components/views/dialogs/BaseDialog.js
index d567a0ba9a..e7b8a687f5 100644
--- a/src/components/views/dialogs/BaseDialog.js
+++ b/src/components/views/dialogs/BaseDialog.js
@@ -59,8 +59,10 @@ export default React.createClass({
// Don't let key down events get any further, so they only trigger this and nothing more
_onKeyDown: function(e) {
- e.stopPropagation();
- e.preventDefault();
+ if (e.keyCode === KeyCode.ESCAPE) {
+ e.stopPropagation();
+ e.preventDefault();
+ }
},
// Must be when the key is released (and not pressed) otherwise componentWillUnmount
From bd32df4ef6dbf66f1ba5d9b23117fefbe2132c72 Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@googlemail.com>
Date: Sun, 7 May 2017 20:58:30 +0100
Subject: [PATCH 0027/1016] comment wording
---
src/components/views/dialogs/BaseDialog.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/views/dialogs/BaseDialog.js b/src/components/views/dialogs/BaseDialog.js
index e7b8a687f5..ac36dfd056 100644
--- a/src/components/views/dialogs/BaseDialog.js
+++ b/src/components/views/dialogs/BaseDialog.js
@@ -57,7 +57,7 @@ export default React.createClass({
}
},
- // Don't let key down events get any further, so they only trigger this and nothing more
+ // Don't let esc keydown events get any further, so they only trigger this and nothing more
_onKeyDown: function(e) {
if (e.keyCode === KeyCode.ESCAPE) {
e.stopPropagation();
From 39323647d13532993872ed870e1f68760e73bf73 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Mon, 8 May 2017 14:01:44 +0100
Subject: [PATCH 0028/1016] Don't show null URL previews
These are URLs that were spidered by the server without error but yielded an empty response from the server. There's nothing to display, so return an empty div.
---
src/components/views/rooms/LinkPreviewWidget.js | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/components/views/rooms/LinkPreviewWidget.js b/src/components/views/rooms/LinkPreviewWidget.js
index ef8fb29cbc..35e6d28b1f 100644
--- a/src/components/views/rooms/LinkPreviewWidget.js
+++ b/src/components/views/rooms/LinkPreviewWidget.js
@@ -100,7 +100,9 @@ module.exports = React.createClass({
render: function() {
var p = this.state.preview;
- if (!p) return
;
+ if (!p || Object.keys(p).length === 0) {
+ return
;
+ }
// FIXME: do we want to factor out all image displaying between this and MImageBody - especially for lightboxing?
var image = p["og:image"];
From d1f467ee914423e4ea640e8116cb8625c9ba70c8 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Mon, 8 May 2017 14:53:00 +0100
Subject: [PATCH 0029/1016] Remove auto-generated component-index
This will now be generated as part of the build process (`npm run build`) and whilst developing (`npm run start`).
---
src/component-index.js | 265 -----------------------------------------
1 file changed, 265 deletions(-)
delete mode 100644 src/component-index.js
diff --git a/src/component-index.js b/src/component-index.js
deleted file mode 100644
index 090a27d5ed..0000000000
--- a/src/component-index.js
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
-Copyright 2015, 2016 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-/*
- * THIS FILE IS AUTO-GENERATED
- * You can edit it you like, but your changes will be overwritten,
- * so you'd just be trying to swim upstream like a salmon.
- * You are not a salmon.
- *
- * To update it, run:
- * ./reskindex.js -h header
- */
-
-module.exports.components = {};
-import structures$ContextualMenu from './components/structures/ContextualMenu';
-structures$ContextualMenu && (module.exports.components['structures.ContextualMenu'] = structures$ContextualMenu);
-import structures$CreateRoom from './components/structures/CreateRoom';
-structures$CreateRoom && (module.exports.components['structures.CreateRoom'] = structures$CreateRoom);
-import structures$FilePanel from './components/structures/FilePanel';
-structures$FilePanel && (module.exports.components['structures.FilePanel'] = structures$FilePanel);
-import structures$InteractiveAuth from './components/structures/InteractiveAuth';
-structures$InteractiveAuth && (module.exports.components['structures.InteractiveAuth'] = structures$InteractiveAuth);
-import structures$LoggedInView from './components/structures/LoggedInView';
-structures$LoggedInView && (module.exports.components['structures.LoggedInView'] = structures$LoggedInView);
-import structures$MatrixChat from './components/structures/MatrixChat';
-structures$MatrixChat && (module.exports.components['structures.MatrixChat'] = structures$MatrixChat);
-import structures$MessagePanel from './components/structures/MessagePanel';
-structures$MessagePanel && (module.exports.components['structures.MessagePanel'] = structures$MessagePanel);
-import structures$NotificationPanel from './components/structures/NotificationPanel';
-structures$NotificationPanel && (module.exports.components['structures.NotificationPanel'] = structures$NotificationPanel);
-import structures$RoomStatusBar from './components/structures/RoomStatusBar';
-structures$RoomStatusBar && (module.exports.components['structures.RoomStatusBar'] = structures$RoomStatusBar);
-import structures$RoomView from './components/structures/RoomView';
-structures$RoomView && (module.exports.components['structures.RoomView'] = structures$RoomView);
-import structures$ScrollPanel from './components/structures/ScrollPanel';
-structures$ScrollPanel && (module.exports.components['structures.ScrollPanel'] = structures$ScrollPanel);
-import structures$TimelinePanel from './components/structures/TimelinePanel';
-structures$TimelinePanel && (module.exports.components['structures.TimelinePanel'] = structures$TimelinePanel);
-import structures$UploadBar from './components/structures/UploadBar';
-structures$UploadBar && (module.exports.components['structures.UploadBar'] = structures$UploadBar);
-import structures$UserSettings from './components/structures/UserSettings';
-structures$UserSettings && (module.exports.components['structures.UserSettings'] = structures$UserSettings);
-import structures$login$ForgotPassword from './components/structures/login/ForgotPassword';
-structures$login$ForgotPassword && (module.exports.components['structures.login.ForgotPassword'] = structures$login$ForgotPassword);
-import structures$login$Login from './components/structures/login/Login';
-structures$login$Login && (module.exports.components['structures.login.Login'] = structures$login$Login);
-import structures$login$PostRegistration from './components/structures/login/PostRegistration';
-structures$login$PostRegistration && (module.exports.components['structures.login.PostRegistration'] = structures$login$PostRegistration);
-import structures$login$Registration from './components/structures/login/Registration';
-structures$login$Registration && (module.exports.components['structures.login.Registration'] = structures$login$Registration);
-import views$avatars$BaseAvatar from './components/views/avatars/BaseAvatar';
-views$avatars$BaseAvatar && (module.exports.components['views.avatars.BaseAvatar'] = views$avatars$BaseAvatar);
-import views$avatars$MemberAvatar from './components/views/avatars/MemberAvatar';
-views$avatars$MemberAvatar && (module.exports.components['views.avatars.MemberAvatar'] = views$avatars$MemberAvatar);
-import views$avatars$RoomAvatar from './components/views/avatars/RoomAvatar';
-views$avatars$RoomAvatar && (module.exports.components['views.avatars.RoomAvatar'] = views$avatars$RoomAvatar);
-import views$create_room$CreateRoomButton from './components/views/create_room/CreateRoomButton';
-views$create_room$CreateRoomButton && (module.exports.components['views.create_room.CreateRoomButton'] = views$create_room$CreateRoomButton);
-import views$create_room$Presets from './components/views/create_room/Presets';
-views$create_room$Presets && (module.exports.components['views.create_room.Presets'] = views$create_room$Presets);
-import views$create_room$RoomAlias from './components/views/create_room/RoomAlias';
-views$create_room$RoomAlias && (module.exports.components['views.create_room.RoomAlias'] = views$create_room$RoomAlias);
-import views$dialogs$BaseDialog from './components/views/dialogs/BaseDialog';
-views$dialogs$BaseDialog && (module.exports.components['views.dialogs.BaseDialog'] = views$dialogs$BaseDialog);
-import views$dialogs$ChatCreateOrReuseDialog from './components/views/dialogs/ChatCreateOrReuseDialog';
-views$dialogs$ChatCreateOrReuseDialog && (module.exports.components['views.dialogs.ChatCreateOrReuseDialog'] = views$dialogs$ChatCreateOrReuseDialog);
-import views$dialogs$ChatInviteDialog from './components/views/dialogs/ChatInviteDialog';
-views$dialogs$ChatInviteDialog && (module.exports.components['views.dialogs.ChatInviteDialog'] = views$dialogs$ChatInviteDialog);
-import views$dialogs$ConfirmRedactDialog from './components/views/dialogs/ConfirmRedactDialog';
-views$dialogs$ConfirmRedactDialog && (module.exports.components['views.dialogs.ConfirmRedactDialog'] = views$dialogs$ConfirmRedactDialog);
-import views$dialogs$ConfirmUserActionDialog from './components/views/dialogs/ConfirmUserActionDialog';
-views$dialogs$ConfirmUserActionDialog && (module.exports.components['views.dialogs.ConfirmUserActionDialog'] = views$dialogs$ConfirmUserActionDialog);
-import views$dialogs$DeactivateAccountDialog from './components/views/dialogs/DeactivateAccountDialog';
-views$dialogs$DeactivateAccountDialog && (module.exports.components['views.dialogs.DeactivateAccountDialog'] = views$dialogs$DeactivateAccountDialog);
-import views$dialogs$ErrorDialog from './components/views/dialogs/ErrorDialog';
-views$dialogs$ErrorDialog && (module.exports.components['views.dialogs.ErrorDialog'] = views$dialogs$ErrorDialog);
-import views$dialogs$InteractiveAuthDialog from './components/views/dialogs/InteractiveAuthDialog';
-views$dialogs$InteractiveAuthDialog && (module.exports.components['views.dialogs.InteractiveAuthDialog'] = views$dialogs$InteractiveAuthDialog);
-import views$dialogs$NeedToRegisterDialog from './components/views/dialogs/NeedToRegisterDialog';
-views$dialogs$NeedToRegisterDialog && (module.exports.components['views.dialogs.NeedToRegisterDialog'] = views$dialogs$NeedToRegisterDialog);
-import views$dialogs$QuestionDialog from './components/views/dialogs/QuestionDialog';
-views$dialogs$QuestionDialog && (module.exports.components['views.dialogs.QuestionDialog'] = views$dialogs$QuestionDialog);
-import views$dialogs$SessionRestoreErrorDialog from './components/views/dialogs/SessionRestoreErrorDialog';
-views$dialogs$SessionRestoreErrorDialog && (module.exports.components['views.dialogs.SessionRestoreErrorDialog'] = views$dialogs$SessionRestoreErrorDialog);
-import views$dialogs$SetDisplayNameDialog from './components/views/dialogs/SetDisplayNameDialog';
-views$dialogs$SetDisplayNameDialog && (module.exports.components['views.dialogs.SetDisplayNameDialog'] = views$dialogs$SetDisplayNameDialog);
-import views$dialogs$TextInputDialog from './components/views/dialogs/TextInputDialog';
-views$dialogs$TextInputDialog && (module.exports.components['views.dialogs.TextInputDialog'] = views$dialogs$TextInputDialog);
-import views$dialogs$UnknownDeviceDialog from './components/views/dialogs/UnknownDeviceDialog';
-views$dialogs$UnknownDeviceDialog && (module.exports.components['views.dialogs.UnknownDeviceDialog'] = views$dialogs$UnknownDeviceDialog);
-import views$elements$AccessibleButton from './components/views/elements/AccessibleButton';
-views$elements$AccessibleButton && (module.exports.components['views.elements.AccessibleButton'] = views$elements$AccessibleButton);
-import views$elements$ActionButton from './components/views/elements/ActionButton';
-views$elements$ActionButton && (module.exports.components['views.elements.ActionButton'] = views$elements$ActionButton);
-import views$elements$AddressSelector from './components/views/elements/AddressSelector';
-views$elements$AddressSelector && (module.exports.components['views.elements.AddressSelector'] = views$elements$AddressSelector);
-import views$elements$AddressTile from './components/views/elements/AddressTile';
-views$elements$AddressTile && (module.exports.components['views.elements.AddressTile'] = views$elements$AddressTile);
-import views$elements$CreateRoomButton from './components/views/elements/CreateRoomButton';
-views$elements$CreateRoomButton && (module.exports.components['views.elements.CreateRoomButton'] = views$elements$CreateRoomButton);
-import views$elements$DeviceVerifyButtons from './components/views/elements/DeviceVerifyButtons';
-views$elements$DeviceVerifyButtons && (module.exports.components['views.elements.DeviceVerifyButtons'] = views$elements$DeviceVerifyButtons);
-import views$elements$DirectorySearchBox from './components/views/elements/DirectorySearchBox';
-views$elements$DirectorySearchBox && (module.exports.components['views.elements.DirectorySearchBox'] = views$elements$DirectorySearchBox);
-import views$elements$Dropdown from './components/views/elements/Dropdown';
-views$elements$Dropdown && (module.exports.components['views.elements.Dropdown'] = views$elements$Dropdown);
-import views$elements$EditableText from './components/views/elements/EditableText';
-views$elements$EditableText && (module.exports.components['views.elements.EditableText'] = views$elements$EditableText);
-import views$elements$EditableTextContainer from './components/views/elements/EditableTextContainer';
-views$elements$EditableTextContainer && (module.exports.components['views.elements.EditableTextContainer'] = views$elements$EditableTextContainer);
-import views$elements$EmojiText from './components/views/elements/EmojiText';
-views$elements$EmojiText && (module.exports.components['views.elements.EmojiText'] = views$elements$EmojiText);
-import views$elements$HomeButton from './components/views/elements/HomeButton';
-views$elements$HomeButton && (module.exports.components['views.elements.HomeButton'] = views$elements$HomeButton);
-import views$elements$MemberEventListSummary from './components/views/elements/MemberEventListSummary';
-views$elements$MemberEventListSummary && (module.exports.components['views.elements.MemberEventListSummary'] = views$elements$MemberEventListSummary);
-import views$elements$PowerSelector from './components/views/elements/PowerSelector';
-views$elements$PowerSelector && (module.exports.components['views.elements.PowerSelector'] = views$elements$PowerSelector);
-import views$elements$ProgressBar from './components/views/elements/ProgressBar';
-views$elements$ProgressBar && (module.exports.components['views.elements.ProgressBar'] = views$elements$ProgressBar);
-import views$elements$RoomDirectoryButton from './components/views/elements/RoomDirectoryButton';
-views$elements$RoomDirectoryButton && (module.exports.components['views.elements.RoomDirectoryButton'] = views$elements$RoomDirectoryButton);
-import views$elements$SettingsButton from './components/views/elements/SettingsButton';
-views$elements$SettingsButton && (module.exports.components['views.elements.SettingsButton'] = views$elements$SettingsButton);
-import views$elements$StartChatButton from './components/views/elements/StartChatButton';
-views$elements$StartChatButton && (module.exports.components['views.elements.StartChatButton'] = views$elements$StartChatButton);
-import views$elements$TintableSvg from './components/views/elements/TintableSvg';
-views$elements$TintableSvg && (module.exports.components['views.elements.TintableSvg'] = views$elements$TintableSvg);
-import views$elements$TruncatedList from './components/views/elements/TruncatedList';
-views$elements$TruncatedList && (module.exports.components['views.elements.TruncatedList'] = views$elements$TruncatedList);
-import views$elements$UserSelector from './components/views/elements/UserSelector';
-views$elements$UserSelector && (module.exports.components['views.elements.UserSelector'] = views$elements$UserSelector);
-import views$login$CaptchaForm from './components/views/login/CaptchaForm';
-views$login$CaptchaForm && (module.exports.components['views.login.CaptchaForm'] = views$login$CaptchaForm);
-import views$login$CasLogin from './components/views/login/CasLogin';
-views$login$CasLogin && (module.exports.components['views.login.CasLogin'] = views$login$CasLogin);
-import views$login$CountryDropdown from './components/views/login/CountryDropdown';
-views$login$CountryDropdown && (module.exports.components['views.login.CountryDropdown'] = views$login$CountryDropdown);
-import views$login$CustomServerDialog from './components/views/login/CustomServerDialog';
-views$login$CustomServerDialog && (module.exports.components['views.login.CustomServerDialog'] = views$login$CustomServerDialog);
-import views$login$InteractiveAuthEntryComponents from './components/views/login/InteractiveAuthEntryComponents';
-views$login$InteractiveAuthEntryComponents && (module.exports.components['views.login.InteractiveAuthEntryComponents'] = views$login$InteractiveAuthEntryComponents);
-import views$login$LoginFooter from './components/views/login/LoginFooter';
-views$login$LoginFooter && (module.exports.components['views.login.LoginFooter'] = views$login$LoginFooter);
-import views$login$LoginHeader from './components/views/login/LoginHeader';
-views$login$LoginHeader && (module.exports.components['views.login.LoginHeader'] = views$login$LoginHeader);
-import views$login$PasswordLogin from './components/views/login/PasswordLogin';
-views$login$PasswordLogin && (module.exports.components['views.login.PasswordLogin'] = views$login$PasswordLogin);
-import views$login$RegistrationForm from './components/views/login/RegistrationForm';
-views$login$RegistrationForm && (module.exports.components['views.login.RegistrationForm'] = views$login$RegistrationForm);
-import views$login$ServerConfig from './components/views/login/ServerConfig';
-views$login$ServerConfig && (module.exports.components['views.login.ServerConfig'] = views$login$ServerConfig);
-import views$messages$MAudioBody from './components/views/messages/MAudioBody';
-views$messages$MAudioBody && (module.exports.components['views.messages.MAudioBody'] = views$messages$MAudioBody);
-import views$messages$MFileBody from './components/views/messages/MFileBody';
-views$messages$MFileBody && (module.exports.components['views.messages.MFileBody'] = views$messages$MFileBody);
-import views$messages$MImageBody from './components/views/messages/MImageBody';
-views$messages$MImageBody && (module.exports.components['views.messages.MImageBody'] = views$messages$MImageBody);
-import views$messages$MVideoBody from './components/views/messages/MVideoBody';
-views$messages$MVideoBody && (module.exports.components['views.messages.MVideoBody'] = views$messages$MVideoBody);
-import views$messages$MessageEvent from './components/views/messages/MessageEvent';
-views$messages$MessageEvent && (module.exports.components['views.messages.MessageEvent'] = views$messages$MessageEvent);
-import views$messages$SenderProfile from './components/views/messages/SenderProfile';
-views$messages$SenderProfile && (module.exports.components['views.messages.SenderProfile'] = views$messages$SenderProfile);
-import views$messages$TextualBody from './components/views/messages/TextualBody';
-views$messages$TextualBody && (module.exports.components['views.messages.TextualBody'] = views$messages$TextualBody);
-import views$messages$TextualEvent from './components/views/messages/TextualEvent';
-views$messages$TextualEvent && (module.exports.components['views.messages.TextualEvent'] = views$messages$TextualEvent);
-import views$messages$UnknownBody from './components/views/messages/UnknownBody';
-views$messages$UnknownBody && (module.exports.components['views.messages.UnknownBody'] = views$messages$UnknownBody);
-import views$room_settings$AliasSettings from './components/views/room_settings/AliasSettings';
-views$room_settings$AliasSettings && (module.exports.components['views.room_settings.AliasSettings'] = views$room_settings$AliasSettings);
-import views$room_settings$ColorSettings from './components/views/room_settings/ColorSettings';
-views$room_settings$ColorSettings && (module.exports.components['views.room_settings.ColorSettings'] = views$room_settings$ColorSettings);
-import views$room_settings$UrlPreviewSettings from './components/views/room_settings/UrlPreviewSettings';
-views$room_settings$UrlPreviewSettings && (module.exports.components['views.room_settings.UrlPreviewSettings'] = views$room_settings$UrlPreviewSettings);
-import views$rooms$Autocomplete from './components/views/rooms/Autocomplete';
-views$rooms$Autocomplete && (module.exports.components['views.rooms.Autocomplete'] = views$rooms$Autocomplete);
-import views$rooms$AuxPanel from './components/views/rooms/AuxPanel';
-views$rooms$AuxPanel && (module.exports.components['views.rooms.AuxPanel'] = views$rooms$AuxPanel);
-import views$rooms$EntityTile from './components/views/rooms/EntityTile';
-views$rooms$EntityTile && (module.exports.components['views.rooms.EntityTile'] = views$rooms$EntityTile);
-import views$rooms$EventTile from './components/views/rooms/EventTile';
-views$rooms$EventTile && (module.exports.components['views.rooms.EventTile'] = views$rooms$EventTile);
-import views$rooms$LinkPreviewWidget from './components/views/rooms/LinkPreviewWidget';
-views$rooms$LinkPreviewWidget && (module.exports.components['views.rooms.LinkPreviewWidget'] = views$rooms$LinkPreviewWidget);
-import views$rooms$MemberDeviceInfo from './components/views/rooms/MemberDeviceInfo';
-views$rooms$MemberDeviceInfo && (module.exports.components['views.rooms.MemberDeviceInfo'] = views$rooms$MemberDeviceInfo);
-import views$rooms$MemberInfo from './components/views/rooms/MemberInfo';
-views$rooms$MemberInfo && (module.exports.components['views.rooms.MemberInfo'] = views$rooms$MemberInfo);
-import views$rooms$MemberList from './components/views/rooms/MemberList';
-views$rooms$MemberList && (module.exports.components['views.rooms.MemberList'] = views$rooms$MemberList);
-import views$rooms$MemberTile from './components/views/rooms/MemberTile';
-views$rooms$MemberTile && (module.exports.components['views.rooms.MemberTile'] = views$rooms$MemberTile);
-import views$rooms$MessageComposer from './components/views/rooms/MessageComposer';
-views$rooms$MessageComposer && (module.exports.components['views.rooms.MessageComposer'] = views$rooms$MessageComposer);
-import views$rooms$MessageComposerInput from './components/views/rooms/MessageComposerInput';
-views$rooms$MessageComposerInput && (module.exports.components['views.rooms.MessageComposerInput'] = views$rooms$MessageComposerInput);
-import views$rooms$MessageComposerInputOld from './components/views/rooms/MessageComposerInputOld';
-views$rooms$MessageComposerInputOld && (module.exports.components['views.rooms.MessageComposerInputOld'] = views$rooms$MessageComposerInputOld);
-import views$rooms$PresenceLabel from './components/views/rooms/PresenceLabel';
-views$rooms$PresenceLabel && (module.exports.components['views.rooms.PresenceLabel'] = views$rooms$PresenceLabel);
-import views$rooms$ReadReceiptMarker from './components/views/rooms/ReadReceiptMarker';
-views$rooms$ReadReceiptMarker && (module.exports.components['views.rooms.ReadReceiptMarker'] = views$rooms$ReadReceiptMarker);
-import views$rooms$RoomHeader from './components/views/rooms/RoomHeader';
-views$rooms$RoomHeader && (module.exports.components['views.rooms.RoomHeader'] = views$rooms$RoomHeader);
-import views$rooms$RoomList from './components/views/rooms/RoomList';
-views$rooms$RoomList && (module.exports.components['views.rooms.RoomList'] = views$rooms$RoomList);
-import views$rooms$RoomNameEditor from './components/views/rooms/RoomNameEditor';
-views$rooms$RoomNameEditor && (module.exports.components['views.rooms.RoomNameEditor'] = views$rooms$RoomNameEditor);
-import views$rooms$RoomPreviewBar from './components/views/rooms/RoomPreviewBar';
-views$rooms$RoomPreviewBar && (module.exports.components['views.rooms.RoomPreviewBar'] = views$rooms$RoomPreviewBar);
-import views$rooms$RoomSettings from './components/views/rooms/RoomSettings';
-views$rooms$RoomSettings && (module.exports.components['views.rooms.RoomSettings'] = views$rooms$RoomSettings);
-import views$rooms$RoomTile from './components/views/rooms/RoomTile';
-views$rooms$RoomTile && (module.exports.components['views.rooms.RoomTile'] = views$rooms$RoomTile);
-import views$rooms$RoomTopicEditor from './components/views/rooms/RoomTopicEditor';
-views$rooms$RoomTopicEditor && (module.exports.components['views.rooms.RoomTopicEditor'] = views$rooms$RoomTopicEditor);
-import views$rooms$SearchResultTile from './components/views/rooms/SearchResultTile';
-views$rooms$SearchResultTile && (module.exports.components['views.rooms.SearchResultTile'] = views$rooms$SearchResultTile);
-import views$rooms$SearchableEntityList from './components/views/rooms/SearchableEntityList';
-views$rooms$SearchableEntityList && (module.exports.components['views.rooms.SearchableEntityList'] = views$rooms$SearchableEntityList);
-import views$rooms$SimpleRoomHeader from './components/views/rooms/SimpleRoomHeader';
-views$rooms$SimpleRoomHeader && (module.exports.components['views.rooms.SimpleRoomHeader'] = views$rooms$SimpleRoomHeader);
-import views$rooms$TabCompleteBar from './components/views/rooms/TabCompleteBar';
-views$rooms$TabCompleteBar && (module.exports.components['views.rooms.TabCompleteBar'] = views$rooms$TabCompleteBar);
-import views$rooms$TopUnreadMessagesBar from './components/views/rooms/TopUnreadMessagesBar';
-views$rooms$TopUnreadMessagesBar && (module.exports.components['views.rooms.TopUnreadMessagesBar'] = views$rooms$TopUnreadMessagesBar);
-import views$rooms$UserTile from './components/views/rooms/UserTile';
-views$rooms$UserTile && (module.exports.components['views.rooms.UserTile'] = views$rooms$UserTile);
-import views$settings$AddPhoneNumber from './components/views/settings/AddPhoneNumber';
-views$settings$AddPhoneNumber && (module.exports.components['views.settings.AddPhoneNumber'] = views$settings$AddPhoneNumber);
-import views$settings$ChangeAvatar from './components/views/settings/ChangeAvatar';
-views$settings$ChangeAvatar && (module.exports.components['views.settings.ChangeAvatar'] = views$settings$ChangeAvatar);
-import views$settings$ChangeDisplayName from './components/views/settings/ChangeDisplayName';
-views$settings$ChangeDisplayName && (module.exports.components['views.settings.ChangeDisplayName'] = views$settings$ChangeDisplayName);
-import views$settings$ChangePassword from './components/views/settings/ChangePassword';
-views$settings$ChangePassword && (module.exports.components['views.settings.ChangePassword'] = views$settings$ChangePassword);
-import views$settings$DevicesPanel from './components/views/settings/DevicesPanel';
-views$settings$DevicesPanel && (module.exports.components['views.settings.DevicesPanel'] = views$settings$DevicesPanel);
-import views$settings$DevicesPanelEntry from './components/views/settings/DevicesPanelEntry';
-views$settings$DevicesPanelEntry && (module.exports.components['views.settings.DevicesPanelEntry'] = views$settings$DevicesPanelEntry);
-import views$settings$EnableNotificationsButton from './components/views/settings/EnableNotificationsButton';
-views$settings$EnableNotificationsButton && (module.exports.components['views.settings.EnableNotificationsButton'] = views$settings$EnableNotificationsButton);
-import views$voip$CallView from './components/views/voip/CallView';
-views$voip$CallView && (module.exports.components['views.voip.CallView'] = views$voip$CallView);
-import views$voip$IncomingCallBox from './components/views/voip/IncomingCallBox';
-views$voip$IncomingCallBox && (module.exports.components['views.voip.IncomingCallBox'] = views$voip$IncomingCallBox);
-import views$voip$VideoFeed from './components/views/voip/VideoFeed';
-views$voip$VideoFeed && (module.exports.components['views.voip.VideoFeed'] = views$voip$VideoFeed);
-import views$voip$VideoView from './components/views/voip/VideoView';
-views$voip$VideoView && (module.exports.components['views.voip.VideoView'] = views$voip$VideoView);
From ca7989a1fdd0d9b69f29bcad06ebf7580d68bd65 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Mon, 8 May 2017 14:55:55 +0100
Subject: [PATCH 0030/1016] .gitignore auto-generated component-index
---
.gitignore | 3 +++
1 file changed, 3 insertions(+)
diff --git a/.gitignore b/.gitignore
index 5139d614ad..dcfe1c355d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,3 +9,6 @@ npm-debug.log
# test reports created by karma
/karma-reports
+
+# ignore auto-generated component index
+/src/component-index.js
From 1addc7e2c573394696a0a1e1cda675cb3a2c0b62 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Mon, 8 May 2017 14:56:38 +0100
Subject: [PATCH 0031/1016] Update header copyright
---
header | 1 +
1 file changed, 1 insertion(+)
diff --git a/header b/header
index 060709b82e..beee1ebe89 100644
--- a/header
+++ b/header
@@ -1,5 +1,6 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
+Copyright 2017 Vector Creations Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
From 2eaaa974516ce224602b0295f937549de1bb1b8e Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Mon, 8 May 2017 14:57:04 +0100
Subject: [PATCH 0032/1016] Give `reskindex.js` a watch mode (-w)
`scripts/reskindex.js -w` will run reskindex in watch mode whereby FS events will cause a reskindex to occur.
This depends on `chokidar`
---
package.json | 1 +
scripts/reskindex.js | 92 +++++++++++++++++++++++++-------------------
2 files changed, 54 insertions(+), 39 deletions(-)
diff --git a/package.json b/package.json
index 2001f0d4ad..b019c63da5 100644
--- a/package.json
+++ b/package.json
@@ -46,6 +46,7 @@
"blueimp-canvas-to-blob": "^3.5.0",
"browser-encrypt-attachment": "^0.3.0",
"browser-request": "^0.3.3",
+ "chokidar": "^1.6.1",
"classnames": "^2.1.2",
"commonmark": "^0.27.0",
"draft-js": "^0.8.1",
diff --git a/scripts/reskindex.js b/scripts/reskindex.js
index f9cbc2a711..e82104f35c 100755
--- a/scripts/reskindex.js
+++ b/scripts/reskindex.js
@@ -1,53 +1,67 @@
#!/usr/bin/env node
-
var fs = require('fs');
var path = require('path');
var glob = require('glob');
-
var args = require('optimist').argv;
-
-var header = args.h || args.header;
-
-var componentsDir = path.join('src', 'components');
+var chokidar = require('chokidar');
var componentIndex = path.join('src', 'component-index.js');
+var componentsDir = path.join('src', 'components');
-var packageJson = JSON.parse(fs.readFileSync('./package.json'));
+function reskindex() {
+ var header = args.h || args.header;
+ var packageJson = JSON.parse(fs.readFileSync('./package.json'));
-var strm = fs.createWriteStream(componentIndex);
+ var strm = fs.createWriteStream(componentIndex);
-if (header) {
- strm.write(fs.readFileSync(header));
- strm.write('\n');
+ if (header) {
+ strm.write(fs.readFileSync(header));
+ strm.write('\n');
+ }
+
+ strm.write("/*\n");
+ strm.write(" * THIS FILE IS AUTO-GENERATED\n");
+ strm.write(" * You can edit it you like, but your changes will be overwritten,\n");
+ strm.write(" * so you'd just be trying to swim upstream like a salmon.\n");
+ strm.write(" * You are not a salmon.\n");
+ strm.write(" */\n\n");
+
+ if (packageJson['matrix-react-parent']) {
+ strm.write(
+ "module.exports.components = require('"+
+ packageJson['matrix-react-parent']+
+ "/lib/component-index').components;\n\n"
+ );
+ } else {
+ strm.write("module.exports.components = {};\n");
+ }
+
+ var files = glob.sync('**/*.js', {cwd: componentsDir}).sort();
+ for (var i = 0; i < files.length; ++i) {
+ var file = files[i].replace('.js', '');
+
+ var moduleName = (file.replace(/\//g, '.'));
+ var importName = moduleName.replace(/\./g, "$");
+
+ strm.write("import " + importName + " from './components/" + file + "';\n");
+ strm.write(importName + " && (module.exports.components['"+moduleName+"'] = " + importName + ");");
+ strm.write('\n');
+ strm.uncork();
+ }
+
+ strm.end();
+ console.log('Reskindex: completed');
}
-strm.write("/*\n");
-strm.write(" * THIS FILE IS AUTO-GENERATED\n");
-strm.write(" * You can edit it you like, but your changes will be overwritten,\n");
-strm.write(" * so you'd just be trying to swim upstream like a salmon.\n");
-strm.write(" * You are not a salmon.\n");
-strm.write(" *\n");
-strm.write(" * To update it, run:\n");
-strm.write(" * ./reskindex.js -h header\n");
-strm.write(" */\n\n");
-
-if (packageJson['matrix-react-parent']) {
- strm.write("module.exports.components = require('"+packageJson['matrix-react-parent']+"/lib/component-index').components;\n\n");
-} else {
- strm.write("module.exports.components = {};\n");
+// -w indicates watch mode where any FS events will trigger reskindex
+if (!args.w) {
+ reskindex();
+ return;
}
-var files = glob.sync('**/*.js', {cwd: componentsDir}).sort();
-for (var i = 0; i < files.length; ++i) {
- var file = files[i].replace('.js', '');
-
- var moduleName = (file.replace(/\//g, '.'));
- var importName = moduleName.replace(/\./g, "$");
-
- strm.write("import " + importName + " from './components/" + file + "';\n");
- strm.write(importName + " && (module.exports.components['"+moduleName+"'] = " + importName + ");");
- strm.write('\n');
- strm.uncork();
-}
-
-strm.end();
+var watchDebouncer = null;
+chokidar.watch('./src').on('all', (event, path) => {
+ if (path === componentIndex) return;
+ if (watchDebouncer) clearTimeout(watchDebouncer);
+ watchDebouncer = setTimeout(reskindex, 1000);
+});
From 7d1940620db557203887b710292c23cfff2a910e Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Mon, 8 May 2017 15:07:58 +0100
Subject: [PATCH 0033/1016] Add (watching) reskindex to `npm start`
also add reskindex in non-watching mode to `npm run build`
---
package.json | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/package.json b/package.json
index b019c63da5..28f77f9460 100644
--- a/package.json
+++ b/package.json
@@ -32,8 +32,10 @@
},
"scripts": {
"reskindex": "scripts/reskindex.js -h header",
- "build": "babel src -d lib --source-maps",
- "start": "babel src -w -d lib --source-maps",
+ "reskindex:watch": "scripts/reskindex.js -h header -w",
+ "build": "npm run reskindex && babel src -d lib --source-maps",
+ "build:watch": "babel src -w -d lib --source-maps",
+ "start": "parallelshell \"npm run build:watch\" \"npm run reskindex:watch\"",
"lint": "eslint src/",
"lintall": "eslint src/ test/",
"clean": "rimraf lib",
@@ -106,6 +108,7 @@
"karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "^1.7.0",
"mocha": "^2.4.5",
+ "parallelshell": "^1.2.0",
"phantomjs-prebuilt": "^2.1.7",
"react-addons-test-utils": "^15.4.0",
"require-json": "0.0.1",
From 534f9277d436e66725a7f8875d88d78f7cf59f02 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Mon, 8 May 2017 15:37:40 +0100
Subject: [PATCH 0034/1016] Fix this/self fail in LeftPanel
---
src/components/views/dialogs/ChatInviteDialog.js | 5 +++++
src/components/views/rooms/RoomList.js | 14 +++++++-------
2 files changed, 12 insertions(+), 7 deletions(-)
diff --git a/src/components/views/dialogs/ChatInviteDialog.js b/src/components/views/dialogs/ChatInviteDialog.js
index 7ba503099a..b349b94e5d 100644
--- a/src/components/views/dialogs/ChatInviteDialog.js
+++ b/src/components/views/dialogs/ChatInviteDialog.js
@@ -191,6 +191,7 @@ module.exports = React.createClass({
this.queryChangedDebouncer = setTimeout(() => {
// Only do search if there is something to search
if (query.length > 0 && query != '@') {
+ performance.mark('start');
// Weighted keys prefer to match userIds when first char is @
this._fuse.options.keys = [{
name: 'displayName',
@@ -199,6 +200,7 @@ module.exports = React.createClass({
name: 'userId',
weight: query[0] === '@' ? 0.9 : 0.1,
}];
+ performance.mark('middle');
queryList = this._fuse.search(query).map((user) => {
// Return objects, structure of which is defined
// by InviteAddressType
@@ -210,6 +212,9 @@ module.exports = React.createClass({
isKnown: true,
}
});
+ performance.mark('end');
+ performance.measure('setopts', 'start', 'middle');
+ performance.measure('search', 'middle', 'end');
// If the query is a valid address, add an entry for that
// This is important, otherwise there's no way to invite
diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js
index 8d396b5536..a595a91ba9 100644
--- a/src/components/views/rooms/RoomList.js
+++ b/src/components/views/rooms/RoomList.js
@@ -652,7 +652,7 @@ module.exports = React.createClass({
Date: Mon, 8 May 2017 16:03:52 +0100
Subject: [PATCH 0035/1016] Fix Create Room button
Opened the DM dialog rather than the new room dialog
---
src/components/views/elements/CreateRoomButton.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/views/elements/CreateRoomButton.js b/src/components/views/elements/CreateRoomButton.js
index e7e526d36b..73c984a860 100644
--- a/src/components/views/elements/CreateRoomButton.js
+++ b/src/components/views/elements/CreateRoomButton.js
@@ -21,7 +21,7 @@ import PropTypes from 'prop-types';
const CreateRoomButton = function(props) {
const ActionButton = sdk.getComponent('elements.ActionButton');
return (
-
Date: Mon, 8 May 2017 16:24:13 +0100
Subject: [PATCH 0036/1016] Move chokidar to devDeps
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 28f77f9460..4b0e8de5cd 100644
--- a/package.json
+++ b/package.json
@@ -48,7 +48,6 @@
"blueimp-canvas-to-blob": "^3.5.0",
"browser-encrypt-attachment": "^0.3.0",
"browser-request": "^0.3.3",
- "chokidar": "^1.6.1",
"classnames": "^2.1.2",
"commonmark": "^0.27.0",
"draft-js": "^0.8.1",
@@ -92,6 +91,7 @@
"babel-preset-es2016": "^6.11.3",
"babel-preset-es2017": "^6.14.0",
"babel-preset-react": "^6.11.1",
+ "chokidar": "^1.6.1",
"eslint": "^3.13.1",
"eslint-config-google": "^0.7.1",
"eslint-plugin-babel": "^4.0.1",
From 9af9603373ea9a3459170fc02413ae74200a04da Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Mon, 8 May 2017 16:29:53 +0100
Subject: [PATCH 0037/1016] Only watch indexed files
---
scripts/reskindex.js | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/scripts/reskindex.js b/scripts/reskindex.js
index e82104f35c..1db22f9e10 100755
--- a/scripts/reskindex.js
+++ b/scripts/reskindex.js
@@ -7,6 +7,7 @@ var chokidar = require('chokidar');
var componentIndex = path.join('src', 'component-index.js');
var componentsDir = path.join('src', 'components');
+var componentGlob = '**/*.js';
function reskindex() {
var header = args.h || args.header;
@@ -36,7 +37,7 @@ function reskindex() {
strm.write("module.exports.components = {};\n");
}
- var files = glob.sync('**/*.js', {cwd: componentsDir}).sort();
+ var files = glob.sync(componentGlob, {cwd: componentsDir}).sort();
for (var i = 0; i < files.length; ++i) {
var file = files[i].replace('.js', '');
@@ -60,7 +61,7 @@ if (!args.w) {
}
var watchDebouncer = null;
-chokidar.watch('./src').on('all', (event, path) => {
+chokidar.watch(path.join(componentsDir, componentGlob)).on('all', (event, path) => {
if (path === componentIndex) return;
if (watchDebouncer) clearTimeout(watchDebouncer);
watchDebouncer = setTimeout(reskindex, 1000);
From 805354bd2c3d2e6233c55833f561d489d8b40445 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Mon, 8 May 2017 16:39:11 +0100
Subject: [PATCH 0038/1016] Revert unintentional change
---
src/components/views/dialogs/ChatInviteDialog.js | 5 -----
1 file changed, 5 deletions(-)
diff --git a/src/components/views/dialogs/ChatInviteDialog.js b/src/components/views/dialogs/ChatInviteDialog.js
index b349b94e5d..7ba503099a 100644
--- a/src/components/views/dialogs/ChatInviteDialog.js
+++ b/src/components/views/dialogs/ChatInviteDialog.js
@@ -191,7 +191,6 @@ module.exports = React.createClass({
this.queryChangedDebouncer = setTimeout(() => {
// Only do search if there is something to search
if (query.length > 0 && query != '@') {
- performance.mark('start');
// Weighted keys prefer to match userIds when first char is @
this._fuse.options.keys = [{
name: 'displayName',
@@ -200,7 +199,6 @@ module.exports = React.createClass({
name: 'userId',
weight: query[0] === '@' ? 0.9 : 0.1,
}];
- performance.mark('middle');
queryList = this._fuse.search(query).map((user) => {
// Return objects, structure of which is defined
// by InviteAddressType
@@ -212,9 +210,6 @@ module.exports = React.createClass({
isKnown: true,
}
});
- performance.mark('end');
- performance.measure('setopts', 'start', 'middle');
- performance.measure('search', 'middle', 'end');
// If the query is a valid address, add an entry for that
// This is important, otherwise there's no way to invite
From 85ed39b9d87214a069af6c6ab2b642b6ff8b3386 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Mon, 8 May 2017 16:49:40 +0100
Subject: [PATCH 0039/1016] Put room name in 'leave room' confirmation dialog
https://github.com/vector-im/riot-web/issues/3850
---
src/components/structures/MatrixChat.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index 9b8aa3426a..8865d77d51 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -392,9 +392,10 @@ module.exports = React.createClass({
this.notifyNewScreen('forgot_password');
break;
case 'leave_room':
+ const roomToLeave = MatrixClientPeg.get().getRoom(payload.room_id);
Modal.createDialog(QuestionDialog, {
title: "Leave room",
- description: "Are you sure you want to leave the room?",
+ description: Are you sure you want to leave the room {roomToLeave.name} ? ,
onFinished: (should_leave) => {
if (should_leave) {
const d = MatrixClientPeg.get().leave(payload.room_id);
From 488fa3745b567cdff57041b1f81352c484aef0ec Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Tue, 9 May 2017 10:03:40 +0100
Subject: [PATCH 0040/1016] Fix RM not updating if RR event unpaginated
If the RR event has been unpaginated, the logic in `sendReadReceipt` will now fallback on the event ID of the RM which in theory is always =< RR event ID stream-wise.
---
src/components/structures/ScrollPanel.js | 2 +-
src/components/structures/TimelinePanel.js | 10 +++++++++-
2 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/src/components/structures/ScrollPanel.js b/src/components/structures/ScrollPanel.js
index a652bcc827..22eb2240ed 100644
--- a/src/components/structures/ScrollPanel.js
+++ b/src/components/structures/ScrollPanel.js
@@ -575,7 +575,7 @@ module.exports = React.createClass({
var boundingRect = node.getBoundingClientRect();
var scrollDelta = boundingRect.bottom + pixelOffset - wrapperRect.bottom;
- debuglog("ScrollPanel: scrolling to token '" + scrollToken + "'+" +
+ console.log("ScrollPanel: scrolling to token '" + scrollToken + "'+" +
pixelOffset + " (delta: "+scrollDelta+")");
if(scrollDelta != 0) {
diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js
index 7c89694a29..d85282b5dc 100644
--- a/src/components/structures/TimelinePanel.js
+++ b/src/components/structures/TimelinePanel.js
@@ -510,8 +510,10 @@ var TimelinePanel = React.createClass({
var currentReadUpToEventId = this._getCurrentReadReceipt(true);
var currentReadUpToEventIndex = this._indexForEventId(currentReadUpToEventId);
+ currentReadUpToEventIndex = currentReadUpToEventIndex ||
+ this._indexForEventId(this.state.readMarkerEventId);
// We want to avoid sending out read receipts when we are looking at
- // events in the past which are before the latest RR.
+ // events in the past which are before the latest RR/RM.
//
// For now, let's apply a heuristic: if (a) the event corresponding to
// the latest RR (either from the server, or sent by ourselves) doesn't
@@ -523,6 +525,7 @@ var TimelinePanel = React.createClass({
// RRs) - but that is a bit of a niche case. It will sort itself out when
// the user eventually hits the live timeline.
//
+ console.log(currentReadUpToEventId, currentReadUpToEventIndex, this._timelineWindow.canPaginate(EventTimeline.FORWARDS));
if (currentReadUpToEventId && currentReadUpToEventIndex === null &&
this._timelineWindow.canPaginate(EventTimeline.FORWARDS)) {
return;
@@ -544,6 +547,11 @@ var TimelinePanel = React.createClass({
this.last_rr_sent_event_id = lastReadEvent.getId();
this.last_rm_sent_event_id = this.state.readMarkerEventId;
+ console.log('TimelinePanel: Sending Read Markers for ',
+ this.props.timelineSet.room.roomId,
+ 'rm', this.state.readMarkerEventId,
+ 'rr', lastReadEvent.getId(),
+ );
MatrixClientPeg.get().setRoomReadMarkers(
this.props.timelineSet.room.roomId,
this.state.readMarkerEventId,
From ac25fd6d87214e962897667d7abdd8b766cef2b9 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Tue, 9 May 2017 10:16:37 +0100
Subject: [PATCH 0041/1016] Remove log
---
src/components/structures/TimelinePanel.js | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js
index d85282b5dc..ba233780ec 100644
--- a/src/components/structures/TimelinePanel.js
+++ b/src/components/structures/TimelinePanel.js
@@ -525,7 +525,6 @@ var TimelinePanel = React.createClass({
// RRs) - but that is a bit of a niche case. It will sort itself out when
// the user eventually hits the live timeline.
//
- console.log(currentReadUpToEventId, currentReadUpToEventIndex, this._timelineWindow.canPaginate(EventTimeline.FORWARDS));
if (currentReadUpToEventId && currentReadUpToEventIndex === null &&
this._timelineWindow.canPaginate(EventTimeline.FORWARDS)) {
return;
From f02e659fb7502ebb524651494e9c9bd1098fcc9b Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@googlemail.com>
Date: Tue, 9 May 2017 11:27:06 +0100
Subject: [PATCH 0042/1016] Consume key{up,down,pressed} events
so they don't trigger other things bubbling up
until Modal is closed
---
src/components/views/dialogs/BaseDialog.js | 19 ++++++++++---------
1 file changed, 10 insertions(+), 9 deletions(-)
diff --git a/src/components/views/dialogs/BaseDialog.js b/src/components/views/dialogs/BaseDialog.js
index ac36dfd056..02460148b3 100644
--- a/src/components/views/dialogs/BaseDialog.js
+++ b/src/components/views/dialogs/BaseDialog.js
@@ -57,28 +57,25 @@ export default React.createClass({
}
},
- // Don't let esc keydown events get any further, so they only trigger this and nothing more
- _onKeyDown: function(e) {
- if (e.keyCode === KeyCode.ESCAPE) {
- e.stopPropagation();
- e.preventDefault();
- }
+ // Don't let key{down,press} events escape the modal. Consume them all.
+ _eatKeyEvent: function(e) {
+ e.stopPropagation();
},
// Must be when the key is released (and not pressed) otherwise componentWillUnmount
// will focus another element which will receive future key events
_onKeyUp: function(e) {
if (e.keyCode === KeyCode.ESCAPE) {
- e.stopPropagation();
e.preventDefault();
this.props.onFinished();
} else if (e.keyCode === KeyCode.ENTER) {
if (this.props.onEnterPressed) {
- e.stopPropagation();
e.preventDefault();
this.props.onEnterPressed(e);
}
}
+ // Consume all keyup events while Modal is open
+ e.stopPropagation();
},
_onCancelClick: function(e) {
@@ -89,7 +86,11 @@ export default React.createClass({
const TintableSvg = sdk.getComponent("elements.TintableSvg");
return (
-
+
From fdf48def00d336dd55ee189d21ea854cc72fd1d8 Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@googlemail.com>
Date: Tue, 9 May 2017 17:13:27 +0100
Subject: [PATCH 0043/1016] make reskindex windows friendly
makes #871 windows friendly
---
package.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/package.json b/package.json
index 4b0e8de5cd..21add8ccb7 100644
--- a/package.json
+++ b/package.json
@@ -31,8 +31,8 @@
"reskindex": "scripts/reskindex.js"
},
"scripts": {
- "reskindex": "scripts/reskindex.js -h header",
- "reskindex:watch": "scripts/reskindex.js -h header -w",
+ "reskindex": "node scripts/reskindex.js -h header",
+ "reskindex:watch": "node scripts/reskindex.js -h header -w",
"build": "npm run reskindex && babel src -d lib --source-maps",
"build:watch": "babel src -w -d lib --source-maps",
"start": "parallelshell \"npm run build:watch\" \"npm run reskindex:watch\"",
From ca79d9bb6eed092b7b6ca7c7f89ae345624f4047 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Tue, 9 May 2017 17:36:19 +0100
Subject: [PATCH 0044/1016] Separate predicates for RM/RR
Instead of modifying the condition for updating the RR, separate the RM and RR conditions and use an OR to decide when to set both.
Make some logs only log when DEBUG.
---
src/components/structures/ScrollPanel.js | 2 +-
src/components/structures/TimelinePanel.js | 27 ++++++++++++----------
2 files changed, 16 insertions(+), 13 deletions(-)
diff --git a/src/components/structures/ScrollPanel.js b/src/components/structures/ScrollPanel.js
index 22eb2240ed..a652bcc827 100644
--- a/src/components/structures/ScrollPanel.js
+++ b/src/components/structures/ScrollPanel.js
@@ -575,7 +575,7 @@ module.exports = React.createClass({
var boundingRect = node.getBoundingClientRect();
var scrollDelta = boundingRect.bottom + pixelOffset - wrapperRect.bottom;
- console.log("ScrollPanel: scrolling to token '" + scrollToken + "'+" +
+ debuglog("ScrollPanel: scrolling to token '" + scrollToken + "'+" +
pixelOffset + " (delta: "+scrollDelta+")");
if(scrollDelta != 0) {
diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js
index ba233780ec..973c619904 100644
--- a/src/components/structures/TimelinePanel.js
+++ b/src/components/structures/TimelinePanel.js
@@ -504,16 +504,15 @@ var TimelinePanel = React.createClass({
// very possible have logged out within that timeframe, so check
// we still have a client.
const cli = MatrixClientPeg.get();
- // if no client or client is guest don't send RR
+ // if no client or client is guest don't send RR or RM
if (!cli || cli.isGuest()) return;
+ let shouldSendRR = true;
+
var currentReadUpToEventId = this._getCurrentReadReceipt(true);
var currentReadUpToEventIndex = this._indexForEventId(currentReadUpToEventId);
-
- currentReadUpToEventIndex = currentReadUpToEventIndex ||
- this._indexForEventId(this.state.readMarkerEventId);
// We want to avoid sending out read receipts when we are looking at
- // events in the past which are before the latest RR/RM.
+ // events in the past which are before the latest RR.
//
// For now, let's apply a heuristic: if (a) the event corresponding to
// the latest RR (either from the server, or sent by ourselves) doesn't
@@ -527,26 +526,30 @@ var TimelinePanel = React.createClass({
//
if (currentReadUpToEventId && currentReadUpToEventIndex === null &&
this._timelineWindow.canPaginate(EventTimeline.FORWARDS)) {
- return;
+ shouldSendRR = false;
}
var lastReadEventIndex = this._getLastDisplayedEventIndex({
ignoreOwn: true
});
- if (lastReadEventIndex === null) return;
+ if (lastReadEventIndex === null) {
+ shouldSendRR = false;
+ }
var lastReadEvent = this.state.events[lastReadEventIndex];
+ shouldSendRR = shouldSendRR &&
+ (lastReadEventIndex > currentReadUpToEventIndex &&
+ this.last_rr_sent_event_id != lastReadEvent.getId());
+
+ const shouldSendRM = this.last_rm_sent_event_id != this.state.readMarkerEventId;
// we also remember the last read receipt we sent to avoid spamming the
// same one at the server repeatedly
- if ((lastReadEventIndex > currentReadUpToEventIndex &&
- this.last_rr_sent_event_id != lastReadEvent.getId()) ||
- this.last_rm_sent_event_id != this.state.readMarkerEventId) {
-
+ if (shouldSendRR || shouldSendRM) {
this.last_rr_sent_event_id = lastReadEvent.getId();
this.last_rm_sent_event_id = this.state.readMarkerEventId;
- console.log('TimelinePanel: Sending Read Markers for ',
+ debuglog('TimelinePanel: Sending Read Markers for ',
this.props.timelineSet.room.roomId,
'rm', this.state.readMarkerEventId,
'rr', lastReadEvent.getId(),
From ad2ed129800bbcc79544c66426f2fd70a5708dfa Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Wed, 10 May 2017 14:22:17 +0100
Subject: [PATCH 0045/1016] Redesign mxID chooser, add availability checking
Requires https://github.com/matrix-org/matrix-js-sdk/pull/432 for availability checking.
Changes:
- Redesign the dialog to look more like https://github.com/vector-im/riot-web/issues/3604#issuecomment-299226875
- Attempt to fix wrong password being stored by generating one per SetMxIdDialog (there's no issue tracking this for now, I shall open one if it persists)
- Backwards compatible with servers that don't support register/availability - a spinner will appear the first time a username is checked because server support can only be determined after a request.
- Rate-limited by a 2s debounce
- General style improvements
---
src/components/structures/RoomView.js | 11 +-
src/components/views/dialogs/SetMxIdDialog.js | 154 +++++++++++++++---
2 files changed, 137 insertions(+), 28 deletions(-)
diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js
index 848a8cc7ba..710f333322 100644
--- a/src/components/structures/RoomView.js
+++ b/src/components/structures/RoomView.js
@@ -775,7 +775,8 @@ module.exports = React.createClass({
const SetMxIdDialog = sdk.getComponent('views.dialogs.SetMxIdDialog');
const defered = q.defer();
mxIdPromise = defered.promise;
- Modal.createDialog(SetMxIdDialog, {
+ const close = Modal.createDialog(SetMxIdDialog, {
+ homeserverUrl: cli.getHomeserverUrl(),
onFinished: (submitted, credentials) => {
if (!submitted) {
defered.reject();
@@ -783,8 +784,12 @@ module.exports = React.createClass({
}
this.props.onRegistered(credentials);
defered.resolve();
- }
- });
+ },
+ onDifferentServerClicked: (ev) => {
+ dis.dispatch({action: 'start_registration'});
+ close();
+ },
+ }).close;
}
mxIdPromise.then(() => {
diff --git a/src/components/views/dialogs/SetMxIdDialog.js b/src/components/views/dialogs/SetMxIdDialog.js
index d139a4ae3b..445b7eb77f 100644
--- a/src/components/views/dialogs/SetMxIdDialog.js
+++ b/src/components/views/dialogs/SetMxIdDialog.js
@@ -19,6 +19,11 @@ import q from 'q';
import React from 'react';
import sdk from '../../../index';
import MatrixClientPeg from '../../../MatrixClientPeg';
+import classnames from 'classnames';
+
+// The amount of time to wait for further changes to the input username before
+// sending a request to the server
+const USERNAME_CHECK_DEBOUNCE_MS = 2000;
/**
* Prompt the user to set a display name.
@@ -33,9 +38,20 @@ export default React.createClass({
getInitialState: function() {
return {
- username : '',
+ // The entered username
+ username: '',
+ // Indicate ongoing work on the username
+ usernameBusy: false,
+ // Indicate error with username
+ usernameError: '',
+ // Assume the homeserver supports username checking until "M_UNRECOGNIZED"
+ usernameCheckSupport: true,
+
+ // Whether the auth UI is currently being used
doingUIAuth: false,
- }
+ // Indicate error with auth
+ authError: '',
+ };
},
componentDidMount: function() {
@@ -46,7 +62,28 @@ export default React.createClass({
onValueChange: function(ev) {
this.setState({
- username: ev.target.value
+ username: ev.target.value,
+ usernameBusy: true,
+ usernameError: '',
+ }, () => {
+ if (!this.state.username || !this.state.usernameCheckSupport) {
+ this.setState({
+ usernameBusy: false,
+ });
+ return;
+ }
+
+ // Debounce the username check to limit number of requests sent
+ if (this._usernameCheckTimeout) {
+ clearTimeout(this._usernameCheckTimeout);
+ }
+ this._usernameCheckTimeout = setTimeout(() => {
+ this._doUsernameCheck().finally(() => {
+ this.setState({
+ usernameBusy: false,
+ });
+ });
+ }, USERNAME_CHECK_DEBOUNCE_MS);
});
},
@@ -56,6 +93,40 @@ export default React.createClass({
});
},
+ _doUsernameCheck: function() {
+ // Check if username is available
+ return this._matrixClient.isUsernameAvailable(this.state.username).then(
+ (isAvailable) => {
+ if (isAvailable) {
+ this.setState({usernameError: ''});
+ }
+ },
+ (err) => {
+ // Indicate whether the homeserver supports username checking
+ const newState = {
+ usernameCheckSupport: err.errcode !== "M_UNRECOGNIZED",
+ };
+ switch (err.errcode) {
+ case "M_USER_IN_USE":
+ newState.usernameError = 'Username not available';
+ break;
+ case "M_INVALID_USERNAME":
+ newState.usernameError = 'Username invalid: ' + err.message;
+ break;
+ case "M_UNRECOGNIZED":
+ // This homeserver doesn't support username checking, assume it's
+ // fine and rely on the error appearing in registration step.
+ newState.usernameError = '';
+ break;
+ default:
+ newState.usernameError = 'An error occurred' + err.message;
+ break;
+ }
+ this.setState(newState);
+ },
+ );
+ },
+
_generatePassword: function() {
return Math.random().toString(36).slice(2);
},
@@ -63,8 +134,9 @@ export default React.createClass({
_makeRegisterRequest: function(auth) {
// Not upgrading - changing mxids
const guestAccessToken = null;
- this._generatedPassword = this._generatePassword();
-
+ if (!this._generatedPassword) {
+ this._generatedPassword = this._generatePassword();
+ }
return this._matrixClient.register(
this.state.username,
this._generatedPassword,
@@ -79,10 +151,9 @@ export default React.createClass({
this.setState({
doingUIAuth: false,
});
- console.info('Auth Finsihed', arguments);
if (!success) {
- this.setState({ errorText : response.message });
+ this.setState({ authError: response.message });
return;
}
@@ -104,6 +175,7 @@ export default React.createClass({
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
const InteractiveAuth = sdk.getComponent('structures.InteractiveAuth');
const Spinner = sdk.getComponent('elements.Spinner');
+
let auth;
if (this.state.doingUIAuth) {
auth = ;
}
+ const inputClasses = classnames({
+ "mx_SetMxIdDialog_input": true,
+ "error": Boolean(this.state.usernameError),
+ });
+
+ let usernameIndicator = null;
+ let usernameBusyIndicator = null;
+ if (this.state.usernameBusy) {
+ usernameBusyIndicator = ;
+ } else {
+ const usernameAvailable = this.state.username &&
+ this.state.usernameCheckSupport && !this.state.usernameError;
+ const usernameIndicatorClasses = classnames({
+ "error": Boolean(this.state.usernameError),
+ "success": usernameAvailable,
+ });
+ usernameIndicator =
+ { usernameAvailable ? 'Username available' : this.state.usernameError }
+
;
+ }
+
+ let authErrorIndicator = null;
+ if (this.state.authError) {
+ authErrorIndicator =
+ { this.state.authError }
+
;
+ }
+ const canContinue = this.state.username &&
+ !this.state.usernameError &&
+ !this.state.usernameBusy;
+
return (
-
- Beyond this point you're going to need to pick a username - your
- unique identifier in Riot.
-
-
-
- You can't change your username, but you can always choose how you
- appear to other people in Riot by changing your display name.
-
-
-
- { auth }
-
- { this.state.errorText }
+
+
+ { usernameBusyIndicator }
+ { usernameIndicator }
+
+ This will be your account name on
+ the {this.props.homeserverUrl} homeserver,
+ or you can pick a
+
+ different server
+ .
+
+ { auth }
+ { authErrorIndicator }
From 6257bfcd87828610159d07c29cf7c277aafcd640 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Wed, 10 May 2017 14:28:48 +0100
Subject: [PATCH 0046/1016] Add prop type for onDifferentServerClicked
---
src/components/views/dialogs/SetMxIdDialog.js | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/components/views/dialogs/SetMxIdDialog.js b/src/components/views/dialogs/SetMxIdDialog.js
index 445b7eb77f..86b5fccbc2 100644
--- a/src/components/views/dialogs/SetMxIdDialog.js
+++ b/src/components/views/dialogs/SetMxIdDialog.js
@@ -34,6 +34,8 @@ export default React.createClass({
displayName: 'SetMxIdDialog',
propTypes: {
onFinished: React.PropTypes.func.isRequired,
+ // Called when the user requests to register with a different homeserver
+ onDifferentServerClicked: React.PropTypes.func.isRequired,
},
getInitialState: function() {
From 7f766d89c32feba745c3d3000c64e94be5431226 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Wed, 10 May 2017 14:42:06 +0100
Subject: [PATCH 0047/1016] Rename variables, more comments
---
src/components/structures/TimelinePanel.js | 20 ++++++++++++--------
1 file changed, 12 insertions(+), 8 deletions(-)
diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js
index 973c619904..874c6b1ac0 100644
--- a/src/components/structures/TimelinePanel.js
+++ b/src/components/structures/TimelinePanel.js
@@ -507,7 +507,7 @@ var TimelinePanel = React.createClass({
// if no client or client is guest don't send RR or RM
if (!cli || cli.isGuest()) return;
- let shouldSendRR = true;
+ let shouldSendReadReceipt = true;
var currentReadUpToEventId = this._getCurrentReadReceipt(true);
var currentReadUpToEventIndex = this._indexForEventId(currentReadUpToEventId);
@@ -526,26 +526,30 @@ var TimelinePanel = React.createClass({
//
if (currentReadUpToEventId && currentReadUpToEventIndex === null &&
this._timelineWindow.canPaginate(EventTimeline.FORWARDS)) {
- shouldSendRR = false;
+ shouldSendReadReceipt = false;
}
var lastReadEventIndex = this._getLastDisplayedEventIndex({
ignoreOwn: true
});
if (lastReadEventIndex === null) {
- shouldSendRR = false;
+ shouldSendReadReceipt = false;
}
var lastReadEvent = this.state.events[lastReadEventIndex];
- shouldSendRR = shouldSendRR &&
- (lastReadEventIndex > currentReadUpToEventIndex &&
- this.last_rr_sent_event_id != lastReadEvent.getId());
+ shouldSendReadReceipt = shouldSendReadReceipt &&
+ // Only send a RR if the last read Event is ahead in the timeline relative to
+ // the current RR event.
+ lastReadEventIndex > currentReadUpToEventIndex &&
+ // Only send a RR if the last RR set != the one we would send
+ this.last_rr_sent_event_id != lastReadEvent.getId();
- const shouldSendRM = this.last_rm_sent_event_id != this.state.readMarkerEventId;
+ // Only send a RM if the last RM sent != the one we would send
+ const shouldSendReadMarker = this.last_rm_sent_event_id != this.state.readMarkerEventId;
// we also remember the last read receipt we sent to avoid spamming the
// same one at the server repeatedly
- if (shouldSendRR || shouldSendRM) {
+ if (shouldSendReadReceipt || shouldSendReadMarker) {
this.last_rr_sent_event_id = lastReadEvent.getId();
this.last_rm_sent_event_id = this.state.readMarkerEventId;
From 30e183a7f1e0c5378d837f3ab45dfc8abbe6ade0 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Wed, 10 May 2017 14:48:01 +0100
Subject: [PATCH 0048/1016] Only send RR if we should
---
src/components/structures/TimelinePanel.js | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js
index 874c6b1ac0..26bf9dd400 100644
--- a/src/components/structures/TimelinePanel.js
+++ b/src/components/structures/TimelinePanel.js
@@ -550,23 +550,27 @@ var TimelinePanel = React.createClass({
// we also remember the last read receipt we sent to avoid spamming the
// same one at the server repeatedly
if (shouldSendReadReceipt || shouldSendReadMarker) {
- this.last_rr_sent_event_id = lastReadEvent.getId();
+ if (shouldSendReadReceipt) {
+ this.last_rr_sent_event_id = lastReadEvent.getId();
+ } else {
+ lastReadEvent = null;
+ }
this.last_rm_sent_event_id = this.state.readMarkerEventId;
debuglog('TimelinePanel: Sending Read Markers for ',
this.props.timelineSet.room.roomId,
'rm', this.state.readMarkerEventId,
- 'rr', lastReadEvent.getId(),
+ lastReadEvent ? 'rr ' + lastReadEvent.getId() : '',
);
MatrixClientPeg.get().setRoomReadMarkers(
this.props.timelineSet.room.roomId,
this.state.readMarkerEventId,
- lastReadEvent
+ lastReadEvent, // Could be null, in which case no RR is sent
).catch((e) => {
// /read_markers API is not implemented on this HS, fallback to just RR
- if (e.errcode === 'M_UNRECOGNIZED') {
+ if (e.errcode === 'M_UNRECOGNIZED' && lastReadEvent) {
return MatrixClientPeg.get().sendReadReceipt(
- lastReadEvent
+ lastReadEvent,
).catch(() => {
this.last_rr_sent_event_id = undefined;
});
From fe8ea4ffe7dcc5844c52a3baf96563838cac8a90 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Wed, 10 May 2017 14:51:47 +0100
Subject: [PATCH 0049/1016] Rename vars, linting
---
src/components/structures/TimelinePanel.js | 30 +++++++++++-----------
1 file changed, 15 insertions(+), 15 deletions(-)
diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js
index 26bf9dd400..8a6c80980e 100644
--- a/src/components/structures/TimelinePanel.js
+++ b/src/components/structures/TimelinePanel.js
@@ -177,8 +177,8 @@ var TimelinePanel = React.createClass({
componentWillMount: function() {
debuglog("TimelinePanel: mounting");
- this.last_rr_sent_event_id = undefined;
- this.last_rm_sent_event_id = undefined;
+ this.lastRRSentSentId = undefined;
+ this.lastRMSentEventId = undefined;
this.dispatcherRef = dis.register(this.onAction);
MatrixClientPeg.get().on("Room.timeline", this.onRoomTimeline);
@@ -509,8 +509,8 @@ var TimelinePanel = React.createClass({
let shouldSendReadReceipt = true;
- var currentReadUpToEventId = this._getCurrentReadReceipt(true);
- var currentReadUpToEventIndex = this._indexForEventId(currentReadUpToEventId);
+ const currentReadUpToEventId = this._getCurrentReadReceipt(true);
+ const currentReadUpToEventIndex = this._indexForEventId(currentReadUpToEventId);
// We want to avoid sending out read receipts when we are looking at
// events in the past which are before the latest RR.
//
@@ -529,33 +529,33 @@ var TimelinePanel = React.createClass({
shouldSendReadReceipt = false;
}
- var lastReadEventIndex = this._getLastDisplayedEventIndex({
- ignoreOwn: true
+ const lastReadEventIndex = this._getLastDisplayedEventIndex({
+ ignoreOwn: true,
});
if (lastReadEventIndex === null) {
shouldSendReadReceipt = false;
}
-
- var lastReadEvent = this.state.events[lastReadEventIndex];
+ let lastReadEvent = this.state.events[lastReadEventIndex];
shouldSendReadReceipt = shouldSendReadReceipt &&
// Only send a RR if the last read Event is ahead in the timeline relative to
// the current RR event.
lastReadEventIndex > currentReadUpToEventIndex &&
// Only send a RR if the last RR set != the one we would send
- this.last_rr_sent_event_id != lastReadEvent.getId();
+ this.lastRRSentSentId != lastReadEvent.getId();
// Only send a RM if the last RM sent != the one we would send
- const shouldSendReadMarker = this.last_rm_sent_event_id != this.state.readMarkerEventId;
+ const shouldSendReadMarker =
+ this.lastRMSentEventId != this.state.readMarkerEventId;
// we also remember the last read receipt we sent to avoid spamming the
// same one at the server repeatedly
if (shouldSendReadReceipt || shouldSendReadMarker) {
if (shouldSendReadReceipt) {
- this.last_rr_sent_event_id = lastReadEvent.getId();
+ this.lastRRSentSentId = lastReadEvent.getId();
} else {
lastReadEvent = null;
}
- this.last_rm_sent_event_id = this.state.readMarkerEventId;
+ this.lastRMSentEventId = this.state.readMarkerEventId;
debuglog('TimelinePanel: Sending Read Markers for ',
this.props.timelineSet.room.roomId,
@@ -572,12 +572,12 @@ var TimelinePanel = React.createClass({
return MatrixClientPeg.get().sendReadReceipt(
lastReadEvent,
).catch(() => {
- this.last_rr_sent_event_id = undefined;
+ this.lastRRSentSentId = undefined;
});
}
// it failed, so allow retries next time the user is active
- this.last_rr_sent_event_id = undefined;
- this.last_rm_sent_event_id = undefined;
+ this.lastRRSentSentId = undefined;
+ this.lastRMSentEventId = undefined;
});
// do a quick-reset of our unreadNotificationCount to avoid having
From 856ef58d4678e4b243f4df94bdc20bb3b6e70efb Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Wed, 10 May 2017 14:55:58 +0100
Subject: [PATCH 0050/1016] fix commen
---
src/components/structures/TimelinePanel.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js
index 8a6c80980e..08b94fbdbf 100644
--- a/src/components/structures/TimelinePanel.js
+++ b/src/components/structures/TimelinePanel.js
@@ -537,7 +537,7 @@ var TimelinePanel = React.createClass({
}
let lastReadEvent = this.state.events[lastReadEventIndex];
shouldSendReadReceipt = shouldSendReadReceipt &&
- // Only send a RR if the last read Event is ahead in the timeline relative to
+ // Only send a RR if the last read event is ahead in the timeline relative to
// the current RR event.
lastReadEventIndex > currentReadUpToEventIndex &&
// Only send a RR if the last RR set != the one we would send
From 50092a0f1f432ad8b7aed33120765b264e32e7cb Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Wed, 10 May 2017 15:16:49 +0100
Subject: [PATCH 0051/1016] fixes vector-im/riot-web#3881
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
---
src/components/views/rooms/RoomSettings.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/views/rooms/RoomSettings.js b/src/components/views/rooms/RoomSettings.js
index 2c29dd433c..798aacfa8e 100644
--- a/src/components/views/rooms/RoomSettings.js
+++ b/src/components/views/rooms/RoomSettings.js
@@ -926,7 +926,7 @@ module.exports = React.createClass({
-
To redact messages, you must be a
+
To redact other users' messages, you must be a
From 3815ad6cd031a9cd14081a18ba6c6c0448fcbc6c Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Thu, 11 May 2017 09:20:34 +0100
Subject: [PATCH 0052/1016] Sent -> Event
---
src/components/structures/TimelinePanel.js | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js
index 08b94fbdbf..5a5abc6257 100644
--- a/src/components/structures/TimelinePanel.js
+++ b/src/components/structures/TimelinePanel.js
@@ -177,7 +177,7 @@ var TimelinePanel = React.createClass({
componentWillMount: function() {
debuglog("TimelinePanel: mounting");
- this.lastRRSentSentId = undefined;
+ this.lastRRSentEventId = undefined;
this.lastRMSentEventId = undefined;
this.dispatcherRef = dis.register(this.onAction);
@@ -541,7 +541,7 @@ var TimelinePanel = React.createClass({
// the current RR event.
lastReadEventIndex > currentReadUpToEventIndex &&
// Only send a RR if the last RR set != the one we would send
- this.lastRRSentSentId != lastReadEvent.getId();
+ this.lastRRSentEventId != lastReadEvent.getId();
// Only send a RM if the last RM sent != the one we would send
const shouldSendReadMarker =
@@ -551,7 +551,7 @@ var TimelinePanel = React.createClass({
// same one at the server repeatedly
if (shouldSendReadReceipt || shouldSendReadMarker) {
if (shouldSendReadReceipt) {
- this.lastRRSentSentId = lastReadEvent.getId();
+ this.lastRRSentEventId = lastReadEvent.getId();
} else {
lastReadEvent = null;
}
@@ -572,11 +572,11 @@ var TimelinePanel = React.createClass({
return MatrixClientPeg.get().sendReadReceipt(
lastReadEvent,
).catch(() => {
- this.lastRRSentSentId = undefined;
+ this.lastRRSentEventId = undefined;
});
}
// it failed, so allow retries next time the user is active
- this.lastRRSentSentId = undefined;
+ this.lastRRSentEventId = undefined;
this.lastRMSentEventId = undefined;
});
From 852e1eb72016d71c22b817515547c1d448bdc67a Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Thu, 11 May 2017 09:31:59 +0100
Subject: [PATCH 0053/1016] Rename some variables
`ReadUpTo` -> `RR`
`ReadReceipt` -> `RR`
`ReadMarker` -> `RM`
---
src/components/structures/TimelinePanel.js | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js
index 5a5abc6257..de43bd1c19 100644
--- a/src/components/structures/TimelinePanel.js
+++ b/src/components/structures/TimelinePanel.js
@@ -507,10 +507,10 @@ var TimelinePanel = React.createClass({
// if no client or client is guest don't send RR or RM
if (!cli || cli.isGuest()) return;
- let shouldSendReadReceipt = true;
+ let shouldSendRR = true;
- const currentReadUpToEventId = this._getCurrentReadReceipt(true);
- const currentReadUpToEventIndex = this._indexForEventId(currentReadUpToEventId);
+ const currentRREventId = this._getCurrentReadReceipt(true);
+ const currentRREventIndex = this._indexForEventId(currentRREventId);
// We want to avoid sending out read receipts when we are looking at
// events in the past which are before the latest RR.
//
@@ -524,33 +524,33 @@ var TimelinePanel = React.createClass({
// RRs) - but that is a bit of a niche case. It will sort itself out when
// the user eventually hits the live timeline.
//
- if (currentReadUpToEventId && currentReadUpToEventIndex === null &&
+ if (currentRREventId && currentRREventIndex === null &&
this._timelineWindow.canPaginate(EventTimeline.FORWARDS)) {
- shouldSendReadReceipt = false;
+ shouldSendRR = false;
}
const lastReadEventIndex = this._getLastDisplayedEventIndex({
ignoreOwn: true,
});
if (lastReadEventIndex === null) {
- shouldSendReadReceipt = false;
+ shouldSendRR = false;
}
let lastReadEvent = this.state.events[lastReadEventIndex];
- shouldSendReadReceipt = shouldSendReadReceipt &&
+ shouldSendRR = shouldSendRR &&
// Only send a RR if the last read event is ahead in the timeline relative to
// the current RR event.
- lastReadEventIndex > currentReadUpToEventIndex &&
+ lastReadEventIndex > currentRREventIndex &&
// Only send a RR if the last RR set != the one we would send
this.lastRRSentEventId != lastReadEvent.getId();
// Only send a RM if the last RM sent != the one we would send
- const shouldSendReadMarker =
+ const shouldSendRM =
this.lastRMSentEventId != this.state.readMarkerEventId;
// we also remember the last read receipt we sent to avoid spamming the
// same one at the server repeatedly
- if (shouldSendReadReceipt || shouldSendReadMarker) {
- if (shouldSendReadReceipt) {
+ if (shouldSendRR || shouldSendRM) {
+ if (shouldSendRR) {
this.lastRRSentEventId = lastReadEvent.getId();
} else {
lastReadEvent = null;
From 6326a95b39f335d3da3b66bb48a53a9d456c208c Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Thu, 11 May 2017 17:04:11 +0100
Subject: [PATCH 0054/1016] Prevent ROUs from creating new chats/new rooms
Spawn a SetMxIdDialog instead and do nothing.
---
src/components/structures/MatrixChat.js | 38 +++++++++++++++++++++----
1 file changed, 32 insertions(+), 6 deletions(-)
diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index 0c8c60ba5c..eeab10b326 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -502,21 +502,23 @@ module.exports = React.createClass({
this.notifyNewScreen('settings');
break;
case 'view_create_room':
- //this._setPage(PageTypes.CreateRoom);
- //this.notifyNewScreen('new');
+ if (MatrixClientPeg.get().isGuest()) {
+ dis.dispatch({action: 'view_set_mxid'});
+ break;
+ }
var TextInputDialog = sdk.getComponent("dialogs.TextInputDialog");
Modal.createDialog(TextInputDialog, {
title: "Create Room",
description: "Room name (optional)",
button: "Create Room",
- onFinished: (should_create, name) => {
- if (should_create) {
+ onFinished: (shouldCreate, name) => {
+ if (shouldCreate) {
const createOpts = {};
if (name) createOpts.name = name;
createRoom({createOpts}).done();
}
- }
+ },
});
break;
case 'view_room_directory':
@@ -531,6 +533,9 @@ module.exports = React.createClass({
this._setPage(PageTypes.HomePage);
this.notifyNewScreen('home');
break;
+ case 'view_set_mxid':
+ this._setMxId();
+ break;
case 'view_create_chat':
this._createChat();
break;
@@ -679,8 +684,29 @@ 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) {
+ return;
+ }
+ this.onRegistered(credentials);
+ },
+ onDifferentServerClicked: (ev) => {
+ dis.dispatch({action: 'start_registration'});
+ close();
+ },
+ }).close;
+ },
+
_createChat: function() {
- var ChatInviteDialog = sdk.getComponent("dialogs.ChatInviteDialog");
+ if (MatrixClientPeg.get().isGuest()) {
+ dis.dispatch({action: 'view_set_mxid'});
+ return;
+ }
+ const ChatInviteDialog = sdk.getComponent("dialogs.ChatInviteDialog");
Modal.createDialog(ChatInviteDialog, {
title: "Start a new chat",
});
From cfa108a28c80a8f68311c00cdc8183b5be3b750c Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Thu, 11 May 2017 17:07:03 +0100
Subject: [PATCH 0055/1016] No need to dispatch, just call setMxId
---
src/components/structures/MatrixChat.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index eeab10b326..d63bf897c9 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -703,7 +703,7 @@ module.exports = React.createClass({
_createChat: function() {
if (MatrixClientPeg.get().isGuest()) {
- dis.dispatch({action: 'view_set_mxid'});
+ this._setMxId();
return;
}
const ChatInviteDialog = sdk.getComponent("dialogs.ChatInviteDialog");
From f55b27f43281e860ca92b37635a8207907b9134d Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Thu, 11 May 2017 17:32:23 +0100
Subject: [PATCH 0056/1016] looks like it is passed and accessed like a string
so unless I'm going insane, it should be a string.
fixes
```
rageshake.js:61 Warning: Failed prop type: The prop `onClick` is marked as required in `AccessibleButton`, but its value is `undefined`.
in AccessibleButton (created by RoomHeader)
in RoomHeader (created by RoomView)
in div (created by RoomView)
in RoomView (created by LoggedInView)
in main (created by LoggedInView)
in div (created by LoggedInView)
in div (created by LoggedInView)
in LoggedInView (created by MatrixChat)
in MatrixChat
```
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
---
src/components/views/rooms/RoomPreviewBar.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/views/rooms/RoomPreviewBar.js b/src/components/views/rooms/RoomPreviewBar.js
index 43c3b05295..5b7ec9347b 100644
--- a/src/components/views/rooms/RoomPreviewBar.js
+++ b/src/components/views/rooms/RoomPreviewBar.js
@@ -47,7 +47,7 @@ module.exports = React.createClass({
// The alias that was used to access this room, if appropriate
// If given, this will be how the room is referred to (eg.
// in error messages).
- roomAlias: React.PropTypes.object,
+ roomAlias: React.PropTypes.string,
},
getDefaultProps: function() {
From 5e4467adced6ac7cd7eb8b20464efbeb0d8830db Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Thu, 11 May 2017 17:35:06 +0100
Subject: [PATCH 0057/1016] hide settings/search appropriately
pass inRoom prop to RoomHeader (defaults to false)
remove default onSettingsClick, handle if it is passed EVERYWHERE
if onSettingsClick is passes, show that button
show search button only if we are in the room, seems to fail otherwise
this seems to handle all cases I could throw at it. Give it your best
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
---
src/components/structures/RoomView.js | 1 +
src/components/views/rooms/RoomHeader.js | 30 +++++++++++++++++-------
2 files changed, 22 insertions(+), 9 deletions(-)
diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js
index 1b3ed6e80d..af0c595ea9 100644
--- a/src/components/structures/RoomView.js
+++ b/src/components/structures/RoomView.js
@@ -1772,6 +1772,7 @@ module.exports = React.createClass({
oobData={this.props.oobData}
editing={this.state.editingRoomSettings}
saving={this.state.uploadingRoomSettings}
+ inRoom={myMember && myMember.membership === 'join'}
collapsedRhs={ this.props.collapsedRhs }
onSearchClick={this.onSearchClick}
onSettingsClick={this.onSettingsClick}
diff --git a/src/components/views/rooms/RoomHeader.js b/src/components/views/rooms/RoomHeader.js
index 94f2691f2c..5a7da47cbf 100644
--- a/src/components/views/rooms/RoomHeader.js
+++ b/src/components/views/rooms/RoomHeader.js
@@ -39,6 +39,7 @@ module.exports = React.createClass({
oobData: React.PropTypes.object,
editing: React.PropTypes.bool,
saving: React.PropTypes.bool,
+ inRoom: React.PropTypes.bool,
collapsedRhs: React.PropTypes.bool,
onSettingsClick: React.PropTypes.func,
onSaveClick: React.PropTypes.func,
@@ -49,7 +50,7 @@ module.exports = React.createClass({
getDefaultProps: function() {
return {
editing: false,
- onSettingsClick: function() {},
+ inRoom: false,
onSaveClick: function() {},
};
},
@@ -225,12 +226,17 @@ module.exports = React.createClass({
roomName = this.props.room.name;
}
+ const innerName =
+ {roomName} ;
+
+ if (this.props.onSettingsClick) {
+ name = { innerName }{ searchStatus }
;
+ } else {
+ name = { innerName }{ searchStatus }
;
+ }
- name =
-
- {roomName}
- { searchStatus }
-
;
}
if (can_set_room_topic) {
@@ -299,6 +305,14 @@ module.exports = React.createClass({
;
}
+ let search_button;
+ if (this.props.onSearchClick && this.props.inRoom) {
+ search_button =
+
+
+ ;
+ }
+
var rightPanel_buttons;
if (this.props.collapsedRhs) {
rightPanel_buttons =
@@ -313,9 +327,7 @@ module.exports = React.createClass({
{ settings_button }
{ forget_button }
-
-
-
+ { search_button }
{ rightPanel_buttons }
;
}
From 8725ef38639573bf6f0b36d6ee6cac305e5ef70a Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Thu, 11 May 2017 17:47:45 +0100
Subject: [PATCH 0058/1016] Remove "Current Password" input if mx_pass exists
If the user is PWLU, do not show "Current Password" field in ChangePassword and when setting a new password, use the cached password.
---
src/components/structures/LoggedInView.js | 1 +
src/components/structures/MatrixChat.js | 9 ++++-
src/components/structures/UserSettings.js | 5 +++
.../views/settings/ChangePassword.js | 38 +++++++++++--------
4 files changed, 37 insertions(+), 16 deletions(-)
diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js
index 4001227355..2afb43bf47 100644
--- a/src/components/structures/LoggedInView.js
+++ b/src/components/structures/LoggedInView.js
@@ -216,6 +216,7 @@ export default React.createClass({
enableLabs={this.props.config.enableLabs}
referralBaseUrl={this.props.config.referralBaseUrl}
teamToken={this.props.teamToken}
+ cachedPassword={this.props.cachedPassword}
/>;
if (!this.props.collapse_rhs) right_panel = ;
break;
diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index 0c8c60ba5c..b3fa6e9040 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -590,6 +590,12 @@ module.exports = React.createClass({
payload.releaseNotes
);
break;
+ case 'password_changed':
+ this.setState({
+ userHasGeneratedPassword: false,
+ });
+ localStorage.removeItem("mx_pass");
+ break;
}
},
@@ -1176,7 +1182,8 @@ module.exports = React.createClass({
onUserSettingsClose={this.onUserSettingsClose}
onRegistered={this.onRegistered}
teamToken={this._teamToken}
- userHasGeneratedPassword={this.state.userHasGeneratedPassword}
+ cachedPassword={this.state.userHasGeneratedPassword ?
+ localStorage.getItem('mx_pass') : null}
{...this.props}
{...this.state}
/>
diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js
index 46dce8bd2e..fa0fcadf0e 100644
--- a/src/components/structures/UserSettings.js
+++ b/src/components/structures/UserSettings.js
@@ -139,6 +139,9 @@ module.exports = React.createClass({
// Team token for the referral link. If falsy, the referral section will
// not appear
teamToken: React.PropTypes.string,
+
+ // the user is a PWLU (/w password stashed in localStorage 'mx_pass')
+ cachedPassword: React.PropTypes.string,
},
getDefaultProps: function() {
@@ -331,6 +334,7 @@ module.exports = React.createClass({
receive push notifications on other devices until you
log back in to them.`,
});
+ dis.dispatch({action: 'password_changed'});
},
onUpgradeClicked: function() {
@@ -894,6 +898,7 @@ module.exports = React.createClass({
rowLabelClassName="mx_UserSettings_profileLabelCell"
rowInputClassName="mx_UserSettings_profileInputCell"
buttonClassName="mx_UserSettings_button mx_UserSettings_changePasswordButton"
+ cachedPassword={this.props.cachedPassword}
onError={this.onPasswordChangeError}
onFinished={this.onPasswordChanged} />
);
diff --git a/src/components/views/settings/ChangePassword.js b/src/components/views/settings/ChangePassword.js
index 20ce45e5dd..bbb5d14219 100644
--- a/src/components/views/settings/ChangePassword.js
+++ b/src/components/views/settings/ChangePassword.js
@@ -31,7 +31,10 @@ module.exports = React.createClass({
rowClassName: React.PropTypes.string,
rowLabelClassName: React.PropTypes.string,
rowInputClassName: React.PropTypes.string,
- buttonClassName: React.PropTypes.string
+ buttonClassName: React.PropTypes.string,
+
+ // user is a PWLU (/w password stashed in localStorage 'mx_pass')
+ cachedPassword: React.PropTypes.string,
},
Phases: {
@@ -121,10 +124,10 @@ module.exports = React.createClass({
matrixClient: MatrixClientPeg.get(),
}
);
- },
+ },
onClickChange: function() {
- var old_password = this.refs.old_input.value;
+ var old_password = this.props.cachedPassword || this.refs.old_input.value;
var new_password = this.refs.new_input.value;
var confirm_password = this.refs.confirm_input.value;
var err = this.props.onCheckPassword(
@@ -139,23 +142,28 @@ module.exports = React.createClass({
},
render: function() {
- var rowClassName = this.props.rowClassName;
- var rowLabelClassName = this.props.rowLabelClassName;
- var rowInputClassName = this.props.rowInputClassName;
- var buttonClassName = this.props.buttonClassName;
+ const rowClassName = this.props.rowClassName;
+ const rowLabelClassName = this.props.rowLabelClassName;
+ const rowInputClassName = this.props.rowInputClassName;
+ const buttonClassName = this.props.buttonClassName;
+
+ let currentPassword = null;
+ if (!this.props.cachedPassword) {
+ currentPassword =
+
+ Current password
+
+
+
+
+
;
+ }
switch (this.state.phase) {
case this.Phases.Edit:
return (
-
-
- Current password
-
-
-
-
-
+ { currentPassword }
New password
From 1176573f39b3c2531887e36a2d6c79b3136c5ab5 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Fri, 12 May 2017 12:02:45 +0100
Subject: [PATCH 0059/1016] Implement SessionStore
This wraps session-related state into a basic flux store. The localStorage item 'mx_pass' is the only thing managed by this store for now but it could easily be extended to track other items (like the teamToken which is passed around through props a lot)
---
src/Lifecycle.js | 12 ++--
src/components/structures/LoggedInView.js | 3 +-
src/components/structures/MatrixChat.js | 29 +++++----
src/components/structures/UserSettings.js | 4 --
.../views/settings/ChangePassword.js | 25 ++++++--
src/stores/SessionStore.js | 63 +++++++++++++++++++
6 files changed, 104 insertions(+), 32 deletions(-)
create mode 100644 src/stores/SessionStore.js
diff --git a/src/Lifecycle.js b/src/Lifecycle.js
index a7a06401da..decb544b3c 100644
--- a/src/Lifecycle.js
+++ b/src/Lifecycle.js
@@ -289,7 +289,6 @@ export function setLoggedIn(credentials) {
// Resolves by default
let teamPromise = Promise.resolve(null);
- let isPasswordStored = false;
// persist the session
if (localStorage) {
@@ -312,8 +311,11 @@ export function setLoggedIn(credentials) {
// 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.
if (credentials.password) {
- localStorage.setItem("mx_pass", credentials.password);
- isPasswordStored = true;
+ // Update SessionStore
+ dis.dispatch({
+ action: 'cached_password',
+ cachedPassword: credentials.password,
+ });
}
console.log("Session persisted for %s", credentials.userId);
@@ -339,10 +341,10 @@ export function setLoggedIn(credentials) {
MatrixClientPeg.replaceUsingCreds(credentials);
teamPromise.then((teamToken) => {
- dis.dispatch({action: 'on_logged_in', teamToken: teamToken, isPasswordStored});
+ dis.dispatch({action: 'on_logged_in', teamToken: teamToken});
}, (err) => {
console.warn("Failed to get team token on login", err);
- dis.dispatch({action: 'on_logged_in', teamToken: null, isPasswordStored});
+ dis.dispatch({action: 'on_logged_in', teamToken: null});
});
startMatrixClient();
diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js
index 2afb43bf47..0851c01a18 100644
--- a/src/components/structures/LoggedInView.js
+++ b/src/components/structures/LoggedInView.js
@@ -51,7 +51,7 @@ export default React.createClass({
// Has the user generated a password that is stored in local storage?
// (are they a PWLU?)
- userHasGeneratedPassword: React.PropTypes.boolean,
+ userHasGeneratedPassword: React.PropTypes.bool,
// and lots and lots of other stuff.
},
@@ -216,7 +216,6 @@ export default React.createClass({
enableLabs={this.props.config.enableLabs}
referralBaseUrl={this.props.config.referralBaseUrl}
teamToken={this.props.teamToken}
- cachedPassword={this.props.cachedPassword}
/>;
if (!this.props.collapse_rhs) right_panel = ;
break;
diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index b3fa6e9040..d7e24c019a 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -40,6 +40,8 @@ var PageTypes = require('../../PageTypes');
var createRoom = require("../../createRoom");
import * as UDEHandler from '../../UnknownDeviceErrorHandler';
+import getSessionStore from '../../stores/SessionStore';
+
module.exports = React.createClass({
displayName: 'MatrixChat',
@@ -139,8 +141,7 @@ module.exports = React.createClass({
register_is_url: null,
register_id_sid: null,
- // Initially, use localStorage as source of truth
- userHasGeneratedPassword: localStorage && localStorage.getItem('mx_pass'),
+ userHasGeneratedPassword: false,
};
return s;
},
@@ -249,6 +250,10 @@ module.exports = React.createClass({
register_hs_url: paramHs,
});
}
+
+ this._sessionStore = getSessionStore();
+ this._sessionStore.on('update', this._setStateFromSessionStore);
+ this._setStateFromSessionStore();
},
componentDidMount: function() {
@@ -590,12 +595,6 @@ module.exports = React.createClass({
payload.releaseNotes
);
break;
- case 'password_changed':
- this.setState({
- userHasGeneratedPassword: false,
- });
- localStorage.removeItem("mx_pass");
- break;
}
},
@@ -765,15 +764,11 @@ module.exports = React.createClass({
/**
* Called when a new logged in session has started
*/
- _onLoggedIn: function(teamToken, isPasswordStored) {
+ _onLoggedIn: function(teamToken) {
this.setState({
guestCreds: null,
loggedIn: true,
loggingIn: false,
- // isPasswordStored only true when ROU sets a username and becomes PWLU.
- // (the password was randomly generated and stored in localStorage).
- userHasGeneratedPassword:
- this.state.userHasGeneratedPassword || isPasswordStored,
});
if (teamToken) {
@@ -902,6 +897,12 @@ module.exports = React.createClass({
});
},
+ _setStateFromSessionStore() {
+ this.setState({
+ userHasGeneratedPassword: Boolean(this._sessionStore.getCachedPassword()),
+ });
+ },
+
onFocus: function(ev) {
dis.dispatch({action: 'focus_composer'});
},
@@ -1182,8 +1183,6 @@ module.exports = React.createClass({
onUserSettingsClose={this.onUserSettingsClose}
onRegistered={this.onRegistered}
teamToken={this._teamToken}
- cachedPassword={this.state.userHasGeneratedPassword ?
- localStorage.getItem('mx_pass') : null}
{...this.props}
{...this.state}
/>
diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js
index fa0fcadf0e..d352d5cae8 100644
--- a/src/components/structures/UserSettings.js
+++ b/src/components/structures/UserSettings.js
@@ -139,9 +139,6 @@ module.exports = React.createClass({
// Team token for the referral link. If falsy, the referral section will
// not appear
teamToken: React.PropTypes.string,
-
- // the user is a PWLU (/w password stashed in localStorage 'mx_pass')
- cachedPassword: React.PropTypes.string,
},
getDefaultProps: function() {
@@ -898,7 +895,6 @@ module.exports = React.createClass({
rowLabelClassName="mx_UserSettings_profileLabelCell"
rowInputClassName="mx_UserSettings_profileInputCell"
buttonClassName="mx_UserSettings_button mx_UserSettings_changePasswordButton"
- cachedPassword={this.props.cachedPassword}
onError={this.onPasswordChangeError}
onFinished={this.onPasswordChanged} />
);
diff --git a/src/components/views/settings/ChangePassword.js b/src/components/views/settings/ChangePassword.js
index bbb5d14219..3a1c777cd9 100644
--- a/src/components/views/settings/ChangePassword.js
+++ b/src/components/views/settings/ChangePassword.js
@@ -22,6 +22,8 @@ var Modal = require("../../../Modal");
var sdk = require("../../../index");
import AccessibleButton from '../elements/AccessibleButton';
+import getSessionStore from '../../../stores/SessionStore';
+
module.exports = React.createClass({
displayName: 'ChangePassword',
propTypes: {
@@ -32,9 +34,6 @@ module.exports = React.createClass({
rowLabelClassName: React.PropTypes.string,
rowInputClassName: React.PropTypes.string,
buttonClassName: React.PropTypes.string,
-
- // user is a PWLU (/w password stashed in localStorage 'mx_pass')
- cachedPassword: React.PropTypes.string,
},
Phases: {
@@ -63,10 +62,24 @@ module.exports = React.createClass({
getInitialState: function() {
return {
- phase: this.Phases.Edit
+ phase: this.Phases.Edit,
+ cachedPassword: null,
};
},
+ componentWillMount: function() {
+ this.sessionStore = getSessionStore();
+ this.sessionStore.on('update', this.setStateFromSessionStore);
+
+ this.setStateFromSessionStore();
+ },
+
+ setStateFromSessionStore: function() {
+ this.setState({
+ cachedPassword: this.sessionStore.getCachedPassword(),
+ });
+ },
+
changePassword: function(old_password, new_password) {
var cli = MatrixClientPeg.get();
@@ -127,7 +140,7 @@ module.exports = React.createClass({
},
onClickChange: function() {
- var old_password = this.props.cachedPassword || this.refs.old_input.value;
+ var old_password = this.state.cachedPassword || this.refs.old_input.value;
var new_password = this.refs.new_input.value;
var confirm_password = this.refs.confirm_input.value;
var err = this.props.onCheckPassword(
@@ -148,7 +161,7 @@ module.exports = React.createClass({
const buttonClassName = this.props.buttonClassName;
let currentPassword = null;
- if (!this.props.cachedPassword) {
+ if (!this.state.cachedPassword) {
currentPassword =
Current password
diff --git a/src/stores/SessionStore.js b/src/stores/SessionStore.js
new file mode 100644
index 0000000000..1c19494e23
--- /dev/null
+++ b/src/stores/SessionStore.js
@@ -0,0 +1,63 @@
+import dis from '../dispatcher';
+import EventEmitter from 'events';
+
+/**
+ * A class for storing application state to do with the session. This is a simple flux
+ * store that listens for actions and updates its state accordingly, informing any
+ * listeners (views) of state changes via the 'update' event.
+ */
+function SessionStore() {
+ // Initialise state
+ this._state = {
+ cachedPassword: localStorage.getItem('mx_pass'),
+ };
+
+ dis.register(this._onAction.bind(this));
+}
+
+// Inherit from EventEmitter
+SessionStore.prototype = EventEmitter.prototype;
+
+SessionStore.prototype._update = function() {
+ // Persist state to localStorage
+ if (this._state.cachedPassword) {
+ localStorage.setItem('mx_pass', this._state.cachedPassword);
+ } else {
+ localStorage.removeItem('mx_pass', this._state.cachedPassword);
+ }
+
+ this.emit('update');
+};
+
+SessionStore.prototype._setState = function(newState) {
+ this._state = Object.assign(this._state, newState);
+ this._update();
+};
+
+SessionStore.prototype._onAction = function(payload) {
+ switch (payload.action) {
+ case 'cached_password':
+ this._setState({
+ cachedPassword: payload.cachedPassword,
+ });
+ break;
+ case 'password_changed':
+ this._setState({
+ cachedPassword: null,
+ });
+ break;
+ }
+};
+
+SessionStore.prototype.getCachedPassword = function() {
+ return this._state.cachedPassword;
+};
+
+// Export singleton getter
+let singletonSessionStore = null;
+export default function getSessionStore() {
+ if (!singletonSessionStore) {
+ singletonSessionStore = new SessionStore();
+ }
+ return singletonSessionStore;
+}
From 5c8187dc8f9804e01ad4d01af27d07801caebc2c Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Fri, 12 May 2017 15:47:37 +0100
Subject: [PATCH 0060/1016] Explicitly pass thru userHasGeneratedPassword
---
src/components/structures/MatrixChat.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index d7e24c019a..5975d6cf5f 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -1183,6 +1183,7 @@ module.exports = React.createClass({
onUserSettingsClose={this.onUserSettingsClose}
onRegistered={this.onRegistered}
teamToken={this._teamToken}
+ userHasGeneratedPassword={this.state.userHasGeneratedPassword}
{...this.props}
{...this.state}
/>
From 6ffe7ef9b2aabcb41c10043fe762c529f80617dc Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Fri, 12 May 2017 15:50:01 +0100
Subject: [PATCH 0061/1016] Use same singleton impl as MatrixClientPeg for
SessionStore
---
src/stores/SessionStore.js | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/src/stores/SessionStore.js b/src/stores/SessionStore.js
index 1c19494e23..7be3885fde 100644
--- a/src/stores/SessionStore.js
+++ b/src/stores/SessionStore.js
@@ -55,9 +55,7 @@ SessionStore.prototype.getCachedPassword = function() {
// Export singleton getter
let singletonSessionStore = null;
-export default function getSessionStore() {
- if (!singletonSessionStore) {
- singletonSessionStore = new SessionStore();
- }
- return singletonSessionStore;
+if (!singletonSessionStore) {
+ singletonSessionStore = new SessionStore();
}
+module.exports = singletonSessionStore;
From 536724e7c5b6f55352311ad2856e95ece60ab5cb Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Fri, 12 May 2017 15:58:44 +0100
Subject: [PATCH 0062/1016] ES6 SessionStore
---
src/components/structures/MatrixChat.js | 4 +-
.../views/settings/ChangePassword.js | 4 +-
src/stores/SessionStore.js | 89 ++++++++++---------
3 files changed, 49 insertions(+), 48 deletions(-)
diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index 5975d6cf5f..b8b3f51422 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -40,7 +40,7 @@ var PageTypes = require('../../PageTypes');
var createRoom = require("../../createRoom");
import * as UDEHandler from '../../UnknownDeviceErrorHandler';
-import getSessionStore from '../../stores/SessionStore';
+import sessionStore from '../../stores/SessionStore';
module.exports = React.createClass({
displayName: 'MatrixChat',
@@ -251,7 +251,7 @@ module.exports = React.createClass({
});
}
- this._sessionStore = getSessionStore();
+ this._sessionStore = sessionStore;
this._sessionStore.on('update', this._setStateFromSessionStore);
this._setStateFromSessionStore();
},
diff --git a/src/components/views/settings/ChangePassword.js b/src/components/views/settings/ChangePassword.js
index 3a1c777cd9..c20bc47152 100644
--- a/src/components/views/settings/ChangePassword.js
+++ b/src/components/views/settings/ChangePassword.js
@@ -22,7 +22,7 @@ var Modal = require("../../../Modal");
var sdk = require("../../../index");
import AccessibleButton from '../elements/AccessibleButton';
-import getSessionStore from '../../../stores/SessionStore';
+import sessionStore from '../../../stores/SessionStore';
module.exports = React.createClass({
displayName: 'ChangePassword',
@@ -68,7 +68,7 @@ module.exports = React.createClass({
},
componentWillMount: function() {
- this.sessionStore = getSessionStore();
+ this.sessionStore = sessionStore;
this.sessionStore.on('update', this.setStateFromSessionStore);
this.setStateFromSessionStore();
diff --git a/src/stores/SessionStore.js b/src/stores/SessionStore.js
index 7be3885fde..bf605d7f07 100644
--- a/src/stores/SessionStore.js
+++ b/src/stores/SessionStore.js
@@ -6,53 +6,54 @@ import EventEmitter from 'events';
* store that listens for actions and updates its state accordingly, informing any
* listeners (views) of state changes via the 'update' event.
*/
-function SessionStore() {
- // Initialise state
- this._state = {
- cachedPassword: localStorage.getItem('mx_pass'),
- };
+class SessionStore extends EventEmitter {
+ constructor() {
+ super();
- dis.register(this._onAction.bind(this));
+ // Initialise state
+ this._state = {
+ cachedPassword: localStorage.getItem('mx_pass'),
+ };
+
+ dis.register(this._onAction.bind(this));
+ }
+
+ _update() {
+ // Persist state to localStorage
+ if (this._state.cachedPassword) {
+ localStorage.setItem('mx_pass', this._state.cachedPassword);
+ } else {
+ localStorage.removeItem('mx_pass', this._state.cachedPassword);
+ }
+
+ this.emit('update');
+ }
+
+ _setState(newState) {
+ this._state = Object.assign(this._state, newState);
+ this._update();
+ }
+
+ _onAction(payload) {
+ switch (payload.action) {
+ case 'cached_password':
+ this._setState({
+ cachedPassword: payload.cachedPassword,
+ });
+ break;
+ case 'password_changed':
+ this._setState({
+ cachedPassword: null,
+ });
+ break;
+ }
+ }
+
+ getCachedPassword() {
+ return this._state.cachedPassword;
+ }
}
-// Inherit from EventEmitter
-SessionStore.prototype = EventEmitter.prototype;
-
-SessionStore.prototype._update = function() {
- // Persist state to localStorage
- if (this._state.cachedPassword) {
- localStorage.setItem('mx_pass', this._state.cachedPassword);
- } else {
- localStorage.removeItem('mx_pass', this._state.cachedPassword);
- }
-
- this.emit('update');
-};
-
-SessionStore.prototype._setState = function(newState) {
- this._state = Object.assign(this._state, newState);
- this._update();
-};
-
-SessionStore.prototype._onAction = function(payload) {
- switch (payload.action) {
- case 'cached_password':
- this._setState({
- cachedPassword: payload.cachedPassword,
- });
- break;
- case 'password_changed':
- this._setState({
- cachedPassword: null,
- });
- break;
- }
-};
-
-SessionStore.prototype.getCachedPassword = function() {
- return this._state.cachedPassword;
-};
-
// Export singleton getter
let singletonSessionStore = null;
if (!singletonSessionStore) {
From 2b4c87aca6eac0d32081624093a1f25fd0683621 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Fri, 12 May 2017 16:02:38 +0100
Subject: [PATCH 0063/1016] Remove useless comment
---
src/stores/SessionStore.js | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/stores/SessionStore.js b/src/stores/SessionStore.js
index bf605d7f07..d3370d2df3 100644
--- a/src/stores/SessionStore.js
+++ b/src/stores/SessionStore.js
@@ -54,7 +54,6 @@ class SessionStore extends EventEmitter {
}
}
-// Export singleton getter
let singletonSessionStore = null;
if (!singletonSessionStore) {
singletonSessionStore = new SessionStore();
From 683f1b8a1ac2cd108d61413414431776d3f10517 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Fri, 12 May 2017 17:39:38 +0100
Subject: [PATCH 0064/1016] Invite the welcome user after registration if
configured
This will shift focus to the welcome user DM.
We probably don't want to do this for teams, but I shall leave that for another PR that fixes teams WRT to new-guest-access.
---
src/components/structures/MatrixChat.js | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index d63bf897c9..ee3c601146 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -578,7 +578,7 @@ module.exports = React.createClass({
this.setState({loggingIn: true});
break;
case 'on_logged_in':
- this._onLoggedIn(payload.teamToken, payload.isPasswordStored);
+ this._onLoggedIn(payload.teamToken);
break;
case 'on_logged_out':
this._onLoggedOut();
@@ -801,8 +801,12 @@ module.exports = React.createClass({
this._teamToken = teamToken;
dis.dispatch({action: 'view_home_page'});
} else if (this._is_registered) {
+ if (this.props.config.welcomeUserId) {
+ createRoom({dmUserId: this.props.config.welcomeUserId});
+ return;
+ }
// The user has just logged in after registering
- dis.dispatch({action: 'view_user_settings'});
+ dis.dispatch({action: 'view_room_directory'});
} else {
this._showScreenAfterLogin();
}
From 69d860e98207df27eaaefdb1a53f9e82918f94bc Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Fri, 12 May 2017 21:06:36 +0100
Subject: [PATCH 0065/1016] revert name overengineering, undefined onClick
should be fine on div
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
---
src/components/views/rooms/RoomHeader.js | 16 +++++-----------
1 file changed, 5 insertions(+), 11 deletions(-)
diff --git a/src/components/views/rooms/RoomHeader.js b/src/components/views/rooms/RoomHeader.js
index 5a7da47cbf..c10228c334 100644
--- a/src/components/views/rooms/RoomHeader.js
+++ b/src/components/views/rooms/RoomHeader.js
@@ -226,17 +226,11 @@ module.exports = React.createClass({
roomName = this.props.room.name;
}
- const innerName =
- {roomName} ;
-
- if (this.props.onSettingsClick) {
- name = { innerName }{ searchStatus }
;
- } else {
- name = { innerName }{ searchStatus }
;
- }
-
+ name =
+
+ {roomName}
+ { searchStatus }
+
;
}
if (can_set_room_topic) {
From 822f2f10f28a724654d3cc574ecc4672d7f243fa Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Fri, 12 May 2017 21:16:55 +0100
Subject: [PATCH 0066/1016] conform to Luke's comment
https://github.com/matrix-org/matrix-react-sdk/pull/880#discussion_r116257726
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
---
src/components/views/rooms/RoomHeader.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/views/rooms/RoomHeader.js b/src/components/views/rooms/RoomHeader.js
index c10228c334..e9bb10c60c 100644
--- a/src/components/views/rooms/RoomHeader.js
+++ b/src/components/views/rooms/RoomHeader.js
@@ -228,7 +228,7 @@ module.exports = React.createClass({
name =
- {roomName}
+ { roomName }
{ searchStatus }
;
}
From 60b13d76a505fbc99940e72f425f21d7984bef1b Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Fri, 12 May 2017 21:20:56 +0100
Subject: [PATCH 0067/1016] conform to Luke's other comment
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
---
src/components/views/rooms/RoomHeader.js | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/components/views/rooms/RoomHeader.js b/src/components/views/rooms/RoomHeader.js
index e9bb10c60c..4cf20454a0 100644
--- a/src/components/views/rooms/RoomHeader.js
+++ b/src/components/views/rooms/RoomHeader.js
@@ -17,6 +17,7 @@ limitations under the License.
'use strict';
var React = require('react');
+var classNames = require('classnames');
var sdk = require('../../../index');
var MatrixClientPeg = require('../../../MatrixClientPeg');
var Modal = require("../../../Modal");
@@ -226,9 +227,10 @@ module.exports = React.createClass({
roomName = this.props.room.name;
}
+ const emojiTextClasses = classNames('mx_RoomHeader_nametext', { mx_RoomHeader_settingsHint: settingsHint });
name =
- { roomName }
+ { roomName }
{ searchStatus }
;
}
From 29568feb9519c2068e852037894451573aee645a Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Fri, 12 May 2017 22:38:57 +0100
Subject: [PATCH 0068/1016] show error if we can't set a filter this way it
still works for a room we've been in before
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
---
src/components/structures/FilePanel.js | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/src/components/structures/FilePanel.js b/src/components/structures/FilePanel.js
index fc4cbd9423..778a0cd6f3 100644
--- a/src/components/structures/FilePanel.js
+++ b/src/components/structures/FilePanel.js
@@ -59,6 +59,8 @@ var FilePanel = React.createClass({
var client = MatrixClientPeg.get();
var room = client.getRoom(roomId);
+ this.noRoom = !room;
+
if (room) {
var filter = new Matrix.Filter(client.credentials.userId);
filter.setDefinition(
@@ -82,13 +84,18 @@ var FilePanel = React.createClass({
console.error("Failed to get or create file panel filter", error);
}
);
- }
- else {
+ } else {
console.error("Failed to add filtered timelineSet for FilePanel as no room!");
}
},
render: function() {
+ if (this.noRoom) {
+ return
+
You must join the room to see its files
+
;
+ }
+
// wrap a TimelinePanel with the jump-to-event bits turned off.
var TimelinePanel = sdk.getComponent("structures.TimelinePanel");
var Loader = sdk.getComponent("elements.Spinner");
From 6ec799a028dfff7af27e471df9c13f438ae4e7d0 Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Sat, 13 May 2017 15:04:20 +0100
Subject: [PATCH 0069/1016] I broke UserSettings for webpack-dev-server where
version file doesn't exist, version starts as null then gets set to undefined
by the promise this wasn't handled and now undefined is understood to be
unknown rather than null
also picked up on a small casing error
threePids vs threepids, most things using the latter apart from the init
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
---
src/components/structures/UserSettings.js | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js
index 46dce8bd2e..1740f066d6 100644
--- a/src/components/structures/UserSettings.js
+++ b/src/components/structures/UserSettings.js
@@ -36,7 +36,7 @@ const REACT_SDK_VERSION = 'dist' in packageJson ? packageJson.version : packageJ
// Simple method to help prettify GH Release Tags and Commit Hashes.
const semVerRegex = /^v?(\d+\.\d+\.\d+(?:-rc.+)?)(?:-(?:\d+-g)?([0-9a-fA-F]+))?(?:-dirty)?$/i;
-const gHVersionLabel = function(repo, token) {
+const gHVersionLabel = function(repo, token='') {
const match = token.match(semVerRegex);
let url;
if (match && match[1]) { // basic semVer string possibly with commit hash
@@ -151,10 +151,10 @@ module.exports = React.createClass({
getInitialState: function() {
return {
avatarUrl: null,
- threePids: [],
+ threepids: [],
phase: "UserSettings.LOADING", // LOADING, DISPLAY
email_add_pending: false,
- vectorVersion: null,
+ vectorVersion: undefined,
rejectingInvites: false,
};
},
@@ -1004,7 +1004,7 @@ module.exports = React.createClass({
? gHVersionLabel('matrix-org/matrix-react-sdk', REACT_SDK_VERSION)
: REACT_SDK_VERSION
}
- riot-web version: {(this.state.vectorVersion !== null)
+ riot-web version: {(this.state.vectorVersion !== undefined)
? gHVersionLabel('vector-im/riot-web', this.state.vectorVersion)
: 'unknown'
}
From d7c88a9813087db5df9360eac1c9f57eafa67246 Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Sat, 13 May 2017 15:20:31 +0100
Subject: [PATCH 0070/1016] only removed `/me `, remove anyway to fix
vector-im/riot-web#3733
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
---
src/components/views/rooms/MessageComposerInput.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js
index 8efd2fa579..e2fcc19f67 100644
--- a/src/components/views/rooms/MessageComposerInput.js
+++ b/src/components/views/rooms/MessageComposerInput.js
@@ -542,9 +542,9 @@ export default class MessageComposerInput extends React.Component {
let sendTextFn = this.client.sendTextMessage;
if (contentText.startsWith('/me')) {
- contentText = contentText.replace('/me ', '');
+ contentText = contentText.substring(4);
// bit of a hack, but the alternative would be quite complicated
- if (contentHTML) contentHTML = contentHTML.replace('/me ', '');
+ if (contentHTML) contentHTML = contentHTML.replace(/\/me ?/, '');
sendHtmlFn = this.client.sendHtmlEmote;
sendTextFn = this.client.sendEmoteMessage;
}
From f3274426db369814b5c819aee6b46141fedb5584 Mon Sep 17 00:00:00 2001
From: Matthew Hodgson
Date: Mon, 15 May 2017 00:25:12 +0100
Subject: [PATCH 0071/1016] revert f999aa9 and support full date formats when
desired
---
src/DateUtils.js | 20 +++++++++++---------
1 file changed, 11 insertions(+), 9 deletions(-)
diff --git a/src/DateUtils.js b/src/DateUtils.js
index 07bab4ae7b..c58c09d4de 100644
--- a/src/DateUtils.js
+++ b/src/DateUtils.js
@@ -19,13 +19,14 @@ limitations under the License.
var days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
+function pad(n) {
+ return (n < 10 ? '0' : '') + n;
+}
+
module.exports = {
formatDate: function(date) {
// date.toLocaleTimeString is completely system dependent.
// just go 24h for now
- function pad(n) {
- return (n < 10 ? '0' : '') + n;
- }
var now = new Date();
if (date.toDateString() === now.toDateString()) {
@@ -34,19 +35,20 @@ module.exports = {
else if (now.getTime() - date.getTime() < 6 * 24 * 60 * 60 * 1000) {
return days[date.getDay()] + " " + pad(date.getHours()) + ':' + pad(date.getMinutes());
}
- else /* if (now.getFullYear() === date.getFullYear()) */ {
+ else if (now.getFullYear() === date.getFullYear()) {
return days[date.getDay()] + ", " + months[date.getMonth()] + " " + date.getDate() + " " + pad(date.getHours()) + ':' + pad(date.getMinutes());
}
- /*
else {
- return days[date.getDay()] + ", " + months[date.getMonth()] + " " + date.getDate() + " " + date.getFullYear() + " " + pad(date.getHours()) + ':' + pad(date.getMinutes());
+ return this.formatFullDate(date);
}
- */
+ },
+
+ formatFullDate: function(date) {
+ return days[date.getDay()] + ", " + months[date.getMonth()] + " " + date.getDate() + " " + date.getFullYear() + " " + pad(date.getHours()) + ':' + pad(date.getMinutes());
},
formatTime: function(date) {
- //return pad(date.getHours()) + ':' + pad(date.getMinutes());
- return ('00' + date.getHours()).slice(-2) + ':' + ('00' + date.getMinutes()).slice(-2);
+ return pad(date.getHours()) + ':' + pad(date.getMinutes());
}
};
From c0cead15465ee6c7bf0091d58b4c131491c240f1 Mon Sep 17 00:00:00 2001
From: Matthew Hodgson
Date: Mon, 15 May 2017 01:32:37 +0100
Subject: [PATCH 0072/1016] workaround for
https://github.com/vector-im/riot-web/issues/3633.
unsure our vector url match could ever return undefined, but apparently it is...
---
src/HtmlUtils.js | 21 +++++++++++----------
1 file changed, 11 insertions(+), 10 deletions(-)
diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js
index a31601790f..4acb314c2f 100644
--- a/src/HtmlUtils.js
+++ b/src/HtmlUtils.js
@@ -148,17 +148,18 @@ var sanitizeHtmlParams = {
attribs.href = m[1];
delete attribs.target;
}
-
- m = attribs.href.match(linkifyMatrix.MATRIXTO_URL_PATTERN);
- if (m) {
- var entity = m[1];
- if (entity[0] === '@') {
- attribs.href = '#/user/' + entity;
+ else {
+ m = attribs.href.match(linkifyMatrix.MATRIXTO_URL_PATTERN);
+ if (m) {
+ var entity = m[1];
+ if (entity[0] === '@') {
+ attribs.href = '#/user/' + entity;
+ }
+ else if (entity[0] === '#' || entity[0] === '!') {
+ attribs.href = '#/room/' + entity;
+ }
+ delete attribs.target;
}
- else if (entity[0] === '#' || entity[0] === '!') {
- attribs.href = '#/room/' + entity;
- }
- delete attribs.target;
}
}
attribs.rel = 'noopener'; // https://mathiasbynens.github.io/rel-noopener/
From c5f2b69e48de2606ba1d420ee40086fb4445f3d6 Mon Sep 17 00:00:00 2001
From: Matthew Hodgson
Date: Mon, 15 May 2017 01:37:24 +0100
Subject: [PATCH 0073/1016] add alt attributes to e2e icons on msgs; fixes
https://github.com/vector-im/riot-web/issues/3786
---
src/components/views/rooms/EventTile.js | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js
index d1486d22c9..e2e203f680 100644
--- a/src/components/views/rooms/EventTile.js
+++ b/src/components/views/rooms/EventTile.js
@@ -492,22 +492,22 @@ module.exports = WithMatrixClient(React.createClass({
var e2e;
// cosmetic padlocks:
if ((e2eEnabled && this.props.eventSendStatus) || this.props.mxEvent.getType() === 'm.room.encryption') {
- e2e = ;
+ e2e = ;
}
// real padlocks
else if (this.props.mxEvent.isEncrypted() || (e2eEnabled && this.props.eventSendStatus)) {
if (this.props.mxEvent.getContent().msgtype === 'm.bad.encrypted') {
- e2e = ;
+ e2e = ;
}
else if (this.state.verified == true || (e2eEnabled && this.props.eventSendStatus)) {
- e2e = ;
+ e2e = ;
}
else {
- e2e = ;
+ e2e = ;
}
}
else if (e2eEnabled) {
- e2e = ;
+ e2e = ;
}
const timestamp = this.props.mxEvent.getTs() ?
: null;
From 48864b0880f5c98dc46b043902833b5295d19b78 Mon Sep 17 00:00:00 2001
From: Matthew Hodgson
Date: Mon, 15 May 2017 01:39:57 +0100
Subject: [PATCH 0074/1016] fix visibility of topbar close on dark theme,
fixing https://github.com/vector-im/riot-web/issues/3783
---
src/components/views/rooms/TopUnreadMessagesBar.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/views/rooms/TopUnreadMessagesBar.js b/src/components/views/rooms/TopUnreadMessagesBar.js
index 72b489a406..74c689c395 100644
--- a/src/components/views/rooms/TopUnreadMessagesBar.js
+++ b/src/components/views/rooms/TopUnreadMessagesBar.js
@@ -38,7 +38,7 @@ module.exports = React.createClass({
title="Scroll to unread messages"/>
Jump to first unread message.
-
From 82092dc2d81401b24af4dc60dd0b1bcec5f05a27 Mon Sep 17 00:00:00 2001
From: Matthew Hodgson
Date: Mon, 15 May 2017 02:14:50 +0100
Subject: [PATCH 0075/1016] onClick MELS avatars = expand MELS; fixes
https://github.com/vector-im/riot-web/issues/3899
---
src/components/views/elements/MemberEventListSummary.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/views/elements/MemberEventListSummary.js b/src/components/views/elements/MemberEventListSummary.js
index ae8678894d..dcf1810468 100644
--- a/src/components/views/elements/MemberEventListSummary.js
+++ b/src/components/views/elements/MemberEventListSummary.js
@@ -269,7 +269,7 @@ module.exports = React.createClass({
);
});
return (
-
+
{avatars}
);
From 5c0c49e1f60a45f0f866ab19474d94cb1a446b0d Mon Sep 17 00:00:00 2001
From: Matthew Hodgson
Date: Mon, 15 May 2017 02:18:18 +0100
Subject: [PATCH 0076/1016] ignore voip answer/hangup in unread events - fixes
https://github.com/vector-im/riot-web/issues/3827
---
src/Unread.js | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/Unread.js b/src/Unread.js
index d7490c8632..67166dc24f 100644
--- a/src/Unread.js
+++ b/src/Unread.js
@@ -25,7 +25,9 @@ module.exports = {
eventTriggersUnreadCount: function(ev) {
if (ev.sender && ev.sender.userId == MatrixClientPeg.get().credentials.userId) {
return false;
- } else if (ev.getType() == "m.room.member") {
+ } else if (ev.getType() == 'm.room.member') {
+ return false;
+ } else if (ev.getType() == 'm.call.answer' || ev.getType() == 'm.call.hangup') {
return false;
} else if (ev.getType == 'm.room.message' && ev.getContent().msgtype == 'm.notify') {
return false;
From 6879f7ee6f8f2f1e943b1609dee12800b735d4e0 Mon Sep 17 00:00:00 2001
From: Matthew Hodgson
Date: Mon, 15 May 2017 02:43:23 +0100
Subject: [PATCH 0077/1016] add presence to MemberInfo, fixes
https://github.com/vector-im/riot-web/issues/3720
---
src/components/views/rooms/MemberInfo.js | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/src/components/views/rooms/MemberInfo.js b/src/components/views/rooms/MemberInfo.js
index 1a9a8d5e0f..839405c922 100644
--- a/src/components/views/rooms/MemberInfo.js
+++ b/src/components/views/rooms/MemberInfo.js
@@ -717,8 +717,16 @@ module.exports = WithMatrixClient(React.createClass({
const memberName = this.props.member.name;
+ if (this.props.member.user) {
+ var presenceState = this.props.member.user.presence;
+ var presenceLastActiveAgo = this.props.member.user.lastActiveAgo;
+ var presenceLastTs = this.props.member.user.lastPresenceTs;
+ var presenceCurrentlyActive = this.props.member.user.currentlyActive;
+ }
+
var MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
var PowerSelector = sdk.getComponent('elements.PowerSelector');
+ var PresenceLabel = sdk.getComponent('rooms.PresenceLabel');
const EmojiText = sdk.getComponent('elements.EmojiText');
return (
@@ -736,6 +744,11 @@ module.exports = WithMatrixClient(React.createClass({
+
{ adminTools }
From 486301cffb55aee0a7cfb3df1df3ff5a8ad3c228 Mon Sep 17 00:00:00 2001
From: Matthew Hodgson
Date: Mon, 15 May 2017 02:55:07 +0100
Subject: [PATCH 0078/1016] remove dodgy heuristics for hiding dates on RRs and
use DateUtils instead.
reverts https://github.com/matrix-org/matrix-react-sdk/pull/586/commits/5d99d68a64ec0c81b42c5a8e6959376dab798feb
fixes https://github.com/vector-im/riot-web/issues/3523
---
src/components/views/rooms/EventTile.js | 11 -----------
src/components/views/rooms/ReadReceiptMarker.js | 17 ++++-------------
2 files changed, 4 insertions(+), 24 deletions(-)
diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js
index e2e203f680..44c4051995 100644
--- a/src/components/views/rooms/EventTile.js
+++ b/src/components/views/rooms/EventTile.js
@@ -295,16 +295,6 @@ module.exports = WithMatrixClient(React.createClass({
const receiptOffset = 15;
let left = 0;
- // It's possible that the receipt was sent several days AFTER the event.
- // If it is, we want to display the complete date along with the HH:MM:SS,
- // rather than just HH:MM:SS.
- let dayAfterEvent = new Date(this.props.mxEvent.getTs());
- dayAfterEvent.setDate(dayAfterEvent.getDate() + 1);
- dayAfterEvent.setHours(0);
- dayAfterEvent.setMinutes(0);
- dayAfterEvent.setSeconds(0);
- let dayAfterEventTime = dayAfterEvent.getTime();
-
var receipts = this.props.readReceipts || [];
for (var i = 0; i < receipts.length; ++i) {
var receipt = receipts[i];
@@ -340,7 +330,6 @@ module.exports = WithMatrixClient(React.createClass({
suppressAnimation={this._suppressReadReceiptAnimation}
onClick={this.toggleAllReadAvatars}
timestamp={receipt.ts}
- showFullTimestamp={receipt.ts >= dayAfterEventTime}
/>
);
}
diff --git a/src/components/views/rooms/ReadReceiptMarker.js b/src/components/views/rooms/ReadReceiptMarker.js
index 230efbd1ea..0911d50c3e 100644
--- a/src/components/views/rooms/ReadReceiptMarker.js
+++ b/src/components/views/rooms/ReadReceiptMarker.js
@@ -24,6 +24,8 @@ var sdk = require('../../../index');
var Velociraptor = require('../../../Velociraptor');
require('../../../VelocityBounce');
+import DateUtils from '../../../DateUtils';
+
var bounce = false;
try {
if (global.localStorage) {
@@ -63,9 +65,6 @@ module.exports = React.createClass({
// Timestamp when the receipt was read
timestamp: React.PropTypes.number,
-
- // True to show the full date/time rather than just the time
- showFullTimestamp: React.PropTypes.bool,
},
getDefaultProps: function() {
@@ -170,16 +169,8 @@ module.exports = React.createClass({
let title;
if (this.props.timestamp) {
- const prefix = "Seen by " + this.props.member.userId + " at ";
- let ts = new Date(this.props.timestamp);
- if (this.props.showFullTimestamp) {
- // "15/12/2016, 7:05:45 PM (@alice:matrix.org)"
- title = prefix + ts.toLocaleString();
- }
- else {
- // "7:05:45 PM (@alice:matrix.org)"
- title = prefix + ts.toLocaleTimeString();
- }
+ title = "Seen by " + this.props.member.userId + " at " +
+ DateUtils.formatDate(new Date(this.props.timestamp));
}
return (
From 317e24852dc7eaed7cc2a9a055a1139bab934801 Mon Sep 17 00:00:00 2001
From: Matthew Hodgson
Date: Mon, 15 May 2017 03:03:17 +0100
Subject: [PATCH 0079/1016] explicitly label email & phone add sections; fixes
https://github.com/vector-im/riot-web/issues/3531
---
src/components/structures/UserSettings.js | 1 +
src/components/views/settings/AddPhoneNumber.js | 1 +
2 files changed, 2 insertions(+)
diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js
index 1740f066d6..2c1f17ee3e 100644
--- a/src/components/structures/UserSettings.js
+++ b/src/components/structures/UserSettings.js
@@ -855,6 +855,7 @@ module.exports = React.createClass({
addEmailSection = (
+ Email
+ Phone
From 83cb1e6e29a639dc65e45254fdbd706d2d06c347 Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Mon, 15 May 2017 10:15:35 +0100
Subject: [PATCH 0080/1016] tell guests they can't use filepanel until they
register
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
---
src/components/structures/FilePanel.js | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/components/structures/FilePanel.js b/src/components/structures/FilePanel.js
index 778a0cd6f3..e7438afc4d 100644
--- a/src/components/structures/FilePanel.js
+++ b/src/components/structures/FilePanel.js
@@ -90,7 +90,11 @@ var FilePanel = React.createClass({
},
render: function() {
- if (this.noRoom) {
+ if (MatrixClientPeg.get().isGuest()) {
+ return
+
You must register to use this functionality
+
;
+ } else if (this.noRoom) {
return
You must join the room to see its files
;
From 15201d86aa3b5246242293f0fd4bd9346073aad0 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Mon, 15 May 2017 10:16:47 +0100
Subject: [PATCH 0081/1016] Prevent reskindex -w from running when file names
have not changed
---
scripts/reskindex.js | 22 +++++++++++++++++++++-
1 file changed, 21 insertions(+), 1 deletion(-)
diff --git a/scripts/reskindex.js b/scripts/reskindex.js
index 1db22f9e10..9516614fa5 100755
--- a/scripts/reskindex.js
+++ b/scripts/reskindex.js
@@ -8,8 +8,15 @@ var chokidar = require('chokidar');
var componentIndex = path.join('src', 'component-index.js');
var componentsDir = path.join('src', 'components');
var componentGlob = '**/*.js';
+var prevFiles = [];
function reskindex() {
+ var files = glob.sync(componentGlob, {cwd: componentsDir}).sort();
+ if (!filesHaveChanged(files, prevFiles)) {
+ return;
+ }
+ prevFiles = files;
+
var header = args.h || args.header;
var packageJson = JSON.parse(fs.readFileSync('./package.json'));
@@ -37,7 +44,6 @@ function reskindex() {
strm.write("module.exports.components = {};\n");
}
- var files = glob.sync(componentGlob, {cwd: componentsDir}).sort();
for (var i = 0; i < files.length; ++i) {
var file = files[i].replace('.js', '');
@@ -54,6 +60,20 @@ function reskindex() {
console.log('Reskindex: completed');
}
+// Expects both arrays of file names to be sorted
+function filesHaveChanged(files, prevFiles) {
+ if (files.length !== prevFiles.length) {
+ return true;
+ }
+ // Check for name changes
+ for (var i = 0; i < files.length; i++) {
+ if (prevFiles[i] !== files[i]) {
+ return true;
+ }
+ }
+ return false;
+}
+
// -w indicates watch mode where any FS events will trigger reskindex
if (!args.w) {
reskindex();
From 8715b5233c63f9bedfc92f81dea2e4c36d90f715 Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Mon, 15 May 2017 10:31:17 +0100
Subject: [PATCH 0082/1016] link to #/register
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
---
src/components/structures/FilePanel.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/structures/FilePanel.js b/src/components/structures/FilePanel.js
index e7438afc4d..d83b6b5564 100644
--- a/src/components/structures/FilePanel.js
+++ b/src/components/structures/FilePanel.js
@@ -92,7 +92,7 @@ var FilePanel = React.createClass({
render: function() {
if (MatrixClientPeg.get().isGuest()) {
return
-
You must register to use this functionality
+
You must
register to use this functionality
;
} else if (this.noRoom) {
return
From da3cb0ee48aaea420a31fd9e060e159dfd7e910f Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Mon, 15 May 2017 14:52:19 +0100
Subject: [PATCH 0083/1016] SessionStore extends flux.Store
---
src/components/structures/MatrixChat.js | 2 +-
.../views/settings/ChangePassword.js | 10 ++++-----
src/stores/SessionStore.js | 21 ++++++++++++-------
3 files changed, 19 insertions(+), 14 deletions(-)
diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index b8b3f51422..4556148986 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -252,7 +252,7 @@ module.exports = React.createClass({
}
this._sessionStore = sessionStore;
- this._sessionStore.on('update', this._setStateFromSessionStore);
+ this._sessionStore.addListener(this._setStateFromSessionStore);
this._setStateFromSessionStore();
},
diff --git a/src/components/views/settings/ChangePassword.js b/src/components/views/settings/ChangePassword.js
index c20bc47152..4d8373bc52 100644
--- a/src/components/views/settings/ChangePassword.js
+++ b/src/components/views/settings/ChangePassword.js
@@ -68,15 +68,15 @@ module.exports = React.createClass({
},
componentWillMount: function() {
- this.sessionStore = sessionStore;
- this.sessionStore.on('update', this.setStateFromSessionStore);
+ this._sessionStore = sessionStore;
+ this._sessionStore.addListener(this._setStateFromSessionStore);
- this.setStateFromSessionStore();
+ this._setStateFromSessionStore();
},
- setStateFromSessionStore: function() {
+ _setStateFromSessionStore: function() {
this.setState({
- cachedPassword: this.sessionStore.getCachedPassword(),
+ cachedPassword: this._sessionStore.getCachedPassword(),
});
},
diff --git a/src/stores/SessionStore.js b/src/stores/SessionStore.js
index d3370d2df3..1570f58688 100644
--- a/src/stores/SessionStore.js
+++ b/src/stores/SessionStore.js
@@ -1,21 +1,26 @@
import dis from '../dispatcher';
-import EventEmitter from 'events';
+import {Store} from 'flux/utils';
/**
* A class for storing application state to do with the session. This is a simple flux
* store that listens for actions and updates its state accordingly, informing any
- * listeners (views) of state changes via the 'update' event.
+ * listeners (views) of state changes.
+ *
+ * Usage:
+ * ```
+ * sessionStore.addListener(() => {
+ * this.setState({ cachedPassword: sessionStore.getCachedPassword() })
+ * })
+ * ```
*/
-class SessionStore extends EventEmitter {
+class SessionStore extends Store {
constructor() {
- super();
+ super(dis);
// Initialise state
this._state = {
cachedPassword: localStorage.getItem('mx_pass'),
};
-
- dis.register(this._onAction.bind(this));
}
_update() {
@@ -26,7 +31,7 @@ class SessionStore extends EventEmitter {
localStorage.removeItem('mx_pass', this._state.cachedPassword);
}
- this.emit('update');
+ this.__emitChange();
}
_setState(newState) {
@@ -34,7 +39,7 @@ class SessionStore extends EventEmitter {
this._update();
}
- _onAction(payload) {
+ __onDispatch(payload) {
switch (payload.action) {
case 'cached_password':
this._setState({
From f73cf772fb83db136cd44fd3cc40e50aec83905e Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Mon, 15 May 2017 14:56:05 +0100
Subject: [PATCH 0084/1016] Move sessionStore ref from MatrixChat to
LoggedInView
MatrixChat didn't actually use the sessionStore, so this is one less prop to pass.
---
src/components/structures/LoggedInView.js | 17 ++++++++++++-----
src/components/structures/MatrixChat.js | 12 ------------
2 files changed, 12 insertions(+), 17 deletions(-)
diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js
index 0851c01a18..240a3499a2 100644
--- a/src/components/structures/LoggedInView.js
+++ b/src/components/structures/LoggedInView.js
@@ -23,6 +23,7 @@ import Notifier from '../../Notifier';
import PageTypes from '../../PageTypes';
import sdk from '../../index';
import dis from '../../dispatcher';
+import sessionStore from '../../stores/SessionStore';
/**
* This is what our MatrixChat shows when we are logged in. The precise view is
@@ -49,10 +50,6 @@ export default React.createClass({
teamToken: React.PropTypes.string,
- // Has the user generated a password that is stored in local storage?
- // (are they a PWLU?)
- userHasGeneratedPassword: React.PropTypes.bool,
-
// and lots and lots of other stuff.
},
@@ -80,6 +77,10 @@ export default React.createClass({
this._scrollStateMap = {};
document.addEventListener('keydown', this._onKeyDown);
+
+ this._sessionStore = sessionStore;
+ this._sessionStore.addListener(this._setStateFromSessionStore);
+ this._setStateFromSessionStore();
},
componentWillUnmount: function() {
@@ -97,6 +98,12 @@ export default React.createClass({
return this.refs.roomView.canResetTimeline();
},
+ _setStateFromSessionStore() {
+ this.setState({
+ userHasGeneratedPassword: Boolean(this._sessionStore.getCachedPassword()),
+ });
+ },
+
_onKeyDown: function(ev) {
/*
// Remove this for now as ctrl+alt = alt-gr so this breaks keyboards which rely on alt-gr for numbers
@@ -257,7 +264,7 @@ export default React.createClass({
/>;
} else if (this.props.matrixClient.isGuest()) {
topBar = ;
- } else if (this.props.userHasGeneratedPassword) {
+ } else if (this.state.userHasGeneratedPassword) {
topBar = ;
} else if (Notifier.supportsDesktopNotifications() && !Notifier.isEnabled() && !Notifier.isToolbarHidden()) {
topBar = ;
diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index 4556148986..45b4d07055 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -40,8 +40,6 @@ var PageTypes = require('../../PageTypes');
var createRoom = require("../../createRoom");
import * as UDEHandler from '../../UnknownDeviceErrorHandler';
-import sessionStore from '../../stores/SessionStore';
-
module.exports = React.createClass({
displayName: 'MatrixChat',
@@ -250,10 +248,6 @@ module.exports = React.createClass({
register_hs_url: paramHs,
});
}
-
- this._sessionStore = sessionStore;
- this._sessionStore.addListener(this._setStateFromSessionStore);
- this._setStateFromSessionStore();
},
componentDidMount: function() {
@@ -897,12 +891,6 @@ module.exports = React.createClass({
});
},
- _setStateFromSessionStore() {
- this.setState({
- userHasGeneratedPassword: Boolean(this._sessionStore.getCachedPassword()),
- });
- },
-
onFocus: function(ev) {
dis.dispatch({action: 'focus_composer'});
},
From eb0041d21ab603e3b0d1f0474068ded68f09dba7 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Mon, 15 May 2017 17:03:54 +0100
Subject: [PATCH 0085/1016] Remove redundant state
---
src/components/structures/MatrixChat.js | 3 ---
1 file changed, 3 deletions(-)
diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index 45b4d07055..c5b58c3285 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -138,8 +138,6 @@ module.exports = React.createClass({
register_hs_url: null,
register_is_url: null,
register_id_sid: null,
-
- userHasGeneratedPassword: false,
};
return s;
},
@@ -1171,7 +1169,6 @@ module.exports = React.createClass({
onUserSettingsClose={this.onUserSettingsClose}
onRegistered={this.onRegistered}
teamToken={this._teamToken}
- userHasGeneratedPassword={this.state.userHasGeneratedPassword}
{...this.props}
{...this.state}
/>
From 269fd511300bc001604d70a02e96e6e8bef1c283 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Mon, 15 May 2017 17:17:32 +0100
Subject: [PATCH 0086/1016] Remove SessionStore listener on unmount
---
src/components/structures/LoggedInView.js | 7 ++++++-
src/components/views/settings/ChangePassword.js | 10 +++++++++-
2 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js
index 240a3499a2..bbbf6dff0e 100644
--- a/src/components/structures/LoggedInView.js
+++ b/src/components/structures/LoggedInView.js
@@ -79,12 +79,17 @@ export default React.createClass({
document.addEventListener('keydown', this._onKeyDown);
this._sessionStore = sessionStore;
- this._sessionStore.addListener(this._setStateFromSessionStore);
+ this._removeSSListener = this._sessionStore.addListener(
+ this._setStateFromSessionStore,
+ ).remove;
this._setStateFromSessionStore();
},
componentWillUnmount: function() {
document.removeEventListener('keydown', this._onKeyDown);
+ if (this._removeSSListener) {
+ this._removeSSListener();
+ }
},
getScrollStateForRoom: function(roomId) {
diff --git a/src/components/views/settings/ChangePassword.js b/src/components/views/settings/ChangePassword.js
index 4d8373bc52..07680818df 100644
--- a/src/components/views/settings/ChangePassword.js
+++ b/src/components/views/settings/ChangePassword.js
@@ -69,11 +69,19 @@ module.exports = React.createClass({
componentWillMount: function() {
this._sessionStore = sessionStore;
- this._sessionStore.addListener(this._setStateFromSessionStore);
+ this._removeSSListener = this._sessionStore.addListener(
+ this._setStateFromSessionStore,
+ ).remove;
this._setStateFromSessionStore();
},
+ componentWillUnmount: function() {
+ if (this._removeSSListener) {
+ this._removeSSListener();
+ }
+ },
+
_setStateFromSessionStore: function() {
this.setState({
cachedPassword: this._sessionStore.getCachedPassword(),
From f199f3599ea8231a3d68ef1dd1b6adeac28e7329 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Mon, 15 May 2017 17:31:26 +0100
Subject: [PATCH 0087/1016] Replace NeedToRegisterDialog /w SetMxIdDialog
This uses MatrixChat's `view_set_mxid`
---
src/components/structures/RoomView.js | 12 +--
src/components/structures/UserSettings.js | 12 +--
.../views/dialogs/ChatInviteDialog.js | 6 +-
.../views/dialogs/NeedToRegisterDialog.js | 78 -------------------
.../views/room_settings/ColorSettings.js | 8 +-
src/components/views/rooms/MemberInfo.js | 6 +-
src/components/views/rooms/MessageComposer.js | 6 +-
src/createRoom.js | 7 +-
8 files changed, 11 insertions(+), 124 deletions(-)
delete mode 100644 src/components/views/dialogs/NeedToRegisterDialog.js
diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js
index 3cbe76b289..92049bb113 100644
--- a/src/components/structures/RoomView.js
+++ b/src/components/structures/RoomView.js
@@ -869,11 +869,7 @@ module.exports = React.createClass({
MatrixClientPeg.get().isGuest()
)
) {
- var NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog");
- Modal.createDialog(NeedToRegisterDialog, {
- title: "Failed to join the room",
- description: "This room is private or inaccessible to guests. You may be able to join if you register."
- });
+ dis.dispatch({action: 'view_set_mxid'});
} else {
var msg = error.message ? error.message : JSON.stringify(error);
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
@@ -933,11 +929,7 @@ module.exports = React.createClass({
uploadFile: function(file) {
if (MatrixClientPeg.get().isGuest()) {
- var NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog");
- Modal.createDialog(NeedToRegisterDialog, {
- title: "Please Register",
- description: "Guest users can't upload files. Please register to upload."
- });
+ dis.dispatch({action: 'view_set_mxid'});
return;
}
diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js
index 46dce8bd2e..96c60d7cd8 100644
--- a/src/components/structures/UserSettings.js
+++ b/src/components/structures/UserSettings.js
@@ -245,11 +245,7 @@ module.exports = React.createClass({
onAvatarPickerClick: function(ev) {
if (MatrixClientPeg.get().isGuest()) {
- const NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog");
- Modal.createDialog(NeedToRegisterDialog, {
- title: "Please Register",
- description: "Guests can't set avatars. Please register.",
- });
+ dis.dispatch({action: 'view_set_mxid'});
return;
}
@@ -700,11 +696,7 @@ module.exports = React.createClass({
onChange={(e) => {
if (MatrixClientPeg.get().isGuest()) {
e.target.checked = false;
- const NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog");
- Modal.createDialog(NeedToRegisterDialog, {
- title: "Please Register",
- description: "Guests can't use labs features. Please register.",
- });
+ dis.dispatch({action: 'view_set_mxid'});
return;
}
diff --git a/src/components/views/dialogs/ChatInviteDialog.js b/src/components/views/dialogs/ChatInviteDialog.js
index 7ba503099a..06c029287f 100644
--- a/src/components/views/dialogs/ChatInviteDialog.js
+++ b/src/components/views/dialogs/ChatInviteDialog.js
@@ -284,11 +284,7 @@ module.exports = React.createClass({
_startChat: function(addrs) {
if (MatrixClientPeg.get().isGuest()) {
- var NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog");
- Modal.createDialog(NeedToRegisterDialog, {
- title: "Please Register",
- description: "Guest users can't invite users. Please register."
- });
+ dis.dispatch({action: 'view_set_mxid'});
return;
}
diff --git a/src/components/views/dialogs/NeedToRegisterDialog.js b/src/components/views/dialogs/NeedToRegisterDialog.js
deleted file mode 100644
index f4df5913d5..0000000000
--- a/src/components/views/dialogs/NeedToRegisterDialog.js
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
-Copyright 2016 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-/*
- * Usage:
- * Modal.createDialog(NeedToRegisterDialog, {
- * title: "some text", (default: "Registration required")
- * description: "some more text",
- * onFinished: someFunction,
- * });
- */
-
-import React from 'react';
-import dis from '../../../dispatcher';
-import sdk from '../../../index';
-
-module.exports = React.createClass({
- displayName: 'NeedToRegisterDialog',
- propTypes: {
- title: React.PropTypes.string,
- description: React.PropTypes.oneOfType([
- React.PropTypes.element,
- React.PropTypes.string,
- ]),
- onFinished: React.PropTypes.func.isRequired,
- },
-
- getDefaultProps: function() {
- return {
- title: "Registration required",
- description: "A registered account is required for this action",
- };
- },
-
- onRegisterClicked: function() {
- dis.dispatch({
- action: "start_upgrade_registration",
- });
- if (this.props.onFinished) {
- this.props.onFinished();
- }
- },
-
- render: function() {
- const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
- return (
-
-
- {this.props.description}
-
-
-
- Cancel
-
-
- Register
-
-
-
- );
- },
-});
diff --git a/src/components/views/room_settings/ColorSettings.js b/src/components/views/room_settings/ColorSettings.js
index 6a455d9c3c..5fc845a541 100644
--- a/src/components/views/room_settings/ColorSettings.js
+++ b/src/components/views/room_settings/ColorSettings.js
@@ -21,6 +21,8 @@ var Tinter = require('../../../Tinter');
var MatrixClientPeg = require("../../../MatrixClientPeg");
var Modal = require("../../../Modal");
+import dis from '../../../dispatcher';
+
var ROOM_COLORS = [
// magic room default values courtesy of Ribot
["#76cfa6", "#eaf5f0"],
@@ -86,11 +88,7 @@ module.exports = React.createClass({
}
).catch(function(err) {
if (err.errcode == 'M_GUEST_ACCESS_FORBIDDEN') {
- var NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog");
- Modal.createDialog(NeedToRegisterDialog, {
- title: "Please Register",
- description: "Saving room color settings is only available to registered users"
- });
+ dis.dispatch({action: 'view_set_mxid'});
}
});
}
diff --git a/src/components/views/rooms/MemberInfo.js b/src/components/views/rooms/MemberInfo.js
index 1a9a8d5e0f..1f286e9e12 100644
--- a/src/components/views/rooms/MemberInfo.js
+++ b/src/components/views/rooms/MemberInfo.js
@@ -374,11 +374,7 @@ module.exports = WithMatrixClient(React.createClass({
console.log("Mod toggle success");
}, function(err) {
if (err.errcode == 'M_GUEST_ACCESS_FORBIDDEN') {
- var NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog");
- Modal.createDialog(NeedToRegisterDialog, {
- title: "Please Register",
- description: "This action cannot be performed by a guest user. Please register to be able to do this."
- });
+ dis.dispatch({action: 'view_set_mxid'});
} else {
console.error("Toggle moderator error:" + err);
Modal.createDialog(ErrorDialog, {
diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js
index 0ee3c2082d..df7d0c3640 100644
--- a/src/components/views/rooms/MessageComposer.js
+++ b/src/components/views/rooms/MessageComposer.js
@@ -90,11 +90,7 @@ export default class MessageComposer extends React.Component {
onUploadClick(ev) {
if (MatrixClientPeg.get().isGuest()) {
- let NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog");
- Modal.createDialog(NeedToRegisterDialog, {
- title: "Please Register",
- description: "Guest users can't upload files. Please register to upload.",
- });
+ dis.dispatch({action: 'view_set_mxid'});
return;
}
diff --git a/src/createRoom.js b/src/createRoom.js
index 674fe23d28..72f4016502 100644
--- a/src/createRoom.js
+++ b/src/createRoom.js
@@ -41,12 +41,7 @@ function createRoom(opts) {
const client = MatrixClientPeg.get();
if (client.isGuest()) {
- setTimeout(()=>{
- Modal.createDialog(NeedToRegisterDialog, {
- title: "Please Register",
- description: "Guest users can't create new rooms. Please register to create room and start a chat."
- });
- }, 0);
+ dis.dispatch({action: 'view_set_mxid'});
return q(null);
}
From 93ecdc90a9741dbdc5ad9aa6e70ed4d3743217fb Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Tue, 16 May 2017 11:45:01 +0100
Subject: [PATCH 0088/1016] Make confirmation optional on ChangePassword
Add option to disable password change confirmation (`disabledConfirmation`). Style fixes, use `` element on ChangePassword submit button.
---
.../views/settings/ChangePassword.js | 74 ++++++++++---------
1 file changed, 41 insertions(+), 33 deletions(-)
diff --git a/src/components/views/settings/ChangePassword.js b/src/components/views/settings/ChangePassword.js
index 20ce45e5dd..a5e695a1ff 100644
--- a/src/components/views/settings/ChangePassword.js
+++ b/src/components/views/settings/ChangePassword.js
@@ -64,10 +64,15 @@ module.exports = React.createClass({
};
},
- changePassword: function(old_password, new_password) {
- var cli = MatrixClientPeg.get();
+ changePassword: function(oldPassword, newPassword) {
+ const cli = MatrixClientPeg.get();
- var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
+ if (this.props.disableConfirmation) {
+ this._changePassword(cli, oldPassword, newPassword);
+ return;
+ }
+
+ const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createDialog(QuestionDialog, {
title: "Warning",
description:
@@ -86,31 +91,34 @@ module.exports = React.createClass({
],
onFinished: (confirmed) => {
if (confirmed) {
- var authDict = {
- type: 'm.login.password',
- user: cli.credentials.userId,
- password: old_password
- };
-
- this.setState({
- phase: this.Phases.Uploading
- });
-
- var self = this;
- cli.setPassword(authDict, new_password).then(function() {
- self.props.onFinished();
- }, function(err) {
- self.props.onError(err);
- }).finally(function() {
- self.setState({
- phase: self.Phases.Edit
- });
- }).done();
+ this._changePassword(cli, oldPassword, newPassword);
}
},
});
},
+ _changePassword: function(cli, oldPassword, newPassword) {
+ const authDict = {
+ type: 'm.login.password',
+ user: cli.credentials.userId,
+ password: oldPassword,
+ };
+
+ this.setState({
+ phase: this.Phases.Uploading,
+ });
+
+ cli.setPassword(authDict, newPassword).then(() => {
+ this.props.onFinished();
+ }, (err) => {
+ this.props.onError(err);
+ }).finally(() => {
+ this.setState({
+ phase: this.Phases.Edit,
+ });
+ }).done();
+ },
+
_onExportE2eKeysClicked: function() {
Modal.createDialogAsync(
(cb) => {
@@ -121,20 +129,19 @@ module.exports = React.createClass({
matrixClient: MatrixClientPeg.get(),
}
);
- },
+ },
onClickChange: function() {
- var old_password = this.refs.old_input.value;
- var new_password = this.refs.new_input.value;
- var confirm_password = this.refs.confirm_input.value;
- var err = this.props.onCheckPassword(
- old_password, new_password, confirm_password
+ const oldPassword = this.refs.old_input.value;
+ const newPassword = this.refs.new_input.value;
+ const confirmPassword = this.refs.confirm_input.value;
+ const err = this.props.onCheckPassword(
+ oldPassword, newPassword, confirmPassword,
);
if (err) {
this.props.onError(err);
- }
- else {
- this.changePassword(old_password, new_password);
+ } else {
+ this.changePassword(oldPassword, newPassword);
}
},
@@ -173,7 +180,8 @@ module.exports = React.createClass({
+ onClick={this.onClickChange}
+ element="button">
Change Password
From f7e6a996c5e6720eadfc7065ef81e9e959d774d3 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Tue, 16 May 2017 11:51:09 +0100
Subject: [PATCH 0089/1016] Add proptype
---
src/components/views/settings/ChangePassword.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/components/views/settings/ChangePassword.js b/src/components/views/settings/ChangePassword.js
index a5e695a1ff..e8a07fd225 100644
--- a/src/components/views/settings/ChangePassword.js
+++ b/src/components/views/settings/ChangePassword.js
@@ -31,7 +31,8 @@ module.exports = React.createClass({
rowClassName: React.PropTypes.string,
rowLabelClassName: React.PropTypes.string,
rowInputClassName: React.PropTypes.string,
- buttonClassName: React.PropTypes.string
+ buttonClassName: React.PropTypes.string,
+ disableConfirmation: React.PropTypes.bool,
},
Phases: {
From eb36e979c2591f252fb007bc2460b9d1b2dcbd6a Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Tue, 16 May 2017 11:52:51 +0100
Subject: [PATCH 0090/1016] Reference store token, call .remove on it on
unmount
---
src/components/structures/LoggedInView.js | 8 ++++----
src/components/views/settings/ChangePassword.js | 8 ++++----
2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js
index bbbf6dff0e..a64ae0a25c 100644
--- a/src/components/structures/LoggedInView.js
+++ b/src/components/structures/LoggedInView.js
@@ -79,16 +79,16 @@ export default React.createClass({
document.addEventListener('keydown', this._onKeyDown);
this._sessionStore = sessionStore;
- this._removeSSListener = this._sessionStore.addListener(
+ this._sessionStoreToken = this._sessionStore.addListener(
this._setStateFromSessionStore,
- ).remove;
+ );
this._setStateFromSessionStore();
},
componentWillUnmount: function() {
document.removeEventListener('keydown', this._onKeyDown);
- if (this._removeSSListener) {
- this._removeSSListener();
+ if (this._sessionStoreToken) {
+ this._sessionStoreToken.remove();
}
},
diff --git a/src/components/views/settings/ChangePassword.js b/src/components/views/settings/ChangePassword.js
index 07680818df..e3845390de 100644
--- a/src/components/views/settings/ChangePassword.js
+++ b/src/components/views/settings/ChangePassword.js
@@ -69,16 +69,16 @@ module.exports = React.createClass({
componentWillMount: function() {
this._sessionStore = sessionStore;
- this._removeSSListener = this._sessionStore.addListener(
+ this._sessionStoreToken = this._sessionStore.addListener(
this._setStateFromSessionStore,
- ).remove;
+ );
this._setStateFromSessionStore();
},
componentWillUnmount: function() {
- if (this._removeSSListener) {
- this._removeSSListener();
+ if (this._sessionStoreToken) {
+ this._sessionStoreToken.remove();
}
},
From 633c6b39f6a1dd98613898287499ec7696a95099 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Tue, 16 May 2017 11:58:37 +0100
Subject: [PATCH 0091/1016] Add comment to Lifecycle
---
src/Lifecycle.js | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/src/Lifecycle.js b/src/Lifecycle.js
index decb544b3c..20d5836dae 100644
--- a/src/Lifecycle.js
+++ b/src/Lifecycle.js
@@ -185,6 +185,14 @@ function _registerAsGuest(hsUrl, isUrl, defaultDeviceDisplayName) {
// returns a promise which resolves to true if a session is found in
// localstorage
+//
+// N.B. Lifecycle.js should not maintain any further localStorage state, we
+// are moving towards using SessionStore to keep track of state related
+// to the current session (which is typically backed by localStorage).
+//
+// The plan is to gradually move the localStorage access done here into
+// SessionStore to avoid bugs where the view becomes out-of-sync with
+// localStorage (e.g. teamToken, isGuest etc.)
function _restoreFromLocalStorage() {
if (!localStorage) {
return q(false);
From 5a3c32044e764bb56ec4d4bf1dd413e4bd9bd2f9 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Tue, 16 May 2017 12:45:14 +0100
Subject: [PATCH 0092/1016] disableConfirmation -> confirm
---
src/components/views/settings/ChangePassword.js | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/src/components/views/settings/ChangePassword.js b/src/components/views/settings/ChangePassword.js
index e8a07fd225..257e0ac056 100644
--- a/src/components/views/settings/ChangePassword.js
+++ b/src/components/views/settings/ChangePassword.js
@@ -32,7 +32,7 @@ module.exports = React.createClass({
rowLabelClassName: React.PropTypes.string,
rowInputClassName: React.PropTypes.string,
buttonClassName: React.PropTypes.string,
- disableConfirmation: React.PropTypes.bool,
+ confirm: React.PropTypes.bool,
},
Phases: {
@@ -55,7 +55,8 @@ module.exports = React.createClass({
error: "Passwords can't be empty"
};
}
- }
+ },
+ confirm: true,
};
},
@@ -68,7 +69,7 @@ module.exports = React.createClass({
changePassword: function(oldPassword, newPassword) {
const cli = MatrixClientPeg.get();
- if (this.props.disableConfirmation) {
+ if (!this.props.confirm) {
this._changePassword(cli, oldPassword, newPassword);
return;
}
From 6bd7af29176d952f171591b1be147f4711802b69 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Tue, 16 May 2017 14:00:09 +0100
Subject: [PATCH 0093/1016] Revert "Merge pull request #867 from
matrix-org/t3chguy/BaseDialog-patch1"
This reverts commit 3549ff254325cdba689b425307218e678c89a2c8, reversing
changes made to 1db677141ea281ac2fba361006597768110f77a5.
---
src/components/views/dialogs/BaseDialog.js | 15 +++------------
1 file changed, 3 insertions(+), 12 deletions(-)
diff --git a/src/components/views/dialogs/BaseDialog.js b/src/components/views/dialogs/BaseDialog.js
index 02460148b3..279dedbd43 100644
--- a/src/components/views/dialogs/BaseDialog.js
+++ b/src/components/views/dialogs/BaseDialog.js
@@ -57,25 +57,20 @@ export default React.createClass({
}
},
- // Don't let key{down,press} events escape the modal. Consume them all.
- _eatKeyEvent: function(e) {
- e.stopPropagation();
- },
-
// Must be when the key is released (and not pressed) otherwise componentWillUnmount
// will focus another element which will receive future key events
_onKeyUp: function(e) {
if (e.keyCode === KeyCode.ESCAPE) {
+ e.stopPropagation();
e.preventDefault();
this.props.onFinished();
} else if (e.keyCode === KeyCode.ENTER) {
if (this.props.onEnterPressed) {
+ e.stopPropagation();
e.preventDefault();
this.props.onEnterPressed(e);
}
}
- // Consume all keyup events while Modal is open
- e.stopPropagation();
},
_onCancelClick: function(e) {
@@ -86,11 +81,7 @@ export default React.createClass({
const TintableSvg = sdk.getComponent("elements.TintableSvg");
return (
-
+
From 2c5fb01f03b9ac5e98f2e488c1a6acb26c7e5d22 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Tue, 16 May 2017 14:13:22 +0100
Subject: [PATCH 0094/1016] Fix bugs introduced by dodgy merge
---
src/components/views/settings/ChangePassword.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/components/views/settings/ChangePassword.js b/src/components/views/settings/ChangePassword.js
index 422761601d..601b774932 100644
--- a/src/components/views/settings/ChangePassword.js
+++ b/src/components/views/settings/ChangePassword.js
@@ -90,7 +90,7 @@ module.exports = React.createClass({
});
},
- changePassword: function(old_password, new_password) {
+ changePassword: function(oldPassword, newPassword) {
const cli = MatrixClientPeg.get();
if (!this.props.confirm) {
@@ -158,7 +158,7 @@ module.exports = React.createClass({
},
onClickChange: function() {
- const oldPassword = this.refs.old_input.value;
+ const oldPassword = this.state.cachedPassword || this.refs.old_input.value;
const newPassword = this.refs.new_input.value;
const confirmPassword = this.refs.confirm_input.value;
const err = this.props.onCheckPassword(
From ca907f42dc980e97c764b2f535dd26e8615ec066 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Tue, 16 May 2017 14:24:24 +0100
Subject: [PATCH 0095/1016] Fix redundant getComponent
---
src/createRoom.js | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/createRoom.js b/src/createRoom.js
index 72f4016502..e18f9cf032 100644
--- a/src/createRoom.js
+++ b/src/createRoom.js
@@ -36,7 +36,6 @@ function createRoom(opts) {
opts = opts || {};
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
- const NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog");
const Loader = sdk.getComponent("elements.Spinner");
const client = MatrixClientPeg.get();
From f8d1a6d24042925692827f6ee100416a34fa516f Mon Sep 17 00:00:00 2001
From: David Baker
Date: Tue, 16 May 2017 14:26:46 +0100
Subject: [PATCH 0096/1016] Revert "Fix 'start chat' button"
This reverts commit c841eb641b85995bddb235d3db2c779daffe97a1.
---
src/components/views/elements/StartChatButton.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/views/elements/StartChatButton.js b/src/components/views/elements/StartChatButton.js
index 747f75d1b3..02d5677a7c 100644
--- a/src/components/views/elements/StartChatButton.js
+++ b/src/components/views/elements/StartChatButton.js
@@ -21,7 +21,7 @@ import PropTypes from 'prop-types';
const StartChatButton = function(props) {
const ActionButton = sdk.getComponent('elements.ActionButton');
return (
-
Date: Tue, 16 May 2017 14:30:02 +0100
Subject: [PATCH 0097/1016] Revert "Fix Create Room button"
This reverts commit 9cae667c063e67b32e60b89e7256d714a056559b.
---
src/components/views/elements/CreateRoomButton.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/views/elements/CreateRoomButton.js b/src/components/views/elements/CreateRoomButton.js
index 73c984a860..e7e526d36b 100644
--- a/src/components/views/elements/CreateRoomButton.js
+++ b/src/components/views/elements/CreateRoomButton.js
@@ -21,7 +21,7 @@ import PropTypes from 'prop-types';
const CreateRoomButton = function(props) {
const ActionButton = sdk.getComponent('elements.ActionButton');
return (
-
Date: Tue, 16 May 2017 14:49:55 +0100
Subject: [PATCH 0098/1016] Revert "Merge pull request #859 from
matrix-org/dbkr/left_panel_for_newbies_2"
This reverts commit 3366d3bbae203ce28ff85f8d7b80a6e3077a02a6, reversing
changes made to ceb71a4ef65607cfcf5c50452839edb1aef40593.
---
package.json | 1 -
src/components/views/elements/ActionButton.js | 80 ------------
.../views/elements/CreateRoomButton.js | 38 ------
src/components/views/elements/HomeButton.js | 38 ------
.../views/elements/RoomDirectoryButton.js | 38 ------
.../views/elements/SettingsButton.js | 38 ------
.../views/elements/StartChatButton.js | 38 ------
src/components/views/rooms/RoomList.js | 121 ++++--------------
8 files changed, 24 insertions(+), 368 deletions(-)
delete mode 100644 src/components/views/elements/ActionButton.js
delete mode 100644 src/components/views/elements/CreateRoomButton.js
delete mode 100644 src/components/views/elements/HomeButton.js
delete mode 100644 src/components/views/elements/RoomDirectoryButton.js
delete mode 100644 src/components/views/elements/SettingsButton.js
delete mode 100644 src/components/views/elements/StartChatButton.js
diff --git a/package.json b/package.json
index 21add8ccb7..3e1fa2d8f3 100644
--- a/package.json
+++ b/package.json
@@ -65,7 +65,6 @@
"lodash": "^4.13.1",
"matrix-js-sdk": "matrix-org/matrix-js-sdk#develop",
"optimist": "^0.6.1",
- "prop-types": "^15.5.8",
"q": "^1.4.1",
"react": "^15.4.0",
"react-addons-css-transition-group": "15.3.2",
diff --git a/src/components/views/elements/ActionButton.js b/src/components/views/elements/ActionButton.js
deleted file mode 100644
index 267388daf6..0000000000
--- a/src/components/views/elements/ActionButton.js
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
-Copyright 2017 Vector Creations Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-import React from 'react';
-import PropTypes from 'prop-types';
-import AccessibleButton from './AccessibleButton';
-import dis from '../../../dispatcher';
-import sdk from '../../../index';
-
-export default React.createClass({
- displayName: 'RoleButton',
-
- propTypes: {
- size: PropTypes.string,
- tooltip: PropTypes.bool,
- action: PropTypes.string.isRequired,
- label: PropTypes.string.isRequired,
- iconPath: PropTypes.string.isRequired,
- },
-
- getDefaultProps: function() {
- return {
- size: "25",
- tooltip: false,
- };
- },
-
- getInitialState: function() {
- return {
- showTooltip: false,
- };
- },
-
- _onClick: function(ev) {
- ev.stopPropagation();
- dis.dispatch({action: this.props.action});
- },
-
- _onMouseEnter: function() {
- if (this.props.tooltip) this.setState({showTooltip: true});
- },
-
- _onMouseLeave: function() {
- this.setState({showTooltip: false});
- },
-
- render: function() {
- const TintableSvg = sdk.getComponent("elements.TintableSvg");
-
- let tooltip;
- if (this.state.showTooltip) {
- const RoomTooltip = sdk.getComponent("rooms.RoomTooltip");
- tooltip = ;
- }
-
- return (
-
-
- {tooltip}
-
- );
- }
-});
diff --git a/src/components/views/elements/CreateRoomButton.js b/src/components/views/elements/CreateRoomButton.js
deleted file mode 100644
index e7e526d36b..0000000000
--- a/src/components/views/elements/CreateRoomButton.js
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
-Copyright 2017 Vector Creations Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-import React from 'react';
-import sdk from '../../../index';
-import PropTypes from 'prop-types';
-
-const CreateRoomButton = function(props) {
- const ActionButton = sdk.getComponent('elements.ActionButton');
- return (
-
- );
-};
-
-CreateRoomButton.propTypes = {
- size: PropTypes.string,
- tooltip: PropTypes.bool,
-};
-
-export default CreateRoomButton;
diff --git a/src/components/views/elements/HomeButton.js b/src/components/views/elements/HomeButton.js
deleted file mode 100644
index 5c446f24c9..0000000000
--- a/src/components/views/elements/HomeButton.js
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
-Copyright 2017 Vector Creations Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-import React from 'react';
-import sdk from '../../../index';
-import PropTypes from 'prop-types';
-
-const HomeButton = function(props) {
- const ActionButton = sdk.getComponent('elements.ActionButton');
- return (
-
- );
-};
-
-HomeButton.propTypes = {
- size: PropTypes.string,
- tooltip: PropTypes.bool,
-};
-
-export default HomeButton;
diff --git a/src/components/views/elements/RoomDirectoryButton.js b/src/components/views/elements/RoomDirectoryButton.js
deleted file mode 100644
index 5e68776a15..0000000000
--- a/src/components/views/elements/RoomDirectoryButton.js
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
-Copyright 2017 Vector Creations Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-import React from 'react';
-import sdk from '../../../index';
-import PropTypes from 'prop-types';
-
-const RoomDirectoryButton = function(props) {
- const ActionButton = sdk.getComponent('elements.ActionButton');
- return (
-
- );
-};
-
-RoomDirectoryButton.propTypes = {
- size: PropTypes.string,
- tooltip: PropTypes.bool,
-};
-
-export default RoomDirectoryButton;
diff --git a/src/components/views/elements/SettingsButton.js b/src/components/views/elements/SettingsButton.js
deleted file mode 100644
index c6438da277..0000000000
--- a/src/components/views/elements/SettingsButton.js
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
-Copyright 2017 Vector Creations Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-import React from 'react';
-import sdk from '../../../index';
-import PropTypes from 'prop-types';
-
-const SettingsButton = function(props) {
- const ActionButton = sdk.getComponent('elements.ActionButton');
- return (
-
- );
-};
-
-SettingsButton.propTypes = {
- size: PropTypes.string,
- tooltip: PropTypes.bool,
-};
-
-export default SettingsButton;
diff --git a/src/components/views/elements/StartChatButton.js b/src/components/views/elements/StartChatButton.js
deleted file mode 100644
index 02d5677a7c..0000000000
--- a/src/components/views/elements/StartChatButton.js
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
-Copyright 2017 Vector Creations Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-import React from 'react';
-import sdk from '../../../index';
-import PropTypes from 'prop-types';
-
-const StartChatButton = function(props) {
- const ActionButton = sdk.getComponent('elements.ActionButton');
- return (
-
- );
-};
-
-StartChatButton.propTypes = {
- size: PropTypes.string,
- tooltip: PropTypes.bool,
-};
-
-export default StartChatButton;
diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js
index a595a91ba9..49af1560f1 100644
--- a/src/components/views/rooms/RoomList.js
+++ b/src/components/views/rooms/RoomList.js
@@ -1,6 +1,5 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
-Copyright 2017 Vector Creations Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -29,16 +28,8 @@ var Rooms = require('../../../Rooms');
import DMRoomMap from '../../../utils/DMRoomMap';
var Receipt = require('../../../utils/Receipt');
var constantTimeDispatcher = require('../../../ConstantTimeDispatcher');
-import AccessibleButton from '../elements/AccessibleButton';
-const HIDE_CONFERENCE_CHANS = true;
-
-const VERBS = {
- 'm.favourite': 'favourite',
- 'im.vector.fake.direct': 'tag direct chat',
- 'im.vector.fake.recent': 'restore',
- 'm.lowpriority': 'demote',
-};
+var HIDE_CONFERENCE_CHANS = true;
module.exports = React.createClass({
displayName: 'RoomList',
@@ -62,7 +53,6 @@ module.exports = React.createClass({
getInitialState: function() {
return {
isLoadingLeftRooms: false,
- totalRoomCount: null,
lists: {},
incomingCall: null,
};
@@ -83,7 +73,8 @@ module.exports = React.createClass({
// lookup for which lists a given roomId is currently in.
this.listsForRoomId = {};
- this.refreshRoomList();
+ var s = this.getRoomLists();
+ this.setState(s);
// order of the sublists
//this.listOrder = [];
@@ -326,29 +317,21 @@ module.exports = React.createClass({
// any changes to it incrementally, updating the appropriate sublists
// as needed.
// Alternatively we'd do something magical with Immutable.js or similar.
- const lists = this.getRoomLists();
- let totalRooms = 0;
- for (const l of Object.values(lists)) {
- totalRooms += l.length;
- }
- this.setState({
- lists: this.getRoomLists(),
- totalRoomCount: totalRooms,
- });
-
+ this.setState(this.getRoomLists());
+
// this._lastRefreshRoomListTs = Date.now();
},
getRoomLists: function() {
var self = this;
- const lists = {};
+ var s = { lists: {} };
- lists["im.vector.fake.invite"] = [];
- lists["m.favourite"] = [];
- lists["im.vector.fake.recent"] = [];
- lists["im.vector.fake.direct"] = [];
- lists["m.lowpriority"] = [];
- lists["im.vector.fake.archived"] = [];
+ s.lists["im.vector.fake.invite"] = [];
+ s.lists["m.favourite"] = [];
+ s.lists["im.vector.fake.recent"] = [];
+ s.lists["im.vector.fake.direct"] = [];
+ s.lists["m.lowpriority"] = [];
+ s.lists["im.vector.fake.archived"] = [];
this.listsForRoomId = {};
var otherTagNames = {};
@@ -370,7 +353,7 @@ module.exports = React.createClass({
if (me.membership == "invite") {
self.listsForRoomId[room.roomId].push("im.vector.fake.invite");
- lists["im.vector.fake.invite"].push(room);
+ s.lists["im.vector.fake.invite"].push(room);
}
else if (HIDE_CONFERENCE_CHANS && Rooms.isConfCallRoom(room, me, self.props.ConferenceHandler)) {
// skip past this room & don't put it in any lists
@@ -383,8 +366,8 @@ module.exports = React.createClass({
if (tagNames.length) {
for (var i = 0; i < tagNames.length; i++) {
var tagName = tagNames[i];
- lists[tagName] = lists[tagName] || [];
- lists[tagName].push(room);
+ s.lists[tagName] = s.lists[tagName] || [];
+ s.lists[tagName].push(room);
self.listsForRoomId[room.roomId].push(tagName);
otherTagNames[tagName] = 1;
}
@@ -392,16 +375,16 @@ module.exports = React.createClass({
else if (dmRoomMap.getUserIdForRoomId(room.roomId)) {
// "Direct Message" rooms (that we're still in and that aren't otherwise tagged)
self.listsForRoomId[room.roomId].push("im.vector.fake.direct");
- lists["im.vector.fake.direct"].push(room);
+ s.lists["im.vector.fake.direct"].push(room);
}
else {
self.listsForRoomId[room.roomId].push("im.vector.fake.recent");
- lists["im.vector.fake.recent"].push(room);
+ s.lists["im.vector.fake.recent"].push(room);
}
}
else if (me.membership === "leave") {
self.listsForRoomId[room.roomId].push("im.vector.fake.archived");
- lists["im.vector.fake.archived"].push(room);
+ s.lists["im.vector.fake.archived"].push(room);
}
else {
console.error("unrecognised membership: " + me.membership + " - this should never happen");
@@ -425,7 +408,7 @@ module.exports = React.createClass({
];
*/
- return lists;
+ return s;
},
_getScrollNode: function() {
@@ -455,7 +438,6 @@ module.exports = React.createClass({
var incomingCallBox = document.getElementById("incomingCallBox");
if (incomingCallBox && incomingCallBox.parentElement) {
var scrollArea = this._getScrollNode();
- if (!scrollArea) return;
// Use the offset of the top of the scroll area from the window
// as this is used to calculate the CSS fixed top position for the stickies
var scrollAreaOffset = scrollArea.getBoundingClientRect().top + window.pageYOffset;
@@ -479,7 +461,6 @@ module.exports = React.createClass({
// properly through React
_initAndPositionStickyHeaders: function(initialise, scrollToPosition) {
var scrollArea = this._getScrollNode();
- if (!scrollArea) return;
// Use the offset of the top of the scroll area from the window
// as this is used to calculate the CSS fixed top position for the stickies
var scrollAreaOffset = scrollArea.getBoundingClientRect().top + window.pageYOffset;
@@ -577,58 +558,6 @@ module.exports = React.createClass({
this.refs.gemscroll.forceUpdate();
},
- _getEmptyContent: function(section) {
- const RoomDropTarget = sdk.getComponent('rooms.RoomDropTarget');
-
- if (this.props.collapsed) {
- return ;
- }
-
- const StartChatButton = sdk.getComponent('elements.StartChatButton');
- const RoomDirectoryButton = sdk.getComponent('elements.RoomDirectoryButton');
- const CreateRoomButton = sdk.getComponent('elements.CreateRoomButton');
- if (this.state.totalRoomCount === 0) {
- const TintableSvg = sdk.getComponent('elements.TintableSvg');
- switch (section) {
- case 'im.vector.fake.direct':
- return
- Press
-
- to start a chat with someone
-
;
- case 'im.vector.fake.recent':
- return
- You're not in any rooms yet! Press
-
- to make a room or
-
- to browse the directory
-
;
- }
- }
-
- const labelText = 'Drop here to ' + (VERBS[section] || 'tag ' + section);
-
- return ;
- },
-
- _getHeaderItems: function(section) {
- const StartChatButton = sdk.getComponent('elements.StartChatButton');
- const RoomDirectoryButton = sdk.getComponent('elements.RoomDirectoryButton');
- const CreateRoomButton = sdk.getComponent('elements.CreateRoomButton');
- switch (section) {
- case 'im.vector.fake.direct':
- return
-
- ;
- case 'im.vector.fake.recent':
- return
-
-
- ;
- }
- },
-
render: function() {
var RoomSubList = sdk.getComponent('structures.RoomSubList');
var self = this;
@@ -652,7 +581,7 @@ module.exports = React.createClass({
Date: Tue, 16 May 2017 14:50:19 +0100
Subject: [PATCH 0099/1016] Revert "Merge pull request #841 from
matrix-org/luke/fix-double-dialogs"
This reverts commit 1913a32fbd0c303080645f2661cacff25ccff232, reversing
changes made to 0c16298c452089188bcc156cf213358988ce7341.
---
src/components/views/dialogs/BaseDialog.js | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/src/components/views/dialogs/BaseDialog.js b/src/components/views/dialogs/BaseDialog.js
index 279dedbd43..d0f34c5fbd 100644
--- a/src/components/views/dialogs/BaseDialog.js
+++ b/src/components/views/dialogs/BaseDialog.js
@@ -57,9 +57,7 @@ export default React.createClass({
}
},
- // Must be when the key is released (and not pressed) otherwise componentWillUnmount
- // will focus another element which will receive future key events
- _onKeyUp: function(e) {
+ _onKeyDown: function(e) {
if (e.keyCode === KeyCode.ESCAPE) {
e.stopPropagation();
e.preventDefault();
@@ -81,7 +79,7 @@ export default React.createClass({
const TintableSvg = sdk.getComponent("elements.TintableSvg");
return (
-
+
From ff9c40472a5b1f52450347a890ea720c78e1aa39 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Tue, 16 May 2017 14:50:29 +0100
Subject: [PATCH 0100/1016] Revert "Merge pull request #822 from
t3chguy/BaseDialog_restore_focus"
This reverts commit 0ac836919d54cd04705002d8e53d3514dcce8dde, reversing
changes made to 7e07ffd55fe1475ce30e172fa5b0b37061e7375f.
---
src/components/views/dialogs/BaseDialog.js | 12 +-----------
1 file changed, 1 insertion(+), 11 deletions(-)
diff --git a/src/components/views/dialogs/BaseDialog.js b/src/components/views/dialogs/BaseDialog.js
index d0f34c5fbd..0b2ca5225d 100644
--- a/src/components/views/dialogs/BaseDialog.js
+++ b/src/components/views/dialogs/BaseDialog.js
@@ -47,16 +47,6 @@ export default React.createClass({
children: React.PropTypes.node,
},
- componentWillMount: function() {
- this.priorActiveElement = document.activeElement;
- },
-
- componentWillUnmount: function() {
- if (this.priorActiveElement !== null) {
- this.priorActiveElement.focus();
- }
- },
-
_onKeyDown: function(e) {
if (e.keyCode === KeyCode.ESCAPE) {
e.stopPropagation();
@@ -77,7 +67,7 @@ export default React.createClass({
render: function() {
const TintableSvg = sdk.getComponent("elements.TintableSvg");
-
+
return (
Date: Tue, 16 May 2017 15:35:22 +0100
Subject: [PATCH 0101/1016] Revert "comment out spammy CTD logging"
This reverts commit 19482d751d82c162321d99c5bf95ac8fe950768b.
---
src/ConstantTimeDispatcher.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/ConstantTimeDispatcher.js b/src/ConstantTimeDispatcher.js
index 6c2c3266aa..265ee11fd4 100644
--- a/src/ConstantTimeDispatcher.js
+++ b/src/ConstantTimeDispatcher.js
@@ -47,7 +47,7 @@ class ConstantTimeDispatcher {
dispatch(type, arg, params) {
if (!this.listeners[type] || !this.listeners[type][arg]) {
- //console.warn("No registered listeners for dispatch (type=" + type + ", arg=" + arg + ")");
+ console.warn("No registered listeners for dispatch (type=" + type + ", arg=" + arg + ")");
return;
}
this.listeners[type][arg].forEach(listener=>{
From 714cd6a10fd49df86caa9df4406e7a31cc7bfea1 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Tue, 16 May 2017 16:00:34 +0100
Subject: [PATCH 0102/1016] Revert "recalculate roomlist when your invites
change"
This reverts commit ec6a1c4c750f959017cdf823402a6c9d86b16fe2.
---
src/components/views/rooms/RoomList.js | 13 +++----------
1 file changed, 3 insertions(+), 10 deletions(-)
diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js
index 49af1560f1..101f0838a9 100644
--- a/src/components/views/rooms/RoomList.js
+++ b/src/components/views/rooms/RoomList.js
@@ -265,16 +265,9 @@ module.exports = React.createClass({
},
onRoomStateMember: function(ev, state, member) {
- if (ev.getStateKey() === MatrixClientPeg.get().credentials.userId &&
- ev.getPrevContent() && ev.getPrevContent().membership === "invite")
- {
- this._delayedRefreshRoomList();
- }
- else {
- constantTimeDispatcher.dispatch(
- "RoomTile.refresh", member.roomId, {}
- );
- }
+ constantTimeDispatcher.dispatch(
+ "RoomTile.refresh", member.roomId, {}
+ );
},
onRoomMemberName: function(ev, member) {
From b063c605a86bdd9f216506e09bd93f54e42a29df Mon Sep 17 00:00:00 2001
From: David Baker
Date: Tue, 16 May 2017 16:01:14 +0100
Subject: [PATCH 0103/1016] Revert "fix stupid typos in RoomList's
shouldComponentUpdate"
This reverts commit b0288ebd89841ad362a6804b41839ef9d3bdca7f.
---
src/components/views/rooms/RoomList.js | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js
index 101f0838a9..b99d26376f 100644
--- a/src/components/views/rooms/RoomList.js
+++ b/src/components/views/rooms/RoomList.js
@@ -44,9 +44,9 @@ module.exports = React.createClass({
shouldComponentUpdate: function(nextProps, nextState) {
if (nextProps.collapsed !== this.props.collapsed) return true;
if (nextProps.searchFilter !== this.props.searchFilter) return true;
- if (nextState.lists !== this.state.lists ||
- nextState.isLoadingLeftRooms !== this.state.isLoadingLeftRooms ||
- nextState.incomingCall !== this.state.incomingCall) return true;
+ if (nextState.lists !== this.props.lists ||
+ nextState.isLoadingLeftRooms !== this.isLoadingLeftRooms ||
+ nextState.incomingCall !== this.incomingCall) return true;
return false;
},
From 7a949b6a4591cdb7c5fe93b8aef610388ef8eba4 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Tue, 16 May 2017 16:01:32 +0100
Subject: [PATCH 0104/1016] Revert "oops, actually refresh roomlist when its
state changes!"
This reverts commit 35a16edcccd07fdd4209783ce1efaee8115eb4b4.
---
src/components/views/rooms/RoomList.js | 3 ---
1 file changed, 3 deletions(-)
diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js
index b99d26376f..b4ab25a0f2 100644
--- a/src/components/views/rooms/RoomList.js
+++ b/src/components/views/rooms/RoomList.js
@@ -44,9 +44,6 @@ module.exports = React.createClass({
shouldComponentUpdate: function(nextProps, nextState) {
if (nextProps.collapsed !== this.props.collapsed) return true;
if (nextProps.searchFilter !== this.props.searchFilter) return true;
- if (nextState.lists !== this.props.lists ||
- nextState.isLoadingLeftRooms !== this.isLoadingLeftRooms ||
- nextState.incomingCall !== this.incomingCall) return true;
return false;
},
From eddc2af92d7a465f755c472f3ea3da3cca3a185f Mon Sep 17 00:00:00 2001
From: David Baker
Date: Tue, 16 May 2017 16:01:54 +0100
Subject: [PATCH 0105/1016] Revert "HOW DID THIS EVER WORK?"
This reverts commit 0d8d3c67106a3e84fd30de4016b9c852870f99b3.
---
src/components/views/rooms/RoomList.js | 3 ---
1 file changed, 3 deletions(-)
diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js
index b4ab25a0f2..b86d17d290 100644
--- a/src/components/views/rooms/RoomList.js
+++ b/src/components/views/rooms/RoomList.js
@@ -558,7 +558,6 @@ module.exports = React.createClass({
Date: Tue, 16 May 2017 16:02:13 +0100
Subject: [PATCH 0106/1016] Revert "unbreak stack overflow which fires on tests
due to mocked timers"
This reverts commit e69ea68133bb01dfd2093ffc5644edef24fbed70.
---
src/components/views/rooms/RoomList.js | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)
diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js
index b86d17d290..d6e2ab633d 100644
--- a/src/components/views/rooms/RoomList.js
+++ b/src/components/views/rooms/RoomList.js
@@ -74,11 +74,7 @@ module.exports = React.createClass({
this.setState(s);
// order of the sublists
- //this.listOrder = [];
-
- // loop count to stop a stack overflow if the user keeps waggling the
- // mouse for >30s in a row, or if running under mocha
- this._delayedRefreshRoomListLoopCount = 0
+ this.listOrder = [];
},
componentDidMount: function() {
@@ -288,12 +284,10 @@ module.exports = React.createClass({
// if the mouse has been moving over the RoomList in the last 500ms
// then delay the refresh further to avoid bouncing around under the
// cursor
- if (Date.now() - this._lastMouseOverTs > 500 || this._delayedRefreshRoomListLoopCount > 60) {
+ if (Date.now() - this._lastMouseOverTs > 500) {
this.refreshRoomList();
- this._delayedRefreshRoomListLoopCount = 0;
}
else {
- this._delayedRefreshRoomListLoopCount++;
this._delayedRefreshRoomList();
}
}, 500),
From ebfafb363972e77ef02f0e518573bf68ebc0ed15 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Tue, 16 May 2017 16:11:01 +0100
Subject: [PATCH 0107/1016] Revert "Merge pull request #807 from
matrix-org/matthew/quick-search"
This reverts commit 0ad1d8caf3f83aca517d1afff492f1c3d01e9ad5, reversing
changes made to 1189368aab8cfb22c1895f8ce6c0d8a8fbe7ca0b.
---
package.json | 2 +-
src/ConstantTimeDispatcher.js | 62 -----
src/KeyCode.js | 1 -
src/components/structures/TimelinePanel.js | 1 -
.../views/dialogs/QuestionDialog.js | 8 +-
src/components/views/rooms/RoomList.js | 233 ++++++------------
src/components/views/rooms/RoomTile.js | 57 ++---
7 files changed, 99 insertions(+), 265 deletions(-)
delete mode 100644 src/ConstantTimeDispatcher.js
diff --git a/package.json b/package.json
index 3e1fa2d8f3..444d1c5369 100644
--- a/package.json
+++ b/package.json
@@ -69,7 +69,7 @@
"react": "^15.4.0",
"react-addons-css-transition-group": "15.3.2",
"react-dom": "^15.4.0",
- "react-gemini-scrollbar": "matrix-org/react-gemini-scrollbar#39d858c",
+ "react-gemini-scrollbar": "matrix-org/react-gemini-scrollbar#5e97aef",
"sanitize-html": "^1.11.1",
"text-encoding-utf-8": "^1.0.1",
"velocity-vector": "vector-im/velocity#059e3b2",
diff --git a/src/ConstantTimeDispatcher.js b/src/ConstantTimeDispatcher.js
deleted file mode 100644
index 265ee11fd4..0000000000
--- a/src/ConstantTimeDispatcher.js
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
-Copyright 2017 Vector Creations Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-// singleton which dispatches invocations of a given type & argument
-// rather than just a type (as per EventEmitter and Flux's dispatcher etc)
-//
-// This means you can have a single point which listens for an EventEmitter event
-// and then dispatches out to one of thousands of RoomTiles (for instance) rather than
-// having each RoomTile register for the EventEmitter event and having to
-// iterate over all of them.
-class ConstantTimeDispatcher {
- constructor() {
- // type -> arg -> [ listener(arg, params) ]
- this.listeners = {};
- }
-
- register(type, arg, listener) {
- if (!this.listeners[type]) this.listeners[type] = {};
- if (!this.listeners[type][arg]) this.listeners[type][arg] = [];
- this.listeners[type][arg].push(listener);
- }
-
- unregister(type, arg, listener) {
- if (this.listeners[type] && this.listeners[type][arg]) {
- var i = this.listeners[type][arg].indexOf(listener);
- if (i > -1) {
- this.listeners[type][arg].splice(i, 1);
- }
- }
- else {
- console.warn("Unregistering unrecognised listener (type=" + type + ", arg=" + arg + ")");
- }
- }
-
- dispatch(type, arg, params) {
- if (!this.listeners[type] || !this.listeners[type][arg]) {
- console.warn("No registered listeners for dispatch (type=" + type + ", arg=" + arg + ")");
- return;
- }
- this.listeners[type][arg].forEach(listener=>{
- listener.call(arg, params);
- });
- }
-}
-
-if (!global.constantTimeDispatcher) {
- global.constantTimeDispatcher = new ConstantTimeDispatcher();
-}
-module.exports = global.constantTimeDispatcher;
diff --git a/src/KeyCode.js b/src/KeyCode.js
index f164dbc15c..c9cac01239 100644
--- a/src/KeyCode.js
+++ b/src/KeyCode.js
@@ -32,5 +32,4 @@ module.exports = {
DELETE: 46,
KEY_D: 68,
KEY_E: 69,
- KEY_K: 75,
};
diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js
index de43bd1c19..8794713501 100644
--- a/src/components/structures/TimelinePanel.js
+++ b/src/components/structures/TimelinePanel.js
@@ -590,7 +590,6 @@ var TimelinePanel = React.createClass({
this.props.timelineSet.room.setUnreadNotificationCount('highlight', 0);
dis.dispatch({
action: 'on_room_read',
- room: this.props.timelineSet.room,
});
}
}
diff --git a/src/components/views/dialogs/QuestionDialog.js b/src/components/views/dialogs/QuestionDialog.js
index 8e20b0d2bc..6012541b94 100644
--- a/src/components/views/dialogs/QuestionDialog.js
+++ b/src/components/views/dialogs/QuestionDialog.js
@@ -47,12 +47,6 @@ export default React.createClass({
this.props.onFinished(false);
},
- componentDidMount: function() {
- if (this.props.focus) {
- this.refs.button.focus();
- }
- },
-
render: function() {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
const cancelButton = this.props.hasCancelButton ? (
@@ -69,7 +63,7 @@ export default React.createClass({
{this.props.description}
-
+
{this.props.button}
{this.props.extraButtons}
diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js
index d6e2ab633d..611dd10780 100644
--- a/src/components/views/rooms/RoomList.js
+++ b/src/components/views/rooms/RoomList.js
@@ -21,13 +21,13 @@ var GeminiScrollbar = require('react-gemini-scrollbar');
var MatrixClientPeg = require("../../../MatrixClientPeg");
var CallHandler = require('../../../CallHandler');
var RoomListSorter = require("../../../RoomListSorter");
+var Unread = require('../../../Unread');
var dis = require("../../../dispatcher");
var sdk = require('../../../index');
var rate_limited_func = require('../../../ratelimitedfunc');
var Rooms = require('../../../Rooms');
import DMRoomMap from '../../../utils/DMRoomMap';
var Receipt = require('../../../utils/Receipt');
-var constantTimeDispatcher = require('../../../ConstantTimeDispatcher');
var HIDE_CONFERENCE_CHANS = true;
@@ -37,16 +37,10 @@ module.exports = React.createClass({
propTypes: {
ConferenceHandler: React.PropTypes.any,
collapsed: React.PropTypes.bool.isRequired,
- selectedRoom: React.PropTypes.string,
+ currentRoom: React.PropTypes.string,
searchFilter: React.PropTypes.string,
},
- shouldComponentUpdate: function(nextProps, nextState) {
- if (nextProps.collapsed !== this.props.collapsed) return true;
- if (nextProps.searchFilter !== this.props.searchFilter) return true;
- return false;
- },
-
getInitialState: function() {
return {
isLoadingLeftRooms: false,
@@ -63,18 +57,12 @@ module.exports = React.createClass({
cli.on("Room.name", this.onRoomName);
cli.on("Room.tags", this.onRoomTags);
cli.on("Room.receipt", this.onRoomReceipt);
- cli.on("RoomState.members", this.onRoomStateMember);
+ cli.on("RoomState.events", this.onRoomStateEvents);
cli.on("RoomMember.name", this.onRoomMemberName);
cli.on("accountData", this.onAccountData);
- // lookup for which lists a given roomId is currently in.
- this.listsForRoomId = {};
-
var s = this.getRoomLists();
this.setState(s);
-
- // order of the sublists
- this.listOrder = [];
},
componentDidMount: function() {
@@ -83,22 +71,7 @@ module.exports = React.createClass({
this._updateStickyHeaders(true);
},
- componentWillReceiveProps: function(nextProps) {
- // short-circuit react when the room changes
- // to avoid rerendering all the sublists everywhere
- if (nextProps.selectedRoom !== this.props.selectedRoom) {
- if (this.props.selectedRoom) {
- constantTimeDispatcher.dispatch(
- "RoomTile.select", this.props.selectedRoom, {}
- );
- }
- constantTimeDispatcher.dispatch(
- "RoomTile.select", nextProps.selectedRoom, { selected: true }
- );
- }
- },
-
- componentDidUpdate: function(prevProps, prevState) {
+ componentDidUpdate: function() {
// Reinitialise the stickyHeaders when the component is updated
this._updateStickyHeaders(true);
this._repositionIncomingCallBox(undefined, false);
@@ -124,24 +97,10 @@ module.exports = React.createClass({
}
break;
case 'on_room_read':
- // poke the right RoomTile to refresh, using the constantTimeDispatcher
- // to avoid each and every RoomTile registering to the 'on_room_read' event
- // XXX: if we like the constantTimeDispatcher we might want to dispatch
- // directly from TimelinePanel rather than needlessly bouncing via here.
- constantTimeDispatcher.dispatch(
- "RoomTile.refresh", payload.room.roomId, {}
- );
-
- // also have to poke the right list(s)
- var lists = this.listsForRoomId[payload.room.roomId];
- if (lists) {
- lists.forEach(list=>{
- constantTimeDispatcher.dispatch(
- "RoomSubList.refreshHeader", list, { room: payload.room }
- );
- });
- }
-
+ // Force an update because the notif count state is too deep to cause
+ // an update. This forces the local echo of reading notifs to be
+ // reflected by the RoomTiles.
+ this.forceUpdate();
break;
}
},
@@ -155,7 +114,7 @@ module.exports = React.createClass({
MatrixClientPeg.get().removeListener("Room.name", this.onRoomName);
MatrixClientPeg.get().removeListener("Room.tags", this.onRoomTags);
MatrixClientPeg.get().removeListener("Room.receipt", this.onRoomReceipt);
- MatrixClientPeg.get().removeListener("RoomState.members", this.onRoomStateMember);
+ MatrixClientPeg.get().removeListener("RoomState.events", this.onRoomStateEvents);
MatrixClientPeg.get().removeListener("RoomMember.name", this.onRoomMemberName);
MatrixClientPeg.get().removeListener("accountData", this.onAccountData);
}
@@ -164,14 +123,10 @@ module.exports = React.createClass({
},
onRoom: function(room) {
- // XXX: this happens rarely; ideally we should only update the correct
- // sublists when it does (e.g. via a constantTimeDispatch to the right sublist)
this._delayedRefreshRoomList();
},
onDeleteRoom: function(roomId) {
- // XXX: this happens rarely; ideally we should only update the correct
- // sublists when it does (e.g. via a constantTimeDispatch to the right sublist)
this._delayedRefreshRoomList();
},
@@ -194,10 +149,6 @@ module.exports = React.createClass({
}
},
- _onMouseOver: function(ev) {
- this._lastMouseOverTs = Date.now();
- },
-
onSubListHeaderClick: function(isHidden, scrollToPosition) {
// The scroll area has expanded or contracted, so re-calculate sticky headers positions
this._updateStickyHeaders(true, scrollToPosition);
@@ -207,89 +158,41 @@ module.exports = React.createClass({
if (toStartOfTimeline) return;
if (!room) return;
if (data.timeline.getTimelineSet() !== room.getUnfilteredTimelineSet()) return;
-
- // rather than regenerate our full roomlists, which is very heavy, we poke the
- // correct sublists to just re-sort themselves. This isn't enormously reacty,
- // but is much faster than the default react reconciler, or having to do voodoo
- // with shouldComponentUpdate and a pleaseRefresh property or similar.
- var lists = this.listsForRoomId[room.roomId];
- if (lists) {
- lists.forEach(list=>{
- constantTimeDispatcher.dispatch("RoomSubList.sort", list, { room: room });
- });
- }
-
- // we have to explicitly hit the roomtile which just changed
- constantTimeDispatcher.dispatch(
- "RoomTile.refresh", room.roomId, {}
- );
+ this._delayedRefreshRoomList();
},
onRoomReceipt: function(receiptEvent, room) {
// because if we read a notification, it will affect notification count
// only bother updating if there's a receipt from us
if (Receipt.findReadReceiptFromUserId(receiptEvent, MatrixClientPeg.get().credentials.userId)) {
- var lists = this.listsForRoomId[room.roomId];
- if (lists) {
- lists.forEach(list=>{
- constantTimeDispatcher.dispatch(
- "RoomSubList.refreshHeader", list, { room: room }
- );
- });
- }
-
- // we have to explicitly hit the roomtile which just changed
- constantTimeDispatcher.dispatch(
- "RoomTile.refresh", room.roomId, {}
- );
+ this._delayedRefreshRoomList();
}
},
onRoomName: function(room) {
- constantTimeDispatcher.dispatch(
- "RoomTile.refresh", room.roomId, {}
- );
- },
-
- onRoomTags: function(event, room) {
- // XXX: this happens rarely; ideally we should only update the correct
- // sublists when it does (e.g. via a constantTimeDispatch to the right sublist)
this._delayedRefreshRoomList();
},
- onRoomStateMember: function(ev, state, member) {
- constantTimeDispatcher.dispatch(
- "RoomTile.refresh", member.roomId, {}
- );
+ onRoomTags: function(event, room) {
+ this._delayedRefreshRoomList();
+ },
+
+ onRoomStateEvents: function(ev, state) {
+ this._delayedRefreshRoomList();
},
onRoomMemberName: function(ev, member) {
- constantTimeDispatcher.dispatch(
- "RoomTile.refresh", member.roomId, {}
- );
+ this._delayedRefreshRoomList();
},
onAccountData: function(ev) {
if (ev.getType() == 'm.direct') {
- // XXX: this happens rarely; ideally we should only update the correct
- // sublists when it does (e.g. via a constantTimeDispatch to the right sublist)
- this._delayedRefreshRoomList();
- }
- else if (ev.getType() == 'm.push_rules') {
this._delayedRefreshRoomList();
}
},
_delayedRefreshRoomList: new rate_limited_func(function() {
- // if the mouse has been moving over the RoomList in the last 500ms
- // then delay the refresh further to avoid bouncing around under the
- // cursor
- if (Date.now() - this._lastMouseOverTs > 500) {
- this.refreshRoomList();
- }
- else {
- this._delayedRefreshRoomList();
- }
+ this.refreshRoomList();
}, 500),
refreshRoomList: function() {
@@ -297,10 +200,12 @@ module.exports = React.createClass({
// (!this._lastRefreshRoomListTs ? "-" : (Date.now() - this._lastRefreshRoomListTs))
// );
- // TODO: ideally we'd calculate this once at start, and then maintain
- // any changes to it incrementally, updating the appropriate sublists
- // as needed.
- // Alternatively we'd do something magical with Immutable.js or similar.
+ // TODO: rather than bluntly regenerating and re-sorting everything
+ // every time we see any kind of room change from the JS SDK
+ // we could do incremental updates on our copy of the state
+ // based on the room which has actually changed. This would stop
+ // us re-rendering all the sublists every time anything changes anywhere
+ // in the state of the client.
this.setState(this.getRoomLists());
// this._lastRefreshRoomListTs = Date.now();
@@ -317,9 +222,6 @@ module.exports = React.createClass({
s.lists["m.lowpriority"] = [];
s.lists["im.vector.fake.archived"] = [];
- this.listsForRoomId = {};
- var otherTagNames = {};
-
const dmRoomMap = new DMRoomMap(MatrixClientPeg.get());
MatrixClientPeg.get().getRooms().forEach(function(room) {
@@ -331,12 +233,7 @@ module.exports = React.createClass({
// ", target = " + me.events.member.getStateKey() +
// ", prevMembership = " + me.events.member.getPrevContent().membership);
- if (!self.listsForRoomId[room.roomId]) {
- self.listsForRoomId[room.roomId] = [];
- }
-
if (me.membership == "invite") {
- self.listsForRoomId[room.roomId].push("im.vector.fake.invite");
s.lists["im.vector.fake.invite"].push(room);
}
else if (HIDE_CONFERENCE_CHANS && Rooms.isConfCallRoom(room, me, self.props.ConferenceHandler)) {
@@ -347,27 +244,23 @@ module.exports = React.createClass({
{
// Used to split rooms via tags
var tagNames = Object.keys(room.tags);
+
if (tagNames.length) {
for (var i = 0; i < tagNames.length; i++) {
var tagName = tagNames[i];
s.lists[tagName] = s.lists[tagName] || [];
- s.lists[tagName].push(room);
- self.listsForRoomId[room.roomId].push(tagName);
- otherTagNames[tagName] = 1;
+ s.lists[tagNames[i]].push(room);
}
}
else if (dmRoomMap.getUserIdForRoomId(room.roomId)) {
// "Direct Message" rooms (that we're still in and that aren't otherwise tagged)
- self.listsForRoomId[room.roomId].push("im.vector.fake.direct");
s.lists["im.vector.fake.direct"].push(room);
}
else {
- self.listsForRoomId[room.roomId].push("im.vector.fake.recent");
s.lists["im.vector.fake.recent"].push(room);
}
}
else if (me.membership === "leave") {
- self.listsForRoomId[room.roomId].push("im.vector.fake.archived");
s.lists["im.vector.fake.archived"].push(room);
}
else {
@@ -375,22 +268,44 @@ module.exports = React.createClass({
}
});
- // we actually apply the sorting to this when receiving the prop in RoomSubLists.
+ if (s.lists["im.vector.fake.direct"].length == 0 &&
+ MatrixClientPeg.get().getAccountData('m.direct') === undefined &&
+ !MatrixClientPeg.get().isGuest())
+ {
+ // scan through the 'recents' list for any rooms which look like DM rooms
+ // and make them DM rooms
+ const oldRecents = s.lists["im.vector.fake.recent"];
+ s.lists["im.vector.fake.recent"] = [];
- // we'll need this when we get to iterating through lists programatically - e.g. ctrl-shift-up/down
-/*
- this.listOrder = [
- "im.vector.fake.invite",
- "m.favourite",
- "im.vector.fake.recent",
- "im.vector.fake.direct",
- Object.keys(otherTagNames).filter(tagName=>{
- return (!tagName.match(/^m\.(favourite|lowpriority)$/));
- }).sort(),
- "m.lowpriority",
- "im.vector.fake.archived"
- ];
-*/
+ for (const room of oldRecents) {
+ const me = room.getMember(MatrixClientPeg.get().credentials.userId);
+
+ if (me && Rooms.looksLikeDirectMessageRoom(room, me)) {
+ s.lists["im.vector.fake.direct"].push(room);
+ } else {
+ s.lists["im.vector.fake.recent"].push(room);
+ }
+ }
+
+ // save these new guessed DM rooms into the account data
+ const newMDirectEvent = {};
+ for (const room of s.lists["im.vector.fake.direct"]) {
+ const me = room.getMember(MatrixClientPeg.get().credentials.userId);
+ const otherPerson = Rooms.getOnlyOtherMember(room, me);
+ if (!otherPerson) continue;
+
+ const roomList = newMDirectEvent[otherPerson.userId] || [];
+ roomList.push(room.roomId);
+ newMDirectEvent[otherPerson.userId] = roomList;
+ }
+
+ // if this fails, fine, we'll just do the same thing next time we get the room lists
+ MatrixClientPeg.get().setAccountData('m.direct', newMDirectEvent).done();
+ }
+
+ //console.log("calculated new roomLists; im.vector.fake.recent = " + s.lists["im.vector.fake.recent"]);
+
+ // we actually apply the sorting to this when receiving the prop in RoomSubLists.
return s;
},
@@ -548,15 +463,15 @@ module.exports = React.createClass({
return (
-
+ autoshow={true} onScroll={ self._whenScrolling } ref="gemscroll">
+
@@ -567,9 +482,9 @@ module.exports = React.createClass({
verb="favourite"
editable={ true }
order="manual"
+ selectedRoom={ self.props.selectedRoom }
incomingCall={ self.state.incomingCall }
collapsed={ self.props.collapsed }
- selectedRoom={ self.props.selectedRoom }
searchFilter={ self.props.searchFilter }
onHeaderClick={ self.onSubListHeaderClick }
onShowMoreRooms={ self.onShowMoreRooms } />
@@ -580,9 +495,9 @@ module.exports = React.createClass({
verb="tag direct chat"
editable={ true }
order="recent"
+ selectedRoom={ self.props.selectedRoom }
incomingCall={ self.state.incomingCall }
collapsed={ self.props.collapsed }
- selectedRoom={ self.props.selectedRoom }
alwaysShowHeader={ true }
searchFilter={ self.props.searchFilter }
onHeaderClick={ self.onSubListHeaderClick }
@@ -593,14 +508,14 @@ module.exports = React.createClass({
editable={ true }
verb="restore"
order="recent"
+ selectedRoom={ self.props.selectedRoom }
incomingCall={ self.state.incomingCall }
collapsed={ self.props.collapsed }
- selectedRoom={ self.props.selectedRoom }
searchFilter={ self.props.searchFilter }
onHeaderClick={ self.onSubListHeaderClick }
onShowMoreRooms={ self.onShowMoreRooms } />
- { Object.keys(self.state.lists).sort().map(function(tagName) {
+ { Object.keys(self.state.lists).map(function(tagName) {
if (!tagName.match(/^(m\.(favourite|lowpriority)|im\.vector\.fake\.(invite|recent|direct|archived))$/)) {
return
;
@@ -625,9 +540,9 @@ module.exports = React.createClass({
verb="demote"
editable={ true }
order="recent"
+ selectedRoom={ self.props.selectedRoom }
incomingCall={ self.state.incomingCall }
collapsed={ self.props.collapsed }
- selectedRoom={ self.props.selectedRoom }
searchFilter={ self.props.searchFilter }
onHeaderClick={ self.onSubListHeaderClick }
onShowMoreRooms={ self.onShowMoreRooms } />
@@ -636,8 +551,8 @@ module.exports = React.createClass({
label="Historical"
editable={ false }
order="recent"
- collapsed={ self.props.collapsed }
selectedRoom={ self.props.selectedRoom }
+ collapsed={ self.props.collapsed }
alwaysShowHeader={ true }
startAsHidden={ true }
showSpinner={ self.state.isLoadingLeftRooms }
diff --git a/src/components/views/rooms/RoomTile.js b/src/components/views/rooms/RoomTile.js
index 3b37d4608f..c123f31d19 100644
--- a/src/components/views/rooms/RoomTile.js
+++ b/src/components/views/rooms/RoomTile.js
@@ -27,8 +27,6 @@ var RoomNotifs = require('../../../RoomNotifs');
var FormattingUtils = require('../../../utils/FormattingUtils');
import AccessibleButton from '../elements/AccessibleButton';
var UserSettingsStore = require('../../../UserSettingsStore');
-var constantTimeDispatcher = require('../../../ConstantTimeDispatcher');
-var Unread = require('../../../Unread');
module.exports = React.createClass({
displayName: 'RoomTile',
@@ -38,10 +36,12 @@ module.exports = React.createClass({
connectDropTarget: React.PropTypes.func,
onClick: React.PropTypes.func,
isDragging: React.PropTypes.bool,
- selectedRoom: React.PropTypes.string,
room: React.PropTypes.object.isRequired,
collapsed: React.PropTypes.bool.isRequired,
+ selected: React.PropTypes.bool.isRequired,
+ unread: React.PropTypes.bool.isRequired,
+ highlight: React.PropTypes.bool.isRequired,
isInvite: React.PropTypes.bool.isRequired,
incomingCall: React.PropTypes.object,
},
@@ -54,11 +54,10 @@ module.exports = React.createClass({
getInitialState: function() {
return({
- hover: false,
- badgeHover: false,
+ hover : false,
+ badgeHover : false,
menuDisplayed: false,
notifState: RoomNotifs.getRoomNotifsState(this.props.room.roomId),
- selected: this.props.room ? (this.props.selectedRoom === this.props.room.roomId) : false,
});
},
@@ -80,32 +79,23 @@ module.exports = React.createClass({
}
},
+ onAccountData: function(accountDataEvent) {
+ if (accountDataEvent.getType() == 'm.push_rules') {
+ this.setState({
+ notifState: RoomNotifs.getRoomNotifsState(this.props.room.roomId),
+ });
+ }
+ },
+
componentWillMount: function() {
- constantTimeDispatcher.register("RoomTile.refresh", this.props.room.roomId, this.onRefresh);
- constantTimeDispatcher.register("RoomTile.select", this.props.room.roomId, this.onSelect);
- this.onRefresh();
+ MatrixClientPeg.get().on("accountData", this.onAccountData);
},
componentWillUnmount: function() {
- constantTimeDispatcher.unregister("RoomTile.refresh", this.props.room.roomId, this.onRefresh);
- constantTimeDispatcher.unregister("RoomTile.select", this.props.room.roomId, this.onSelect);
- },
-
- componentWillReceiveProps: function(nextProps) {
- this.onRefresh();
- },
-
- onRefresh: function(params) {
- this.setState({
- unread: Unread.doesRoomHaveUnreadMessages(this.props.room),
- highlight: this.props.room.getUnreadNotificationCount('highlight') > 0 || this.props.isInvite,
- });
- },
-
- onSelect: function(params) {
- this.setState({
- selected: params.selected,
- });
+ var cli = MatrixClientPeg.get();
+ if (cli) {
+ MatrixClientPeg.get().removeListener("accountData", this.onAccountData);
+ }
},
onClick: function(ev) {
@@ -179,13 +169,13 @@ module.exports = React.createClass({
// var highlightCount = this.props.room.getUnreadNotificationCount("highlight");
const notifBadges = notificationCount > 0 && this._shouldShowNotifBadge();
- const mentionBadges = this.state.highlight && this._shouldShowMentionBadge();
+ const mentionBadges = this.props.highlight && this._shouldShowMentionBadge();
const badges = notifBadges || mentionBadges;
var classes = classNames({
'mx_RoomTile': true,
- 'mx_RoomTile_selected': this.state.selected,
- 'mx_RoomTile_unread': this.state.unread,
+ 'mx_RoomTile_selected': this.props.selected,
+ 'mx_RoomTile_unread': this.props.unread,
'mx_RoomTile_unreadNotify': notifBadges,
'mx_RoomTile_highlight': mentionBadges,
'mx_RoomTile_invited': (me && me.membership == 'invite'),
@@ -231,7 +221,7 @@ module.exports = React.createClass({
'mx_RoomTile_badgeShown': badges || this.state.badgeHover || this.state.menuDisplayed,
});
- if (this.state.selected) {
+ if (this.props.selected) {
let nameSelected =
{name} ;
label =
{ nameSelected }
;
@@ -265,8 +255,7 @@ module.exports = React.createClass({
let ret = (
{ /* Only native elements can be wrapped in a DnD object. */}
-
+
From 75eea89c08354caf2f65e9bbeb60a572830c21c2 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Tue, 16 May 2017 16:12:57 +0100
Subject: [PATCH 0108/1016] Revert "Merge pull request #765 from
t3chguy/t3chguy/escape-closes-user-settings"
This reverts commit a29d8c2af200af9bd5f55da60a10d1fc9c473764, reversing
changes made to 1d836c7d02a6935313bfb05d94fc38ae05439480.
---
src/components/structures/LoggedInView.js | 12 ------------
1 file changed, 12 deletions(-)
diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js
index c4eeb03d5f..25ca025a23 100644
--- a/src/components/structures/LoggedInView.js
+++ b/src/components/structures/LoggedInView.js
@@ -107,18 +107,6 @@ export default React.createClass({
var handled = false;
switch (ev.keyCode) {
- case KeyCode.ESCAPE:
-
- // Implemented this way so possible handling for other pages is neater
- switch (this.props.page_type) {
- case PageTypes.UserSettings:
- this.props.onUserSettingsClose();
- handled = true;
- break;
- }
-
- break;
-
case KeyCode.UP:
case KeyCode.DOWN:
if (ev.altKey && !ev.shiftKey && !ev.ctrlKey && !ev.metaKey) {
From 8695397abbe00d8a3d938580539929ff5f889a0c Mon Sep 17 00:00:00 2001
From: Richard van der Hoff
Date: Wed, 17 May 2017 01:41:42 +0100
Subject: [PATCH 0109/1016] Support for pasting files into normal composer
We don't seem to be in any danger of getting a working RTE any time soon, so
implement file pasting in the normal composer too.
---
src/components/views/rooms/MessageComposer.js | 10 +++++----
.../views/rooms/MessageComposerInput.js | 9 ++------
.../views/rooms/MessageComposerInputOld.js | 22 ++++++++++++++++++-
3 files changed, 29 insertions(+), 12 deletions(-)
diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js
index 0ee3c2082d..830d3f38ff 100644
--- a/src/components/views/rooms/MessageComposer.js
+++ b/src/components/views/rooms/MessageComposer.js
@@ -33,6 +33,7 @@ export default class MessageComposer extends React.Component {
this.onHangupClick = this.onHangupClick.bind(this);
this.onUploadClick = this.onUploadClick.bind(this);
this.onUploadFileSelected = this.onUploadFileSelected.bind(this);
+ this.uploadFiles = this.uploadFiles.bind(this);
this.onVoiceCallClick = this.onVoiceCallClick.bind(this);
this.onInputContentChanged = this.onInputContentChanged.bind(this);
this.onUpArrow = this.onUpArrow.bind(this);
@@ -101,10 +102,11 @@ export default class MessageComposer extends React.Component {
this.refs.uploadInput.click();
}
- onUploadFileSelected(files, isPasted) {
- if (!isPasted)
- files = files.target.files;
+ onUploadFileSelected(files) {
+ this.uploadFiles(files.target.files);
+ }
+ uploadFiles(files) {
let QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
let TintableSvg = sdk.getComponent("elements.TintableSvg");
@@ -310,7 +312,7 @@ export default class MessageComposer extends React.Component {
tryComplete={this._tryComplete}
onUpArrow={this.onUpArrow}
onDownArrow={this.onDownArrow}
- onUploadFileSelected={this.onUploadFileSelected}
+ onFilesPasted={this.uploadFiles}
tabComplete={this.props.tabComplete} // used for old messagecomposerinput/tabcomplete
onContentChanged={this.onInputContentChanged}
onInputStateChanged={this.onInputStateChanged} />,
diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js
index e2fcc19f67..af361db235 100644
--- a/src/components/views/rooms/MessageComposerInput.js
+++ b/src/components/views/rooms/MessageComposerInput.js
@@ -84,7 +84,6 @@ export default class MessageComposerInput extends React.Component {
this.onAction = this.onAction.bind(this);
this.handleReturn = this.handleReturn.bind(this);
this.handleKeyCommand = this.handleKeyCommand.bind(this);
- this.handlePastedFiles = this.handlePastedFiles.bind(this);
this.onEditorContentChanged = this.onEditorContentChanged.bind(this);
this.setEditorState = this.setEditorState.bind(this);
this.onUpArrow = this.onUpArrow.bind(this);
@@ -477,10 +476,6 @@ export default class MessageComposerInput extends React.Component {
return false;
}
- handlePastedFiles(files) {
- this.props.onUploadFileSelected(files, true);
- }
-
handleReturn(ev) {
if (ev.shiftKey) {
this.onEditorContentChanged(RichUtils.insertSoftNewline(this.state.editorState));
@@ -734,7 +729,7 @@ export default class MessageComposerInput extends React.Component {
keyBindingFn={MessageComposerInput.getKeyBinding}
handleKeyCommand={this.handleKeyCommand}
handleReturn={this.handleReturn}
- handlePastedFiles={this.handlePastedFiles}
+ handlePastedFiles={this.props.onFilesPasted}
stripPastedStyles={!this.state.isRichtextEnabled}
onTab={this.onTab}
onUpArrow={this.onUpArrow}
@@ -764,7 +759,7 @@ MessageComposerInput.propTypes = {
onDownArrow: React.PropTypes.func,
- onUploadFileSelected: React.PropTypes.func,
+ onFilesPasted: React.PropTypes.func,
// attempts to confirm currently selected completion, returns whether actually confirmed
tryComplete: React.PropTypes.func,
diff --git a/src/components/views/rooms/MessageComposerInputOld.js b/src/components/views/rooms/MessageComposerInputOld.js
index 378644478c..adc6bc2c91 100644
--- a/src/components/views/rooms/MessageComposerInputOld.js
+++ b/src/components/views/rooms/MessageComposerInputOld.js
@@ -69,6 +69,9 @@ export default React.createClass({
// The text to use a placeholder in the input box
placeholder: React.PropTypes.string.isRequired,
+
+ // callback to handle files pasted into the composer
+ onFilesPasted: React.PropTypes.func,
},
componentWillMount: function() {
@@ -439,10 +442,27 @@ export default React.createClass({
this.refs.textarea.focus();
},
+ _onPaste: function(ev) {
+ const items = ev.clipboardData.items;
+ const files = [];
+ for (const item of items) {
+ if (item.kind === 'file') {
+ files.push(item.getAsFile());
+ }
+ }
+ if (files.length && this.props.onFilesPasted) {
+ this.props.onFilesPasted(files);
+ return true;
+ }
+ return false;
+ },
+
render: function() {
return (
-
+
);
}
From e1089574ae02bee6036cb745513c026381558f43 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Wed, 17 May 2017 09:46:17 +0100
Subject: [PATCH 0110/1016] Write some tests for the RTS UI
Add tests that make assertions about the UI during registration when registration is done with a user recognised as a team member (by the mock rtsClient).
---
.../structures/login/Registration.js | 3 +-
.../structures/login/Registration-test.js | 105 ++++++++++++++++++
.../views/login/RegistrationForm-test.js | 86 ++++++++++++++
test/test-utils.js | 14 +++
4 files changed, 206 insertions(+), 2 deletions(-)
create mode 100644 test/components/structures/login/Registration-test.js
create mode 100644 test/components/views/login/RegistrationForm-test.js
diff --git a/src/components/structures/login/Registration.js b/src/components/structures/login/Registration.js
index 5501a39b58..5eecfa5ff6 100644
--- a/src/components/structures/login/Registration.js
+++ b/src/components/structures/login/Registration.js
@@ -98,7 +98,7 @@ module.exports = React.createClass({
this.props.teamServerConfig.teamServerURL &&
!this._rtsClient
) {
- this._rtsClient = new RtsClient(this.props.teamServerConfig.teamServerURL);
+ this._rtsClient = this.props.rtsClient || new RtsClient(this.props.teamServerConfig.teamServerURL);
this.setState({
teamServerBusy: true,
@@ -221,7 +221,6 @@ module.exports = React.createClass({
}
trackPromise.then((teamToken) => {
- console.info('Team token promise',teamToken);
this.props.onLoggedIn({
userId: response.user_id,
deviceId: response.device_id,
diff --git a/test/components/structures/login/Registration-test.js b/test/components/structures/login/Registration-test.js
new file mode 100644
index 0000000000..b4b54a6315
--- /dev/null
+++ b/test/components/structures/login/Registration-test.js
@@ -0,0 +1,105 @@
+/*
+Copyright 2017 Vector Creations Ltd
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+const React = require('react');
+const ReactDOM = require('react-dom');
+const ReactTestUtils = require('react-addons-test-utils');
+const expect = require('expect');
+
+const testUtils = require('test-utils');
+
+const sdk = require('matrix-react-sdk');
+const Registration = sdk.getComponent('structures.login.Registration');
+
+let rtsClient;
+let client;
+
+const TEAM_CONFIG = {
+ supportEmail: 'support@some.domain',
+ teamServerURL: 'http://someteamserver.bla',
+};
+
+const CREDENTIALS = {userId: '@me:here'};
+const MOCK_REG_RESPONSE = {
+ user_id: CREDENTIALS.userId,
+ device_id: 'mydevice',
+ access_token: '2234569864534231',
+};
+
+describe('Registration', function() {
+ beforeEach(function() {
+ testUtils.beforeEach(this);
+ client = testUtils.createTestClient();
+ client.credentials = CREDENTIALS;
+
+ // Mock an RTS client that supports one team and naively returns team tokens when
+ // tracking by mapping email SIDs to team tokens. This is fine because we only
+ // want to assert the client behaviour such that a user recognised by the
+ // rtsClient (which would normally talk to the RTS server) as a team member is
+ // correctly logged in as one (and other such assertions).
+ rtsClient = testUtils.createTestRtsClient(
+ {
+ 'myawesometeam123': {
+ name: 'Team Awesome',
+ domain: 'team.awesome.net',
+ },
+ },
+ {'someEmailSid1234': 'myawesometeam123'},
+ );
+ });
+
+ it('should track a referral following successful registration of a team member', function(done) {
+ const expectedCreds = {
+ userId: MOCK_REG_RESPONSE.user_id,
+ deviceId: MOCK_REG_RESPONSE.device_id,
+ homeserverUrl: client.getHomeserverUrl(),
+ identityServerUrl: client.getIdentityServerUrl(),
+ accessToken: MOCK_REG_RESPONSE.access_token,
+ };
+ const onLoggedIn = function(creds, teamToken) {
+ expect(creds).toEqual(expectedCreds);
+ expect(teamToken).toBe('myawesometeam123');
+ done();
+ };
+
+ const res = ReactTestUtils.renderIntoDocument(
+ ,
+ );
+
+ res._onUIAuthFinished(true, MOCK_REG_RESPONSE, {emailSid: 'someEmailSid1234'});
+ });
+
+ it('should NOT track a referral following successful registration of a non-team member', function(done) {
+ const onLoggedIn = expect.createSpy().andCall(function(creds, teamToken) {
+ expect(teamToken).toNotExist();
+ done();
+ });
+
+ const res = ReactTestUtils.renderIntoDocument(
+ ,
+ );
+
+ res._onUIAuthFinished(true, MOCK_REG_RESPONSE, {emailSid: 'someOtherEmailSid11'});
+ });
+});
diff --git a/test/components/views/login/RegistrationForm-test.js b/test/components/views/login/RegistrationForm-test.js
new file mode 100644
index 0000000000..81db5b487b
--- /dev/null
+++ b/test/components/views/login/RegistrationForm-test.js
@@ -0,0 +1,86 @@
+/*
+Copyright 2017 Vector Creations Ltd
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+const React = require('react');
+const ReactDOM = require("react-dom");
+const ReactTestUtils = require('react-addons-test-utils');
+const expect = require('expect');
+
+const testUtils = require('test-utils');
+
+const sdk = require('matrix-react-sdk');
+const RegistrationForm = sdk.getComponent('views.login.RegistrationForm');
+
+const TEAM_CONFIG = {
+ supportEmail: "support@some.domain",
+ teams: [
+ { name: "The Team Org.", domain: "team.ac.uk" },
+ { name: "The Super Team", domain: "superteam.ac.uk" },
+ ],
+};
+
+function doInputEmail(inputEmail, onTeamSelected) {
+ const res = ReactTestUtils.renderIntoDocument(
+ ,
+ );
+
+ const teamInput = res.refs.email;
+ teamInput.value = inputEmail;
+
+ ReactTestUtils.Simulate.change(teamInput);
+ ReactTestUtils.Simulate.blur(teamInput);
+
+ return res;
+}
+
+function expectTeamSelectedFromEmailInput(inputEmail, expectedTeam) {
+ const onTeamSelected = expect.createSpy();
+ doInputEmail(inputEmail, onTeamSelected);
+
+ expect(onTeamSelected).toHaveBeenCalledWith(expectedTeam);
+}
+
+function expectSupportFromEmailInput(inputEmail, isSupportShown) {
+ const onTeamSelected = expect.createSpy();
+ const res = doInputEmail(inputEmail, onTeamSelected);
+
+ expect(res.state.showSupportEmail).toBe(isSupportShown);
+}
+
+describe('RegistrationForm', function() {
+ beforeEach(function() {
+ testUtils.beforeEach(this);
+ });
+
+ it('should select a team when a team email is entered', function() {
+ expectTeamSelectedFromEmailInput("member@team.ac.uk", TEAM_CONFIG.teams[0]);
+ });
+
+ it('should not select a team when an unrecognised team email is entered', function() {
+ expectTeamSelectedFromEmailInput("member@someunknownteam.ac.uk", null);
+ });
+
+ it('should show support when an unrecognised team email is entered', function() {
+ expectSupportFromEmailInput("member@someunknownteam.ac.uk", true);
+ });
+
+ it('should NOT show support when an unrecognised non-team email is entered', function() {
+ expectSupportFromEmailInput("someone@yahoo.com", false);
+ });
+});
diff --git a/test/test-utils.js b/test/test-utils.js
index 9f404f98eb..2c866d345c 100644
--- a/test/test-utils.js
+++ b/test/test-utils.js
@@ -137,6 +137,20 @@ export function createTestClient() {
};
}
+export function createTestRtsClient(teamMap, sidMap) {
+ return {
+ getTeamsConfig() {
+ return q(Object.keys(teamMap).map((token) => teamMap[token]));
+ },
+ trackReferral(referrer, emailSid, clientSecret) {
+ return q({team_token: sidMap[emailSid]});
+ },
+ getTeam(teamToken) {
+ return q(teamMap[teamToken]);
+ },
+ };
+}
+
/**
* Create an Event.
* @param {Object} opts Values for the event.
From c44d7b6c44bdb2e0a4e0243e0ca5ae307f14dc48 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Wed, 17 May 2017 13:04:06 +0100
Subject: [PATCH 0111/1016] Improve phone number country dropdown for
registration and login
This implements https://github.com/vector-im/riot-web/issues/3895
---
src/components/views/login/CountryDropdown.js | 21 +++++++++++--
src/components/views/login/PasswordLogin.js | 30 +++++++++---------
.../views/login/RegistrationForm.js | 31 +++++++++----------
3 files changed, 47 insertions(+), 35 deletions(-)
diff --git a/src/components/views/login/CountryDropdown.js b/src/components/views/login/CountryDropdown.js
index 6323b3f558..097b5eb1ec 100644
--- a/src/components/views/login/CountryDropdown.js
+++ b/src/components/views/login/CountryDropdown.js
@@ -38,6 +38,7 @@ export default class CountryDropdown extends React.Component {
super(props);
this._onSearchChange = this._onSearchChange.bind(this);
this._onOptionChange = this._onOptionChange.bind(this);
+ this._getShortOption = this._getShortOption.bind(this);
this.state = {
searchQuery: '',
@@ -73,6 +74,20 @@ export default class CountryDropdown extends React.Component {
);
}
+ _getShortOption(iso2) {
+ if (!this.props.isSmall) {
+ return undefined;
+ }
+ let countryPrefix;
+ if (this.props.showPrefix) {
+ countryPrefix = '+' + COUNTRIES_BY_ISO2[iso2].prefix;
+ }
+ return
+ { this._flagImgForIso2(iso2) }
+ { countryPrefix }
+ ;
+ }
+
render() {
const Dropdown = sdk.getComponent('elements.Dropdown');
@@ -107,11 +122,9 @@ export default class CountryDropdown extends React.Component {
// values between mounting and the initial value propgating
const value = this.props.value || COUNTRIES[0].iso2;
- const getShortOption = this.props.isSmall ? this._flagImgForIso2 : undefined;
-
return
{options}
@@ -122,6 +135,8 @@ export default class CountryDropdown extends React.Component {
CountryDropdown.propTypes = {
className: React.PropTypes.string,
isSmall: React.PropTypes.bool,
+ // if isSmall, show +44 in the selected value
+ showPrefix: React.PropTypes.bool,
onOptionChange: React.PropTypes.func.isRequired,
value: React.PropTypes.string,
};
diff --git a/src/components/views/login/PasswordLogin.js b/src/components/views/login/PasswordLogin.js
index 349dd0d139..46c9598751 100644
--- a/src/components/views/login/PasswordLogin.js
+++ b/src/components/views/login/PasswordLogin.js
@@ -149,28 +149,26 @@ class PasswordLogin extends React.Component {
;
case PasswordLogin.LOGIN_FIELD_PHONE:
const CountryDropdown = sdk.getComponent('views.login.CountryDropdown');
- const prefix = this.state.phonePrefix;
return
;
}
}
diff --git a/src/components/views/login/RegistrationForm.js b/src/components/views/login/RegistrationForm.js
index 2bc2b8946a..e55a224531 100644
--- a/src/components/views/login/RegistrationForm.js
+++ b/src/components/views/login/RegistrationForm.js
@@ -314,24 +314,23 @@ module.exports = React.createClass({
const phoneSection = (
+
-
-
+{this.state.phonePrefix}
-
-
);
From e38437e6cea11177c65980570ca7b0f2c954aaec Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Fri, 12 May 2017 17:39:38 +0100
Subject: [PATCH 0112/1016] Invite the welcome user after registration if
configured
This will shift focus to the welcome user DM.
We probably don't want to do this for teams, but I shall leave that for another PR that fixes teams WRT to new-guest-access.
---
src/components/structures/MatrixChat.js | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index 8865d77d51..5b2c4f55ef 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -768,8 +768,12 @@ module.exports = React.createClass({
this._teamToken = teamToken;
dis.dispatch({action: 'view_home_page'});
} else if (this._is_registered) {
+ if (this.props.config.welcomeUserId) {
+ createRoom({dmUserId: this.props.config.welcomeUserId});
+ return;
+ }
// The user has just logged in after registering
- dis.dispatch({action: 'view_user_settings'});
+ dis.dispatch({action: 'view_room_directory'});
} else {
this._showScreenAfterLogin();
}
From c991b52d2cabc4ee0475613b973e83164e3d4601 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Thu, 18 May 2017 11:45:23 +0100
Subject: [PATCH 0113/1016] Swap to new flag files (which are stored as GB.png)
Requires https://github.com/vector-im/riot-web/pull/3953
---
src/components/views/login/CountryDropdown.js | 13 +++----------
1 file changed, 3 insertions(+), 10 deletions(-)
diff --git a/src/components/views/login/CountryDropdown.js b/src/components/views/login/CountryDropdown.js
index 6323b3f558..9d5de8df9c 100644
--- a/src/components/views/login/CountryDropdown.js
+++ b/src/components/views/login/CountryDropdown.js
@@ -19,7 +19,6 @@ import React from 'react';
import sdk from '../../../index';
import { COUNTRIES } from '../../../phonenumber';
-import { charactersToImageNode } from '../../../HtmlUtils';
const COUNTRIES_BY_ISO2 = new Object(null);
for (const c of COUNTRIES) {
@@ -41,7 +40,7 @@ export default class CountryDropdown extends React.Component {
this.state = {
searchQuery: '',
- }
+ };
}
componentWillMount() {
@@ -64,13 +63,7 @@ export default class CountryDropdown extends React.Component {
}
_flagImgForIso2(iso2) {
- // Unicode Regional Indicator Symbol letter 'A'
- const RIS_A = 0x1F1E6;
- const ASCII_A = 65;
- return charactersToImageNode(iso2, true,
- RIS_A + (iso2.charCodeAt(0) - ASCII_A),
- RIS_A + (iso2.charCodeAt(1) - ASCII_A),
- );
+ return ;
}
render() {
@@ -115,7 +108,7 @@ export default class CountryDropdown extends React.Component {
value={value} searchEnabled={true}
>
{options}
-
+ ;
}
}
From 2350277d2936160e42e991ece509c3da11f0302c Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Thu, 18 May 2017 14:03:02 +0100
Subject: [PATCH 0114/1016] Add left_aligned class to CountryDropdown
This indiciates that the dd chevron should be on the left
---
src/components/views/login/CountryDropdown.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/views/login/CountryDropdown.js b/src/components/views/login/CountryDropdown.js
index 83fe09f597..8f342203bf 100644
--- a/src/components/views/login/CountryDropdown.js
+++ b/src/components/views/login/CountryDropdown.js
@@ -115,7 +115,7 @@ export default class CountryDropdown extends React.Component {
// values between mounting and the initial value propgating
const value = this.props.value || COUNTRIES[0].iso2;
- return
Date: Thu, 18 May 2017 16:58:57 +0100
Subject: [PATCH 0115/1016] Fixed pressing space or clicking dd input closes dd
---
src/components/views/elements/Dropdown.js | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/src/components/views/elements/Dropdown.js b/src/components/views/elements/Dropdown.js
index b4d2545e04..1b1e27cb65 100644
--- a/src/components/views/elements/Dropdown.js
+++ b/src/components/views/elements/Dropdown.js
@@ -152,10 +152,12 @@ export default class Dropdown extends React.Component {
}
_onInputClick(ev) {
- this.setState({
- expanded: !this.state.expanded,
- });
- ev.preventDefault();
+ if (!this.state.expanded) {
+ this.setState({
+ expanded: !this.state.expanded,
+ });
+ ev.preventDefault();
+ }
}
_onMenuOptionClick(dropdownKey) {
From a425909b76c771820529b85c98bb453c78bc81bc Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Thu, 18 May 2017 17:01:01 +0100
Subject: [PATCH 0116/1016] Get rid of react key warning when no results
---
src/components/views/elements/Dropdown.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/views/elements/Dropdown.js b/src/components/views/elements/Dropdown.js
index 1b1e27cb65..718050c010 100644
--- a/src/components/views/elements/Dropdown.js
+++ b/src/components/views/elements/Dropdown.js
@@ -254,7 +254,7 @@ export default class Dropdown extends React.Component {
);
});
if (options.length === 0) {
- return [
+ return [
No results
];
}
From 384f50609d92ed9e525f261ea6bf4a64d97b401c Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Thu, 18 May 2017 17:01:40 +0100
Subject: [PATCH 0117/1016] Allow searching by partial prefix (/w or /wo '+')
---
src/components/views/login/CountryDropdown.js | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/components/views/login/CountryDropdown.js b/src/components/views/login/CountryDropdown.js
index 8f342203bf..8369912570 100644
--- a/src/components/views/login/CountryDropdown.js
+++ b/src/components/views/login/CountryDropdown.js
@@ -26,9 +26,14 @@ for (const c of COUNTRIES) {
}
function countryMatchesSearchQuery(query, country) {
+ // Remove '+' if present (when searching for a prefix)
+ if (query[0] === '+') {
+ query = query.slice(1);
+ }
+
if (country.name.toUpperCase().indexOf(query.toUpperCase()) == 0) return true;
if (country.iso2 == query.toUpperCase()) return true;
- if (country.prefix == query) return true;
+ if (country.prefix.indexOf(query) !== -1) return true;
return false;
}
From ee64f4a8dd4aedf29d385eaac92b424ad6a03edc Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Thu, 18 May 2017 17:02:19 +0100
Subject: [PATCH 0118/1016] Add country prefix to country dd options
---
src/components/views/login/CountryDropdown.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/views/login/CountryDropdown.js b/src/components/views/login/CountryDropdown.js
index 8369912570..7024db339c 100644
--- a/src/components/views/login/CountryDropdown.js
+++ b/src/components/views/login/CountryDropdown.js
@@ -112,7 +112,7 @@ export default class CountryDropdown extends React.Component {
const options = displayedCountries.map((country) => {
return
{this._flagImgForIso2(country.iso2)}
- {country.name}
+ {country.name} (+{country.prefix})
;
});
From 32818aff4f888579cfc9b7cb6bea5fb55522e3e7 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Thu, 18 May 2017 17:08:26 +0100
Subject: [PATCH 0119/1016] Simplify expression
---
src/components/views/elements/Dropdown.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/views/elements/Dropdown.js b/src/components/views/elements/Dropdown.js
index 718050c010..82f8d753a9 100644
--- a/src/components/views/elements/Dropdown.js
+++ b/src/components/views/elements/Dropdown.js
@@ -154,7 +154,7 @@ export default class Dropdown extends React.Component {
_onInputClick(ev) {
if (!this.state.expanded) {
this.setState({
- expanded: !this.state.expanded,
+ expanded: true,
});
ev.preventDefault();
}
From 01955146e99bcee254e8bc191a5755bea096b598 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Thu, 18 May 2017 17:33:32 +0100
Subject: [PATCH 0120/1016] Prevent an exception getting scroll node
Don't try to findDOMNode before we're mounted as it makes react
angry.
---
src/components/views/rooms/RoomList.js | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js
index 611dd10780..7a502f0461 100644
--- a/src/components/views/rooms/RoomList.js
+++ b/src/components/views/rooms/RoomList.js
@@ -50,6 +50,8 @@ module.exports = React.createClass({
},
componentWillMount: function() {
+ this.mounted = false;
+
var cli = MatrixClientPeg.get();
cli.on("Room", this.onRoom);
cli.on("deleteRoom", this.onDeleteRoom);
@@ -69,9 +71,12 @@ module.exports = React.createClass({
this.dispatcherRef = dis.register(this.onAction);
// Initialise the stickyHeaders when the component is created
this._updateStickyHeaders(true);
+
+ this.mounted = true;
},
- componentDidUpdate: function() {
+ componentDidUpdate: function(prevp, nextp) {
+ console.log(prevp, nextp);
// Reinitialise the stickyHeaders when the component is updated
this._updateStickyHeaders(true);
this._repositionIncomingCallBox(undefined, false);
@@ -106,6 +111,8 @@ module.exports = React.createClass({
},
componentWillUnmount: function() {
+ this.mounted = false;
+
dis.unregister(this.dispatcherRef);
if (MatrixClientPeg.get()) {
MatrixClientPeg.get().removeListener("Room", this.onRoom);
@@ -311,6 +318,7 @@ module.exports = React.createClass({
},
_getScrollNode: function() {
+ if (!this.mounted) return null;
var panel = ReactDOM.findDOMNode(this);
if (!panel) return null;
@@ -337,6 +345,7 @@ module.exports = React.createClass({
var incomingCallBox = document.getElementById("incomingCallBox");
if (incomingCallBox && incomingCallBox.parentElement) {
var scrollArea = this._getScrollNode();
+ if (!scrollArea) return;
// Use the offset of the top of the scroll area from the window
// as this is used to calculate the CSS fixed top position for the stickies
var scrollAreaOffset = scrollArea.getBoundingClientRect().top + window.pageYOffset;
@@ -360,6 +369,7 @@ module.exports = React.createClass({
// properly through React
_initAndPositionStickyHeaders: function(initialise, scrollToPosition) {
var scrollArea = this._getScrollNode();
+ if (!scrollArea) return;
// Use the offset of the top of the scroll area from the window
// as this is used to calculate the CSS fixed top position for the stickies
var scrollAreaOffset = scrollArea.getBoundingClientRect().top + window.pageYOffset;
From 8fe3fa917801a4e82eef0525e7c31f931c00ff92 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Thu, 18 May 2017 17:35:22 +0100
Subject: [PATCH 0121/1016] Remove debug logging
---
src/components/views/rooms/RoomList.js | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js
index 7a502f0461..9a06fb4efa 100644
--- a/src/components/views/rooms/RoomList.js
+++ b/src/components/views/rooms/RoomList.js
@@ -75,8 +75,7 @@ module.exports = React.createClass({
this.mounted = true;
},
- componentDidUpdate: function(prevp, nextp) {
- console.log(prevp, nextp);
+ componentDidUpdate: function() {
// Reinitialise the stickyHeaders when the component is updated
this._updateStickyHeaders(true);
this._repositionIncomingCallBox(undefined, false);
From 3e7a31ac752d1a1e01e2850c04dd2d0e862dd008 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Thu, 18 May 2017 19:03:51 +0100
Subject: [PATCH 0122/1016] Revert "fix scroll behaviour on macs with no
gemini"
This reverts commit 1347d9fa65f878cb65aaf0898536645e4a642429.
---
src/components/views/rooms/RoomList.js | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js
index 9a06fb4efa..760b0543c6 100644
--- a/src/components/views/rooms/RoomList.js
+++ b/src/components/views/rooms/RoomList.js
@@ -321,10 +321,11 @@ module.exports = React.createClass({
var panel = ReactDOM.findDOMNode(this);
if (!panel) return null;
- // empirically, if we have gm-prevented for some reason, the scroll node
- // is still the 3rd child (i.e. the view child). This looks to be due
- // to vdh's improved resize updater logic...?
- return panel.children[2]; // XXX: Fragile!
+ if (panel.classList.contains('gm-prevented')) {
+ return panel;
+ } else {
+ return panel.children[2]; // XXX: Fragile!
+ }
},
_whenScrolling: function(e) {
@@ -372,7 +373,7 @@ module.exports = React.createClass({
// Use the offset of the top of the scroll area from the window
// as this is used to calculate the CSS fixed top position for the stickies
var scrollAreaOffset = scrollArea.getBoundingClientRect().top + window.pageYOffset;
- // Use the offset of the top of the component from the window
+ // Use the offset of the top of the componet from the window
// as this is used to calculate the CSS fixed top position for the stickies
var scrollAreaHeight = ReactDOM.findDOMNode(this).getBoundingClientRect().height;
From 2ac3371ea4aec331898c459e33384187d725d285 Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@googlemail.com>
Date: Thu, 18 May 2017 22:51:22 +0100
Subject: [PATCH 0123/1016] Don't suggest vars!!
---
code_style.md | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/code_style.md b/code_style.md
index f0eca75ffc..29e54057f8 100644
--- a/code_style.md
+++ b/code_style.md
@@ -74,20 +74,20 @@ General Style
treat yourself to another `var`:
```javascript
- var key = "foo",
+ const key = "foo",
comparator = function(x, y) {
return x - y;
}; // Bad
- var key = "foo";
- var comparator = function(x, y) {
+ const key = "foo";
+ const comparator = function(x, y) {
return x - y;
}; // Good
- var x = 0, y = 0; // Fine
+ let x = 0, y = 0; // Fine
- var x = 0;
- var y = 0; // Also fine
+ let x = 0;
+ let y = 0; // Also fine
```
- A single line `if` is fine, all others have braces. This prevents errors when adding to the code.:
From 73d68c551303e03404f4d300c62ddf820cd7f0ff Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@googlemail.com>
Date: Thu, 18 May 2017 23:03:34 +0100
Subject: [PATCH 0124/1016] no leading lines for else,finally,catch etc
---
code_style.md | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/code_style.md b/code_style.md
index 29e54057f8..2cac303e54 100644
--- a/code_style.md
+++ b/code_style.md
@@ -69,6 +69,22 @@ General Style
console.log("I am a fish"); // Bad
}
```
+- No new line before else, catch, finally, etc:
+
+ ```javascript
+ if (x) {
+ console.log("I am a fish");
+ } else {
+ console.log("I am a chimp"); // Good
+ }
+
+ if (x) {
+ console.log("I am a fish");
+ }
+ else {
+ console.log("I am a chimp"); // Bad
+ }
+ ```
- Declare one variable per var statement (consistent with Node). Unless they
are simple and closely related. If you put the next declaration on a new line,
treat yourself to another `var`:
From 0e7e4d8595f71edcd13064760dd810cba0085563 Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Fri, 19 May 2017 00:20:32 +0100
Subject: [PATCH 0125/1016] replace weird sidebar snapping with better
ui_opacity
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
---
src/components/structures/LoggedInView.js | 14 ++++++-------
src/components/structures/MatrixChat.js | 21 ++++++++------------
src/components/views/rooms/ForwardMessage.js | 7 +++----
3 files changed, 18 insertions(+), 24 deletions(-)
diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js
index 4c012b42a8..6ac7fcb3c4 100644
--- a/src/components/structures/LoggedInView.js
+++ b/src/components/structures/LoggedInView.js
@@ -194,7 +194,7 @@ export default React.createClass({
ConferenceHandler={this.props.ConferenceHandler}
scrollStateMap={this._scrollStateMap}
/>;
- if (!this.props.collapse_rhs) right_panel = ;
+ if (!this.props.collapse_rhs) right_panel = ;
break;
case PageTypes.UserSettings:
@@ -206,7 +206,7 @@ export default React.createClass({
referralBaseUrl={this.props.config.referralBaseUrl}
teamToken={this.props.teamToken}
/>;
- if (!this.props.collapse_rhs) right_panel = ;
+ if (!this.props.collapse_rhs) right_panel = ;
break;
case PageTypes.CreateRoom:
@@ -214,7 +214,7 @@ export default React.createClass({
onRoomCreated={this.props.onRoomCreated}
collapsedRhs={this.props.collapse_rhs}
/>;
- if (!this.props.collapse_rhs) right_panel = ;
+ if (!this.props.collapse_rhs) right_panel = ;
break;
case PageTypes.RoomDirectory:
@@ -223,7 +223,7 @@ export default React.createClass({
collapsedRhs={this.props.collapse_rhs}
config={this.props.config.roomDirectory}
/>;
- if (!this.props.collapse_rhs) right_panel = ;
+ if (!this.props.collapse_rhs) right_panel = ;
break;
case PageTypes.HomePage:
@@ -232,12 +232,12 @@ export default React.createClass({
teamServerUrl={this.props.config.teamServerConfig.teamServerURL}
teamToken={this.props.teamToken}
/>
- if (!this.props.collapse_rhs) right_panel =
+ if (!this.props.collapse_rhs) right_panel =
break;
case PageTypes.UserView:
page_element = null; // deliberately null for now
- right_panel = ;
+ right_panel = ;
break;
}
@@ -266,7 +266,7 @@ export default React.createClass({
diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index 25ec644787..c78a395185 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -119,8 +119,9 @@ module.exports = React.createClass({
collapse_rhs: false,
ready: false,
width: 10000,
- sideOpacity: 1.0,
+ leftOpacity: 1.0,
middleOpacity: 1.0,
+ rightOpacity: 1.0,
version: null,
newVersion: null,
@@ -547,29 +548,23 @@ module.exports = React.createClass({
});
break;
case 'hide_right_panel':
- this.was_rhs_collapsed = this.state.collapse_rhs;
this.setState({
collapse_rhs: true,
});
break;
case 'show_right_panel':
- this.was_rhs_collapsed = this.state.collapse_rhs;
this.setState({
collapse_rhs: false,
});
break;
- // sets the panel to its state before last show/hide event
- case 'restore_right_panel':
+ case 'ui_opacity': {
+ const sideDefault = payload.sideOpacity >= 0.0 ? payload.sideOpacity : 1.0;
this.setState({
- collapse_rhs: this.was_rhs_collapsed,
+ leftOpacity: payload.leftOpacity >= 0.0 ? payload.leftOpacity : sideDefault,
+ middleOpacity: payload.middleOpacity || 1.0,
+ rightOpacity: payload.rightOpacity >= 0.0 ? payload.rightOpacity : sideDefault,
});
- break;
- case 'ui_opacity':
- this.setState({
- sideOpacity: payload.sideOpacity,
- middleOpacity: payload.middleOpacity,
- });
- break;
+ break; }
case 'set_theme':
this._onSetTheme(payload.value);
break;
diff --git a/src/components/views/rooms/ForwardMessage.js b/src/components/views/rooms/ForwardMessage.js
index e5be89b2e0..cd57bb1ba1 100644
--- a/src/components/views/rooms/ForwardMessage.js
+++ b/src/components/views/rooms/ForwardMessage.js
@@ -34,11 +34,11 @@ module.exports = React.createClass({
componentWillMount: function() {
this._unmounted = false;
- dis.dispatch({action: 'hide_right_panel'});
dis.dispatch({
action: 'ui_opacity',
- sideOpacity: 1.0,
- middleOpacity: 0.3,
+ leftOpacity: 1.0,
+ rightOpacity: 0.3,
+ middleOpacity: 0.5,
});
},
@@ -50,7 +50,6 @@ module.exports = React.createClass({
componentWillUnmount: function() {
this._unmounted = true;
- dis.dispatch({action: 'restore_right_panel'});
dis.dispatch({
action: 'ui_opacity',
sideOpacity: 1.0,
From bf0fe637590897f1e43d39584cef4122a2cf8bf3 Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Fri, 19 May 2017 00:20:51 +0100
Subject: [PATCH 0126/1016] don't know why I'm even tracking mounted state.
Never refd
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
---
src/components/views/rooms/ForwardMessage.js | 4 ----
1 file changed, 4 deletions(-)
diff --git a/src/components/views/rooms/ForwardMessage.js b/src/components/views/rooms/ForwardMessage.js
index cd57bb1ba1..c3cec6fb36 100644
--- a/src/components/views/rooms/ForwardMessage.js
+++ b/src/components/views/rooms/ForwardMessage.js
@@ -32,8 +32,6 @@ module.exports = React.createClass({
},
componentWillMount: function() {
- this._unmounted = false;
-
dis.dispatch({
action: 'ui_opacity',
leftOpacity: 1.0,
@@ -48,8 +46,6 @@ module.exports = React.createClass({
},
componentWillUnmount: function() {
- this._unmounted = true;
-
dis.dispatch({
action: 'ui_opacity',
sideOpacity: 1.0,
From 475646a2a73296f25f118ac945b969fa34396df2 Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Fri, 19 May 2017 00:34:35 +0100
Subject: [PATCH 0127/1016] Change wording
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
---
src/components/views/rooms/ForwardMessage.js | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/src/components/views/rooms/ForwardMessage.js b/src/components/views/rooms/ForwardMessage.js
index c3cec6fb36..e478630303 100644
--- a/src/components/views/rooms/ForwardMessage.js
+++ b/src/components/views/rooms/ForwardMessage.js
@@ -85,10 +85,7 @@ module.exports = React.createClass({
render: function() {
return (
-
-
Select a room to send the message to
- Use the left sidebar Room List to select forwarding target
-
+ Please select the destination room for this message
);
},
From cc7edbf86d0154a7b4daa4a4c2bc27a7a8d3e77a Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Fri, 19 May 2017 01:29:11 +0100
Subject: [PATCH 0128/1016] allow for sending arbitrary events, also override
highlight with event currently being forwarded while forwardingEvent is set
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
---
src/components/structures/RoomView.js | 15 +++++++--------
src/components/views/rooms/ForwardMessage.js | 7 +++++--
2 files changed, 12 insertions(+), 10 deletions(-)
diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js
index 9a0534cac7..c7417eeafd 100644
--- a/src/components/structures/RoomView.js
+++ b/src/components/structures/RoomView.js
@@ -125,7 +125,7 @@ module.exports = React.createClass({
roomId: null,
roomLoading: true,
- forwardingMessage: null,
+ forwardingEvent: null,
editingRoomSettings: false,
uploadingRoomSettings: false,
numUnreadMessages: 0,
@@ -454,9 +454,9 @@ module.exports = React.createClass({
});
break;
- case 'forward_message':
+ case 'forward_event':
this.setState({
- forwardingMessage: payload.content,
+ forwardingEvent: payload.content,
});
break;
}
@@ -1203,7 +1203,7 @@ module.exports = React.createClass({
this.updateTint();
this.setState({
editingRoomSettings: false,
- forwardingMessage: null,
+ forwardingEvent: null,
});
dis.dispatch({action: 'focus_composer'});
},
@@ -1621,8 +1621,8 @@ module.exports = React.createClass({
}
let aux = null;
- if (this.state.forwardingMessage !== null) {
- aux = ;
+ if (this.state.forwardingEvent !== null) {
+ aux = ;
} else if (this.state.editingRoomSettings) {
aux = ;
} else if (this.state.uploadingRoomSettings) {
@@ -1742,14 +1742,13 @@ module.exports = React.createClass({
}
// console.log("ShowUrlPreview for %s is %s", this.state.room.roomId, this.state.showUrlPreview);
-
var messagePanel = (
{
+ Client.sendEvent(payload.room_id, event.getType(), event.getContent()).done(() => {
dis.dispatch({action: 'message_sent'});
}, (err) => {
if (err.name === "UnknownDeviceError") {
From 96c3bf56f8dbbbcd242bee9292ef87085414c63e Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Fri, 19 May 2017 09:43:56 +0100
Subject: [PATCH 0129/1016] Implement warm-fuzzy success dialog for
SetMxIdDialog
---
src/components/views/dialogs/SetMxIdDialog.js | 38 ++++++++++++++++++-
1 file changed, 37 insertions(+), 1 deletion(-)
diff --git a/src/components/views/dialogs/SetMxIdDialog.js b/src/components/views/dialogs/SetMxIdDialog.js
index 86b5fccbc2..d9d07d517b 100644
--- a/src/components/views/dialogs/SetMxIdDialog.js
+++ b/src/components/views/dialogs/SetMxIdDialog.js
@@ -53,6 +53,9 @@ export default React.createClass({
doingUIAuth: false,
// Indicate error with auth
authError: '',
+
+ // Indicate success of setting mxid
+ success: false,
};
},
@@ -95,6 +98,10 @@ export default React.createClass({
});
},
+ onSuccessContinue: function() {
+ this.props.onFinished(true, this._registeredCreds);
+ },
+
_doUsernameCheck: function() {
// Check if username is available
return this._matrixClient.isUsernameAvailable(this.state.username).then(
@@ -162,7 +169,7 @@ export default React.createClass({
// XXX Implement RTS /register here
const teamToken = null;
- this.props.onFinished(true, {
+ this._registeredCreds = {
userId: response.user_id,
deviceId: response.device_id,
homeserverUrl: this._matrixClient.getHomeserverUrl(),
@@ -170,6 +177,11 @@ export default React.createClass({
accessToken: response.access_token,
password: this._generatedPassword,
teamToken: teamToken,
+ };
+
+ // Before continuing, show a warm-fuzzy success and only submit onSuccessContinue
+ this.setState({
+ success: true,
});
},
@@ -219,6 +231,30 @@ export default React.createClass({
!this.state.usernameError &&
!this.state.usernameBusy;
+ if (this.state.success) {
+ return (
+
+
+
+ You have successfully
+ picked { this.state.username } as your
+ username and you now have access to the full
+ set of features on Riot.
+
+
+
+
+
+
+ );
+ }
+
return (
Date: Fri, 19 May 2017 10:03:51 +0100
Subject: [PATCH 0130/1016] Remove suffix and prefix from login input username
This is an attempt reduce confusion when entering a custom home server: https://github.com/vector-im/riot-web/issues/3736
---
src/components/structures/login/Login.js | 8 -----
src/components/views/login/PasswordLogin.js | 37 ++++++---------------
2 files changed, 10 insertions(+), 35 deletions(-)
diff --git a/src/components/structures/login/Login.js b/src/components/structures/login/Login.js
index a3635177e2..63aeb35687 100644
--- a/src/components/structures/login/Login.js
+++ b/src/components/structures/login/Login.js
@@ -19,7 +19,6 @@ limitations under the License.
import React from 'react';
import ReactDOM from 'react-dom';
-import url from 'url';
import sdk from '../../../index';
import Login from '../../../Login';
@@ -242,12 +241,6 @@ module.exports = React.createClass({
switch (step) {
case 'm.login.password':
const PasswordLogin = sdk.getComponent('login.PasswordLogin');
- // HSs that are not matrix.org may not be configured to have their
- // domain name === domain part.
- let hsDomain = url.parse(this.state.enteredHomeserverUrl).hostname;
- if (hsDomain !== 'matrix.org') {
- hsDomain = null;
- }
return (
);
case 'm.login.cas':
diff --git a/src/components/views/login/PasswordLogin.js b/src/components/views/login/PasswordLogin.js
index 46c9598751..55817043ee 100644
--- a/src/components/views/login/PasswordLogin.js
+++ b/src/components/views/login/PasswordLogin.js
@@ -121,32 +121,16 @@ class PasswordLogin extends React.Component {
autoFocus
/>;
case PasswordLogin.LOGIN_FIELD_MXID:
- const mxidInputClasses = classNames({
- "mx_Login_field": true,
- "mx_Login_username": true,
- "mx_Login_field_has_prefix": true,
- "mx_Login_field_has_suffix": Boolean(this.props.hsDomain),
- });
- let suffix = null;
- if (this.props.hsDomain) {
- suffix =
- :{this.props.hsDomain}
-
;
- }
- return ;
+ return ;
case PasswordLogin.LOGIN_FIELD_PHONE:
const CountryDropdown = sdk.getComponent('views.login.CountryDropdown');
return
@@ -237,7 +221,6 @@ PasswordLogin.propTypes = {
onPhoneNumberChanged: React.PropTypes.func,
onPasswordChanged: React.PropTypes.func,
loginIncorrect: React.PropTypes.bool,
- hsDomain: React.PropTypes.string,
};
module.exports = PasswordLogin;
From f615bc129f0416700faa9e3b7d956229448c8655 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Fri, 19 May 2017 10:37:33 +0100
Subject: [PATCH 0131/1016] Released js-sdk
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 444d1c5369..5b7f7c5f26 100644
--- a/package.json
+++ b/package.json
@@ -63,7 +63,7 @@
"isomorphic-fetch": "^2.2.1",
"linkifyjs": "^2.1.3",
"lodash": "^4.13.1",
- "matrix-js-sdk": "matrix-org/matrix-js-sdk#develop",
+ "matrix-js-sdk": "0.7.8-rc.1",
"optimist": "^0.6.1",
"q": "^1.4.1",
"react": "^15.4.0",
From 4cfd04969ef628040bd98a7d1edca0c4a322ecc6 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Fri, 19 May 2017 10:39:14 +0100
Subject: [PATCH 0132/1016] Prepare changelog for v0.8.9-rc.1
---
CHANGELOG.md | 144 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 144 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 97dda666de..9db62ec629 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,147 @@
+Changes in [0.8.9-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v0.8.9-rc.1) (2017-05-19)
+=============================================================================================================
+[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v0.8.8...v0.8.9-rc.1)
+
+ * Prevent an exception getting scroll node
+ [\#902](https://github.com/matrix-org/matrix-react-sdk/pull/902)
+ * Fix a few remaining snags with country dd
+ [\#901](https://github.com/matrix-org/matrix-react-sdk/pull/901)
+ * Add left_aligned class to CountryDropdown
+ [\#900](https://github.com/matrix-org/matrix-react-sdk/pull/900)
+ * Swap to new flag files (which are stored as GB.png)
+ [\#899](https://github.com/matrix-org/matrix-react-sdk/pull/899)
+ * Improve phone number country dropdown for registration and login (Act. 2,
+ Return of the Prefix)
+ [\#897](https://github.com/matrix-org/matrix-react-sdk/pull/897)
+ * Support for pasting files into normal composer
+ [\#892](https://github.com/matrix-org/matrix-react-sdk/pull/892)
+ * tell guests they can't use filepanel until they register
+ [\#887](https://github.com/matrix-org/matrix-react-sdk/pull/887)
+ * Prevent reskindex -w from running when file names have not changed
+ [\#888](https://github.com/matrix-org/matrix-react-sdk/pull/888)
+ * I broke UserSettings for webpack-dev-server
+ [\#884](https://github.com/matrix-org/matrix-react-sdk/pull/884)
+ * various fixes to RoomHeader
+ [\#880](https://github.com/matrix-org/matrix-react-sdk/pull/880)
+ * remove /me whether or not it has a space after it
+ [\#885](https://github.com/matrix-org/matrix-react-sdk/pull/885)
+ * show error if we can't set a filter because no room
+ [\#883](https://github.com/matrix-org/matrix-react-sdk/pull/883)
+ * Fix RM not updating if RR event unpaginated
+ [\#874](https://github.com/matrix-org/matrix-react-sdk/pull/874)
+ * change roomsettings wording
+ [\#878](https://github.com/matrix-org/matrix-react-sdk/pull/878)
+ * make reskindex windows friendly
+ [\#875](https://github.com/matrix-org/matrix-react-sdk/pull/875)
+ * Fixes 2 issues with Dialog closing
+ [\#867](https://github.com/matrix-org/matrix-react-sdk/pull/867)
+ * Automatic Reskindex
+ [\#871](https://github.com/matrix-org/matrix-react-sdk/pull/871)
+ * Put room name in 'leave room' confirmation dialog
+ [\#873](https://github.com/matrix-org/matrix-react-sdk/pull/873)
+ * Fix this/self fail in LeftPanel
+ [\#872](https://github.com/matrix-org/matrix-react-sdk/pull/872)
+ * Don't show null URL previews
+ [\#870](https://github.com/matrix-org/matrix-react-sdk/pull/870)
+ * Fix keys for AddressSelector
+ [\#869](https://github.com/matrix-org/matrix-react-sdk/pull/869)
+ * Make left panel better for new users (mk II)
+ [\#859](https://github.com/matrix-org/matrix-react-sdk/pull/859)
+ * Explicitly save composer content onUnload
+ [\#866](https://github.com/matrix-org/matrix-react-sdk/pull/866)
+ * Warn on unload
+ [\#851](https://github.com/matrix-org/matrix-react-sdk/pull/851)
+ * Log deviceid at login
+ [\#862](https://github.com/matrix-org/matrix-react-sdk/pull/862)
+ * Guests can't send RR so no point trying
+ [\#860](https://github.com/matrix-org/matrix-react-sdk/pull/860)
+ * Remove babelcheck
+ [\#861](https://github.com/matrix-org/matrix-react-sdk/pull/861)
+ * T3chguy/settings versions improvements
+ [\#857](https://github.com/matrix-org/matrix-react-sdk/pull/857)
+ * Change max-len 90->120
+ [\#852](https://github.com/matrix-org/matrix-react-sdk/pull/852)
+ * Remove DM-guessing code
+ [\#829](https://github.com/matrix-org/matrix-react-sdk/pull/829)
+ * Fix jumping to an unread event when in MELS
+ [\#855](https://github.com/matrix-org/matrix-react-sdk/pull/855)
+ * Validate phone number on login
+ [\#856](https://github.com/matrix-org/matrix-react-sdk/pull/856)
+ * Failed to enable HTML5 Notifications Error Dialogs
+ [\#827](https://github.com/matrix-org/matrix-react-sdk/pull/827)
+ * Pin filesize ver to fix break upstream
+ [\#854](https://github.com/matrix-org/matrix-react-sdk/pull/854)
+ * Improve RoomDirectory Look & Feel
+ [\#848](https://github.com/matrix-org/matrix-react-sdk/pull/848)
+ * Only show jumpToReadMarker bar when RM !== RR
+ [\#845](https://github.com/matrix-org/matrix-react-sdk/pull/845)
+ * Allow MELS to have its own RM
+ [\#846](https://github.com/matrix-org/matrix-react-sdk/pull/846)
+ * Use document.onkeydown instead of onkeypress
+ [\#844](https://github.com/matrix-org/matrix-react-sdk/pull/844)
+ * (Room)?Avatar: Request 96x96 avatars on high DPI screens
+ [\#808](https://github.com/matrix-org/matrix-react-sdk/pull/808)
+ * Add mx_EventTile_emote class
+ [\#842](https://github.com/matrix-org/matrix-react-sdk/pull/842)
+ * Fix dialog reappearing after hitting Enter
+ [\#841](https://github.com/matrix-org/matrix-react-sdk/pull/841)
+ * Fix spinner that shows until the first sync
+ [\#840](https://github.com/matrix-org/matrix-react-sdk/pull/840)
+ * Show spinner until first sync has completed
+ [\#839](https://github.com/matrix-org/matrix-react-sdk/pull/839)
+ * Style fixes for LoggedInView
+ [\#838](https://github.com/matrix-org/matrix-react-sdk/pull/838)
+ * Fix specifying custom server for registration
+ [\#834](https://github.com/matrix-org/matrix-react-sdk/pull/834)
+ * Improve country dropdown UX and expose +prefix
+ [\#833](https://github.com/matrix-org/matrix-react-sdk/pull/833)
+ * Fix user settings store
+ [\#836](https://github.com/matrix-org/matrix-react-sdk/pull/836)
+ * show the room name in the UDE Dialog
+ [\#832](https://github.com/matrix-org/matrix-react-sdk/pull/832)
+ * summarise profile changes in MELS
+ [\#826](https://github.com/matrix-org/matrix-react-sdk/pull/826)
+ * Transform h1 and h2 tags to h3 tags
+ [\#820](https://github.com/matrix-org/matrix-react-sdk/pull/820)
+ * limit our keyboard shortcut modifiers correctly
+ [\#825](https://github.com/matrix-org/matrix-react-sdk/pull/825)
+ * Specify cross platform regexes and add olm to noParse
+ [\#823](https://github.com/matrix-org/matrix-react-sdk/pull/823)
+ * Remember element that was in focus before rendering dialog
+ [\#822](https://github.com/matrix-org/matrix-react-sdk/pull/822)
+ * move user settings outward and use built in read receipts disabling
+ [\#824](https://github.com/matrix-org/matrix-react-sdk/pull/824)
+ * File Download Consistency
+ [\#802](https://github.com/matrix-org/matrix-react-sdk/pull/802)
+ * Show Access Token under Advanced in Settings
+ [\#806](https://github.com/matrix-org/matrix-react-sdk/pull/806)
+ * Link tags/commit hashes in the UserSettings version section
+ [\#810](https://github.com/matrix-org/matrix-react-sdk/pull/810)
+ * On return to RoomView from auxPanel, send focus back to Composer
+ [\#813](https://github.com/matrix-org/matrix-react-sdk/pull/813)
+ * Change presence status labels to 'for' instead of 'ago'
+ [\#817](https://github.com/matrix-org/matrix-react-sdk/pull/817)
+ * Disable Scalar Integrations if urls passed to it are falsey
+ [\#816](https://github.com/matrix-org/matrix-react-sdk/pull/816)
+ * Add option to hide other people's read receipts.
+ [\#818](https://github.com/matrix-org/matrix-react-sdk/pull/818)
+ * Add option to not send typing notifications
+ [\#819](https://github.com/matrix-org/matrix-react-sdk/pull/819)
+ * Sync RM across instances of Riot
+ [\#805](https://github.com/matrix-org/matrix-react-sdk/pull/805)
+ * First iteration on improving login UI
+ [\#811](https://github.com/matrix-org/matrix-react-sdk/pull/811)
+ * focus on composer after jumping to bottom
+ [\#809](https://github.com/matrix-org/matrix-react-sdk/pull/809)
+ * Improve RoomList performance via side-stepping React
+ [\#807](https://github.com/matrix-org/matrix-react-sdk/pull/807)
+ * Don't show link preview when link is inside of a quote
+ [\#762](https://github.com/matrix-org/matrix-react-sdk/pull/762)
+ * Escape closes UserSettings
+ [\#765](https://github.com/matrix-org/matrix-react-sdk/pull/765)
+ * Implement user power-level changes in timeline
+ [\#794](https://github.com/matrix-org/matrix-react-sdk/pull/794)
+
Changes in [0.8.8](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v0.8.8) (2017-04-25)
===================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v0.8.8-rc.2...v0.8.8)
From 1addd6e304e7c954016346d52683569b237573c8 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Fri, 19 May 2017 10:39:14 +0100
Subject: [PATCH 0133/1016] v0.8.9-rc.1
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 5b7f7c5f26..72ccaad9b7 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "matrix-react-sdk",
- "version": "0.8.8",
+ "version": "0.8.9-rc.1",
"description": "SDK for matrix.org using React",
"author": "matrix.org",
"repository": {
From 4df51a2b0b6137617d37d5c62a3ea833518299c3 Mon Sep 17 00:00:00 2001
From: Richard van der Hoff
Date: Fri, 19 May 2017 11:44:04 +0100
Subject: [PATCH 0134/1016] Reskindex to a temp file, to confuse the babel
watcher less (#908)
I'm seeing a lot of instances where the babel watcher picks up a half-written src/component-index,
and generates an empty lib file - which it then doesn't update when src/component-index is updated.
Empirically, this seems to make it better.
---
scripts/reskindex.js | 27 +++++++++++++++++++--------
1 file changed, 19 insertions(+), 8 deletions(-)
diff --git a/scripts/reskindex.js b/scripts/reskindex.js
index 9516614fa5..833151a298 100755
--- a/scripts/reskindex.js
+++ b/scripts/reskindex.js
@@ -6,6 +6,7 @@ var args = require('optimist').argv;
var chokidar = require('chokidar');
var componentIndex = path.join('src', 'component-index.js');
+var componentIndexTmp = componentIndex+".tmp";
var componentsDir = path.join('src', 'components');
var componentGlob = '**/*.js';
var prevFiles = [];
@@ -20,7 +21,7 @@ function reskindex() {
var header = args.h || args.header;
var packageJson = JSON.parse(fs.readFileSync('./package.json'));
- var strm = fs.createWriteStream(componentIndex);
+ var strm = fs.createWriteStream(componentIndexTmp);
if (header) {
strm.write(fs.readFileSync(header));
@@ -35,13 +36,16 @@ function reskindex() {
strm.write(" */\n\n");
if (packageJson['matrix-react-parent']) {
+ const parentIndex = packageJson['matrix-react-parent'] +
+ '/lib/component-index';
strm.write(
- "module.exports.components = require('"+
- packageJson['matrix-react-parent']+
- "/lib/component-index').components;\n\n"
- );
+`let components = require('${parentIndex}').components;
+if (!components) {
+ throw new Error("'${parentIndex}' didn't export components");
+}
+`);
} else {
- strm.write("module.exports.components = {};\n");
+ strm.write("let components = {};\n");
}
for (var i = 0; i < files.length; ++i) {
@@ -51,13 +55,20 @@ function reskindex() {
var importName = moduleName.replace(/\./g, "$");
strm.write("import " + importName + " from './components/" + file + "';\n");
- strm.write(importName + " && (module.exports.components['"+moduleName+"'] = " + importName + ");");
+ strm.write(importName + " && (components['"+moduleName+"'] = " + importName + ");");
strm.write('\n');
strm.uncork();
}
+ strm.write("export {components};\n");
strm.end();
- console.log('Reskindex: completed');
+ fs.rename(componentIndexTmp, componentIndex, function(err) {
+ if(err) {
+ console.error("Error moving new index into place: " + err);
+ } else {
+ console.log('Reskindex: completed');
+ }
+ });
}
// Expects both arrays of file names to be sorted
From c61294f71c36c27da37987019a30f08c8f92c5e2 Mon Sep 17 00:00:00 2001
From: Richard van der Hoff
Date: Fri, 19 May 2017 13:53:11 +0100
Subject: [PATCH 0135/1016] Delintify some of MatrixChat (#907)
... it annoyed me too much.
---
src/components/structures/MatrixChat.js | 315 ++++++++++++------------
1 file changed, 163 insertions(+), 152 deletions(-)
diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index 5b2c4f55ef..54b8c8d1d8 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -17,27 +17,24 @@ limitations under the License.
import q from 'q';
-var React = require('react');
-var Matrix = require("matrix-js-sdk");
+import React from 'react';
+import Matrix from "matrix-js-sdk";
-var MatrixClientPeg = require("../../MatrixClientPeg");
-var PlatformPeg = require("../../PlatformPeg");
-var SdkConfig = require("../../SdkConfig");
-var ContextualMenu = require("./ContextualMenu");
-var RoomListSorter = require("../../RoomListSorter");
-var UserActivity = require("../../UserActivity");
-var Presence = require("../../Presence");
-var dis = require("../../dispatcher");
+import MatrixClientPeg from "../../MatrixClientPeg";
+import PlatformPeg from "../../PlatformPeg";
+import SdkConfig from "../../SdkConfig";
+import * as RoomListSorter from "../../RoomListSorter";
+import dis from "../../dispatcher";
-var Modal = require("../../Modal");
-var Tinter = require("../../Tinter");
-var sdk = require('../../index');
-var Rooms = require('../../Rooms');
-var linkifyMatrix = require("../../linkify-matrix");
-var Lifecycle = require('../../Lifecycle');
-var PageTypes = require('../../PageTypes');
+import Modal from "../../Modal";
+import Tinter from "../../Tinter";
+import sdk from '../../index';
+import * as Rooms from '../../Rooms';
+import linkifyMatrix from "../../linkify-matrix";
+import * as Lifecycle from '../../Lifecycle';
+import PageTypes from '../../PageTypes';
-var createRoom = require("../../createRoom");
+import createRoom from "../../createRoom";
import * as UDEHandler from '../../UnknownDeviceErrorHandler';
module.exports = React.createClass({
@@ -89,7 +86,7 @@ module.exports = React.createClass({
},
getInitialState: function() {
- var s = {
+ const s = {
loading: true,
screen: undefined,
screenAfterLogin: this.props.initialScreenAfterLogin,
@@ -156,11 +153,9 @@ module.exports = React.createClass({
return this.state.register_hs_url;
} else if (MatrixClientPeg.get()) {
return MatrixClientPeg.get().getHomeserverUrl();
- }
- else if (window.localStorage && window.localStorage.getItem("mx_hs_url")) {
+ } else if (window.localStorage && window.localStorage.getItem("mx_hs_url")) {
return window.localStorage.getItem("mx_hs_url");
- }
- else {
+ } else {
return this.getDefaultHsUrl();
}
},
@@ -178,11 +173,9 @@ module.exports = React.createClass({
return this.state.register_is_url;
} else if (MatrixClientPeg.get()) {
return MatrixClientPeg.get().getIdentityServerUrl();
- }
- else if (window.localStorage && window.localStorage.getItem("mx_is_url")) {
+ } else if (window.localStorage && window.localStorage.getItem("mx_is_url")) {
return window.localStorage.getItem("mx_is_url");
- }
- else {
+ } else {
return this.getDefaultIsUrl();
}
},
@@ -324,28 +317,14 @@ module.exports = React.createClass({
onAction: function(payload) {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
- var roomIndexDelta = 1;
+ const TextInputDialog = sdk.getComponent("dialogs.TextInputDialog");
- var self = this;
switch (payload.action) {
case 'logout':
Lifecycle.logout();
break;
case 'start_registration':
- const params = payload.params || {};
- this.setStateForNewScreen({
- screen: '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,
- });
- this.notifyNewScreen('register');
+ this._startRegistration(payload.params || {});
break;
case 'start_login':
if (MatrixClientPeg.get() &&
@@ -362,7 +341,7 @@ module.exports = React.createClass({
break;
case 'start_post_registration':
this.setState({ // don't clobber loggedIn status
- screen: 'post_registration'
+ screen: 'post_registration',
});
break;
case 'start_upgrade_registration':
@@ -392,34 +371,7 @@ module.exports = React.createClass({
this.notifyNewScreen('forgot_password');
break;
case 'leave_room':
- const roomToLeave = MatrixClientPeg.get().getRoom(payload.room_id);
- Modal.createDialog(QuestionDialog, {
- title: "Leave room",
- description: Are you sure you want to leave the room {roomToLeave.name} ? ,
- onFinished: (should_leave) => {
- if (should_leave) {
- const d = MatrixClientPeg.get().leave(payload.room_id);
-
- // FIXME: controller shouldn't be loading a view :(
- const Loader = sdk.getComponent("elements.Spinner");
- const modal = Modal.createDialog(Loader, null, 'mx_Dialog_spinner');
-
- d.then(() => {
- modal.close();
- 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, {
- title: "Failed to leave room",
- description: (err && err.message ? err.message : "Server may be unavailable, overloaded, or you hit a bug."),
- });
- });
- }
- }
- });
+ this._leaveRoom(payload.room_id);
break;
case 'reject_invite':
Modal.createDialog(QuestionDialog, {
@@ -440,11 +392,11 @@ module.exports = React.createClass({
modal.close();
Modal.createDialog(ErrorDialog, {
title: "Failed to reject invitation",
- description: err.toString()
+ description: err.toString(),
});
});
}
- }
+ },
});
break;
case 'view_user':
@@ -469,30 +421,13 @@ module.exports = React.createClass({
this._viewRoom(payload);
break;
case 'view_prev_room':
- roomIndexDelta = -1;
+ this._viewNextRoom(-1);
+ break;
case 'view_next_room':
- var allRooms = RoomListSorter.mostRecentActivityFirst(
- MatrixClientPeg.get().getRooms()
- );
- var roomIndex = -1;
- for (var i = 0; i < allRooms.length; ++i) {
- if (allRooms[i].roomId == this.state.currentRoomId) {
- roomIndex = i;
- break;
- }
- }
- roomIndex = (roomIndex + roomIndexDelta) % allRooms.length;
- if (roomIndex < 0) roomIndex = allRooms.length - 1;
- this._viewRoom({ room_id: allRooms[roomIndex].roomId });
+ this._viewNextRoom(1);
break;
case 'view_indexed_room':
- var allRooms = RoomListSorter.mostRecentActivityFirst(
- MatrixClientPeg.get().getRooms()
- );
- var roomIndex = payload.roomIndex;
- if (allRooms[roomIndex]) {
- this._viewRoom({ room_id: allRooms[roomIndex].roomId });
- }
+ this._viewIndexedRoom(payload.roomIndex);
break;
case 'view_user_settings':
this._setPage(PageTypes.UserSettings);
@@ -501,19 +436,17 @@ module.exports = React.createClass({
case 'view_create_room':
//this._setPage(PageTypes.CreateRoom);
//this.notifyNewScreen('new');
-
- var TextInputDialog = sdk.getComponent("dialogs.TextInputDialog");
Modal.createDialog(TextInputDialog, {
title: "Create Room",
description: "Room name (optional)",
button: "Create Room",
- onFinished: (should_create, name) => {
- if (should_create) {
+ onFinished: (shouldCreate, name) => {
+ if (shouldCreate) {
const createOpts = {};
if (name) createOpts.name = name;
createRoom({createOpts}).done();
}
- }
+ },
});
break;
case 'view_room_directory':
@@ -584,7 +517,7 @@ module.exports = React.createClass({
case 'new_version':
this.onVersion(
payload.currentVersion, payload.newVersion,
- payload.releaseNotes
+ payload.releaseNotes,
);
break;
}
@@ -596,6 +529,47 @@ module.exports = React.createClass({
});
},
+ _startRegistration: function(params) {
+ this.setStateForNewScreen({
+ screen: '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,
+ });
+ this.notifyNewScreen('register');
+ },
+
+ _viewNextRoom: function(roomIndexDelta) {
+ const allRooms = RoomListSorter.mostRecentActivityFirst(
+ MatrixClientPeg.get().getRooms(),
+ );
+ let roomIndex = -1;
+ for (let i = 0; i < allRooms.length; ++i) {
+ if (allRooms[i].roomId == this.state.currentRoomId) {
+ roomIndex = i;
+ break;
+ }
+ }
+ roomIndex = (roomIndex + roomIndexDelta) % allRooms.length;
+ if (roomIndex < 0) roomIndex = allRooms.length - 1;
+ this._viewRoom({ room_id: allRooms[roomIndex].roomId });
+ },
+
+ _viewIndexedRoom: function(roomIndex) {
+ const allRooms = RoomListSorter.mostRecentActivityFirst(
+ MatrixClientPeg.get().getRooms(),
+ );
+ if (allRooms[roomIndex]) {
+ this._viewRoom({ room_id: allRooms[roomIndex].roomId });
+ }
+ },
+
// switch view to the given room
//
// @param {Object} room_info Object containing data about the room to be joined
@@ -615,7 +589,7 @@ module.exports = React.createClass({
_viewRoom: function(room_info) {
this.focusComposer = true;
- var newState = {
+ const newState = {
initialEventId: room_info.event_id,
highlightedEventId: room_info.event_id,
initialEventPixelOffset: undefined,
@@ -635,7 +609,7 @@ module.exports = React.createClass({
//
// TODO: do this in RoomView rather than here
if (!room_info.event_id && this.refs.loggedInView) {
- var scrollState = this.refs.loggedInView.getScrollStateForRoom(room_info.room_id);
+ const scrollState = this.refs.loggedInView.getScrollStateForRoom(room_info.room_id);
if (scrollState) {
newState.initialEventId = scrollState.focussedEvent;
newState.initialEventPixelOffset = scrollState.pixelOffset;
@@ -677,14 +651,14 @@ module.exports = React.createClass({
},
_createChat: function() {
- var ChatInviteDialog = sdk.getComponent("dialogs.ChatInviteDialog");
+ const ChatInviteDialog = sdk.getComponent("dialogs.ChatInviteDialog");
Modal.createDialog(ChatInviteDialog, {
title: "Start a new chat",
});
},
_invite: function(roomId) {
- var ChatInviteDialog = sdk.getComponent("dialogs.ChatInviteDialog");
+ const ChatInviteDialog = sdk.getComponent("dialogs.ChatInviteDialog");
Modal.createDialog(ChatInviteDialog, {
title: "Invite new room members",
button: "Send Invites",
@@ -693,6 +667,41 @@ module.exports = React.createClass({
});
},
+ _leaveRoom: function(roomId) {
+ const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
+ const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
+
+ const roomToLeave = MatrixClientPeg.get().getRoom(roomId);
+ Modal.createDialog(QuestionDialog, {
+ title: "Leave room",
+ description: Are you sure you want to leave the room {roomToLeave.name} ? ,
+ onFinished: (shouldLeave) => {
+ if (shouldLeave) {
+ const d = MatrixClientPeg.get().leave(roomId);
+
+ // FIXME: controller shouldn't be loading a view :(
+ const Loader = sdk.getComponent("elements.Spinner");
+ const modal = Modal.createDialog(Loader, null, 'mx_Dialog_spinner');
+
+ d.then(() => {
+ modal.close();
+ if (this.currentRoomId === roomId) {
+ dis.dispatch({action: 'view_next_room'});
+ }
+ }, (err) => {
+ modal.close();
+ console.error("Failed to leave room " + roomId + " " + err);
+ Modal.createDialog(ErrorDialog, {
+ title: "Failed to leave room",
+ description: (err && err.message ? err.message :
+ "Server may be unavailable, overloaded, or you hit a bug."),
+ });
+ });
+ }
+ },
+ });
+ },
+
/**
* Called when the sessionloader has finished
*/
@@ -711,6 +720,8 @@ module.exports = React.createClass({
/**
* Called whenever someone changes the theme
+ *
+ * @param {string} theme new theme
*/
_onSetTheme: function(theme) {
if (!theme) {
@@ -719,12 +730,12 @@ module.exports = React.createClass({
// look for the stylesheet elements.
// styleElements is a map from style name to HTMLLinkElement.
- var styleElements = Object.create(null);
- var i, a;
- for (i = 0; (a = document.getElementsByTagName("link")[i]); i++) {
- var href = a.getAttribute("href");
+ const styleElements = Object.create(null);
+ let a;
+ for (let i = 0; (a = document.getElementsByTagName("link")[i]); i++) {
+ const href = a.getAttribute("href");
// shouldn't we be using the 'title' tag rather than the href?
- var match = href.match(/^bundles\/.*\/theme-(.*)\.css$/);
+ const match = href.match(/^bundles\/.*\/theme-(.*)\.css$/);
if (match) {
styleElements[match[1]] = a;
}
@@ -747,14 +758,15 @@ module.exports = React.createClass({
// abuse the tinter to change all the SVG's #fff to #2d2d2d
// XXX: obviously this shouldn't be hardcoded here.
Tinter.tintSvgWhite('#2d2d2d');
- }
- else {
+ } else {
Tinter.tintSvgWhite('#ffffff');
}
},
/**
* Called when a new logged in session has started
+ *
+ * @param {string} teamToken
*/
_onLoggedIn: function(teamToken) {
this.setState({
@@ -785,7 +797,7 @@ module.exports = React.createClass({
if (this.state.screenAfterLogin && this.state.screenAfterLogin.screen) {
this.showScreen(
this.state.screenAfterLogin.screen,
- this.state.screenAfterLogin.params
+ this.state.screenAfterLogin.params,
);
this.notifyNewScreen(this.state.screenAfterLogin.screen);
this.setState({screenAfterLogin: null});
@@ -826,8 +838,8 @@ module.exports = React.createClass({
* (useful for setting listeners)
*/
_onWillStartClient() {
- var self = this;
- var cli = MatrixClientPeg.get();
+ const self = this;
+ const cli = MatrixClientPeg.get();
// Allow the JS SDK to reap timeline events. This reduces the amount of
// memory consumed as the JS SDK stores multiple distinct copies of room
@@ -868,17 +880,17 @@ module.exports = React.createClass({
cli.on('Call.incoming', function(call) {
dis.dispatch({
action: 'incoming_call',
- call: call
+ call: call,
});
});
cli.on('Session.logged_out', function(call) {
- var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
+ const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
title: "Signed Out",
- description: "For security, this session has been signed out. Please sign in again."
+ description: "For security, this session has been signed out. Please sign in again.",
});
dis.dispatch({
- action: 'logout'
+ action: 'logout',
});
});
cli.on("accountData", function(ev) {
@@ -901,17 +913,17 @@ module.exports = React.createClass({
if (screen == 'register') {
dis.dispatch({
action: 'start_registration',
- params: params
+ params: params,
});
} else if (screen == 'login') {
dis.dispatch({
action: 'start_login',
- params: params
+ params: params,
});
} else if (screen == 'forgot_password') {
dis.dispatch({
action: 'start_password_recovery',
- params: params
+ params: params,
});
} else if (screen == 'new') {
dis.dispatch({
@@ -934,26 +946,26 @@ module.exports = React.createClass({
action: 'start_post_registration',
});
} else if (screen.indexOf('room/') == 0) {
- var segments = screen.substring(5).split('/');
- var roomString = segments[0];
- var eventId = segments[1]; // undefined if no event id given
+ const segments = screen.substring(5).split('/');
+ const roomString = segments[0];
+ const eventId = segments[1]; // undefined if no event id given
// FIXME: sort_out caseConsistency
- var third_party_invite = {
+ const thirdPartyInvite = {
inviteSignUrl: params.signurl,
invitedEmail: params.email,
};
- var oob_data = {
+ const oobData = {
name: params.room_name,
avatarUrl: params.room_avatar_url,
inviterName: params.inviter_name,
};
- var payload = {
+ const payload = {
action: 'view_room',
event_id: eventId,
- third_party_invite: third_party_invite,
- oob_data: oob_data,
+ third_party_invite: thirdPartyInvite,
+ oob_data: oobData,
};
if (roomString[0] == '#') {
payload.room_alias = roomString;
@@ -967,19 +979,18 @@ module.exports = React.createClass({
dis.dispatch(payload);
}
} else if (screen.indexOf('user/') == 0) {
- var userId = screen.substring(5);
+ const userId = screen.substring(5);
this.setState({ viewUserId: userId });
this._setPage(PageTypes.UserView);
this.notifyNewScreen('user/' + userId);
- var member = new Matrix.RoomMember(null, userId);
+ const member = new Matrix.RoomMember(null, userId);
if (member) {
dis.dispatch({
action: 'view_user',
member: member,
});
}
- }
- else {
+ } else {
console.info("Ignoring showScreen for '%s'", screen);
}
},
@@ -998,7 +1009,7 @@ module.exports = React.createClass({
onUserClick: function(event, userId) {
event.preventDefault();
- var member = new Matrix.RoomMember(null, userId);
+ const member = new Matrix.RoomMember(null, userId);
if (!member) { return; }
dis.dispatch({
action: 'view_user',
@@ -1008,17 +1019,17 @@ module.exports = React.createClass({
onLogoutClick: function(event) {
dis.dispatch({
- action: 'logout'
+ action: 'logout',
});
event.stopPropagation();
event.preventDefault();
},
handleResize: function(e) {
- var hideLhsThreshold = 1000;
- var showLhsThreshold = 1000;
- var hideRhsThreshold = 820;
- var showRhsThreshold = 820;
+ const hideLhsThreshold = 1000;
+ const showLhsThreshold = 1000;
+ const hideRhsThreshold = 820;
+ const showRhsThreshold = 820;
if (this.state.width > hideLhsThreshold && window.innerWidth <= hideLhsThreshold) {
dis.dispatch({ action: 'hide_left_panel' });
@@ -1036,10 +1047,10 @@ module.exports = React.createClass({
this.setState({width: window.innerWidth});
},
- onRoomCreated: function(room_id) {
+ onRoomCreated: function(roomId) {
dis.dispatch({
action: "view_room",
- room_id: room_id,
+ room_id: roomId,
});
},
@@ -1073,7 +1084,7 @@ module.exports = React.createClass({
onFinishPostRegistration: function() {
// Don't confuse this with "PageType" which is the middle window to show
this.setState({
- screen: undefined
+ screen: undefined,
});
this.showScreen("settings");
},
@@ -1088,10 +1099,10 @@ module.exports = React.createClass({
},
updateStatusIndicator: function(state, prevState) {
- var notifCount = 0;
+ let notifCount = 0;
- var rooms = MatrixClientPeg.get().getRooms();
- for (var i = 0; i < rooms.length; ++i) {
+ const rooms = MatrixClientPeg.get().getRooms();
+ for (let i = 0; i < rooms.length; ++i) {
if (rooms[i].hasMembershipState(MatrixClientPeg.get().credentials.userId, 'invite')) {
notifCount++;
} else if (rooms[i].getUnreadNotificationCount()) {
@@ -1118,19 +1129,18 @@ module.exports = React.createClass({
action: 'view_room',
room_id: this.state.currentRoomId,
});
- }
- else {
+ } else {
dis.dispatch({
action: 'view_room_directory',
});
}
},
- onRoomIdResolved: function(room_id) {
+ 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: room_id});
+ this.setState({currentRoomId: roomId});
},
_makeRegistrationUrl: function(params) {
@@ -1153,8 +1163,9 @@ module.exports = React.createClass({
);
}
+
// needs to be before normal PageTypes as you are logged in technically
- else if (this.state.screen == 'post_registration') {
+ if (this.state.screen == 'post_registration') {
const PostRegistration = sdk.getComponent('structures.login.PostRegistration');
return (
);
}
- }
+ },
});
From e3dbf057fbf866c82c1fbdcc4db91ba07a450c73 Mon Sep 17 00:00:00 2001
From: Richard van der Hoff
Date: Wed, 17 May 2017 18:49:59 +0100
Subject: [PATCH 0136/1016] Fix 'missing page_type' error
LoggedInView will complain if it is instantiated without a page_type, so let's
keep showing the syncing spinner until we have one.
---
src/components/structures/MatrixChat.js | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index 54b8c8d1d8..0de38ab226 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -1171,7 +1171,12 @@ module.exports = React.createClass({
);
- } else if (this.state.loggedIn && this.state.ready) {
+ }
+
+ // `ready` and `loggedIn` may be set before `page_type` (because the
+ // latter is set via the dispatcher). If we don't yet have a `page_type`,
+ // keep showing the spinner for now.
+ if (this.state.loggedIn && this.state.ready && this.state.page_type) {
/* 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
* as using something like redux to avoid having a billion bits of state kicking around.
From cc3673d2d2fd091a3cb88b3e74d2f5d08621efe5 Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Fri, 19 May 2017 17:39:21 +0100
Subject: [PATCH 0137/1016] Make the linked versions open a new tab, turt2live
complained :P
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
---
src/components/structures/UserSettings.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js
index 2c1f17ee3e..ca475fbf0c 100644
--- a/src/components/structures/UserSettings.js
+++ b/src/components/structures/UserSettings.js
@@ -46,7 +46,7 @@ const gHVersionLabel = function(repo, token='') {
} else {
url = `https://github.com/${repo}/commit/${token.split('-')[0]}`;
}
- return {token} ;
+ return {token} ;
};
// Enumerate some simple 'flip a bit' UI settings (if any).
From 008cc95e9ca97199add9e9591a85012b3dbf8759 Mon Sep 17 00:00:00 2001
From: Maxwell Kepler
Date: Thu, 18 May 2017 22:00:44 +0100
Subject: [PATCH 0138/1016] Add 12 hour support
---
src/DateUtils.js | 34 +++++++++++++++--------
src/components/structures/UserSettings.js | 2 +-
src/components/views/rooms/EventTile.js | 19 +++++++++----
3 files changed, 38 insertions(+), 17 deletions(-)
diff --git a/src/DateUtils.js b/src/DateUtils.js
index c58c09d4de..f787fc0e20 100644
--- a/src/DateUtils.js
+++ b/src/DateUtils.js
@@ -16,27 +16,37 @@ limitations under the License.
'use strict';
-var days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
-var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
+import UserSettingsStore from './UserSettingsStore';
+const days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
+const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
+let time;
function pad(n) {
return (n < 10 ? '0' : '') + n;
}
+function twentyfourhour(date) {
+ let hours = date.getHours();
+ let minutes = date.getMinutes();
+ let ampm = hours >= 12 ? 'PM' : 'AM';
+ hours = hours % 12;
+ hours = hours ? hours : 12;
+ minutes = minutes < 10 ? '0'+minutes : minutes;
+ var strTime = hours + ':' + minutes + ' ' + ampm;
+ return strTime;
+}
+
module.exports = {
formatDate: function(date) {
- // date.toLocaleTimeString is completely system dependent.
- // just go 24h for now
-
var now = new Date();
if (date.toDateString() === now.toDateString()) {
- return pad(date.getHours()) + ':' + pad(date.getMinutes());
+ return this.formatTime(date);
}
else if (now.getTime() - date.getTime() < 6 * 24 * 60 * 60 * 1000) {
- return days[date.getDay()] + " " + pad(date.getHours()) + ':' + pad(date.getMinutes());
+ return days[date.getDay()] + " " + this.formatTime(date);
}
else if (now.getFullYear() === date.getFullYear()) {
- return days[date.getDay()] + ", " + months[date.getMonth()] + " " + date.getDate() + " " + pad(date.getHours()) + ':' + pad(date.getMinutes());
+ return days[date.getDay()] + ", " + months[date.getMonth()] + " " + date.getDate() + " " + this.formatTime(date);
}
else {
return this.formatFullDate(date);
@@ -44,11 +54,13 @@ module.exports = {
},
formatFullDate: function(date) {
- return days[date.getDay()] + ", " + months[date.getMonth()] + " " + date.getDate() + " " + date.getFullYear() + " " + pad(date.getHours()) + ':' + pad(date.getMinutes());
+ return days[date.getDay()] + ", " + months[date.getMonth()] + " " + date.getDate() + " " + date.getFullYear() + " " + this.formatTime(date);
},
formatTime: function(date) {
+ if (UserSettingsStore.getSyncedSetting('showTwelveHourTimestamps')) {
+ return twentyfourhour(date);
+ }
return pad(date.getHours()) + ':' + pad(date.getMinutes());
- }
+ },
};
-
diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js
index 2c1f17ee3e..45774d7a6a 100644
--- a/src/components/structures/UserSettings.js
+++ b/src/components/structures/UserSettings.js
@@ -65,7 +65,6 @@ const SETTINGS_LABELS = [
id: 'dontSendTypingNotifications',
label: "Don't send typing notifications",
},
-/*
{
id: 'alwaysShowTimestamps',
label: 'Always show message timestamps',
@@ -74,6 +73,7 @@ const SETTINGS_LABELS = [
id: 'showTwelveHourTimestamps',
label: 'Show timestamps in 12 hour format (e.g. 2:30pm)',
},
+/*
{
id: 'useCompactLayout',
label: 'Use compact timeline layout',
diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js
index 44c4051995..6ec076ba95 100644
--- a/src/components/views/rooms/EventTile.js
+++ b/src/components/views/rooms/EventTile.js
@@ -16,6 +16,8 @@ limitations under the License.
'use strict';
+import UserSettingsStore from '../../../UserSettingsStore';
+
var React = require('react');
var classNames = require("classnames");
var Modal = require('../../../Modal');
@@ -479,24 +481,31 @@ module.exports = WithMatrixClient(React.createClass({
);
var e2e;
+ let e2e_style;
// cosmetic padlocks:
+ if (UserSettingsStore.getSyncedSetting('showTwelveHourTimestamps')) {
+ e2e_style = "mx_EventTile_e2eIcon mx_EventTile_e2eIcon_12hr";
+ }
+ else {
+ e2e_style = "mx_EventTile_e2eIcon";
+ }
if ((e2eEnabled && this.props.eventSendStatus) || this.props.mxEvent.getType() === 'm.room.encryption') {
- e2e = ;
+ e2e = ;
}
// real padlocks
else if (this.props.mxEvent.isEncrypted() || (e2eEnabled && this.props.eventSendStatus)) {
if (this.props.mxEvent.getContent().msgtype === 'm.bad.encrypted') {
- e2e = ;
+ e2e = ;
}
else if (this.state.verified == true || (e2eEnabled && this.props.eventSendStatus)) {
- e2e = ;
+ e2e = ;
}
else {
- e2e = ;
+ e2e = ;
}
}
else if (e2eEnabled) {
- e2e = ;
+ e2e = ;
}
const timestamp = this.props.mxEvent.getTs() ?
: null;
From 5aa1bc418550fda7f4cbc96167151792d91b5633 Mon Sep 17 00:00:00 2001
From: Kieran Gould
Date: Fri, 19 May 2017 21:31:24 +0100
Subject: [PATCH 0139/1016] Rename twentyFourHour Pad time
---
src/DateUtils.js | 19 ++++++++-----------
1 file changed, 8 insertions(+), 11 deletions(-)
diff --git a/src/DateUtils.js b/src/DateUtils.js
index f787fc0e20..d0222325b7 100644
--- a/src/DateUtils.js
+++ b/src/DateUtils.js
@@ -19,21 +19,18 @@ limitations under the License.
import UserSettingsStore from './UserSettingsStore';
const days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
-let time;
function pad(n) {
return (n < 10 ? '0' : '') + n;
}
-function twentyfourhour(date) {
- let hours = date.getHours();
- let minutes = date.getMinutes();
- let ampm = hours >= 12 ? 'PM' : 'AM';
- hours = hours % 12;
- hours = hours ? hours : 12;
- minutes = minutes < 10 ? '0'+minutes : minutes;
- var strTime = hours + ':' + minutes + ' ' + ampm;
- return strTime;
+function twentyFourHour(date) {
+ let hours = date.getHours() % 12;
+ let minutes = pad(date.getMinutes());
+ const ampm = hours >= 12 ? 'PM' : 'AM';
+ hours = pad(hours ? hours : 12);
+ minutes = pad(minutes);
+ return hours + ':' + minutes + ' ' + ampm;
}
module.exports = {
@@ -59,7 +56,7 @@ module.exports = {
formatTime: function(date) {
if (UserSettingsStore.getSyncedSetting('showTwelveHourTimestamps')) {
- return twentyfourhour(date);
+ return twentyFourHour(date);
}
return pad(date.getHours()) + ':' + pad(date.getMinutes());
},
From f9152b205cfcc299d0cf3f08b3ad4ca1a31d992b Mon Sep 17 00:00:00 2001
From: Kieran Gould
Date: Fri, 19 May 2017 22:24:02 +0100
Subject: [PATCH 0140/1016] Add parameter `showTwelveHours` to formatTime
---
src/DateUtils.js | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/src/DateUtils.js b/src/DateUtils.js
index d0222325b7..1f231088a1 100644
--- a/src/DateUtils.js
+++ b/src/DateUtils.js
@@ -16,7 +16,6 @@ limitations under the License.
'use strict';
-import UserSettingsStore from './UserSettingsStore';
const days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
@@ -24,13 +23,13 @@ function pad(n) {
return (n < 10 ? '0' : '') + n;
}
-function twentyFourHour(date) {
+function twelveHourTime(date) {
let hours = date.getHours() % 12;
let minutes = pad(date.getMinutes());
const ampm = hours >= 12 ? 'PM' : 'AM';
hours = pad(hours ? hours : 12);
minutes = pad(minutes);
- return hours + ':' + minutes + ' ' + ampm;
+ return `${hours}:${minutes} ${ampm}`;
}
module.exports = {
@@ -54,9 +53,9 @@ module.exports = {
return days[date.getDay()] + ", " + months[date.getMonth()] + " " + date.getDate() + " " + date.getFullYear() + " " + this.formatTime(date);
},
- formatTime: function(date) {
- if (UserSettingsStore.getSyncedSetting('showTwelveHourTimestamps')) {
- return twentyFourHour(date);
+ formatTime: function(date, showTwelveHour=false) {
+ if (showTwelveHour) {
+ return twelveHourTime(date);
}
return pad(date.getHours()) + ':' + pad(date.getMinutes());
},
From 6b32975e0c1ddf152053f1842642adf089406422 Mon Sep 17 00:00:00 2001
From: Kieran Gould
Date: Fri, 19 May 2017 22:26:24 +0100
Subject: [PATCH 0141/1016] Add 12 hour class to mx_EventTile
---
src/DateUtils.js | 7 ++----
src/components/views/rooms/EventTile.js | 32 ++++++++++---------------
2 files changed, 15 insertions(+), 24 deletions(-)
diff --git a/src/DateUtils.js b/src/DateUtils.js
index 1f231088a1..d516cf07bf 100644
--- a/src/DateUtils.js
+++ b/src/DateUtils.js
@@ -25,10 +25,9 @@ function pad(n) {
function twelveHourTime(date) {
let hours = date.getHours() % 12;
- let minutes = pad(date.getMinutes());
+ const minutes = pad(date.getMinutes());
const ampm = hours >= 12 ? 'PM' : 'AM';
hours = pad(hours ? hours : 12);
- minutes = pad(minutes);
return `${hours}:${minutes} ${ampm}`;
}
@@ -44,9 +43,7 @@ module.exports = {
else if (now.getFullYear() === date.getFullYear()) {
return days[date.getDay()] + ", " + months[date.getMonth()] + " " + date.getDate() + " " + this.formatTime(date);
}
- else {
- return this.formatFullDate(date);
- }
+ return this.formatFullDate(date);
},
formatFullDate: function(date) {
diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js
index 6ec076ba95..fd5fa81390 100644
--- a/src/components/views/rooms/EventTile.js
+++ b/src/components/views/rooms/EventTile.js
@@ -406,9 +406,12 @@ module.exports = WithMatrixClient(React.createClass({
var isSending = (['sending', 'queued', 'encrypting'].indexOf(this.props.eventSendStatus) !== -1);
const isRedacted = (eventType === 'm.room.message') && this.props.isRedacted;
- var classes = classNames({
+ const isTwelveHour = UserSettingsStore.getSyncedSetting('showTwelveHourTimestamps');
+
+ const classes = classNames({
mx_EventTile: true,
mx_EventTile_info: isInfoMessage,
+ mx_EventTile_12hr: isTwelveHour,
mx_EventTile_encrypting: this.props.eventSendStatus == 'encrypting',
mx_EventTile_sending: isSending,
mx_EventTile_notSent: this.props.eventSendStatus == 'not_sent',
@@ -476,43 +479,34 @@ module.exports = WithMatrixClient(React.createClass({
}
}
- var editButton = (
+ const editButton = (
);
-
- var e2e;
- let e2e_style;
+ let e2e;
// cosmetic padlocks:
- if (UserSettingsStore.getSyncedSetting('showTwelveHourTimestamps')) {
- e2e_style = "mx_EventTile_e2eIcon mx_EventTile_e2eIcon_12hr";
- }
- else {
- e2e_style = "mx_EventTile_e2eIcon";
- }
if ((e2eEnabled && this.props.eventSendStatus) || this.props.mxEvent.getType() === 'm.room.encryption') {
- e2e = ;
+ e2e = ;
}
// real padlocks
else if (this.props.mxEvent.isEncrypted() || (e2eEnabled && this.props.eventSendStatus)) {
if (this.props.mxEvent.getContent().msgtype === 'm.bad.encrypted') {
- e2e = ;
+ e2e = ;
}
else if (this.state.verified == true || (e2eEnabled && this.props.eventSendStatus)) {
- e2e = ;
+ e2e = ;
}
else {
- e2e = ;
+ e2e = ;
}
}
else if (e2eEnabled) {
- e2e = ;
+ e2e = ;
}
const timestamp = this.props.mxEvent.getTs() ?
- : null;
+ : null;
if (this.props.tileShape === "notif") {
- var room = this.props.matrixClient.getRoom(this.props.mxEvent.getRoomId());
-
+ const room = this.props.matrixClient.getRoom(this.props.mxEvent.getRoomId());
return (
From 47e5e8d678fd24e5ad5f9e9250db74368a28b6f7 Mon Sep 17 00:00:00 2001
From: Kieran Gould
Date: Fri, 19 May 2017 22:45:56 +0100
Subject: [PATCH 0142/1016] Moved isTwelveHour alllll the way up to
TimelinePanel.
---
src/components/structures/MessagePanel.js | 13 ++++++++-----
src/components/structures/TimelinePanel.js | 8 ++++++--
src/components/views/rooms/EventTile.js | 10 +++++-----
3 files changed, 19 insertions(+), 12 deletions(-)
diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js
index d4bf147ad5..d98a464aef 100644
--- a/src/components/structures/MessagePanel.js
+++ b/src/components/structures/MessagePanel.js
@@ -84,6 +84,9 @@ module.exports = React.createClass({
// shape parameter to be passed to EventTiles
tileShape: React.PropTypes.string,
+
+ // show twelve hour timestamps
+ isTwelveHour: React.PropTypes.bool,
},
componentWillMount: function() {
@@ -230,8 +233,8 @@ module.exports = React.createClass({
},
_getEventTiles: function() {
- var EventTile = sdk.getComponent('rooms.EventTile');
- var DateSeparator = sdk.getComponent('messages.DateSeparator');
+ const EventTile = sdk.getComponent('rooms.EventTile');
+ const DateSeparator = sdk.getComponent('messages.DateSeparator');
const MemberEventListSummary = sdk.getComponent('views.elements.MemberEventListSummary');
this.eventNodes = {};
@@ -413,8 +416,8 @@ module.exports = React.createClass({
},
_getTilesForEvent: function(prevEvent, mxEv, last) {
- var EventTile = sdk.getComponent('rooms.EventTile');
- var DateSeparator = sdk.getComponent('messages.DateSeparator');
+ const EventTile = sdk.getComponent('rooms.EventTile');
+ const DateSeparator = sdk.getComponent('messages.DateSeparator');
var ret = [];
// is this a continuation of the previous message?
@@ -468,7 +471,6 @@ module.exports = React.createClass({
if (this.props.manageReadReceipts) {
readReceipts = this._getReadReceiptsForEvent(mxEv);
}
-
ret.push(
);
diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js
index 8794713501..c629f51c31 100644
--- a/src/components/structures/TimelinePanel.js
+++ b/src/components/structures/TimelinePanel.js
@@ -29,6 +29,7 @@ var ObjectUtils = require('../../ObjectUtils');
var Modal = require("../../Modal");
var UserActivity = require("../../UserActivity");
var KeyCode = require('../../KeyCode');
+import UserSettingsStore from '../../UserSettingsStore';
var PAGINATE_SIZE = 20;
var INITIAL_SIZE = 20;
@@ -122,7 +123,7 @@ var TimelinePanel = React.createClass({
let initialReadMarker = null;
if (this.props.manageReadMarkers) {
const readmarker = this.props.timelineSet.room.getAccountData('m.fully_read');
- if (readmarker){
+ if (readmarker) {
initialReadMarker = readmarker.getContent().event_id;
} else {
initialReadMarker = this._getCurrentReadReceipt();
@@ -171,6 +172,9 @@ var TimelinePanel = React.createClass({
// cache of matrixClient.getSyncState() (but from the 'sync' event)
clientSyncState: MatrixClientPeg.get().getSyncState(),
+
+ // should the event tiles have twelve hour times
+ isTwelveHour: UserSettingsStore.getSyncedSetting('showTwelveHourTimestamps'),
};
},
@@ -1106,7 +1110,6 @@ var TimelinePanel = React.createClass({
const forwardPaginating = (
this.state.forwardPaginating || this.state.clientSyncState == 'PREPARED'
);
-
return (
diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js
index fd5fa81390..67a2f03590 100644
--- a/src/components/views/rooms/EventTile.js
+++ b/src/components/views/rooms/EventTile.js
@@ -16,7 +16,6 @@ limitations under the License.
'use strict';
-import UserSettingsStore from '../../../UserSettingsStore';
var React = require('react');
var classNames = require("classnames");
@@ -131,6 +130,9 @@ module.exports = WithMatrixClient(React.createClass({
* for now.
*/
tileShape: React.PropTypes.string,
+
+ // show twelve hour timestamps
+ isTwelveHour: React.PropTypes.bool,
},
getInitialState: function() {
@@ -406,12 +408,10 @@ module.exports = WithMatrixClient(React.createClass({
var isSending = (['sending', 'queued', 'encrypting'].indexOf(this.props.eventSendStatus) !== -1);
const isRedacted = (eventType === 'm.room.message') && this.props.isRedacted;
- const isTwelveHour = UserSettingsStore.getSyncedSetting('showTwelveHourTimestamps');
-
const classes = classNames({
mx_EventTile: true,
mx_EventTile_info: isInfoMessage,
- mx_EventTile_12hr: isTwelveHour,
+ mx_EventTile_12hr: this.props.isTwelveHour,
mx_EventTile_encrypting: this.props.eventSendStatus == 'encrypting',
mx_EventTile_sending: isSending,
mx_EventTile_notSent: this.props.eventSendStatus == 'not_sent',
@@ -503,7 +503,7 @@ module.exports = WithMatrixClient(React.createClass({
e2e = ;
}
const timestamp = this.props.mxEvent.getTs() ?
- : null;
+ : null;
if (this.props.tileShape === "notif") {
const room = this.props.matrixClient.getRoom(this.props.mxEvent.getRoomId());
From 02a1c1868cf796f451c73e48970d628fa16b6ce7 Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Sat, 20 May 2017 00:07:55 +0100
Subject: [PATCH 0143/1016] autoFocus input box
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
---
src/components/views/elements/DirectorySearchBox.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/views/elements/DirectorySearchBox.js b/src/components/views/elements/DirectorySearchBox.js
index 467878caad..eaa6ee34ba 100644
--- a/src/components/views/elements/DirectorySearchBox.js
+++ b/src/components/views/elements/DirectorySearchBox.js
@@ -93,7 +93,7 @@ export default class DirectorySearchBox extends React.Component {
className="mx_DirectorySearchBox_input"
ref={this._collectInput}
onChange={this._onChange} onKeyUp={this._onKeyUp}
- placeholder={this.props.placeholder}
+ placeholder={this.props.placeholder} autoFocus
/>
{join_button}
From 574b820d1194eef7c7291b82a93f67c7b4f3452a Mon Sep 17 00:00:00 2001
From: Richard van der Hoff
Date: Wed, 17 May 2017 16:40:48 +0100
Subject: [PATCH 0144/1016] Factor out DeviceVerifyDialog
---
.../views/dialogs/DeviceVerifyDialog.js | 74 +++++++++++++++++++
.../views/elements/DeviceVerifyButtons.js | 40 +---------
2 files changed, 78 insertions(+), 36 deletions(-)
create mode 100644 src/components/views/dialogs/DeviceVerifyDialog.js
diff --git a/src/components/views/dialogs/DeviceVerifyDialog.js b/src/components/views/dialogs/DeviceVerifyDialog.js
new file mode 100644
index 0000000000..8446334afb
--- /dev/null
+++ b/src/components/views/dialogs/DeviceVerifyDialog.js
@@ -0,0 +1,74 @@
+/*
+Copyright 2016 OpenMarket Ltd
+Copyright 2017 Vector Creations Ltd
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import React from 'react';
+import MatrixClientPeg from '../../../MatrixClientPeg';
+import sdk from '../../../index';
+
+export default function DeviceVerifyDialog(props) {
+ const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
+
+ const body = (
+
+
+ To verify that this device can be trusted, please contact its
+ owner using some other means (e.g. in person or a phone call)
+ and ask them whether the key they see in their User Settings
+ for this device matches the key below:
+
+
+
+ Device name: { props.device.getDisplayName() }
+ Device ID: { props.device.deviceId}
+ Device key: { props.device.getFingerprint() }
+
+
+
+ If it matches, press the verify button below.
+ If it doesnt, then someone else is intercepting this device
+ and you probably want to press the blacklist button instead.
+
+
+ In future this verification process will be more sophisticated.
+
+
+ );
+
+ function onFinished(confirm) {
+ if (confirm) {
+ MatrixClientPeg.get().setDeviceVerified(
+ props.userId, props.device.deviceId, true,
+ );
+ }
+ props.onFinished(confirm);
+ }
+
+ return (
+
+ );
+}
+
+DeviceVerifyDialog.propTypes = {
+ userId: React.PropTypes.string.isRequired,
+ device: React.PropTypes.object.isRequired,
+ onFinished: React.PropTypes.func.isRequired,
+};
diff --git a/src/components/views/elements/DeviceVerifyButtons.js b/src/components/views/elements/DeviceVerifyButtons.js
index fdd34e6ad2..28a36c429e 100644
--- a/src/components/views/elements/DeviceVerifyButtons.js
+++ b/src/components/views/elements/DeviceVerifyButtons.js
@@ -50,42 +50,10 @@ export default React.createClass({
},
onVerifyClick: function() {
- var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
- Modal.createDialog(QuestionDialog, {
- title: "Verify device",
- description: (
-
-
- To verify that this device can be trusted, please contact its
- owner using some other means (e.g. in person or a phone call)
- and ask them whether the key they see in their User Settings
- for this device matches the key below:
-
-
-
- Device name: { this.state.device.getDisplayName() }
- Device ID: { this.state.device.deviceId}
- Device key: { this.state.device.getFingerprint() }
-
-
-
- If it matches, press the verify button below.
- If it doesnt, then someone else is intercepting this device
- and you probably want to press the blacklist button instead.
-
-
- In future this verification process will be more sophisticated.
-
-
- ),
- button: "I verify that the keys match",
- onFinished: confirm=>{
- if (confirm) {
- MatrixClientPeg.get().setDeviceVerified(
- this.props.userId, this.state.device.deviceId, true
- );
- }
- },
+ const DeviceVerifyDialog = sdk.getComponent('views.dialogs.DeviceVerifyDialog');
+ Modal.createDialog(DeviceVerifyDialog, {
+ userId: this.props.userId,
+ device: this.state.device,
});
},
From 3a0ee385b801b7ca6b82e68b7fd7880e886d638e Mon Sep 17 00:00:00 2001
From: David Baker
Date: Mon, 22 May 2017 11:35:02 +0100
Subject: [PATCH 0145/1016] Released js-sdk
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 72ccaad9b7..32be90e462 100644
--- a/package.json
+++ b/package.json
@@ -63,7 +63,7 @@
"isomorphic-fetch": "^2.2.1",
"linkifyjs": "^2.1.3",
"lodash": "^4.13.1",
- "matrix-js-sdk": "0.7.8-rc.1",
+ "matrix-js-sdk": "0.7.8",
"optimist": "^0.6.1",
"q": "^1.4.1",
"react": "^15.4.0",
From 545bcbdc7794d53bfdd0b06dd5ba1f1304f3e730 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Mon, 22 May 2017 11:36:55 +0100
Subject: [PATCH 0146/1016] Prepare changelog for v0.8.9
---
CHANGELOG.md | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9db62ec629..3b9ecdb325 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,10 @@
+Changes in [0.8.9](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v0.8.9) (2017-05-22)
+===================================================================================================
+[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v0.8.9-rc.1...v0.8.9)
+
+ * No changes
+
+
Changes in [0.8.9-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v0.8.9-rc.1) (2017-05-19)
=============================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v0.8.8...v0.8.9-rc.1)
From eadd6192e819ee717e194e0f5adcc854e1dad78f Mon Sep 17 00:00:00 2001
From: David Baker
Date: Mon, 22 May 2017 11:36:56 +0100
Subject: [PATCH 0147/1016] v0.8.9
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 32be90e462..059fdd390f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "matrix-react-sdk",
- "version": "0.8.9-rc.1",
+ "version": "0.8.9",
"description": "SDK for matrix.org using React",
"author": "matrix.org",
"repository": {
From fc08dc33c6ceec92b9fa861453877fbb7dd4cbc9 Mon Sep 17 00:00:00 2001
From: Richard van der Hoff
Date: Mon, 22 May 2017 12:01:09 +0100
Subject: [PATCH 0148/1016] Group e2e keys into blocks of 4 characters
Hopefully this will make them a bit easier to compare.
---
src/components/structures/UserSettings.js | 8 +++++++-
src/components/views/dialogs/DeviceVerifyDialog.js | 4 +++-
src/utils/FormattingUtils.js | 11 +++++++++++
3 files changed, 21 insertions(+), 2 deletions(-)
diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js
index 2c1f17ee3e..342e3983ec 100644
--- a/src/components/structures/UserSettings.js
+++ b/src/components/structures/UserSettings.js
@@ -29,6 +29,7 @@ const Email = require('../../email');
const AddThreepid = require('../../AddThreepid');
const SdkConfig = require('../../SdkConfig');
import AccessibleButton from '../views/elements/AccessibleButton';
+import * as FormattingUtils from '../../utils/FormattingUtils';
// if this looks like a release, use the 'version' from package.json; else use
// the git sha. Prepend version with v, to look like riot-web version
@@ -603,7 +604,12 @@ module.exports = React.createClass({
_renderCryptoInfo: function() {
const client = MatrixClientPeg.get();
const deviceId = client.deviceId;
- const identityKey = client.getDeviceEd25519Key() || "";
+ let identityKey = client.getDeviceEd25519Key();
+ if (!identityKey) {
+ identityKey = "";
+ } else {
+ identityKey = FormattingUtils.formatCryptoKey(identityKey);
+ }
let importExportButtons = null;
diff --git a/src/components/views/dialogs/DeviceVerifyDialog.js b/src/components/views/dialogs/DeviceVerifyDialog.js
index 8446334afb..f9feb718b0 100644
--- a/src/components/views/dialogs/DeviceVerifyDialog.js
+++ b/src/components/views/dialogs/DeviceVerifyDialog.js
@@ -18,10 +18,12 @@ limitations under the License.
import React from 'react';
import MatrixClientPeg from '../../../MatrixClientPeg';
import sdk from '../../../index';
+import * as FormattingUtils from '../../../utils/FormattingUtils';
export default function DeviceVerifyDialog(props) {
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
+ const key = FormattingUtils.formatCryptoKey(props.device.getFingerprint());
const body = (
@@ -34,7 +36,7 @@ export default function DeviceVerifyDialog(props) {
Device name: { props.device.getDisplayName() }
Device ID: { props.device.deviceId}
- Device key: { props.device.getFingerprint() }
+ Device key: { key }
diff --git a/src/utils/FormattingUtils.js b/src/utils/FormattingUtils.js
index 414784d101..a27851951f 100644
--- a/src/utils/FormattingUtils.js
+++ b/src/utils/FormattingUtils.js
@@ -26,3 +26,14 @@ export function formatCount(count) {
if (count < 100000000) return (count / 1000000).toFixed(0) + "M";
return (count / 1000000000).toFixed(1) + "B"; // 10B is enough for anyone, right? :S
}
+
+/**
+ * format a key into groups of 4 characters, for easier visual inspection
+ *
+ * @param {string} key key to format
+ *
+ * @return {string}
+ */
+export function formatCryptoKey(key) {
+ return key.match(/.{1,4}/g).join(" ");
+}
From a1f10ff46de6323338907c57b1df84a260ffa9f4 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Mon, 22 May 2017 14:46:49 +0100
Subject: [PATCH 0149/1016] Add prop to toggle whether new password input is
autoFocused
---
src/components/views/settings/ChangePassword.js | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/components/views/settings/ChangePassword.js b/src/components/views/settings/ChangePassword.js
index 601b774932..bfc9ac264e 100644
--- a/src/components/views/settings/ChangePassword.js
+++ b/src/components/views/settings/ChangePassword.js
@@ -35,6 +35,8 @@ module.exports = React.createClass({
rowInputClassName: React.PropTypes.string,
buttonClassName: React.PropTypes.string,
confirm: React.PropTypes.bool,
+ // Whether to autoFocus the new password input
+ autoFocusNewPasswordInput: React.PropTypes.bool,
},
Phases: {
@@ -199,7 +201,7 @@ module.exports = React.createClass({
New password
-
+
From 2c462bdfb4fac7ea4d48fea568e0fa4b4d04341d Mon Sep 17 00:00:00 2001
From: David Baker
Date: Mon, 22 May 2017 16:27:41 +0100
Subject: [PATCH 0150/1016] Add right-branch logic to travis test script
---
.travis-test-riot.sh | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/.travis-test-riot.sh b/.travis-test-riot.sh
index c280044246..bab8242146 100755
--- a/.travis-test-riot.sh
+++ b/.travis-test-riot.sh
@@ -9,7 +9,10 @@ set -ev
RIOT_WEB_DIR=riot-web
REACT_SDK_DIR=`pwd`
-git clone --depth=1 --branch develop https://github.com/vector-im/riot-web.git \
+curbranch="${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH}"
+echo "Determined branch to be $curbranch"
+
+git clone --depth=1 --branch "$curbranch" https://github.com/vector-im/riot-web.git \
"$RIOT_WEB_DIR"
cd "$RIOT_WEB_DIR"
From b0a824c94190c317f6c18a238d6f3e04727d58a6 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Mon, 22 May 2017 16:28:23 +0100
Subject: [PATCH 0151/1016] Remove double declaration of TextInputDialog
---
src/components/structures/MatrixChat.js | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index 85c12979f6..59ce1b622d 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -439,7 +439,6 @@ module.exports = React.createClass({
break;
}
- var TextInputDialog = sdk.getComponent("dialogs.TextInputDialog");
Modal.createDialog(TextInputDialog, {
title: "Create Room",
description: "Room name (optional)",
From 77cd9d78db07a0030f00ff87b1bcc2536217df2a Mon Sep 17 00:00:00 2001
From: David Baker
Date: Mon, 22 May 2017 16:34:15 +0100
Subject: [PATCH 0152/1016] Use develop if no matching branch found
---
.travis-test-riot.sh | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/.travis-test-riot.sh b/.travis-test-riot.sh
index bab8242146..ee0f45e1ec 100755
--- a/.travis-test-riot.sh
+++ b/.travis-test-riot.sh
@@ -12,9 +12,11 @@ REACT_SDK_DIR=`pwd`
curbranch="${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH}"
echo "Determined branch to be $curbranch"
-git clone --depth=1 --branch "$curbranch" https://github.com/vector-im/riot-web.git \
+git clone --depth=1 https://github.com/vector-im/riot-web.git \
"$RIOT_WEB_DIR"
+git checkout "$curbranch" || git checkout develop
+
cd "$RIOT_WEB_DIR"
mkdir node_modules
From c5a873c316c0d6e5857db5513cd3ccb43d909bc9 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Mon, 22 May 2017 16:41:43 +0100
Subject: [PATCH 0153/1016] Remove depth=1 otherwise we won't find the branch
---
.travis-test-riot.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.travis-test-riot.sh b/.travis-test-riot.sh
index ee0f45e1ec..c0cd911f08 100755
--- a/.travis-test-riot.sh
+++ b/.travis-test-riot.sh
@@ -12,7 +12,7 @@ REACT_SDK_DIR=`pwd`
curbranch="${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH}"
echo "Determined branch to be $curbranch"
-git clone --depth=1 https://github.com/vector-im/riot-web.git \
+git clone https://github.com/vector-im/riot-web.git \
"$RIOT_WEB_DIR"
git checkout "$curbranch" || git checkout develop
From 52d3dd4de90259cb1374e02b3c7dd7a0829ac390 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Mon, 22 May 2017 16:48:53 +0100
Subject: [PATCH 0154/1016] cd first so we checkout the right repo
---
.travis-test-riot.sh | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.travis-test-riot.sh b/.travis-test-riot.sh
index c0cd911f08..4296c72e6c 100755
--- a/.travis-test-riot.sh
+++ b/.travis-test-riot.sh
@@ -15,10 +15,10 @@ echo "Determined branch to be $curbranch"
git clone https://github.com/vector-im/riot-web.git \
"$RIOT_WEB_DIR"
-git checkout "$curbranch" || git checkout develop
-
cd "$RIOT_WEB_DIR"
+git checkout "$curbranch" || git checkout develop
+
mkdir node_modules
npm install
From 024964acebd2d5c50890a15c596888750ec27534 Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Mon, 22 May 2017 18:33:14 +0100
Subject: [PATCH 0155/1016] pass call state through, for things that don't have
access to full API
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
---
src/CallHandler.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/CallHandler.js b/src/CallHandler.js
index 5199ef0a67..6a30121992 100644
--- a/src/CallHandler.js
+++ b/src/CallHandler.js
@@ -179,7 +179,8 @@ function _setCallState(call, roomId, status) {
}
dis.dispatch({
action: 'call_state',
- room_id: roomId
+ room_id: roomId,
+ state: status,
});
}
From 26c8540d035017b55fcb31b1b90054f6a2077dab Mon Sep 17 00:00:00 2001
From: Richard van der Hoff
Date: Tue, 23 May 2017 09:24:18 +0100
Subject: [PATCH 0156/1016] Add in a "verify" slash command to confirm signing
keys (#912)
Allows users to send a text string via an alternative channel (like email
or SMS) which Riot can leverage to confirm that the signing keys match.
Effectively removes the tedium of checking keys until a better mechanism
is completed.
Signed-off-by: Kit Sczudlo
---
src/SlashCommands.js | 55 +++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 54 insertions(+), 1 deletion(-)
diff --git a/src/SlashCommands.js b/src/SlashCommands.js
index 1ddcf4832d..ddba344e18 100644
--- a/src/SlashCommands.js
+++ b/src/SlashCommands.js
@@ -282,7 +282,60 @@ var commands = {
}
}
return reject(this.getUsage());
- })
+ }),
+
+ // Verify a user, device, and pubkey tuple
+ verify: new Command("verify", " ", function(room_id, args) {
+ if (args) {
+ var matches = args.match(/^(\S+) +(\S+) +(\S+)$/);
+ if (matches) {
+ const userId = matches[1];
+ const deviceId = matches[2];
+ const fingerprint = matches[3];
+
+ const device = MatrixClientPeg.get().getStoredDevice(userId, deviceId);
+ if (!device) {
+ return reject(`Unknown (user, device) pair: (${userId}, ${deviceId})`);
+ }
+
+ if (device.isVerified()) {
+ if (device.getFingerprint() === fingerprint) {
+ return reject(`Device already verified!`);
+ } else {
+ return reject(`WARNING: Device already verified, but keys do NOT MATCH!`);
+ }
+ }
+
+ if (device.getFingerprint() === fingerprint) {
+ MatrixClientPeg.get().setDeviceVerified(
+ userId, deviceId, true
+ );
+
+ // Tell the user we verified everything!
+ const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
+ Modal.createDialog(QuestionDialog, {
+ title: "Verified key",
+ description: (
+
+
+ The signing key you provided matches the signing key you received
+ from { userId }'s device { deviceId }. Device marked as verified.
+
+
+ ),
+ hasCancelButton: false,
+ });
+
+ return success();
+ } else {
+ return reject(`WARNING: KEY VERIFICATION FAILED! The signing key for ${userId} and device
+ ${deviceId} is "${device.getFingerprint()}" which does not match the provided key
+ "${fingerprint}". This could mean your communications are being intercepted!`);
+ }
+ }
+ }
+ return reject(this.getUsage());
+ }),
};
// helpful aliases
From 5df4b9de16fd06aebf3ef89a83fd3fefeb3fca45 Mon Sep 17 00:00:00 2001
From: Richard van der Hoff
Date: Tue, 23 May 2017 09:44:11 +0100
Subject: [PATCH 0157/1016] Fix lint errors in SlashCommands
---
src/SlashCommands.js | 192 ++++++++++++++++++++++---------------------
1 file changed, 100 insertions(+), 92 deletions(-)
diff --git a/src/SlashCommands.js b/src/SlashCommands.js
index ddba344e18..bd68f1a6fe 100644
--- a/src/SlashCommands.js
+++ b/src/SlashCommands.js
@@ -14,9 +14,9 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-var MatrixClientPeg = require("./MatrixClientPeg");
-var dis = require("./dispatcher");
-var Tinter = require("./Tinter");
+import MatrixClientPeg from "./MatrixClientPeg";
+import dis from "./dispatcher";
+import Tinter from "./Tinter";
import sdk from './index';
import Modal from './Modal';
@@ -45,19 +45,25 @@ class Command {
}
}
-var reject = function(msg) {
+function reject(msg) {
return {
- error: msg
+ error: msg,
};
-};
+}
-var success = function(promise) {
+function success(promise) {
return {
- promise: promise
+ promise: promise,
};
-};
+}
-var commands = {
+/* Disable the "unexpected this" error for these commands - all of the run
+ * functions are called with `this` bound to the Command instance.
+ */
+
+/* eslint-disable babel/no-invalid-this */
+
+const commands = {
ddg: new Command("ddg", "", function(roomId, args) {
const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog');
// TODO Don't explain this away, actually show a search UI here.
@@ -69,30 +75,30 @@ var commands = {
}),
// Change your nickname
- nick: new Command("nick", "", function(room_id, args) {
+ nick: new Command("nick", "", function(roomId, args) {
if (args) {
return success(
- MatrixClientPeg.get().setDisplayName(args)
+ MatrixClientPeg.get().setDisplayName(args),
);
}
return reject(this.getUsage());
}),
// Changes the colorscheme of your current room
- tint: new Command("tint", " []", function(room_id, args) {
+ tint: new Command("tint", " []", function(roomId, args) {
if (args) {
- var matches = args.match(/^(#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}))( +(#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})))?$/);
+ const matches = args.match(/^(#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}))( +(#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})))?$/);
if (matches) {
Tinter.tint(matches[1], matches[4]);
- var colorScheme = {};
+ const colorScheme = {};
colorScheme.primary_color = matches[1];
if (matches[4]) {
colorScheme.secondary_color = matches[4];
}
return success(
MatrixClientPeg.get().setRoomAccountData(
- room_id, "org.matrix.room.color_scheme", colorScheme
- )
+ roomId, "org.matrix.room.color_scheme", colorScheme,
+ ),
);
}
}
@@ -100,22 +106,22 @@ var commands = {
}),
// Change the room topic
- topic: new Command("topic", "", function(room_id, args) {
+ topic: new Command("topic", "", function(roomId, args) {
if (args) {
return success(
- MatrixClientPeg.get().setRoomTopic(room_id, args)
+ MatrixClientPeg.get().setRoomTopic(roomId, args),
);
}
return reject(this.getUsage());
}),
// Invite a user
- invite: new Command("invite", "", function(room_id, args) {
+ invite: new Command("invite", "", function(roomId, args) {
if (args) {
- var matches = args.match(/^(\S+)$/);
+ const matches = args.match(/^(\S+)$/);
if (matches) {
return success(
- MatrixClientPeg.get().invite(room_id, matches[1])
+ MatrixClientPeg.get().invite(roomId, matches[1]),
);
}
}
@@ -123,21 +129,21 @@ var commands = {
}),
// Join a room
- join: new Command("join", "#alias:domain", function(room_id, args) {
+ join: new Command("join", "#alias:domain", function(roomId, args) {
if (args) {
- var matches = args.match(/^(\S+)$/);
+ const matches = args.match(/^(\S+)$/);
if (matches) {
- var room_alias = matches[1];
- if (room_alias[0] !== '#') {
+ let roomAlias = matches[1];
+ if (roomAlias[0] !== '#') {
return reject(this.getUsage());
}
- if (!room_alias.match(/:/)) {
- room_alias += ':' + MatrixClientPeg.get().getDomain();
+ if (!roomAlias.match(/:/)) {
+ roomAlias += ':' + MatrixClientPeg.get().getDomain();
}
dis.dispatch({
action: 'view_room',
- room_alias: room_alias,
+ roomAlias: roomAlias,
auto_join: true,
});
@@ -147,29 +153,29 @@ var commands = {
return reject(this.getUsage());
}),
- part: new Command("part", "[#alias:domain]", function(room_id, args) {
- var targetRoomId;
+ part: new Command("part", "[#alias:domain]", function(roomId, args) {
+ let targetRoomId;
if (args) {
- var matches = args.match(/^(\S+)$/);
+ const matches = args.match(/^(\S+)$/);
if (matches) {
- var room_alias = matches[1];
- if (room_alias[0] !== '#') {
+ let roomAlias = matches[1];
+ if (roomAlias[0] !== '#') {
return reject(this.getUsage());
}
- if (!room_alias.match(/:/)) {
- room_alias += ':' + MatrixClientPeg.get().getDomain();
+ if (!roomAlias.match(/:/)) {
+ roomAlias += ':' + MatrixClientPeg.get().getDomain();
}
// Try to find a room with this alias
- var rooms = MatrixClientPeg.get().getRooms();
- for (var i = 0; i < rooms.length; i++) {
- var aliasEvents = rooms[i].currentState.getStateEvents(
- "m.room.aliases"
+ const rooms = MatrixClientPeg.get().getRooms();
+ for (let i = 0; i < rooms.length; i++) {
+ const aliasEvents = rooms[i].currentState.getStateEvents(
+ "m.room.aliases",
);
- for (var j = 0; j < aliasEvents.length; j++) {
- var aliases = aliasEvents[j].getContent().aliases || [];
- for (var k = 0; k < aliases.length; k++) {
- if (aliases[k] === room_alias) {
+ for (let j = 0; j < aliasEvents.length; j++) {
+ const aliases = aliasEvents[j].getContent().aliases || [];
+ for (let k = 0; k < aliases.length; k++) {
+ if (aliases[k] === roomAlias) {
targetRoomId = rooms[i].roomId;
break;
}
@@ -178,27 +184,28 @@ var commands = {
}
if (targetRoomId) { break; }
}
- }
- if (!targetRoomId) {
- return reject("Unrecognised room alias: " + room_alias);
+ if (!targetRoomId) {
+ return reject("Unrecognised room alias: " + roomAlias);
+ }
}
}
- if (!targetRoomId) targetRoomId = room_id;
+ if (!targetRoomId) targetRoomId = roomId;
return success(
MatrixClientPeg.get().leave(targetRoomId).then(
- function() {
- dis.dispatch({action: 'view_next_room'});
- })
+ function() {
+ dis.dispatch({action: 'view_next_room'});
+ },
+ ),
);
}),
// Kick a user from the room with an optional reason
- kick: new Command("kick", " []", function(room_id, args) {
+ kick: new Command("kick", " []", function(roomId, args) {
if (args) {
- var matches = args.match(/^(\S+?)( +(.*))?$/);
+ const matches = args.match(/^(\S+?)( +(.*))?$/);
if (matches) {
return success(
- MatrixClientPeg.get().kick(room_id, matches[1], matches[3])
+ MatrixClientPeg.get().kick(roomId, matches[1], matches[3]),
);
}
}
@@ -206,12 +213,12 @@ var commands = {
}),
// Ban a user from the room with an optional reason
- ban: new Command("ban", " []", function(room_id, args) {
+ ban: new Command("ban", " []", function(roomId, args) {
if (args) {
- var matches = args.match(/^(\S+?)( +(.*))?$/);
+ const matches = args.match(/^(\S+?)( +(.*))?$/);
if (matches) {
return success(
- MatrixClientPeg.get().ban(room_id, matches[1], matches[3])
+ MatrixClientPeg.get().ban(roomId, matches[1], matches[3]),
);
}
}
@@ -219,13 +226,13 @@ var commands = {
}),
// Unban a user from the room
- unban: new Command("unban", "", function(room_id, args) {
+ unban: new Command("unban", "", function(roomId, args) {
if (args) {
- var matches = args.match(/^(\S+)$/);
+ const matches = args.match(/^(\S+)$/);
if (matches) {
// Reset the user membership to "leave" to unban him
return success(
- MatrixClientPeg.get().unban(room_id, matches[1])
+ MatrixClientPeg.get().unban(roomId, matches[1]),
);
}
}
@@ -233,27 +240,27 @@ var commands = {
}),
// Define the power level of a user
- op: new Command("op", " []", function(room_id, args) {
+ op: new Command("op", " []", function(roomId, args) {
if (args) {
- var matches = args.match(/^(\S+?)( +(\d+))?$/);
- var powerLevel = 50; // default power level for op
+ const matches = args.match(/^(\S+?)( +(\d+))?$/);
+ let powerLevel = 50; // default power level for op
if (matches) {
- var user_id = matches[1];
+ const userId = matches[1];
if (matches.length === 4 && undefined !== matches[3]) {
powerLevel = parseInt(matches[3]);
}
- if (powerLevel !== NaN) {
- var room = MatrixClientPeg.get().getRoom(room_id);
+ if (!isNaN(powerLevel)) {
+ const room = MatrixClientPeg.get().getRoom(roomId);
if (!room) {
- return reject("Bad room ID: " + room_id);
+ return reject("Bad room ID: " + roomId);
}
- var powerLevelEvent = room.currentState.getStateEvents(
- "m.room.power_levels", ""
+ const powerLevelEvent = room.currentState.getStateEvents(
+ "m.room.power_levels", "",
);
return success(
MatrixClientPeg.get().setPowerLevel(
- room_id, user_id, powerLevel, powerLevelEvent
- )
+ roomId, userId, powerLevel, powerLevelEvent,
+ ),
);
}
}
@@ -262,22 +269,22 @@ var commands = {
}),
// Reset the power level of a user
- deop: new Command("deop", "", function(room_id, args) {
+ deop: new Command("deop", "", function(roomId, args) {
if (args) {
- var matches = args.match(/^(\S+)$/);
+ const matches = args.match(/^(\S+)$/);
if (matches) {
- var room = MatrixClientPeg.get().getRoom(room_id);
+ const room = MatrixClientPeg.get().getRoom(roomId);
if (!room) {
- return reject("Bad room ID: " + room_id);
+ return reject("Bad room ID: " + roomId);
}
- var powerLevelEvent = room.currentState.getStateEvents(
- "m.room.power_levels", ""
+ const powerLevelEvent = room.currentState.getStateEvents(
+ "m.room.power_levels", "",
);
return success(
MatrixClientPeg.get().setPowerLevel(
- room_id, args, undefined, powerLevelEvent
- )
+ roomId, args, undefined, powerLevelEvent,
+ ),
);
}
}
@@ -285,9 +292,9 @@ var commands = {
}),
// Verify a user, device, and pubkey tuple
- verify: new Command("verify", " ", function(room_id, args) {
+ verify: new Command("verify", " ", function(roomId, args) {
if (args) {
- var matches = args.match(/^(\S+) +(\S+) +(\S+)$/);
+ const matches = args.match(/^(\S+) +(\S+) +(\S+)$/);
if (matches) {
const userId = matches[1];
const deviceId = matches[2];
@@ -308,7 +315,7 @@ var commands = {
if (device.getFingerprint() === fingerprint) {
MatrixClientPeg.get().setDeviceVerified(
- userId, deviceId, true
+ userId, deviceId, true,
);
// Tell the user we verified everything!
@@ -337,10 +344,12 @@ var commands = {
return reject(this.getUsage());
}),
};
+/* eslint-enable babel/no-invalid-this */
+
// helpful aliases
-var aliases = {
- j: "join"
+const aliases = {
+ j: "join",
};
module.exports = {
@@ -357,13 +366,13 @@ module.exports = {
// IRC-style commands
input = input.replace(/\s+$/, "");
if (input[0] === "/" && input[1] !== "/") {
- var bits = input.match(/^(\S+?)( +((.|\n)*))?$/);
- var cmd, args;
+ const bits = input.match(/^(\S+?)( +((.|\n)*))?$/);
+ let cmd;
+ let args;
if (bits) {
cmd = bits[1].substring(1).toLowerCase();
args = bits[3];
- }
- else {
+ } else {
cmd = input;
}
if (cmd === "me") return null;
@@ -372,8 +381,7 @@ module.exports = {
}
if (commands[cmd]) {
return commands[cmd].run(roomId, args);
- }
- else {
+ } else {
return reject("Unrecognised command: " + input);
}
}
@@ -382,12 +390,12 @@ module.exports = {
getCommandList: function() {
// Return all the commands plus /me and /markdown which aren't handled like normal commands
- var cmds = Object.keys(commands).sort().map(function(cmdKey) {
+ const cmds = Object.keys(commands).sort().map(function(cmdKey) {
return commands[cmdKey];
});
cmds.push(new Command("me", "", function() {}));
cmds.push(new Command("markdown", "", function() {}));
return cmds;
- }
+ },
};
From a10c2faac1769da708ab31b8f80a012499fba2d4 Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@googlemail.com>
Date: Tue, 23 May 2017 12:24:27 +0100
Subject: [PATCH 0158/1016] lets not open an attack vector :)
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
---
src/components/structures/UserSettings.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js
index ca475fbf0c..2e163133fb 100644
--- a/src/components/structures/UserSettings.js
+++ b/src/components/structures/UserSettings.js
@@ -46,7 +46,7 @@ const gHVersionLabel = function(repo, token='') {
} else {
url = `https://github.com/${repo}/commit/${token.split('-')[0]}`;
}
- return {token} ;
+ return {token} ;
};
// Enumerate some simple 'flip a bit' UI settings (if any).
From d419c42a4f32b993dcf3e2d5c674f83c41490a85 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Tue, 23 May 2017 15:16:31 +0100
Subject: [PATCH 0159/1016] Squash merge
https://github.com/matrix-org/matrix-react-sdk/pull/801
---
README.md | 3 +-
package.json | 1 +
src/AddThreepid.js | 7 +-
src/CallHandler.js | 48 +-
src/ContentMessages.js | 10 +-
src/DateUtils.js | 49 +-
src/Lifecycle.js | 11 +-
src/Notifier.js | 10 +-
src/PasswordReset.js | 7 +-
src/Roles.js | 17 +-
src/ScalarMessaging.js | 33 +-
src/SlashCommands.js | 8 +-
src/TextForEvent.js | 95 ++-
src/UserSettingsStore.js | 6 +-
src/WhoIsTyping.js | 13 +-
.../views/dialogs/EncryptedEventDialog.js | 45 +-
src/autocomplete/CommandProvider.js | 6 +-
src/autocomplete/EmojiProvider.js | 3 +-
src/autocomplete/RoomProvider.js | 3 +-
src/autocomplete/UserProvider.js | 3 +-
src/components/structures/CreateRoom.js | 23 +-
src/components/structures/FilePanel.js | 14 +-
src/components/structures/MatrixChat.js | 38 +-
.../structures/NotificationPanel.js | 5 +-
src/components/structures/RoomStatusBar.js | 34 +-
src/components/structures/RoomView.js | 74 +-
src/components/structures/TimelinePanel.js | 13 +-
src/components/structures/UserSettings.js | 192 +++--
.../structures/login/ForgotPassword.js | 55 +-
src/components/structures/login/Login.js | 22 +-
.../structures/login/PostRegistration.js | 13 +-
.../structures/login/Registration.js | 25 +-
.../views/dialogs/ChatInviteDialog.js | 9 +-
.../views/dialogs/ConfirmUserActionDialog.js | 7 +-
src/components/views/dialogs/ErrorDialog.js | 3 -
.../views/dialogs/InteractiveAuthDialog.js | 6 -
.../views/dialogs/NeedToRegisterDialog.js | 7 -
.../views/dialogs/QuestionDialog.js | 1 -
.../views/dialogs/TextInputDialog.js | 4 +-
.../views/elements/LanguageDropdown.js | 140 ++++
.../views/elements/MemberEventListSummary.js | 164 +++-
.../views/elements/PowerSelector.js | 17 +-
src/components/views/login/PasswordLogin.js | 17 +-
src/components/views/messages/MFileBody.js | 17 +-
.../views/room_settings/AliasSettings.js | 27 +-
src/components/views/rooms/EventTile.js | 7 +-
src/components/views/rooms/MemberInfo.js | 62 +-
src/components/views/rooms/MemberList.js | 9 +-
src/components/views/rooms/MessageComposer.js | 33 +-
.../views/rooms/MessageComposerInput.js | 13 +-
.../views/rooms/MessageComposerInputOld.js | 16 +-
src/components/views/rooms/RoomHeader.js | 18 +-
src/components/views/rooms/RoomList.js | 24 +-
src/components/views/rooms/RoomPreviewBar.js | 15 +-
src/components/views/rooms/RoomSettings.js | 112 +--
.../views/rooms/SearchableEntityList.js | 9 +-
.../views/rooms/TopUnreadMessagesBar.js | 6 +-
.../views/settings/AddPhoneNumber.js | 16 +-
.../views/settings/ChangePassword.js | 29 +-
.../views/settings/DevicesPanelEntry.js | 8 +-
src/createRoom.js | 10 +-
src/i18n/strings/basefile.json | 1 +
src/i18n/strings/da.json | 215 ++++++
src/i18n/strings/de_DE.json | 702 +++++++++++++++++
src/i18n/strings/en_EN.json | 656 ++++++++++++++++
src/i18n/strings/fr.json | 170 +++++
src/i18n/strings/ml.json | 2 +
src/i18n/strings/pt.json | 503 +++++++++++++
src/i18n/strings/pt_BR.json | 706 ++++++++++++++++++
src/i18n/strings/ru.json | 476 ++++++++++++
src/index.js | 4 +-
src/languageHandler.js | 166 ++++
test/test-utils.js | 6 +
73 files changed, 4660 insertions(+), 639 deletions(-)
create mode 100644 src/components/views/elements/LanguageDropdown.js
create mode 100644 src/i18n/strings/basefile.json
create mode 100644 src/i18n/strings/da.json
create mode 100644 src/i18n/strings/de_DE.json
create mode 100644 src/i18n/strings/en_EN.json
create mode 100644 src/i18n/strings/fr.json
create mode 100644 src/i18n/strings/ml.json
create mode 100644 src/i18n/strings/pt.json
create mode 100644 src/i18n/strings/pt_BR.json
create mode 100644 src/i18n/strings/ru.json
create mode 100644 src/languageHandler.js
diff --git a/README.md b/README.md
index 3627225299..981a217c10 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,5 @@
+[](https://translate.nordgedanken.de/engage/riot-web/?utm_source=widget)
+
matrix-react-sdk
================
@@ -190,4 +192,3 @@ Alternative instructions:
* Create an index.html file pulling in your compiled javascript and the
CSS bundle from the skin you use. For now, you'll also need to manually
import CSS from any skins that your skin inherts from.
-
diff --git a/package.json b/package.json
index 059fdd390f..11106325f1 100644
--- a/package.json
+++ b/package.json
@@ -50,6 +50,7 @@
"browser-request": "^0.3.3",
"classnames": "^2.1.2",
"commonmark": "^0.27.0",
+ "counterpart-riot": "^0.18.1",
"draft-js": "^0.8.1",
"draft-js-export-html": "^0.5.0",
"draft-js-export-markdown": "^0.2.0",
diff --git a/src/AddThreepid.js b/src/AddThreepid.js
index c89de4f5fa..0503f4ff9d 100644
--- a/src/AddThreepid.js
+++ b/src/AddThreepid.js
@@ -16,6 +16,7 @@ limitations under the License.
*/
var MatrixClientPeg = require("./MatrixClientPeg");
+import _t from 'counterpart-riot';
/**
* Allows a user to add a third party identifier to their Home Server and,
@@ -44,7 +45,7 @@ class AddThreepid {
return res;
}, function(err) {
if (err.errcode == 'M_THREEPID_IN_USE') {
- err.message = "This email address is already in use";
+ err.message = _t('This email address is already in use');
} else if (err.httpStatus) {
err.message = err.message + ` (Status ${err.httpStatus})`;
}
@@ -69,7 +70,7 @@ class AddThreepid {
return res;
}, function(err) {
if (err.errcode == 'M_THREEPID_IN_USE') {
- err.message = "This phone number is already in use";
+ err.message = _t('This phone number is already in use');
} else if (err.httpStatus) {
err.message = err.message + ` (Status ${err.httpStatus})`;
}
@@ -91,7 +92,7 @@ class AddThreepid {
id_server: identityServerDomain
}, this.bind).catch(function(err) {
if (err.httpStatus === 401) {
- err.message = "Failed to verify email address: make sure you clicked the link in the email";
+ err.message = _t('Failed to verify email address: make sure you clicked the link in the email');
}
else if (err.httpStatus) {
err.message += ` (Status ${err.httpStatus})`;
diff --git a/src/CallHandler.js b/src/CallHandler.js
index 5199ef0a67..f5283f6d49 100644
--- a/src/CallHandler.js
+++ b/src/CallHandler.js
@@ -55,6 +55,7 @@ var MatrixClientPeg = require('./MatrixClientPeg');
var PlatformPeg = require("./PlatformPeg");
var Modal = require('./Modal');
var sdk = require('./index');
+import _t from 'counterpart-riot';
var Matrix = require("matrix-js-sdk");
var dis = require("./dispatcher");
@@ -142,8 +143,9 @@ function _setCallListeners(call) {
play("busyAudio");
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
- title: "Call Timeout",
- description: "The remote side failed to pick up."
+ title: _t('Call Timeout'),
+ description: _t('The remote side failed to pick up') + '.',
+ button: _t("OK"),
});
}
else if (oldState === "invite_sent") {
@@ -203,8 +205,9 @@ function _onAction(payload) {
console.log("Can't capture screen: " + screenCapErrorString);
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
- title: "Unable to capture screen",
- description: screenCapErrorString
+ title: _t('Unable to capture screen'),
+ description: screenCapErrorString,
+ button: _t("OK"),
});
return;
}
@@ -223,8 +226,9 @@ function _onAction(payload) {
if (module.exports.getAnyActiveCall()) {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
- title: "Existing Call",
- description: "You are already in a call."
+ title: _t('Existing Call'),
+ description: _t('You are already in a call') + '.',
+ button: _t("OK"),
});
return; // don't allow >1 call to be placed.
}
@@ -233,8 +237,9 @@ function _onAction(payload) {
if (!MatrixClientPeg.get().supportsVoip()) {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
- title: "VoIP is unsupported",
- description: "You cannot place VoIP calls in this browser."
+ title: _t('VoIP is unsupported'),
+ description: _t('You cannot place VoIP calls in this browser') + '.',
+ button: _t("OK"),
});
return;
}
@@ -249,7 +254,9 @@ function _onAction(payload) {
if (members.length <= 1) {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
- description: "You cannot place a call with yourself."
+ description: _t('You cannot place a call with yourself') + '.',
+ title: _t("Error"),
+ button: _t("OK"),
});
return;
}
@@ -275,14 +282,17 @@ function _onAction(payload) {
if (!ConferenceHandler) {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
- description: "Conference calls are not supported in this client"
+ description: _t('Conference calls are not supported in this client'),
+ title: _t("Error"),
+ button: _t("OK"),
});
}
else if (!MatrixClientPeg.get().supportsVoip()) {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
- title: "VoIP is unsupported",
- description: "You cannot place VoIP calls in this browser."
+ title: _t('VoIP is unsupported'),
+ description: _t('You cannot place VoIP calls in this browser') + '.',
+ button: _t("OK"),
});
}
else if (MatrixClientPeg.get().isRoomEncrypted(payload.room_id)) {
@@ -294,14 +304,17 @@ function _onAction(payload) {
// Therefore we disable conference calling in E2E rooms.
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
- description: "Conference calls are not supported in encrypted rooms",
+ title: _t("Error"),
+ button: _t("OK"),
+ description: _t('Conference calls are not supported in encrypted rooms'),
});
}
else {
var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createDialog(QuestionDialog, {
- title: "Warning!",
- description: "Conference calling is in development and may not be reliable.",
+ title: _t('Warning') + '!',
+ description: _t('Conference calling is in development and may not be reliable') + '.',
+ button: _t("OK"),
onFinished: confirm=>{
if (confirm) {
ConferenceHandler.createNewMatrixCall(
@@ -312,8 +325,9 @@ function _onAction(payload) {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Conference call failed: " + err);
Modal.createDialog(ErrorDialog, {
- title: "Failed to set up conference call",
- description: "Conference call failed. " + ((err && err.message) ? err.message : ""),
+ title: _t('Failed to set up conference call'),
+ description: _t('Conference call failed') + '. ' + ((err && err.message) ? err.message : ''),
+ button: _t("OK"),
});
});
}
diff --git a/src/ContentMessages.js b/src/ContentMessages.js
index 4ab982c98f..6e31a60556 100644
--- a/src/ContentMessages.js
+++ b/src/ContentMessages.js
@@ -21,6 +21,7 @@ var extend = require('./extend');
var dis = require('./dispatcher');
var MatrixClientPeg = require('./MatrixClientPeg');
var sdk = require('./index');
+import _t from 'counterpart-riot';
var Modal = require('./Modal');
var encrypt = require("browser-encrypt-attachment");
@@ -347,14 +348,15 @@ class ContentMessages {
}, function(err) {
error = err;
if (!upload.canceled) {
- var desc = "The file '"+upload.fileName+"' failed to upload.";
+ var desc = _t('The file \'%(fileName)s\' failed to upload', {fileName: upload.fileName}) + '.';
if (err.http_status == 413) {
- desc = "The file '"+upload.fileName+"' exceeds this home server's size limit for uploads";
+ desc = _t('The file \'%(fileName)s\' exceeds this home server\'s size limit for uploads', {fileName: upload.fileName});
}
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
- title: "Upload Failed",
- description: desc
+ title: _t('Upload Failed'),
+ description: desc,
+ button: _t("OK"),
});
}
}).finally(() => {
diff --git a/src/DateUtils.js b/src/DateUtils.js
index c58c09d4de..87b6fa690f 100644
--- a/src/DateUtils.js
+++ b/src/DateUtils.js
@@ -15,9 +15,36 @@ limitations under the License.
*/
'use strict';
+import _t from 'counterpart-riot';
-var days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
-var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
+function getDaysArray() {
+ var days = [];
+ days.push(_t('Sun'));
+ days.push(_t('Mon'));
+ days.push(_t('Tue'));
+ days.push(_t('Wed'));
+ days.push(_t('Thu'));
+ days.push(_t('Fri'));
+ days.push(_t('Sat'));
+ return days;
+}
+
+function getMonthsArray() {
+ var months = [];
+ months.push(_t('Jan'));
+ months.push(_t('Feb'));
+ months.push(_t('Mar'));
+ months.push(_t('Apr'));
+ months.push(_t('May'));
+ months.push(_t('Jun'));
+ months.push(_t('Jul'));
+ months.push(_t('Aug'));
+ months.push(_t('Sep'));
+ months.push(_t('Oct'));
+ months.push(_t('Nov'));
+ months.push(_t('Dec'));
+ return months;
+}
function pad(n) {
return (n < 10 ? '0' : '') + n;
@@ -27,16 +54,22 @@ module.exports = {
formatDate: function(date) {
// date.toLocaleTimeString is completely system dependent.
// just go 24h for now
+ const days = getDaysArray();
+ const months = getMonthsArray();
+ // TODO: use standard date localize function provided in counterpart
+ var hoursAndMinutes = pad(date.getHours()) + ':' + pad(date.getMinutes());
var now = new Date();
if (date.toDateString() === now.toDateString()) {
- return pad(date.getHours()) + ':' + pad(date.getMinutes());
+ return hoursAndMinutes;
}
else if (now.getTime() - date.getTime() < 6 * 24 * 60 * 60 * 1000) {
- return days[date.getDay()] + " " + pad(date.getHours()) + ':' + pad(date.getMinutes());
+ // TODO: use standard date localize function provided in counterpart
+ return _t('%(weekDayName)s %(time)s', {weekDayName: days[date.getDay()], time: hoursAndMinutes});
}
else if (now.getFullYear() === date.getFullYear()) {
- return days[date.getDay()] + ", " + months[date.getMonth()] + " " + date.getDate() + " " + pad(date.getHours()) + ':' + pad(date.getMinutes());
+ // TODO: use standard date localize function provided in counterpart
+ return _t('%(weekDayName)s, %(monthName)s %(day)s %(time)s', {weekDayName: days[date.getDay()], monthName: months[date.getMonth()], day: date.getDate(), time: hoursAndMinutes});
}
else {
return this.formatFullDate(date);
@@ -44,11 +77,13 @@ module.exports = {
},
formatFullDate: function(date) {
- return days[date.getDay()] + ", " + months[date.getMonth()] + " " + date.getDate() + " " + date.getFullYear() + " " + pad(date.getHours()) + ':' + pad(date.getMinutes());
+ const days = getDaysArray();
+ const months = getMonthsArray();
+ var hoursAndMinutes = pad(date.getHours()) + ':' + pad(date.getMinutes());
+ return _t('%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s', {weekDayName: days[date.getDay()], monthName: months[date.getMonth()], day: date.getDate(), fullYear: date.getFullYear(),time: hoursAndMinutes});
},
formatTime: function(date) {
return pad(date.getHours()) + ':' + pad(date.getMinutes());
}
};
-
diff --git a/src/Lifecycle.js b/src/Lifecycle.js
index f34aeae0e5..56af920609 100644
--- a/src/Lifecycle.js
+++ b/src/Lifecycle.js
@@ -27,6 +27,7 @@ import DMRoomMap from './utils/DMRoomMap';
import RtsClient from './RtsClient';
import Modal from './Modal';
import sdk from './index';
+import _t from 'counterpart-riot';
/**
* Called at startup, to attempt to build a logged-in Matrix session. It tries
@@ -229,14 +230,16 @@ function _handleRestoreFailure(e) {
let msg = e.message;
if (msg == "OLM.BAD_LEGACY_ACCOUNT_PICKLE") {
- msg = "You need to log back in to generate end-to-end encryption keys "
- + "for this device and submit the public key to your homeserver. "
- + "This is a once off; sorry for the inconvenience.";
+ msg = _t(
+ 'You need to log back in to generate end-to-end ' +
+ 'encryption keys for this device and submit the public key to your homeserver. ' +
+ 'This is a once off; sorry for the inconvenience'
+ ) + '.';
_clearLocalStorage();
return q.reject(new Error(
- "Unable to restore previous session: " + msg,
+ _t('Unable to restore previous session') + ': ' + msg,
));
}
diff --git a/src/Notifier.js b/src/Notifier.js
index 6473ab4d9c..9d3e114b14 100644
--- a/src/Notifier.js
+++ b/src/Notifier.js
@@ -21,6 +21,7 @@ import TextForEvent from './TextForEvent';
import Avatar from './Avatar';
import dis from './dispatcher';
import sdk from './index';
+import _t from 'counterpart-riot';
import Modal from './Modal';
/*
@@ -134,14 +135,13 @@ const Notifier = {
if (result !== 'granted') {
// The permission request was dismissed or denied
const description = result === 'denied'
- ? 'Riot does not have permission to send you notifications'
- + ' - please check your browser settings'
- : 'Riot was not given permission to send notifications'
- + ' - please try again';
+ ? _t('Riot does not have permission to send you notifications - please check your browser settings')
+ : _t('Riot was not given permission to send notifications - please try again');
const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog');
Modal.createDialog(ErrorDialog, {
- title: 'Unable to enable Notifications',
+ title: _t('Unable to enable Notifications'),
description,
+ button: _t("OK"),
});
return;
}
diff --git a/src/PasswordReset.js b/src/PasswordReset.js
index a03a565459..d5992e3e0a 100644
--- a/src/PasswordReset.js
+++ b/src/PasswordReset.js
@@ -15,6 +15,7 @@ limitations under the License.
*/
var Matrix = require("matrix-js-sdk");
+import _t from 'counterpart-riot';
/**
* Allows a user to reset their password on a homeserver.
@@ -53,7 +54,7 @@ class PasswordReset {
return res;
}, function(err) {
if (err.errcode == 'M_THREEPID_NOT_FOUND') {
- err.message = "This email address was not found";
+ err.message = _t('This email address was not found');
} else if (err.httpStatus) {
err.message = err.message + ` (Status ${err.httpStatus})`;
}
@@ -78,10 +79,10 @@ class PasswordReset {
}
}, this.password).catch(function(err) {
if (err.httpStatus === 401) {
- err.message = "Failed to verify email address: make sure you clicked the link in the email";
+ err.message = _t('Failed to verify email address: make sure you clicked the link in the email');
}
else if (err.httpStatus === 404) {
- err.message = "Your email address does not appear to be associated with a Matrix ID on this Homeserver.";
+ err.message = _t('Your email address does not appear to be associated with a Matrix ID on this Homeserver') + '.';
}
else if (err.httpStatus) {
err.message += ` (Status ${err.httpStatus})`;
diff --git a/src/Roles.js b/src/Roles.js
index cef8670aad..0c7463c48a 100644
--- a/src/Roles.js
+++ b/src/Roles.js
@@ -13,14 +13,19 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
-export const LEVEL_ROLE_MAP = {
- undefined: 'Default',
- 0: 'User',
- 50: 'Moderator',
- 100: 'Admin',
-};
+import _t from 'counterpart-riot';
+
+export function levelRoleMap() {
+ const LEVEL_ROLE_MAP = {};
+ LEVEL_ROLE_MAP[undefined] = _t('Default');
+ LEVEL_ROLE_MAP[0] = _t('User');
+ LEVEL_ROLE_MAP[50] = _t('Moderator');
+ LEVEL_ROLE_MAP[100] = _t('Admin');
+ return LEVEL_ROLE_MAP;
+}
export function textualPowerLevel(level, userDefault) {
+ const LEVEL_ROLE_MAP = this.levelRoleMap();
if (LEVEL_ROLE_MAP[level]) {
return LEVEL_ROLE_MAP[level] + (level !== undefined ? ` (${level})` : ` (${userDefault})`);
} else {
diff --git a/src/ScalarMessaging.js b/src/ScalarMessaging.js
index dbb7e405df..abe39056e7 100644
--- a/src/ScalarMessaging.js
+++ b/src/ScalarMessaging.js
@@ -125,6 +125,7 @@ const SdkConfig = require('./SdkConfig');
const MatrixClientPeg = require("./MatrixClientPeg");
const MatrixEvent = require("matrix-js-sdk").MatrixEvent;
const dis = require("./dispatcher");
+import _t from 'counterpart-riot';
function sendResponse(event, res) {
const data = JSON.parse(JSON.stringify(event.data));
@@ -150,7 +151,7 @@ function inviteUser(event, roomId, userId) {
console.log(`Received request to invite ${userId} into room ${roomId}`);
const client = MatrixClientPeg.get();
if (!client) {
- sendError(event, "You need to be logged in.");
+ sendError(event, _t('You need to be logged in') + '.');
return;
}
const room = client.getRoom(roomId);
@@ -170,7 +171,7 @@ function inviteUser(event, roomId, userId) {
success: true,
});
}, function(err) {
- sendError(event, "You need to be able to invite users to do that.", err);
+ sendError(event, _t('You need to be able to invite users to do that') + '.', err);
});
}
@@ -181,7 +182,7 @@ function setPlumbingState(event, roomId, status) {
console.log(`Received request to set plumbing state to status "${status}" in room ${roomId}`);
const client = MatrixClientPeg.get();
if (!client) {
- sendError(event, "You need to be logged in.");
+ sendError(event, _t('You need to be logged in') + '.');
return;
}
client.sendStateEvent(roomId, "m.room.plumbing", { status : status }).done(() => {
@@ -189,7 +190,7 @@ function setPlumbingState(event, roomId, status) {
success: true,
});
}, (err) => {
- sendError(event, err.message ? err.message : "Failed to send request.", err);
+ sendError(event, err.message ? err.message : _t('Failed to send request') + '.', err);
});
}
@@ -197,7 +198,7 @@ function setBotOptions(event, roomId, userId) {
console.log(`Received request to set options for bot ${userId} in room ${roomId}`);
const client = MatrixClientPeg.get();
if (!client) {
- sendError(event, "You need to be logged in.");
+ sendError(event, _t('You need to be logged in') + '.');
return;
}
client.sendStateEvent(roomId, "m.room.bot.options", event.data.content, "_" + userId).done(() => {
@@ -205,20 +206,20 @@ function setBotOptions(event, roomId, userId) {
success: true,
});
}, (err) => {
- sendError(event, err.message ? err.message : "Failed to send request.", err);
+ sendError(event, err.message ? err.message : _t('Failed to send request') + '.', err);
});
}
function setBotPower(event, roomId, userId, level) {
if (!(Number.isInteger(level) && level >= 0)) {
- sendError(event, "Power level must be positive integer.");
+ sendError(event, _t('Power level must be positive integer') + '.');
return;
}
console.log(`Received request to set power level to ${level} for bot ${userId} in room ${roomId}.`);
const client = MatrixClientPeg.get();
if (!client) {
- sendError(event, "You need to be logged in.");
+ sendError(event, _t('You need to be logged in') + '.');
return;
}
@@ -235,7 +236,7 @@ function setBotPower(event, roomId, userId, level) {
success: true,
});
}, (err) => {
- sendError(event, err.message ? err.message : "Failed to send request.", err);
+ sendError(event, err.message ? err.message : _t('Failed to send request') + '.', err);
});
});
}
@@ -258,12 +259,12 @@ function botOptions(event, roomId, userId) {
function returnStateEvent(event, roomId, eventType, stateKey) {
const client = MatrixClientPeg.get();
if (!client) {
- sendError(event, "You need to be logged in.");
+ sendError(event, _t('You need to be logged in') + '.');
return;
}
const room = client.getRoom(roomId);
if (!room) {
- sendError(event, "This room is not recognised.");
+ sendError(event, _t('This room is not recognised') + '.');
return;
}
const stateEvent = room.currentState.getStateEvents(eventType, stateKey);
@@ -313,13 +314,13 @@ const onMessage = function(event) {
const roomId = event.data.room_id;
const userId = event.data.user_id;
if (!roomId) {
- sendError(event, "Missing room_id in request");
+ sendError(event, _t('Missing room_id in request'));
return;
}
let promise = Promise.resolve(currentRoomId);
if (!currentRoomId) {
if (!currentRoomAlias) {
- sendError(event, "Must be viewing a room");
+ sendError(event, _t('Must be viewing a room'));
return;
}
// no room ID but there is an alias, look it up.
@@ -331,7 +332,7 @@ const onMessage = function(event) {
promise.then((viewingRoomId) => {
if (roomId !== viewingRoomId) {
- sendError(event, "Room " + roomId + " not visible");
+ sendError(event, _t('Room %(roomId)s not visible', {roomId: roomId}));
return;
}
@@ -345,7 +346,7 @@ const onMessage = function(event) {
}
if (!userId) {
- sendError(event, "Missing user_id in request");
+ sendError(event, _t('Missing user_id in request'));
return;
}
switch (event.data.action) {
@@ -370,7 +371,7 @@ const onMessage = function(event) {
}
}, (err) => {
console.error(err);
- sendError(event, "Failed to lookup current room.");
+ sendError(event, _t('Failed to lookup current room') + '.');
});
};
diff --git a/src/SlashCommands.js b/src/SlashCommands.js
index bd68f1a6fe..e2c69ad46a 100644
--- a/src/SlashCommands.js
+++ b/src/SlashCommands.js
@@ -18,6 +18,7 @@ import MatrixClientPeg from "./MatrixClientPeg";
import dis from "./dispatcher";
import Tinter from "./Tinter";
import sdk from './index';
+import _t from 'counterpart-riot';
import Modal from './Modal';
@@ -41,7 +42,7 @@ class Command {
}
getUsage() {
- return "Usage: " + this.getCommandWithArgs();
+ return _t('Usage') + ': ' + this.getCommandWithArgs();
}
}
@@ -68,8 +69,9 @@ const commands = {
const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog');
// TODO Don't explain this away, actually show a search UI here.
Modal.createDialog(ErrorDialog, {
- title: "/ddg is not a command",
- description: "To use it, just wait for autocomplete results to load and tab through them.",
+ title: _t('/ddg is not a command'),
+ description: _t('To use it, just wait for autocomplete results to load and tab through them') + '.',
+ button: _t("OK"),
});
return success();
}),
diff --git a/src/TextForEvent.js b/src/TextForEvent.js
index 3f200a089d..25af1e5b5d 100644
--- a/src/TextForEvent.js
+++ b/src/TextForEvent.js
@@ -16,7 +16,7 @@ limitations under the License.
var MatrixClientPeg = require("./MatrixClientPeg");
var CallHandler = require("./CallHandler");
-
+import _t from 'counterpart-riot';
import * as Roles from './Roles';
function textForMemberEvent(ev) {
@@ -25,45 +25,42 @@ function textForMemberEvent(ev) {
var targetName = ev.target ? ev.target.name : ev.getStateKey();
var ConferenceHandler = CallHandler.getConferenceHandler();
var reason = ev.getContent().reason ? (
- " Reason: " + ev.getContent().reason
+ _t('Reason') + ': ' + ev.getContent().reason
) : "";
switch (ev.getContent().membership) {
case 'invite':
var threePidContent = ev.getContent().third_party_invite;
if (threePidContent) {
if (threePidContent.display_name) {
- return targetName + " accepted the invitation for " +
- threePidContent.display_name + ".";
+ return _t('%(targetName)s accepted the invitation for %(displayName)s', {targetName: targetName, displayName: threePidContent.display_name}) + ".";
} else {
- return targetName + " accepted an invitation.";
+ return _t('%(targetName)s accepted an invitation', {targetName: targetName}) + '.';
}
}
else {
if (ConferenceHandler && ConferenceHandler.isConferenceUser(ev.getStateKey())) {
- return senderName + " requested a VoIP conference";
+ return _t('%(senderName)s requested a VoIP conference', {senderName: senderName});
}
else {
- return senderName + " invited " + targetName + ".";
+ return _t('%(senderName)s invited %(targetName)s', {senderName: senderName, targetName: targetName}) + '.';
}
}
case 'ban':
- return senderName + " banned " + targetName + "." + reason;
+ return _t('%(senderName)s banned %(targetName)s', {senderName: senderName, targetName: targetName}) + '. ' + reason;
case 'join':
if (ev.getPrevContent() && ev.getPrevContent().membership == 'join') {
if (ev.getPrevContent().displayname && ev.getContent().displayname && ev.getPrevContent().displayname != ev.getContent().displayname) {
- return ev.getSender() + " changed their display name from " +
- ev.getPrevContent().displayname + " to " +
- ev.getContent().displayname;
+ return _t('%(senderName)s changed their display name from %(oldDisplayName)s to %(displayName)s', {senderName: ev.getSender(), oldDisplayName: ev.getPrevContent().displayname, displayName: ev.getContent().displayname});
} else if (!ev.getPrevContent().displayname && ev.getContent().displayname) {
- return ev.getSender() + " set their display name to " + ev.getContent().displayname;
+ return _t('%(senderName)s set their display name to %(displayName)s', {senderName: ev.getSender(), displayName: ev.getContent().displayname});
} else if (ev.getPrevContent().displayname && !ev.getContent().displayname) {
- return ev.getSender() + " removed their display name (" + ev.getPrevContent().displayname + ")";
+ return _t('%(senderName)s removed their display name (%(oldDisplayName)s)', {senderName: ev.getSender(), oldDisplayName: ev.getPrevContent().displayname});
} else if (ev.getPrevContent().avatar_url && !ev.getContent().avatar_url) {
- return senderName + " removed their profile picture";
+ return _t('%(senderName)s removed their profile picture', {senderName: senderName});
} else if (ev.getPrevContent().avatar_url && ev.getContent().avatar_url && ev.getPrevContent().avatar_url != ev.getContent().avatar_url) {
- return senderName + " changed their profile picture";
+ return _t('%(senderName)s changed their profile picture', {senderName: senderName});
} else if (!ev.getPrevContent().avatar_url && ev.getContent().avatar_url) {
- return senderName + " set a profile picture";
+ return _t('%(senderName)s set a profile picture', {senderName: senderName});
} else {
// suppress null rejoins
return '';
@@ -71,49 +68,48 @@ function textForMemberEvent(ev) {
} else {
if (!ev.target) console.warn("Join message has no target! -- " + ev.getContent().state_key);
if (ConferenceHandler && ConferenceHandler.isConferenceUser(ev.getStateKey())) {
- return "VoIP conference started";
+ return _t('VoIP conference started');
}
else {
- return targetName + " joined the room.";
+ return _t('%(targetName)s joined the room', {targetName: targetName}) + '.';
}
}
case 'leave':
if (ev.getSender() === ev.getStateKey()) {
if (ConferenceHandler && ConferenceHandler.isConferenceUser(ev.getStateKey())) {
- return "VoIP conference finished";
+ return _t('VoIP conference finished');
}
else if (ev.getPrevContent().membership === "invite") {
- return targetName + " rejected the invitation.";
+ return _t('%(targetName)s rejected the invitation', {targetName: targetName}) + '.';
}
else {
- return targetName + " left the room.";
+ return _t('%(targetName)s left the room', {targetName: targetName}) + '.';
}
}
else if (ev.getPrevContent().membership === "ban") {
- return senderName + " unbanned " + targetName + ".";
+ return _t('%(senderName)s unbanned %(targetName)s', {senderName: senderName, targetName: targetName}) + '.';
}
else if (ev.getPrevContent().membership === "join") {
- return senderName + " kicked " + targetName + "." + reason;
+ return _t('%(senderName)s kicked %(targetName)s', {senderName: senderName, targetName: targetName}) + '. ' + reason;
}
else if (ev.getPrevContent().membership === "invite") {
- return senderName + " withdrew " + targetName + "'s invitation." + reason;
+ return _t('%(senderName)s withdrew %(targetName)s\'s inivitation', {senderName: senderName, targetName: targetName}) + '. ' + reason;
}
else {
- return targetName + " left the room.";
+ return _t('%(targetName)s left the room', {targetName: targetName}) + '.';
}
}
}
function textForTopicEvent(ev) {
var senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
-
- return senderDisplayName + ' changed the topic to "' + ev.getContent().topic + '"';
+ return _t('%(senderDisplayName)s changed the topic to "%(topic)s"', {senderDisplayName: senderDisplayName, topic: ev.getContent().topic});
}
function textForRoomNameEvent(ev) {
var senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
- return senderDisplayName + ' changed the room name to "' + ev.getContent().name + '"';
+ return _t('%(senderDisplayName)s changed the room name to %(roomName)s', {senderDisplayName: senderDisplayName, roomName: ev.getContent().name});
}
function textForMessageEvent(ev) {
@@ -122,66 +118,65 @@ function textForMessageEvent(ev) {
if (ev.getContent().msgtype === "m.emote") {
message = "* " + senderDisplayName + " " + message;
} else if (ev.getContent().msgtype === "m.image") {
- message = senderDisplayName + " sent an image.";
+ message = _t('%(senderDisplayName)s sent an image', {senderDisplayName: senderDisplayName}) + '.';
}
return message;
}
function textForCallAnswerEvent(event) {
- var senderName = event.sender ? event.sender.name : "Someone";
- var supported = MatrixClientPeg.get().supportsVoip() ? "" : " (not supported by this browser)";
- return senderName + " answered the call." + supported;
+ var senderName = event.sender ? event.sender.name : _t('Someone');
+ var supported = MatrixClientPeg.get().supportsVoip() ? "" : _t('(not supported by this browser)');
+ return _t('%(senderName)s answered the call', {senderName: senderName}) + '. ' + supported;
}
function textForCallHangupEvent(event) {
- var senderName = event.sender ? event.sender.name : "Someone";
- var supported = MatrixClientPeg.get().supportsVoip() ? "" : " (not supported by this browser)";
- return senderName + " ended the call." + supported;
+ var senderName = event.sender ? event.sender.name : _t('Someone');
+ var supported = MatrixClientPeg.get().supportsVoip() ? "" : _t('(not supported by this browser)');
+ return _t('%(senderName)s ended the call', {senderName: senderName}) + '. ' + supported;
}
function textForCallInviteEvent(event) {
- var senderName = event.sender ? event.sender.name : "Someone";
+ var senderName = event.sender ? event.sender.name : _t('Someone');
// FIXME: Find a better way to determine this from the event?
var type = "voice";
if (event.getContent().offer && event.getContent().offer.sdp &&
event.getContent().offer.sdp.indexOf('m=video') !== -1) {
type = "video";
}
- var supported = MatrixClientPeg.get().supportsVoip() ? "" : " (not supported by this browser)";
- return senderName + " placed a " + type + " call." + supported;
+ var supported = MatrixClientPeg.get().supportsVoip() ? "" : _t('(not supported by this browser)');
+ return _t('%(senderName)s placed a %(callType)s call', {senderName: senderName, callType: type}) + '. ' + supported;
}
function textForThreePidInviteEvent(event) {
var senderName = event.sender ? event.sender.name : event.getSender();
- return senderName + " sent an invitation to " + event.getContent().display_name +
- " to join the room.";
+ return _t('%(senderName)s sent an invitation to %(targetDisplayName)s to join the room', {senderName: senderName, targetDisplayName: event.getContent().display_name}) + ".";
}
function textForHistoryVisibilityEvent(event) {
var senderName = event.sender ? event.sender.name : event.getSender();
var vis = event.getContent().history_visibility;
- var text = senderName + " made future room history visible to ";
+ var text = _t('%(senderName)s made future room history visible to', {senderName: senderName}) + ' ';
if (vis === "invited") {
- text += "all room members, from the point they are invited.";
+ text += _t('all room members, from the point they are invited') + '.';
}
else if (vis === "joined") {
- text += "all room members, from the point they joined.";
+ text += _t('all room members, from the point they joined') + '.';
}
else if (vis === "shared") {
- text += "all room members.";
+ text += _t('all room members') + '.';
}
else if (vis === "world_readable") {
- text += "anyone.";
+ text += _t('anyone') + '.';
}
else {
- text += " unknown (" + vis + ")";
+ text += ' ' + _t('unknown') + ' (' + vis + ')';
}
return text;
}
function textForEncryptionEvent(event) {
var senderName = event.sender ? event.sender.name : event.getSender();
- return senderName + " turned on end-to-end encryption (algorithm " + event.getContent().algorithm + ")";
+ return _t('%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s)', {senderName: senderName, algorithm: algorithm});
}
// Currently will only display a change if a user's power level is changed
@@ -211,16 +206,14 @@ function textForPowerEvent(event) {
const to = event.getContent().users[userId];
if (to !== from) {
diff.push(
- userId +
- ' from ' + Roles.textualPowerLevel(from, userDefault) +
- ' to ' + Roles.textualPowerLevel(to, userDefault)
+ _t('%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s', {userId: userId, fromPowerLevel: Roles.textualPowerLevel(from, userDefault), toPowerLevel: Roles.textualPowerLevel(to, userDefault)})
);
}
});
if (!diff.length) {
return '';
}
- return senderName + ' changed the power level of ' + diff.join(', ');
+ return _t('%(senderName)s changed the power level of %(powerLevelDiffText)s', {senderName: senderName, powerLevelDiffText: diff.join(", ")});
}
var handlers = {
diff --git a/src/UserSettingsStore.js b/src/UserSettingsStore.js
index 9de291249f..fd5ccb0de3 100644
--- a/src/UserSettingsStore.js
+++ b/src/UserSettingsStore.js
@@ -23,10 +23,14 @@ import Notifier from './Notifier';
* TODO: Make things use this. This is all WIP - see UserSettings.js for usage.
*/
+/*
+ * TODO: Find a way to translate the names of LABS_FEATURES. In other words, guarantee that languages were already loaded before building this array.
+ */
+
module.exports = {
LABS_FEATURES: [
{
- name: 'New Composer & Autocomplete',
+ name: "New Composer & Autocomplete",
id: 'rich_text_editor',
default: false,
},
diff --git a/src/WhoIsTyping.js b/src/WhoIsTyping.js
index 4502b0ccd9..b2eef42f5a 100644
--- a/src/WhoIsTyping.js
+++ b/src/WhoIsTyping.js
@@ -15,6 +15,7 @@ limitations under the License.
*/
var MatrixClientPeg = require("./MatrixClientPeg");
+import _t from 'counterpart-riot';
module.exports = {
usersTypingApartFromMe: function(room) {
@@ -56,18 +57,18 @@ module.exports = {
if (whoIsTyping.length == 0) {
return '';
} else if (whoIsTyping.length == 1) {
- return whoIsTyping[0].name + ' is typing';
+ return _t('%(displayName)s is typing', {displayName: whoIsTyping[0].name});
}
const names = whoIsTyping.map(function(m) {
return m.name;
});
- if (othersCount) {
- const other = ' other' + (othersCount > 1 ? 's' : '');
- return names.slice(0, limit - 1).join(', ') + ' and ' +
- othersCount + other + ' are typing';
+ if (othersCount==1) {
+ return _t('%(names)s and one other are typing', {names: names.slice(0, limit - 1).join(', ')});
+ } else if (othersCount>1) {
+ return _t('%(names)s and %(count)s others are typing', {names: names.slice(0, limit - 1).join(', '), count: othersCount});
} else {
const lastPerson = names.pop();
- return names.join(', ') + ' and ' + lastPerson + ' are typing';
+ return _t('%(names)s and %(lastPerson)s are typing', {names: names.join(', '), lastPerson: lastPerson});
}
}
};
diff --git a/src/async-components/views/dialogs/EncryptedEventDialog.js b/src/async-components/views/dialogs/EncryptedEventDialog.js
index ba706e0aa5..279f6bbda1 100644
--- a/src/async-components/views/dialogs/EncryptedEventDialog.js
+++ b/src/async-components/views/dialogs/EncryptedEventDialog.js
@@ -15,6 +15,7 @@ limitations under the License.
*/
var React = require("react");
+import _t from 'counterpart-riot';
var sdk = require('../../../index');
var MatrixClientPeg = require("../../../MatrixClientPeg");
@@ -78,33 +79,33 @@ module.exports = React.createClass({
_renderDeviceInfo: function() {
var device = this.state.device;
if (!device) {
- return (unknown device );
+ return ({ _t('unknown device') } );
}
- var verificationStatus = (NOT verified );
+ var verificationStatus = ({ _t('NOT verified') } );
if (device.isBlocked()) {
- verificationStatus = (Blacklisted );
+ verificationStatus = ({ _t('Blacklisted') } );
} else if (device.isVerified()) {
- verificationStatus = "verified";
+ verificationStatus = _t('verified');
}
return (
- Name
+ { _t('Name') }
{ device.getDisplayName() }
- Device ID
+ { _t('Device ID') }
{ device.deviceId }
- Verification
+ { _t('Verification') }
{ verificationStatus }
- Ed25519 fingerprint
+ { _t('Ed25519 fingerprint') }
{device.getFingerprint()}
@@ -119,32 +120,32 @@ module.exports = React.createClass({
- User ID
+ { _t('User ID') }
{ event.getSender() }
- Curve25519 identity key
- { event.getSenderKey() || none }
+ { _t('Curve25519 identity key') }
+ { event.getSenderKey() || { _t('none') } }
- Claimed Ed25519 fingerprint key
- { event.getKeysClaimed().ed25519 || none }
+ { _t('Claimed Ed25519 fingerprint key') }
+ { event.getKeysClaimed().ed25519 || { _t('none') } }
- Algorithm
- { event.getWireContent().algorithm || unencrypted }
+ { _t('Algorithm') }
+ { event.getWireContent().algorithm || { _t('unencrypted') } }
{
event.getContent().msgtype === 'm.bad.encrypted' ? (
- Decryption error
+ { _t('Decryption error') }
{ event.getContent().body }
) : null
}
- Session ID
- { event.getWireContent().session_id || none }
+ { _t('Session ID') }
+ { event.getWireContent().session_id || { _t('none') } }
@@ -166,18 +167,18 @@ module.exports = React.createClass({
return (
- End-to-end encryption information
+ { _t('End-to-end encryption information') }
-
Event information
+ { _t('Event information') }
{this._renderEventInfo()}
- Sender device information
+ { _t('Sender device information') }
{this._renderDeviceInfo()}
- OK
+ { _t('OK') }
{buttons}
diff --git a/src/autocomplete/CommandProvider.js b/src/autocomplete/CommandProvider.js
index 60171bc72f..d72c8d1c07 100644
--- a/src/autocomplete/CommandProvider.js
+++ b/src/autocomplete/CommandProvider.js
@@ -1,8 +1,10 @@
import React from 'react';
+import _t from 'counterpart-riot';
import AutocompleteProvider from './AutocompleteProvider';
import Fuse from 'fuse.js';
import {TextualCompletion} from './Components';
+// Warning: Since the description string will be translated in _t(result.description), all these strings below must be in i18n/strings/en_EN.json file
const COMMANDS = [
{
command: '/me',
@@ -68,7 +70,7 @@ export default class CommandProvider extends AutocompleteProvider {
component: (
),
range,
};
@@ -78,7 +80,7 @@ export default class CommandProvider extends AutocompleteProvider {
}
getName() {
- return '*️⃣ Commands';
+ return '*️⃣ ' + _t('Commands');
}
static getInstance(): CommandProvider {
diff --git a/src/autocomplete/EmojiProvider.js b/src/autocomplete/EmojiProvider.js
index d488ac53ae..d0675e9ffc 100644
--- a/src/autocomplete/EmojiProvider.js
+++ b/src/autocomplete/EmojiProvider.js
@@ -1,4 +1,5 @@
import React from 'react';
+import _t from 'counterpart-riot';
import AutocompleteProvider from './AutocompleteProvider';
import {emojioneList, shortnameToImage, shortnameToUnicode} from 'emojione';
import Fuse from 'fuse.js';
@@ -39,7 +40,7 @@ export default class EmojiProvider extends AutocompleteProvider {
}
getName() {
- return '😃 Emoji';
+ return '😃 ' + _t('Emoji');
}
static getInstance() {
diff --git a/src/autocomplete/RoomProvider.js b/src/autocomplete/RoomProvider.js
index 8d1e555e56..175aaf2691 100644
--- a/src/autocomplete/RoomProvider.js
+++ b/src/autocomplete/RoomProvider.js
@@ -1,4 +1,5 @@
import React from 'react';
+import _t from 'counterpart-riot';
import AutocompleteProvider from './AutocompleteProvider';
import MatrixClientPeg from '../MatrixClientPeg';
import Fuse from 'fuse.js';
@@ -50,7 +51,7 @@ export default class RoomProvider extends AutocompleteProvider {
}
getName() {
- return '💬 Rooms';
+ return '💬 ' + _t('Rooms');
}
static getInstance() {
diff --git a/src/autocomplete/UserProvider.js b/src/autocomplete/UserProvider.js
index 4d40fbdf94..c2fccbc418 100644
--- a/src/autocomplete/UserProvider.js
+++ b/src/autocomplete/UserProvider.js
@@ -1,4 +1,5 @@
import React from 'react';
+import _t from 'counterpart-riot';
import AutocompleteProvider from './AutocompleteProvider';
import Q from 'q';
import Fuse from 'fuse.js';
@@ -51,7 +52,7 @@ export default class UserProvider extends AutocompleteProvider {
}
getName() {
- return '👥 Users';
+ return '👥 ' + _t('Users');
}
setUserList(users) {
diff --git a/src/components/structures/CreateRoom.js b/src/components/structures/CreateRoom.js
index 24ebfea07f..48ce4e274e 100644
--- a/src/components/structures/CreateRoom.js
+++ b/src/components/structures/CreateRoom.js
@@ -16,15 +16,16 @@ limitations under the License.
'use strict';
-var React = require("react");
-var MatrixClientPeg = require("../../MatrixClientPeg");
-var PresetValues = {
+import React from 'react';
+import q from 'q';
+import _t from 'counterpart-riot';
+import sdk from '../../index';
+import MatrixClientPeg from '../../MatrixClientPeg';
+const PresetValues = {
PrivateChat: "private_chat",
PublicChat: "public_chat",
Custom: "custom",
};
-var q = require('q');
-var sdk = require('../../index');
module.exports = React.createClass({
displayName: 'CreateRoom',
@@ -231,7 +232,7 @@ module.exports = React.createClass({
if (curr_phase == this.phases.ERROR) {
error_box = (
- An error occured: {this.state.error_string}
+ {_t('An error occured: %(error_string)s', {error_string: this.state.error_string})}
);
}
@@ -248,27 +249,27 @@ module.exports = React.createClass({
@@ -283,13 +284,12 @@ module.exports = React.createClass({
- Resend all
- or
{_t('or')}
- cancel all
- now. You can also select individual messages to
- resend or cancel.
+ {_t('cancel all')}
+ {_t('now. You can also select individual messages to resend or cancel.')}
);
@@ -324,7 +324,7 @@ module.exports = React.createClass({
if (this.props.hasActiveCall) {
return (
- Active call
+ {_t('Active call')}
);
}
diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js
index af0c595ea9..2b7e7acc3d 100644
--- a/src/components/structures/RoomView.js
+++ b/src/components/structures/RoomView.js
@@ -25,6 +25,7 @@ var ReactDOM = require("react-dom");
var q = require("q");
var classNames = require("classnames");
var Matrix = require("matrix-js-sdk");
+import _t from 'counterpart-riot';
var UserSettingsStore = require('../../UserSettingsStore');
var MatrixClientPeg = require("../../MatrixClientPeg");
@@ -296,7 +297,7 @@ module.exports = React.createClass({
componentWillReceiveProps: function(newProps) {
if (newProps.roomAddress != this.props.roomAddress) {
- throw new Error("changing room on a RoomView is not supported");
+ throw new Error(_t("changing room on a RoomView is not supported"));
}
if (newProps.eventId != this.props.eventId) {
@@ -370,10 +371,10 @@ module.exports = React.createClass({
onPageUnload(event) {
if (ContentMessages.getCurrentUploads().length > 0) {
return event.returnValue =
- 'You seem to be uploading files, are you sure you want to quit?';
+ _t("You seem to be uploading files, are you sure you want to quit?");
} else if (this._getCallForRoom() && this.state.callState !== 'ended') {
return event.returnValue =
- 'You seem to be in a call, are you sure you want to quit?';
+ _t("You seem to be in a call, are you sure you want to quit?");
}
},
@@ -530,16 +531,17 @@ module.exports = React.createClass({
if (!userHasUsedEncryption) {
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createDialog(QuestionDialog, {
- title: "Warning!",
+ title: _t("Warning") + "!",
hasCancelButton: false,
description: (
-
End-to-end encryption is in beta and may not be reliable.
-
You should not yet trust it to secure data.
-
Devices will not yet be able to decrypt history from before they joined the room.
-
Encrypted messages will not be visible on clients that do not yet implement encryption.
+
{ _t("End-to-end encryption is in beta and may not be reliable") }.
+
{ _t("You should not yet trust it to secure data") }.
+
{ _t("Devices will not yet be able to decrypt history from before they joined the room") }.
+
{ _t("Encrypted messages will not be visible on clients that do not yet implement encryption") }.
),
+ button: "OK",
});
}
if (localStorage) {
@@ -708,10 +710,10 @@ module.exports = React.createClass({
if (!unsentMessages.length) return "";
for (const event of unsentMessages) {
if (!event.error || event.error.name !== "UnknownDeviceError") {
- return "Some of your messages have not been sent.";
+ return _t("Some of your messages have not been sent") + ".";
}
}
- return "Message not sent due to unknown devices being present";
+ return _t("Message not sent due to unknown devices being present");
},
_getUnsentMessages: function(room) {
@@ -871,15 +873,16 @@ module.exports = React.createClass({
) {
var NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog");
Modal.createDialog(NeedToRegisterDialog, {
- title: "Failed to join the room",
- description: "This room is private or inaccessible to guests. You may be able to join if you register."
+ title: _t("Failed to join the room"),
+ description: _t("This room is private or inaccessible to guests. You may be able to join if you register") + "."
});
} else {
var msg = error.message ? error.message : JSON.stringify(error);
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
- title: "Failed to join room",
- description: msg
+ title: _t("Failed to join room"),
+ description: msg,
+ button: _t("OK"),
});
}
}).done();
@@ -939,8 +942,8 @@ module.exports = React.createClass({
if (MatrixClientPeg.get().isGuest()) {
var NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog");
Modal.createDialog(NeedToRegisterDialog, {
- title: "Please Register",
- description: "Guest users can't upload files. Please register to upload."
+ title: _t("Please Register"),
+ description: _t("Guest users can't upload files. Please register to upload") + "."
});
return;
}
@@ -959,8 +962,9 @@ module.exports = React.createClass({
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Failed to upload file " + file + " " + error);
Modal.createDialog(ErrorDialog, {
- title: "Failed to upload file",
- description: ((error && error.message) ? error.message : "Server may be unavailable, overloaded, or the file too big"),
+ title: _t('Failed to upload file'),
+ description: ((error && error.message) ? error.message : _t("Server may be unavailable, overloaded, or the file too big")),
+ button: _t("OK"),
});
});
},
@@ -1046,8 +1050,9 @@ module.exports = React.createClass({
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Search failed: " + error);
Modal.createDialog(ErrorDialog, {
- title: "Search failed",
- description: ((error && error.message) ? error.message : "Server may be unavailable, overloaded, or search timed out :("),
+ title: _t("Search failed"),
+ description: ((error && error.message) ? error.message : _t("Server may be unavailable, overloaded, or search timed out :(")),
+ button: _t("OK"),
});
}).finally(function() {
self.setState({
@@ -1082,12 +1087,12 @@ module.exports = React.createClass({
if (!this.state.searchResults.next_batch) {
if (this.state.searchResults.results.length == 0) {
ret.push(
- No results
+ { _t("No results") }
);
} else {
ret.push(
- No more results
+ { _t("No more results") }
);
}
@@ -1124,10 +1129,10 @@ module.exports = React.createClass({
// it. We should tell the js sdk to go and find out about
// it. But that's not an issue currently, as synapse only
// returns results for rooms we're joined to.
- var roomName = room ? room.name : "Unknown room "+roomId;
+ var roomName = room ? room.name : _t("Unknown room %(roomId)s", { roomId: roomId });
ret.push(
- Room: { roomName }
+ { _t("Room") }: { roomName }
);
lastRoomId = roomId;
}
@@ -1173,8 +1178,9 @@ module.exports = React.createClass({
});
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
- title: "Failed to save settings",
+ title: _t("Failed to save settings"),
description: fails.map(function(result) { return result.reason; }).join("\n"),
+ button: _t("OK"),
});
// still editing room settings
}
@@ -1209,11 +1215,12 @@ module.exports = React.createClass({
MatrixClientPeg.get().forget(this.state.room.roomId).done(function() {
dis.dispatch({ action: 'view_next_room' });
}, function(err) {
- var errCode = err.errcode || "unknown error code";
+ var errCode = err.errcode || _t("unknown error code");
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
- title: "Error",
- description: `Failed to forget room (${errCode})`
+ title: _t("Error"),
+ description: _t("Failed to forget room %(errCode)s", { errCode: errCode }),
+ button: _t("OK"),
});
});
},
@@ -1234,8 +1241,9 @@ module.exports = React.createClass({
var msg = error.message ? error.message : JSON.stringify(error);
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
- title: "Failed to reject invite",
- description: msg
+ title: _t("Failed to reject invite"),
+ description: msg,
+ button: _t("OK"),
});
self.setState({
@@ -1681,7 +1689,7 @@ module.exports = React.createClass({
if (call.type === "video") {
zoomButton = (
-
+
);
@@ -1689,14 +1697,14 @@ module.exports = React.createClass({
videoMuteButton =
;
}
voiceMuteButton =
;
diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js
index 8794713501..8b180077e0 100644
--- a/src/components/structures/TimelinePanel.js
+++ b/src/components/structures/TimelinePanel.js
@@ -23,6 +23,7 @@ var Matrix = require("matrix-js-sdk");
var EventTimeline = Matrix.EventTimeline;
var sdk = require('../../index');
+import _t from 'counterpart-riot';
var MatrixClientPeg = require("../../MatrixClientPeg");
var dis = require("../../dispatcher");
var ObjectUtils = require('../../ObjectUtils');
@@ -907,15 +908,13 @@ var TimelinePanel = React.createClass({
});
};
}
- var message = "Tried to load a specific point in this room's timeline, but ";
- if (error.errcode == 'M_FORBIDDEN') {
- message += "you do not have permission to view the message in question.";
- } else {
- message += "was unable to find it.";
- }
+ var message = (error.errcode == 'M_FORBIDDEN')
+ ? _t("Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question") + "."
+ : _t("Tried to load a specific point in this room's timeline, but was unable to find it") + ".";
Modal.createDialog(ErrorDialog, {
- title: "Failed to load timeline position",
+ title: _t("Failed to load timeline position"),
description: message,
+ button: _t("OK"),
onFinished: onFinished,
});
};
diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js
index 2d76047d33..ff3d2e774c 100644
--- a/src/components/structures/UserSettings.js
+++ b/src/components/structures/UserSettings.js
@@ -29,6 +29,8 @@ const Email = require('../../email');
const AddThreepid = require('../../AddThreepid');
const SdkConfig = require('../../SdkConfig');
import AccessibleButton from '../views/elements/AccessibleButton';
+import _t from 'counterpart-riot';
+const languageHandler = require('../../languageHandler');
import * as FormattingUtils from '../../utils/FormattingUtils';
// if this looks like a release, use the 'version' from package.json; else use
@@ -53,6 +55,8 @@ const gHVersionLabel = function(repo, token='') {
// Enumerate some simple 'flip a bit' UI settings (if any).
// 'id' gives the key name in the im.vector.web.settings account data event
// 'label' is how we describe it in the UI.
+// Warning: Each "label" string below must be added to i18n/strings/en_EN.json,
+// since they will be translated when rendered.
const SETTINGS_LABELS = [
{
id: 'autoplayGifsAndVideos',
@@ -86,6 +90,8 @@ const SETTINGS_LABELS = [
*/
];
+// Warning: Each "label" string below must be added to i18n/strings/en_EN.json,
+// since they will be translated when rendered.
const CRYPTO_SETTINGS_LABELS = [
{
id: 'blacklistUnverifiedDevices',
@@ -119,7 +125,6 @@ const THEMES = [
},
];
-
module.exports = React.createClass({
displayName: 'UserSettings',
@@ -197,6 +202,16 @@ module.exports = React.createClass({
this._syncedSettings = syncedSettings;
this._localSettings = UserSettingsStore.getLocalSettings();
+ if (!this._localSettings.hasOwnProperty('language')) {
+ const language = languageHandler.normalizeLanguageKey(languageHandler.getLanguageFromBrowser());
+ this.setState({
+ Language: language
+ });
+ } else {
+ this.setState({
+ Language: this._localSettings.language
+ });
+ }
},
componentDidMount: function() {
@@ -232,8 +247,9 @@ module.exports = React.createClass({
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Failed to load user settings: " + error);
Modal.createDialog(ErrorDialog, {
- title: "Can't load user settings",
- description: ((error && error.message) ? error.message : "Server may be unavailable or overloaded"),
+ title: _t("Can't load user settings"),
+ description: ((error && error.message) ? error.message : _t("Server may be unavailable or overloaded")),
+ button: _t("OK"),
});
});
},
@@ -248,8 +264,8 @@ module.exports = React.createClass({
if (MatrixClientPeg.get().isGuest()) {
const NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog");
Modal.createDialog(NeedToRegisterDialog, {
- title: "Please Register",
- description: "Guests can't set avatars. Please register.",
+ title: _t("Please Register"),
+ description: _t("Guests can't set avatars. Please register") + ".",
});
return;
}
@@ -274,8 +290,9 @@ module.exports = React.createClass({
console.error("Failed to set avatar: " + err);
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
- title: "Failed to set avatar",
- description: ((err && err.message) ? err.message : "Operation failed"),
+ title: _t("Failed to set avatar"),
+ description: ((err && err.message) ? err.message : _t("Operation failed")),
+ button: _t("OK")
});
});
},
@@ -283,19 +300,16 @@ module.exports = React.createClass({
onLogoutClicked: function(ev) {
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createDialog(QuestionDialog, {
- title: "Sign out?",
+ title: _t("Sign out") + "?",
description:
- For security, logging out will delete any end-to-end encryption keys from this browser.
-
- If you want to be able to decrypt your conversation history from future Riot sessions,
- please export your room keys for safe-keeping.
+ { _t("For security, logging out will delete any end-to-end encryption keys from this browser. If you want to be able to decrypt your conversation history from future Riot sessions, please export your room keys for safe-keeping") }.
,
- button: "Sign out",
+ button: _t("Sign out"),
extraButtons: [
- Export E2E room keys
+ { _t("Export E2E room keys") }
,
],
onFinished: (confirmed) => {
@@ -312,25 +326,25 @@ module.exports = React.createClass({
onPasswordChangeError: function(err) {
let errMsg = err.error || "";
if (err.httpStatus === 403) {
- errMsg = "Failed to change password. Is your password correct?";
+ errMsg = _t("Failed to change password. Is your password correct?");
} else if (err.httpStatus) {
errMsg += ` (HTTP status ${err.httpStatus})`;
}
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Failed to change password: " + errMsg);
Modal.createDialog(ErrorDialog, {
- title: "Error",
+ title: _t("Error"),
description: errMsg,
+ button: _t("OK"),
});
},
onPasswordChanged: function() {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
- title: "Success",
- description: `Your password was successfully changed. You will not
- receive push notifications on other devices until you
- log back in to them.`,
+ title: _t("Success"),
+ description: _t("Your password was successfully changed. You will not receive push notifications on other devices until you log back in to them") + ".",
+ button: _t("OK"),
});
},
@@ -356,8 +370,9 @@ module.exports = React.createClass({
const emailAddress = this.refs.add_email_input.value;
if (!Email.looksValid(emailAddress)) {
Modal.createDialog(ErrorDialog, {
- title: "Invalid Email Address",
- description: "This doesn't appear to be a valid email address",
+ title: _t("Invalid Email Address"),
+ description: _t("This doesn't appear to be a valid email address"),
+ button: _t("OK"),
});
return;
}
@@ -366,17 +381,18 @@ module.exports = React.createClass({
// same here.
this._addThreepid.addEmailAddress(emailAddress, true).done(() => {
Modal.createDialog(QuestionDialog, {
- title: "Verification Pending",
- description: "Please check your email and click on the link it contains. Once this is done, click continue.",
- button: 'Continue',
+ title: _t("Verification Pending"),
+ description: _t("Please check your email and click on the link it contains. Once this is done, click continue."),
+ button: _t('Continue'),
onFinished: this.onEmailDialogFinished,
});
}, (err) => {
this.setState({email_add_pending: false});
console.error("Unable to add email address " + emailAddress + " " + err);
Modal.createDialog(ErrorDialog, {
- title: "Unable to add email address",
- description: ((err && err.message) ? err.message : "Operation failed"),
+ title: _t("Unable to add email address"),
+ description: ((err && err.message) ? err.message : _t("Operation failed")),
+ button: _t("OK"),
});
});
ReactDOM.findDOMNode(this.refs.add_email_input).blur();
@@ -386,9 +402,9 @@ module.exports = React.createClass({
onRemoveThreepidClicked: function(threepid) {
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createDialog(QuestionDialog, {
- title: "Remove Contact Information?",
- description: "Remove " + threepid.address + "?",
- button: 'Remove',
+ title: _t("Remove Contact Information?"),
+ description: _t("Remove ") + threepid.address + "?",
+ button: _t('Remove'),
onFinished: (submit) => {
if (submit) {
this.setState({
@@ -400,8 +416,9 @@ module.exports = React.createClass({
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Unable to remove contact information: " + err);
Modal.createDialog(ErrorDialog, {
- title: "Unable to remove contact information",
- description: ((err && err.message) ? err.message : "Operation failed"),
+ title: _t("Unable to remove contact information"),
+ description: ((err && err.message) ? err.message : _t("Operation failed")),
+ button: _t("OK"),
});
}).done();
}
@@ -427,22 +444,23 @@ module.exports = React.createClass({
this.setState({email_add_pending: false});
}, (err) => {
this.setState({email_add_pending: false});
- if (err.errcode === 'M_THREEPID_AUTH_FAILED') {
+ if (err.errcode == 'M_THREEPID_AUTH_FAILED') {
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
- let message = "Unable to verify email address. ";
- message += "Please check your email and click on the link it contains. Once this is done, click continue.";
+ let message = _t("Unable to verify email address. ");
+ message += _t("Please check your email and click on the link it contains. Once this is done, click continue.");
Modal.createDialog(QuestionDialog, {
- title: "Verification Pending",
+ title: _t("Verification Pending"),
description: message,
- button: 'Continue',
+ button: _t('Continue'),
onFinished: this.onEmailDialogFinished,
});
} else {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Unable to verify email address: " + err);
Modal.createDialog(ErrorDialog, {
- title: "Unable to verify email address",
- description: ((err && err.message) ? err.message : "Operation failed"),
+ title: _t("Unable to verify email address"),
+ description: ((err && err.message) ? err.message : _t("Operation failed")),
+ button: _t("OK"),
});
}
});
@@ -532,20 +550,37 @@ module.exports = React.createClass({
Referral
- Refer a friend to Riot:
{href}
+ {_t("Refer a friend to Riot: ")}
{href}
);
},
+ onLanguageChange: function(l) {
+ UserSettingsStore.setLocalSetting('language', l);
+ this.setState({
+ Language: l,
+ });
+ PlatformPeg.get().reload();
+ },
+
+ _renderLanguageSetting: function () {
+ const LanguageDropdown = sdk.getComponent('views.elements.LanguageDropdown');
+ return
;
+ },
+
_renderUserInterfaceSettings: function() {
return (
-
User Interface
+
{ _t("User Interface") }
{ this._renderUrlPreviewSelector() }
{ SETTINGS_LABELS.map( this._renderSyncedSetting ) }
{ THEMES.map( this._renderThemeSelector ) }
+ { this._renderLanguageSetting() }
+
);
@@ -559,7 +594,7 @@ module.exports = React.createClass({
onChange={ (e) => UserSettingsStore.setUrlPreviewsDisabled(e.target.checked) }
/>
- Disable inline URL previews by default
+ { _t("Disable inline URL previews by default") }
;
},
@@ -572,7 +607,7 @@ module.exports = React.createClass({
onChange={ (e) => UserSettingsStore.setSyncedSetting(setting.id, e.target.checked) }
/>
- { setting.label }
+ { _t(setting.label) }
;
},
@@ -606,7 +641,7 @@ module.exports = React.createClass({
const deviceId = client.deviceId;
let identityKey = client.getDeviceEd25519Key();
if (!identityKey) {
- identityKey = "";
+ identityKey = _t("");
} else {
identityKey = FormattingUtils.formatCryptoKey(identityKey);
}
@@ -618,18 +653,18 @@ module.exports = React.createClass({
- Export E2E room keys
+ { _t("Export E2E room keys") }
- Import E2E room keys
+ { _t("Import E2E room keys") }
);
}
return (
-
Cryptography
+
{ _t("Cryptography") }
Device ID: {deviceId}
@@ -660,7 +695,7 @@ module.exports = React.createClass({
}
/>
- { setting.label }
+ { _t(setting.label) }
;
},
@@ -681,9 +716,9 @@ module.exports = React.createClass({
}
return (
-
Bug Report
+
{ _t("Bug Report") }
-
Found a bug?
+
{ _t("Found a bug?") }
Report it
@@ -708,8 +743,8 @@ module.exports = React.createClass({
e.target.checked = false;
const NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog");
Modal.createDialog(NeedToRegisterDialog, {
- title: "Please Register",
- description: "Guests can't use labs features. Please register.",
+ title: _t("Please Register"),
+ description: _t("Guests can't use labs features. Please register") + ".",
});
return;
}
@@ -722,9 +757,9 @@ module.exports = React.createClass({
));
return (
-
Labs
+
{ _t("Labs") }
-
These are experimental features that may break in unexpected ways. Use with caution.
+
{ _t("These are experimental features that may break in unexpected ways") }. { _t("Use with caution") }.
{features}
@@ -736,10 +771,10 @@ module.exports = React.createClass({
if (MatrixClientPeg.get().isGuest()) return null;
return
-
Deactivate Account
+
{ _t("Deactivate Account") }
Deactivate my account
+ onClick={this._onDeactivateAccountClicked}> { _t("Deactivate my account") }
;
@@ -747,11 +782,11 @@ module.exports = React.createClass({
_renderClearCache: function() {
return
-
Clear Cache
+
{ _t("Clear Cache") }
- Clear Cache and Reload
+ { _t("Clear Cache and Reload") }
;
@@ -780,7 +815,7 @@ module.exports = React.createClass({
}
return
-
Bulk Options
+
{ _t("Bulk Options") }
{reject}
@@ -800,7 +835,7 @@ module.exports = React.createClass({
},
nameForMedium: function(medium) {
- if (medium === 'msisdn') return 'Phone';
+ if (medium === 'msisdn') return _t('Phone');
return medium[0].toUpperCase() + medium.slice(1);
},
@@ -849,7 +884,7 @@ module.exports = React.createClass({
/>
-
+
);
@@ -868,7 +903,7 @@ module.exports = React.createClass({
ref="add_email_input"
className="mx_UserSettings_editable"
placeholderClassName="mx_UserSettings_threepidPlaceholder"
- placeholder={ "Add email address" }
+ placeholder={ _t("Add email address") }
blurToCancel={ false }
onValueChanged={ this._onAddEmailEditFinished } />
@@ -890,7 +925,7 @@ module.exports = React.createClass({
if (MatrixClientPeg.get().isGuest()) {
accountJsx = (
- Create an account
+ { _t("Create an account") }
);
} else {
@@ -908,7 +943,7 @@ module.exports = React.createClass({
let notificationArea;
if (!MatrixClientPeg.get().isGuest() && this.state.threepids !== undefined) {
notificationArea = (
-
Notifications
+
{ _t("Notifications") }
@@ -927,7 +962,7 @@ module.exports = React.createClass({
return (
@@ -935,13 +970,13 @@ module.exports = React.createClass({
- Profile
+ { _t("Profile") }
- Display name
+ { _t('Display name') }
@@ -958,7 +993,7 @@ module.exports = React.createClass({
-
Account
+
{ _t("Account") }
-
+
- Sign out
+ { _t("Sign out") }
{accountJsx}
@@ -988,23 +1023,20 @@ module.exports = React.createClass({
{this._renderBulkOptions()}
{this._renderBugReport()}
-
Advanced
+
{ _t("Advanced") }
- Logged in as {this._me}
+ { _t("Logged in as") } {this._me}
- Access Token: <click to reveal>
+ Access Token: <{ _t("click to reveal") }>
- Homeserver is { MatrixClientPeg.get().getHomeserverUrl() }
+ { _t("Homeserver is") } { MatrixClientPeg.get().getHomeserverUrl() }
- Identity Server is { MatrixClientPeg.get().getIdentityServerUrl() }
+ { _t("Identity Server is") } { MatrixClientPeg.get().getIdentityServerUrl() }
matrix-react-sdk version: {(REACT_SDK_VERSION !== '')
@@ -1015,7 +1047,7 @@ module.exports = React.createClass({
? gHVersionLabel('vector-im/riot-web', this.state.vectorVersion)
: 'unknown'
}
- olm version: {olmVersionString}
+ { _t("olm version: ") } {olmVersionString}
diff --git a/src/components/structures/login/ForgotPassword.js b/src/components/structures/login/ForgotPassword.js
index d75c7b7584..6a250eb531 100644
--- a/src/components/structures/login/ForgotPassword.js
+++ b/src/components/structures/login/ForgotPassword.js
@@ -17,6 +17,7 @@ limitations under the License.
'use strict';
var React = require('react');
+import _t from 'counterpart-riot';
var sdk = require('../../../index');
var Modal = require("../../../Modal");
var MatrixClientPeg = require('../../../MatrixClientPeg');
@@ -54,7 +55,7 @@ module.exports = React.createClass({
progress: "sent_email"
});
}, (err) => {
- this.showErrorDialog("Failed to send email: " + err.message);
+ this.showErrorDialog(_t('Failed to send email') + ": " + err.message);
this.setState({
progress: null
});
@@ -78,30 +79,35 @@ module.exports = React.createClass({
ev.preventDefault();
if (!this.state.email) {
- this.showErrorDialog("The email address linked to your account must be entered.");
+ this.showErrorDialog(_t('The email address linked to your account must be entered.'));
}
else if (!this.state.password || !this.state.password2) {
- this.showErrorDialog("A new password must be entered.");
+ this.showErrorDialog(_t('A new password must be entered') + ".");
}
else if (this.state.password !== this.state.password2) {
- this.showErrorDialog("New passwords must match each other.");
+ this.showErrorDialog(_t('New passwords must match each other.'));
}
else {
var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createDialog(QuestionDialog, {
- title: "Warning",
+ title: _t('Warning'),
description:
- Resetting password will currently reset any end-to-end encryption keys on all devices,
- making encrypted chat history unreadable, unless you first export your room keys
- and re-import them afterwards.
- In future this
will be improved .
+ { _t(
+ 'Resetting password will currently reset any ' +
+ 'end-to-end encryption keys on all devices, ' +
+ 'making encrypted chat history unreadable, ' +
+ 'unless you first export your room keys and re-import ' +
+ 'them afterwards. In future this ' +
+ '
' +
+ 'will be improved '
+ ) }.
,
- button: "Continue",
+ button: _t('Continue'),
extraButtons: [
- Export E2E room keys
+ { _t('Export E2E room keys') }
],
onFinished: (confirmed) => {
@@ -150,7 +156,8 @@ module.exports = React.createClass({
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
title: title,
- description: body
+ description: body,
+ button: _t("OK"),
});
},
@@ -168,22 +175,20 @@ module.exports = React.createClass({
else if (this.state.progress === "sent_email") {
resetPasswordJsx = (
- An email has been sent to {this.state.email}. Once you've followed
- the link it contains, click below.
+ { _t('An email has been sent to') } {this.state.email}. { _t('Once you've followed the link it contains, click below') }.
+ value={ _t('I have verified my email address') } />
);
}
else if (this.state.progress === "complete") {
resetPasswordJsx = (
-
Your password has been reset.
-
You have been logged out of all devices and will no longer receive push notifications.
- To re-enable notifications, sign in again on each device.
+
{ _t('Your password has been reset') }.
+
{ _t('You have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, sign in again on each device') }.
+ value={ _t('Return to login screen') } />
);
}
@@ -191,7 +196,7 @@ module.exports = React.createClass({
resetPasswordJsx = (
- To reset your password, enter the email address linked to your account:
+ { _t('To reset your password, enter the email address linked to your account') }:
diff --git a/src/components/structures/login/Login.js b/src/components/structures/login/Login.js
index a3635177e2..17d9833340 100644
--- a/src/components/structures/login/Login.js
+++ b/src/components/structures/login/Login.js
@@ -18,6 +18,7 @@ limitations under the License.
'use strict';
import React from 'react';
+import _t from 'counterpart-riot';
import ReactDOM from 'react-dom';
import url from 'url';
import sdk from '../../../index';
@@ -222,15 +223,17 @@ module.exports = React.createClass({
(this.state.enteredHomeserverUrl.startsWith("http:") ||
!this.state.enteredHomeserverUrl.startsWith("http")))
{
+ const urlStart =
;
+ const urlEnd = ;
errorText =
- Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar.
- Either use HTTPS or enable unsafe scripts
+ { _t('Can\'t connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or %(urlStart)s enable unsafe scripts %(urlEnd)s', {urlStart: urlStart, urlEnd: urlEnd})}
;
}
else {
+ const urlStart =
;
+ const urlEnd = ;
errorText =
- Can't connect to homeserver - please check your connectivity and ensure
- your homeserver's SSL certificate is trusted.
+ { _t('Can\'t connect to homeserver - please check your connectivity and ensure your %(urlStart)s homeserver\'s SSL certificate %(urlEnd)s is trusted', {urlStart: urlStart, urlEnd: urlEnd})}
;
}
}
@@ -273,8 +276,7 @@ module.exports = React.createClass({
}
return (
- Sorry, this homeserver is using a login which is not
- recognised ({step})
+ { _t('Sorry, this homeserver is using a login which is not recognised ')}({step})
);
}
@@ -291,7 +293,7 @@ module.exports = React.createClass({
if (this.props.enableGuest) {
loginAsGuestJsx =
- Login as guest
+ { _t('Login as guest')}
;
}
@@ -299,7 +301,7 @@ module.exports = React.createClass({
if (this.props.onCancelClick) {
returnToAppJsx =
- Return to app
+ { _t('Return to app')}
;
}
@@ -308,7 +310,7 @@ module.exports = React.createClass({
-
Sign in
+ { _t('Sign in')}
{ loader }
{ this.componentForStep(this.state.currentFlow) }
@@ -324,7 +326,7 @@ module.exports = React.createClass({
{ this.state.errorText }
- Create a new account
+ { _t('Create an account')}
{ loginAsGuestJsx }
{ returnToAppJsx }
diff --git a/src/components/structures/login/PostRegistration.js b/src/components/structures/login/PostRegistration.js
index c95f94cf4f..c3425cf68b 100644
--- a/src/components/structures/login/PostRegistration.js
+++ b/src/components/structures/login/PostRegistration.js
@@ -16,9 +16,10 @@ limitations under the License.
'use strict';
-var React = require('react');
-var sdk = require('../../../index');
-var MatrixClientPeg = require('../../../MatrixClientPeg');
+import React from 'react';
+import sdk from '../../../index';
+import MatrixClientPeg from '../../../MatrixClientPeg';
+import _t from 'counterpart-riot';
module.exports = React.createClass({
displayName: 'PostRegistration',
@@ -64,12 +65,12 @@ module.exports = React.createClass({
- Set a display name:
+ { _t('Set a display name:') }
- Upload an avatar:
+ { _t('Upload an avatar:') }
- Continue
+ { _t('Continue') }
{this.state.errorString}
diff --git a/src/components/structures/login/Registration.js b/src/components/structures/login/Registration.js
index 5501a39b58..b78f0ec80a 100644
--- a/src/components/structures/login/Registration.js
+++ b/src/components/structures/login/Registration.js
@@ -27,6 +27,7 @@ import MatrixClientPeg from '../../../MatrixClientPeg';
import RegistrationForm from '../../views/login/RegistrationForm';
import CaptchaForm from '../../views/login/CaptchaForm';
import RtsClient from '../../../RtsClient';
+import _t from 'counterpart-riot';
const MIN_PASSWORD_LENGTH = 6;
@@ -162,7 +163,7 @@ module.exports = React.createClass({
msisdn_available |= flow.stages.indexOf('m.login.msisdn') > -1;
}
if (!msisdn_available) {
- msg = "This server does not support authentication with a phone number";
+ msg = _t('This server does not support authentication with a phone number.');
}
}
this.setState({
@@ -260,29 +261,29 @@ module.exports = React.createClass({
var errMsg;
switch (errCode) {
case "RegistrationForm.ERR_PASSWORD_MISSING":
- errMsg = "Missing password.";
+ errMsg = _t('Missing password.');
break;
case "RegistrationForm.ERR_PASSWORD_MISMATCH":
- errMsg = "Passwords don't match.";
+ errMsg = _t('Passwords don\'t match.');
break;
case "RegistrationForm.ERR_PASSWORD_LENGTH":
- errMsg = `Password too short (min ${MIN_PASSWORD_LENGTH}).`;
+ errMsg = _t('Password too short (min %(MIN_PASSWORD_LENGTH)s).', {MIN_PASSWORD_LENGTH: $MIN_PASSWORD_LENGTH})
break;
case "RegistrationForm.ERR_EMAIL_INVALID":
- errMsg = "This doesn't look like a valid email address";
+ errMsg = _t('This doesn\'t look like a valid email address.');
break;
case "RegistrationForm.ERR_PHONE_NUMBER_INVALID":
- errMsg = "This doesn't look like a valid phone number";
+ errMsg = _t('This doesn\'t look like a valid phone number.');
break;
case "RegistrationForm.ERR_USERNAME_INVALID":
- errMsg = "User names may only contain letters, numbers, dots, hyphens and underscores.";
+ errMsg = _t('User names may only contain letters, numbers, dots, hyphens and underscores.');
break;
case "RegistrationForm.ERR_USERNAME_BLANK":
- errMsg = "You need to enter a user name";
+ errMsg = _t('You need to enter a user name.');
break;
default:
console.error("Unknown error code: %s", errCode);
- errMsg = "An unknown error occurred.";
+ errMsg = _t('An unknown error occurred.');
break;
}
this.setState({
@@ -400,7 +401,7 @@ module.exports = React.createClass({
if (this.props.onCancelClick) {
returnToAppJsx = (
- Return to app
+ {_t('Return to app')}
);
}
@@ -413,10 +414,10 @@ module.exports = React.createClass({
this.state.teamSelected.domain + "/icon.png" :
null}
/>
-
Create an account
+
{_t('Create an account')}
{registerBody}
- I already have an account
+ {_t('I already have an account')}
{returnToAppJsx}
diff --git a/src/components/views/dialogs/ChatInviteDialog.js b/src/components/views/dialogs/ChatInviteDialog.js
index 7ba503099a..032a705a0e 100644
--- a/src/components/views/dialogs/ChatInviteDialog.js
+++ b/src/components/views/dialogs/ChatInviteDialog.js
@@ -16,6 +16,7 @@ limitations under the License.
import React from 'react';
import classNames from 'classnames';
+import _t from 'counterpart-riot';
import sdk from '../../../index';
import { getAddressType, inviteMultipleToRoom } from '../../../Invite';
import createRoom from '../../../createRoom';
@@ -48,11 +49,7 @@ module.exports = React.createClass({
getDefaultProps: function() {
return {
- title: "Start a chat",
- description: "Who would you like to communicate with?",
value: "",
- placeholder: "Email, name or matrix ID",
- button: "Start Chat",
focus: true
};
},
@@ -310,6 +307,7 @@ module.exports = React.createClass({
Modal.createDialog(ErrorDialog, {
title: "Failed to invite",
description: ((err && err.message) ? err.message : "Operation failed"),
+ button: _t("OK"),
});
return null;
})
@@ -323,6 +321,7 @@ module.exports = React.createClass({
Modal.createDialog(ErrorDialog, {
title: "Failed to invite user",
description: ((err && err.message) ? err.message : "Operation failed"),
+ button: _t("OK"),
});
return null;
})
@@ -344,6 +343,7 @@ module.exports = React.createClass({
Modal.createDialog(ErrorDialog, {
title: "Failed to invite",
description: ((err && err.message) ? err.message : "Operation failed"),
+ button: _t("OK"),
});
return null;
})
@@ -403,6 +403,7 @@ module.exports = React.createClass({
Modal.createDialog(ErrorDialog, {
title: "Failed to invite the following users to the " + room.name + " room:",
description: errorList.join(", "),
+ button: _t("OK"),
});
}
return addrs;
diff --git a/src/components/views/dialogs/ConfirmUserActionDialog.js b/src/components/views/dialogs/ConfirmUserActionDialog.js
index 6cfaac65d4..aded0dabb9 100644
--- a/src/components/views/dialogs/ConfirmUserActionDialog.js
+++ b/src/components/views/dialogs/ConfirmUserActionDialog.js
@@ -16,6 +16,7 @@ limitations under the License.
import React from 'react';
import sdk from '../../../index';
+import _t from 'counterpart-riot';
import classnames from 'classnames';
/*
@@ -69,7 +70,7 @@ export default React.createClass({
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
const MemberAvatar = sdk.getComponent("views.avatars.MemberAvatar");
- const title = this.props.action + " this person?";
+ const title = _t("%(actionVerb)s this person?", { actionVerb: this.props.action});
const confirmButtonClass = classnames({
'mx_Dialog_primary': true,
'danger': this.props.danger,
@@ -82,7 +83,7 @@ export default React.createClass({
@@ -111,7 +112,7 @@ export default React.createClass({
- Cancel
+ { _t("Cancel") }
diff --git a/src/components/views/dialogs/ErrorDialog.js b/src/components/views/dialogs/ErrorDialog.js
index ef6fdbbead..fb7286e304 100644
--- a/src/components/views/dialogs/ErrorDialog.js
+++ b/src/components/views/dialogs/ErrorDialog.js
@@ -43,9 +43,6 @@ export default React.createClass({
getDefaultProps: function() {
return {
- title: "Error",
- description: "An error has occurred.",
- button: "OK",
focus: true,
};
},
diff --git a/src/components/views/dialogs/InteractiveAuthDialog.js b/src/components/views/dialogs/InteractiveAuthDialog.js
index 145b4b6453..f0eeead29a 100644
--- a/src/components/views/dialogs/InteractiveAuthDialog.js
+++ b/src/components/views/dialogs/InteractiveAuthDialog.js
@@ -46,12 +46,6 @@ export default React.createClass({
title: React.PropTypes.string,
},
- getDefaultProps: function() {
- return {
- title: "Authentication",
- };
- },
-
getInitialState: function() {
return {
authError: null,
diff --git a/src/components/views/dialogs/NeedToRegisterDialog.js b/src/components/views/dialogs/NeedToRegisterDialog.js
index f4df5913d5..875af6a7fd 100644
--- a/src/components/views/dialogs/NeedToRegisterDialog.js
+++ b/src/components/views/dialogs/NeedToRegisterDialog.js
@@ -38,13 +38,6 @@ module.exports = React.createClass({
onFinished: React.PropTypes.func.isRequired,
},
- getDefaultProps: function() {
- return {
- title: "Registration required",
- description: "A registered account is required for this action",
- };
- },
-
onRegisterClicked: function() {
dis.dispatch({
action: "start_upgrade_registration",
diff --git a/src/components/views/dialogs/QuestionDialog.js b/src/components/views/dialogs/QuestionDialog.js
index 6012541b94..e3d38922ee 100644
--- a/src/components/views/dialogs/QuestionDialog.js
+++ b/src/components/views/dialogs/QuestionDialog.js
@@ -33,7 +33,6 @@ export default React.createClass({
title: "",
description: "",
extraButtons: null,
- button: "OK",
focus: true,
hasCancelButton: true,
};
diff --git a/src/components/views/dialogs/TextInputDialog.js b/src/components/views/dialogs/TextInputDialog.js
index 6e40efffd8..080e6ac1b6 100644
--- a/src/components/views/dialogs/TextInputDialog.js
+++ b/src/components/views/dialogs/TextInputDialog.js
@@ -16,6 +16,7 @@ limitations under the License.
import React from 'react';
import sdk from '../../../index';
+import _t from 'counterpart-riot';
export default React.createClass({
displayName: 'TextInputDialog',
@@ -36,7 +37,6 @@ export default React.createClass({
title: "",
value: "",
description: "",
- button: "OK",
focus: true,
};
},
@@ -73,7 +73,7 @@ export default React.createClass({
- Cancel
+ { _t("Cancel") }
{this.props.button}
diff --git a/src/components/views/elements/LanguageDropdown.js b/src/components/views/elements/LanguageDropdown.js
new file mode 100644
index 0000000000..0da05ff07e
--- /dev/null
+++ b/src/components/views/elements/LanguageDropdown.js
@@ -0,0 +1,140 @@
+/*
+Copyright 2017 Marcel Radzio (MTRNord)
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import React from 'react';
+
+import sdk from '../../../index';
+import UserSettingsStore from '../../../UserSettingsStore';
+const _localSettings = UserSettingsStore.getLocalSettings();
+import _t from 'counterpart-riot';
+const languageHandler = require('../../../languageHandler');
+var SdkConfig = require("../../../SdkConfig");
+
+let LANGUAGES = [];
+
+const LANGUAGES_BY_VALUE = new Object(null);
+
+function languageMatchesSearchQuery(query, language) {
+ if (language.label.toUpperCase().indexOf(query.toUpperCase()) == 0) return true;
+ if (language.value.toUpperCase() == query.toUpperCase()) return true;
+ return false;
+}
+
+export default class LanguageDropdown extends React.Component {
+ constructor(props) {
+ super(props);
+ this._onSearchChange = this._onSearchChange.bind(this);
+
+ this.state = {
+ searchQuery: '',
+ }
+ }
+
+ componentWillMount() {
+
+ const languageKeys = SdkConfig.get().languages;
+
+ // Build const LANGUAGES in a way that counterpart allows translation inside object:
+ languageKeys.forEach(function(languageKey) {
+ var l = {};
+ l.id = "language";
+ l.label = _t(languageKey);
+ l.value = languageKey;
+ LANGUAGES.push(l);
+ });
+
+ LANGUAGES = LANGUAGES.sort(function(a, b){
+ if(a.label < b.label) return -1;
+ if(a.label > b.label) return 1;
+ return 0;
+ })
+
+ for (const l of LANGUAGES) {
+ LANGUAGES_BY_VALUE[l.value] = l;
+ }
+
+
+ if (!this.props.value) {
+ // If no value is given, we start with the first
+ // country selected, but our parent component
+ // doesn't know this, therefore we do this.
+ if (_localSettings.hasOwnProperty('language')) {
+ this.props.onOptionChange(_localSettings.language);
+ }else {
+ const language = languageHandler.normalizeLanguageKey(languageHandler.getLanguageFromBrowser());
+ this.props.onOptionChange(language);
+ }
+ }
+ }
+
+ _onSearchChange(search) {
+ this.setState({
+ searchQuery: search,
+ });
+ }
+
+ render() {
+ const Dropdown = sdk.getComponent('elements.Dropdown');
+
+ let displayedLanguages;
+ if (this.state.searchQuery) {
+ displayedLanguages = LANGUAGES.filter(
+ languageMatchesSearchQuery.bind(this, this.state.searchQuery),
+ );
+ if (
+ this.state.searchQuery.length == 2 &&
+ LANGUAGES_BY_VALUE[this.state.searchQuery.toUpperCase()]
+ ) {
+ const matched = LANGUAGES_BY_VALUE[this.state.searchQuery.toUpperCase()];
+ displayedLanguages = displayedLanguages.filter((l) => {
+ return l.id != matched.id;
+ });
+ displayedLanguages.unshift(matched);
+ }
+ } else {
+ displayedLanguages = LANGUAGES;
+ }
+
+ const options = displayedLanguages.map((language) => {
+ return
+ {language.label}
+
;
+ });
+
+ // default value here too, otherwise we need to handle null / undefined
+ // values between mounting and the initial value propgating
+ let value = null;
+ if (_localSettings.hasOwnProperty('language')) {
+ value = this.props.value || _localSettings.language;
+ } else {
+ const language = navigator.language || navigator.userLanguage;
+ value = this.props.value || language;
+ }
+
+ return
+ {options}
+
+ }
+}
+
+LanguageDropdown.propTypes = {
+ className: React.PropTypes.string,
+ onOptionChange: React.PropTypes.func.isRequired,
+ value: React.PropTypes.string,
+};
diff --git a/src/components/views/elements/MemberEventListSummary.js b/src/components/views/elements/MemberEventListSummary.js
index dcf1810468..747e5019a8 100644
--- a/src/components/views/elements/MemberEventListSummary.js
+++ b/src/components/views/elements/MemberEventListSummary.js
@@ -15,6 +15,7 @@ limitations under the License.
*/
import React from 'react';
const MemberAvatar = require('../avatars/MemberAvatar.js');
+import _t from 'counterpart-riot';
module.exports = React.createClass({
displayName: 'MemberEventListSummary',
@@ -203,30 +204,146 @@ module.exports = React.createClass({
* @param {boolean} plural whether there were multiple users undergoing the same
* transition.
* @param {number} repeats the number of times the transition was repeated in a row.
- * @returns {string} the written English equivalent of the transition.
+ * @returns {string} the written Human Readable equivalent of the transition.
*/
_getDescriptionForTransition(t, plural, repeats) {
- const beConjugated = plural ? "were" : "was";
- const invitation = "their invitation" + (plural || (repeats > 1) ? "s" : "");
-
+ // The empty interpolations 'severalUsers' and 'oneUser'
+ // are there only to show translators to non-English languages
+ // that the verb is conjugated to plural or singular Subject.
let res = null;
- const map = {
- "joined": "joined",
- "left": "left",
- "joined_and_left": "joined and left",
- "left_and_joined": "left and rejoined",
- "invite_reject": "rejected " + invitation,
- "invite_withdrawal": "had " + invitation + " withdrawn",
- "invited": beConjugated + " invited",
- "banned": beConjugated + " banned",
- "unbanned": beConjugated + " unbanned",
- "kicked": beConjugated + " kicked",
- "changed_name": "changed name",
- "changed_avatar": "changed avatar",
- };
+ switch(t) {
+ case "joined":
+ if (repeats > 1) {
+ res = (plural)
+ ? _t("%(severalUsers)s joined %(repeats)s times", { severalUsers: "", repeats: repeats })
+ : _t("%(oneUser)s joined %(repeats)s times", { oneUser: "", repeats: repeats });
+ } else {
+ res = (plural)
+ ? _t("%(severalUsers)s joined", { severalUsers: "" })
+ : _t("%(oneUser)s joined", { oneUser: "" });
+ }
- if (Object.keys(map).includes(t)) {
- res = map[t] + (repeats > 1 ? " " + repeats + " times" : "" );
+ break;
+ case "left":
+ if (repeats > 1) {
+ res = (plural)
+ ? _t("%(severalUsers)s left %(repeats)s times", { severalUsers: "", repeats: repeats })
+ : _t("%(oneUser)s left %(repeats)s times", { oneUser: "", repeats: repeats });
+ } else {
+ res = (plural)
+ ? _t("%(severalUsers)s left", { severalUsers: "" })
+ : _t("%(oneUser)s left", { oneUser: "" });
+ } break;
+ case "joined_and_left":
+ if (repeats > 1) {
+ res = (plural)
+ ? _t("%(severalUsers)s joined and left %(repeats)s times", { severalUsers: "", repeats: repeats })
+ : _t("%(oneUser)s joined and left %(repeats)s times", { oneUser: "", repeats: repeats });
+ } else {
+ res = (plural)
+ ? _t("%(severalUsers)s joined and left", { severalUsers: "" })
+ : _t("%(oneUser)s joined and left", { oneUser: "" });
+ }
+ break;
+ case "left_and_joined":
+ if (repeats > 1) {
+ res = (plural)
+ ? _t("%(severalUsers)s left and rejoined %(repeats)s times", { severalUsers: "", repeats: repeats })
+ : _t("%(oneUser)s left and rejoined %(repeats)s times", { oneUser: "", repeats: repeats });
+ } else {
+ res = (plural)
+ ? _t("%(severalUsers)s left and rejoined", { severalUsers: "" })
+ : _t("%(oneUser)s left and rejoined", { oneUser: "" });
+ } break;
+ break;
+ case "invite_reject":
+ if (repeats > 1) {
+ res = (plural)
+ ? _t("%(severalUsers)s rejected their invitations %(repeats)s times", { severalUsers: "", repeats: repeats })
+ : _t("%(oneUser)s rejected his/her invitation %(repeats)s times", { oneUser: "", repeats: repeats });
+ } else {
+ res = (plural)
+ ? _t("%(severalUsers)s rejected their invitations", { severalUsers: "" })
+ : _t("%(oneUser)s rejected his/her invitation", { oneUser: "" });
+ }
+ break;
+ case "invite_withdrawal":
+ if (repeats > 1) {
+ res = (plural)
+ ? _t("%(severalUsers)s had their invitations withdrawn %(repeats)s times", { severalUsers: "", repeats: repeats })
+ : _t("%(oneUser)s had his/her invitation withdrawn %(repeats)s times", { oneUser: "", repeats: repeats });
+ } else {
+ res = (plural)
+ ? _t("%(severalUsers)s had their invitations withdrawn", { severalUsers: "" })
+ : _t("%(oneUser)s had his/her invitation withdrawn", { oneUser: "" });
+ }
+ break;
+ case "invited":
+ if (repeats > 1) {
+ res = (plural)
+ ? _t("were invited %(repeats)s times", { repeats: repeats })
+ : _t("was invited %(repeats)s times", { repeats: repeats });
+ } else {
+ res = (plural)
+ ? _t("were invited")
+ : _t("was invited");
+ }
+ break;
+ case "banned":
+ if (repeats > 1) {
+ res = (plural)
+ ? _t("were banned %(repeats)s times", { repeats: repeats })
+ : _t("was banned %(repeats)s times", { repeats: repeats });
+ } else {
+ res = (plural)
+ ? _t("were banned")
+ : _t("was banned");
+ }
+ break;
+ case "unbanned":
+ if (repeats > 1) {
+ res = (plural)
+ ? _t("were unbanned %(repeats)s times", { repeats: repeats })
+ : _t("was unbanned %(repeats)s times", { repeats: repeats });
+ } else {
+ res = (plural)
+ ? _t("were unbanned")
+ : _t("was unbanned");
+ }
+ break;
+ case "kicked":
+ if (repeats > 1) {
+ res = (plural)
+ ? _t("were kicked %(repeats)s times", { repeats: repeats })
+ : _t("was kicked %(repeats)s times", { repeats: repeats });
+ } else {
+ res = (plural)
+ ? _t("were kicked")
+ : _t("was kicked");
+ }
+ break;
+ case "changed_name":
+ if (repeats > 1) {
+ res = (plural)
+ ? _t("%(severalUsers)s changed their name %(repeats)s times", { severalUsers: "", repeats: repeats })
+ : _t("%(oneUser)s changed his/her name %(repeats)s times", { oneUser: "", repeats: repeats });
+ } else {
+ res = (plural)
+ ? _t("%(severalUsers)s changed their name", { severalUsers: "" })
+ : _t("%(oneUser)s changed his/her name", { oneUser: "" });
+ }
+ break;
+ case "changed_avatar":
+ if (repeats > 1) {
+ res = (plural)
+ ? _t("%(severalUsers)s changed their avatar %(repeats)s times", { severalUsers: "", repeats: repeats })
+ : _t("%(oneUser)s changed his/her avatar %(repeats)s times", { oneUser: "", repeats: repeats });
+ } else {
+ res = (plural)
+ ? _t("%(severalUsers)s changed their avatar", { severalUsers: "" })
+ : _t("%(oneUser)s changed his/her avatar", { oneUser: "" });
+ }
+ break;
}
return res;
@@ -254,11 +371,12 @@ module.exports = React.createClass({
return items[0];
} else if (remaining) {
items = items.slice(0, itemLimit);
- const other = " other" + (remaining > 1 ? "s" : "");
- return items.join(', ') + ' and ' + remaining + other;
+ return (remaining > 1)
+ ? _t("%(items)s and %(remaining)s others", { items: items.join(', '), remaining: remaining } )
+ : _t("%(items)s and one other", { items: items.join(', ') });
} else {
const lastItem = items.pop();
- return items.join(', ') + ' and ' + lastItem;
+ return _t("%(items)s and %(lastItem)s", { items: items.join(', '), lastItem: lastItem });
}
},
diff --git a/src/components/views/elements/PowerSelector.js b/src/components/views/elements/PowerSelector.js
index 5eec464ead..f12ef3ac9d 100644
--- a/src/components/views/elements/PowerSelector.js
+++ b/src/components/views/elements/PowerSelector.js
@@ -19,10 +19,8 @@ limitations under the License.
import React from 'react';
import * as Roles from '../../../Roles';
+var LEVEL_ROLE_MAP = {};
var reverseRoles = {};
-Object.keys(Roles.LEVEL_ROLE_MAP).forEach(function(key) {
- reverseRoles[Roles.LEVEL_ROLE_MAP[key]] = key;
-});
module.exports = React.createClass({
displayName: 'PowerSelector',
@@ -44,9 +42,16 @@ module.exports = React.createClass({
getInitialState: function() {
return {
- custom: (Roles.LEVEL_ROLE_MAP[this.props.value] === undefined),
+ custom: (LEVEL_ROLE_MAP[this.props.value] === undefined),
};
},
+
+ componentWillMount: function() {
+ LEVEL_ROLE_MAP = Roles.levelRoleMap();
+ Object.keys(LEVEL_ROLE_MAP).forEach(function(key) {
+ reverseRoles[LEVEL_ROLE_MAP[key]] = key;
+ });
+ },
onSelectChange: function(event) {
this.setState({ custom: event.target.value === "Custom" });
@@ -94,7 +99,7 @@ module.exports = React.createClass({
selectValue = "Custom";
}
else {
- selectValue = Roles.LEVEL_ROLE_MAP[this.props.value] || "Custom";
+ selectValue = LEVEL_ROLE_MAP[this.props.value] || "Custom";
}
var select;
if (this.props.disabled) {
@@ -105,7 +110,7 @@ module.exports = React.createClass({
const levels = [0, 50, 100];
let options = levels.map((level) => {
return {
- value: Roles.LEVEL_ROLE_MAP[level],
+ value: LEVEL_ROLE_MAP[level],
// Give a userDefault (users_default in the power event) of 0 but
// because level !== undefined, this should never be used.
text: Roles.textualPowerLevel(level, 0),
diff --git a/src/components/views/login/PasswordLogin.js b/src/components/views/login/PasswordLogin.js
index 46c9598751..6530df83e4 100644
--- a/src/components/views/login/PasswordLogin.js
+++ b/src/components/views/login/PasswordLogin.js
@@ -19,6 +19,7 @@ import React from 'react';
import ReactDOM from 'react-dom';
import classNames from 'classnames';
import sdk from '../../../index';
+import _t from 'counterpart-riot';
import {field_input_incorrect} from '../../../UiEffects';
@@ -141,7 +142,7 @@ class PasswordLogin extends React.Component {
type="text"
name="username" // make it a little easier for browser's remember-password
onChange={this.onUsernameChanged}
- placeholder="username"
+ placeholder={_t('username')}
value={this.state.username}
autoFocus
/>
@@ -179,7 +180,7 @@ class PasswordLogin extends React.Component {
if (this.props.onForgotPasswordClick) {
forgotPasswordJsx = (
- Forgot your password?
+ { _t('Forgot your password?') }
);
}
@@ -197,24 +198,24 @@ class PasswordLogin extends React.Component {
);
diff --git a/src/components/views/messages/MFileBody.js b/src/components/views/messages/MFileBody.js
index 029a8a9fe4..d7609eae15 100644
--- a/src/components/views/messages/MFileBody.js
+++ b/src/components/views/messages/MFileBody.js
@@ -20,6 +20,7 @@ import React from 'react';
import filesize from 'filesize';
import MatrixClientPeg from '../../../MatrixClientPeg';
import sdk from '../../../index';
+import _t from 'counterpart-riot';
import {decryptFile} from '../../../utils/DecryptFile';
import Tinter from '../../../Tinter';
import request from 'browser-request';
@@ -202,7 +203,7 @@ module.exports = React.createClass({
* @return {string} the human readable link text for the attachment.
*/
presentableTextForFile: function(content) {
- var linkText = 'Attachment';
+ var linkText = _t("Attachment");
if (content.body && content.body.length > 0) {
// The content body should be the name of the file including a
// file extension.
@@ -261,7 +262,7 @@ module.exports = React.createClass({
const content = this.props.mxEvent.getContent();
const text = this.presentableTextForFile(content);
const isEncrypted = content.file !== undefined;
- const fileName = content.body && content.body.length > 0 ? content.body : "Attachment";
+ const fileName = content.body && content.body.length > 0 ? content.body : _t("Attachment");
const contentUrl = this._getContentUrl();
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
@@ -283,7 +284,9 @@ module.exports = React.createClass({
}).catch((err) => {
console.warn("Unable to decrypt attachment: ", err);
Modal.createDialog(ErrorDialog, {
- description: "Error decrypting attachment"
+ title: _t("Error"),
+ description: _t("Error decrypting attachment"),
+ button: _t("OK"),
});
}).finally(() => {
decrypting = false;
@@ -295,7 +298,7 @@ module.exports = React.createClass({
@@ -314,7 +317,7 @@ module.exports = React.createClass({
// We can't provide a Content-Disposition header like we would for HTTP.
download: fileName,
target: "_blank",
- textContent: "Download " + text,
+ textContent: _t("Download %(text)s", { text: text }),
}, "*");
};
@@ -362,7 +365,7 @@ module.exports = React.createClass({
@@ -371,7 +374,7 @@ module.exports = React.createClass({
} else {
var extra = text ? (': ' + text) : '';
return
- Invalid file{extra}
+ { _t("Invalid file%(extra)s", { extra: extra }) }
;
}
},
diff --git a/src/components/views/room_settings/AliasSettings.js b/src/components/views/room_settings/AliasSettings.js
index 6543f2a17d..a6a9302190 100644
--- a/src/components/views/room_settings/AliasSettings.js
+++ b/src/components/views/room_settings/AliasSettings.js
@@ -19,6 +19,7 @@ var React = require('react');
var ObjectUtils = require("../../../ObjectUtils");
var MatrixClientPeg = require('../../../MatrixClientPeg');
var sdk = require("../../../index");
+import _t from 'counterpart-riot';
var Modal = require("../../../Modal");
module.exports = React.createClass({
@@ -154,8 +155,9 @@ module.exports = React.createClass({
else {
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
- title: "Invalid alias format",
- description: "'" + alias + "' is not a valid format for an alias",
+ title: _t('Invalid alias format'),
+ description: _t('"%(alias)s" is not a valid format for an alias', { alias: alias }),
+ button: _t('OK'),
});
}
},
@@ -170,8 +172,9 @@ module.exports = React.createClass({
else {
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
- title: "Invalid address format",
- description: "'" + alias + "' is not a valid format for an address",
+ title: _t('Invalid address format'),
+ description: _t('"%(alias)s" is not a valid format for an address', { alias: alias }),
+ button: _t('OK'),
});
}
},
@@ -203,7 +206,7 @@ module.exports = React.createClass({
if (this.props.canSetCanonicalAlias) {
canonical_alias_section = (
- not specified
+ { _t('not specified') }
{
Object.keys(self.state.domainToAliases).map(function(domain, i) {
return self.state.domainToAliases[domain].map(function(alias, j) {
@@ -220,7 +223,7 @@ module.exports = React.createClass({
}
else {
canonical_alias_section = (
- { this.state.canonicalAlias || "not set" }
+ { this.state.canonicalAlias || _t('not set') }
);
}
@@ -254,13 +257,13 @@ module.exports = React.createClass({
Addresses
- The main address for this room is: { canonical_alias_section }
+ { _t('The main address for this room is') }: { canonical_alias_section }
{ (this.state.domainToAliases[localDomain] &&
this.state.domainToAliases[localDomain].length > 0)
- ? "Local addresses for this room:"
- : "This room has no local addresses" }
+ ? _t('Local addresses for this room:')
+ : _t('This room has no local addresses') }
{ (this.state.domainToAliases[localDomain] || []).map((alias, i) => {
@@ -268,7 +271,7 @@ module.exports = React.createClass({
if (this.props.canSetAliases) {
deleteButton = (
+ alt={ _t('Delete') } onClick={ self.onAliasDeleted.bind(self, localDomain, i) } />
);
}
return (
@@ -276,7 +279,7 @@ module.exports = React.createClass({
diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js
index 44c4051995..3e2c3f5de0 100644
--- a/src/components/views/rooms/EventTile.js
+++ b/src/components/views/rooms/EventTile.js
@@ -18,6 +18,7 @@ limitations under the License.
var React = require('react');
var classNames = require("classnames");
+import _t from 'counterpart-riot';
var Modal = require('../../../Modal');
var sdk = require('../../../index');
@@ -464,9 +465,9 @@ module.exports = WithMatrixClient(React.createClass({
if (needsSenderProfile) {
let aux = null;
if (!this.props.tileShape) {
- if (msgtype === 'm.image') aux = "sent an image";
- else if (msgtype === 'm.video') aux = "sent a video";
- else if (msgtype === 'm.file') aux = "uploaded a file";
+ if (msgtype === 'm.image') aux = _t('sent an image');
+ else if (msgtype === 'm.video') aux = _t('sent a video');
+ else if (msgtype === 'm.file') aux = _t('uploaded a file');
sender =
;
}
else {
diff --git a/src/components/views/rooms/MemberInfo.js b/src/components/views/rooms/MemberInfo.js
index 839405c922..fed307c216 100644
--- a/src/components/views/rooms/MemberInfo.js
+++ b/src/components/views/rooms/MemberInfo.js
@@ -31,6 +31,7 @@ import classNames from 'classnames';
import dis from '../../../dispatcher';
import Modal from '../../../Modal';
import sdk from '../../../index';
+import _t from 'counterpart-riot';
import createRoom from '../../../createRoom';
import DMRoomMap from '../../../utils/DMRoomMap';
import Unread from '../../../Unread';
@@ -219,7 +220,7 @@ module.exports = WithMatrixClient(React.createClass({
onKick: function() {
const membership = this.props.member.membership;
- const kickLabel = membership === "invite" ? "Disinvite" : "Kick";
+ const kickLabel = membership === "invite" ? _t("Disinvite") : _t("Kick");
const ConfirmUserActionDialog = sdk.getComponent("dialogs.ConfirmUserActionDialog");
Modal.createDialog(ConfirmUserActionDialog, {
member: this.props.member,
@@ -241,8 +242,9 @@ module.exports = WithMatrixClient(React.createClass({
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Kick error: " + err);
Modal.createDialog(ErrorDialog, {
- title: "Failed to kick",
+ title: _t("Failed to kick"),
description: ((err && err.message) ? err.message : "Operation failed"),
+ button: _t("OK"),
});
}
).finally(()=>{
@@ -256,7 +258,7 @@ module.exports = WithMatrixClient(React.createClass({
const ConfirmUserActionDialog = sdk.getComponent("dialogs.ConfirmUserActionDialog");
Modal.createDialog(ConfirmUserActionDialog, {
member: this.props.member,
- action: this.props.member.membership == 'ban' ? 'Unban' : 'Ban',
+ action: this.props.member.membership == 'ban' ? _t("Unban") : _t("Ban"),
askReason: this.props.member.membership != 'ban',
danger: this.props.member.membership != 'ban',
onFinished: (proceed, reason) => {
@@ -283,8 +285,9 @@ module.exports = WithMatrixClient(React.createClass({
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Ban error: " + err);
Modal.createDialog(ErrorDialog, {
- title: "Error",
- description: "Failed to ban user",
+ title: _t("Error"),
+ description: _t("Failed to ban user"),
+ button: _t("OK"),
});
}
).finally(()=>{
@@ -333,8 +336,9 @@ module.exports = WithMatrixClient(React.createClass({
}, function(err) {
console.error("Mute error: " + err);
Modal.createDialog(ErrorDialog, {
- title: "Error",
- description: "Failed to mute user",
+ title: _t("Error"),
+ description: _t("Failed to mute user"),
+ button: _t("OK"),
});
}
).finally(()=>{
@@ -376,14 +380,15 @@ module.exports = WithMatrixClient(React.createClass({
if (err.errcode == 'M_GUEST_ACCESS_FORBIDDEN') {
var NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog");
Modal.createDialog(NeedToRegisterDialog, {
- title: "Please Register",
- description: "This action cannot be performed by a guest user. Please register to be able to do this."
+ title: _t("Please Register"),
+ description: _t("This action cannot be performed by a guest user. Please register to be able to do this") + ".",
});
} else {
console.error("Toggle moderator error:" + err);
Modal.createDialog(ErrorDialog, {
- title: "Error",
- description: "Failed to toggle moderator status",
+ title: _t("Error"),
+ description: _t("Failed to toggle moderator status"),
+ button: _t("OK"),
});
}
}
@@ -403,8 +408,9 @@ module.exports = WithMatrixClient(React.createClass({
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Failed to change power level " + err);
Modal.createDialog(ErrorDialog, {
- title: "Error",
- description: "Failed to change power level",
+ title: _t("Error"),
+ description: _t("Failed to change power level"),
+ button: _t("OK"),
});
}
).finally(()=>{
@@ -432,13 +438,13 @@ module.exports = WithMatrixClient(React.createClass({
if (parseInt(myPower) === parseInt(powerLevel)) {
var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createDialog(QuestionDialog, {
- title: "Warning",
+ title: _t("Warning"),
description:
- You will not be able to undo this change as you are promoting the user to have the same power level as yourself.
- Are you sure?
+ { _t("You will not be able to undo this change as you are promoting the user to have the same power level as yourself") }.
+ { _t("Are you sure?") }
,
- button: "Continue",
+ button: _t("Continue"),
onFinished: function(confirmed) {
if (confirmed) {
self._applyPowerChange(roomId, target, powerLevel, powerLevelEvent);
@@ -581,9 +587,9 @@ module.exports = WithMatrixClient(React.createClass({
// still loading
devComponents =
;
} else if (devices === null) {
- devComponents = "Unable to load device list";
+ devComponents = _t("Unable to load device list");
} else if (devices.length === 0) {
- devComponents = "No devices with registered encryption keys";
+ devComponents = _t("No devices with registered encryption keys");
} else {
devComponents = [];
for (var i = 0; i < devices.length; i++) {
@@ -595,7 +601,7 @@ module.exports = WithMatrixClient(React.createClass({
return (
-
Devices
+
{ _t("Devices") }
{devComponents}
@@ -644,11 +650,11 @@ module.exports = WithMatrixClient(React.createClass({
-
Start new chat
+
{ _t("Start a chat") }
;
startChat =
-
Direct chats
+ { _t("Direct chats") }
{tiles}
{startNewChat}
;
@@ -661,7 +667,7 @@ module.exports = WithMatrixClient(React.createClass({
if (this.state.can.kick) {
const membership = this.props.member.membership;
- const kickLabel = membership === "invite" ? "Disinvite" : "Kick";
+ const kickLabel = membership === "invite" ? _t("Disinvite") : _t("Kick");
kickButton = (
@@ -670,9 +676,9 @@ module.exports = WithMatrixClient(React.createClass({
);
}
if (this.state.can.ban) {
- let label = 'Ban';
+ let label = _t("Ban");
if (this.props.member.membership == 'ban') {
- label = 'Unban';
+ label = _t("Unban");
}
banButton = (
@@ -691,7 +697,7 @@ module.exports = WithMatrixClient(React.createClass({
);
}
if (this.state.can.toggleMod) {
- var giveOpLabel = this.state.isTargetMod ? "Revoke Moderator" : "Make Moderator";
+ var giveOpLabel = this.state.isTargetMod ? _t("Revoke Moderator") : _t("Make Moderator");
giveModButton =
{giveOpLabel}
;
@@ -742,7 +748,7 @@ module.exports = WithMatrixClient(React.createClass({
{ this.props.member.userId }
- Level:
+ { _t("Level") }:
1 ? "s" : "") + "...";
+ var text = (overflowCount > 1)
+ ? _t("and %(overflowCount)s others...", { overflowCount: overflowCount })
+ : _t("and one other...");
return (
@@ -363,8 +366,8 @@ module.exports = React.createClass({
var inputBox = (
);
diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js
index 830d3f38ff..a9b65d4611 100644
--- a/src/components/views/rooms/MessageComposer.js
+++ b/src/components/views/rooms/MessageComposer.js
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
var React = require('react');
-
+import _t from 'counterpart-riot';
var CallHandler = require('../../../CallHandler');
var MatrixClientPeg = require('../../../MatrixClientPeg');
var Modal = require('../../../Modal');
@@ -93,8 +93,8 @@ export default class MessageComposer extends React.Component {
if (MatrixClientPeg.get().isGuest()) {
let NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog");
Modal.createDialog(NeedToRegisterDialog, {
- title: "Please Register",
- description: "Guest users can't upload files. Please register to upload.",
+ title: _t('Please Register'),
+ description: _t('Guest users can\'t upload files. Please register to upload') + '.',
});
return;
}
@@ -118,15 +118,16 @@ export default class MessageComposer extends React.Component {
}
Modal.createDialog(QuestionDialog, {
- title: "Upload Files",
+ title: _t('Upload Files'),
description: (
-
Are you sure you want upload the following files?
+
{ _t('Are you sure you want upload the following files?') }
),
+ button: _t("OK"),
onFinished: (shouldUpload) => {
if(shouldUpload) {
// MessageComposer shouldn't have to rely on its parent passing in a callback to upload a file
@@ -240,11 +241,11 @@ export default class MessageComposer extends React.Component {
if (roomIsEncrypted) {
// FIXME: show a /!\ if there are untrusted devices in the room...
e2eImg = 'img/e2e-verified.svg';
- e2eTitle = 'Encrypted room';
+ e2eTitle = _t('Encrypted room');
e2eClass = 'mx_MessageComposer_e2eIcon';
} else {
e2eImg = 'img/e2e-unencrypted.svg';
- e2eTitle = 'Unencrypted room';
+ e2eTitle = _t('Unencrypted room');
e2eClass = 'mx_MessageComposer_e2eIcon mx_filterFlipColor';
}
@@ -257,16 +258,16 @@ export default class MessageComposer extends React.Component {
if (this.props.callState && this.props.callState !== 'ended') {
hangupButton =
-
+
;
}
else {
callButton =
-
+
;
videoCallButton =
-
+
;
}
@@ -280,7 +281,7 @@ export default class MessageComposer extends React.Component {
// complex because of conference calls.
var uploadButton = (
+ onClick={this.onUploadClick} title={ _t('Upload file') }>
- You do not have permission to post to this room
+ { _t('You do not have permission to post to this room') }
);
}
@@ -354,7 +355,7 @@ export default class MessageComposer extends React.Component {
mx_filterFlipColor: true,
});
return
{formatButtons}
-
-
diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js
index af361db235..adb2a4c6aa 100644
--- a/src/components/views/rooms/MessageComposerInput.js
+++ b/src/components/views/rooms/MessageComposerInput.js
@@ -30,6 +30,7 @@ import type {MatrixClient} from 'matrix-js-sdk/lib/matrix';
import SlashCommands from '../../../SlashCommands';
import Modal from '../../../Modal';
import sdk from '../../../index';
+import _t from 'counterpart-riot';
import dis from '../../../dispatcher';
import KeyCode from '../../../KeyCode';
@@ -504,8 +505,9 @@ export default class MessageComposerInput extends React.Component {
console.error("Command failure: %s", err);
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
- title: "Server error",
- description: ((err && err.message) ? err.message : "Server unavailable, overloaded, or something else went wrong."),
+ title: _t("Server error"),
+ description: ((err && err.message) ? err.message : _t("Server unavailable, overloaded, or something else went wrong") + "."),
+ button: _t("OK"),
});
});
}
@@ -513,8 +515,9 @@ export default class MessageComposerInput extends React.Component {
console.error(cmd.error);
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
- title: "Command error",
- description: cmd.error
+ title: _t("Command error"),
+ description: cmd.error,
+ button: _t("OK"),
});
}
return true;
@@ -719,7 +722,7 @@ export default class MessageComposerInput extends React.Component {
(~{ this.props.searchInfo.searchCount } results) ;
+ searchStatus =
{ _t("(~%(searchCount)s results)", { searchCount: this.props.searchInfo.searchCount }) }
;
}
// XXX: this is a bit inefficient - we could just compare room.name for 'Empty room'...
@@ -220,7 +222,7 @@ module.exports = React.createClass({
}
}
- var roomName = 'Join Room';
+ var roomName = _t("Join Room");
if (this.props.oobData && this.props.oobData.name) {
roomName = this.props.oobData.name;
} else if (this.props.room) {
@@ -261,7 +263,7 @@ module.exports = React.createClass({
@@ -296,7 +298,7 @@ module.exports = React.createClass({
var forget_button;
if (this.props.onForgetClick) {
forget_button =
-
+
;
}
@@ -304,7 +306,7 @@ module.exports = React.createClass({
let search_button;
if (this.props.onSearchClick && this.props.inRoom) {
search_button =
-
+
;
}
@@ -312,7 +314,7 @@ module.exports = React.createClass({
var rightPanel_buttons;
if (this.props.collapsedRhs) {
rightPanel_buttons =
-
+
;
}
diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js
index 760b0543c6..b463928482 100644
--- a/src/components/views/rooms/RoomList.js
+++ b/src/components/views/rooms/RoomList.js
@@ -17,6 +17,7 @@ limitations under the License.
'use strict';
var React = require("react");
var ReactDOM = require("react-dom");
+import _t from 'counterpart-riot';
var GeminiScrollbar = require('react-gemini-scrollbar');
var MatrixClientPeg = require("../../../MatrixClientPeg");
var CallHandler = require('../../../CallHandler');
@@ -470,13 +471,12 @@ module.exports = React.createClass({
render: function() {
var RoomSubList = sdk.getComponent('structures.RoomSubList');
var self = this;
-
return (
{ name } : fallback;
+ return name ? { name } : fallback;
},
render: function() {
@@ -128,13 +130,14 @@ module.exports = React.createClass({
;
}
}
+ // TODO: find a way to respect HTML in counterpart!
joinBlock = (
- You have been invited to join this room by { this.props.inviterName }
+ { _t('You have been invited to join this room by %(inviterName)s', {inviterName: this.props.inviterName}) }
{emailMatchBlock}
@@ -186,8 +189,8 @@ module.exports = React.createClass({
joinBlock = (
- You are trying to access { name }.
-
Click here to join the discussion!
+ { _t('You are trying to access %(roomName)s', {roomName: name}) }.
+
{ _t('Click here') } { _t('to join the discussion') }!
);
@@ -196,7 +199,7 @@ module.exports = React.createClass({
if (this.props.canPreview) {
previewBlock = (
- This is a preview of this room. Room interactions have been disabled.
+ { _t('This is a preview of this room. Room interactions have been disabled') }.
);
}
diff --git a/src/components/views/rooms/RoomSettings.js b/src/components/views/rooms/RoomSettings.js
index 798aacfa8e..02efd439a4 100644
--- a/src/components/views/rooms/RoomSettings.js
+++ b/src/components/views/rooms/RoomSettings.js
@@ -17,6 +17,7 @@ limitations under the License.
import q from 'q';
import React from 'react';
+import _t from 'counterpart-riot';
import MatrixClientPeg from '../../../MatrixClientPeg';
import SdkConfig from '../../../SdkConfig';
import sdk from '../../../index';
@@ -56,8 +57,9 @@ const BannedUser = React.createClass({
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Failed to unban: " + err);
Modal.createDialog(ErrorDialog, {
- title: "Error",
- description: "Failed to unban",
+ title: _t('Error'),
+ description: _t('Failed to unban'),
+ button: _t("OK"),
});
}).done();
},
@@ -70,7 +72,7 @@ const BannedUser = React.createClass({
- Unban
+ { _t('Unban') }
{this.props.member.userId}
@@ -400,13 +402,13 @@ module.exports = React.createClass({
var value = ev.target.value;
Modal.createDialog(QuestionDialog, {
- title: "Privacy warning",
+ title: _t('Privacy warning'),
description:
- Changes to who can read history will only apply to future messages in this room.
- The visibility of existing history will be unchanged.
+ { _t('Changes to who can read history will only apply to future messages in this room') }.
+ { _t('The visibility of existing history will be unchanged') }.
,
- button: "Continue",
+ button: _t('Continue'),
onFinished: function(confirmed) {
if (confirmed) {
self.setState({
@@ -523,11 +525,12 @@ module.exports = React.createClass({
MatrixClientPeg.get().forget(this.props.room.roomId).done(function() {
dis.dispatch({ action: 'view_next_room' });
}, function(err) {
- var errCode = err.errcode || "unknown error code";
+ var errCode = err.errcode || _t('unknown error code');
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
- title: "Error",
- description: `Failed to forget room (${errCode})`
+ title: _t('Error'),
+ description: _t("Failed to forget room %(errCode)s", { errCode: errCode }),
+ button: _t("OK"),
});
});
},
@@ -537,16 +540,17 @@ module.exports = React.createClass({
var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createDialog(QuestionDialog, {
- title: "Warning!",
+ title: _t('Warning') + '!',
description: (
-
End-to-end encryption is in beta and may not be reliable.
-
You should not yet trust it to secure data.
-
Devices will not yet be able to decrypt history from before they joined the room.
-
Once encryption is enabled for a room it cannot be turned off again (for now).
-
Encrypted messages will not be visible on clients that do not yet implement encryption.
+
{ _t('End-to-end encryption is in beta and may not be reliable') }.
+
{ _t('You should not yet trust it to secure data') }.
+
{ _t('Devices will not yet be able to decrypt history from before they joined the room') }.
+
{ _t('Once encryption is enabled for a room it cannot be turned off again (for now)') }.
+
{ _t('Encrypted messages will not be visible on clients that do not yet implement encryption') }.
),
+ button: "OK",
onFinished: confirm=>{
if (!confirm) {
this.refs.encrypt.checked = false;
@@ -572,7 +576,7 @@ module.exports = React.createClass({
- Never send encrypted messages to unverified devices in this room from this device.
+ { _t('Never send encrypted messages to unverified devices in this room from this device') }.
;
if (!isEncrypted &&
@@ -582,7 +586,7 @@ module.exports = React.createClass({
- Enable encryption (warning: cannot be disabled again!)
+ { _t('Enable encryption') } { _t('(warning: cannot be disabled again!)') }
{ settings }
@@ -596,7 +600,7 @@ module.exports = React.createClass({
?
:
}
- Encryption is { isEncrypted ? "" : "not " } enabled in this room.
+ { isEncrypted ? "Encryption is enabled in this room" : "Encryption is not enabled in this room" }.
{ settings }
@@ -647,12 +651,12 @@ module.exports = React.createClass({
if (Object.keys(user_levels).length) {
userLevelsSection =
-
Privileged Users
+
{ _t('Privileged Users') }
{Object.keys(user_levels).map(function(user, i) {
return (
- { user } is a
+ { user } { _t('is a') }
);
})}
@@ -660,7 +664,7 @@ module.exports = React.createClass({
;
}
else {
- userLevelsSection =
No users have specific privileges in this room.
;
+ userLevelsSection =
{ _t('No users have specific privileges in this room') }.
;
}
var banned = this.props.room.getMembersWithMembership("ban");
@@ -668,7 +672,7 @@ module.exports = React.createClass({
if (banned.length) {
bannedUsersSection =
-
Banned users
+
{ _t('Banned users') }
{banned.map(function(member) {
return (
@@ -683,7 +687,7 @@ module.exports = React.createClass({
if (this._yankValueFromEvent("m.room.create", "m.federate") === false) {
unfederatableSection = (
- Ths room is not accessible by remote Matrix servers.
+ { _t('This room is not accessible by remote Matrix servers') }.
);
}
@@ -694,14 +698,14 @@ module.exports = React.createClass({
if (myMember.membership === "join") {
leaveButton = (
- Leave room
+ { _t('Leave room') }
);
}
else if (myMember.membership === "leave") {
leaveButton = (
- Forget room
+ { _t('Forget room') }
);
}
@@ -711,8 +715,8 @@ module.exports = React.createClass({
// TODO: support editing custom user_levels
var tags = [
- { name: "m.favourite", label: "Favourite", ref: "tag_favourite" },
- { name: "m.lowpriority", label: "Low priority", ref: "tag_lowpriority" },
+ { name: "m.favourite", label: _t('Favourite'), ref: "tag_favourite" },
+ { name: "m.lowpriority", label: _t('Low priority'), ref: "tag_lowpriority" },
];
Object.keys(this.state.tags).sort().forEach(function(tagName) {
@@ -753,7 +757,7 @@ module.exports = React.createClass({
if (this.state.join_rule === "public" && aliasCount == 0) {
addressWarning =
;
}
@@ -761,10 +765,10 @@ module.exports = React.createClass({
if (this.state.join_rule !== "public" && this.state.guest_access === "forbidden") {
inviteGuestWarning =
;
}
@@ -776,7 +780,7 @@ module.exports = React.createClass({
console.error(this.state.scalar_error);
integrationsError = (
- Could not connect to the integration server
+ { _t('Could not connect to the integration server') }
);
}
@@ -784,7 +788,7 @@ module.exports = React.createClass({
if (this.scalarClient.hasCredentials()) {
integrationsButton = (
- Manage Integrations
+ { _t('Manage Integrations') }
);
} else if (this.state.scalar_error) {
@@ -797,7 +801,7 @@ module.exports = React.createClass({
} else {
integrationsButton = (
- Manage Integrations
+ { _t('Manage Integrations') }
);
}
@@ -813,28 +817,28 @@ module.exports = React.createClass({
-
Room Colour
+ { _t('Room Colour') }
@@ -899,41 +903,41 @@ module.exports = React.createClass({
- Permissions
+ { _t('Permissions') }
-
The default role for new room members is
+
{ _t('The default role for new room members is') }
-
To send messages, you must be a
+
{ _t('To send messages') }, { _t('you must be a') }
-
To invite users into the room, you must be a
+
{ _t('To invite users into the room') }, { _t('you must be a') }
-
To configure the room, you must be a
+
{ _t('To configure the room') }, { _t('you must be a') }
-
To kick users, you must be a
+
{ _t('To kick users') }, { _t('you must be a') }
-
To ban users, you must be a
+
{ _t('To ban users') }, { _t('you must be a') }
-
To redact other users' messages, you must be a
+
{ _t('To redact other users\' messages') }, { _t('you must be a') }
{Object.keys(events_levels).map(function(event_type, i) {
return (
-
To send events of type { event_type }
, you must be a
+
{ _t('To send events of type') } { event_type }
, { _t('you must be a') }
);
@@ -946,9 +950,9 @@ module.exports = React.createClass({
{ bannedUsersSection }
-
Advanced
+
{ _t('Advanced') }
- This room's internal ID is { this.props.room.roomId }
+ { _t('This room\'s internal ID is') } { this.props.room.roomId }
);
diff --git a/src/components/views/rooms/SearchableEntityList.js b/src/components/views/rooms/SearchableEntityList.js
index 50169edad5..1c3ddea705 100644
--- a/src/components/views/rooms/SearchableEntityList.js
+++ b/src/components/views/rooms/SearchableEntityList.js
@@ -17,6 +17,7 @@ var React = require('react');
var MatrixClientPeg = require("../../../MatrixClientPeg");
var Modal = require("../../../Modal");
var sdk = require("../../../index");
+import _t from 'counterpart-riot';
var GeminiScrollbar = require('react-gemini-scrollbar');
// A list capable of displaying entities which conform to the SearchableEntity
@@ -25,7 +26,6 @@ var SearchableEntityList = React.createClass({
displayName: 'SearchableEntityList',
propTypes: {
- searchPlaceholderText: React.PropTypes.string,
emptyQueryShowsAll: React.PropTypes.bool,
showInputBox: React.PropTypes.bool,
onQueryChanged: React.PropTypes.func, // fn(inputText)
@@ -37,7 +37,6 @@ var SearchableEntityList = React.createClass({
getDefaultProps: function() {
return {
showInputBox: true,
- searchPlaceholderText: "Search",
entities: [],
emptyQueryShowsAll: false,
onSubmit: function() {},
@@ -118,7 +117,9 @@ var SearchableEntityList = React.createClass({
_createOverflowEntity: function(overflowCount, totalCount) {
var EntityTile = sdk.getComponent("rooms.EntityTile");
var BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
- var text = "and " + overflowCount + " other" + (overflowCount > 1 ? "s" : "") + "...";
+ var text = (overflowCount > 1)
+ ? _t("and %(overflowCount)s others...", { overflowCount: overflowCount })
+ : _t("and one other...");
return (
@@ -137,7 +138,7 @@ var SearchableEntityList = React.createClass({
onChange={this.onQueryChanged} value={this.state.query}
onFocus= {() => { this.setState({ focused: true }); }}
onBlur= {() => { this.setState({ focused: false }); }}
- placeholder={this.props.searchPlaceholderText} />
+ placeholder={ _t("Search") } />
);
}
diff --git a/src/components/views/rooms/TopUnreadMessagesBar.js b/src/components/views/rooms/TopUnreadMessagesBar.js
index 74c689c395..0d68b0dc0d 100644
--- a/src/components/views/rooms/TopUnreadMessagesBar.js
+++ b/src/components/views/rooms/TopUnreadMessagesBar.js
@@ -18,6 +18,7 @@ limitations under the License.
'use strict';
var React = require('react');
+import _t from 'counterpart-riot';
var sdk = require('../../../index');
module.exports = React.createClass({
@@ -34,8 +35,8 @@ module.exports = React.createClass({
+ alt={ _t('Scroll to unread messages') }
+ title={ _t('Scroll to unread messages') }/>
Jump to first unread message.
{
if (this._unmounted) return;
@@ -98,20 +99,19 @@ export default WithMatrixClient(React.createClass({
if (this._unmounted) return;
const TextInputDialog = sdk.getComponent("dialogs.TextInputDialog");
let msgElements = [
- A text message has been sent to +{msisdn}.
- Please enter the verification code it contains
+ { _t("A text message has been sent to +%(msisdn)s. Please enter the verification code it contains", { msisdn: msisdn} ) }
];
if (err) {
let msg = err.error;
if (err.errcode == 'M_THREEPID_AUTH_FAILED') {
- msg = "Incorrect verification code";
+ msg = _t("Incorrect verification code");
}
msgElements.push({msg}
);
}
Modal.createDialog(TextInputDialog, {
- title: "Enter Code",
+ title: _t("Enter Code"),
description: {msgElements}
,
- button: "Submit",
+ button: _t("Submit"),
onFinished: (should_verify, token) => {
if (!should_verify) {
this._addThreepid = null;
@@ -159,7 +159,7 @@ export default WithMatrixClient(React.createClass({
diff --git a/src/components/views/settings/ChangePassword.js b/src/components/views/settings/ChangePassword.js
index 20ce45e5dd..71356fc5f6 100644
--- a/src/components/views/settings/ChangePassword.js
+++ b/src/components/views/settings/ChangePassword.js
@@ -21,6 +21,7 @@ var MatrixClientPeg = require("../../../MatrixClientPeg");
var Modal = require("../../../Modal");
var sdk = require("../../../index");
import AccessibleButton from '../elements/AccessibleButton';
+import _t from 'counterpart-riot';
module.exports = React.createClass({
displayName: 'ChangePassword',
@@ -47,11 +48,11 @@ module.exports = React.createClass({
onCheckPassword: function(oldPass, newPass, confirmPass) {
if (newPass !== confirmPass) {
return {
- error: "New passwords don't match."
+ error: _t("New passwords don't match") + "."
};
} else if (!newPass || newPass.length === 0) {
return {
- error: "Passwords can't be empty"
+ error: _t("Passwords can't be empty")
};
}
}
@@ -69,19 +70,21 @@ module.exports = React.createClass({
var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createDialog(QuestionDialog, {
- title: "Warning",
+ title: _t("Warning"),
description:
- Changing password will currently reset any end-to-end encryption keys on all devices,
- making encrypted chat history unreadable, unless you first export your room keys
- and re-import them afterwards.
- In future this
will be improved .
+ { _t(
+ 'Changing password will currently reset any end-to-end encryption keys on all devices, ' +
+ 'making encrypted chat history unreadable, unless you first export your room keys ' +
+ 'and re-import them afterwards. ' +
+ 'In future this will be improved. '
+ ) } (
https://github.com/vector-im/riot-web/issues/2671 )
,
- button: "Continue",
+ button: _t("Continue"),
extraButtons: [
- Export E2E room keys
+ { _t('Export E2E room keys') }
],
onFinished: (confirmed) => {
@@ -150,7 +153,7 @@ module.exports = React.createClass({
- Current password
+ { _t('Current password') }
@@ -158,7 +161,7 @@ module.exports = React.createClass({
- New password
+ { _t('New password') }
@@ -166,7 +169,7 @@ module.exports = React.createClass({
);
diff --git a/src/components/views/settings/DevicesPanelEntry.js b/src/components/views/settings/DevicesPanelEntry.js
index 51f3a83010..c076c5f670 100644
--- a/src/components/views/settings/DevicesPanelEntry.js
+++ b/src/components/views/settings/DevicesPanelEntry.js
@@ -17,6 +17,7 @@ limitations under the License.
import React from 'react';
import sdk from '../../../index';
+import _t from 'counterpart-riot';
import MatrixClientPeg from '../../../MatrixClientPeg';
import Modal from '../../../Modal';
import DateUtils from '../../../DateUtils';
@@ -48,7 +49,7 @@ export default class DevicesPanelEntry extends React.Component {
display_name: value,
}).catch((e) => {
console.error("Error setting device display name", e);
- throw new Error("Failed to set display name");
+ throw new Error(_t("Failed to set display name"));
});
}
@@ -71,6 +72,7 @@ export default class DevicesPanelEntry extends React.Component {
var InteractiveAuthDialog = sdk.getComponent("dialogs.InteractiveAuthDialog");
Modal.createDialog(InteractiveAuthDialog, {
+ title: _t("Authentication"),
matrixClient: MatrixClientPeg.get(),
authData: error.data,
makeRequest: this._makeDeleteRequest,
@@ -84,7 +86,7 @@ export default class DevicesPanelEntry extends React.Component {
if (this._unmounted) { return; }
this.setState({
deleting: false,
- deleteError: "Failed to delete device",
+ deleteError: _t("Failed to delete device"),
});
}).done();
}
@@ -132,7 +134,7 @@ export default class DevicesPanelEntry extends React.Component {
deleteButton = (
- Delete
+ { _t("Delete") }
);
}
diff --git a/src/createRoom.js b/src/createRoom.js
index 674fe23d28..78e7485237 100644
--- a/src/createRoom.js
+++ b/src/createRoom.js
@@ -17,6 +17,7 @@ limitations under the License.
var MatrixClientPeg = require('./MatrixClientPeg');
var Modal = require('./Modal');
var sdk = require('./index');
+import _t from 'counterpart-riot';
var dis = require("./dispatcher");
var Rooms = require("./Rooms");
@@ -43,8 +44,8 @@ function createRoom(opts) {
if (client.isGuest()) {
setTimeout(()=>{
Modal.createDialog(NeedToRegisterDialog, {
- title: "Please Register",
- description: "Guest users can't create new rooms. Please register to create room and start a chat."
+ title: _t('Please Register'),
+ description: _t('Guest users can\'t create new rooms. Please register to create room and start a chat') + '.'
});
}, 0);
return q(null);
@@ -104,8 +105,9 @@ function createRoom(opts) {
}, function(err) {
console.error("Failed to create room " + roomId + " " + err);
Modal.createDialog(ErrorDialog, {
- title: "Failure to create room",
- description: "Server may be unavailable, overloaded, or you hit a bug.",
+ title: _t("Failure to create room"),
+ description: _t("Server may be unavailable, overloaded, or you hit a bug") + ".",
+ button: _t("OK"),
});
return null;
});
diff --git a/src/i18n/strings/basefile.json b/src/i18n/strings/basefile.json
new file mode 100644
index 0000000000..9e26dfeeb6
--- /dev/null
+++ b/src/i18n/strings/basefile.json
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/src/i18n/strings/da.json b/src/i18n/strings/da.json
new file mode 100644
index 0000000000..26889c3bb6
--- /dev/null
+++ b/src/i18n/strings/da.json
@@ -0,0 +1,215 @@
+{
+ "Filter room members": "Filter medlemmer",
+ "You have no visible notifications": "Du har ingen synlige meddelelser",
+ "Invites": "Invitationer",
+ "Favourites": "Favoritter",
+ "People": "Personilg chat",
+ "Rooms": "Rum",
+ "Low priority": "Lav prioritet",
+ "Historical": "Historisk",
+ "New passwords must match each other.": "Nye adgangskoder skal matche hinanden.",
+ "A new password must be entered.": "Der skal indtastes en ny adgangskode.",
+ "The email address linked to your account must be entered.": "Den emailadresse, der tilhører til din adgang, skal indtastes.",
+ "Failed to send email: ": "Kunne ikke sende e-mail: ",
+ "unknown device": "ukendt enhed",
+ "NOT verified": "IKKE verificeret",
+ "Blacklisted": "blokeret",
+ "verified": "verificeret",
+ "Name": "Navn",
+ "Device ID": "Enheds-ID",
+ "Verification": "Verifikation",
+ "Ed25519 fingerprint": "Ed25519 fingerprint",
+ "User ID": "Bruger ID",
+ "Curve25519 identity key": "Curve25519 identitetsnøgle",
+ "Claimed Ed25519 fingerprint key": "Påstået Ed25519 fingeraftryk nøgle",
+ "none": "ingen",
+ "Algorithm": "Algoritme",
+ "unencrypted": "ukrypteret",
+ "Decryption error": "Dekrypteringsfejl",
+ "Session ID": "Sessions ID",
+ "End-to-end encryption information": "End-to-end krypterings oplysninger",
+ "Event information": "Hændelses information",
+ "Sender device information": "Afsende enheds-oplysning",
+ "Displays action": "Viser handling",
+ "Bans user with given id": "Forbyder bruger med givet id",
+ "Deops user with given id": "Fjerner OP af bruger med givet id",
+ "Invites user with given id to current room": "Inviterer bruger med givet id til nuværende rum",
+ "Joins room with given alias": "Kommer ind i rum med givet alias",
+ "Kicks user with given id": "Smid bruger med givet id ud",
+ "Changes your display nickname": "Ændrer dit visningsnavn",
+ "Searches DuckDuckGo for results": "Søger DuckDuckGo for resultater",
+ "Commands": "kommandoer",
+ "Emoji": "Emoji",
+ "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar.": "Kan ikke oprette forbindelse til hjemmeserver via HTTP, når en HTTPS-URL er i din browserbjælke.",
+ "Sorry, this homeserver is using a login which is not recognised ": "Beklager, denne homeerver bruger et login, der ikke kan genkendes ",
+ "Login as guest": "Log ind som gæst",
+ "Return to app": "Tilbage til app",
+ "Sign in": "Log ind",
+ "Create a new account": "Oprette en ny bruger",
+ "Send an encrypted message": "Send en krypteret meddelelse",
+ "Send a message (unencrypted)": "Send en meddelelse (ukrypteret)",
+ "Warning": "Advarsel",
+ "accept": "acceptere",
+ "accepted an invitation": "Godkendt en invitation",
+ "accepted the invitation for": "Accepteret invitationen til",
+ "Account": "Konto",
+ "Add email address": "Tilføj e-mail-adresse",
+ "Add phone number": "Tilføj telefonnummer",
+ "Admin": "Administrator",
+ "Advanced": "Avanceret",
+ "all room members": "Alle rum medlemmer",
+ "all room members, from the point they are invited": "Alle rum medlemmer, siden invitations-tidspunkt",
+ "all room members, from the point they joined": "Alle rum medlemmer, siden de deltog",
+ "an address": "en adresse",
+ "and": "og",
+ "An email has been sent to": "En e-mail blev sendt til",
+ "answered the call.": "svarede på kaldet",
+ "anyone.": "alle",
+ "Anyone who knows the room's link, apart from guests": "Alle der kender link til rummet, bortset fra gæster",
+ "Anyone who knows the room's link, including guests": "Alle der kender link til rummet, inklusiv gæster",
+ "Are you sure you want to leave the room?": "Er du sikker på du vil forlade rummet?",
+ "Are you sure you want to reject the invitation?": "Er du sikker på du vil afvise invitationen?",
+ "Are you sure you want upload the following files?": "Er du sikker på du vil sende de følgende filer?",
+ "banned": "bortvist",
+ "Banned users": "Bortviste brugere",
+ "Bug Report": "Fejlrapport",
+ "Bulk Options": "Masseindstillinger",
+ "Can't connect to homeserver - please check your connectivity and ensure your": "Kan ikke oprette forbindelse til homeserver - Kontroller din forbindelse og sørg for din ",
+ "Can't load user settings": "Kan ikke indlæse brugerindstillinger",
+ "changed avatar": "Ændret avatar",
+ "changed name": "Ændret navn",
+ "changed their display name from": "Ændret deres visningsnavn fra",
+ "changed their profile picture": "Ændret deres profilbillede",
+ "changed the power level of": "Ændret effektniveauet på",
+ "changed the room name to": "Ændrede rumnavnet til",
+ "changed the topic to": "Ændret emnet til",
+ "Changes to who can read history will only apply to future messages in this room": "Ændringer til hvem der kan læse historie gælder kun for fremtidige meddelelser i dette rum",
+ "Clear Cache and Reload": "Ryd cache og genindlæs",
+ "Clear Cache": "Ryd cache",
+ "Click here": "Klik her",
+ "Click here to fix": "Klik her for at rette",
+ "*️⃣ Commands": "kommandoer",
+ "Confirm your new password": "Bekræft din nye adgangskode",
+ "Continue": "fortsætte",
+ "Could not connect to the integration server": "Kunne ikke oprette forbindelse til integrationsserveren",
+ "Create an account": "Opret en brugerkonto",
+ "Create Room": "Opret rum",
+ "Cryptography": "Kryptografi",
+ "Deactivate Account": "Deaktiver brugerkonto",
+ "Deactivate my account": "Deaktiver min brugerkonto",
+ "decline": "nedgang",
+ "Default": "Standard",
+ "demote": "degradere",
+ "Devices will
not yet be able to decrypt history from before they joined the room": "Enhederne vil
ikke være i stand til at dekryptere historikken fra, før de kom til rummet",
+ "Direct Chat": "Personligt Chat",
+ "Disable inline URL previews by default": "Deaktiver forrige weblinks forhåndsvisninger som standard",
+ "Display name": "Visningsnavn",
+ "Email Address": "Email adresse",
+ "Email, name or matrix ID": "E-mail, navn eller matrix-id",
+ "Encrypted messages will not be visible on clients that do not yet implement encryption": "Krypterede meddelelser vil ikke være synlige på klienter, der endnu ikke implementerer kryptering",
+ "Encrypted room": "Krypteret rummet",
+ "Encryption is enabled in this room": "Kryptering er aktiveret i dette rum",
+ "Encryption is not enabled in this room": "Kryptering er ikke aktiveret i dette rum",
+ "ended the call.": "Afsluttede opkaldet.",
+ "End-to-end encryption is in beta and may not be reliable": "End-to-end kryptering er i beta og kan ikke være pålidelig",
+ "Error": "Fejl",
+ "Export E2E room keys": "Eksporter E2E rum nøgler",
+ "Failed to change password. Is your password correct?": "Kunne ikke ændre adgangskode. Er din adgangskode korrekt?",
+ "Failed to forget room": "Kunne ikke glemme rummet",
+ "Failed to leave room": "Kunne ikke forlade rum",
+ "Failed to reject invitation": "Kunne ikke afvise invitationen",
+ "Failed to send email": "Kunne ikke sende e-mail",
+ "Failed to set avatar.": "Kunne ikke angive avatar.",
+ "Failed to unban": "Var ikke i stand til at ophæve forbuddet",
+ "Favourite": "Favorit",
+ "Notifications": "Meddelser",
+ "Please Register": "Vær venlig at registrere",
+ "Remove": "Fjerne",
+ "Settings": "Indstillinger",
+ "unknown error code": "Ukendt fejlkode",
+ "en": "Engelsk",
+ "pt-br": "Brasiliansk Portugisisk",
+ "de": "Tysk",
+ "da": "Dansk",
+ "ru": "Russisk",
+ "%(targetName)s accepted an invitation": "%(targetName)s accepterede en invitation",
+ "%(targetName)s accepted the invitation for %(displayName)s": "%(targetName)s accepteret invitationen til %(displayName)s",
+ "%(names)s and %(lastPerson)s are typing": "%(names)s og %(lastPerson)s er ved at skrive",
+ "%(names)s and one other are typing": "%(names)s og den anden skriver",
+ "%(names)s and %(count)s others are typing": "%(names)s og %(count)s andre skriver",
+ "%(senderName)s answered the call": "%(senderName)s besvarede opkaldet",
+ "af": "Afrikaans",
+ "ar-eg": "Arabisk (Egypten)",
+ "ar-ma": "Arabisk (Marokko)",
+ "ar-sa": "Arabisk (Saudiarabien",
+ "ar-sy": "Arabisk (Syrien)",
+ "be": "Hviderussisk",
+ "bg": "Bulgarisk",
+ "ca": "Katalansk",
+ "cs": "Tjekkisk",
+ "de-at": "Tysk (Østrig)",
+ "de-ch": "Tysk (Schweitz)",
+ "el": "Græsk",
+ "en-au": "Engelsk (Australien)",
+ "en-ca": "Engelsk (Canada)",
+ "en-ie": "Engelsk (Irland)",
+ "en-nz": "Engelsk (New Zealand)",
+ "en-us": "Engelsk (USA)",
+ "en-za": "Engelsk (Sydafrika)",
+ "es-ar": "Spansk (Argentina)",
+ "es-bo": "Spansk (Bolivia)",
+ "es-cl": "Spansk (Chile)",
+ "es-ec": "Spansk (Ecuador)",
+ "es-hn": "Spansk (Honduras)",
+ "es-mx": "Spansk (Mexico)",
+ "es-ni": "Spansk (Nicaragua)",
+ "es-py": "Spansk (Paraguay)",
+ "es": "Spansk (Spanien)",
+ "es-uy": "Spansk (Uruguay)",
+ "es-ve": "Spansk (Venezuela)",
+ "et": "Estonsk",
+ "fa": "Farsi",
+ "fi": "Finsk",
+ "fr-be": "Fransk (Belgien)",
+ "fr-ca": "Fransk (Canada)",
+ "fr-ch": "Fransk (Schweitz)",
+ "fr": "French",
+ "ga": "Irsk",
+ "he": "Hebræisk",
+ "hi": "Hindi",
+ "hr": "Kroatisk",
+ "hu": "Ungarsk",
+ "id": "Indonesisk",
+ "is": "Islandsk",
+ "it": "Italian",
+ "ja": "Japansk",
+ "ji": "Yiddish",
+ "lt": "Littauisk",
+ "lv": "Lettisk",
+ "ms": "Malaysisk",
+ "mt": "Maltesisk",
+ "nl": "Dutch",
+ "no": "Norsk",
+ "pl": "Polsk",
+ "pt": "Portuguese",
+ "ro": "Rumænsk",
+ "sb": "Sorbisk",
+ "sk": "Slovakisk",
+ "sl": "Slovensk",
+ "sq": "Albansk",
+ "sr": "Serbisk (Latin)",
+ "sv": "Svensk",
+ "th": "Thai",
+ "tn": "Tswana",
+ "tr": "Tyrkisk",
+ "ts": "Tonga",
+ "uk": "Ukrainsk",
+ "ur": "Urdu",
+ "ve": "Venda",
+ "vi": "Vietnamesisk",
+ "xh": "Xhosa",
+ "zh-cn": "Kinesisk (Folkerepublikken Kina)",
+ "zh-sg": "Kinesisk (Singapore)",
+ "zh-tw": "Kinesisk (Taiwan)",
+ "zu": "Zulu"
+}
diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json
new file mode 100644
index 0000000000..1c6b3eb5f3
--- /dev/null
+++ b/src/i18n/strings/de_DE.json
@@ -0,0 +1,702 @@
+{
+ "Filter room members": "Raum Benutzer filtern",
+ "You have no visible notifications": "Du hast keine sichtbaren Benachrichtigungen",
+ "Invites": "Einladungen",
+ "Favourites": "Favoriten",
+ "People": "Direkt-Chats",
+ "Rooms": "Räume",
+ "Low priority": "Niedrige Priorität",
+ "Historical": "Historisch",
+ "New passwords must match each other.": "Die neuen Passwörter müssen identisch sein.",
+ "A new password must be entered.": "Es muss ein neues Passwort eingegeben werden.",
+ "The email address linked to your account must be entered.": "Es muss die Email-Adresse eingeben werden, welche zum Account gehört.",
+ "Failed to send email: ": "Email konnte nicht versendet werden: ",
+ "unknown device": "Unbekanntes Gerät",
+ "NOT verified": "NICHT verifiziert",
+ "Blacklisted": "Blockiert",
+ "verified": "verifiziert",
+ "Name": "Name",
+ "Device ID": "Geräte ID",
+ "Verification": "Verifizierung",
+ "Ed25519 fingerprint": "Ed25519 Fingerprint",
+ "User ID": "Benutzer ID",
+ "Curve25519 identity key": "Curve25519 Identity Schlüssel",
+ "Claimed Ed25519 fingerprint key": "Geforderter Ed25519 Fingerprint Schlüssel",
+ "none": "keiner",
+ "Algorithm": "Algorithmus",
+ "unencrypted": "unverschlüsselt",
+ "Decryption error": "Entschlüsselungs Fehler",
+ "Session ID": "Sitzungs-ID",
+ "End-to-end encryption information": "Ende-zu-Ende Verschlüsselungs Informationen",
+ "Event information": "Ereignis Informationen",
+ "Sender device information": "Absender Geräte Informationen",
+ "Displays action": "Zeigt Aktionen an",
+ "Bans user with given id": "Sperrt Benutzer mit der angegebenen ID",
+ "Deops user with given id": "Entfernt OP beim Benutzer mit der angegebenen ID",
+ "Invites user with given id to current room": "Lädt Benutzer mit der angegebenen ID in den aktuellen Raum ein",
+ "Joins room with given alias": "Betrete Raum mit angegebenen Alias",
+ "Kicks user with given id": "Kickt Benutzer mit angegebener ID",
+ "Changes your display nickname": "Ändert deinen angezeigten Nicknamen",
+ "Searches DuckDuckGo for results": "Suche in DuckDuckGo nach Ergebnissen",
+ "Commands": "Kommandos",
+ "Emoji": "Smileys",
+ "Sorry, this homeserver is using a login which is not recognised ": "Entschuldigung, dieser homeserver nutzt eine Anmeldetechnik die nicht bekannt ist ",
+ "Login as guest": "Anmelden als Gast",
+ "Return to app": "Zurück zur Anwendung",
+ "Sign in": "Anmelden",
+ "Create a new account": "Erstelle einen neuen Benutzer",
+ "Send an encrypted message": "Sende eine verschlüsselte Nachricht",
+ "Send a message (unencrypted)": "Sende eine Nachricht (unverschlüsselt)",
+ "Warning": "Warnung",
+ "Direct Chat": "Privater Chat",
+ "Error": "Fehler",
+ "accept": "akzeptiere",
+ "accepted an invitation": "Einladung akzeptieren",
+ "accepted the invitation for": "Akzeptierte die Einladung für",
+ "Add email address": "Füge E-Mail-Adresse hinzu",
+ "Advanced": "Erweitert",
+ "all room members, from the point they joined": "Alle Raum-Mitglieder - seitdem sie beigetreten sind",
+ "and": "und",
+ "An email has been sent to": "Eine E-Mail wurde gesendet an",
+ "anyone.": "Jeder",
+ "Anyone who knows the room's link, apart from guests": "Jeder der den Raum-Link kennt - abgesehen von Gästen",
+ "Anyone who knows the room's link, including guests": "Jeder der den Raum-Link kennt - auch Gäste",
+ "Are you sure you want to leave the room?": "Bist du sicher, dass du den Raum verlassen willst?",
+ "Are you sure you want to reject the invitation?": "Bist du sicher, dass die die Einladung ablehnen willst?",
+ "Are you sure you want upload the following files?": "Bist du sicher, dass du die folgenden Dateien hochladen willst?",
+ "banned": "gebannt",
+ "Banned users": "Gebannte Nutzer",
+ "Bug Report": "Fehlerbericht",
+ "changed avatar": "änderte Avatar",
+ "changed their display name from": "änderte seinen Anzeigenamen von",
+ "changed their profile picture": "änderte sein Profilbild",
+ "changed the room name to": "änderte den Raumnamen zu",
+ "changed the topic to": "änderte das Thema zu",
+ "Changes to who can read history will only apply to future messages in this room": "Änderungen bzgl. wer die Historie lesen kann betrifft nur zukünftige Nachrichten in diesem Raum",
+ "Clear Cache and Reload": "Leere Cache und lade neu",
+ "Click here": "Klicke hier",
+ "Confirm your new password": "Bestätige dein neues Passwort",
+ "Continue": "Fortfahren",
+ "Create an account": "Erstelle einen Account",
+ "Create Room": "Erstelle Raum",
+ "Cryptography": "Kryptografie",
+ "Deactivate Account": "Deaktiviere Account",
+ "Deactivate my account": "Deaktiviere meinen Account",
+ "decline": "Ablehnen",
+ "Devices will
not yet be able to decrypt history from before they joined the room": "Geräte werden
nicht in der Lage sein, die Historie vor dem Beitritt in den Raum zu entschlüsseln",
+ "Display name": "Anzeigename",
+ "Email Address": "E-Mail-Adresse",
+ "Email, name or matrix ID": "E-Mail, Name oder Matrix-ID",
+ "Encrypted messages will not be visible on clients that do not yet implement encryption": "Verschlüsselte Nachrichten werden an Clients nicht sichtbar sein, die Verschlüsselung noch nicht implementiert haben",
+ "Encrypted room": "Verschlüsselter Raum",
+ "Encryption is enabled in this room": "Verschlüsselung ist in diesem Raum aktiviert",
+ "Encryption is not enabled in this room": "Verschlüsselung ist in diesem Raum nicht aktiviert",
+ "ended the call.": "beendete den Anruf.",
+ "End-to-end encryption is in beta and may not be reliable": "Ende-zu-Ende-Verschlüsselung ist im Beta-Status und ist evtl. nicht zuverlässig",
+ "Failed to send email": "Fehler beim Senden der E-Mail",
+ "Account": "Konto",
+ "Add phone number": "Füge Telefonnummer hinzu",
+ "an address": "an Adresse",
+ "Your password was successfully changed. You will not receive push notifications on other devices until you log back in to them": "Dein Passwort wurde erfolgreich geändert. Du wirst keine Benachrichtigungen an anderen Geräten empfangen bis du dich dort erneut anmeldest",
+ "all room members": "Alle Raum-Mitglieder",
+ "all room members, from the point they are invited": "Alle Raum-Mitglieder - seitdem sie eingeladen wurden",
+ "answered the call.": "beantwortete den Anruf.",
+ "Can't load user settings": "Kann Nutzereinstellungen nicht laden",
+ "changed name": "änderte Namen",
+ "changed the power level of": "änderte Berechtigungslevel von",
+ "Clear Cache": "Leere Cache",
+ "Click here to fix": "Klicke hier zum reparieren",
+ "*️⃣ Commands": "*️⃣ Befehle",
+ "Default": "Standard",
+ "demote": "Zum zurückstufen",
+ "Export E2E room keys": "Exportiere E2E-Raum-Schlüssel",
+ "Failed to change password. Is your password correct?": "Passwort-Änderung schlug fehl. Ist dein Passwort korrekt?",
+ "Failed to forget room": "Vergessen des Raums schlug fehl",
+ "Failed to leave room": "Fehler beim Verlassen des Raums",
+ "Failed to reject invitation": "Fehler beim Abweisen der Einladung",
+ "Failed to set avatar.": "Fehler beim Setzen des Avatars.",
+ "Failed to unban": "Entbannen fehlgeschlagen",
+ "Failed to upload file": "Dateiupload fehlgeschlagen",
+ "Favourite": "Favorit",
+ "favourite": "Favoriten",
+ "Forget room": "Raum vergessen",
+ "Forgot your password?": "Passwort vergessen?",
+ "For security, logging out will delete any end-to-end encryption keys from this browser. If you want to be able to decrypt your conversation history from future Riot sessions, please export your room keys for safe-keeping.": "Aus Sicherheitsgründen werden beim Ausloggen alle Ende-zu-Ende-Verschlüsselungsschlüssel von diesem Browser gelöscht. Wenn du in späteren Riot-Sitzungen die Konversationshistorie entschlüsseln möchtest, exportiere bitte deine Schlüssel zur sicheren Verwahrung.",
+ "For security, this session has been signed out. Please sign in again": "Zur Sicherheit wurde diese Sitzung abgemeldet. Bitte melde dich erneut an",
+ "Found a bug?": "Fehler gefunden?",
+ "Guests cannot join this room even if explicitly invited": "Gäste können diesem Raum nicht beitreten auch wenn sie explizit eingeladen werden",
+ "Guests can't set avatars. Please register.": "Gäste können keine Avatare setzen. Bitte registriere dich.",
+ "Guest users can't upload files. Please register to upload.": "Gäste können keine Dateien hochladen. Bitte registrieren um hochzuladen.",
+ "had": "hatte",
+ "Hangup": "Auflegen",
+ "Homeserver is": "Der Homeserver ist",
+ "Identity Server is": "Der Identitätsserver ist",
+ "I have verified my email address": "Ich habe meine E-Mail-Adresse verifiziert",
+ "Import E2E room keys": "Importe E2E-Raum-Schlüssel",
+ "Invalid Email Address": "Ungültige E-Mail-Adresse",
+ "invited": "eingeladen",
+ "Invite new room members": "Lade neue Raum-Mitglieder ein",
+ "is a": "ist ein",
+ "is trusted": "wird vertraut",
+ "I want to sign in with": "Ich möchte mich anmelden mit",
+ "joined and left": "trat bei und ging",
+ "joined": "trat bei",
+ "joined the room": "trat dem Raum bei",
+ "Leave room": "Verlasse Raum",
+ "left and rejoined": "ging(en) und trat(en) erneut bei",
+ "left": "ging",
+ "left the room": "verließ den Raum",
+ "Logged in as": "Angemeldet als",
+ "Logout": "Abmelden",
+ "made future room history visible to": "mache kommende Raum-Historie sichtbar für",
+ "Manage Integrations": "Verwalte Integrationen",
+ "Members only": "Nur Mitglieder",
+ "Mobile phone number": "Mobile Telefonnummer",
+ "Moderator": "Moderator",
+ "my Matrix ID": "Meine Matrix-ID",
+ "Never send encrypted messages to unverified devices from this device": "Niemals verschlüsselte Nachrichten an unverifizierte Geräte von diesem Gerät aus versenden",
+ "Never send encrypted messages to unverified devices in this room from this device": "Niemals verschlüsselte Nachrichten an unverifizierte Geräte in diesem Raum von diesem Gerät aus senden",
+ "New password": "Neues Passwort",
+ "Notifications": "Benachrichtigungen",
+ " (not supported by this browser)": " (von diesem Browser nicht unterstützt)",
+ "
": "",
+ "No users have specific privileges in this room": "Keine Nutzer haben in diesem Raum besondere Berechtigungen",
+ "olm version": "OLM-Version",
+ "Once encryption is enabled for a room it cannot be turned off again (for now)": "Sobald Verschlüsselung für einen Raum aktiviert wird, kann diese (aktuell noch) nicht wieder deaktiviert werden",
+ "Only people who have been invited": "Nur Personen die eingeladen wurden",
+ "or": "oder",
+ "other": "weiteres",
+ "others": "andere",
+ "Password": "Passwort",
+ "Permissions": "Berechtigungen",
+ "Phone": "Telefon",
+ "placed a": "plazierte einen",
+ "Please check your email and click on the link it contains. Once this is done, click continue.": "Bitte prüfen sie ihre E-Mails und klicken sie auf den enthaltenden Link. Anschließend klicke auf \"Fortsetzen\".",
+ "Please Register": "Bitte registrieren",
+ "Privacy warning": "Datenschutzwarnung",
+ "Privileged Users": "Privilegierte Nutzer",
+ "Profile": "Profil",
+ "Refer a friend to Riot": "Lade eine(n) Freund(in) zu Riot ein",
+ "rejected": "abgeleht",
+ "Once you've followed the link it contains, click below": "Nachdem du dem Link gefolgt bist, klicke unten",
+ "rejected the invitation.": "lehnte die Einladung ab.",
+ "Reject invitation": "Einladung ablehnen",
+ "Remove Contact Information?": "Lösche Kontakt-Informationen?",
+ "removed their display name": "löschte den eigenen Anzeigenamen",
+ "Remove": "Entferne",
+ "requested a VoIP conference": "hat eine VoIP-Konferenz angefordert",
+ "Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved ": "Eine Passwortänderung sorgt aktuell dafür, dass alle Ende-zu-Ende-Schlüssel von allen Geräten zurückgesetzt werden. Dadurch wird die verschlüsselte Chat-Historie unlesbar, es sei denn Sie exportieren vorher Ihre Raum-Schlüssel und importieren sie nachher wieder. In Zukunft wird dies verbessert ",
+ "restore": "Zum zurücksetzen",
+ "Return to login screen": "Zur Anmeldung zurückkehren",
+ "Room Colour": "Raumfarbe",
+ "Room name (optional)": "Raumname (optional)",
+ "Scroll to unread messages": "Scrolle zu ungelesenen Nachrichten",
+ "Send Invites": "Sende Einladungen",
+ "Send Reset Email": "Sende Rücksetz-E-mail",
+ "sent an image": "sandte ein Bild",
+ "sent an invitation to": "sandte eine Einladung an",
+ "sent a video": "sandte ein Video",
+ "Server may be unavailable or overloaded": "Server könnte nicht verfügbar oder überlastet sein",
+ "set a profile picture": "setzte ein Profilbild",
+ "set their display name to": "setzte den Anzeigenamen auf",
+ "Settings": "Einstellungen",
+ "Signed Out": "Abgemeldet",
+ "Sign out": "Abmelden",
+ "since the point in time of selecting this option": "seitdem diese Option gewählt wird",
+ "since they joined": "seitdem sie beitraten",
+ "since they were invited": "seitdem sie eingeladen wurden",
+ "Someone": "Jemand",
+ "Start a chat": "Starte einen Chat",
+ "Start Chat": "Starte Chat",
+ "Success": "Erfolg",
+ "tag direct chat": "Zum kennzeichnen als direkten Chat",
+ "The default role for new room members is": "Die Standard-Rolle for neue Raum-Mitglieder ist",
+ "their invitations": "ihre Einladungen",
+ "their invitation": "ihre Einladung",
+ "These are experimental features that may break in unexpected ways. Use with caution": "Dies sind experimentelle Funktionen die in unerwarteter Weise Fehler verursachen können. Mit Vorsicht benutzen",
+ "The visibility of existing history will be unchanged": "Die Sichtbarkeit der existenten Historie bleibt unverändert",
+ "This doesn't appear to be a valid email address": "Die scheint keine valide E-Mail-Adresse zu sein",
+ "this invitation?": "diese Einladung?",
+ "This is a preview of this room. Room interactions have been disabled": "Dies ist eine Vorschau dieses Raumes. Raum-Interaktionen wurden deaktiviert",
+ "This room is not accessible by remote Matrix servers": "Dieser Raum ist über entfernte Matrix-Server nicht zugreifbar",
+ "This room's internal ID is": "Die interne ID dieses Raumes ist",
+ "To ban users": "Zum Nutzer bannen",
+ "To configure the room": "Zum Raum konfigurieren",
+ "To invite users into the room": "Zum Nutzer in den Raum einladen",
+ "to join the discussion": "Zum mitdiskutieren",
+ "To kick users": "Zum Nutzer kicken",
+ "Admin": "Administrator",
+ "Server may be unavailable, overloaded, or you hit a bug": "Server könnte nicht verfügbar oder überlastet sein oder du bist auf einen Fehler gestoßen",
+ "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar": "Verbindung zum Homeserver ist über HTTP nicht möglich, wenn HTTPS in deiner Browserleiste steht",
+ "Could not connect to the integration server": "Konnte keine Verbindung zum Integrations-Server herstellen",
+ "Disable inline URL previews by default": "Deaktiviere URL-Vorschau im Chat standardmäßig",
+ "Guests can't use labs features. Please register.": "Gäste können keine Labor-Funktionen nutzen. Bitte registrieren.",
+ "Labs": "Labor",
+ "Show panel": "Zeige Panel",
+ "To redact messages": "Zum Nachrichten verbergen",
+ "Can't connect to homeserver - please check your connectivity and ensure your": "Die Verbindung mit dem Homeserver ist fehlgeschlagen. Bitte überprüfe deine Verbindung und stelle sicher, dass dein(e) ",
+ "tag as": "kennzeichne als",
+ "To reset your password, enter the email address linked to your account": "Um dein Passwort zurückzusetzen, gebe deine E-Mail-Adresse, die mit deinem Account verbunden ist, ein",
+ "To send messages": "Zum Nachrichten senden",
+ "turned on end-to-end encryption (algorithm": "aktivierte Ende-zu-Ende-Verschlüsselung (Algorithmus",
+ "Unable to add email address": "Unfähig die E-Mail-Adresse hinzuzufügen",
+ "Unable to remove contact information": "Unfähig die Kontakt-Informationen zu löschen",
+ "Unable to verify email address": "Unfähig die E-Mail-Adresse zu verifizieren",
+ "Unban": "Entbannen",
+ "Unencrypted room": "Unverschlüsselter Raum",
+ "unknown error code": "Unbekannter Fehlercode",
+ "unknown": "unbekannt",
+ "Upload avatar": "Avatar hochladen",
+ "uploaded a file": "lud eine Datei hoch",
+ "Upload Files": "Dateien hochladen",
+ "Upload file": "Datei hochladen",
+ "User Interface": "Nutzerschnittstelle",
+ "username": "Nutzername",
+ "Users": "Nutzer",
+ "User": "Nutzer",
+ "Verification Pending": "Verifizierung ausstehend",
+ "Video call": "Videoanruf",
+ "Voice call": "Sprachanruf",
+ "VoIP conference finished": "VoIP-Konferenz beendet",
+ "VoIP conference started": "VoIP-Konferenz gestartet",
+ "(warning: cannot be disabled again!)": "(Warnung: Kann nicht wieder deaktiviert werden!)",
+ "was banned": "wurde gebannt",
+ "was invited": "wurde eingeladen",
+ "was kicked": "wurde gekickt",
+ "was unbanned": "wurde entbannt",
+ "was": "wurde",
+ "Who can access this room?": "Wer hat Zugang zu diesem Raum?",
+ "Who can read history?": "Wer kann die Historie lesen?",
+ "Who would you like to add to this room?": "Wen möchtest du zu diesem Raum hinzufügen?",
+ "Who would you like to communicate with?": "Mit wem möchtest du kommunizieren?",
+ "Would you like to": "Möchtest du",
+ "You are trying to access": "Du möchtest Zugang zu %(sth)s bekommen",
+ "You do not have permission to post to this room": "Du hast keine Berechtigung an diesen Raum etwas zu senden",
+ "You have been invited to join this room by %(inviterName)s": "Du wurdest in diesen Raum eingeladen von %(inviterName)s",
+ "You have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, sign in again on each device": "Du wurdest von allen Geräten ausgeloggt und wirst keine Push-Benachrichtigungen mehr bekommen. Um Benachrichtigungen zu reaktivieren melde dich auf jedem Gerät neu an",
+ "you must be a": "nötige Rolle",
+ "Your password has been reset": "Dein Passwort wurde zurückgesetzt",
+ "You should not yet trust it to secure data": "Du solltest nicht darauf vertrauen um deine Daten abzusichern",
+ "removed their profile picture": "löschte das eigene Profilbild",
+ "times": "mal",
+ "Bulk Options": "Bulk-Optionen",
+ "Call Timeout": "Anruf-Timeout",
+ "Conference call failed": "Konferenzgespräch fehlgeschlagen",
+ "Conference calling is in development and may not be reliable": "Konferenzgespräche sind in Entwicklung und evtl. nicht zuverlässig",
+ "Conference calls are not supported in encrypted rooms": "Konferenzgespräche sind in verschlüsselten Räumen nicht unterstützt",
+ "Conference calls are not supported in this client": "Konferenzgespräche sind in diesem Client nicht unterstützt",
+ "Existing Call": "Existierender Anruf",
+ "Failed to set up conference call": "Aufbau des Konferenzgesprächs fehlgeschlagen",
+ "Failed to verify email address: make sure you clicked the link in the email": "Verifizierung der E-Mail-Adresse fehlgeschlagen: Stelle sicher, dass du den Link in der E-Mail anklicktest",
+ "Failure to create room": "Raumerstellung fehlgeschlagen",
+ "Guest users can't create new rooms. Please register to create room and start a chat": "Gäste können keine neuen Räume erstellen. Bitte registrieren um einen Raum zu erstellen und einen Chat zu starten",
+ "Riot does not have permission to send you notifications - please check your browser settings": "Riot hat keine Berechtigung Benachrichtigungen zu senden - bitte prüfe deine Browser-Einstellungen",
+ "Riot was not given permission to send notifications - please try again": "Riot hat das Recht nicht bekommen Benachrichtigungen zu senden. Bitte erneut probieren",
+ "This email address is already in use": "Diese E-Mail-Adresse wird bereits verwendet",
+ "This email address was not found": "Diese E-Mail-Adresse wurde nicht gefunden",
+ "The file '%(fileName)s' exceeds this home server's size limit for uploads": "Die Datei '%(fileName)s' überschreitet das Größen-Limit für Uploads auf diesem Homeserver",
+ "The file '%(fileName)s' failed to upload": "Das Hochladen der Datei '%(fileName)s' schlug fehl",
+ "The remote side failed to pick up": "Die Gegenstelle konnte nicht abheben",
+ "This phone number is already in use": "Diese Telefonnummer wird bereits verwendet",
+ "Unable to restore previous session": "Frühere Sitzung nicht wiederherstellbar",
+ "Unable to capture screen": "Unfähig den Bildschirm aufzunehmen",
+ "Unable to enable Notifications": "Unfähig Benachrichtigungen zu aktivieren",
+ "Upload Failed": "Upload fehlgeschlagen",
+ "VoIP is unsupported": "VoIP ist nicht unterstützt",
+ "You are already in a call": "Du bist bereits bei einem Anruf",
+ "You cannot place a call with yourself": "Du kannst keinen Anruf mit dir selbst starten",
+ "You cannot place VoIP calls in this browser": "Du kannst kein VoIP-Gespräch in diesem Browser starten",
+ "You need to log back in to generate end-to-end encryption keys for this device and submit the public key to your homeserver. This is a once off; sorry for the inconvenience": "Du musst dich erneut anmelden um Ende-zu-Ende-Verschlüsselungscodes für dieses Gerät zu generieren und den öffentl. Schlüssel an deinen Homeserver zu senden. Dies muss einmal gemacht werden. Entschuldige die Unannehmlichkeit",
+ "Your email address does not appear to be associated with a Matrix ID on this Homeserver": "Deine E-Mail-Adresse scheint nicht mit einer Matrix-ID auf diesem Homeserver verknüpft zu sein",
+ "Sun": "So",
+ "Mon": "Mo",
+ "Tue": "Di",
+ "Wed": "Mi",
+ "Thu": "Do",
+ "Fri": "Fr",
+ "Sat": "Sa",
+ "Jan": "Jan",
+ "Feb": "Feb",
+ "Mar": "März",
+ "Apr": "April",
+ "May": "Mai",
+ "Jun": "Juni",
+ "Jul": "Juli",
+ "Aug": "Aug",
+ "Sep": "Sep",
+ "Oct": "Okt",
+ "Nov": "Nov",
+ "Dec": "Dez",
+ "%(weekDayName)s, %(monthName)s %(day)s %(time)s": "%(weekDayName)s, %(day)s. %(monthName)s %(time)s",
+ "%(weekDayName)s %(time)s": "%(weekDayName)s %(time)s",
+ "Set a display name:": "Setze einen Anzeigenamen:",
+ "Upload an avatar:": "Lade einen Avatar hoch:",
+ "This server does not support authentication with a phone number.": "Dieser Server unterstützt keine Authentifizierung mittels Telefonnummer.",
+ "Missing password.": "Fehlendes Passwort.",
+ "Passwords don't match.": "Passwörter passen nicht zusammen.",
+ "Password too short (min %(MIN_PASSWORD_LENGTH)s).": "Passwort zu kurz (min. %(MIN_PASSWORD_LENGTH)s).",
+ "This doesn't look like a valid email address.": "Dies sieht nicht nach einer validen E-Mail-Adresse aus.",
+ "This doesn't look like a valid phone number.": "Dies sieht nicht nach einer validen Telefonnummer aus.",
+ "User names may only contain letters, numbers, dots, hyphens and underscores.": "Benutzernamen sollen nur Buchstaben, Nummern, Binde- und Unterstriche enthalten.",
+ "An unknown error occurred.": "Ein unbekannter Fehler trat auf.",
+ "I already have an account": "Ich habe bereits einen Account",
+ "An error occured: %(error_string)s": "Ein Fehler trat auf: %(error_string)s",
+ "Topic": "Thema",
+ "Make this room private": "Mache diesen Raum privat",
+ "Share message history with new users": "Teile Nachrichtenhistorie mit neuen Nutzern",
+ "Encrypt room": "Entschlüssele Raum",
+ "To send events of type": "Zum Senden von Ereignissen mit Typ",
+ "%(names)s and %(lastPerson)s are typing": "%(names)s und %(lastPerson)s schreiben",
+ "%(targetName)s accepted an invitation": "%(targetName)s akzeptierte eine Einladung",
+ "%(targetName)s accepted the invitation for %(displayName)s": "%(targetName)s akzeptierte eine Einladung für %(displayName)s",
+ "%(names)s and one other are typing": "%(names)s und eine weitere Person tippen",
+ "%(names)s and %(count)s others are typing": "%(names)s und %(count)s weitere Personen tippen",
+ "%(senderName)s answered the call": "%(senderName)s beantwortete den Anruf",
+ "%(senderName)s banned %(targetName)s": "%(senderName)s bannte %(targetName)s",
+ "%(senderName)s changed their display name from %(oldDisplayName)s to %(displayName)s": "%(senderName)s änderte den Anzeigenamen von %(oldDisplayName)s zu %(displayName)s",
+ "%(senderName)s changed their profile picture": "%(senderName)s änderte das Profilbild",
+ "%(senderName)s changed the power level of %(powerLevelDiffText)s": "%(senderName)s änderte das Berechtigungslevel von %(powerLevelDiffText)s",
+ "%(senderDisplayName)s changed the room name to %(roomName)s": "%(senderDisplayName)s änderte den Raumnamen zu %(roomName)s",
+ "%(senderDisplayName)s changed the topic to %(topic)s": "%(senderDisplayName)s änderte das Thema zu %(topic)s",
+ "/ddg is not a command": "/ddg ist kein Kommando",
+ "%(senderName)s ended the call": "%(senderName)s beendete den Anruf",
+ "Failed to lookup current room": "Aktuellen Raum nachzuschlagen schlug fehl",
+ "Failed to send request": "Anfrage zu senden schlug fehl",
+ "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s von %(fromPowerLevel)s zu %(toPowerLevel)s",
+ "%(senderName)s invited %(targetName)s": "%(senderName)s lud %(targetName)s ein",
+ "%(displayName)s is typing": "%(displayName)s tippt",
+ "%(targetName)s joined the room": "%(targetName)s trat dem Raum bei",
+ "%(senderName)s kicked %(targetName)s": "%(senderName)s kickte %(targetName)s",
+ "%(targetName)s left the room": "%(targetName)s verließ den Raum",
+ "%(senderName)s made future room history visible to": "%(senderName)s machte die zukünftige Raumhistorie sichtbar für",
+ "Missing room_id in request": "Fehlende room_id in Anfrage",
+ "Missing user_id in request": "Fehlende user_id in Anfrage",
+ "Must be viewing a room": "Muss einen Raum ansehen",
+ "New Composer & Autocomplete": "Neuer Eingabeverarbeiter & Autovervollständigung",
+ "(not supported by this browser)": "(nicht von diesem Browser unterstützt)",
+ "%(senderName)s placed a %(callType)s call": "%(senderName)s startete einen %(callType)s-Anruf",
+ "Power level must be positive integer": "Berechtigungslevel muss eine positive Zahl sein",
+ "Reason": "Grund",
+ "%(targetName)s rejected the invitation": "%(targetName)s lehnte die Einladung ab",
+ "%(senderName)s removed their display name (%(oldDisplayName)s)": "%(senderName)s löschte den Anzeigenamen (%(oldDisplayName)s)",
+ "%(senderName)s removed their profile picture": "%(senderName)s löschte das Profilbild",
+ "%(senderName)s requested a VoIP conference": "%(senderName)s fragte nach einer VoIP-Konferenz",
+ "Room %(roomId)s not visible": "Raum %(roomId)s ist nicht sichtbar",
+ "%(senderDisplayName)s sent an image": "%(senderDisplayName)s hat ein Bild gesendet",
+ "%(senderName)s sent an invitation to %(targetDisplayName)s to join the room": "%(senderName)s sandte eine Einladung an %(targetDisplayName)s um diesem Raum beizutreten",
+ "%(senderName)s set a profile picture": "%(senderName)s setzte ein Profilbild",
+ "%(senderName)s set their display name to %(displayName)s": "%(senderName)s setzte den Anzeigenamen zu %(displayName)s",
+ "This room is not recognised": "Dieser Raum wurde nicht erkannt",
+ "These are experimental features that may break in unexpected ways": "Dies sind experimentelle Funktionen, die in unerwarteter Weise Fehler verursachen können",
+ "To use it, just wait for autocomplete results to load and tab through them": "Um dies zu nutzen, warte auf die Autovervollständigungsergebnisse und benutze die TAB Taste",
+ "%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s)": "%(senderName)s schaltete Ende-zu-Ende-Verschlüsselung ein (Algorithmus: %(algorithm)s)",
+ "%(senderName)s unbanned %(targetName)s": "%(senderName)s zog Bann für %(targetName)s zurück",
+ "Usage": "Verwendung",
+ "Use with caution": "Mit Vorsicht benutzen",
+ "%(senderName)s withdrew %(targetName)s's inivitation": "%(senderName)s zog die Einladung für %(targetName)s zurück",
+ "You need to be able to invite users to do that": "Du musst in der Lage sein Nutzer einzuladen um dies zu tun",
+ "You need to be logged in": "Du musst angemeldet sein",
+ "There are no visible files in this room": "Es gibt keine sichtbaren Dateien in diesem Raum",
+ "Error changing language": "Fehler beim Ändern der Sprache",
+ "Riot was unable to find the correct Data for the selected Language.": "Riot war nicht in der Lage die korrekten Daten für die ausgewählte Sprache zu finden.",
+ "Connectivity to the server has been lost.": "Verbindung zum Server untergebrochen.",
+ "Sent messages will be stored until your connection has returned.": "Gesendete Nachrichten werden gespeichert bis die Verbindung wiederhergestellt wurde.",
+ "Auto-complete": "Autovervollständigung",
+ "Resend all": "Alles erneut senden",
+ "cancel all": "alles abbrechen",
+ "now. You can also select individual messages to resend or cancel.": "jetzt. Du kannst auch einzelne Nachrichten zum erneuten Senden oder Abbrechen auswählen.",
+ "Active call": "Aktiver Anruf",
+ "withdrawn": "zurückgezogen",
+ "To link to a room it must have": "Um einen Raum zu verlinken, benötigt er",
+ "were": "wurden",
+ "en": "Englisch",
+ "pt-br": "Portugisisch (Brasilien)",
+ "de": "Deutsch",
+ "da": "Dänisch",
+ "ru": "Russisch",
+ "Drop here %(toAction)s": "Hierher ziehen: %(toAction)s",
+ "Drop here to tag %(section)s": "Hierher ziehen: %(section)s taggen",
+ "Press": "Drücke",
+ "tag as %(tagName)s": "als %(tagName)s taggen",
+ "to browse the directory": "um das Verzeichnis anzusehen",
+ "to demote": "um die Priorität herabzusetzen",
+ "to favourite": "zum Favorisieren",
+ "to make a room or": "um einen Raum zu erstellen, oder",
+ "to restore": "zum wiederherstellen",
+ "to start a chat with someone": "um einen Chat mit jemandem zu starten",
+ "to tag direct chat": "als direkten Chat markieren",
+ "You're not in any rooms yet! Press": "Du bist noch keinem Raum beigetreten! Drücke",
+ "click to reveal": "Klicke zum anzeigen",
+ "To redact other users' messages": "Um Nachrichten anderer zu verbergen",
+ "You are trying to access %(roomName)s": "Du versuchst auf %(roomName)s zuzugreifen",
+ "af": "Afrikaans",
+ "ar-ae": "Arabisch (U.A.E.)",
+ "ar-bh": "Arabisch (Bahrain)",
+ "ar-dz": "Arabisch (Algeria)",
+ "ar-eg": "Arabisch (Ägypten)",
+ "ar-iq": "Arabisch (Irak)",
+ "ar-jo": "Arabisch (Jordanien)",
+ "ar-kw": "Arabisch (Kuwait)",
+ "ar-lb": "Arabisch (Libanon)",
+ "ar-ly": "Arabisch (Lybien)",
+ "ar-ma": "Arabisch (Marokko)",
+ "ar-om": "Arabisch (Oman)",
+ "ar-qa": "Arabisch (Qatar)",
+ "ar-sa": "Arabisch (Saudi Arabien)",
+ "ar-sy": "Arabisch (Syrien)",
+ "ar-tn": "Arabisch (Tunisien)",
+ "ar-ye": "Arabisch (Yemen)",
+ "be": "Weißrussisch",
+ "bg": "Bulgarisch",
+ "cs": "Tschechisch",
+ "de-at": "Deutsch (Österreich)",
+ "de-ch": "Deutsch (Schweiz)",
+ "de-li": "Deutsch (Liechtenstein)",
+ "de-lu": "Deutsch (Luxemburg)",
+ "el": "Griechisch",
+ "en-au": "Englisch (Australien)",
+ "en-bz": "Englisch (Belize)",
+ "en-ca": "Englisch (Kanada)",
+ "en-gb": "Englisch (Vereintes Königreich)",
+ "en-ie": "Englisch (Irland)",
+ "en-jm": "Englisch (Jamaika)",
+ "en-nz": "Englisch (Neuseeland)",
+ "en-tt": "Englisch (Trinidad)",
+ "en-us": "Englisch (Vereinigte Staaten)",
+ "en-za": "Englisch (Süd-Afrika)",
+ "es-ar": "Spanisch (Argentinien)",
+ "es-bo": "Spanisch (Bolivien)",
+ "es-cl": "Spanisch (Chile)",
+ "es-co": "Spanisch (Kolombien)",
+ "es-cr": "Spanisch (Costa Rica)",
+ "es-do": "Spanisch (Dominikanische Republik)",
+ "es-ec": "Spanisch (Ecuador)",
+ "es-gt": "Spanisch (Guatemala)",
+ "es-hn": "Spanisch (Honduras)",
+ "es-mx": "Spanisch (Mexico)",
+ "es-ni": "Spanisch (Nicaragua)",
+ "es-pa": "Spanisch (Panama)",
+ "es-pe": "Spanisch (Peru)",
+ "es-pr": "Spanisch (Puerto Rico)",
+ "es-py": "Spanisch (Paraguay)",
+ "es": "Spanisch (Spanien)",
+ "es-sv": "Spanisch (El Salvador)",
+ "es-uy": "Spanisch (Uruguay)",
+ "es-ve": "Spanisch (Venezuela)",
+ "et": "Estländisch",
+ "eu": "Baskisch (Baskenland)",
+ "fa": "Persisch (Farsi)",
+ "fr-be": "Französisch (Belgien)",
+ "fr-ca": "Französisch (Kanada)",
+ "fr-ch": "Französisch (Schweiz)",
+ "fr": "Französisch",
+ "fr-lu": "Französisch (Luxemburg)",
+ "gd": "Gälisch (Schottland)",
+ "he": "Hebräisch",
+ "hr": "Kroatisch",
+ "hu": "Ungarisch",
+ "id": "Indonesisch",
+ "is": "Isländisch",
+ "it-ch": "Italienisch (Schweiz)",
+ "it": "Italienisch",
+ "ja": "Japanisch",
+ "ji": "Jiddisch",
+ "ko": "Koreanisch (Johab)",
+ "lt": "Litauisch",
+ "lv": "lettisch",
+ "mk": "Mazedonisch (FYROM)",
+ "ms": "Malaysisch",
+ "mt": "Maltesisch",
+ "nl-be": "Niederländisch (Belgien)",
+ "nl": "Niederländisch",
+ "no": "Norwegisch",
+ "pl": "Polnisch",
+ "pt": "Portugiesisch",
+ "rm": "Rätoromanisch",
+ "ro-mo": "Rumänisch (Republik von Moldavien)",
+ "ro": "Romanian",
+ "ru-mo": "Russisch",
+ "sb": "Sorbian",
+ "sk": "Slowakisch",
+ "sl": "Slowenisch",
+ "sq": "Albanisch",
+ "sr": "Serbisch (lateinisch)",
+ "sv-fi": "Schwedisch (Finnland)",
+ "sv": "Schwedisch",
+ "sx": "Sutu",
+ "sz": "Samisch (Lappish)",
+ "th": "Thailändisch",
+ "tn": "Setswana",
+ "tr": "Türkisch",
+ "ts": "Tsonga",
+ "uk": "Ukrainisch",
+ "ur": "Urdu",
+ "ve": "Tshivenda",
+ "vi": "Vietnamesisch",
+ "zh-cn": "Chinesisch (PRC)",
+ "zh-hk": "Chinesisch (Hong Kong SAR)",
+ "zh-sg": "Chinesisch (Singapur)",
+ "zh-tw": "Chinesisch (Taiwan)",
+ "zu": "Zulu",
+ "ca": "katalanisch",
+ "fi": "Finnish",
+ "fo": "Färöisch",
+ "ga": "Irisch",
+ "hi": "Hindi",
+ "xh": "Xhosa",
+ "Monday": "Montag",
+ "Tuesday": "Dienstag",
+ "Wednesday": "Mittwoch",
+ "Thursday": "Donnerstag",
+ "Friday": "Freitag",
+ "Saturday": "Samstag",
+ "Sunday": "Sonntag",
+ "Failed to forget room %(errCode)s": "Das Entfernen des Raums %(errCode)s aus deiner Liste ist fehlgeschlagen",
+ "Failed to join the room": "Fehler beim Betreten des Raumes",
+ "A text message has been sent to +%(msisdn)s. Please enter the verification code it contains": "Eine Textnachricht wurde an +%(msisdn)s gesendet. Bitte gebe den Verifikationscode ein, den er beinhaltet",
+ "and %(overflowCount)s others...": "und %(overflowCount)s weitere...",
+ "and one other...": "und ein(e) weitere(r)...",
+ "Are you sure?": "Bist du sicher?",
+ "Attachment": "Anhang",
+ "Ban": "Banne",
+ "Can't connect to homeserver - please check your connectivity and ensure your %(urlStart)s homeserver's SSL certificate %(urlEnd)s is trusted": "Kann nicht zum Heimserver verbinden - bitte checke eine Verbindung und stelle sicher, dass dem %(urlStart)s SSL-Zertifikat deines Heimservers %(urlEnd)s vertraut wird",
+ "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or %(urlStart)s enable unsafe scripts %(urlEnd)s": "Kann nicht zum Heimserver via HTTP verbinden, wenn eine HTTPS-Url in deiner Adresszeile steht. Nutzer HTTPS oder %(urlStart)s aktiviere unsichere Skripte %(urlEnd)s",
+ "changing room on a RoomView is not supported": "Das Ändern eines Raumes in einer RaumAnsicht wird nicht unterstützt",
+ "Click to mute audio": "Klicke um den Ton stumm zu stellen",
+ "Click to mute video": "Klicke um das Video stumm zu stellen",
+ "Command error": "Befehlsfehler",
+ "Decrypt %(text)s": "Entschlüssele %(text)s",
+ "Delete": "Lösche",
+ "Devices": "Geräte",
+ "Direct chats": "Direkte Chats",
+ "Disinvite": "Ausladen",
+ "Download %(text)s": "%(text)s herunterladen",
+ "Enter Code": "Code eingeben",
+ "Failed to ban user": "Bannen des Nutzers fehlgeschlagen",
+ "Failed to change power level": "Ändern des Berechtigungslevels fehlgeschlagen",
+ "Failed to delete device": "Löschen des Geräts fehlgeschlagen",
+ "Failed to join room": "Raumbeitritt fehlgeschlagen",
+ "Failed to kick": "Kicken fehlgeschlagen",
+ "Failed to mute user": "Nutzer lautlos zu stellen fehlgeschlagen",
+ "Failed to reject invite": "Einladung abzulehnen fehlgeschlagen",
+ "Failed to save settings": "Einstellungen speichern fehlgeschlagen",
+ "Failed to set display name": "Anzeigenamen zu ändern fehlgeschlagen",
+ "Fill screen": "Fülle Bildschirm",
+ "Guest users can't upload files. Please register to upload": "Gäste können Dateien nicht hochlagen. Bitte registrieren um hochzuladen",
+ "Hide Text Formatting Toolbar": "Verberge Text-Formatierungs-Toolbar",
+ "Incorrect verification code": "Verifikationscode inkorrekt",
+ "Invalid alias format": "Ungültiges Alias-Format",
+ "Invalid address format": "Ungültiges Adressformat",
+ "'%(alias)s' is not a valid format for an address": "'%(alias)s' ist kein valides Adressformat",
+ "'%(alias)s' is not a valid format for an alias": "'%(alias)s' hat kein valides Aliasformat",
+ "Join Room": "Raum beitreten",
+ "Kick": "Kicke",
+ "Level": "Level",
+ "Local addresses for this room:": "Lokale Adressen dieses Raumes:",
+ "Markdown is disabled": "Markdown ist deaktiviert",
+ "Markdown is enabled": "Markdown ist aktiviert",
+ "Message not sent due to unknown devices being present": "Nachrichten wurden nicht gesendet, da unbekannte Geräte anwesend sind",
+ "New address (e.g. #foo:%(localDomain)s)": "Neue Adresse (z.B. #foo:%(localDomain)s)",
+ "not set": "nicht gesetzt",
+ "not specified": "nicht spezifiziert",
+ "No devices with registered encryption keys": "Keine Geräte mit registrierten Verschlüsselungsschlüsseln",
+ "No more results": "Keine weiteren Ergebnisse",
+ "No results": "Keine Ergebnisse",
+ "OK": "OK",
+ "Revoke Moderator": "Moderator zurückziehen",
+ "Search": "Suche",
+ "Search failed": "Suche fehlgeschlagen",
+ "Server error": "Serverfehler",
+ "Server may be unavailable, overloaded, or search timed out :(": "Server ist entweder nicht verfügbar, überlastet oder die Suchezeit ist abgelaufen :(",
+ "Server may be unavailable, overloaded, or the file too big": "Server ist entweder nicht verfügbar, überlastet oder die Datei ist zu groß",
+ "Server unavailable, overloaded, or something else went wrong": "Server ist entweder nicht verfügbar, überlastet oder etwas anderes schlug fehl",
+ "Some of your messages have not been sent": "Einige deiner Nachrichten wurden noch nicht gesendet",
+ "Submit": "Absenden",
+ "The main address for this room is: %(canonical_alias_section)s": "Die Hauptadresse für diesen Raum ist: %(canonical_alias_section)s",
+ "This action cannot be performed by a guest user. Please register to be able to do this": "Diese Aktion kann nicht von einem Gast ausgeführt werden. Bitte registriere dich um dies zu tun",
+ "%(actionVerb)s this person?": "Diese Person %(actionVerb)s?",
+ "This room has no local addresses": "Dieser Raum hat keine lokale Adresse",
+ "This room is private or inaccessible to guests. You may be able to join if you register": "Dieser Raum ist privat oder für Gäste nicht zugreifbar. Du kannst evtl. zugreifen, wenn du dich registrierst",
+ "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question": "Versuchte einen spezifischen Punkt in der Raum-Chronik zu laden, aber du hast keine Berechtigung die angeforderte Nachricht anzuzeigen",
+ "Tried to load a specific point in this room's timeline, but was unable to find it": "Versuchte einen spezifischen Punkt in der Raum-Chronik zu laden, aber er konnte nicht gefunden werden",
+ "Turn Markdown off": "Markdown abschalten",
+ "Turn Markdown on": "Markdown einschalten",
+ "Unable to load device list": "Laden der Geräteliste nicht möglich",
+ "Unknown command": "Unbekannter Befehl",
+ "Unknown room %(roomId)s": "Unbekannter Raum %(roomId)s",
+ "Usage: /markdown on|off": "Verwendung: /markdown on|off",
+ "You seem to be in a call, are you sure you want to quit?": "Du scheinst in einem Anruf zu sein. Bist du sicher schließen zu wollen?",
+ "You seem to be uploading files, are you sure you want to quit?": "Du scheinst Dateien hochzuladen. Bist du sicher schließen zu wollen?",
+ "You will not be able to undo this change as you are promoting the user to have the same power level as yourself": "Du wirst nicht in der Lage sein, diese Änderung zu ändern da den Nutzer auf dasselbe Berechtigungslevel wie du hebst",
+ "Make Moderator": "Mache zum Moderator",
+ "Room": "Raum",
+ "(~%(searchCount)s results)": "(~%(searchCount)s Ergebnisse)",
+ "Cancel": "Abbrechen",
+ "bold": "fett",
+ "italic": "kursiv",
+ "strike": "durchstreichen",
+ "underline": "unterstreichen",
+ "code": "Code",
+ "quote": "Zitat",
+ "bullet": "Aufzählung",
+ "Click to unmute video": "Klicke um Video zu reaktivieren",
+ "Click to unmute audio": "Klicke um Ton zu reaktivieren",
+ "Failed to load timeline position": "Laden der Position im Zeitstrahl fehlgeschlagen",
+ "Failed to toggle moderator status": "Umschalten des Moderatorstatus fehlgeschlagen",
+ "Enable encryption": "Verschlüsselung aktivieren",
+ "The main address for this room is": "Die Hauptadresse für diesen Raum ist",
+ "Autoplay GIFs and videos": "Automatisch GIF's und Videos abspielen",
+ "Don't send typing notifications": "Nicht senden, wenn ich tippe",
+ "Hide read receipts": "Verberge Lesebestätigungen",
+ "Never send encrypted messages to unverified devices in this room": "In diesem Raum keine verschlüsselten Nachrichten an unverifizierte Geräte senden",
+ "numbullet": "Nummerierung",
+ "%(items)s and %(remaining)s others": "%(items)s und %(remaining)s weitere",
+ "%(items)s and one other": "%(items)s und ein(e) weitere(r)",
+ "%(items)s and %(lastItem)s": "%(items)s und %(lastItem)s",
+ "%(severalUsers)s joined %(repeats)s times": "%(severalUsers)s trat(en) %(repeats)s mal bei",
+ "%(oneUser)s joined %(repeats)s times": "%(oneUser)s trat %(repeats)s mal bei",
+ "%(severalUsers)s joined": "%(severalUsers)s traten bei",
+ "%(oneUser)s joined": "%(oneUser)s trat bei",
+ "%(severalUsers)s left %(repeats)s times": "%(severalUsers)s gingen %(repeats)s mal",
+ "%(oneUser)s left %(repeats)s times": "%(oneUser)s ging %(repeats)s mal",
+ "%(severalUsers)s left": "%(severalUsers)s gingen",
+ "%(oneUser)s left": "%(oneUser)s ging",
+ "%(severalUsers)s joined and left %(repeats)s times": "%(severalUsers)s traten bei und gingen %(repeats)s mal",
+ "%(oneUser)s joined and left %(repeats)s times": "%(oneUser)s trat bei und ging %(repeats)s mal",
+ "%(severalUsers)s joined and left": "%(severalUsers)s traten bei und gingen",
+ "%(oneUser)s joined and left": "%(oneUser)s trat bei und ging",
+ "%(severalUsers)s left and rejoined %(repeats)s times": "%(severalUsers)s gingen und traten erneut bei - %(repeats)s mal",
+ "%(oneUser)s left and rejoined %(repeats)s times": "%(oneUser)s ging und trat erneut bei - %(repeats)s mal",
+ "%(severalUsers)s left and rejoined": "%(severalUsers)s gingen und traten erneut bei",
+ "%(oneUser)s left left and rejoined": "%(oneUser)s ging und trat erneut bei",
+ "%(severalUsers)s rejected their invitations %(repeats)s times": "%(severalUsers)s lehnten %(repeats)s mal ihre Einladung ab",
+ "%(oneUser)s rejected his/her invitation %(repeats)s times": "%(oneUser)s lehnte seine/ihre Einladung %(repeats)s mal ab",
+ "%(severalUsers)s rejected their invitations": "%(severalUsers)s lehnten ihre Einladung ab",
+ "%(oneUser)s rejected his/her invitation": "%(oneUser)s lehnte seine/ihre Einladung ab",
+ "%(severalUsers)s had their invitations withdrawn %(repeats)s times": "%(severalUsers)s zogen ihre Einladungen %(repeats)s mal zurück",
+ "%(oneUser)s had his/her invitation withdrawn %(repeats)s times": "%(oneUser)s zog seine/ihre Einladung %(repeats)s mal zurück",
+ "%(severalUsers)s had their invitations withdrawn": "%(severalUsers)s zogen ihre Einladungen zurück",
+ "%(oneUser)s had his/her invitation withdrawn": "%(oneUser)s zog seine/ihre Einladung zurück",
+ "were invited %(repeats)s times": "wurden %(repeats)s mal eingeladen",
+ "was invited %(repeats)s times": "wurde %(repeats)s mal eingeladen",
+ "were invited": "wurden eingeladen",
+ "were banned %(repeats)s times": "wurden %(repeats)s mal gebannt",
+ "was banned %(repeats)s times": "wurde %(repeats)s mal gebannt",
+ "were banned": "wurden gebannt",
+ "were unbanned %(repeats)s times": "wurden %(repeats)s mal entbannt",
+ "was unbanned %(repeats)s times": "wurde %(repeats)s mal entbannt",
+ "were unbanned": "wurden entbannt",
+ "were kicked %(repeats)s times": "wurden %(repeats)s mal gekickt",
+ "was kicked %(repeats)s times": "wurde %(repeats)s mal gekickt",
+ "were kicked": "wurden gekickt",
+ "%(severalUsers)s changed their name %(repeats)s times": "%(severalUsers)s änderten %(repeats)s mal ihre Namen",
+ "%(oneUser)s changed his/her name %(repeats)s times": "%(oneUser)s änderte %(repeats)s mal seinen/ihren Namen",
+ "%(severalUsers)s changed their name": "%(severalUsers)s änderten ihre Namen",
+ "%(oneUser)s changed his/her name": "%(oneUser)s änderte seinen/ihren Namen",
+ "%(severalUsers)s changed their avatar %(repeats)s times": "%(severalUsers)s änderten %(repeats)s mal ihren Avatar",
+ "%(oneUser)s changed his/her avatar %(repeats)s times": "%(oneUser)s änderte %(repeats)s mal seinen/ihren Avatar",
+ "%(severalUsers)s changed their avatar": "%(severalUsers)s änderten ihre Avatare",
+ "%(oneUser)s changed his/her avatar": "%(severalUsers)s änderte seinen/ihren Avatar",
+ "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s, %(day)s. %(monthName)s %(fullYear)s %(time)s",
+ "%(oneUser)s left and rejoined": "%(oneUser)s ging und trat erneut bei"
+}
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
new file mode 100644
index 0000000000..175dbe3c47
--- /dev/null
+++ b/src/i18n/strings/en_EN.json
@@ -0,0 +1,656 @@
+{
+ "af":"Afrikaans",
+ "ar-ae":"Arabic (U.A.E.)",
+ "ar-bh":"Arabic (Bahrain)",
+ "ar-dz":"Arabic (Algeria)",
+ "ar-eg":"Arabic (Egypt)",
+ "ar-iq":"Arabic (Iraq)",
+ "ar-jo":"Arabic (Jordan)",
+ "ar-kw":"Arabic (Kuwait)",
+ "ar-lb":"Arabic (Lebanon)",
+ "ar-ly":"Arabic (Libya)",
+ "ar-ma":"Arabic (Morocco)",
+ "ar-om":"Arabic (Oman)",
+ "ar-qa":"Arabic (Qatar)",
+ "ar-sa":"Arabic (Saudi Arabia)",
+ "ar-sy":"Arabic (Syria)",
+ "ar-tn":"Arabic (Tunisia)",
+ "ar-ye":"Arabic (Yemen)",
+ "be":"Belarusian",
+ "bg":"Bulgarian",
+ "ca":"Catalan",
+ "cs":"Czech",
+ "da":"Danish",
+ "de-at":"German (Austria)",
+ "de-ch":"German (Switzerland)",
+ "de":"German",
+ "de-li":"German (Liechtenstein)",
+ "de-lu":"German (Luxembourg)",
+ "el":"Greek",
+ "en-au":"English (Australia)",
+ "en-bz":"English (Belize)",
+ "en-ca":"English (Canada)",
+ "en":"English",
+ "en-gb":"English (United Kingdom)",
+ "en-ie":"English (Ireland)",
+ "en-jm":"English (Jamaica)",
+ "en-nz":"English (New Zealand)",
+ "en-tt":"English (Trinidad)",
+ "en-us":"English (United States)",
+ "en-za":"English (South Africa)",
+ "es-ar":"Spanish (Argentina)",
+ "es-bo":"Spanish (Bolivia)",
+ "es-cl":"Spanish (Chile)",
+ "es-co":"Spanish (Colombia)",
+ "es-cr":"Spanish (Costa Rica)",
+ "es-do":"Spanish (Dominican Republic)",
+ "es-ec":"Spanish (Ecuador)",
+ "es-gt":"Spanish (Guatemala)",
+ "es-hn":"Spanish (Honduras)",
+ "es-mx":"Spanish (Mexico)",
+ "es-ni":"Spanish (Nicaragua)",
+ "es-pa":"Spanish (Panama)",
+ "es-pe":"Spanish (Peru)",
+ "es-pr":"Spanish (Puerto Rico)",
+ "es-py":"Spanish (Paraguay)",
+ "es":"Spanish (Spain)",
+ "es-sv":"Spanish (El Salvador)",
+ "es-uy":"Spanish (Uruguay)",
+ "es-ve":"Spanish (Venezuela)",
+ "et":"Estonian",
+ "eu":"Basque (Basque)",
+ "fa":"Farsi",
+ "fi":"Finnish",
+ "fo":"Faeroese",
+ "fr-be":"French (Belgium)",
+ "fr-ca":"French (Canada)",
+ "fr-ch":"French (Switzerland)",
+ "fr":"French",
+ "fr-lu":"French (Luxembourg)",
+ "ga":"Irish",
+ "gd":"Gaelic (Scotland)",
+ "he":"Hebrew",
+ "hi":"Hindi",
+ "hr":"Croatian",
+ "hu":"Hungarian",
+ "id":"Indonesian",
+ "is":"Icelandic",
+ "it-ch":"Italian (Switzerland)",
+ "it":"Italian",
+ "ja":"Japanese",
+ "ji":"Yiddish",
+ "ko":"Korean",
+ "ko":"Korean (Johab)",
+ "lt":"Lithuanian",
+ "lv":"Latvian",
+ "mk":"Macedonian (FYROM)",
+ "ms":"Malaysian",
+ "mt":"Maltese",
+ "nl-be":"Dutch (Belgium)",
+ "nl":"Dutch",
+ "no":"Norwegian",
+ "pl":"Polish",
+ "pt-br":"Brazilian Portuguese",
+ "pt":"Portuguese",
+ "rm":"Rhaeto-Romanic",
+ "ro-mo":"Romanian (Republic of Moldova)",
+ "ro":"Romanian",
+ "ru-mo":"Russian (Republic of Moldova)",
+ "ru":"Russian",
+ "sb":"Sorbian",
+ "sk":"Slovak",
+ "sl":"Slovenian",
+ "sq":"Albanian",
+ "sr":"Serbian (Cyrillic)",
+ "sr":"Serbian (Latin)",
+ "sv-fi":"Swedish (Finland)",
+ "sv":"Swedish",
+ "sx":"Sutu",
+ "sz":"Sami (Lappish)",
+ "th":"Thai",
+ "tn":"Tswana",
+ "tr":"Turkish",
+ "ts":"Tsonga",
+ "uk":"Ukrainian",
+ "ur":"Urdu",
+ "ve":"Venda",
+ "vi":"Vietnamese",
+ "xh":"Xhosa",
+ "zh-cn":"Chinese (PRC)",
+ "zh-hk":"Chinese (Hong Kong SAR)",
+ "zh-sg":"Chinese (Singapore)",
+ "zh-tw":"Chinese (Taiwan)",
+ "zu":"Zulu",
+ "A text message has been sent to +%(msisdn)s. Please enter the verification code it contains": "A text message has been sent to +%(msisdn)s. Please enter the verification code it contains",
+ "accept": "accept",
+ "%(targetName)s accepted an invitation": "%(targetName)s accepted an invitation",
+ "%(targetName)s accepted the invitation for %(displayName)s": "%(targetName)s accepted the invitation for %(displayName)s",
+ "Account": "Account",
+ "Add email address": "Add email address",
+ "Add phone number": "Add phone number",
+ "Admin": "Admin",
+ "Advanced": "Advanced",
+ "Algorithm": "Algorithm",
+ "all room members": "all room members",
+ "all room members, from the point they are invited": "all room members, from the point they are invited",
+ "all room members, from the point they joined": "all room members, from the point they joined",
+ "an address": "an address",
+ "and": "and",
+ "%(items)s and %(remaining)s others": "%(items)s and %(remaining)s others",
+ "%(items)s and one other": "%(items)s and one other",
+ "%(items)s and %(lastItem)s": "%(items)s and %(lastItem)s",
+ "and %(overflowCount)s others...": "and %(overflowCount)s others...",
+ "and one other...": "and one other...",
+ "%(names)s and %(lastPerson)s are typing": "%(names)s and %(lastPerson)s are typing",
+ "%(names)s and one other are typing": "%(names)s and one other are typing",
+ "%(names)s and %(count)s others are typing": "%(names)s and %(count)s others are typing",
+ "An email has been sent to": "An email has been sent to",
+ "A new password must be entered.": "A new password must be entered.",
+ "%(senderName)s answered the call": "%(senderName)s answered the call",
+ "anyone.": "anyone",
+ "Anyone who knows the room's link, apart from guests": "Anyone who knows the room's link, apart from guests",
+ "Anyone who knows the room's link, including guests": "Anyone who knows the room's link, including guests",
+ "Are you sure?": "Are you sure?",
+ "Are you sure you want to reject the invitation?": "Are you sure you want to reject the invitation?",
+ "Are you sure you want upload the following files?": "Are you sure you want upload the following files?",
+ "Attachment": "Attachment",
+ "Autoplay GIFs and videos": "Autoplay GIFs and videos",
+ "%(senderName)s banned %(targetName)s": "%(senderName)s banned %(targetName)s",
+ "Ban": "Ban",
+ "Banned users": "Banned users",
+ "Bans user with given id": "Bans user with given id",
+ "Blacklisted": "Blacklisted",
+ "Bug Report": "Bug Report",
+ "Bulk Options": "Bulk Options",
+ "Call Timeout": "Call Timeout",
+ "Can't connect to homeserver - please check your connectivity and ensure your %(urlStart)s homeserver's SSL certificate %(urlEnd)s is trusted": "Can't connect to homeserver - please check your connectivity and ensure your %(urlStart)s homeserver's SSL certificate %(urlEnd)s is trusted",
+ "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or %(urlStart)s enable unsafe scripts %(urlEnd)s": "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or %(urlStart)s enable unsafe scripts %(urlEnd)s",
+ "Can't load user settings": "Can't load user settings",
+ "Change Password": "Change Password",
+ "%(senderName)s changed their display name from %(oldDisplayName)s to %(displayName)s": "%(senderName)s changed their display name from %(oldDisplayName)s to %(displayName)s",
+ "%(senderName)s changed their profile picture": "%(senderName)s changed their profile picture",
+ "%(senderName)s changed the power level of %(powerLevelDiffText)s": "%(senderName)s changed the power level of %(powerLevelDiffText)s",
+ "%(senderDisplayName)s changed the room name to %(roomName)s": "%(senderDisplayName)s changed the room name to %(roomName)s",
+ "%(senderDisplayName)s changed the topic to \"%(topic)s\"": "%(senderDisplayName)s changed the topic to \"%(topic)s\"",
+ "Changes to who can read history will only apply to future messages in this room": "Changes to who can read history will only apply to future messages in this room",
+ "Changes your display nickname": "Changes your display nickname",
+ "changing room on a RoomView is not supported": "changing room on a RoomView is not supported",
+ "Claimed Ed25519 fingerprint key": "Claimed Ed25519 fingerprint key",
+ "Clear Cache and Reload": "Clear Cache and Reload",
+ "Clear Cache": "Clear Cache",
+ "Click here": "Click here",
+ "Click here to fix": "Click here to fix",
+ "Click to mute audio": "Click to mute audio",
+ "Click to mute video": "Click to mute video",
+ "click to reveal": "click to reveal",
+ "Click to unmute video": "Click to unmute video",
+ "Click to unmute audio": "Click to unmute audio",
+ "Command error": "Command error",
+ "Commands": "Commands",
+ "Conference call failed": "Conference call failed",
+ "Conference calling is in development and may not be reliable": "Conference calling is in development and may not be reliable",
+ "Conference calls are not supported in encrypted rooms": "Conference calls are not supported in encrypted rooms",
+ "Conference calls are not supported in this client": "Conference calls are not supported in this client",
+ "Confirm password": "Confirm password",
+ "Confirm your new password": "Confirm your new password",
+ "Continue": "Continue",
+ "Could not connect to the integration server": "Could not connect to the integration server",
+ "Create an account": "Create an account",
+ "Create Room": "Create Room",
+ "Cryptography": "Cryptography",
+ "Current password": "Current password",
+ "Curve25519 identity key": "Curve25519 identity key",
+ "/ddg is not a command": "/ddg is not a command",
+ "Deactivate Account": "Deactivate Account",
+ "Deactivate my account": "Deactivate my account",
+ "decline": "decline",
+ "Decrypt %(text)s": "Decrypt %(text)s",
+ "Decryption error": "Decryption error",
+ "Delete": "Delete",
+ "demote": "demote",
+ "Deops user with given id": "Deops user with given id",
+ "Device ID": "Device ID",
+ "Devices": "Devices",
+ "Devices will not yet be able to decrypt history from before they joined the room": "Devices will not yet be able to decrypt history from before they joined the room",
+ "Direct Chat": "Direct Chat",
+ "Direct chats": "Direct chats",
+ "Disable inline URL previews by default": "Disable inline URL previews by default",
+ "Disinvite": "Disinvite",
+ "Display name": "Display name",
+ "Displays action": "Displays action",
+ "Don't send typing notifications": "Don't send typing notifications",
+ "Download %(text)s": "Download %(text)s",
+ "Drop here %(toAction)s": "Drop here %(toAction)s",
+ "Drop here to tag %(section)s": "Drop here to tag %(section)s",
+ "Ed25519 fingerprint": "Ed25519 fingerprint",
+ "Email Address": "Email Address",
+ "Email, name or matrix ID": "Email, name or matrix ID",
+ "Emoji": "Emoji",
+ "Enable encryption": "Enable encryption",
+ "Encrypted messages will not be visible on clients that do not yet implement encryption": "Encrypted messages will not be visible on clients that do not yet implement encryption",
+ "Encrypted room": "Encrypted room",
+ "%(senderName)s ended the call": "%(senderName)s ended the call",
+ "End-to-end encryption information": "End-to-end encryption information",
+ "End-to-end encryption is in beta and may not be reliable": "End-to-end encryption is in beta and may not be reliable",
+ "Enter Code": "Enter Code",
+ "Error": "Error",
+ "Event information": "Event information",
+ "Existing Call": "Existing Call",
+ "Export E2E room keys": "Export E2E room keys",
+ "Failed to ban user": "Failed to ban user",
+ "Failed to change password. Is your password correct?": "Failed to change password. Is your password correct?",
+ "Failed to change power level": "Failed to change power level",
+ "Failed to delete device": "Failed to delete device",
+ "Failed to forget room %(errCode)s": "Failed to forget room %(errCode)s",
+ "Failed to join room": "Failed to join room",
+ "Failed to join the room": "Failed to join the room",
+ "Failed to kick": "Failed to kick",
+ "Failed to leave room": "Failed to leave room",
+ "Failed to load timeline position": "Failed to load timeline position",
+ "Failed to lookup current room": "Failed to lookup current room",
+ "Failed to mute user": "Failed to mute user",
+ "Failed to reject invite": "Failed to reject invite",
+ "Failed to reject invitation": "Failed to reject invitation",
+ "Failed to save settings": "Failed to save settings",
+ "Failed to send email": "Failed to send email",
+ "Failed to send request": "Failed to send request",
+ "Failed to set display name": "Failed to set display name",
+ "Failed to set up conference call": "Failed to set up conference call",
+ "Failed to toggle moderator status": "Failed to toggle moderator status",
+ "Failed to unban": "Failed to unban",
+ "Failed to upload file": "Failed to upload file",
+ "Failed to verify email address: make sure you clicked the link in the email": "Failed to verify email address: make sure you clicked the link in the email",
+ "Failure to create room": "Failure to create room",
+ "Favourite": "Favourite",
+ "favourite": "favourite",
+ "Favourites": "Favourites",
+ "Fill screen": "Fill screen",
+ "Filter room members": "Filter room members",
+ "Forget room": "Forget room",
+ "Forgot your password?": "Forgot your password?",
+ "For security, this session has been signed out. Please sign in again": "For security, this session has been signed out. Please sign in again",
+ "Found a bug?": "Found a bug?",
+ "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s from %(fromPowerLevel)s to %(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",
+ "Guest users can't upload files. Please register to upload": "Guest users can't upload files. Please register to upload",
+ "had": "had",
+ "Hangup": "Hangup",
+ "Hide read receipts": "Hide read receipts",
+ "Hide Text Formatting Toolbar": "Hide Text Formatting Toolbar",
+ "Historical": "Historical",
+ "Homeserver is": "Homeserver is",
+ "Identity Server is": "Identity Server is",
+ "I have verified my email address": "I have verified my email address",
+ "Import E2E room keys": "Import E2E room keys",
+ "Incorrect verification code": "Incorrect verification code",
+ "Invalid alias format": "Invalid alias format",
+ "Invalid address format": "Invalid address format",
+ "Invalid Email Address": "Invalid Email Address",
+ "%(senderName)s invited %(targetName)s": "%(senderName)s invited %(targetName)s",
+ "Invite new room members": "Invite new room members",
+ "Invites": "Invites",
+ "Invites user with given id to current room": "Invites user with given id to current room",
+ "is a": "is a",
+ "'%(alias)s' is not a valid format for an address": "'%(alias)s' is not a valid format for an address",
+ "'%(alias)s' is not a valid format for an alias": "'%(alias)s' is not a valid format for an alias",
+ "%(displayName)s is typing": "%(displayName)s is typing",
+ "I want to sign in with": "I want to sign in with",
+ "Join Room": "Join Room",
+ "joined and left": "joined and left",
+ "joined": "joined",
+ "%(targetName)s joined the room": "%(targetName)s joined the room",
+ "Joins room with given alias": "Joins room with given alias",
+ "%(senderName)s kicked %(targetName)s": "%(senderName)s kicked %(targetName)s",
+ "Kick": "Kick",
+ "Kicks user with given id": "Kicks user with given id",
+ "Labs": "Labs",
+ "Leave room": "Leave room",
+ "left and rejoined": "left and rejoined",
+ "left": "left",
+ "%(targetName)s left the room": "%(targetName)s left the room",
+ "Level": "Level",
+ "Local addresses for this room:": "Local addresses for this room:",
+ "Logged in as": "Logged in as",
+ "Login as guest": "Login as guest",
+ "Logout": "Logout",
+ "Low priority": "Low priority",
+ "%(senderName)s made future room history visible to": "%(senderName)s made future room history visible to",
+ "Manage Integrations": "Manage Integrations",
+ "Markdown is disabled": "Markdown is disabled",
+ "Markdown is enabled": "Markdown is enabled",
+ "Members only": "Members only",
+ "Message not sent due to unknown devices being present": "Message not sent due to unknown devices being present",
+ "Missing room_id in request": "Missing room_id in request",
+ "Missing user_id in request": "Missing user_id in request",
+ "Mobile phone number": "Mobile phone number",
+ "Moderator": "Moderator",
+ "Must be viewing a room": "Must be viewing a room",
+ "my Matrix ID": "my Matrix ID",
+ "Name": "Name",
+ "Never send encrypted messages to unverified devices from this device": "Never send encrypted messages to unverified devices from this device",
+ "Never send encrypted messages to unverified devices in this room": "Never send encrypted messages to unverified devices in this room",
+ "Never send encrypted messages to unverified devices in this room from this device": "Never send encrypted messages to unverified devices in this room from this device",
+ "New address (e.g. #foo:%(localDomain)s)": "New address (e.g. #foo:%(localDomain)s)",
+ "New Composer & Autocomplete": "New Composer & Autocomplete",
+ "New password": "New password",
+ "New passwords don't match": "New passwords don't match",
+ "New passwords must match each other.": "New passwords must match each other.",
+ "none": "none",
+ "not set": "not set",
+ "not specified": "not specified",
+ "Notifications": "Notifications",
+ "(not supported by this browser)": "(not supported by this browser)",
+ "": "",
+ "NOT verified": "NOT verified",
+ "No devices with registered encryption keys": "No devices with registered encryption keys",
+ "No more results": "No more results",
+ "No results": "No results",
+ "No users have specific privileges in this room": "No users have specific privileges in this room",
+ "OK": "OK",
+ "olm version": "olm version",
+ "Once encryption is enabled for a room it cannot be turned off again (for now)": "Once encryption is enabled for a room it cannot be turned off again (for now)",
+ "Once you've followed the link it contains, click below": "Once you've followed the link it contains, click below",
+ "Only people who have been invited": "Only people who have been invited",
+ "or": "or",
+ "Password": "Password",
+ "Passwords can't be empty": "Passwords can't be empty",
+ "People": "People",
+ "Permissions": "Permissions",
+ "Phone": "Phone",
+ "%(senderName)s placed a %(callType)s call": "%(senderName)s placed a %(callType)s call",
+ "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.",
+ "Please Register": "Please Register",
+ "Power level must be positive integer": "Power level must be positive integer",
+ "Press": "Press",
+ "Privacy warning": "Privacy warning",
+ "Privileged Users": "Privileged Users",
+ "Profile": "Profile",
+ "Reason": "Reason",
+ "Revoke Moderator": "Revoke Moderator",
+ "Refer a friend to Riot": "Refer a friend to Riot",
+ "rejected": "rejected",
+ "%(targetName)s rejected the invitation": "%(targetName)s rejected the invitation",
+ "Reject invitation": "Reject invitation",
+ "Remove Contact Information?": "Remove Contact Information?",
+ "%(senderName)s removed their display name (%(oldDisplayName)s)": "%(senderName)s removed their display name (%(oldDisplayName)s)",
+ "%(senderName)s removed their profile picture": "%(senderName)s removed their profile picture",
+ "Remove": "Remove",
+ "%(senderName)s requested a VoIP conference": "%(senderName)s requested a VoIP conference",
+ "Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved ": "Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved ",
+ "restore": "restore",
+ "Return to app": "Return to app",
+ "Return to login screen": "Return to login screen",
+ "Riot does not have permission to send you notifications - please check your browser settings": "Riot does not have permission to send you notifications - please check your browser settings",
+ "Riot was not given permission to send notifications - please try again": "Riot was not given permission to send notifications - please try again",
+ "Room %(roomId)s not visible": "Room %(roomId)s not visible",
+ "Room Colour": "Room Colour",
+ "Room name (optional)": "Room name (optional)",
+ "Rooms": "Rooms",
+ "Scroll to bottom of page": "Scroll to bottom of page",
+ "Scroll to unread messages": "Scroll to unread messages",
+ "Search": "Search",
+ "Search failed": "Search failed",
+ "Searches DuckDuckGo for results": "Searches DuckDuckGo for results",
+ "Send a message (unencrypted)": "Send a message (unencrypted)",
+ "Send an encrypted message": "Send an encrypted message",
+ "Sender device information": "Sender device information",
+ "Send Invites": "Send Invites",
+ "Send Reset Email": "Send Reset Email",
+ "%(senderDisplayName)s sent an image": "%(senderDisplayName)s sent an image",
+ "%(senderName)s sent an invitation to %(targetDisplayName)s to join the room": "%(senderName)s sent an invitation to %(targetDisplayName)s to join the room",
+ "sent a video": "sent a video",
+ "Server error": "Server error",
+ "Server may be unavailable or overloaded": "Server may be unavailable or overloaded",
+ "Server may be unavailable, overloaded, or search timed out :(": "Server may be unavailable, overloaded, or search timed out :(",
+ "Server may be unavailable, overloaded, or the file too big": "Server may be unavailable, overloaded, or the file too big",
+ "Server may be unavailable, overloaded, or you hit a bug": "Server may be unavailable, overloaded, or you hit a bug",
+ "Server unavailable, overloaded, or something else went wrong": "Server unavailable, overloaded, or something else went wrong",
+ "Session ID": "Session ID",
+ "%(senderName)s set a profile picture": "%(senderName)s set a profile picture",
+ "%(senderName)s set their display name to %(displayName)s": "%(senderName)s set their display name to %(displayName)s",
+ "Settings": "Settings",
+ "Show panel": "Show panel",
+ "Signed Out": "Signed Out",
+ "Sign in": "Sign in",
+ "Sign out": "Sign out",
+ "since the point in time of selecting this option": "since the point in time of selecting this option",
+ "since they joined": "since they joined",
+ "since they were invited": "since they were invited",
+ "Some of your messages have not been sent": "Some of your messages have not been sent",
+ "Someone": "Someone",
+ "Sorry, this homeserver is using a login which is not recognised ": "Sorry, this homeserver is using a login which is not recognised ",
+ "Start a chat": "Start a chat",
+ "Start Chat": "Start Chat",
+ "Submit": "Submit",
+ "Success": "Success",
+ "tag as %(tagName)s": "tag as %(tagName)s",
+ "tag direct chat": "tag direct chat",
+ "The default role for new room members is": "The default role for new room members is",
+ "The main address for this room is": "The main address for this room is",
+ "This action cannot be performed by a guest user. Please register to be able to do this": "This action cannot be performed by a guest user. Please register to be able to do this",
+ "This email address is already in use": "This email address is already in use",
+ "This email address was not found": "This email address was not found",
+ "%(actionVerb)s this person?": "%(actionVerb)s this person?",
+ "The email address linked to your account must be entered.": "The email address linked to your account must be entered.",
+ "The file '%(fileName)s' exceeds this home server's size limit for uploads": "The file '%(fileName)s' exceeds this home server's size limit for uploads",
+ "The file '%(fileName)s' failed to upload": "The file '%(fileName)s' failed to upload",
+ "The remote side failed to pick up": "The remote side failed to pick up",
+ "This room has no local addresses": "This room has no local addresses",
+ "This room is not recognised": "This room is not recognised",
+ "This room is private or inaccessible to guests. You may be able to join if you register": "This room is private or inaccessible to guests. You may be able to join if you register",
+ "These are experimental features that may break in unexpected ways": "These are experimental features that may break in unexpected ways",
+ "The visibility of existing history will be unchanged": "The visibility of existing history will be unchanged",
+ "This doesn't appear to be a valid email address": "This doesn't appear to be a valid email address",
+ "this invitation?": "this invitation?",
+ "This is a preview of this room. Room interactions have been disabled": "This is a preview of this room. Room interactions have been disabled",
+ "This phone number is already in use": "This phone number is already in use",
+ "This room is not accessible by remote Matrix servers": "This room is not accessible by remote Matrix servers",
+ "This room's internal ID is": "This room's internal ID is",
+ "times": "times",
+ "To ban users": "To ban users",
+ "to browse the directory": "to browse the directory",
+ "To configure the room": "To configure the room",
+ "to demote": "to demote",
+ "to favourite": "to favourite",
+ "To invite users into the room": "To invite users into the room",
+ "to join the discussion": "to join the discussion",
+ "To kick users": "To kick users",
+ "To link to a room it must have": "To link to a room it must have",
+ "to make a room or": "to make a room or",
+ "To redact other users' messages": "To redact other users' messages",
+ "To reset your password, enter the email address linked to your account": "To reset your password, enter the email address linked to your account",
+ "to restore": "to restore",
+ "To send events of type": "To send events of type",
+ "To send messages": "To send messages",
+ "to start a chat with someone": "to start a chat with someone",
+ "to tag as %(tagName)s": "to tag as %(tagName)s",
+ "to tag direct chat": "to tag direct chat",
+ "To use it, just wait for autocomplete results to load and tab through them": "To use it, just wait for autocomplete results to load and tab through them",
+ "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question": "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question",
+ "Tried to load a specific point in this room's timeline, but was unable to find it": "Tried to load a specific point in this room's timeline, but was unable to find it",
+ "Turn Markdown off": "Turn Markdown off",
+ "Turn Markdown on": "Turn Markdown on",
+ "%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s)": "%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s)",
+ "Unable to add email address": "Unable to add email address",
+ "Unable to remove contact information": "Unable to remove contact information",
+ "Unable to restore previous session": "Unable to restore previous session",
+ "Unable to verify email address": "Unable to verify email address",
+ "Unban": "Unban",
+ "%(senderName)s unbanned %(targetName)s": "%(senderName)s unbanned %(targetName)s",
+ "Unable to capture screen": "Unable to capture screen",
+ "Unable to enable Notifications": "Unable to enable Notifications",
+ "Unable to load device list": "Unable to load device list",
+ "Unencrypted room": "Unencrypted room",
+ "unencrypted": "unencrypted",
+ "Unknown command": "Unknown command",
+ "unknown device": "unknown device",
+ "unknown error code": "unknown error code",
+ "Unknown room %(roomId)s": "Unknown room %(roomId)s",
+ "unknown": "unknown",
+ "uploaded a file": "uploaded a file",
+ "Upload avatar": "Upload avatar",
+ "Upload Failed": "Upload Failed",
+ "Upload Files": "Upload Files",
+ "Upload file": "Upload file",
+ "Usage": "Usage",
+ "Use with caution": "Use with caution",
+ "User ID": "User ID",
+ "User Interface": "User Interface",
+ "username": "username",
+ "Users": "Users",
+ "User": "User",
+ "Verification Pending": "Verification Pending",
+ "Verification": "Verification",
+ "verified": "verified",
+ "Video call": "Video call",
+ "Voice call": "Voice call",
+ "VoIP conference finished": "VoIP conference finished",
+ "VoIP conference started": "VoIP conference started",
+ "VoIP is unsupported": "VoIP is unsupported",
+ "(warning: cannot be disabled again!)": "(warning: cannot be disabled again!)",
+ "Warning": "Warning",
+ "Who can access this room?": "Who can access this room?",
+ "Who can read history?": "Who can read history?",
+ "Who would you like to add to this room?": "Who would you like to add to this room?",
+ "Who would you like to communicate with?": "Who would you like to communicate with?",
+ "%(senderName)s withdrew %(targetName)s's inivitation": "%(senderName)s withdrew %(targetName)s's inivitation",
+ "Would you like to": "Would you like to",
+ "You are already in a call": "You are already in a call",
+ "You're not in any rooms yet! Press": "You're not in any rooms yet! Press",
+ "You are trying to access %(roomName)s": "You are trying to access %(roomName)s",
+ "You cannot place a call with yourself": "You cannot place a call with yourself",
+ "You cannot place VoIP calls in this browser": "You cannot place VoIP calls in this browser",
+ "You do not have permission to post to this room": "You do not have permission to post to this room",
+ "You have been invited to join this room by %(inviterName)s": "You have been invited to join this room by %(inviterName)s",
+ "You have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, sign in again on each device": "You have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, sign in again on each device",
+ "You have no visible notifications": "You have no visible notifications",
+ "you must be a": "you must be a",
+ "You need to be able to invite users to do that": "You need to be able to invite users to do that",
+ "You need to be logged in": "You need to be logged in",
+ "You need to log back in to generate end-to-end encryption keys for this device and submit the public key to your homeserver. This is a once off; sorry for the inconvenience": "You need to log back in to generate end-to-end encryption keys for this device and submit the public key to your homeserver. This is a once off; sorry for the inconvenience",
+ "Your email address does not appear to be associated with a Matrix ID on this Homeserver": "Your email address does not appear to be associated with a Matrix ID on this Homeserver",
+ "Your password has been reset": "Your password has been reset",
+ "Your password was successfully changed. You will not receive push notifications on other devices until you log back in to them": "Your password was successfully changed. You will not receive push notifications on other devices until you log back in to them",
+ "You seem to be in a call, are you sure you want to quit?": "You seem to be in a call, are you sure you want to quit?",
+ "You seem to be uploading files, are you sure you want to quit?": "You seem to be uploading files, are you sure you want to quit?",
+ "You should not yet trust it to secure data": "You should not yet trust it to secure data",
+ "You will not be able to undo this change as you are promoting the user to have the same power level as yourself": "You will not be able to undo this change as you are promoting the user to have the same power level as yourself",
+ "Sun": "Sun",
+ "Mon": "Mon",
+ "Tue": "Tue",
+ "Wed": "Wed",
+ "Thu": "Thu",
+ "Fri": "Fri",
+ "Sat": "Sat",
+ "Jan": "Jan",
+ "Feb": "Feb",
+ "Mar": "Mar",
+ "Apr": "Apr",
+ "May": "May",
+ "Jun": "Jun",
+ "Jul": "Jul",
+ "Aug": "Aug",
+ "Sep": "Sep",
+ "Oct": "Oct",
+ "Nov": "Nov",
+ "Dec": "Dec",
+ "%(weekDayName)s, %(monthName)s %(day)s %(time)s": "%(weekDayName)s, %(monthName)s %(day)s %(time)s",
+ "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s",
+ "%(weekDayName)s %(time)s": "%(weekDayName)s %(time)s",
+ "Set a display name:": "Set a display name:",
+ "Upload an avatar:": "Upload an avatar:",
+ "This server does not support authentication with a phone number.": "This server does not support authentication with a phone number.",
+ "Missing password.": "Missing password.",
+ "Passwords don't match.": "Passwords don't match.",
+ "Password too short (min %(MIN_PASSWORD_LENGTH)s).": "Password too short (min %(MIN_PASSWORD_LENGTH)s).",
+ "This doesn't look like a valid email address.": "This doesn't look like a valid email address.",
+ "This doesn't look like a valid phone number.": "This doesn't look like a valid phone number.",
+ "User names may only contain letters, numbers, dots, hyphens and underscores.": "User names may only contain letters, numbers, dots, hyphens and underscores.",
+ "An unknown error occurred.": "An unknown error occurred.",
+ "I already have an account": "I already have an account",
+ "An error occured: %(error_string)s": "An error occured: %(error_string)s",
+ "Topic": "Topic",
+ "Make Moderator": "Make Moderator",
+ "Make this room private": "Make this room private",
+ "Share message history with new users": "Share message history with new users",
+ "Encrypt room": "Encrypt room",
+ "There are no visible files in this room": "There are no visible files in this room",
+ "Error changing language": "Error changing language",
+ "Room": "Room",
+ "Room name (optional)": "Room name (optional)",
+ "Who would you like to add to this room?": "Who would you like to add to this room?",
+ "Riot was unable to find the correct Data for the selected Language.": "Riot was unable to find the correct Data for the selected Language.",
+ "Connectivity to the server has been lost.": "Connectivity to the server has been lost.",
+ "Sent messages will be stored until your connection has returned.": "Sent messages will be stored until your connection has returned.",
+ "Auto-complete": "Auto-complete",
+ "Resend all": "Resend all",
+ "(~%(searchCount)s results)": "(~%(searchCount)s results)",
+ "Cancel": "Cancel",
+ "cancel all": "cancel all",
+ "or": "or",
+ "now. You can also select individual messages to resend or cancel.": "now. You can also select individual messages to resend or cancel.",
+ "Active call": "Active call",
+ "Monday": "Monday",
+ "Tuesday": "Tuesday",
+ "Wednesday": "Wednesday",
+ "Thursday": "Thursday",
+ "Friday": "Friday",
+ "Saturday": "Saturday",
+ "Sunday": "Sunday",
+ "bold": "bold",
+ "italic": "italic",
+ "strike": "strike",
+ "underline": "underline",
+ "code":"code",
+ "quote":"quote",
+ "bullet":"bullet",
+ "numbullet":"numbullet",
+ "%(severalUsers)s joined %(repeats)s times": "%(severalUsers)s joined %(repeats)s times",
+ "%(oneUser)s joined %(repeats)s times": "%(oneUser)s joined %(repeats)s times",
+ "%(severalUsers)s joined": "%(severalUsers)s joined",
+ "%(oneUser)s joined": "%(oneUser)s joined",
+ "%(severalUsers)s left %(repeats)s times": "%(severalUsers)s left %(repeats)s times",
+ "%(oneUser)s left %(repeats)s times": "%(oneUser)s left %(repeats)s times",
+ "%(severalUsers)s left": "%(severalUsers)s left",
+ "%(oneUser)s left": "%(oneUser)s left",
+ "%(severalUsers)s joined and left %(repeats)s times": "%(severalUsers)s joined and left %(repeats)s times",
+ "%(oneUser)s joined and left %(repeats)s times": "%(oneUser)s joined and left %(repeats)s times",
+ "%(severalUsers)s joined and left": "%(severalUsers)s joined and left",
+ "%(oneUser)s joined and left": "%(oneUser)s joined and left",
+ "%(severalUsers)s left and rejoined %(repeats)s times": "%(severalUsers)s left and rejoined %(repeats)s times",
+ "%(oneUser)s left and rejoined %(repeats)s times": "%(oneUser)s left and rejoined %(repeats)s times",
+ "%(severalUsers)s left and rejoined": "%(severalUsers)s left and rejoined",
+ "%(oneUser)s left and rejoined": "%(oneUser)s left and rejoined",
+ "%(severalUsers)s rejected their invitations %(repeats)s times": "%(severalUsers)s rejected their invitations %(repeats)s times",
+ "%(oneUser)s rejected his/her invitation %(repeats)s times": "%(oneUser)s rejected his/her invitation %(repeats)s times",
+ "%(severalUsers)s rejected their invitations": "%(severalUsers)s rejected their invitations",
+ "%(oneUser)s rejected his/her invitation": "%(oneUser)s rejected his/her invitation",
+ "%(severalUsers)s had their invitations withdrawn %(repeats)s times": "%(severalUsers)s had their invitations withdrawn %(repeats)s times",
+ "%(oneUser)s had his/her invitation withdrawn %(repeats)s times": "%(oneUser)s had his/her invitation withdrawn %(repeats)s times",
+ "%(severalUsers)s had their invitations withdrawn": "%(severalUsers)s had their invitations withdrawn",
+ "%(oneUser)s had his/her invitation withdrawn": "%(oneUser)s had his/her invitation withdrawn",
+ "were invited %(repeats)s times": "were invited %(repeats)s times",
+ "was invited %(repeats)s times": "was invited %(repeats)s times",
+ "were invited": "were invited",
+ "was invited": "was invited",
+ "were banned %(repeats)s times": "were banned %(repeats)s times",
+ "was banned %(repeats)s times": "was banned %(repeats)s times",
+ "were banned": "were banned",
+ "was banned": "was banned",
+ "were unbanned %(repeats)s times": "were unbanned %(repeats)s times",
+ "was unbanned %(repeats)s times": "was unbanned %(repeats)s times",
+ "were unbanned": "were unbanned",
+ "was unbanned": "was unbanned",
+ "were kicked %(repeats)s times": "were kicked %(repeats)s times",
+ "was kicked %(repeats)s times": "was kicked %(repeats)s times",
+ "were kicked": "were kicked",
+ "was kicked": "was kicked",
+ "%(severalUsers)s changed their name %(repeats)s times": "%(severalUsers)s changed their name %(repeats)s times",
+ "%(oneUser)s changed his/her name %(repeats)s times": "%(oneUser)s changed his/her name %(repeats)s times",
+ "%(severalUsers)s changed their name": "%(severalUsers)s changed their name",
+ "%(oneUser)s changed his/her name": "%(oneUser)s changed his/her name",
+ "%(severalUsers)s changed their avatar %(repeats)s times": "%(severalUsers)s changed their avatar %(repeats)s times",
+ "%(oneUser)s changed his/her avatar %(repeats)s times": "%(oneUser)s changed his/her avatar %(repeats)s times",
+ "%(severalUsers)s changed their avatar": "%(severalUsers)s changed their avatar",
+ "%(oneUser)s changed his/her avatar": "%(oneUser)s changed his/her avatar"
+}
diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json
new file mode 100644
index 0000000000..e393d27bda
--- /dev/null
+++ b/src/i18n/strings/fr.json
@@ -0,0 +1,170 @@
+{
+ "af": "Afrikaans",
+ "ar-ae": "Arabic (U.A.E.)",
+ "ar-bh": "Arabic (Bahrain)",
+ "ar-dz": "Arabic (Algeria)",
+ "ar-eg": "Arabic (Egypt)",
+ "ar-iq": "Arabic (Iraq)",
+ "ar-jo": "Arabic (Jordan)",
+ "ar-kw": "Arabic (Kuwait)",
+ "ar-lb": "Arabic (Lebanon)",
+ "ar-ly": "Arabic (Libya)",
+ "ar-ma": "Arabic (Morocco)",
+ "ar-om": "Arabic (Oman)",
+ "ar-qa": "Arabic (Qatar)",
+ "ar-sa": "Arabic (Saudi Arabia)",
+ "ar-sy": "Arabic (Syria)",
+ "ar-tn": "Arabic (Tunisia)",
+ "ar-ye": "Arabic (Yemen)",
+ "be": "Belarusian",
+ "bg": "Bulgarian",
+ "ca": "Catalan",
+ "cs": "Czech",
+ "da": "Danish",
+ "de-at": "German (Austria)",
+ "de-ch": "German (Switzerland)",
+ "de": "German",
+ "de-li": "German (Liechtenstein)",
+ "de-lu": "German (Luxembourg)",
+ "el": "Greek",
+ "en-au": "English (Australia)",
+ "en-bz": "English (Belize)",
+ "en-ca": "English (Canada)",
+ "en": "English",
+ "en-gb": "English (United Kingdom)",
+ "en-ie": "English (Ireland)",
+ "en-jm": "English (Jamaica)",
+ "en-nz": "English (New Zealand)",
+ "en-tt": "English (Trinidad)",
+ "en-us": "English (United States)",
+ "en-za": "English (South Africa)",
+ "es-ar": "Spanish (Argentina)",
+ "es-bo": "Spanish (Bolivia)",
+ "es-cl": "Spanish (Chile)",
+ "es-co": "Spanish (Colombia)",
+ "es-cr": "Spanish (Costa Rica)",
+ "es-do": "Spanish (Dominican Republic)",
+ "es-ec": "Spanish (Ecuador)",
+ "es-gt": "Spanish (Guatemala)",
+ "es-hn": "Spanish (Honduras)",
+ "es-mx": "Spanish (Mexico)",
+ "es-ni": "Spanish (Nicaragua)",
+ "es-pa": "Spanish (Panama)",
+ "es-pe": "Spanish (Peru)",
+ "es-pr": "Spanish (Puerto Rico)",
+ "es-py": "Spanish (Paraguay)",
+ "es": "Spanish (Spain)",
+ "es-sv": "Spanish (El Salvador)",
+ "es-uy": "Spanish (Uruguay)",
+ "es-ve": "Spanish (Venezuela)",
+ "et": "Estonian",
+ "eu": "Basque (Basque)",
+ "fa": "Farsi",
+ "fi": "Finnish",
+ "fo": "Faeroese",
+ "fr-be": "French (Belgium)",
+ "fr-ca": "French (Canada)",
+ "fr-ch": "French (Switzerland)",
+ "fr": "French",
+ "fr-lu": "French (Luxembourg)",
+ "ga": "Irish",
+ "gd": "Gaelic (Scotland)",
+ "he": "Hebrew",
+ "hi": "Hindi",
+ "hr": "Croatian",
+ "hu": "Hungarian",
+ "id": "Indonesian",
+ "is": "Icelandic",
+ "it-ch": "Italian (Switzerland)",
+ "it": "Italian",
+ "ja": "Japanese",
+ "ji": "Yiddish",
+ "ko": "Korean (Johab)",
+ "lt": "Lithuanian",
+ "lv": "Latvian",
+ "mk": "Macedonian (FYROM)",
+ "ms": "Malaysian",
+ "mt": "Maltese",
+ "nl-be": "Dutch (Belgium)",
+ "nl": "Dutch",
+ "no": "Norwegian",
+ "pl": "Polish",
+ "pt-br": "Brazilian Portuguese",
+ "pt": "Portuguese",
+ "rm": "Rhaeto-Romanic",
+ "ro-mo": "Romanian (Republic of Moldova)",
+ "ro": "Romanian",
+ "ru-mo": "Russian (Republic of Moldova)",
+ "ru": "Russian",
+ "sb": "Sorbian",
+ "sk": "Slovak",
+ "sl": "Slovenian",
+ "sq": "Albanian",
+ "sr": "Serbian (Latin)",
+ "sv-fi": "Swedish (Finland)",
+ "sv": "Swedish",
+ "sx": "Sutu",
+ "sz": "Sami (Lappish)",
+ "th": "Thai",
+ "tn": "Tswana",
+ "tr": "Turkish",
+ "ts": "Tsonga",
+ "uk": "Ukrainian",
+ "ur": "Urdu",
+ "ve": "Venda",
+ "vi": "Vietnamese",
+ "xh": "Xhosa",
+ "zh-cn": "Chinese (PRC)",
+ "zh-hk": "Chinese (Hong Kong SAR)",
+ "zh-sg": "Chinese (Singapore)",
+ "zh-tw": "Chinese (Taiwan)",
+ "zu": "Zulu",
+ "anyone.": "anyone",
+ "Direct Chat": "Chat direct",
+ "Direct chats": "Direct chats",
+ "Disable inline URL previews by default": "Disable inline URL previews by default",
+ "Disinvite": "Disinvite",
+ "Display name": "Display name",
+ "Displays action": "Displays action",
+ "Don't send typing notifications": "Don't send typing notifications",
+ "Download %(text)s": "Download %(text)s",
+ "Drop here %(toAction)s": "Drop here %(toAction)s",
+ "Drop here to tag %(section)s": "Drop here to tag %(section)s",
+ "Ed25519 fingerprint": "Ed25519 fingerprint",
+ "Email Address": "Email Address",
+ "Email, name or matrix ID": "Email, name or matrix ID",
+ "Emoji": "Emoji",
+ "Enable encryption": "Enable encryption",
+ "Encrypted messages will not be visible on clients that do not yet implement encryption": "Encrypted messages will not be visible on clients that do not yet implement encryption",
+ "Encrypted room": "Encrypted room",
+ "%(senderName)s ended the call": "%(senderName)s ended the call",
+ "End-to-end encryption information": "End-to-end encryption information",
+ "End-to-end encryption is in beta and may not be reliable": "End-to-end encryption is in beta and may not be reliable",
+ "Enter Code": "Enter Code",
+ "Error": "Erreur",
+ "Event information": "Event information",
+ "Existing Call": "Existing Call",
+ "Export E2E room keys": "Export E2E room keys",
+ "Failed to ban user": "Failed to ban user",
+ "Failed to change password. Is your password correct?": "Failed to change password. Is your password correct?",
+ "Failed to change power level": "Failed to change power level",
+ "Failed to delete device": "Failed to delete device",
+ "Failed to forget room %(errCode)s": "Echec lors de l'oublie du salon %(errCode)s",
+ "Please Register": "Veuillez vous enregistrer",
+ "Remove": "Supprimer",
+ "was banned": "banned",
+ "was invited": "invited",
+ "was kicked": "kicked",
+ "was unbanned": "unbanned",
+ "Monday": "Lundi",
+ "Tuesday": "Mardi",
+ "Wednesday": "Mercredi",
+ "Thursday": "Jeudi",
+ "Friday": "Vendredi",
+ "Saturday": "Samedi",
+ "Sunday": "Dimanche",
+ "bold": "gras",
+ "italic": "italique",
+ "strike": "barré",
+ "underline": "souligné"
+}
diff --git a/src/i18n/strings/ml.json b/src/i18n/strings/ml.json
new file mode 100644
index 0000000000..2c63c08510
--- /dev/null
+++ b/src/i18n/strings/ml.json
@@ -0,0 +1,2 @@
+{
+}
diff --git a/src/i18n/strings/pt.json b/src/i18n/strings/pt.json
new file mode 100644
index 0000000000..60ab244401
--- /dev/null
+++ b/src/i18n/strings/pt.json
@@ -0,0 +1,503 @@
+{
+ "accept": "aceitar",
+ "accepted an invitation": "aceitou um convite",
+ "accepted the invitation for": "aceitou o convite para",
+ "Account": "Conta",
+ "Add email address": "Adicionar endereço de email",
+ "Add phone number": "Adicionar número de telefone",
+ "Admin": "Administrador/a",
+ "Advanced": "Avançado",
+ "Algorithm": "Algoritmo",
+ "all room members, from the point they are invited.": "todos os membros da sala, a partir de quando foram convidados",
+ "all room members, from the point they joined.": "todos os membros da sala, a partir de quando entraram",
+ "all room members": "todos os membros da sala",
+ "an address": "um endereço",
+ "and": "e",
+ "An email has been sent to": "Um email foi enviado para",
+ "A new password must be entered.": "Uma nova senha precisa ser informada.",
+ "answered the call.": "respondeu à chamada.",
+ "anyone": "qualquer um",
+ "Anyone who knows the room's link, apart from guests": "Qualquer pessoa que tenha o link da sala, exceto visitantes",
+ "Anyone who knows the room's link, including guests": "Qualquer pessoa que tenha o link da sala, incluindo visitantes",
+ "Are you sure you want to leave the room?": "Você tem certeza que deseja sair da sala?",
+ "Are you sure you want to reject the invitation?": "Você tem certeza que deseja rejeitar este convite?",
+ "Are you sure you want upload the following files?": "Você tem certeza que deseja enviar os seguintes arquivos?",
+ "banned": "baniu",
+ "Banned users": "Usuárias/os banidas/os",
+ "Bans user with given id": "Banir usuários com o identificador informado",
+ "Blacklisted": "Bloqueado",
+ "Bug Report": "Repotar problemas de funcionamento",
+ "Bulk Options": "Opcões de Batelada",
+ "Can't load user settings": "Não é possível carregar configurações de usuário",
+ "changed avatar": "mudou sua imagem de perfil (avatar)",
+ "changed name": "mudou seu nome",
+ "changed their display name from": "mudou seu nome para",
+ "changed their profile picture": "alterou sua foto de perfil",
+ "changed the power level of": "mudou o nível de permissões de",
+ "changed the room name to": "mudou o nome da sala para",
+ "changed the topic to": "mudou o tópico para",
+ "Changes to who can read history will only apply to future messages in this room": "As mudanças sobre quem pode ler o histórico da sala só serão aplicadas às mensagens futuras nesta sala",
+ "Changes your display nickname": "Troca o seu apelido",
+ "Claimed Ed25519 fingerprint key": "Chave reivindicada da Impressão Digital Ed25519",
+ "Clear Cache and Reload": "Limpar Memória Cache e Recarregar",
+ "Clear Cache": "Limpar Memória Cache",
+ "Click here": "Clique aqui",
+ "Click here to fix": "Clique aqui para resolver isso",
+ "Commands": "Comandos",
+ "Confirm your new password": "Confirme a nova senha",
+ "Continue": "Continuar",
+ "Could not connect to the integration server": "Não foi possível conectar ao servidor de integrações",
+ "Create an account": "Criar uma conta",
+ "Create a new account": "Criar uma conta",
+ "Create Room": "Criar Sala",
+ "Cryptography": "Criptografia",
+ "Curve25519 identity key": "Chave de Indetificação Curve25519",
+ "Deactivate Account": "Desativar conta",
+ "Deactivate my account": "Desativar minha conta",
+ "decline": "rejeitar",
+ "Decryption error": "Erro de descriptografia",
+ "Default": "Padrão",
+ "demote": "reduzir prioridade",
+ "Deops user with given id": "Retirar função de moderador do usuário com o identificador informado",
+ "Device ID": "Identificador do dispositivo",
+ "Devices will not yet be able to decrypt history from before they joined the room": "Os dispositivos não serão ainda capazes de descriptografar o histórico anterior à sua entrada na sala",
+ "Direct Chat": "Conversa pessoal",
+ "Disable inline URL previews by default": "Desabilitar visualizações prévias por padrão",
+ "Display name": "Nome",
+ "Displays action": "Visualizar atividades",
+ "Ed25519 fingerprint": "Impressão Digital Ed25519",
+ "Email Address": "endereço de email",
+ "Email, name or matrix ID": "Email, nome ou ID matrix",
+ "Emoji": "Emoji",
+ "Encrypted messages will not be visible on clients that do not yet implement encryption": "Mensagens criptografadas não serão visíveis em clientes que ainda não implementaram criptografia",
+ "Encrypted room": "Sala criptografada",
+ "Encryption is enabled in this room": "Criptografia está habilitada nesta sala",
+ "Encryption is not enabled in this room": "Criptografia não está habilitada nesta sala",
+ "ended the call.": "chamada encerrada.",
+ "End-to-end encryption information": "Informação criptografada ponta-a-ponta",
+ "End-to-end encryption is in beta and may not be reliable": "A criptografia ponta a ponta está em estágio beta e não deve ser totalmente confiável",
+ "Error": "Erro",
+ "Event information": "Informação do evento",
+ "Export E2E room keys": "Exportar chaves ponta-a-ponta da sala",
+ "Failed to change password. Is your password correct?": "Não foi possível modificar a senha. A senha informada está correta?",
+ "Failed to forget room": "Não foi possível esquecer a sala",
+ "Failed to leave room": "Falha ao tentar deixar a sala",
+ "Failed to reject invitation": "Falha ao tentar rejeitar convite",
+ "Failed to send email: ": "Falha ao tentar enviar email",
+ "Failed to set avatar.": "Falha ao tentar definir foto do perfil.",
+ "Failed to unban": "Não foi possível desfazer o banimento",
+ "Failed to upload file": "Falha ao enviar o arquivo",
+ "favourite": "favoritar",
+ "Favourite": "Favorito",
+ "Favourites": "Favoritos",
+ "Filter room members": "Filtrar membros de sala",
+ "Forget room": "Esquecer sala",
+ "Forgot your password?": "Esqueceu sua senha?",
+ "For security, logging out will delete any end-to-end encryption keys from this browser. If you want to be able to decrypt your conversation history from future Riot sessions, please export your room keys for safe-keeping.": "Por segurança, deslogar irá remover qualquer chave de criptografia ponta-a-ponta deste navegador. Caso deseje descriptografar o histórico das suas conversas E2E em sessões Riot futuras, por favor exporte as chaves da sala para sua garantia.",
+ "For security, this session has been signed out. Please sign in again": "Por questões de segurança, esta sessão foi encerrada. Por gentileza conecte-se novamente",
+ "Found a bug?": "Encontrou um problema de funcionamento do sistema?",
+ "Guests cannot join this room even if explicitly invited": "Visitantes não podem entrar nesta sala, mesmo se forem explicitamente convidadas/os",
+ "Guests can't set avatars. Please register": "Convidados não podem definir uma foto do perfil. Por favor, registre-se",
+ "Guests can't use labs features. Please register": "Convidados não podem usar as funcionalidades de laboratório (lab), por gentileza se registre",
+ "Guest users can't upload files. Please register to upload": "Usuários não podem fazer envio de arquivos. Por favor se cadastre para enviar arquivos",
+ "had": "teve",
+ "Hangup": "Desligar",
+ "Historical": "Histórico",
+ "Homeserver is": "Servidor padrão é",
+ "Identity Server is": "O servidor de identificação é",
+ "I have verified my email address": "Eu verifiquei o meu endereço de email",
+ "Import E2E room keys": "Importar chave de criptografia ponta-a-ponta (E2E) da sala",
+ "Invalid Email Address": "Endereço de email inválido",
+ "invited": "convidou",
+ "Invite new room members": "Convidar novo membros para sala",
+ "Invites": "Convidar",
+ "Invites user with given id to current room": "Convidar usuários com um dado identificador para esta sala",
+ "is a": "é um(a)",
+ "I want to sign in with": "Quero entrar",
+ "joined and left": "entrou e saiu",
+ "joined": "entrou",
+ "joined the room": "entrou na sala",
+ "Joins room with given alias": "Entra na sala com o nome informado",
+ "Kicks user with given id": "Remove usuário com o identificador informado",
+ "Labs": "Laboratório",
+ "Leave room": "Sair da sala",
+ "left and rejoined": "saiu e entrou novamente",
+ "left": "saiu",
+ "left the room": "saiu da sala",
+ "Logged in as": "Logado como",
+ "Login as guest": "Entrar como visitante",
+ "Logout": "Sair",
+ "Low priority": "Baixa prioridade",
+ "made future room history visible to": "deixou o histórico futuro da sala visível para",
+ "Manage Integrations": "Gerenciar integrações",
+ "Members only": "Apenas integrantes da sala",
+ "Mobile phone number": "Telefone celular",
+ "Moderator": "Moderador/a",
+ "my Matrix ID": "com meu ID do Matrix",
+ "Name": "Nome",
+ "Never send encrypted messages to unverified devices from this device": "Nunca envie mensagens criptografada para um dispositivo não verificado a partir deste dispositivo",
+ "Never send encrypted messages to unverified devices in this room from this device": "Nunca envie mensagens criptografadas para dispositivos não verificados nesta sala a partir deste dispositivo",
+ "New password": "Nova senha",
+ "New passwords must match each other.": "As novas senhas informadas precisam ser idênticas.",
+ "none": "nenhum",
+ "Notifications": "Notificações",
+ " (not supported by this browser)": "não suportado por este navegador",
+ "": "",
+ "NOT verified": "NÃO verificado",
+ "No users have specific privileges in this room": "Nenhum/a usuário/a possui privilégios específicos nesta sala",
+ "olm version: ": "Versão do olm: ",
+ "Once encryption is enabled for a room it cannot be turned off again (for now)": "Assim que a criptografia é ativada para uma sala, ela não poderá ser desativada novamente (ainda)",
+ "Once you've followed the link it contains, click below": "Quando você tiver clicado no link que está no email, clique o botão abaixo",
+ "Only people who have been invited": "Apenas pessoas que tenham sido convidadas",
+ "or": "ou",
+ "other": "outro",
+ "others": "outros",
+ "Password": "Senha",
+ "People": "Pessoas",
+ "Permissions": "Permissões",
+ "Phone": "Telefone",
+ "placed a": "iniciou uma",
+ "Please check your email and click on the link it contains. Once this is done, click continue.": "Por favor verifique seu email e clique no link enviado. Quando finalizar este processo, clique para continuar.",
+ "Please Register": "Por favor, cadastre-se",
+ "Privacy warning": "Alerta sobre privacidade",
+ "Privileged Users": "Usuárias/os privilegiadas/os",
+ "Profile": "Perfil",
+ "Refer a friend to Riot: ": "Indicar um amigo para participar",
+ "rejected": "recusou",
+ "rejected the invitation.": "rejeitou o convite.",
+ "Reject invitation": "Rejeitar convite",
+ "Remove Contact Information?": "Remover informação de contato?",
+ "removed their display name": "removeu seu nome",
+ "removed their profile picture": "removeu sua foto de perfil",
+ "Remove": "Remover",
+ "requested a VoIP conference": "requisitou uma conferência VoIP",
+ "Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved ": "Atualmente, ao alterar sua senha, você também zera todas as chaves de criptografia ponta-a-ponta em todos os dipositivos, fazendo com que o histórico de conversas da sala não possa mais ser lido, a não ser que você antes exporte suas chaves de sala e as reimporte após a alteração da senha. No futuro, isso será melhorado ",
+ "restore": "restaurar",
+ "Return to app": "Retornar ao aplicativo",
+ "Return to login screen": "Retornar à tela de login",
+ "Room Colour": "Cores da sala",
+ "Room name (optional)": "Título da Sala (opcional)",
+ "Rooms": "Salas",
+ "Scroll to unread messages": "Rolar para baixo para ver as mensagens não lidas",
+ "Searches DuckDuckGo for results": "Buscar por resultados no buscador DuckDuckGo",
+ "Send a message (unencrypted)": "Enviar uma mensagem",
+ "Send an encrypted message": "Enviar uma mensagem criptografada",
+ "Sender device information": "Informação do dispositivo emissor",
+ "Send Invites": "Enviar convites",
+ "Send Reset Email": "Enviar email para redefinição de senha",
+ "sent an image": "enviou uma imagem",
+ "sent an invitation to": "enviou um convite para",
+ "sent a video": "enviou um vídeo",
+ "Server may be unavailable or overloaded": "Servidor pode estar indisponível ou sobrecarregado",
+ "Server may be unavailable, overloaded, or you hit a bug": "Servidor pode estar indisponível, sobrecarregado ou aconteceu um erro de execução",
+ "Session ID": "Identificador de sessão",
+ "set a profile picture": "colocou uma foto de perfil",
+ "set their display name to": "configurou seu nome para",
+ "Settings": "Configurações",
+ "Show panel": "Mostrar painel",
+ "Signed Out": "Deslogar",
+ "Sign in": "Entrar",
+ "Sign out": "Sair",
+ "since the point in time of selecting this option": "a partir do momento em que você selecionar esta opção",
+ "since they joined": "desde que entraram na sala",
+ "since they were invited": "desde que foram convidadas/os",
+ "Someone": "Alguém",
+ "Sorry, this homeserver is using a login which is not recognised ": "Desculpe, o servidor padrão está usando um login de acesso que não é válido ",
+ "Start a chat": "Começar uma conversa",
+ "Start Chat": "Começar conversa",
+ "Success": "Sucesso",
+ "tag as": "etiquetar como",
+ "tag direct chat": "definir como conversa pessoal",
+ "The default role for new room members is": "O papel padrão para novas/os integrantes da sala é",
+ "The email address linked to your account must be entered.": "O endereço de email relacionado a sua conta precisa ser informado.",
+ "their invitations": "seus convites",
+ "their invitation": "seu convite",
+ "These are experimental features that may break in unexpected ways. Use with caution": "Estes são recursos experimentais que podem não funcionar corretamente. Use com cuidado.",
+ "The visibility of existing history will be unchanged": "A visibilidade do histórico atual não será alterada",
+ "This doesn't appear to be a valid email address": "Este não aparenta ser um endereço de email válido",
+ "this invitation?": "este convite?",
+ "This is a preview of this room. Room interactions have been disabled": "Esta é uma pré visualização desta sala. As interações com a sala estão desabilitadas",
+ "This room is not accessible by remote Matrix servers": "Esta sala não é acessível para servidores Matrix remotos",
+ "This room's internal ID is": "O ID interno desta sala é",
+ "times": "vezes",
+ "To ban users": "Para banir usuárias/os",
+ "To configure the room": "Para poder configurar a sala",
+ "To invite users into the room": "Para convidar usuárias/os para esta sala",
+ "to join the discussion": "para se juntar à conversa",
+ "To kick users": "Para poder remover pessoas da sala",
+ "To link to a room it must have": "Para fazer um link para uma sala, ela deve ter",
+ "To redact messages": "Para poder apagar mensagens",
+ "To reset your password, enter the email address linked to your account": "Para redefinir sua senha, entre com o email da sua conta",
+ "To send events of type": "Para enviar eventos do tipo",
+ "To send messages": "Para enviar mensagens",
+ "turned on end-to-end encryption (algorithm": "acionou a encriptação ponta-a-ponta (algoritmo",
+ "Unable to add email address": "Não foi possível adicionar endereço de email",
+ "Unable to remove contact information": "Não foi possível remover informação de contato",
+ "Unable to verify email address": "Não foi possível verificar o endereço de email",
+ "Unban": "Desfazer banimento",
+ "Unencrypted room": "Sala não criptografada",
+ "unencrypted": "não criptografado",
+ "unknown device": "dispositivo desconhecido",
+ "unknown error code": "código de erro desconhecido",
+ "unknown": "desconhecido",
+ "Upload avatar": "Enviar icone de perfil de usuário",
+ "uploaded a file": "enviou um arquivo",
+ "Upload Files": "Enviar arquivos",
+ "Upload file": "Enviar arquivo",
+ "User ID": "Identificador de Usuário",
+ "User Interface": "Interface de usuário",
+ "username": "Nome de usuária/o",
+ "Users": "Usuários",
+ "User": "Usuária/o",
+ "Verification Pending": "Verificação pendente",
+ "Verification": "Verificação",
+ "verified": "verificado",
+ "Video call": "Chamada de vídeo",
+ "Voice call": "Chamada de voz",
+ "VoIP conference finished": "Conferência VoIP encerrada",
+ "VoIP conference started": "Conferência VoIP iniciada",
+ "(warning: cannot be disabled again!)": "(atenção: esta operação não poderá ser desfeita depois!)",
+ "Warning": "Atenção",
+ "was banned": "banida/o",
+ "was invited": "convidada/o",
+ "was kicked": "retirada/o da sala",
+ "was unbanned": "des-banida/o",
+ "was": "foi",
+ "were": "foram",
+ "Who can access this room?": "Quem pode acessar esta sala?",
+ "Who can read history?": "Quem pode ler o histórico da sala?",
+ "Who would you like to add to this room?": "Quem Você gostaria que fosse adicionado a esta sala?",
+ "Who would you like to communicate with?": "Com quem você gostaria de se comunicar?",
+ "withdrawn": "retirado",
+ "Would you like to": "Você gostaria de",
+ "You are trying to access": "Você está tentando acessar a sala",
+ "You do not have permission to post to this room": "Você não tem permissão de postar nesta sala",
+ "You have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, sign in again on each device": "Você foi desconectada/o de todos os dispositivos e portanto não receberá mais notificações no seu celular ou no computador. Para reativar as notificações, entre novamente em cada um dos dispositivos que costuma usar",
+ "You have no visible notifications": "Voce não possui notificações visíveis",
+ "you must be a": "você precisa ser",
+ "Your password has been reset": "Sua senha foi redefinida",
+ "Your password was successfully changed. You will not receive push notifications on other devices until you log back in to them": "Sua senha foi alterada com sucesso. Você não receberá notificações em outros dispositivos até que você logue novamente por eles",
+ "You should not yet trust it to secure data": "Você não deve confiar nela ainda para preservar seus dados",
+ "Sun": "Dom",
+ "Mon": "Seg",
+ "Tue": "Ter",
+ "Wed": "Qua",
+ "Thu": "Qui",
+ "Fri": "Sex",
+ "Sat": "Sáb",
+ "Jan": "Jan",
+ "Feb": "Fev",
+ "Mar": "Mar",
+ "Apr": "Abr",
+ "May": "Mai",
+ "Jun": "Jun",
+ "Jul": "Jul",
+ "Aug": "Ago",
+ "Sep": "Set",
+ "Oct": "Out",
+ "Nov": "Nov",
+ "Dec": "Dez",
+ "%(weekDayName)s, %(monthName)s %(day)s %(time)s": "%(weekDayName)s, %(day)s de %(monthName)s às %(time)s",
+ "%(weekDayName)s %(time)s": "%(weekDayName)s às %(time)s",
+ "en": "Inglês",
+ "pt-br": "Português do Brasil",
+ "de": "Alemão",
+ "da": "Dinamarquês",
+ "ru": "Russo",
+ "%(targetName)s accepted an invitation": "%(targetName)s aceitou um convite",
+ "%(targetName)s accepted the invitation for %(displayName)s": "%(targetName)s aceitou o convite para %(displayName)s",
+ "all room members, from the point they are invited": "todas/os as/os integrantes da sala, a partir do momento em que foram convidadas/os",
+ "all room members, from the point they joined": "todas/os as/os integrantes da sala, a partir do momento em que entraram na sala",
+ "%(names)s and %(lastPerson)s are typing": "%(names)s e %(lastPerson)s estão escrevendo",
+ "%(names)s and one other are typing": "%(names)s e uma outra pessoa estão escrevendo",
+ "%(names)s and %(count)s others are typing": "%(names)s e %(count)s outras pessoas estão escrevendo",
+ "%(senderName)s answered the call": "%(senderName)s atendeu à chamada",
+ "anyone.": "qualquer pessoa",
+ "%(senderName)s banned %(targetName)s": "%(senderName)s removeu %(targetName)s da sala",
+ "Call Timeout": "Tempo esgotado. Chamada encerrada",
+ "%(senderName)s changed their display name from %(oldDisplayName)s to %(displayName)s": "%(senderName)s mudou seu nome público de %(oldDisplayName)s para %(displayName)s",
+ "%(senderName)s changed their profile picture": "%(senderName)s alterou sua imagem de perfil",
+ "%(senderName)s changed the power level of %(powerLevelDiffText)s": "%(senderName)s alterou o nível de permissões de %(powerLevelDiffText)s",
+ "%(senderDisplayName)s changed the room name to %(roomName)s": "%(senderDisplayName)s alterou o nome da sala para %(roomName)s",
+ "%(senderDisplayName)s changed the topic to %(topic)s": "%(senderDisplayName)s alterou o tópico para %(topic)s",
+ "click to reveal": "clique para ver",
+ "Conference call failed": "Chamada de conferência falhou",
+ "Conference calling is in development and may not be reliable": "Chamadas de conferência estão em desenvolvimento e portanto podem não funcionar",
+ "Conference calls are not supported in encrypted rooms": "Chamadas de conferência não são possíveis em salas criptografadas",
+ "Conference calls are not supported in this client": "Chamadas de conferência não são possíveis neste navegador",
+ "/ddg is not a command": "/ddg não é um comando",
+ "Drop here %(toAction)s": "Arraste aqui %(toAction)s",
+ "Drop here to tag %(section)s": "Arraste aqui para marcar como %(section)s",
+ "%(senderName)s ended the call": "%(senderName)s finalizou a chamada",
+ "Existing Call": "Chamada em andamento",
+ "Failed to lookup current room": "Não foi possível buscar na sala atual",
+ "Failed to send email": "Não foi possível enviar email",
+ "Failed to send request": "Não foi possível mandar requisição",
+ "Failed to set up conference call": "Não foi possível montar a chamada de conferência",
+ "Failed to verify email address: make sure you clicked the link in the email": "Não foi possível verificar o endereço de email: verifique se você realmente clicou no link que está no seu email",
+ "Failure to create room": "Não foi possível criar a sala",
+ "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s de %(fromPowerLevel)s para %(toPowerLevel)s",
+ "Guest users can't create new rooms. Please register to create room and start a chat": "Visitantes não podem criar novas salas. Por favor, registre-se para criar uma sala e iniciar uma conversa",
+ "%(senderName)s invited %(targetName)s": "%(senderName)s convidou %(targetName)s",
+ "%(displayName)s is typing": "%(displayName)s está escrevendo",
+ "%(targetName)s joined the room": "%(targetName)s entrou na sala",
+ "%(senderName)s kicked %(targetName)s": "%(senderName)s removeu %(targetName)s da sala",
+ "%(targetName)s left the room": "%(targetName)s saiu da sala",
+ "%(senderName)s made future room history visible to": "%(senderName)s deixou o histórico futuro da sala visível para",
+ "Missing room_id in request": "Faltou o id da sala na requisição",
+ "Missing user_id in request": "Faltou o id de usuário na requisição",
+ "Must be viewing a room": "Tem que estar visualizando uma sala",
+ "New Composer & Autocomplete": "Nova ferramenta de formatação de mensagens e autocompletar",
+ "(not supported by this browser)": "(não é compatível com este navegador)",
+ "olm version": "versão olm",
+ "%(senderName)s placed a %(callType)s call": "%(senderName)s fez uma chamada de %(callType)s",
+ "Power level must be positive integer": "O nível de permissões tem que ser um número inteiro e positivo",
+ "Press": "Aperte",
+ "Reason": "Razão",
+ "Refer a friend to Riot": "Recomende Riot a um/a amigo/a",
+ "%(targetName)s rejected the invitation": "%(targetName)s recusou o convite",
+ "%(senderName)s removed their display name (%(oldDisplayName)s)": "%(senderName)s removeu o seu nome público (%(oldDisplayName)s)",
+ "%(senderName)s removed their profile picture": "%(senderName)s removeu sua imagem de perfil",
+ "%(senderName)s requested a VoIP conference": "%(senderName)s está solicitando uma conferência de voz",
+ "Riot does not have permission to send you notifications - please check your browser settings": "Riot não tem permissões para enviar notificações a você - por favor, verifique as configurações do seu navegador",
+ "Riot was not given permission to send notifications - please try again": "Riot não tem permissões para enviar notificações a você - por favor, tente novamente",
+ "Room %(roomId)s not visible": "A sala %(roomId)s não está visível",
+ "%(senderDisplayName)s sent an image": "%(senderDisplayName)s enviou uma imagem",
+ "%(senderName)s sent an invitation to %(targetDisplayName)s to join the room": "%(senderName)s enviou um convite para %(targetDisplayName)s entrar na sala",
+ "%(senderName)s set a profile picture": "%(senderName)s definiu uma imagem de perfil",
+ "%(senderName)s set their display name to %(displayName)s": "%(senderName)s definiu seu nome público para %(displayName)s",
+ "tag as %(tagName)s": "marcar como %(tagName)s",
+ "This email address is already in use": "Este endereço de email já está sendo usado",
+ "This email address was not found": "Este endereço de email não foi encontrado",
+ "The file '%(fileName)s' exceeds this home server's size limit for uploads": "O arquivo '%(fileName)s' ultrapassa o limite de tamanho que nosso servidor permite enviar",
+ "The file '%(fileName)s' failed to upload": "Não foi possível enviar o arquivo '%(fileName)s",
+ "The remote side failed to pick up": "Houve alguma falha que não permitiu a outra pessoa atender à chamada",
+ "This room is not recognised": "Esta sala não é reconhecida",
+ "These are experimental features that may break in unexpected ways": "Estas são funcionalidades experimentais que podem apresentar falhas",
+ "This phone number is already in use": "Este número de telefone já está sendo usado",
+ "to browse the directory": "para navegar na lista pública de salas",
+ "to demote": "para reduzir prioridade",
+ "to favourite": "para favoritar",
+ "to make a room or": "para criar uma sala ou",
+ "To redact other users' messages": "Para apagar mensagens de outras pessoas",
+ "to restore": "para restaurar",
+ "to start a chat with someone": "para iniciar uma conversa com alguém",
+ "to tag direct chat": "para marcar a conversa como pessoal",
+ "To use it, just wait for autocomplete results to load and tab through them": "Para usar esta funcionalidade, espere o carregamento dos resultados de autocompletar e então escolha entre as opções",
+ "%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s)": "%(senderName)s ativou criptografia ponta a ponta (algoritmo %(algorithm)s)",
+ "Unable to restore previous session": "Não foi possível restaurar a sessão anterior",
+ "%(senderName)s unbanned %(targetName)s": "%(senderName)s desfez o banimento de %(targetName)s",
+ "Unable to capture screen": "Não foi possível capturar a imagem da tela",
+ "Unable to enable Notifications": "Não foi possível ativar as notificações",
+ "Upload Failed": "O envio falhou",
+ "Usage": "Uso",
+ "Use with caution": "Use com cautela",
+ "VoIP is unsupported": "Chamada de voz não permitida",
+ "%(senderName)s withdrew %(targetName)s's inivitation": "%(senderName)s desfez o convite a %(targetName)s's",
+ "You are already in a call": "Você já está em uma chamada",
+ "You're not in any rooms yet! Press": "Você ainda não está em nenhuma sala! Pressione",
+ "You are trying to access %(roomName)s": "Você está tentando acessar a sala %(roomName)s",
+ "You cannot place a call with yourself": "Você não pode iniciar uma chamada",
+ "You cannot place VoIP calls in this browser": "Você não pode fazer chamadas de voz neste navegador",
+ "You need to be able to invite users to do that": "Para fazer isso, você tem que ter permissão para convidar outras pessoas",
+ "You need to be logged in": "Você tem que estar logado",
+ "You need to log back in to generate end-to-end encryption keys for this device and submit the public key to your homeserver. This is a once off; sorry for the inconvenience": "É necessário que você faça login novamente para poder gerar as chaves de criptografia ponta-a-ponta para este dispositivo e então enviar sua chave pública para o servidor. Pedimos desculpas pela inconveniência, é preciso fazer isso apenas única uma vez",
+ "Your email address does not appear to be associated with a Matrix ID on this Homeserver": "O seu endereço de email não parece estar associado a uma conta de usuária/o Matrix neste servidor",
+ "Set a display name:": "Defina um nome público para você:",
+ "Upload an avatar:": "Envie uma imagem de perfil para identificar você:",
+ "This server does not support authentication with a phone number.": "Este servidor não permite a autenticação através de números de telefone.",
+ "Missing password.": "Faltou a senha.",
+ "Passwords don't match.": "As senhas não conferem.",
+ "Password too short (min %(MIN_PASSWORD_LENGTH)s).": "A senha é muito curta (o mínimo é de %(MIN_PASSWORD_LENGTH)s caracteres).",
+ "This doesn't look like a valid email address.": "Este endereço de email não parece ser válido.",
+ "This doesn't look like a valid phone number.": "Este número de telefone não parece ser válido.",
+ "User names may only contain letters, numbers, dots, hyphens and underscores.": "Nomes de usuária/o podem conter apenas letras, números, pontos, hífens e linha inferior (_).",
+ "An unknown error occurred.": "Um erro desconhecido ocorreu.",
+ "I already have an account": "Eu já tenho uma conta",
+ "An error occured: %(error_string)s": "Um erro ocorreu: %(error_string)s",
+ "Topic": "Tópico",
+ "Make this room private": "Tornar esta sala privada",
+ "Share message history with new users": "Compartilhar histórico de mensagens com novas/os usuárias/os",
+ "Encrypt room": "Criptografar esta sala",
+ "There are no visible files in this room": "Não há arquivos públicos nesta sala",
+ "Error changing language": "Erro ao mudar de idioma",
+ "Riot was unable to find the correct Data for the selected Language.": "Não foi possível encontrar os dados para o idioma selecionado.",
+ "Connectivity to the server has been lost.": "A conexão com o servidor foi perdida. Verifique sua conexão de internet.",
+ "Sent messages will be stored until your connection has returned.": "Imagens enviadas ficarão armazenadas até que sua conexão seja reestabelecida.",
+ "Auto-complete": "Autocompletar",
+ "Resend all": "Reenviar todas as mensagens",
+ "cancel all": "cancelar todas",
+ "now. You can also select individual messages to resend or cancel.": "agora. Você também pode escolher mensagens individuais e definir se vai reenviar ou cancelar o envio.",
+ "Active call": "Chamada ativa",
+ "af": "Afrikaans",
+ "ar-ae": "Árabe (U.A.E.)",
+ "ar-bh": "Árabe (Bahrain)",
+ "ar-dz": "Árabe (Algéria)",
+ "ar-eg": "Árabe (Egipto)",
+ "ar-tn": "Árabe (Tunisia)",
+ "be": "Bielorusso",
+ "bg": "Búlgaro",
+ "ca": "Catalão",
+ "cs": "Checo",
+ "de-at": "Alemao (Austria)",
+ "el": "Grego",
+ "en-au": "Inglês (Austrália)",
+ "en-ca": "Inglês (Canadá)",
+ "en-ie": "Inglês (Irlanda)",
+ "en-nz": "Inglês (Nova Zelândia)",
+ "en-us": "Inglês (Estados Unidos)",
+ "es-ar": "Espanhol (Argentina)",
+ "es-mx": "Espanhol (Mexico)",
+ "es-py": "Espanhol (Paraguai)",
+ "es": "Espanhol (Espanha)",
+ "et": "Estoniano",
+ "fa": "Farsi",
+ "fi": "Finlandês",
+ "fr-be": "Francês (Bélgica)",
+ "fr-ca": "Francês (Canadá)",
+ "fr-ch": "Francês (Suíça)",
+ "fr": "Francês",
+ "ga": "Irlandês",
+ "he": "Hebreu",
+ "hi": "Hindi",
+ "hr": "Croata",
+ "hu": "Húngaro",
+ "id": "Indonésio",
+ "is": "Islandês",
+ "it": "Italiano",
+ "ja": "Japonês",
+ "ji": "iídiche",
+ "lt": "Lituano",
+ "lv": "Letão",
+ "mt": "Maltês",
+ "nl-be": "Holandês (Bélgica)",
+ "nl": "Holandês",
+ "no": "Norueguês",
+ "pl": "Polaco",
+ "pt": "Português",
+ "ro": "Romeno",
+ "sk": "Eslovaco",
+ "sl": "Esloveno",
+ "sq": "Albanês",
+ "sr": "Sérvio (Latim)",
+ "sv": "Sueco",
+ "th": "Tailandês",
+ "tn": "tswana",
+ "tr": "Turco",
+ "ts": "tsonga",
+ "uk": "Ucraniano",
+ "ur": "urdu",
+ "ve": "venda",
+ "vi": "Vietnamita",
+ "xh": "xosa",
+ "zu": "zulu",
+ "Failed to forget room %(errCode)s": "Falha ao esquecer a sala %(errCode)s",
+ "Failed to join the room": "Falha ao entrar na sala",
+ "Sunday": "Domingo",
+ "Monday": "Segunda",
+ "Tuesday": "Terça",
+ "Wednesday": "Quarta",
+ "Thursday": "Quinta",
+ "Friday": "Sexta",
+ "Saturday": "Sábado"
+}
diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json
new file mode 100644
index 0000000000..3bd74ce14f
--- /dev/null
+++ b/src/i18n/strings/pt_BR.json
@@ -0,0 +1,706 @@
+{
+ "accept": "aceitar",
+ "accepted an invitation": "aceitou um convite",
+ "accepted the invitation for": "aceitou o convite para",
+ "Account": "Conta",
+ "Add email address": "Adicionar endereço de email",
+ "Add phone number": "Adicionar número de telefone",
+ "Admin": "Administrador/a",
+ "Advanced": "Avançado",
+ "Algorithm": "Algoritmo",
+ "all room members, from the point they are invited.": "todos os membros da sala, a partir de quando foram convidados",
+ "all room members, from the point they joined.": "todos os membros da sala, a partir de quando entraram",
+ "all room members": "todas as pessoas da sala",
+ "an address": "um endereço",
+ "and": "e",
+ "An email has been sent to": "Um email foi enviado para",
+ "New passwords don't match": "As novas senhas não conferem",
+ "A new password must be entered.": "Uma nova senha precisa ser informada.",
+ "answered the call.": "respondeu à chamada.",
+ "anyone": "qualquer um",
+ "Anyone who knows the room's link, apart from guests": "Qualquer pessoa que tenha o link da sala, exceto visitantes",
+ "Anyone who knows the room's link, including guests": "Qualquer pessoa que tenha o link da sala, incluindo visitantes",
+ "Are you sure you want to leave the room?": "Você tem certeza que deseja sair da sala?",
+ "Are you sure you want to reject the invitation?": "Você tem certeza que deseja rejeitar este convite?",
+ "Are you sure you want upload the following files?": "Você tem certeza que deseja enviar os seguintes arquivos?",
+ "banned": "baniu",
+ "Banned users": "Usuárias/os banidas/os",
+ "Bans user with given id": "Banir usuários com o identificador informado",
+ "Blacklisted": "Bloqueado",
+ "Bug Report": "Repotar problemas de funcionamento",
+ "Bulk Options": "Opcões de Batelada",
+ "Can't load user settings": "Não é possível carregar configurações de usuário",
+ "changed avatar": "mudou sua imagem de perfil (avatar)",
+ "changed name": "mudou seu nome",
+ "changed their display name from": "mudou seu nome para",
+ "changed their profile picture": "alterou sua foto de perfil",
+ "changed the power level of": "mudou o nível de permissões de",
+ "changed the room name to": "mudou o nome da sala para",
+ "%(senderDisplayName)s changed the topic to \"%(topic)s\"": "%(senderDisplayName)s mudou o tópico para \"%(topic)s\"",
+ "Changes to who can read history will only apply to future messages in this room": "As mudanças sobre quem pode ler o histórico da sala só serão aplicadas às mensagens futuras nesta sala",
+ "Changes your display nickname": "Troca o seu apelido",
+ "Claimed Ed25519 fingerprint key": "Chave reivindicada da Impressão Digital Ed25519",
+ "Clear Cache and Reload": "Limpar Memória Cache e Recarregar",
+ "Clear Cache": "Limpar Memória Cache",
+ "Click here": "Clique aqui",
+ "Click here to fix": "Clique aqui para resolver isso",
+ "Commands": "Comandos",
+ "Confirm password": "Confirme a nova senha",
+ "Confirm your new password": "Confirme a nova senha",
+ "Continue": "Continuar",
+ "Could not connect to the integration server": "Não foi possível conectar ao servidor de integrações",
+ "Create an account": "Criar uma conta",
+ "Create a new account": "Criar uma conta",
+ "Create Room": "Criar Sala",
+ "Cryptography": "Criptografia",
+ "Current password": "Senha atual",
+ "Curve25519 identity key": "Chave de Indetificação Curve25519",
+ "Deactivate Account": "Desativar conta",
+ "Deactivate my account": "Desativar minha conta",
+ "decline": "rejeitar",
+ "Decryption error": "Erro de descriptografia",
+ "Default": "Padrão",
+ "demote": "reduzir prioridade",
+ "Deops user with given id": "Retirar função de moderador do usuário com o identificador informado",
+ "Device ID": "Identificador do dispositivo",
+ "Devices will not yet be able to decrypt history from before they joined the room": "Os dispositivos não serão ainda capazes de descriptografar o histórico anterior à sua entrada na sala",
+ "Direct Chat": "Conversa pessoal",
+ "Disable inline URL previews by default": "Desabilitar visualizações prévias por padrão",
+ "Display name": "Nome",
+ "Displays action": "Visualizar atividades",
+ "Ed25519 fingerprint": "Impressão Digital Ed25519",
+ "Email Address": "endereço de email",
+ "Email, name or matrix ID": "Email, nome ou ID matrix",
+ "Emoji": "Emoji",
+ "Encrypted messages will not be visible on clients that do not yet implement encryption": "Mensagens criptografadas não serão visíveis em clientes que ainda não implementaram criptografia",
+ "Encrypted room": "Sala criptografada",
+ "Encryption is enabled in this room": "Criptografia está habilitada nesta sala",
+ "Encryption is not enabled in this room": "Criptografia não está habilitada nesta sala",
+ "ended the call.": "chamada encerrada.",
+ "End-to-end encryption information": "Informação criptografada ponta-a-ponta",
+ "End-to-end encryption is in beta and may not be reliable": "A criptografia ponta a ponta está em estágio beta e não deve ser totalmente confiável",
+ "Error": "Erro",
+ "Event information": "Informação do evento",
+ "Export E2E room keys": "Exportar chaves ponta-a-ponta da sala",
+ "Failed to change password. Is your password correct?": "Não foi possível modificar a senha. A senha informada está correta?",
+ "Failed to forget room": "Não foi possível esquecer a sala",
+ "Failed to leave room": "Falha ao tentar deixar a sala",
+ "Failed to reject invitation": "Falha ao tentar rejeitar convite",
+ "Failed to send email: ": "Falha ao tentar enviar email",
+ "Failed to set avatar.": "Falha ao tentar definir foto do perfil.",
+ "Failed to unban": "Não foi possível desfazer o banimento",
+ "Failed to upload file": "Falha ao enviar o arquivo",
+ "favourite": "favoritar",
+ "Favourite": "Favorito",
+ "Favourites": "Favoritos",
+ "Filter room members": "Filtrar integrantes da sala",
+ "Forget room": "Esquecer sala",
+ "Forgot your password?": "Esqueceu sua senha?",
+ "For security, logging out will delete any end-to-end encryption keys from this browser. If you want to be able to decrypt your conversation history from future Riot sessions, please export your room keys for safe-keeping.": "Por segurança, deslogar irá remover qualquer chave de criptografia ponta-a-ponta deste navegador. Caso deseje descriptografar o histórico das suas conversas E2E em sessões Riot futuras, por favor exporte as chaves da sala para sua garantia.",
+ "For security, this session has been signed out. Please sign in again": "Por questões de segurança, esta sessão foi encerrada. Por gentileza conecte-se novamente",
+ "Found a bug?": "Encontrou um problema de funcionamento do sistema?",
+ "Guests cannot join this room even if explicitly invited": "Visitantes não podem entrar nesta sala, mesmo se forem explicitamente convidadas/os",
+ "Guests can't set avatars. Please register": "Convidados não podem definir uma foto do perfil. Por favor, registre-se",
+ "Guests can't use labs features. Please register": "Convidados não podem usar as funcionalidades de laboratório (lab), por gentileza se registre",
+ "Guest users can't upload files. Please register to upload": "Usuários não podem fazer envio de arquivos. Por favor se cadastre para enviar arquivos",
+ "had": "teve",
+ "Hangup": "Desligar",
+ "Historical": "Histórico",
+ "Homeserver is": "Servidor padrão é",
+ "Identity Server is": "O servidor de identificação é",
+ "I have verified my email address": "Eu verifiquei o meu endereço de email",
+ "Import E2E room keys": "Importar chave de criptografia ponta-a-ponta (E2E) da sala",
+ "Invalid Email Address": "Endereço de email inválido",
+ "invited": "convidou",
+ "Invite new room members": "Convidar novas pessoas para ingressar na sala",
+ "Invites": "Convidar",
+ "Invites user with given id to current room": "Convidar usuários com um dado identificador para esta sala",
+ "is a": "é um(a)",
+ "I want to sign in with": "Quero entrar",
+ "joined and left": "entrou e saiu",
+ "joined": "entrou",
+ "joined the room": "entrou na sala",
+ "Joins room with given alias": "Entra na sala com o nome informado",
+ "Kicks user with given id": "Remove usuário com o identificador informado",
+ "Labs": "Laboratório",
+ "Leave room": "Sair da sala",
+ "left and rejoined": "saiu e entrou novamente",
+ "left": "saiu",
+ "left the room": "saiu da sala",
+ "Logged in as": "Logado como",
+ "Login as guest": "Entrar como visitante",
+ "Logout": "Sair",
+ "Low priority": "Baixa prioridade",
+ "made future room history visible to": "deixou o histórico futuro da sala visível para",
+ "Manage Integrations": "Gerenciar integrações",
+ "Members only": "Apenas integrantes da sala",
+ "Mobile phone number": "Telefone celular",
+ "Moderator": "Moderador/a",
+ "my Matrix ID": "com meu ID do Matrix",
+ "Name": "Nome",
+ "Never send encrypted messages to unverified devices from this device": "Nunca envie mensagens criptografada para um dispositivo não verificado a partir deste dispositivo",
+ "Never send encrypted messages to unverified devices in this room from this device": "Nunca envie mensagens criptografadas para dispositivos não verificados nesta sala a partir deste dispositivo",
+ "New password": "Nova senha",
+ "New passwords must match each other.": "As novas senhas informadas precisam ser idênticas.",
+ "none": "nenhum",
+ "Notifications": "Notificações",
+ " (not supported by this browser)": "não suportado por este navegador",
+ "": "",
+ "NOT verified": "NÃO verificado",
+ "No users have specific privileges in this room": "Nenhum/a usuário/a possui privilégios específicos nesta sala",
+ "olm version: ": "Versão do olm: ",
+ "Once encryption is enabled for a room it cannot be turned off again (for now)": "Assim que a criptografia é ativada para uma sala, ela não poderá ser desativada novamente (ainda)",
+ "Once you've followed the link it contains, click below": "Quando você tiver clicado no link que está no email, clique o botão abaixo",
+ "Only people who have been invited": "Apenas pessoas que tenham sido convidadas",
+ "or": "ou",
+ "other": "outro",
+ "others": "outros",
+ "Password": "Senha",
+ "Passwords can't be empty": "As senhas não podem estar em branco",
+ "People": "Pessoas",
+ "Permissions": "Permissões",
+ "Phone": "Telefone",
+ "placed a": "iniciou uma",
+ "Please check your email and click on the link it contains. Once this is done, click continue.": "Por favor verifique seu email e clique no link enviado. Quando finalizar este processo, clique para continuar.",
+ "Please Register": "Por favor, cadastre-se",
+ "Privacy warning": "Alerta sobre privacidade",
+ "Privileged Users": "Usuárias/os privilegiadas/os",
+ "Profile": "Perfil",
+ "Refer a friend to Riot: ": "Indicar um amigo para participar",
+ "rejected": "recusou",
+ "rejected the invitation.": "rejeitou o convite.",
+ "Reject invitation": "Rejeitar convite",
+ "Remove Contact Information?": "Remover informação de contato?",
+ "removed their display name": "removeu seu nome",
+ "removed their profile picture": "removeu sua foto de perfil",
+ "Remove": "Remover",
+ "requested a VoIP conference": "requisitou uma conferência VoIP",
+ "Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved ": "Atualmente, ao alterar sua senha, você também zera todas as chaves de criptografia ponta-a-ponta em todos os dipositivos, fazendo com que o histórico de conversas da sala não possa mais ser lido, a não ser que você antes exporte suas chaves de sala e as reimporte após a alteração da senha. No futuro, isso será melhorado ",
+ "restore": "restaurar",
+ "Return to app": "Retornar ao aplicativo",
+ "Return to login screen": "Retornar à tela de login",
+ "Room Colour": "Cores da sala",
+ "Room name (optional)": "Título da Sala (opcional)",
+ "Rooms": "Salas",
+ "Scroll to bottom of page": "Ir para o fim da página",
+ "Scroll to unread messages": "Rolar para baixo para ver as mensagens não lidas",
+ "Searches DuckDuckGo for results": "Buscar por resultados no buscador DuckDuckGo",
+ "Send a message (unencrypted)": "Enviar uma mensagem",
+ "Send an encrypted message": "Enviar uma mensagem criptografada",
+ "Sender device information": "Informação do dispositivo emissor",
+ "Send Invites": "Enviar convites",
+ "Send Reset Email": "Enviar email para redefinição de senha",
+ "sent an image": "enviou uma imagem",
+ "sent an invitation to": "enviou um convite para",
+ "sent a video": "enviou um vídeo",
+ "Server may be unavailable or overloaded": "Servidor pode estar indisponível ou sobrecarregado",
+ "Server may be unavailable, overloaded, or you hit a bug": "Servidor pode estar indisponível, sobrecarregado ou aconteceu um erro de execução",
+ "Session ID": "Identificador de sessão",
+ "set a profile picture": "colocou uma foto de perfil",
+ "set their display name to": "configurou seu nome para",
+ "Settings": "Configurações",
+ "Show panel": "Mostrar painel",
+ "Signed Out": "Deslogar",
+ "Sign in": "Entrar",
+ "Sign out": "Sair",
+ "since the point in time of selecting this option": "a partir do momento em que você selecionar esta opção",
+ "since they joined": "desde que entraram na sala",
+ "since they were invited": "desde que foram convidadas/os",
+ "Someone": "Alguém",
+ "Sorry, this homeserver is using a login which is not recognised ": "Desculpe, o servidor padrão está usando um login de acesso que não é válido ",
+ "Start a chat": "Começar uma conversa",
+ "Start Chat": "Começar conversa",
+ "Success": "Sucesso",
+ "tag as": "etiquetar como",
+ "tag direct chat": "definir como conversa pessoal",
+ "The default role for new room members is": "O papel padrão para novas/os integrantes da sala é",
+ "The email address linked to your account must be entered.": "O endereço de email relacionado a sua conta precisa ser informado.",
+ "their invitations": "seus convites",
+ "their invitation": "seu convite",
+ "These are experimental features that may break in unexpected ways. Use with caution": "Estes são recursos experimentais que podem não funcionar corretamente. Use com cuidado.",
+ "The visibility of existing history will be unchanged": "A visibilidade do histórico atual não será alterada",
+ "This doesn't appear to be a valid email address": "Este não aparenta ser um endereço de email válido",
+ "this invitation?": "este convite?",
+ "This is a preview of this room. Room interactions have been disabled": "Esta é uma pré visualização desta sala. As interações com a sala estão desabilitadas",
+ "This room is not accessible by remote Matrix servers": "Esta sala não é acessível para servidores Matrix remotos",
+ "This room's internal ID is": "O ID interno desta sala é",
+ "times": "vezes",
+ "To ban users": "Para banir usuárias/os",
+ "To configure the room": "Para poder configurar a sala",
+ "To invite users into the room": "Para convidar usuárias/os para esta sala",
+ "to join the discussion": "para se juntar à conversa",
+ "To kick users": "Para poder remover pessoas da sala",
+ "To link to a room it must have": "Para fazer um link para uma sala, ela deve ter",
+ "To redact messages": "Para poder apagar mensagens",
+ "To reset your password, enter the email address linked to your account": "Para redefinir sua senha, entre com o email da sua conta",
+ "To send events of type": "Para enviar eventos do tipo",
+ "To send messages": "Para enviar mensagens",
+ "turned on end-to-end encryption (algorithm": "acionou a encriptação ponta-a-ponta (algoritmo",
+ "Unable to add email address": "Não foi possível adicionar endereço de email",
+ "Unable to remove contact information": "Não foi possível remover informação de contato",
+ "Unable to verify email address": "Não foi possível verificar o endereço de email",
+ "Unban": "Desfazer banimento",
+ "Unencrypted room": "Sala não criptografada",
+ "unencrypted": "não criptografado",
+ "unknown device": "dispositivo desconhecido",
+ "unknown error code": "código de erro desconhecido",
+ "unknown": "desconhecido",
+ "Upload avatar": "Enviar icone de perfil de usuário",
+ "uploaded a file": "enviou um arquivo",
+ "Upload Files": "Enviar arquivos",
+ "Upload file": "Enviar arquivo",
+ "User ID": "Identificador de Usuário",
+ "User Interface": "Interface de usuário",
+ "username": "Nome de usuária/o",
+ "Users": "Usuários",
+ "User": "Usuária/o",
+ "Verification Pending": "Verificação pendente",
+ "Verification": "Verificação",
+ "verified": "verificado",
+ "Video call": "Chamada de vídeo",
+ "Voice call": "Chamada de voz",
+ "VoIP conference finished": "Conferência VoIP encerrada",
+ "VoIP conference started": "Conferência VoIP iniciada",
+ "(warning: cannot be disabled again!)": "(atenção: esta operação não poderá ser desfeita depois!)",
+ "Warning": "Atenção",
+ "was banned": "banida/o",
+ "was invited": "convidada/o",
+ "was kicked": "retirada/o da sala",
+ "was unbanned": "des-banida/o",
+ "was": "foi",
+ "were": "foram",
+ "Who can access this room?": "Quem pode acessar esta sala?",
+ "Who can read history?": "Quem pode ler o histórico da sala?",
+ "Who would you like to add to this room?": "Quais pessoas você gostaria de adicionar a esta sala?",
+ "Who would you like to communicate with?": "Com quem você gostaria de se comunicar?",
+ "withdrawn": "retirado",
+ "Would you like to": "Você gostaria de",
+ "You are trying to access": "Você está tentando acessar a sala",
+ "You do not have permission to post to this room": "Você não tem permissão de postar nesta sala",
+ "You have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, sign in again on each device": "Você foi desconectada/o de todos os dispositivos e portanto não receberá mais notificações no seu celular ou no computador. Para reativar as notificações, entre novamente em cada um dos dispositivos que costuma usar",
+ "You have no visible notifications": "Voce não possui notificações visíveis",
+ "you must be a": "você precisa ser",
+ "Your password has been reset": "Sua senha foi redefinida",
+ "Your password was successfully changed. You will not receive push notifications on other devices until you log back in to them": "Sua senha foi alterada com sucesso. Você não receberá notificações em outros dispositivos até que você logue novamente por eles",
+ "You should not yet trust it to secure data": "Você não deve confiar nela ainda para preservar seus dados",
+ "Sun": "Dom",
+ "Mon": "Seg",
+ "Tue": "Ter",
+ "Wed": "Qua",
+ "Thu": "Qui",
+ "Fri": "Sex",
+ "Sat": "Sáb",
+ "Jan": "Jan",
+ "Feb": "Fev",
+ "Mar": "Mar",
+ "Apr": "Abr",
+ "May": "Mai",
+ "Jun": "Jun",
+ "Jul": "Jul",
+ "Aug": "Ago",
+ "Sep": "Set",
+ "Oct": "Out",
+ "Nov": "Nov",
+ "Dec": "Dez",
+ "%(weekDayName)s, %(monthName)s %(day)s %(time)s": "%(weekDayName)s, %(day)s de %(monthName)s às %(time)s",
+ "%(weekDayName)s %(time)s": "%(weekDayName)s às %(time)s",
+ "en": "Inglês",
+ "pt-br": "Português do Brasil",
+ "de": "Alemão",
+ "da": "Dinamarquês",
+ "ru": "Russo",
+ "%(targetName)s accepted an invitation": "%(targetName)s aceitou um convite",
+ "%(targetName)s accepted the invitation for %(displayName)s": "%(targetName)s aceitou o convite para %(displayName)s",
+ "all room members, from the point they are invited": "todas/os as/os integrantes da sala, a partir do momento em que foram convidadas/os",
+ "all room members, from the point they joined": "todas/os as/os integrantes da sala, a partir do momento em que entraram na sala",
+ "%(names)s and %(lastPerson)s are typing": "%(names)s e %(lastPerson)s estão escrevendo",
+ "%(names)s and one other are typing": "%(names)s e uma outra pessoa estão escrevendo",
+ "%(names)s and %(count)s others are typing": "%(names)s e %(count)s outras pessoas estão escrevendo",
+ "%(senderName)s answered the call": "%(senderName)s atendeu à chamada",
+ "anyone.": "qualquer pessoa",
+ "%(senderName)s banned %(targetName)s": "%(senderName)s removeu %(targetName)s da sala",
+ "Call Timeout": "Tempo esgotado. Chamada encerrada",
+ "%(senderName)s changed their display name from %(oldDisplayName)s to %(displayName)s": "%(senderName)s mudou seu nome público de %(oldDisplayName)s para %(displayName)s",
+ "%(senderName)s changed their profile picture": "%(senderName)s alterou sua imagem de perfil",
+ "%(senderName)s changed the power level of %(powerLevelDiffText)s": "%(senderName)s alterou o nível de permissões de %(powerLevelDiffText)s",
+ "%(senderDisplayName)s changed the room name to %(roomName)s": "%(senderDisplayName)s alterou o nome da sala para %(roomName)s",
+ "%(senderDisplayName)s changed the topic to %(topic)s": "%(senderDisplayName)s alterou o tópico para %(topic)s",
+ "click to reveal": "clique para ver",
+ "Conference call failed": "Chamada de conferência falhou",
+ "Conference calling is in development and may not be reliable": "Chamadas de conferência estão em desenvolvimento e portanto podem não funcionar",
+ "Conference calls are not supported in encrypted rooms": "Chamadas de conferência não são possíveis em salas criptografadas",
+ "Conference calls are not supported in this client": "Chamadas de conferência não são possíveis neste navegador",
+ "/ddg is not a command": "/ddg não é um comando",
+ "Drop here %(toAction)s": "Arraste aqui %(toAction)s",
+ "Drop here to tag %(section)s": "Arraste aqui para marcar como %(section)s",
+ "%(senderName)s ended the call": "%(senderName)s finalizou a chamada",
+ "Existing Call": "Chamada em andamento",
+ "Failed to lookup current room": "Não foi possível buscar na sala atual",
+ "Failed to send email": "Não foi possível enviar email",
+ "Failed to send request": "Não foi possível mandar requisição",
+ "Failed to set up conference call": "Não foi possível montar a chamada de conferência",
+ "Failed to verify email address: make sure you clicked the link in the email": "Não foi possível verificar o endereço de email: verifique se você realmente clicou no link que está no seu email",
+ "Failure to create room": "Não foi possível criar a sala",
+ "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s de %(fromPowerLevel)s para %(toPowerLevel)s",
+ "Guest users can't create new rooms. Please register to create room and start a chat": "Visitantes não podem criar novas salas. Por favor, registre-se para criar uma sala e iniciar uma conversa",
+ "%(senderName)s invited %(targetName)s": "%(senderName)s convidou %(targetName)s",
+ "%(displayName)s is typing": "%(displayName)s está escrevendo",
+ "%(targetName)s joined the room": "%(targetName)s entrou na sala",
+ "%(senderName)s kicked %(targetName)s": "%(senderName)s removeu %(targetName)s da sala",
+ "%(targetName)s left the room": "%(targetName)s saiu da sala",
+ "%(senderName)s made future room history visible to": "%(senderName)s deixou o histórico futuro da sala visível para",
+ "Missing room_id in request": "Faltou o id da sala na requisição",
+ "Missing user_id in request": "Faltou o id de usuário na requisição",
+ "Must be viewing a room": "Tem que estar visualizando uma sala",
+ "New Composer & Autocomplete": "Nova ferramenta de formatação de mensagens e autocompletar",
+ "(not supported by this browser)": "(não é compatível com este navegador)",
+ "olm version": "versão olm",
+ "%(senderName)s placed a %(callType)s call": "%(senderName)s fez uma chamada de %(callType)s",
+ "Power level must be positive integer": "O nível de permissões tem que ser um número inteiro e positivo",
+ "Press": "Aperte",
+ "Reason": "Razão",
+ "Refer a friend to Riot": "Recomende Riot a um/a amigo/a",
+ "%(targetName)s rejected the invitation": "%(targetName)s recusou o convite",
+ "%(senderName)s removed their display name (%(oldDisplayName)s)": "%(senderName)s removeu o seu nome público (%(oldDisplayName)s)",
+ "%(senderName)s removed their profile picture": "%(senderName)s removeu sua imagem de perfil",
+ "%(senderName)s requested a VoIP conference": "%(senderName)s está solicitando uma conferência de voz",
+ "Riot does not have permission to send you notifications - please check your browser settings": "Riot não tem permissões para enviar notificações a você - por favor, verifique as configurações do seu navegador",
+ "Riot was not given permission to send notifications - please try again": "Riot não tem permissões para enviar notificações a você - por favor, tente novamente",
+ "Room %(roomId)s not visible": "A sala %(roomId)s não está visível",
+ "%(senderDisplayName)s sent an image": "%(senderDisplayName)s enviou uma imagem",
+ "%(senderName)s sent an invitation to %(targetDisplayName)s to join the room": "%(senderName)s enviou um convite para %(targetDisplayName)s entrar na sala",
+ "%(senderName)s set a profile picture": "%(senderName)s definiu uma imagem de perfil",
+ "%(senderName)s set their display name to %(displayName)s": "%(senderName)s definiu seu nome público para %(displayName)s",
+ "tag as %(tagName)s": "marcar como %(tagName)s",
+ "This email address is already in use": "Este endereço de email já está sendo usado",
+ "This email address was not found": "Este endereço de email não foi encontrado",
+ "The file '%(fileName)s' exceeds this home server's size limit for uploads": "O arquivo '%(fileName)s' ultrapassa o limite de tamanho que nosso servidor permite enviar",
+ "The file '%(fileName)s' failed to upload": "Não foi possível enviar o arquivo '%(fileName)s",
+ "The remote side failed to pick up": "Houve alguma falha que não permitiu a outra pessoa atender à chamada",
+ "This room is not recognised": "Esta sala não é reconhecida",
+ "These are experimental features that may break in unexpected ways": "Estas são funcionalidades experimentais que podem apresentar falhas",
+ "This phone number is already in use": "Este número de telefone já está sendo usado",
+ "to browse the directory": "para navegar na lista pública de salas",
+ "to demote": "para reduzir prioridade",
+ "to favourite": "para favoritar",
+ "to make a room or": "para criar uma sala ou",
+ "To redact other users' messages": "Para apagar mensagens de outras pessoas",
+ "to restore": "para restaurar",
+ "to start a chat with someone": "para iniciar uma conversa com alguém",
+ "to tag as %(tagName)s": "para marcar como %(tagName)s",
+ "to tag direct chat": "para marcar a conversa como pessoal",
+ "To use it, just wait for autocomplete results to load and tab through them": "Para usar esta funcionalidade, espere o carregamento dos resultados de autocompletar e então escolha entre as opções",
+ "%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s)": "%(senderName)s ativou criptografia ponta a ponta (algoritmo %(algorithm)s)",
+ "Unable to restore previous session": "Não foi possível restaurar a sessão anterior",
+ "%(senderName)s unbanned %(targetName)s": "%(senderName)s desfez o banimento de %(targetName)s",
+ "Unable to capture screen": "Não foi possível capturar a imagem da tela",
+ "Unable to enable Notifications": "Não foi possível ativar as notificações",
+ "Upload Failed": "O envio falhou",
+ "Usage": "Uso",
+ "Use with caution": "Use com cautela",
+ "VoIP is unsupported": "Chamada de voz não permitida",
+ "%(senderName)s withdrew %(targetName)s's inivitation": "%(senderName)s desfez o convite a %(targetName)s",
+ "You are already in a call": "Você já está em uma chamada",
+ "You're not in any rooms yet! Press": "Você ainda não está em nenhuma sala! Pressione",
+ "You are trying to access %(roomName)s": "Você está tentando acessar a sala %(roomName)s",
+ "You cannot place a call with yourself": "Você não pode iniciar uma chamada",
+ "You cannot place VoIP calls in this browser": "Você não pode fazer chamadas de voz neste navegador",
+ "You need to be able to invite users to do that": "Para fazer isso, você tem que ter permissão para convidar outras pessoas",
+ "You need to be logged in": "Você tem que estar logado",
+ "You need to log back in to generate end-to-end encryption keys for this device and submit the public key to your homeserver. This is a once off; sorry for the inconvenience": "É necessário que você faça login novamente para poder gerar as chaves de criptografia ponta-a-ponta para este dispositivo e então enviar sua chave pública para o servidor. Pedimos desculpas pela inconveniência, é preciso fazer isso apenas única uma vez",
+ "Your email address does not appear to be associated with a Matrix ID on this Homeserver": "O seu endereço de email não parece estar associado a uma conta de usuária/o Matrix neste servidor",
+ "Set a display name:": "Defina um nome público para você:",
+ "Upload an avatar:": "Envie uma imagem de perfil para identificar você:",
+ "This server does not support authentication with a phone number.": "Este servidor não permite a autenticação através de números de telefone.",
+ "Missing password.": "Faltou a senha.",
+ "Passwords don't match.": "As senhas não conferem.",
+ "Password too short (min %(MIN_PASSWORD_LENGTH)s).": "A senha é muito curta (o mínimo é de %(MIN_PASSWORD_LENGTH)s caracteres).",
+ "This doesn't look like a valid email address.": "Este endereço de email não parece ser válido.",
+ "This doesn't look like a valid phone number.": "Este número de telefone não parece ser válido.",
+ "User names may only contain letters, numbers, dots, hyphens and underscores.": "Nomes de usuária/o podem conter apenas letras, números, pontos, hífens e linha inferior (_).",
+ "An unknown error occurred.": "Um erro desconhecido ocorreu.",
+ "I already have an account": "Eu já tenho uma conta",
+ "An error occured: %(error_string)s": "Um erro ocorreu: %(error_string)s",
+ "Topic": "Tópico",
+ "Make this room private": "Tornar esta sala privada",
+ "Share message history with new users": "Compartilhar histórico de mensagens com novas/os usuárias/os",
+ "Encrypt room": "Criptografar esta sala",
+ "There are no visible files in this room": "Não há arquivos públicos nesta sala",
+ "Error changing language": "Erro ao mudar de idioma",
+ "Riot was unable to find the correct Data for the selected Language.": "Não foi possível encontrar os dados para o idioma selecionado.",
+ "Connectivity to the server has been lost.": "A conexão com o servidor foi perdida. Verifique sua conexão de internet.",
+ "Sent messages will be stored until your connection has returned.": "Imagens enviadas ficarão armazenadas até que sua conexão seja reestabelecida.",
+ "Auto-complete": "Autocompletar",
+ "Resend all": "Reenviar todas as mensagens",
+ "cancel all": "cancelar todas",
+ "now. You can also select individual messages to resend or cancel.": "agora. Você também pode escolher mensagens individuais e definir se vai reenviar ou cancelar o envio.",
+ "Active call": "Chamada ativa",
+ "af": "Afrikaans",
+ "ar-ae": "Árabe (U.A.E.)",
+ "ar-bh": "Árabe (Bahrain)",
+ "ar-dz": "Árabe (Algéria)",
+ "Sunday": "Domingo",
+ "Monday": "Segunda",
+ "ar-eg": "Árabe (Egito)",
+ "ar-tn": "Árabe (Tunisia)",
+ "be": "Bielorusso",
+ "bg": "Búlgaro",
+ "ca": "Catalão",
+ "cs": "Tcheco",
+ "el": "Grego",
+ "en-au": "Inglês (Austrália)",
+ "en-ca": "Inglês (Canadá)",
+ "en-gb": "Inglês (Reino Unido)",
+ "en-ie": "Inglês (Irlanda)",
+ "en-nz": "Inglês (Nova Zelândia)",
+ "en-us": "Inglês (Estados Unidos)",
+ "es-ar": "Espanhol (Argentina)",
+ "es-py": "Espanhol (Paraguai)",
+ "es": "Espanhol (Espanha)",
+ "et": "Estônia",
+ "fa": "Farsi",
+ "fi": "Finlandês",
+ "fr-be": "Francês (Bélgica)",
+ "fr-ca": "Francês (Canadá)",
+ "fr-ch": "Francês (Suíça)",
+ "fr": "Francês",
+ "ga": "Irlandês",
+ "he": "Hebreu",
+ "hi": "Hindu",
+ "hr": "Croácia",
+ "hu": "Hungria",
+ "id": "Indonésio",
+ "is": "Islandês",
+ "it": "Italiano",
+ "ja": "Japonês",
+ "ji": "Ídiche",
+ "lt": "Lituânia",
+ "lv": "Letão",
+ "ms": "Malaio",
+ "mt": "Maltês",
+ "nl-be": "Holandês (Bélgica)",
+ "nl": "Holandês",
+ "no": "Norueguês",
+ "pl": "Polonês",
+ "pt": "Português (Portugal)",
+ "rm": "Romanche",
+ "ro": "Romeno",
+ "sk": "Eslovaco",
+ "sl": "Esloveno",
+ "sq": "Albanês",
+ "sr": "Sérvio (latino)",
+ "sv": "Suécia",
+ "th": "Tailandês",
+ "tn": "Tsuana",
+ "tr": "Turquia",
+ "ts": "Tsonga",
+ "uk": "Ucraniano",
+ "ur": "Urdu",
+ "vi": "Vietnamita",
+ "xh": "Xhosa",
+ "zu": "Zulu",
+ "Failed to forget room %(errCode)s": "Falhou ao esquecer a sala %(errCode)s",
+ "Failed to join the room": "Falhou ao entrar na sala",
+ "Tuesday": "Terça",
+ "Wednesday": "Quarta",
+ "Thursday": "Quinta",
+ "Friday": "Sexta",
+ "Saturday": "Sábado",
+ "ar-iq": "Árabe (Iraque)",
+ "ar-jo": "Árabe (Jordânia)",
+ "ar-kw": "Árabe (Kuwait)",
+ "ar-lb": "Árabe (Líbano)",
+ "ar-ly": "Árabe (Líbia)",
+ "ar-ma": "Árabe (Marrocos)",
+ "ar-om": "Árabe (Omã)",
+ "ar-qa": "Árabe (Catar)",
+ "ar-sa": "Árabe (Arábia Saudita)",
+ "ar-sy": "Árabe (Síria)",
+ "ar-ye": "Árabe (Iémen)",
+ "de-at": "Alemão (Austria)",
+ "de-ch": "Alemão (Suíça)",
+ "de-li": "Alemão (Liechtenstein)",
+ "de-lu": "Alemão (Luxemburgo)",
+ "en-bz": "Inglês (Belize)",
+ "en-jm": "Inglês (Jamaica)",
+ "en-tt": "English (Trindade)",
+ "en-za": "English (África do Sul)",
+ "es-bo": "Espanhol (Bolívia)",
+ "es-cl": "Espanhol (Chile)",
+ "es-co": "Espanhol (Colômbia)",
+ "es-cr": "Espanhol (Costa Rica)",
+ "es-do": "Espanhol (República Dominicana)",
+ "es-ec": "Espanhol (Equador)",
+ "es-gt": "Espanhol (Guatemala)",
+ "es-hn": "Espanhol (Honduras)",
+ "es-mx": "Espanhol (México)",
+ "es-ni": "Espanhol (Nicarágua)",
+ "es-pa": "Espanhol (Panamá)",
+ "%(oneUser)s changed his/her avatar": "%(oneUser)s alterou sua imagem pública",
+ "es-pe": "Espanhol (Peru)",
+ "es-pr": "Espanhol (Porto Rico)",
+ "es-sv": "Espanhol (El Salvador)",
+ "es-uy": "Espanhol (Uruguai)",
+ "es-ve": "Espanhol (Venezuela)",
+ "eu": "Basco (Basco)",
+ "fr-lu": "Francês (Luxemburgo)",
+ "gd": "Galês (Escócia)",
+ "it-ch": "Italiano (Suíça)",
+ "ko": "Coreano (Johab)",
+ "mk": "Macedônio (República da Macedônia)",
+ "ro-mo": "Romano (Moldávia)",
+ "ru-mo": "Russo (Moldávia)",
+ "sb": "Sorábio",
+ "sv-fi": "Sueco (Finlândia)",
+ "zh-cn": "Chinês (República Popular da China)",
+ "zh-hk": "Chinês (Hong Kong SAR)",
+ "zh-sg": "Chinês (Singapura)",
+ "zh-tw": "Chinês (Taiwan)",
+ "A text message has been sent to +%(msisdn)s. Please enter the verification code it contains": "Uma mensagem de texto foi enviada para +%(msisdn)s. Gentileza entrar com o código de verificação que contém",
+ "%(items)s and %(remaining)s others": "%(items)s e %(remaining)s outros",
+ "%(items)s and one other": "%(items)s e um outro",
+ "%(items)s and %(lastItem)s": "%(items)s e %(lastItem)s",
+ "and %(overflowCount)s others...": "e %(overflowCount)s outros...",
+ "and one other...": "e um outro...",
+ "Are you sure?": "Você tem certeza?",
+ "Attachment": "Anexo",
+ "Autoplay GIFs and videos": "Reproduzir automaticamente GIFs e videos",
+ "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s, %(day)s de %(monthName)s de %(fullYear)s às %(time)s",
+ "fo": "Feroês",
+ "sx": "Sutu",
+ "sz": "Sami (Lappish)",
+ "ve": "Venda",
+ "Can't connect to homeserver - please check your connectivity and ensure your %(urlStart)s homeserver's SSL certificate %(urlEnd)s is trusted": "Não consigo conectar ao servidor padrão - favor checar sua conexão à internet e verificar se o certificado SSL do seu %(urlStart)s servidor padrão %(urlEnd)s é confiável",
+ "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or %(urlStart)s enable unsafe scripts %(urlEnd)s": "Não consigo conectar ao servidor padrão através de HTTP quando uma URL HTTPS está na barra de endereços do seu navegador. Use HTTPS ou então %(urlStart)s habilite scripts não seguros no seu navegador %(urlEnd)s",
+ "Change Password": "Alterar senha",
+ "changing room on a RoomView is not supported": "mudar a sala em uma 'RoomView' não é permitido",
+ "Click to mute audio": "Clique para colocar o áudio no mudo",
+ "Click to mute video": "Clique para desabilitar imagens de vídeo",
+ "Click to unmute video": "Clique para voltar a mostrar imagens de vídeo",
+ "Click to unmute audio": "Clique para retirar áudio do mudo",
+ "Command error": "Erro de comando",
+ "Decrypt %(text)s": "Descriptografar %(text)s",
+ "Delete": "Apagar",
+ "Devices": "Dispositivos",
+ "Direct chats": "Conversas pessoais",
+ "Disinvite": "Desconvidar",
+ "Don't send typing notifications": "Não enviar notificação de estar digitando",
+ "Download %(text)s": "Baixar %(text)s",
+ "Enable encryption": "Habilitar criptografia",
+ "Enter Code": "Entre com o código",
+ "Failed to ban user": "Não foi possível banir o/a usuário/a",
+ "Failed to change power level": "Não foi possível mudar o nível de permissões",
+ "Failed to delete device": "Não foi possível remover o dispositivo",
+ "Failed to join room": "Não foi possível ingressar na sala",
+ "Failed to kick": "Não foi possível remover usuária/o",
+ "Failed to load timeline position": "Não foi possível carregar a posição na linha do tempo",
+ "Failed to mute user": "Não foi possível remover notificações da/do usuária/o",
+ "Failed to reject invite": "Não foi possível rejeitar o convite",
+ "Failed to save settings": "Não foi possível salvar as configurações",
+ "Failed to set display name": "Houve falha ao definir o nome público",
+ "Failed to toggle moderator status": "Houve falha ao alterar o status de moderador/a",
+ "Fill screen": "Tela cheia",
+ "Hide read receipts": "Ocultar recebimentos de leitura",
+ "Hide Text Formatting Toolbar": "Ocultar a barra de formatação de texto",
+ "Incorrect verification code": "Código de verificação incorreto",
+ "Invalid alias format": "Formato de alias é inválido",
+ "Invalid address format": "Formato de endereço é inválido",
+ "'%(alias)s' is not a valid format for an address": "'%(alias)s' não é um formato válido para um endereço",
+ "'%(alias)s' is not a valid format for an alias": "'%(alias)s' não é um formato válido para um alias",
+ "Join Room": "Ingressar na sala",
+ "Kick": "Remover",
+ "Level": "Nível",
+ "Local addresses for this room:": "Endereço local desta sala:",
+ "Markdown is disabled": "A formatação 'Markdown' está desabilitada",
+ "Markdown is enabled": "A formatação 'Markdown' está habilitada",
+ "Message not sent due to unknown devices being present": "A mensagem não foi enviada por causa da presença de dispositivos desconhecidos",
+ "Never send encrypted messages to unverified devices in this room": "Nunca envie mensagens criptografadas para dispositivos não verificados nesta sala",
+ "New address (e.g. #foo:%(localDomain)s)": "Novo endereço (p.ex: #algo:%(localDomain)s)",
+ "not set": "não definido",
+ "not specified": "não especificado",
+ "No devices with registered encryption keys": "Não há dispositivos com chaves de criptografia registradas",
+ "No more results": "Não há mais resultados",
+ "No results": "Sem resultados",
+ "OK": "OK",
+ "Revoke Moderator": "Retirar status de moderador",
+ "Search": "Localizar",
+ "Search failed": "Busca falhou",
+ "Server error": "Erro no servidor",
+ "Server may be unavailable, overloaded, or search timed out :(": "O servidor pode estar indisponível, sobrecarregado, ou a busca ultrapassou o tempo limite :(",
+ "Server may be unavailable, overloaded, or the file too big": "O servidor pode estar indisponível, sobrecarregado, ou o arquivo é muito grande",
+ "Server unavailable, overloaded, or something else went wrong": "O servidor pode estar indisponível, sobrecarregado, ou alguma outra coisa não funcionou",
+ "Some of your messages have not been sent": "Algumas das suas mensagens não foram enviadas",
+ "Submit": "Enviar",
+ "The main address for this room is": "O endereço principal desta sala é",
+ "This action cannot be performed by a guest user. Please register to be able to do this": "Esta ação não pode ser realizada por um/a usuário/a visitante. Por favor, registre-se para poder fazer isso",
+ "%(actionVerb)s this person?": "%(actionVerb)s esta pessoa?",
+ "This room has no local addresses": "Esta sala não tem endereços locais",
+ "This room is private or inaccessible to guests. You may be able to join if you register": "Esta sala é privada ou inacessível para visitantes. Você poderá ingressar nela se registrar-se",
+ "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question": "Tentei carregar um ponto específico na linha do tempo desta sala, mas parece que você não tem permissões para ver a mensagem em questão",
+ "Tried to load a specific point in this room's timeline, but was unable to find it": "Tentei carregar um ponto específico na linha do tempo desta sala, mas não o encontrei",
+ "Turn Markdown off": "Desabilitar a formatação 'Markdown'",
+ "Turn Markdown on": "Habilitar a marcação 'Markdown'",
+ "Unable to load device list": "Não foi possível carregar a lista de dispositivos",
+ "Unknown command": "Comando desconhecido",
+ "Unknown room %(roomId)s": "A sala %(roomId)s é desconhecida",
+ "You have been invited to join this room by %(inviterName)s": "Você foi convidada/o por %(inviterName)s a ingressar nesta sala",
+ "You seem to be in a call, are you sure you want to quit?": "Parece que você está em uma chamada. Tem certeza que quer sair?",
+ "You seem to be uploading files, are you sure you want to quit?": "Parece que você está enviando arquivos. Tem certeza que quer sair?",
+ "You will not be able to undo this change as you are promoting the user to have the same power level as yourself": "Você não poderá desfazer esta mudança, pois estará dando a este(a) usuário(a) o mesmo nível de permissões que você",
+ "Make Moderator": "Tornar moderador(a)",
+ "Room": "Sala",
+ "(~%(searchCount)s results)": "(±%(searchCount)s resultados)",
+ "Cancel": "Cancelar",
+ "bold": "negrito",
+ "italic": "itálico",
+ "strike": "tachado",
+ "underline": "sublinhado",
+ "code": "código de programação",
+ "quote": "citação",
+ "bullet": "marcador de lista",
+ "numbullet": "marcador de numeração",
+ "%(severalUsers)s joined %(repeats)s times": "%(severalUsers)s ingressaram %(repeats)s vezes",
+ "%(oneUser)s joined %(repeats)s times": "%(oneUser)s ingressou %(repeats)s vezes",
+ "%(severalUsers)s joined": "%(severalUsers)s ingressaram",
+ "%(oneUser)s joined": "%(oneUser)s ingressou",
+ "%(severalUsers)s left %(repeats)s times": "%(severalUsers)s saíram %(repeats)s vezes",
+ "%(oneUser)s left %(repeats)s times": "%(oneUser)s saiu %(repeats)s vezes",
+ "%(severalUsers)s left": "%(severalUsers)s saíram",
+ "%(oneUser)s left": "%(oneUser)s saiu",
+ "%(severalUsers)s joined and left %(repeats)s times": "%(severalUsers)s ingressaram e saíram %(repeats)s vezes",
+ "%(oneUser)s joined and left %(repeats)s times": "%(oneUser)s ingressou e saiu %(repeats)s vezes",
+ "%(severalUsers)s joined and left": "%(severalUsers)s ingressaram e saíram",
+ "%(oneUser)s joined and left": "%(oneUser)s ingressou e saiu",
+ "%(severalUsers)s left and rejoined %(repeats)s times": "%(severalUsers)s saíram e entraram novamente %(repeats)s vezes",
+ "%(oneUser)s left and rejoined %(repeats)s times": "%(oneUser)s saiu e entrou novamente %(repeats)s vezes",
+ "%(severalUsers)s left and rejoined": "%(severalUsers)s saíram e entraram novamente",
+ "%(oneUser)s left and rejoined": "%(oneUser)s saiu e entrou novamente",
+ "%(severalUsers)s rejected their invitations %(repeats)s times": "%(severalUsers)s rejeitaram seus convites %(repeats)s vezes",
+ "%(oneUser)s rejected his/her invitation %(repeats)s times": "%(oneUser)s rejeitou seu convite %(repeats)s vezes",
+ "%(severalUsers)s rejected their invitations": "%(severalUsers)s rejeitaram seus convites",
+ "%(oneUser)s rejected his/her invitation": "%(oneUser)s rejeitou seu convite",
+ "%(severalUsers)s had their invitations withdrawn %(repeats)s times": "%(severalUsers)s tiveram seus convites desfeitos %(repeats)s vezes",
+ "%(oneUser)s had his/her invitation withdrawn %(repeats)s times": "%(oneUser)s teve seu convite desfeito %(repeats)s vezes",
+ "%(severalUsers)s had their invitations withdrawn": "%(severalUsers)s tiveram seus convites desfeitos",
+ "%(oneUser)s had his/her invitation withdrawn": "%(oneUser)s teve seu convite desfeito",
+ "were invited %(repeats)s times": "foram convidadas(os) %(repeats)s vezes",
+ "was invited %(repeats)s times": "foi convidada(o) %(repeats)s vezes",
+ "were invited": "foram convidadas(os)",
+ "were banned %(repeats)s times": "foram banidas(os) %(repeats)s vezes",
+ "was banned %(repeats)s times": "foi banida(o) %(repeats)s vezes",
+ "were banned": "foram banidas(os)",
+ "were unbanned %(repeats)s times": "tiveram banimento desfeito %(repeats)s vezes",
+ "was unbanned %(repeats)s times": "teve banimento desfeito %(repeats)s vezes",
+ "were unbanned": "tiveram banimento desfeito",
+ "were kicked %(repeats)s times": "foram expulsas(os) %(repeats)s vezes",
+ "was kicked %(repeats)s times": "foi expulsa(o) %(repeats)s vezes",
+ "were kicked": "foram expulsas(os)",
+ "%(severalUsers)s changed their name %(repeats)s times": "%(severalUsers)s alteraram seu nome %(repeats)s vezes",
+ "%(oneUser)s changed his/her name %(repeats)s times": "%(oneUser)s alterou seu nome %(repeats)s vezes",
+ "%(severalUsers)s changed their name": "%(severalUsers)s alteraram seus nomes",
+ "%(oneUser)s changed his/her name": "%(oneUser)s alterou seu nome",
+ "%(severalUsers)s changed their avatar %(repeats)s times": "%(severalUsers)s alteraram sua imagem pública %(repeats)s vezes",
+ "%(oneUser)s changed his/her avatar %(repeats)s times": "%(oneUser)s alterou sua imagem pública %(repeats)s vezes",
+ "%(severalUsers)s changed their avatar": "%(severalUsers)s alteraram sua imagem pública",
+ "Ban": "Banir"
+}
diff --git a/src/i18n/strings/ru.json b/src/i18n/strings/ru.json
new file mode 100644
index 0000000000..001a5aca6a
--- /dev/null
+++ b/src/i18n/strings/ru.json
@@ -0,0 +1,476 @@
+{
+ "accept": "принимать",
+ "accepted an invitation": "принял приглашение",
+ "accepted the invitation for": "принял приглашение на",
+ "Account": "Аккаунт",
+ "Add email address": "Добавить email адрес",
+ "Add phone number": "Добавить телефонный номер",
+ "Admin": "Admin",
+ "Advanced": "Расширенный",
+ "Algorithm": "Алгоритм",
+ "all room members": "все участники комнаты",
+ "all room members, from the point they are invited": "все участники комнаты, с момента приглашения",
+ "all room members, from the point they joined": "все участники комнаты, с момента вступления",
+ "an address": "адрес",
+ "and": "и",
+ "An email has been sent to": "Email был отправлен",
+ "A new password must be entered.": "Введите новый пароль.",
+ "answered the call.": "принятый звонок.",
+ "anyone.": "кто угодно",
+ "Anyone who knows the room's link, apart from guests": "Любой, кто знает ссылку на комнату, кроме гостей",
+ "Anyone who knows the room's link, including guests": "Любой, кто знает ссылку комнаты, включая гостей",
+ "Are you sure you want to reject the invitation?": "Вы уверены что вы хотите отклонить приглашение?",
+ "Are you sure you want upload the following files?": "Вы уверены что вы хотите закачать следующий файл?",
+ "banned": "banned",
+ "Banned users": "Запрещенный пользователь",
+ "Bans user with given id": "Запретить пользователя с определенным id",
+ "Blacklisted": "В черный список",
+ "Bug Report": "Отчет ошибок",
+ "Bulk Options": "Объемные параметры",
+ "Can't load user settings": "Не может загрузить настройки пользователя",
+ "changed avatar": "изменен аватар",
+ "changed name": "измененное имя",
+ "changed their display name from": "changed their display name from",
+ "changed their profile picture": "changed their profile picture",
+ "changed the power level of": "changed the power level of",
+ "changed the room name to": "changed the room name to",
+ "changed the topic to": "changed the topic to",
+ "Changes to who can read history will only apply to future messages in this room": "Изменения того, кто может прочитать историю, будут только относиться к будущим сообщениям в этой комнате",
+ "Changes your display nickname": "Изменяет Ваш псевдоним",
+ "Claimed Ed25519 fingerprint key": "Требуемый Ed25519 ключ цифрового отпечатка",
+ "Clear Cache and Reload": "Очистите кэш и перезагрузку",
+ "Clear Cache": "Очистите кэш",
+ "Click here": "Нажать здесь",
+ "Click here to fix": "Нажать здесь для фиксации",
+ "Commands": "Команды",
+ "Confirm your new password": "Подтвердите ваш новый пароль",
+ "Continue": "Продолжить",
+ "Could not connect to the integration server": "Не может подключится к серверу интеграции",
+ "Create an account": "Создайте учётную запись",
+ "Create Room": "Создайте Комнату",
+ "Cryptography": "Криптография",
+ "Curve25519 identity key": "Curve25519 идентификационный ключ",
+ "Deactivate Account": "Деактивировать Учётную запись",
+ "Deactivate my account": "Деактивировать мою учётную запись",
+ "decline": "отказаться",
+ "Decryption error": "Ошибка дешифрования",
+ "Default": "Default",
+ "demote": "понижать",
+ "Deops user with given id": "Deops пользователь с данным id",
+ "Device ID": "Устройство ID",
+ "Devices will not yet be able to decrypt history from before they joined the room": "Устройство еще не будет в состоянии дешифровать историю, до присоединения к комнате",
+ "Direct Chat": "Персональное сообщение",
+ "Disable inline URL previews by default": "Отключить встроенные предварительные просмотры URL по умолчанию",
+ "Display name": "Отображаемое имя",
+ "Displays action": "Отображение действий",
+ "Ed25519 fingerprint": "Ed25519 fingerprint",
+ "Email Address": "Email адрес",
+ "Email, name or matrix ID": "Email, имя или matrix ID",
+ "Emoji": "Смайлы",
+ "Encrypted messages will not be visible on clients that do not yet implement encryption": "Зашифрованные сообщения не будут видимы в клиентах, которые еще не подключили шифрование",
+ "Encrypted room": "Зашифрованная комната",
+ "ended the call.": "ended the call.",
+ "End-to-end encryption information": "Информация сквозного шифрования (e2e)",
+ "End-to-end encryption is in beta and may not be reliable": "Сквозное шифрование (e2e) в бета-версии и не может быть надежным",
+ "Error": "Ошибка",
+ "Event information": "Event information",
+ "Export E2E room keys": "Экспорт E2E ключей комнаты",
+ "Failed to change password. Is your password correct?": "Не удалось изменить пароль. Ваш пароль правильный?",
+ "Failed to forget room": "Не удалось забыть комнату",
+ "Failed to leave room": "Не удалось выйти из комнаты",
+ "Failed to reject invitation": "Не удалось отклонить приглашение",
+ "Failed to send email": "Не удалось отослать email",
+ "Failed to unban": "Не удалось отменить запрет",
+ "Failed to upload file": "Не удалось закачать файл",
+ "Favourite": "Фаворит",
+ "favourite": "фаворит",
+ "Favourites": "Фавориты",
+ "Filter room members": "Фильтр участников комнаты",
+ "Forget room": "Забыть комнату",
+ "Forgot your password?": "Вы забыли пароль?",
+ "For security, this session has been signed out. Please sign in again": "Для обеспечения безопасности, эта сессия была подписана. Войдите в систему еще раз.",
+ "Found a bug?": "Обнаружили ошибку?",
+ "had": "имеет",
+ "Hangup": "Отключение",
+ "Historical": "Исторический",
+ "Homeserver is": "Домашний сервер является",
+ "Identity Server is": "Регистрационный Сервер является",
+ "I have verified my email address": "Я проверил мой адрес электронной почты",
+ "Import E2E room keys": "Импортировать E2E ключ комнаты",
+ "Invalid Email Address": "Недействительный адрес электронной почты",
+ "invited": "invited",
+ "Invite new room members": "Прегласить новых учасников комнаты",
+ "Invites": "Приглашать",
+ "Invites user with given id to current room": "Пригласить пользователя с данным id в текущую комнату",
+ "is a": "является",
+ "I want to sign in with": "Я хочу регистрироваться с",
+ "joined and left": "присоединенный и оставленный",
+ "joined": "присоединенный",
+ "joined the room": "joined the room",
+ "Joins room with given alias": "Присоединяется к комнате с данным псевдонимом",
+ "Kicks user with given id": "Кик пользователя с заданным id",
+ "Labs": "Лаборатория",
+ "Leave room": "Уйти из комнаты",
+ "left and rejoined": "Покинуть и переподключится",
+ "left": "покинуть",
+ "left the room": "left the room",
+ "Logged in as": "Зарегистрированный как",
+ "Login as guest": "Вход в систему как гость",
+ "Logout": "Выход из системы",
+ "Low priority": "Низкий приоритет",
+ "made future room history visible to": "made future room history visible to",
+ "Manage Integrations": "Управление интеграций",
+ "Members only": "Только участники",
+ "Mobile phone number": "Номер мобильного телефона",
+ "Moderator": "Ведущий",
+ "my Matrix ID": "мой Matrix ID",
+ "Name": "Имя",
+ "Never send encrypted messages to unverified devices from this device": "Никогда не отправляйте зашифрованные сообщения в непроверенные устройства с этого устройства",
+ "Never send encrypted messages to unverified devices in this room from this device": "Никогда не отправляйте зашифрованные сообщения в непроверенные устройства в этой комнате из этого устройства",
+ "New password": "Новый пароль",
+ "New passwords must match each other.": "Новые пароли должны соответствовать друг другу.",
+ "none": "никто",
+ "Notifications": "Уведомления",
+ " (not supported by this browser)": " (not supported by this browser)",
+ "": "<не поддерживаемый>",
+ "NOT verified": "НЕ проверенный",
+ "No users have specific privileges in this room": "Ни у каких пользователей нет специальных полномочий в этой комнате",
+ "olm version": "olm версия",
+ "Once encryption is enabled for a room it cannot be turned off again (for now)": "Как только шифрование включено для комнаты, оно не может быть выключено снова (на данный момент)",
+ "or": "или",
+ "other": "другой",
+ "others": "другие",
+ "Password": "Пароль",
+ "People": "Люди",
+ "Permissions": "Разрешение",
+ "Phone": "Телефон",
+ "placed a": "placed a",
+ "Please Register": "Пожалуйста зарегистрируйтесь",
+ "rejected the invitation.": "rejected the invitation.",
+ "removed their display name": "removed their display name",
+ "removed their profile picture": "removed their profile picture",
+ "Remove": "Удалить",
+ "requested a VoIP conference": "requested a VoIP conference",
+ "Return to login screen": "Return to login screen",
+ "Send Reset Email": "Send Reset Email",
+ "sent an image": "sent an image",
+ "sent an invitation to": "sent an invitation to",
+ "set a profile picture": "set a profile picture",
+ "set their display name to": "set their display name to",
+ "Settings": "Настройки",
+ "Start a chat": "Start a chat",
+ "Start Chat": "Start Chat",
+ "tag as": "tag as",
+ "These are experimental features that may break in unexpected ways. Use with caution": "These are experimental features that may break in unexpected ways. Use with caution",
+ "To send events of type": "Для отправки типа событий",
+ "To send messages": "Отправить сообщения",
+ "turned on end-to-end encryption (algorithm": "turned on end-to-end encryption (algorithm",
+ "Unable to add email address": "Невозможно добавить email адрес",
+ "Unable to remove contact information": "Невозможно удалить контактную информацию",
+ "Unable to verify email address": "Невозможно проверить адрес электронной почты",
+ "Unban": "Отменить запрет",
+ "Unencrypted room": "Незашифрованная комната",
+ "unencrypted": "незашифрованно",
+ "unknown device": "неизвесное устройство",
+ "unknown error code": "неизвестная ошибка",
+ "unknown": "неизвестно",
+ "Upload avatar": "Загрузить аватар",
+ "uploaded a file": "загруженный файл",
+ "Upload Files": "Загрузка файлов",
+ "Upload file": "Загрузка файла",
+ "User ID": "ID пользователя",
+ "User Interface": "Пользовательский интерфейс",
+ "username": "Имя пользователя",
+ "Users": "Пользователи",
+ "User": "Пользователь",
+ "Verification Pending": "Ожидание проверки",
+ "Verification": "Проверка",
+ "verified": "проверенный",
+ "Video call": "Видио вызов",
+ "Voice call": "Голосовой вызов",
+ "VoIP conference finished": "VoIP конференция закончилась",
+ "VoIP conference started": "VoIP Конференция стартовала",
+ "(warning: cannot be disabled again!)": "(предупреждение: не может быть снова отключен!)",
+ "Warning": "Предупреждение",
+ "was banned": "запрещен",
+ "was invited": "приглашенный",
+ "was kicked": "выброшен",
+ "was unbanned": "незапрещенный",
+ "was": "был",
+ "were": "быть",
+ "Who can access this room?": "Кто может получить доступ к этой комнате?",
+ "Who can read history?": "Кто может читать историю?",
+ "Who would you like to add to this room?": "Кого хотели бы Вы добавлять к этой комнате?",
+ "Who would you like to communicate with?": "С кем хотели бы Вы связываться?",
+ "withdrawn": "уходить",
+ "Would you like to": "Хотели бы Вы",
+ "You do not have permission to post to this room": "У Вас нет разрешения писать в эту комнату",
+ "You have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, sign in again on each device": "Вы вышли из всех устройств и не будет больше получать push-уведомления. Чтобы повторно включить уведомления, войдите в систему еще раз для каждого устройства",
+ "You have no visible notifications": "У Вас нет видимых уведомлений",
+ "you must be a": "Вы должны быть",
+ "Your password has been reset": "Ваш пароль был изменен",
+ "Your password was successfully changed. You will not receive push notifications on other devices until you log back in to them": "Ваш пароль был успешно изменен. Вы не будете получать Push-уведомления о других устройствах, пока не войдете обратно на них",
+ "You should not yet trust it to secure data": "Вы еще не должны доверять этому защиту данных",
+ "en": "Английский",
+ "pt-br": "Португальский Бразилия",
+ "de": "Немецкий",
+ "da": "Датский",
+ "ru": "Русский",
+ "%(targetName)s accepted an invitation": "%(targetName)s принятое приглашение",
+ "%(targetName)s accepted the invitation for %(displayName)s": "%(targetName)s принятое приглашение от %(displayName)s",
+ "Resend all": "Переслать снова всем",
+ "cancel all": "отменить всем",
+ "Active call": "Активный звонок",
+ "%(names)s and %(lastPerson)s are typing": "%(names)s и %(lastPerson)s печатает",
+ "%(names)s and one other are typing": "%(names)s и другой печатают",
+ "%(names)s and %(count)s others are typing": "%(names)s и %(count)s другие печатают",
+ "%(senderName)s answered the call": "%(senderName)s ответил на звонок",
+ "%(senderName)s banned %(targetName)s": "%(senderName)s запрещенный %(targetName)s",
+ "Call Timeout": "Время ожидания вызова",
+ "%(senderName)s changed their display name from %(oldDisplayName)s to %(displayName)s": "%(senderName)s их имя измененное с %(oldDisplayName)s на %(displayName)s",
+ "%(senderName)s changed their profile picture": "%(senderName)s измененное ихнее фото профиля",
+ "%(senderName)s changed the power level of %(powerLevelDiffText)s": "%(senderName)s уровень мощности изменен на %(powerLevelDiffText)s",
+ "%(senderDisplayName)s changed the room name to %(roomName)s": "%(senderDisplayName)s имя комнаты измененно на %(roomName)s",
+ "%(senderDisplayName)s changed the topic to %(topic)s": "%(senderDisplayName)s измененная тема на %(topic)s",
+ "Conference call failed": "Конференц-вызов прервался",
+ "Conference calling is in development and may not be reliable": "Конференц-вызов находится в процессе и может не быть надежным",
+ "Conference calls are not supported in encrypted rooms": "Конференц-вызовы не поддерживаются в зашифрованных комнатах",
+ "Conference calls are not supported in this client": "Конференц-вызовы не поддерживаются в этом клиенте",
+ "/ddg is not a command": "/ddg не команда",
+ "Drop here %(toAction)s": "Вставить здесь %(toAction)s",
+ "Drop here to tag %(section)s": "Вставить здесь для тега %(section)s",
+ "%(senderName)s ended the call": "%(senderName)s прекратил звонок",
+ "Existing Call": "Существующий вызов",
+ "Failed to lookup current room": "Не удалось выполнить поиск текущий комнаты",
+ "Failed to send request": "Не удалось выслать запрос",
+ "Failed to set up conference call": "Не удалось установить конференц-вызов",
+ "Failed to verify email address: make sure you clicked the link in the email": "Не удалось подтвердить email-адрес: убедитесь что вы щелкнули по ссылке электронной почты",
+ "Failure to create room": "Не удалось создать комнату",
+ "%(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": "Гостевые пользователи не могут создавать новые комнаты. Зарегистрируйтесь для создания комнаты и чата",
+ "click to reveal": "нажать для открытия",
+ "%(senderName)s invited %(targetName)s": "%(senderName)s приглашает %(targetName)s",
+ "%(displayName)s is typing": "%(displayName)s вводит текст",
+ "%(targetName)s joined the room": "%(targetName)s присоединенный к комнате",
+ "%(senderName)s kicked %(targetName)s": "%(senderName)s выкинул %(targetName)s",
+ "%(targetName)s left the room": "%(targetName)s покинул комнату",
+ "%(senderName)s made future room history visible to": "%(senderName)s история сделаной будущей комнаты, видимая для",
+ "Missing room_id in request": "Отсутствует room_id в запросе",
+ "Missing user_id in request": "Отсутствует user_id в запросе",
+ "Must be viewing a room": "Комната должна быть посищена",
+ "New Composer & Autocomplete": "Новые Компонист & Автозаполнение",
+ "(not supported by this browser)": "(не поддерживаемый этим браузером)",
+ "af": "Африкаанс",
+ "ar-ae": "Арабский (О.А.Е)",
+ "ar-bh": "Арабский (Бахрейн)",
+ "ar-dz": "Арабский (Алжыр)",
+ "ar-eg": "Арабский (Египет)",
+ "ar-iq": "Арабский (Ирак)",
+ "ar-jo": "Арабский (Иордания)",
+ "ar-kw": "Арабский (Кувейт)",
+ "ar-lb": "Арабский (Ливан)",
+ "ar-ly": "Арабский (Ливия)",
+ "ar-ma": "Арабский (Марокко)",
+ "ar-om": "Арабский (Оман)",
+ "ar-qa": "Арабский (Катар)",
+ "ar-sa": "Арабский (Саудовская Аравия)",
+ "ar-sy": "Арабский (Сирия)",
+ "ar-tn": "Арабский (Тунис)",
+ "ar-ye": "Арабский (Йемен)",
+ "be": "Беларуский",
+ "bg": "Болгарский",
+ "ca": "Каталанский",
+ "cs": "Чешский",
+ "de-at": "Немецкий (Австрия)",
+ "de-ch": "Немецкий (Швейцария)",
+ "de-li": "Немецкий (Лихтенштейн)",
+ "de-lu": "Немецкий (Люксембург)",
+ "el": "Гречиский",
+ "en-au": "Английский (Австралия)",
+ "en-bz": "Английский (Белиз)",
+ "en-ca": "Английский (Канада)",
+ "en-gb": "Английский (UK)",
+ "en-ie": "Английский (Ирландия)",
+ "en-jm": "Английский (Ямайка)",
+ "en-nz": "Английский (Новая Зеландия)",
+ "en-tt": "Английский (Тринидад)",
+ "en-us": "Английский (US)",
+ "en-za": "Английский (Южная Африка)",
+ "es-ar": "Испанский (Аргентина)",
+ "es-bo": "Испанский (Боливия)",
+ "es-cl": "Испанский (Чили)",
+ "es-co": "Испанский (Колумбия)",
+ "es-cr": "Испанский (Коста Рика)",
+ "es-do": "Испанский (Дом. Республика)",
+ "es-ec": "Испанский (Еквадор)",
+ "es-gt": "Испанский (Гватемала)",
+ "es-hn": "Испанский (Гондурас)",
+ "es-mx": "Испанский (Мексика)",
+ "es-ni": "Испанский (Никарагуа)",
+ "es-pa": "Испанский (Панама)",
+ "et": "Эстонский",
+ "fi": "Финский",
+ "fr": "Французкий",
+ "hr": "Хорватский",
+ "it": "Итальянский",
+ "ja": "Японский",
+ "pl": "Польский",
+ "pt": "Португальский",
+ "ru-mo": "Русский (Молдавская Республика)",
+ "ro": "Румынский",
+ "uk": "Украинский",
+ "now. You can also select individual messages to resend or cancel.": "теперь. Вы можете также выбрать отдельные сообщения, чтобы снова послать или отменить.",
+ "Auto-complete": "Автозаполнение",
+ "Error changing language": "Ошибка изменения языка",
+ "Riot was unable to find the correct Data for the selected Language.": "Riot был неспособен найти правельные данные для выбранного языка.",
+ "Connectivity to the server has been lost.": "Связь с сервером была потеряна.",
+ "Sent messages will be stored until your connection has returned.": "Отправленные сообщения будут храниться, пока Ваше соединение не возобновиться.",
+ "There are no visible files in this room": "В этой комнате нет никаких видимых файлов",
+ "This doesn't look like a valid phone number.": "Это не похоже на допустимый телефонный номер.",
+ "Missing password.": "Пароль отсутствует.",
+ "Set a display name:": "Настроить отображаемое имя:",
+ "Passwords don't match.": "Пароли не совпадают.",
+ "Password too short (min %(MIN_PASSWORD_LENGTH)s).": "Пароль слишком короткий (min %(MIN_PASSWORD_LENGTH)s).",
+ "This doesn't look like a valid email address.": "Это не похоже на допустимый email адрес.",
+ "This server does not support authentication with a phone number.": "Этот сервер не поддерживает аутентификацию с телефонным номером.",
+ "User names may only contain letters, numbers, dots, hyphens and underscores.": "Имена пользователей могут только содержать буквы, числа, точки, дефисы и подчеркивания.",
+ "An unknown error occurred.": "Произошла неизвестная ошибка.",
+ "I already have an account": "У меня уже есть учетная запись",
+ "An error occured: %(error_string)s": "Произошла ошибка: %(error_string)s",
+ "Topic": "Тема",
+ "Make this room private": "Сделать эту комнату частной",
+ "Share message history with new users": "Поделись историей сообщений с новыми учасниками",
+ "Encrypt room": "Зашифровать комнату",
+ "es-pe": "Испанский (Перу)",
+ "hu": "Венгерский",
+ "nl": "Датский",
+ "no": "Норвежский",
+ "sv": "Шведский",
+ "th": "Тайландский",
+ "vi": "Ветнамский",
+ "Monday": "Понедельник",
+ "Tuesday": "Вторник",
+ "Wednesday": "Среда",
+ "Thursday": "Четверг",
+ "Friday": "Пятница",
+ "Saturday": "Субота",
+ "Sunday": "Воскресенье",
+ "%(weekDayName)s %(time)s": "%(weekDayName)s %(time)s",
+ "Upload an avatar:": "Загрузить аватар",
+ "You need to be logged in": "Вы должны быть зарегистрированы",
+ "You need to be able to invite users to do that": "Вам необходимо пригласить пользователей чтобы сделать это",
+ "You cannot place VoIP calls in this browser": "Вы не можете сделать вызовы VoIP с этим браузером",
+ "You are already in a call": "Вы уже находитесь в разговоре",
+ "You're not in any rooms yet! Press": "Вы еще не находитесь ни в каких комнатах! Нажать",
+ "You are trying to access %(roomName)s": "Вы пытаетесь получить доступ %(roomName)s",
+ "You cannot place a call with yourself": "Вы не можете позвонить самим себе",
+ "%(senderName)s withdrew %(targetName)s's inivitation": "%(senderName)s анулировал %(targetName)s's преглашение",
+ "Sep": "Сен.",
+ "Jan": "Янв.",
+ "Feb": "Фев.",
+ "Mar": "Мар.",
+ "Apr": "Апр.",
+ "May": "Май",
+ "Jun": "Июн.",
+ "Jul": "Июл.",
+ "Aug": "Авг.",
+ "Oct": "Окт.",
+ "Nov": "Ноя.",
+ "Dec": "Дек.",
+ "%(weekDayName)s, %(monthName)s %(day)s %(time)s": "%(weekDayName)s, %(monthName)s %(day)s %(time)s",
+ "Mon": "Пн",
+ "Sun": "Вс",
+ "Tue": "Вт",
+ "Wed": "Ср",
+ "Thu": "Чт",
+ "Fri": "Пя",
+ "Sat": "Сб",
+ "You need to log back in to generate end-to-end encryption keys for this device and submit the public key to your homeserver. This is a once off; sorry for the inconvenience": "Вам необходимо снова войти в генерировать сквозное шифрование (е2е) ключей для этого устройства и предоставить публичный ключ Вашему домашнему серверу. Это после выключения; приносим извинения за причиненные неудобства",
+ "Your email address does not appear to be associated with a Matrix ID on this Homeserver": "Ваш адрес электронной почты, кажется, не связан с Matrix ID на этом Homeserver",
+ "to start a chat with someone": "Начать чат с кем-то",
+ "to tag direct chat": "Пометить прямой чат",
+ "To use it, just wait for autocomplete results to load and tab through them": "Для его использования, просто подождите результатов автозаполнения для загрузки на вкладке и через них",
+ "%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s)": "%(senderName)s включил сквозное шифрование (algorithm %(algorithm)s)",
+ "Unable to restore previous session": "Невозможно востановить предыдущий сеанс",
+ "%(senderName)s unbanned %(targetName)s": "%(senderName)s запрет отменен %(targetName)s",
+ "Unable to capture screen": "Невозможно записать снимок экрана",
+ "Unable to enable Notifications": "Невозможно включить уведомления",
+ "Upload Failed": "Неудавшаяся загрузка",
+ "Usage": "Использование",
+ "Use with caution": "Используйте с осторожностью",
+ "VoIP is unsupported": "VoIP не поддерживается",
+ "es-pr": "Испанский (Пуэрто-Рико)",
+ "es-py": "Испанский язык (Парагвай)",
+ "es": "Испанский (Испания)",
+ "es-sv": "Испанский (Сальвадор)",
+ "es-uy": "Испанский (Уругвай)",
+ "es-ve": "Испанский (Венесуэла)",
+ "fa": "Фарси",
+ "fo": "Фарезский",
+ "fr-be": "Французский (Бельгия)",
+ "fr-ca": "Французский (Канада)",
+ "fr-ch": "Французский (Швейцария)",
+ "ga": "Ирландский",
+ "he": "Иврит",
+ "hi": "Хинди",
+ "id": "Индонезийский",
+ "is": "Исландский",
+ "ji": "Идиш",
+ "lt": "Литовкий",
+ "lv": "Латвийский",
+ "ms": "Малайцы",
+ "mt": "Мальтийский",
+ "nl-be": "Голландский (Бельгия)",
+ "rm": "Ретороманский",
+ "sb": "Вендский",
+ "sk": "Словацкий",
+ "sl": "Словенский",
+ "sq": "Албанский",
+ "sr": "Сербский (Латиница)",
+ "sv-fi": "Шведский (Финляндия)",
+ "sz": "Саами (лопарский)",
+ "tn": "Тсвана",
+ "tr": "Турецкий",
+ "ts": "Тсонга",
+ "ur": "Урду",
+ "ve": "Венда",
+ "xh": "Коса",
+ "zh-cn": "Китайский (PRC)",
+ "zh-sg": "Китайский (Сингапур)",
+ "zh-tw": "Китайский (Тайвань)",
+ "zu": "Зулусский",
+ "eu": "Баскский",
+ "fr-lu": "Французский (Люксембург)",
+ "gd": "Гэльский (Шотландия)",
+ "it-ch": "Итальянский (Швейцария)",
+ "ko": "Корейский (Johab)",
+ "mk": "Македонский (FYROM)",
+ "ro-mo": "Румынский (Республика Молдова)",
+ "sx": "Суту",
+ "zh-hk": "Китайский (Гонконг)",
+ "A text message has been sent to +%(msisdn)s. Please enter the verification code it contains": "На +%(msisdn)s было отправлено текстовое сообщение. Пожалуйста, введите проверочный код из него",
+ "and %(overflowCount)s others...": "и %(overflowCounts)s других...",
+ "Are you sure?": "Вы уверены?",
+ "Autoplay GIFs and videos": "Проигрывать GIF и видео автоматически",
+ "Can't connect to homeserver - please check your connectivity and ensure your %(urlStart)s homeserver's SSL certificate %(urlEnd)s is trusted": "Невозможно соединиться с домашним сервером - проверьте своё соединение и убедитесь, что %(urlStart)s SSL-сертификат вашего домашнего сервера %(urlEnd)s включён в доверяемые",
+ "changing room on a RoomView is not supported": "изменение комнаты в RoomView не поддерживается",
+ "Click to mute audio": "Выключить звук",
+ "Click to mute video": "Выключить звук у видео",
+ "Click to unmute video": "Включить звук у видео",
+ "Click to unmute audio": "Включить звук",
+ "Decrypt %(text)s": "Расшифровать %(text)s",
+ "Delete": "Удалить",
+ "Devices": "Устройства",
+ "Direct chats": "Личные чаты",
+ "Disinvite": "Отозвать приглашение",
+ "Don't send typing notifications": "Не оповещать, когда я печатаю",
+ "Download %(text)s": "Загрузить %(text)s",
+ "Enable encryption": "Включить шифрование",
+ "Enter Code": "Ввести код",
+ "Failed to ban user": "Не удалось забанить пользователя",
+ "Failed to change power level": "Не удалось изменить уровень привилегий",
+ "Failed to delete device": "Не удалось удалить устройство",
+ "Failed to forget room %(errCode)s": "Не удалось забыть комнату %(errCode)s",
+ "Failed to join room": "Не удалось присоединиться к комнате",
+ "Failed to join the room": "Не удалось присоединиться к комнате"
+}
diff --git a/src/index.js b/src/index.js
index b6d8c0b5f4..03b854a112 100644
--- a/src/index.js
+++ b/src/index.js
@@ -14,7 +14,9 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-var Skinner = require('./Skinner');
+import Skinner from './Skinner';
+import request from 'browser-request';
+import UserSettingsStore from './UserSettingsStore';
module.exports.loadSkin = function(skinObject) {
Skinner.load(skinObject);
diff --git a/src/languageHandler.js b/src/languageHandler.js
new file mode 100644
index 0000000000..e22e93d664
--- /dev/null
+++ b/src/languageHandler.js
@@ -0,0 +1,166 @@
+/*
+Copyright 2017 MTRNord and Cooperative EITA
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import request from 'browser-request';
+// Workaround for broken export
+import * as counterpart from 'counterpart-riot';
+import UserSettingsStore from './UserSettingsStore';
+var q = require('q');
+
+const i18nFolder = 'i18n/';
+
+module.exports.setLanguage = function(languages, extCounterpart=null) {
+ if (!languages || !Array.isArray(languages)) {
+ const languages = this.getNormalizedLanguageKeys(this.getLanguageFromBrowser());
+ console.log("no language found. Got from browser: " + JSON.stringify(languages));
+ }
+
+ request(i18nFolder + 'languages.json', function(err, response, body) {
+ function getLanguage(langPath, langCode, callback) {
+ let response_return = {};
+ let resp_raw = {};
+ request(
+ { method: "GET", url: langPath },
+ (err, response, body) => {
+ if (err || response.status < 200 || response.status >= 300) {
+ if (response) {
+ if (response.status == 404 || (response.status == 0 && body == '')) {
+ resp_raw = {};
+ }
+ }
+ const resp = {err: err, response: resp_raw};
+ err = resp['err'];
+ const response_cb = resp['response'];
+ callback(err, response_cb, langCode);
+ return;
+ }
+
+ response_return = JSON.parse(body);
+ callback(null, response_return, langCode);
+ return;
+ }
+ );
+ return;
+ }
+
+ function registerTranslations(err, langJson, langCode){
+ if (err !== null) {
+ var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
+ Modal.createDialog(ErrorDialog, {
+ title: counterpart.translate('Error changing language'),
+ description: counterpart.translate('Riot was unable to find the correct Data for the selected Language.'),
+ button: counterpart.translate("OK"),
+ });
+ return;
+ } else {
+ if (extCounterpart) {
+ extCounterpart.registerTranslations(langCode, langJson);
+ }
+ counterpart.registerTranslations(langCode, langJson);
+ }
+ }
+
+ let languageFiles = {};
+ if(err){
+ console.error(err);
+ return;
+ } else {
+ if (body) {
+ languageFiles = JSON.parse(body);
+ } else {
+ languageFiles = JSON.parse('{"en": "en_EN.json"}');
+ }
+ }
+
+
+ const isValidFirstLanguage = (languageFiles.hasOwnProperty(languages[0]));
+ var validLanguageKey = "";
+ if ((isValidFirstLanguage) || (languages.length==2 && languageFiles.hasOwnProperty(languages[1]))) {
+ validLanguageKey = (isValidFirstLanguage) ? languages[0] : languages[1];
+ getLanguage(i18nFolder + languageFiles[validLanguageKey], validLanguageKey, registerTranslations);
+ if (extCounterpart) {
+ extCounterpart.setLocale(validLanguageKey);
+ }
+ counterpart.setLocale(validLanguageKey);
+ UserSettingsStore.setLocalSetting('language', validLanguageKey);
+ console.log("set language to "+validLanguageKey);
+ } else {
+ console.log("didnt find any language file");
+ }
+
+ //Set 'en' as fallback language:
+ if (validLanguageKey!="en") {
+ getLanguage(i18nFolder + languageFiles['en'], 'en', registerTranslations);
+ }
+ if (extCounterpart) {
+ extCounterpart.setFallbackLocale('en');
+ }
+ counterpart.setFallbackLocale('en');
+ });
+};
+
+module.exports.getAllLanguageKeysFromJson = function() {
+ let deferred = q.defer();
+
+ request(
+ { method: "GET", url: i18nFolder + 'languages.json' },
+ (err, response, body) => {
+ if (err || response.status < 200 || response.status >= 300) {
+ if (response) {
+ if (response.status == 404 || (response.status == 0 && body == '')) {
+ deferred.resolve({});
+ }
+ }
+ deferred.reject({err: err, response: response});
+ return;
+ }
+ var languages = JSON.parse(body);
+ // If no language is found, fallback to 'en':
+ if (!languages) {
+ languages = [{"en": "en_EN.json"}];
+ }
+ const languageKeys = Object.keys(languages);
+ deferred.resolve(languageKeys);
+ }
+ );
+ return deferred.promise;
+}
+
+module.exports.getLanguageFromBrowser = function() {
+ return navigator.languages[0] || navigator.language || navigator.userLanguage;
+};
+
+module.exports.getNormalizedLanguageKeys = function(language) {
+ if (!language) {
+ return;
+ }
+ const languageKeys = [];
+ const normalizedLanguage = this.normalizeLanguageKey(language);
+ const languageParts = normalizedLanguage.split('-');
+ if (languageParts.length==2 && languageParts[0]==languageParts[1]) {
+ languageKeys.push(languageParts[0]);
+ } else {
+ languageKeys.push(normalizedLanguage);
+ if (languageParts.length==2) {
+ languageKeys.push(languageParts[0]);
+ }
+ }
+ return languageKeys;
+};
+
+module.exports.normalizeLanguageKey = function(language) {
+ return language.toLowerCase().replace("_","-");
+};
diff --git a/test/test-utils.js b/test/test-utils.js
index 5209465362..40be715abd 100644
--- a/test/test-utils.js
+++ b/test/test-utils.js
@@ -3,6 +3,8 @@
import sinon from 'sinon';
import q from 'q';
import ReactTestUtils from 'react-addons-test-utils';
+import * as counterpart from 'counterpart-riot';
+var languageHandler = require("../src/languageHandler");
import peg from '../src/MatrixClientPeg.js';
import jssdk from 'matrix-js-sdk';
@@ -65,6 +67,10 @@ export function beforeEach(context) {
console.log(desc);
console.log(new Array(1 + desc.length).join("="));
+
+ const languages = languageHandler.getNormalizedLanguageKeys('en');
+ languageHandler.setLanguage(languages, counterpart);
+
};
From dd1c50ce80452ed7b445c888817d67b2e14262fc Mon Sep 17 00:00:00 2001
From: David Baker
Date: Tue, 23 May 2017 14:29:21 +0100
Subject: [PATCH 0160/1016] README formatting
Move translation status to its own heading
---
README.md | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 981a217c10..56965c5f86 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,3 @@
-[](https://translate.nordgedanken.de/engage/riot-web/?utm_source=widget)
-
matrix-react-sdk
================
@@ -26,6 +24,11 @@ In the interim, `vector-im/riot-web` and `matrix-org/matrix-react-sdk` should
be considered as a single project (for instance, matrix-react-sdk bugs
are currently filed against vector-im/riot-web rather than this project).
+Translation Status
+==================
+[](https://translate.nordgedanken.de/engage/riot-web/?utm_source=widget)
+
+
Developer Guide
===============
From 9bce417400d1647405068e48f9c79fd01cb79a5b Mon Sep 17 00:00:00 2001
From: MTRNord
Date: Tue, 23 May 2017 19:00:30 +0200
Subject: [PATCH 0161/1016] fix algorithm ReferenceError
---
src/TextForEvent.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/TextForEvent.js b/src/TextForEvent.js
index 25af1e5b5d..36a6eff45f 100644
--- a/src/TextForEvent.js
+++ b/src/TextForEvent.js
@@ -176,7 +176,7 @@ function textForHistoryVisibilityEvent(event) {
function textForEncryptionEvent(event) {
var senderName = event.sender ? event.sender.name : event.getSender();
- return _t('%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s)', {senderName: senderName, algorithm: algorithm});
+ return _t('%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s)', {senderName: senderName, algorithm: event.getContent().algorithm});
}
// Currently will only display a change if a user's power level is changed
From cc0924f1c7afbe7713efe6829c7e9da5005f41fd Mon Sep 17 00:00:00 2001
From: David Baker
Date: Tue, 23 May 2017 18:16:02 +0100
Subject: [PATCH 0162/1016] Fix imports
---
src/components/views/elements/LanguageDropdown.js | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/components/views/elements/LanguageDropdown.js b/src/components/views/elements/LanguageDropdown.js
index 0da05ff07e..4feec57df8 100644
--- a/src/components/views/elements/LanguageDropdown.js
+++ b/src/components/views/elements/LanguageDropdown.js
@@ -18,10 +18,9 @@ import React from 'react';
import sdk from '../../../index';
import UserSettingsStore from '../../../UserSettingsStore';
-const _localSettings = UserSettingsStore.getLocalSettings();
import _t from 'counterpart-riot';
-const languageHandler = require('../../../languageHandler');
-var SdkConfig = require("../../../SdkConfig");
+import languageHandler from '../../../languageHandler';
+import SdkConfig from '../../../SdkConfig';
let LANGUAGES = [];
@@ -44,7 +43,6 @@ export default class LanguageDropdown extends React.Component {
}
componentWillMount() {
-
const languageKeys = SdkConfig.get().languages;
// Build const LANGUAGES in a way that counterpart allows translation inside object:
@@ -71,6 +69,7 @@ export default class LanguageDropdown extends React.Component {
// If no value is given, we start with the first
// country selected, but our parent component
// doesn't know this, therefore we do this.
+ const _localSettings = UserSettingsStore.getLocalSettings();
if (_localSettings.hasOwnProperty('language')) {
this.props.onOptionChange(_localSettings.language);
}else {
@@ -117,6 +116,7 @@ export default class LanguageDropdown extends React.Component {
// default value here too, otherwise we need to handle null / undefined
// values between mounting and the initial value propgating
let value = null;
+ const _localSettings = UserSettingsStore.getLocalSettings();
if (_localSettings.hasOwnProperty('language')) {
value = this.props.value || _localSettings.language;
} else {
From f412552c2c8326edaa6d107c7e3c3d68b8fbe022 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Tue, 23 May 2017 18:32:45 +0100
Subject: [PATCH 0163/1016] Fix imports/exports
---
src/components/structures/UserSettings.js | 2 +-
src/components/views/elements/LanguageDropdown.js | 2 +-
src/languageHandler.js | 12 ++++++------
3 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js
index ff3d2e774c..12bd9fc0d6 100644
--- a/src/components/structures/UserSettings.js
+++ b/src/components/structures/UserSettings.js
@@ -30,7 +30,7 @@ const AddThreepid = require('../../AddThreepid');
const SdkConfig = require('../../SdkConfig');
import AccessibleButton from '../views/elements/AccessibleButton';
import _t from 'counterpart-riot';
-const languageHandler = require('../../languageHandler');
+import * as languageHandler from('../../languageHandler');
import * as FormattingUtils from '../../utils/FormattingUtils';
// if this looks like a release, use the 'version' from package.json; else use
diff --git a/src/components/views/elements/LanguageDropdown.js b/src/components/views/elements/LanguageDropdown.js
index 4feec57df8..27ce8efaab 100644
--- a/src/components/views/elements/LanguageDropdown.js
+++ b/src/components/views/elements/LanguageDropdown.js
@@ -19,7 +19,7 @@ import React from 'react';
import sdk from '../../../index';
import UserSettingsStore from '../../../UserSettingsStore';
import _t from 'counterpart-riot';
-import languageHandler from '../../../languageHandler';
+import * as languageHandler from '../../../languageHandler';
import SdkConfig from '../../../SdkConfig';
let LANGUAGES = [];
diff --git a/src/languageHandler.js b/src/languageHandler.js
index e22e93d664..92e3ccefb6 100644
--- a/src/languageHandler.js
+++ b/src/languageHandler.js
@@ -18,11 +18,11 @@ import request from 'browser-request';
// Workaround for broken export
import * as counterpart from 'counterpart-riot';
import UserSettingsStore from './UserSettingsStore';
-var q = require('q');
+import q from 'q';
const i18nFolder = 'i18n/';
-module.exports.setLanguage = function(languages, extCounterpart=null) {
+export function setLanguage(languages, extCounterpart=null) {
if (!languages || !Array.isArray(languages)) {
const languages = this.getNormalizedLanguageKeys(this.getLanguageFromBrowser());
console.log("no language found. Got from browser: " + JSON.stringify(languages));
@@ -112,7 +112,7 @@ module.exports.setLanguage = function(languages, extCounterpart=null) {
});
};
-module.exports.getAllLanguageKeysFromJson = function() {
+export function getAllLanguageKeysFromJson() {
let deferred = q.defer();
request(
@@ -139,11 +139,11 @@ module.exports.getAllLanguageKeysFromJson = function() {
return deferred.promise;
}
-module.exports.getLanguageFromBrowser = function() {
+export function getLanguageFromBrowser() {
return navigator.languages[0] || navigator.language || navigator.userLanguage;
};
-module.exports.getNormalizedLanguageKeys = function(language) {
+export function getNormalizedLanguageKeys(language) {
if (!language) {
return;
}
@@ -161,6 +161,6 @@ module.exports.getNormalizedLanguageKeys = function(language) {
return languageKeys;
};
-module.exports.normalizeLanguageKey = function(language) {
+export function normalizeLanguageKey(language) {
return language.toLowerCase().replace("_","-");
};
From a95f3252b8fb4c1658095620050ffd78fd43c630 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Wed, 24 May 2017 10:48:10 +0100
Subject: [PATCH 0164/1016] s/tabs/spaces/
---
.../views/elements/LanguageDropdown.js | 24 +++++++++----------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/src/components/views/elements/LanguageDropdown.js b/src/components/views/elements/LanguageDropdown.js
index 27ce8efaab..9524b3ff27 100644
--- a/src/components/views/elements/LanguageDropdown.js
+++ b/src/components/views/elements/LanguageDropdown.js
@@ -43,16 +43,16 @@ export default class LanguageDropdown extends React.Component {
}
componentWillMount() {
- const languageKeys = SdkConfig.get().languages;
+ const languageKeys = SdkConfig.get().languages;
- // Build const LANGUAGES in a way that counterpart allows translation inside object:
- languageKeys.forEach(function(languageKey) {
- var l = {};
- l.id = "language";
- l.label = _t(languageKey);
- l.value = languageKey;
- LANGUAGES.push(l);
- });
+ // Build const LANGUAGES in a way that counterpart allows translation inside object:
+ languageKeys.forEach(function(languageKey) {
+ var l = {};
+ l.id = "language";
+ l.label = _t(languageKey);
+ l.value = languageKey;
+ LANGUAGES.push(l);
+ });
LANGUAGES = LANGUAGES.sort(function(a, b){
if(a.label < b.label) return -1;
@@ -60,9 +60,9 @@ export default class LanguageDropdown extends React.Component {
return 0;
})
- for (const l of LANGUAGES) {
- LANGUAGES_BY_VALUE[l.value] = l;
- }
+ for (const l of LANGUAGES) {
+ LANGUAGES_BY_VALUE[l.value] = l;
+ }
if (!this.props.value) {
From c2df23395daa3fee284b05f10c6c698eb19c0824 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Wed, 24 May 2017 11:25:06 +0100
Subject: [PATCH 0165/1016] Fix up language dropdown
* Read languages from the available ones rather than the config
* Make the search work (add searchEnabled=true)
* Move variables to the class since they were populated there
---
.../views/elements/LanguageDropdown.js | 69 ++++++++-----------
1 file changed, 28 insertions(+), 41 deletions(-)
diff --git a/src/components/views/elements/LanguageDropdown.js b/src/components/views/elements/LanguageDropdown.js
index 9524b3ff27..764657830c 100644
--- a/src/components/views/elements/LanguageDropdown.js
+++ b/src/components/views/elements/LanguageDropdown.js
@@ -20,11 +20,6 @@ import sdk from '../../../index';
import UserSettingsStore from '../../../UserSettingsStore';
import _t from 'counterpart-riot';
import * as languageHandler from '../../../languageHandler';
-import SdkConfig from '../../../SdkConfig';
-
-let LANGUAGES = [];
-
-const LANGUAGES_BY_VALUE = new Object(null);
function languageMatchesSearchQuery(query, language) {
if (language.label.toUpperCase().indexOf(query.toUpperCase()) == 0) return true;
@@ -39,31 +34,28 @@ export default class LanguageDropdown extends React.Component {
this.state = {
searchQuery: '',
+ langs: null,
}
}
componentWillMount() {
- const languageKeys = SdkConfig.get().languages;
-
- // Build const LANGUAGES in a way that counterpart allows translation inside object:
- languageKeys.forEach(function(languageKey) {
- var l = {};
- l.id = "language";
- l.label = _t(languageKey);
- l.value = languageKey;
- LANGUAGES.push(l);
- });
-
- LANGUAGES = LANGUAGES.sort(function(a, b){
- if(a.label < b.label) return -1;
- if(a.label > b.label) return 1;
- return 0;
- })
-
- for (const l of LANGUAGES) {
- LANGUAGES_BY_VALUE[l.value] = l;
- }
-
+ languageHandler.getAllLanguageKeysFromJson().then((langKeys) => {
+ const langs = [];
+ langKeys.forEach((languageKey) => {
+ langs.push({
+ value: languageKey,
+ label: _t(languageKey)
+ });
+ });
+ langs.sort(function(a, b){
+ if(a.label < b.label) return -1;
+ if(a.label > b.label) return 1;
+ return 0;
+ });
+ this.setState({langs});
+ }).catch(() => {
+ this.setState({langs: ['en']});
+ }).done();
if (!this.props.value) {
// If no value is given, we start with the first
@@ -86,25 +78,20 @@ export default class LanguageDropdown extends React.Component {
}
render() {
+ if (this.state.langs === null) {
+ const Spinner = sdk.getComponent('elements.Spinner');
+ return ;
+ }
+
const Dropdown = sdk.getComponent('elements.Dropdown');
let displayedLanguages;
if (this.state.searchQuery) {
- displayedLanguages = LANGUAGES.filter(
- languageMatchesSearchQuery.bind(this, this.state.searchQuery),
- );
- if (
- this.state.searchQuery.length == 2 &&
- LANGUAGES_BY_VALUE[this.state.searchQuery.toUpperCase()]
- ) {
- const matched = LANGUAGES_BY_VALUE[this.state.searchQuery.toUpperCase()];
- displayedLanguages = displayedLanguages.filter((l) => {
- return l.id != matched.id;
- });
- displayedLanguages.unshift(matched);
- }
+ displayedLanguages = this.state.langs.filter((lang) => {
+ return languageMatchesSearchQuery(this.state.searchQuery, lang);
+ });
} else {
- displayedLanguages = LANGUAGES;
+ displayedLanguages = this.state.langs;
}
const options = displayedLanguages.map((language) => {
@@ -126,7 +113,7 @@ export default class LanguageDropdown extends React.Component {
return
{options}
From aef068b00e38d94472573514b166659d2674aacd Mon Sep 17 00:00:00 2001
From: David Baker
Date: Wed, 24 May 2017 14:28:30 +0100
Subject: [PATCH 0166/1016] Indenting
---
src/components/structures/UserSettings.js | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js
index 12bd9fc0d6..1772108e03 100644
--- a/src/components/structures/UserSettings.js
+++ b/src/components/structures/UserSettings.js
@@ -557,18 +557,18 @@ module.exports = React.createClass({
},
onLanguageChange: function(l) {
- UserSettingsStore.setLocalSetting('language', l);
- this.setState({
- Language: l,
- });
- PlatformPeg.get().reload();
+ UserSettingsStore.setLocalSetting('language', l);
+ this.setState({
+ Language: l,
+ });
+ PlatformPeg.get().reload();
},
_renderLanguageSetting: function () {
- const LanguageDropdown = sdk.getComponent('views.elements.LanguageDropdown');
- return ;
+ const LanguageDropdown = sdk.getComponent('views.elements.LanguageDropdown');
+ return ;
},
_renderUserInterfaceSettings: function() {
From b677a2002f67f385a024a6281bae60f7bc095d09 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Wed, 24 May 2017 14:36:14 +0100
Subject: [PATCH 0167/1016] Minimal UI fixes
Add a label. Make css selector match style. Also fix import.
---
src/components/structures/UserSettings.js | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js
index 1772108e03..e0302931f5 100644
--- a/src/components/structures/UserSettings.js
+++ b/src/components/structures/UserSettings.js
@@ -30,7 +30,7 @@ const AddThreepid = require('../../AddThreepid');
const SdkConfig = require('../../SdkConfig');
import AccessibleButton from '../views/elements/AccessibleButton';
import _t from 'counterpart-riot';
-import * as languageHandler from('../../languageHandler');
+import * as languageHandler from '../../languageHandler';
import * as FormattingUtils from '../../utils/FormattingUtils';
// if this looks like a release, use the 'version' from package.json; else use
@@ -566,9 +566,13 @@ module.exports = React.createClass({
_renderLanguageSetting: function () {
const LanguageDropdown = sdk.getComponent('views.elements.LanguageDropdown');
- return ;
+ return
+ Interface Language
+
+
;
},
_renderUserInterfaceSettings: function() {
From 1186207658951385965231e08b813bd0cd91343b Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Wed, 24 May 2017 15:40:50 +0100
Subject: [PATCH 0168/1016] Initial Electron Settings - for Auto Launch (opens
path for Proxy Settings)
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
---
src/BasePlatform.js | 6 ++--
src/components/structures/UserSettings.js | 35 +++++++++++++++++++++++
2 files changed, 39 insertions(+), 2 deletions(-)
diff --git a/src/BasePlatform.js b/src/BasePlatform.js
index 6eed22f436..0fa377c88e 100644
--- a/src/BasePlatform.js
+++ b/src/BasePlatform.js
@@ -70,7 +70,7 @@ export default class BasePlatform {
* Returns a promise that resolves to a string representing
* the current version of the application.
*/
- getAppVersion() {
+ getAppVersion(): Promise {
throw new Error("getAppVersion not implemented!");
}
@@ -79,10 +79,12 @@ export default class BasePlatform {
* with getUserMedia, return a string explaining why not.
* Otherwise, return null.
*/
- screenCaptureErrorString() {
+ screenCaptureErrorString(): string {
return "Not implemented";
}
+ isElectron(): boolean { return false; }
+
/**
* Restarts the application, without neccessarily reloading
* any application code
diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js
index 2d76047d33..0407c9b43a 100644
--- a/src/components/structures/UserSettings.js
+++ b/src/components/structures/UserSettings.js
@@ -197,6 +197,16 @@ module.exports = React.createClass({
this._syncedSettings = syncedSettings;
this._localSettings = UserSettingsStore.getLocalSettings();
+
+ if (PlatformPeg.get().isElectron()) {
+ const {ipcRenderer} = require('electron');
+
+ ipcRenderer.once('settings', (ev, settings) => {
+ this.setState({ electron_settings: settings });
+ });
+
+ ipcRenderer.send('settings_get');
+ }
},
componentDidMount: function() {
@@ -787,6 +797,29 @@ module.exports = React.createClass({
;
},
+ _renderElectronSettings: function() {
+ const settings = this.state.electron_settings;
+ if (!settings) return;
+
+ const {ipcRenderer} = require('electron');
+
+ return
+
Electron Settings
+
+
+ {
+ ipcRenderer.send('settings_set', 'auto-launch', e.target.checked);
+ }}
+ />
+ Start automatically after system login
+
+
+
;
+ },
+
_showSpoiler: function(event) {
const target = event.target;
target.innerHTML = target.getAttribute('data-spoiler');
@@ -988,6 +1021,8 @@ module.exports = React.createClass({
{this._renderBulkOptions()}
{this._renderBugReport()}
+ {PlatformPeg.get().isElectron() && this._renderElectronSettings()}
+
Advanced
From b9ac1222ed08239e56c11e4f2597b300e41afccb Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Wed, 24 May 2017 15:55:37 +0100
Subject: [PATCH 0169/1016] remove listener on unmount
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
---
src/components/structures/UserSettings.js | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js
index 0407c9b43a..5ce0779322 100644
--- a/src/components/structures/UserSettings.js
+++ b/src/components/structures/UserSettings.js
@@ -201,9 +201,7 @@ module.exports = React.createClass({
if (PlatformPeg.get().isElectron()) {
const {ipcRenderer} = require('electron');
- ipcRenderer.once('settings', (ev, settings) => {
- this.setState({ electron_settings: settings });
- });
+ ipcRenderer.on('settings', this._electronSettings);
ipcRenderer.send('settings_get');
}
@@ -226,6 +224,15 @@ module.exports = React.createClass({
if (cli) {
cli.removeListener("RoomMember.membership", this._onInviteStateChange);
}
+
+ if (PlatformPeg.get().isElectron()) {
+ const {ipcRenderer} = require('electron');
+ ipcRenderer.removeListener('settings', this._electronSettings);
+ }
+ },
+
+ _electronSettings: function(ev, settings) {
+ this.setState({ electron_settings: settings });
},
_refreshFromServer: function() {
From 298c5e4df32d87b0707b9f71ff489e12b800fe15 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Wed, 24 May 2017 16:56:13 +0100
Subject: [PATCH 0170/1016] Implement a store for RoomView
This allows for a truely flux-y way of storing the currently viewed room, making some callbacks (like onRoomIdResolved) redundant and making sure that the currently viewed room (ID) is only stored in one place as opposed to the previous many places.
This was required for the `join_room` action which can be dispatched to join the currently viewed room.
Another change was to introduce `LifeCycleStore` which is a start at encorporating state related to the lifecycle of the app into a flux store. Currently it only contains an action which will be dispatched when the sync state has become PREPARED. This was necessary to do a deferred dispatch of `join_room` following the registration of a PWLU (PassWord-Less User).
The following actions are introduced:
- RoomViewStore:
- `view_room`: dispatch to change the currently viewed room ID
- `join_room`: dispatch to join the currently viewed room
- LifecycleStore:
- `do_after_sync_prepared`: dispatch to store an action which will be dispatched when `sync_state` is dispatched with `state = 'PREPARED'`
- MatrixChat:
- `sync_state`: dispatched when the sync state changes. Ideally there'd be a SyncStateStore that emitted an `update` upon receiving this, but for now the `LifecycleStore` will listen for `sync_state` directly.
---
src/components/structures/LoggedInView.js | 5 +-
src/components/structures/MatrixChat.js | 38 ++--
src/components/structures/RoomView.js | 212 ++++++------------
src/components/views/dialogs/SetMxIdDialog.js | 1 +
src/createRoom.js | 15 +-
src/stores/LifecycleStore.js | 73 ++++++
src/stores/RoomViewStore.js | 145 ++++++++++++
src/stores/SessionStore.js | 15 ++
test/components/structures/RoomView-test.js | 67 ------
test/stores/RoomViewStore-test.js | 56 +++++
test/test-utils.js | 13 +-
11 files changed, 399 insertions(+), 241 deletions(-)
create mode 100644 src/stores/LifecycleStore.js
create mode 100644 src/stores/RoomViewStore.js
delete mode 100644 test/components/structures/RoomView-test.js
create mode 100644 test/stores/RoomViewStore-test.js
diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js
index e559a21e1a..df24fbb33b 100644
--- a/src/components/structures/LoggedInView.js
+++ b/src/components/structures/LoggedInView.js
@@ -40,7 +40,6 @@ export default React.createClass({
propTypes: {
matrixClient: React.PropTypes.instanceOf(Matrix.MatrixClient).isRequired,
page_type: React.PropTypes.string.isRequired,
- onRoomIdResolved: React.PropTypes.func,
onRoomCreated: React.PropTypes.func,
onUserSettingsClose: React.PropTypes.func,
@@ -190,16 +189,14 @@ export default React.createClass({
case PageTypes.RoomView:
page_element = {
modal.close();
- if (this.currentRoomId === roomId) {
+ if (this.state.currentRoomId === roomId) {
dis.dispatch({action: 'view_next_room'});
}
}, (err) => {
@@ -807,8 +808,12 @@ module.exports = React.createClass({
this._teamToken = teamToken;
dis.dispatch({action: 'view_home_page'});
} else if (this._is_registered) {
+ this._is_registered = false;
if (this.props.config.welcomeUserId) {
- createRoom({dmUserId: this.props.config.welcomeUserId});
+ createRoom({
+ dmUserId: this.props.config.welcomeUserId,
+ andView: false,
+ });
return;
}
// The user has just logged in after registering
@@ -853,7 +858,6 @@ module.exports = React.createClass({
ready: false,
collapse_lhs: false,
collapse_rhs: false,
- currentRoomAlias: null,
currentRoomId: null,
page_type: PageTypes.RoomDirectory,
});
@@ -891,6 +895,7 @@ module.exports = React.createClass({
});
cli.on('sync', function(state, prevState) {
+ dis.dispatch({action: 'sync_state', prevState, state});
self.updateStatusIndicator(state, prevState);
if (state === "SYNCING" && prevState === "SYNCING") {
return;
@@ -1102,6 +1107,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;
@@ -1163,13 +1170,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;
@@ -1211,10 +1211,10 @@ module.exports = React.createClass({
const LoggedInView = sdk.getComponent('structures.LoggedInView');
return (
{
this.forceUpdate();
- }
+ },
});
- if (this.props.roomAddress[0] == '#') {
- // we always look up the alias from the directory server:
- // we want the room that the given alias is pointing to
- // right now. We may have joined that alias before but there's
- // no guarantee the alias hasn't subsequently been remapped.
- MatrixClientPeg.get().getRoomIdForAlias(this.props.roomAddress).done((result) => {
- if (this.props.onRoomIdResolved) {
- this.props.onRoomIdResolved(result.room_id);
- }
- var room = MatrixClientPeg.get().getRoom(result.room_id);
- this.setState({
- room: room,
- roomId: result.room_id,
- roomLoading: !room,
- unsentMessageError: this._getUnsentMessageError(room),
- }, this._onHaveRoom);
- }, (err) => {
- this.setState({
- roomLoading: false,
- roomLoadError: err,
- });
- });
- } else {
- var room = MatrixClientPeg.get().getRoom(this.props.roomAddress);
- this.setState({
- roomId: this.props.roomAddress,
- room: room,
- roomLoading: !room,
- unsentMessageError: this._getUnsentMessageError(room),
- }, this._onHaveRoom);
+ // Start listening for RoomViewStore updates
+ RoomViewStore.addListener(this._onRoomViewStoreUpdate);
+ this._onRoomViewStoreUpdate(true);
+ },
+
+ _onRoomViewStoreUpdate: function(initial) {
+ if (this.unmounted) {
+ return;
}
+ this.setState({
+ roomId: RoomViewStore.getRoomId(),
+ roomAlias: RoomViewStore.getRoomAlias(),
+ joining: RoomViewStore.isJoining(),
+ joinError: RoomViewStore.getJoinError(),
+ }, () => {
+ this._onHaveRoom();
+ this.onRoom(MatrixClientPeg.get().getRoom(this.state.roomId));
+ });
},
_onHaveRoom: function() {
@@ -224,17 +202,17 @@ module.exports = React.createClass({
// NB. We peek if we are not in the room, although if we try to peek into
// a room in which we have a member event (ie. we've left) synapse will just
// send us the same data as we get in the sync (ie. the last events we saw).
- var user_is_in_room = null;
- if (this.state.room) {
- user_is_in_room = this.state.room.hasMembershipState(
- MatrixClientPeg.get().credentials.userId, 'join'
+ const room = MatrixClientPeg.get().getRoom(this.state.roomId);
+ let isUserJoined = null;
+ if (room) {
+ isUserJoined = room.hasMembershipState(
+ MatrixClientPeg.get().credentials.userId, 'join',
);
- this._updateAutoComplete();
- this.tabComplete.loadEntries(this.state.room);
+ this._updateAutoComplete(room);
+ this.tabComplete.loadEntries(room);
}
-
- if (!user_is_in_room && this.state.roomId) {
+ if (!isUserJoined && !this.state.joining && this.state.roomId) {
if (this.props.autoJoin) {
this.onJoinButtonClicked();
} else if (this.state.roomId) {
@@ -260,9 +238,12 @@ module.exports = React.createClass({
}
}).done();
}
- } else if (user_is_in_room) {
+ } else if (isUserJoined) {
MatrixClientPeg.get().stopPeeking();
- this._onRoomLoaded(this.state.room);
+ this.setState({
+ unsentMessageError: this._getUnsentMessageError(room),
+ });
+ this._onRoomLoaded(room);
}
},
@@ -299,10 +280,6 @@ module.exports = React.createClass({
},
componentWillReceiveProps: function(newProps) {
- if (newProps.roomAddress != this.props.roomAddress) {
- throw new Error("changing room on a RoomView is not supported");
- }
-
if (newProps.eventId != this.props.eventId) {
// when we change focussed event id, hide the search results.
this.setState({searchResults: null});
@@ -523,7 +500,7 @@ module.exports = React.createClass({
this._updatePreviewUrlVisibility(room);
},
- _warnAboutEncryption: function (room) {
+ _warnAboutEncryption: function(room) {
if (!MatrixClientPeg.get().isRoomEncrypted(room.roomId)) {
return;
}
@@ -604,20 +581,14 @@ module.exports = React.createClass({
},
onRoom: function(room) {
- // This event is fired when the room is 'stored' by the JS SDK, which
- // means it's now a fully-fledged room object ready to be used, so
- // set it in our state and start using it (ie. init the timeline)
- // This will happen if we start off viewing a room we're not joined,
- // then join it whilst RoomView is looking at that room.
- if (!this.state.room && room.roomId == this._joiningRoomId) {
- this._joiningRoomId = undefined;
- this.setState({
- room: room,
- joining: false,
- });
-
- this._onRoomLoaded(room);
+ if (!room || room.roomId !== this.state.roomId) {
+ return;
}
+ this.setState({
+ room: room,
+ }, () => {
+ this._onRoomLoaded(room);
+ });
},
updateTint: function() {
@@ -683,7 +654,7 @@ module.exports = React.createClass({
// refresh the tab complete list
this.tabComplete.loadEntries(this.state.room);
- this._updateAutoComplete();
+ this._updateAutoComplete(this.state.room);
// if we are now a member of the room, where we were not before, that
// means we have finished joining a room we were previously peeking
@@ -778,37 +749,43 @@ module.exports = React.createClass({
},
onJoinButtonClicked: function(ev) {
- var self = this;
-
- var cli = MatrixClientPeg.get();
- var mxIdPromise = q();
+ const cli = MatrixClientPeg.get();
// If the user is a ROU, allow them to transition to a PWLU
if (cli && cli.isGuest()) {
+ // Join this room once the user has registered and logged in
+ dis.dispatch({
+ action: 'do_after_sync_prepared',
+ deferred_action: {
+ action: 'join_room',
+ room_id: this.state.roomId,
+ },
+ });
+
const SetMxIdDialog = sdk.getComponent('views.dialogs.SetMxIdDialog');
- const defered = q.defer();
- mxIdPromise = defered.promise;
const close = Modal.createDialog(SetMxIdDialog, {
homeserverUrl: cli.getHomeserverUrl(),
onFinished: (submitted, credentials) => {
- if (!submitted) {
- defered.reject();
- return;
+ if (submitted) {
+ this.props.onRegistered(credentials);
}
- this.props.onRegistered(credentials);
- defered.resolve();
},
onDifferentServerClicked: (ev) => {
dis.dispatch({action: 'start_registration'});
close();
},
}).close;
+ return;
}
- mxIdPromise.then(() => {
- this.setState({
- joining: true
+ q().then(() => {
+ const signUrl = this.props.thirdPartyInvite ?
+ this.props.thirdPartyInvite.inviteSignUrl : undefined;
+ dis.dispatch({
+ action: 'join_room',
+ opts: { inviteSignUrl: signUrl },
});
+
// if this is an invite and has the 'direct' hint set, mark it as a DM room now.
if (this.state.room) {
const me = this.state.room.getMember(MatrixClientPeg.get().credentials.userId);
@@ -820,65 +797,8 @@ module.exports = React.createClass({
}
}
}
-
return q();
- }).then(() => {
- var sign_url = this.props.thirdPartyInvite ? this.props.thirdPartyInvite.inviteSignUrl : undefined;
- return MatrixClientPeg.get().joinRoom(this.props.roomAddress,
- { inviteSignUrl: sign_url } );
- }).then(function(resp) {
- var roomId = resp.roomId;
-
- // It is possible that there is no Room yet if state hasn't come down
- // from /sync - joinRoom will resolve when the HTTP request to join succeeds,
- // NOT when it comes down /sync. If there is no room, we'll keep the
- // joining flag set until we see it.
-
- // We'll need to initialise the timeline when joining, but due to
- // the above, we can't do it here: we do it in onRoom instead,
- // once we have a useable room object.
- var room = MatrixClientPeg.get().getRoom(roomId);
- if (!room) {
- // wait for the room to turn up in onRoom.
- self._joiningRoomId = roomId;
- } else {
- // we've got a valid room, but that might also just mean that
- // it was peekable (so we had one before anyway). If we are
- // not yet a member of the room, we will need to wait for that
- // to happen, in onRoomStateMember.
- var me = MatrixClientPeg.get().credentials.userId;
- self.setState({
- joining: !room.hasMembershipState(me, "join"),
- room: room
- });
- }
- }).catch(function(error) {
- self.setState({
- joining: false,
- joinError: error
- });
-
- if (!error) return;
-
- // https://matrix.org/jira/browse/SYN-659
- // Need specific error message if joining a room is refused because the user is a guest and guest access is not allowed
- if (
- error.errcode == 'M_GUEST_ACCESS_FORBIDDEN' ||
- (
- error.errcode == 'M_FORBIDDEN' &&
- MatrixClientPeg.get().isGuest()
- )
- ) {
- dis.dispatch({action: 'view_set_mxid'});
- } else {
- var msg = error.message ? error.message : JSON.stringify(error);
- var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
- Modal.createDialog(ErrorDialog, {
- title: "Failed to join room",
- description: msg
- });
- }
- }).done();
+ });
},
onMessageListScroll: function(ev) {
@@ -1451,9 +1371,9 @@ module.exports = React.createClass({
}
},
- _updateAutoComplete: function() {
+ _updateAutoComplete: function(room) {
const myUserId = MatrixClientPeg.get().credentials.userId;
- const members = this.state.room.getJoinedMembers().filter(function(member) {
+ const members = room.getJoinedMembers().filter(function(member) {
if (member.userId !== myUserId) return true;
});
UserProvider.getInstance().setUserList(members);
@@ -1491,7 +1411,7 @@ module.exports = React.createClass({
// We have no room object for this room, only the ID.
// We've got to this room by following a link, possibly a third party invite.
- var room_alias = this.props.roomAddress[0] == '#' ? this.props.roomAddress : null;
+ var room_alias = this.state.room_alias;
return (
{
+ * this.setState({ cachedPassword: lifecycleStore.getCachedPassword() })
+ * })
+ * ```
+ */
+class LifecycleStore extends Store {
+ constructor() {
+ super(dis);
+
+ // Initialise state
+ this._state = {
+ deferred_action: null,
+ };
+ }
+
+ _setState(newState) {
+ this._state = Object.assign(this._state, newState);
+ this.__emitChange();
+ }
+
+ __onDispatch(payload) {
+ switch (payload.action) {
+ case 'do_after_sync_prepared':
+ this._setState({
+ deferred_action: payload.deferred_action,
+ });
+ break;
+ case 'sync_state':
+ if (payload.state !== 'PREPARED') {
+ break;
+ }
+ console.warn(this._state);
+ if (!this._state.deferred_action) break;
+ const deferredAction = Object.assign({}, this._state.deferred_action);
+ this._setState({
+ deferred_action: null,
+ });
+ dis.dispatch(deferredAction);
+ break;
+ }
+ }
+}
+
+let singletonLifecycleStore = null;
+if (!singletonLifecycleStore) {
+ singletonLifecycleStore = new LifecycleStore();
+}
+module.exports = singletonLifecycleStore;
diff --git a/src/stores/RoomViewStore.js b/src/stores/RoomViewStore.js
new file mode 100644
index 0000000000..fe57079859
--- /dev/null
+++ b/src/stores/RoomViewStore.js
@@ -0,0 +1,145 @@
+/*
+Copyright 2017 Vector Creations Ltd
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+import dis from '../dispatcher';
+import {Store} from 'flux/utils';
+import MatrixClientPeg from '../MatrixClientPeg';
+
+const INITIAL_STATE = {
+ // Whether we're joining the currently viewed room
+ joining: false,
+ // Any error occurred during joining
+ joinError: null,
+ // The room ID of the room
+ roomId: null,
+ // The room alias of the room (or null if not originally specified in view_room)
+ roomAlias: null,
+ // Whether the current room is loading
+ roomLoading: false,
+ // Any error that has occurred during loading
+ roomLoadError: null,
+};
+
+/**
+ * A class for storing application state for RoomView. This is the RoomView's interface
+* with a subset of the js-sdk.
+ * ```
+ */
+class RoomViewStore extends Store {
+ constructor() {
+ super(dis);
+
+ // Initialise state
+ this._state = INITIAL_STATE;
+ }
+
+ _setState(newState) {
+ this._state = Object.assign(this._state, newState);
+ this.__emitChange();
+ }
+
+ __onDispatch(payload) {
+ switch (payload.action) {
+ // view_room:
+ // - room_alias: '#somealias:matrix.org'
+ // - room_id: '!roomid123:matrix.org'
+ case 'view_room':
+ this._viewRoom(payload);
+ break;
+
+ // join_room:
+ // - opts: options for joinRoom
+ case 'join_room':
+ this._joinRoom(payload);
+ break;
+ }
+ }
+
+ _viewRoom(payload) {
+ const address = payload.room_alias || payload.room_id;
+ if (address[0] == '#') {
+ this._setState({
+ roomLoading: true,
+ });
+ MatrixClientPeg.get().getRoomIdForAlias(address).then(
+ (result) => {
+ this._setState({
+ roomId: result.room_id,
+ roomAlias: address,
+ roomLoading: false,
+ roomLoadError: null,
+ });
+ }, (err) => {
+ console.error(err);
+ this._setState({
+ roomLoading: false,
+ roomLoadError: err,
+ });
+ });
+ } else {
+ this._setState({
+ roomId: address,
+ });
+ }
+ }
+
+ _joinRoom(payload) {
+ this._setState({
+ joining: true,
+ });
+ MatrixClientPeg.get().joinRoom(this._state.roomId, payload.opts).then(
+ () => {
+ this._setState({
+ joining: false,
+ });
+ }, (err) => {
+ this._setState({
+ joining: false,
+ joinError: err,
+ });
+ });
+ }
+
+ reset() {
+ this._state = Object.assign({}, INITIAL_STATE);
+ }
+
+ getRoomId() {
+ return this._state.roomId;
+ }
+
+ getRoomAlias() {
+ return this._state.roomAlias;
+ }
+
+ isRoomLoading() {
+ return this._state.roomLoading;
+ }
+
+ isJoining() {
+ return this._state.joining;
+ }
+
+ getJoinError() {
+ return this._state.joinError;
+ }
+
+}
+
+let singletonRoomViewStore = null;
+if (!singletonRoomViewStore) {
+ singletonRoomViewStore = new RoomViewStore();
+}
+module.exports = singletonRoomViewStore;
diff --git a/src/stores/SessionStore.js b/src/stores/SessionStore.js
index 1570f58688..2fd35ce40a 100644
--- a/src/stores/SessionStore.js
+++ b/src/stores/SessionStore.js
@@ -1,3 +1,18 @@
+/*
+Copyright 2017 Vector Creations Ltd
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
import dis from '../dispatcher';
import {Store} from 'flux/utils';
diff --git a/test/components/structures/RoomView-test.js b/test/components/structures/RoomView-test.js
deleted file mode 100644
index 8e7c8160b8..0000000000
--- a/test/components/structures/RoomView-test.js
+++ /dev/null
@@ -1,67 +0,0 @@
-var React = require('react');
-var expect = require('expect');
-var sinon = require('sinon');
-var ReactDOM = require("react-dom");
-
-var sdk = require('matrix-react-sdk');
-var RoomView = sdk.getComponent('structures.RoomView');
-var peg = require('../../../src/MatrixClientPeg');
-
-var test_utils = require('../../test-utils');
-var q = require('q');
-
-var Skinner = require("../../../src/Skinner");
-var stubComponent = require('../../components/stub-component.js');
-
-describe('RoomView', function () {
- var sandbox;
- var parentDiv;
-
- beforeEach(function() {
- test_utils.beforeEach(this);
- sandbox = test_utils.stubClient();
- parentDiv = document.createElement('div');
-
- this.oldTimelinePanel = Skinner.getComponent('structures.TimelinePanel');
- this.oldRoomHeader = Skinner.getComponent('views.rooms.RoomHeader');
- Skinner.addComponent('structures.TimelinePanel', stubComponent());
- Skinner.addComponent('views.rooms.RoomHeader', stubComponent());
-
- peg.get().credentials = { userId: "@test:example.com" };
- });
-
- afterEach(function() {
- sandbox.restore();
-
- ReactDOM.unmountComponentAtNode(parentDiv);
-
- Skinner.addComponent('structures.TimelinePanel', this.oldTimelinePanel);
- Skinner.addComponent('views.rooms.RoomHeader', this.oldRoomHeader);
- });
-
- it('resolves a room alias to a room id', function (done) {
- peg.get().getRoomIdForAlias.returns(q({room_id: "!randomcharacters:aser.ver"}));
-
- function onRoomIdResolved(room_id) {
- expect(room_id).toEqual("!randomcharacters:aser.ver");
- done();
- }
-
- ReactDOM.render( , parentDiv);
- });
-
- it('joins by alias if given an alias', function (done) {
- peg.get().getRoomIdForAlias.returns(q({room_id: "!randomcharacters:aser.ver"}));
- peg.get().getProfileInfo.returns(q({displayname: "foo"}));
- var roomView = ReactDOM.render( , parentDiv);
-
- peg.get().joinRoom = function(x) {
- expect(x).toEqual('#alias:ser.ver');
- done();
- };
-
- process.nextTick(function() {
- roomView.onJoinButtonClicked();
- });
- });
-});
diff --git a/test/stores/RoomViewStore-test.js b/test/stores/RoomViewStore-test.js
new file mode 100644
index 0000000000..7100dced19
--- /dev/null
+++ b/test/stores/RoomViewStore-test.js
@@ -0,0 +1,56 @@
+import expect from 'expect';
+
+import dis from '../../src/dispatcher';
+import RoomViewStore from '../../src/stores/RoomViewStore';
+
+
+import peg from '../../src/MatrixClientPeg';
+
+import * as testUtils from '../test-utils';
+import q from 'q';
+
+const dispatch = testUtils.getDispatchForStore(RoomViewStore);
+
+describe('RoomViewStore', function() {
+ let sandbox;
+
+ beforeEach(function() {
+ testUtils.beforeEach(this);
+ sandbox = testUtils.stubClient();
+ peg.get().credentials = { userId: "@test:example.com" };
+
+ // Reset the state of the store
+ RoomViewStore.reset();
+ });
+
+ afterEach(function() {
+ sandbox.restore();
+ });
+
+ it('can be used to view a room by ID and join', function(done) {
+ peg.get().joinRoom = (roomId) => {
+ expect(roomId).toBe("!randomcharacters:aser.ver");
+ done();
+ };
+
+ dispatch({ action: 'view_room', room_id: '!randomcharacters:aser.ver' });
+ dispatch({ action: 'join_room' });
+ expect(RoomViewStore.isJoining()).toBe(true);
+ });
+
+ it('can be used to view a room by alias and join', function(done) {
+ peg.get().getRoomIdForAlias.returns(q({room_id: "!randomcharacters:aser.ver"}));
+ peg.get().joinRoom = (roomId) => {
+ expect(roomId).toBe("!randomcharacters:aser.ver");
+ done();
+ };
+
+ dispatch({ action: 'view_room', room_alias: '#somealias2:aser.ver' });
+
+ // Wait for the next event loop to allow for room alias resolution
+ setTimeout(() => {
+ dispatch({ action: 'join_room' });
+ expect(RoomViewStore.isJoining()).toBe(true);
+ }, 0);
+ });
+});
diff --git a/test/test-utils.js b/test/test-utils.js
index 2c866d345c..569208b355 100644
--- a/test/test-utils.js
+++ b/test/test-utils.js
@@ -4,7 +4,8 @@ import sinon from 'sinon';
import q from 'q';
import ReactTestUtils from 'react-addons-test-utils';
-import peg from '../src/MatrixClientPeg.js';
+import peg from '../src/MatrixClientPeg';
+import dis from '../src/dispatcher';
import jssdk from 'matrix-js-sdk';
const MatrixEvent = jssdk.MatrixEvent;
@@ -290,3 +291,13 @@ export function mkStubRoom(roomId = null) {
},
};
}
+
+export function getDispatchForStore(store) {
+ // Mock the dispatcher by gut-wrenching. Stores can only __emitChange whilst a
+ // dispatcher `_isDispatching` is true.
+ return (payload) => {
+ dis._isDispatching = true;
+ dis._callbacks[store._dispatchToken](payload);
+ dis._isDispatching = false;
+ };
+}
From 5f36f797da0b1a195c31b0cf2f45f2d4b8dd5ae6 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Wed, 24 May 2017 17:55:36 +0100
Subject: [PATCH 0171/1016] Implement default welcome page and allow custom URL
/w config
This changes the default behaviour of displaying the room directory to instead displaying the default homepage. If specified, the config "welcomePageUrl" can be used to override the default '/home.html'.
---
src/components/structures/LoggedInView.js | 4 ++--
src/components/structures/MatrixChat.js | 12 ++----------
2 files changed, 4 insertions(+), 12 deletions(-)
diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js
index e559a21e1a..994e6504cf 100644
--- a/src/components/structures/LoggedInView.js
+++ b/src/components/structures/LoggedInView.js
@@ -240,7 +240,8 @@ export default React.createClass({
collapsedRhs={this.props.collapse_rhs}
teamServerUrl={this.props.config.teamServerConfig.teamServerURL}
teamToken={this.props.teamToken}
- />
+ homePageUrl={this.props.config.welcomePageUrl}
+ />;
if (!this.props.collapse_rhs) right_panel =
break;
@@ -276,7 +277,6 @@ export default React.createClass({
selectedRoom={this.props.currentRoomId}
collapsed={this.props.collapse_lhs || false}
opacity={this.props.sideOpacity}
- teamToken={this.props.teamToken}
/>
{page_element}
diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index 59ce1b622d..1882831bdc 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -457,10 +457,6 @@ module.exports = React.createClass({
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;
@@ -812,7 +808,7 @@ module.exports = React.createClass({
return;
}
// The user has just logged in after registering
- dis.dispatch({action: 'view_room_directory'});
+ dis.dispatch({action: 'view_home_page'});
} else {
this._showScreenAfterLogin();
}
@@ -834,12 +830,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'});
}
},
From dcf2fb68aecd5d1eb64a649eac3bba1912dd96d3 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Wed, 24 May 2017 18:02:17 +0100
Subject: [PATCH 0172/1016] Remove console log
---
src/stores/LifecycleStore.js | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/stores/LifecycleStore.js b/src/stores/LifecycleStore.js
index 82a3b1b584..43e2de9d52 100644
--- a/src/stores/LifecycleStore.js
+++ b/src/stores/LifecycleStore.js
@@ -54,7 +54,6 @@ class LifecycleStore extends Store {
if (payload.state !== 'PREPARED') {
break;
}
- console.warn(this._state);
if (!this._state.deferred_action) break;
const deferredAction = Object.assign({}, this._state.deferred_action);
this._setState({
From fffe425730688b1d1adea59c813bdc6b6b695273 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Wed, 24 May 2017 18:04:04 +0100
Subject: [PATCH 0173/1016] Add non-null RoomView key
---
src/components/structures/LoggedInView.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js
index df24fbb33b..5022b983f0 100644
--- a/src/components/structures/LoggedInView.js
+++ b/src/components/structures/LoggedInView.js
@@ -196,7 +196,7 @@ export default React.createClass({
oobData={this.props.roomOobData}
highlightedEventId={this.props.highlightedEventId}
eventPixelOffset={this.props.initialEventPixelOffset}
- key={this.props.currentRoomId}
+ key={this.props.currentRoomId || 'roomview'}
opacity={this.props.middleOpacity}
collapsedRhs={this.props.collapse_rhs}
ConferenceHandler={this.props.ConferenceHandler}
From 09d0ab7df5f9e6602ca2e8e705e4d59a1304f94e Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Thu, 25 May 2017 01:01:40 +0100
Subject: [PATCH 0174/1016] attempt to make media selector work everywhere (TM)
loadDevices not only in electron
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
---
src/CallMediaHandler.js | 2 +
src/components/structures/LoggedInView.js | 4 +-
src/components/structures/UserSettings.js | 52 ++++++++++++++++-------
3 files changed, 40 insertions(+), 18 deletions(-)
diff --git a/src/CallMediaHandler.js b/src/CallMediaHandler.js
index 9133a6548d..4f82e003b9 100644
--- a/src/CallMediaHandler.js
+++ b/src/CallMediaHandler.js
@@ -26,6 +26,8 @@ export default {
const audioIn = {};
const videoIn = {};
+ if (devices.some((device) => !device.label)) return false;
+
devices.forEach((device) => {
switch (device.kind) {
case 'audioinput': audioIn[device.deviceId] = device.label; break;
diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js
index a84661bcd2..1a7b7e06e4 100644
--- a/src/components/structures/LoggedInView.js
+++ b/src/components/structures/LoggedInView.js
@@ -72,9 +72,7 @@ export default React.createClass({
// RoomView.getScrollState()
this._scrollStateMap = {};
- // Only run these in electron, at least until a better mechanism for perms exists
- // https://w3c.github.io/permissions/#dom-permissionname-device-info
- if (window && window.process && window.process.type) CallMediaHandler.loadDevices();
+ CallMediaHandler.loadDevices();
document.addEventListener('keydown', this._onKeyDown);
},
diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js
index de88566300..58c6bb7c20 100644
--- a/src/components/structures/UserSettings.js
+++ b/src/components/structures/UserSettings.js
@@ -178,17 +178,7 @@ module.exports = React.createClass({
});
}
- q().then(() => {
- return CallMediaHandler.getDevices();
- }).then((mediaDevices) => {
- console.log("got mediaDevices", mediaDevices, this._unmounted);
- if (this._unmounted) return;
- this.setState({
- mediaDevices,
- activeAudioInput: this._localSettings['webrtc_audioinput'] || 'default',
- activeVideoInput: this._localSettings['webrtc_videoinput'] || 'default',
- });
- });
+ this._refreshMediaDevices();
// Bulk rejecting invites:
// /sync won't have had time to return when UserSettings re-renders from state changes, so getRooms()
@@ -210,8 +200,6 @@ module.exports = React.createClass({
this._syncedSettings = syncedSettings;
this._localSettings = UserSettingsStore.getLocalSettings();
- this._setAudioInput = this._setAudioInput.bind(this);
- this._setVideoInput = this._setVideoInput.bind(this);
},
componentDidMount: function() {
@@ -233,6 +221,20 @@ module.exports = React.createClass({
}
},
+ _refreshMediaDevices: function() {
+ q().then(() => {
+ return CallMediaHandler.getDevices();
+ }).then((mediaDevices) => {
+ // console.log("got mediaDevices", mediaDevices, this._unmounted);
+ if (this._unmounted) return;
+ this.setState({
+ mediaDevices,
+ activeAudioInput: this._localSettings['webrtc_audioinput'] || 'default',
+ activeVideoInput: this._localSettings['webrtc_videoinput'] || 'default',
+ });
+ });
+ },
+
_refreshFromServer: function() {
const self = this;
q.all([
@@ -818,9 +820,29 @@ module.exports = React.createClass({
CallMediaHandler.setVideoInput(deviceId);
},
+ _requestMediaPermissions: function() {
+ console.log("Request media perms");
+ const getUserMedia = (
+ window.navigator.getUserMedia || window.navigator.webkitGetUserMedia || window.navigator.mozGetUserMedia
+ );
+ if (getUserMedia) {
+ return getUserMedia.apply(window.navigator, [
+ { video: true, audio: true },
+ this._refreshMediaDevices,
+ function() {},
+ ]);
+ }
+ },
+
_renderWebRtcSettings: function() {
- if (!(window && window.process && window.process.type)
- || !this.state.mediaDevices) return;
+ if (this.state.mediaDevices === false) {
+ return
+
WebRTC
+
+
Missing Media Permissions, click to request.
+
+
;
+ } else if (!this.state.mediaDevices) return;
const Dropdown = sdk.getComponent('elements.Dropdown');
From 8158ec6d54c062c6a3c49b92ee5c1fe1e64e969b Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Thu, 25 May 2017 01:25:17 +0100
Subject: [PATCH 0175/1016] touchups
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
---
src/components/structures/UserSettings.js | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)
diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js
index 58c6bb7c20..0d182b27ab 100644
--- a/src/components/structures/UserSettings.js
+++ b/src/components/structures/UserSettings.js
@@ -820,8 +820,7 @@ module.exports = React.createClass({
CallMediaHandler.setVideoInput(deviceId);
},
- _requestMediaPermissions: function() {
- console.log("Request media perms");
+ _requestMediaPermissions: function(event) {
const getUserMedia = (
window.navigator.getUserMedia || window.navigator.webkitGetUserMedia || window.navigator.mozGetUserMedia
);
@@ -829,7 +828,13 @@ module.exports = React.createClass({
return getUserMedia.apply(window.navigator, [
{ video: true, audio: true },
this._refreshMediaDevices,
- function() {},
+ function() {
+ const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog');
+ Modal.createDialog(ErrorDialog, {
+ title: "No media permissions",
+ description: "You may need to manually permit Riot to access your microphone/webcam",
+ });
+ },
]);
}
},
@@ -839,15 +844,17 @@ module.exports = React.createClass({
return
WebRTC
-
Missing Media Permissions, click to request.
+
+ Missing Media Permissions, click here to request.
+
;
} else if (!this.state.mediaDevices) return;
const Dropdown = sdk.getComponent('elements.Dropdown');
- let microphoneDropdown = No Microphones detected ;
- let webcamDropdown = No Webcams detected ;
+ let microphoneDropdown = No Microphones detected
;
+ let webcamDropdown = No Webcams detected
;
const audioInputs = this.state.mediaDevices.audioinput;
if ('default' in audioInputs) {
From 8fc44a9b6661a6a5dab303d6895e903a10d2aed1 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Thu, 25 May 2017 09:30:57 +0100
Subject: [PATCH 0176/1016] Add comment to explain sync_state dispatch
---
src/components/structures/MatrixChat.js | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index b1814bc322..dca73a4601 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -895,6 +895,11 @@ 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") {
From c894c83fbe2eaced0c2313e405008c62e9484914 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Thu, 25 May 2017 11:02:48 +0100
Subject: [PATCH 0177/1016] Remove GuestWarningBar
---
src/components/structures/LoggedInView.js | 3 ---
1 file changed, 3 deletions(-)
diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js
index d2ae57cda4..4687afbee1 100644
--- a/src/components/structures/LoggedInView.js
+++ b/src/components/structures/LoggedInView.js
@@ -178,7 +178,6 @@ export default React.createClass({
const RoomDirectory = sdk.getComponent('structures.RoomDirectory');
const HomePage = sdk.getComponent('structures.HomePage');
const MatrixToolbar = sdk.getComponent('globals.MatrixToolbar');
- const GuestWarningBar = sdk.getComponent('globals.GuestWarningBar');
const NewVersionBar = sdk.getComponent('globals.NewVersionBar');
const PasswordNagBar = sdk.getComponent('globals.PasswordNagBar');
@@ -253,8 +252,6 @@ export default React.createClass({
topBar = ;
- } else if (this.props.matrixClient.isGuest()) {
- topBar = ;
} else if (this.state.userHasGeneratedPassword) {
topBar = ;
} else if (Notifier.supportsDesktopNotifications() && !Notifier.isEnabled() && !Notifier.isToolbarHidden()) {
From ec146c7f450c8d1a589a36f94ac5221137067379 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Thu, 25 May 2017 11:24:17 +0100
Subject: [PATCH 0178/1016] Switch to stock counterpart
We no longer need the custom counterpart. The things that were
changed in it and reasosn we no lomger need them are:
1. set separator - this can be done with standard counterpart
2. ES6 port: this was done to allow importing as _t, but is
unnecessary with the _t wrapper function in react-sdk
(although also seems to work fine with
`import _t from 'counterpart';`)
3. Fallback to key name: unnecessary with
`setMissingEntryGenerator` in 0.18, but we've changed to just
using a fallback locale anyway since No code missing en
translations should ever make it to the devbelop branch.
4. Logging on missing translations. Unnecessary with
`onTranslationNotFound`, but now not used anyway because we
let missing translations show up in the UI as
'missing translation'
Bulk changes to imports coming in separate commit for easy reading.
---
package.json | 2 +-
src/languageHandler.js | 21 ++++++++++++++++++---
2 files changed, 19 insertions(+), 4 deletions(-)
diff --git a/package.json b/package.json
index 11106325f1..b94c8d66eb 100644
--- a/package.json
+++ b/package.json
@@ -50,7 +50,7 @@
"browser-request": "^0.3.3",
"classnames": "^2.1.2",
"commonmark": "^0.27.0",
- "counterpart-riot": "^0.18.1",
+ "counterpart": "^0.18.0",
"draft-js": "^0.8.1",
"draft-js-export-html": "^0.5.0",
"draft-js-export-markdown": "^0.2.0",
diff --git a/src/languageHandler.js b/src/languageHandler.js
index 92e3ccefb6..25c380a53c 100644
--- a/src/languageHandler.js
+++ b/src/languageHandler.js
@@ -1,5 +1,6 @@
/*
Copyright 2017 MTRNord and Cooperative EITA
+Copyright 2017 Vector Creations Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -15,13 +16,27 @@ limitations under the License.
*/
import request from 'browser-request';
-// Workaround for broken export
-import * as counterpart from 'counterpart-riot';
-import UserSettingsStore from './UserSettingsStore';
+import counterpart from 'counterpart';
import q from 'q';
+import UserSettingsStore from './UserSettingsStore';
+
const i18nFolder = 'i18n/';
+// We use english strings as keys, some of which contain full stops
+counterpart.setSeparator('|');
+// Fall back to English
+counterpart.setFallbackLocale('en');
+
+// The translation function. This is just a simple wrapper to counterpart,
+// but exists mostly because we must use the same counterpart instance
+// between modules (ie. here (react-sdk) and the app (riot-web), and if we
+// just import counterpart and use it directly, we end up using a different
+// instance.
+export function _t(...args) {
+ return counterpart.translate(...args);
+}
+
export function setLanguage(languages, extCounterpart=null) {
if (!languages || !Array.isArray(languages)) {
const languages = this.getNormalizedLanguageKeys(this.getLanguageFromBrowser());
From 5531f274354df79980152d05521a23114cb5d25b Mon Sep 17 00:00:00 2001
From: David Baker
Date: Wed, 26 Apr 2017 18:59:16 +0100
Subject: [PATCH 0179/1016] Make the left panel more friendly to new users
https://github.com/vector-im/riot-web/issues/3609
Conflicts:
src/components/views/rooms/RoomList.js
cherry-picking commit f5f35e3.
---
src/components/views/rooms/RoomList.js | 163 +++++++++++++++++++------
1 file changed, 124 insertions(+), 39 deletions(-)
diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js
index 760b0543c6..9dfa99fb44 100644
--- a/src/components/views/rooms/RoomList.js
+++ b/src/components/views/rooms/RoomList.js
@@ -29,7 +29,14 @@ var Rooms = require('../../../Rooms');
import DMRoomMap from '../../../utils/DMRoomMap';
var Receipt = require('../../../utils/Receipt');
-var HIDE_CONFERENCE_CHANS = true;
+const HIDE_CONFERENCE_CHANS = true;
+
+const VERBS = {
+ 'm.favourite': 'favourite',
+ 'im.vector.fake.direct': 'tag direct chat',
+ 'im.vector.fake.recent': 'restore',
+ 'm.lowpriority': 'demote',
+};
module.exports = React.createClass({
displayName: 'RoomList',
@@ -44,6 +51,7 @@ module.exports = React.createClass({
getInitialState: function() {
return {
isLoadingLeftRooms: false,
+ totalRoomCount: null,
lists: {},
incomingCall: null,
};
@@ -63,8 +71,17 @@ module.exports = React.createClass({
cli.on("RoomMember.name", this.onRoomMemberName);
cli.on("accountData", this.onAccountData);
- var s = this.getRoomLists();
- this.setState(s);
+ // lookup for which lists a given roomId is currently in.
+ this.listsForRoomId = {};
+
+ this.refreshRoomList();
+
+ // order of the sublists
+ //this.listOrder = [];
+
+ // loop count to stop a stack overflow if the user keeps waggling the
+ // mouse for >30s in a row, or if running under mocha
+ this._delayedRefreshRoomListLoopCount = 0
},
componentDidMount: function() {
@@ -202,31 +219,33 @@ module.exports = React.createClass({
}, 500),
refreshRoomList: function() {
- // console.log("DEBUG: Refresh room list delta=%s ms",
- // (!this._lastRefreshRoomListTs ? "-" : (Date.now() - this._lastRefreshRoomListTs))
- // );
-
- // TODO: rather than bluntly regenerating and re-sorting everything
- // every time we see any kind of room change from the JS SDK
- // we could do incremental updates on our copy of the state
- // based on the room which has actually changed. This would stop
- // us re-rendering all the sublists every time anything changes anywhere
- // in the state of the client.
- this.setState(this.getRoomLists());
+ // TODO: ideally we'd calculate this once at start, and then maintain
+ // any changes to it incrementally, updating the appropriate sublists
+ // as needed.
+ // Alternatively we'd do something magical with Immutable.js or similar.
+ const lists = this.getRoomLists();
+ let totalRooms = 0;
+ for (const l of Object.values(lists)) {
+ totalRooms += l.length;
+ }
+ this.setState({
+ lists: this.getRoomLists(),
+ totalRoomCount: totalRooms,
+ });
// this._lastRefreshRoomListTs = Date.now();
},
getRoomLists: function() {
var self = this;
- var s = { lists: {} };
+ const lists = {};
- s.lists["im.vector.fake.invite"] = [];
- s.lists["m.favourite"] = [];
- s.lists["im.vector.fake.recent"] = [];
- s.lists["im.vector.fake.direct"] = [];
- s.lists["m.lowpriority"] = [];
- s.lists["im.vector.fake.archived"] = [];
+ lists["im.vector.fake.invite"] = [];
+ lists["m.favourite"] = [];
+ lists["im.vector.fake.recent"] = [];
+ lists["im.vector.fake.direct"] = [];
+ lists["m.lowpriority"] = [];
+ lists["im.vector.fake.archived"] = [];
const dmRoomMap = new DMRoomMap(MatrixClientPeg.get());
@@ -240,7 +259,8 @@ module.exports = React.createClass({
// ", prevMembership = " + me.events.member.getPrevContent().membership);
if (me.membership == "invite") {
- s.lists["im.vector.fake.invite"].push(room);
+ self.listsForRoomId[room.roomId].push("im.vector.fake.invite");
+ lists["im.vector.fake.invite"].push(room);
}
else if (HIDE_CONFERENCE_CHANS && Rooms.isConfCallRoom(room, me, self.props.ConferenceHandler)) {
// skip past this room & don't put it in any lists
@@ -254,48 +274,55 @@ module.exports = React.createClass({
if (tagNames.length) {
for (var i = 0; i < tagNames.length; i++) {
var tagName = tagNames[i];
- s.lists[tagName] = s.lists[tagName] || [];
- s.lists[tagNames[i]].push(room);
+ lists[tagName] = lists[tagName] || [];
+ lists[tagName].push(room);
+ self.listsForRoomId[room.roomId].push(tagName);
+ otherTagNames[tagName] = 1;
}
}
else if (dmRoomMap.getUserIdForRoomId(room.roomId)) {
// "Direct Message" rooms (that we're still in and that aren't otherwise tagged)
- s.lists["im.vector.fake.direct"].push(room);
+ self.listsForRoomId[room.roomId].push("im.vector.fake.direct");
+ lists["im.vector.fake.direct"].push(room);
}
else {
- s.lists["im.vector.fake.recent"].push(room);
+ self.listsForRoomId[room.roomId].push("im.vector.fake.recent");
+ lists["im.vector.fake.recent"].push(room);
}
}
else if (me.membership === "leave") {
- s.lists["im.vector.fake.archived"].push(room);
+ self.listsForRoomId[room.roomId].push("im.vector.fake.archived");
+ lists["im.vector.fake.archived"].push(room);
}
else {
console.error("unrecognised membership: " + me.membership + " - this should never happen");
}
});
- if (s.lists["im.vector.fake.direct"].length == 0 &&
+ if (lists["im.vector.fake.direct"].length == 0 &&
MatrixClientPeg.get().getAccountData('m.direct') === undefined &&
!MatrixClientPeg.get().isGuest())
{
// scan through the 'recents' list for any rooms which look like DM rooms
// and make them DM rooms
- const oldRecents = s.lists["im.vector.fake.recent"];
- s.lists["im.vector.fake.recent"] = [];
+ const oldRecents = lists["im.vector.fake.recent"];
+ lists["im.vector.fake.recent"] = [];
for (const room of oldRecents) {
const me = room.getMember(MatrixClientPeg.get().credentials.userId);
if (me && Rooms.looksLikeDirectMessageRoom(room, me)) {
- s.lists["im.vector.fake.direct"].push(room);
+ self.listsForRoomId[room.roomId].push("im.vector.fake.direct");
+ lists["im.vector.fake.direct"].push(room);
} else {
- s.lists["im.vector.fake.recent"].push(room);
+ self.listsForRoomId[room.roomId].push("im.vector.fake.recent");
+ lists["im.vector.fake.recent"].push(room);
}
}
// save these new guessed DM rooms into the account data
const newMDirectEvent = {};
- for (const room of s.lists["im.vector.fake.direct"]) {
+ for (const room of lists["im.vector.fake.direct"]) {
const me = room.getMember(MatrixClientPeg.get().credentials.userId);
const otherPerson = Rooms.getOnlyOtherMember(room, me);
if (!otherPerson) continue;
@@ -313,7 +340,22 @@ module.exports = React.createClass({
// we actually apply the sorting to this when receiving the prop in RoomSubLists.
- return s;
+ // we'll need this when we get to iterating through lists programatically - e.g. ctrl-shift-up/down
+/*
+ this.listOrder = [
+ "im.vector.fake.invite",
+ "m.favourite",
+ "im.vector.fake.recent",
+ "im.vector.fake.direct",
+ Object.keys(otherTagNames).filter(tagName=>{
+ return (!tagName.match(/^m\.(favourite|lowpriority)$/));
+ }).sort(),
+ "m.lowpriority",
+ "im.vector.fake.archived"
+ ];
+*/
+
+ return lists;
},
_getScrollNode: function() {
@@ -467,6 +509,49 @@ module.exports = React.createClass({
this.refs.gemscroll.forceUpdate();
},
+ _getEmptyContent: function(section) {
+ let greyed = false;
+ if (this.state.totalRoomCount === 0) {
+ const TintableSvg = sdk.getComponent('elements.TintableSvg');
+ switch (section) {
+ case 'm.favourite':
+ case 'm.lowpriority':
+ greyed = true;
+ break;
+ case 'im.vector.fake.direct':
+ return
+
+
+
+ Use the button below to chat with someone!
+
;
+ case 'im.vector.fake.recent':
+ return
+
+
+
+ Use the button below to browse the room directory
+
+
+
+
+ or this button to start a new one!
+
;
+ }
+ }
+ const RoomDropTarget = sdk.getComponent('rooms.RoomDropTarget');
+
+ const labelText = 'Drop here to ' + (VERBS[section] || 'tag ' + section);
+
+ let label;
+ if (greyed) {
+ label = {labelText} ;
+ } else {
+ label = labelText;
+ }
+ return ;
+ },
+
render: function() {
var RoomSubList = sdk.getComponent('structures.RoomSubList');
var self = this;
@@ -489,7 +574,7 @@ module.exports = React.createClass({
Date: Thu, 25 May 2017 11:39:08 +0100
Subject: [PATCH 0180/1016] Bulk change counterpart imports
to use languageHandler wrapper func
---
src/AddThreepid.js | 2 +-
src/CallHandler.js | 2 +-
src/ContentMessages.js | 2 +-
src/DateUtils.js | 2 +-
src/Lifecycle.js | 2 +-
src/Notifier.js | 2 +-
src/PasswordReset.js | 2 +-
src/Roles.js | 2 +-
src/ScalarMessaging.js | 2 +-
src/SlashCommands.js | 2 +-
src/TextForEvent.js | 2 +-
src/WhoIsTyping.js | 2 +-
src/async-components/views/dialogs/EncryptedEventDialog.js | 2 +-
src/autocomplete/CommandProvider.js | 2 +-
src/autocomplete/EmojiProvider.js | 2 +-
src/autocomplete/RoomProvider.js | 2 +-
src/autocomplete/UserProvider.js | 2 +-
src/components/structures/CreateRoom.js | 2 +-
src/components/structures/MatrixChat.js | 2 +-
src/components/structures/NotificationPanel.js | 2 +-
src/components/structures/RoomStatusBar.js | 2 +-
src/components/structures/RoomView.js | 2 +-
src/components/structures/TimelinePanel.js | 2 +-
src/components/structures/UserSettings.js | 2 +-
src/components/structures/login/ForgotPassword.js | 2 +-
src/components/structures/login/Login.js | 2 +-
src/components/structures/login/PostRegistration.js | 2 +-
src/components/structures/login/Registration.js | 2 +-
src/components/views/dialogs/ChatInviteDialog.js | 2 +-
src/components/views/dialogs/ConfirmUserActionDialog.js | 2 +-
src/components/views/dialogs/TextInputDialog.js | 2 +-
src/components/views/elements/LanguageDropdown.js | 2 +-
src/components/views/elements/MemberEventListSummary.js | 2 +-
src/components/views/login/PasswordLogin.js | 2 +-
src/components/views/messages/MFileBody.js | 2 +-
src/components/views/room_settings/AliasSettings.js | 2 +-
src/components/views/rooms/EventTile.js | 2 +-
src/components/views/rooms/MemberInfo.js | 2 +-
src/components/views/rooms/MemberList.js | 2 +-
src/components/views/rooms/MessageComposer.js | 2 +-
src/components/views/rooms/MessageComposerInput.js | 2 +-
src/components/views/rooms/MessageComposerInputOld.js | 2 +-
src/components/views/rooms/RoomHeader.js | 2 +-
src/components/views/rooms/RoomList.js | 2 +-
src/components/views/rooms/RoomPreviewBar.js | 2 +-
src/components/views/rooms/RoomSettings.js | 2 +-
src/components/views/rooms/SearchableEntityList.js | 2 +-
src/components/views/rooms/TopUnreadMessagesBar.js | 2 +-
src/components/views/settings/AddPhoneNumber.js | 2 +-
src/components/views/settings/ChangePassword.js | 2 +-
src/components/views/settings/DevicesPanelEntry.js | 2 +-
src/createRoom.js | 2 +-
52 files changed, 52 insertions(+), 52 deletions(-)
diff --git a/src/AddThreepid.js b/src/AddThreepid.js
index 0503f4ff9d..8be7a19b13 100644
--- a/src/AddThreepid.js
+++ b/src/AddThreepid.js
@@ -16,7 +16,7 @@ limitations under the License.
*/
var MatrixClientPeg = require("./MatrixClientPeg");
-import _t from 'counterpart-riot';
+import { _t } from './languageHandler';
/**
* Allows a user to add a third party identifier to their Home Server and,
diff --git a/src/CallHandler.js b/src/CallHandler.js
index f5283f6d49..a557c735a2 100644
--- a/src/CallHandler.js
+++ b/src/CallHandler.js
@@ -55,7 +55,7 @@ var MatrixClientPeg = require('./MatrixClientPeg');
var PlatformPeg = require("./PlatformPeg");
var Modal = require('./Modal');
var sdk = require('./index');
-import _t from 'counterpart-riot';
+import { _t } from './languageHandler';
var Matrix = require("matrix-js-sdk");
var dis = require("./dispatcher");
diff --git a/src/ContentMessages.js b/src/ContentMessages.js
index 6e31a60556..d4f8cae276 100644
--- a/src/ContentMessages.js
+++ b/src/ContentMessages.js
@@ -21,7 +21,7 @@ var extend = require('./extend');
var dis = require('./dispatcher');
var MatrixClientPeg = require('./MatrixClientPeg');
var sdk = require('./index');
-import _t from 'counterpart-riot';
+import { _t } from './languageHandler';
var Modal = require('./Modal');
var encrypt = require("browser-encrypt-attachment");
diff --git a/src/DateUtils.js b/src/DateUtils.js
index 87b6fa690f..be5f432d06 100644
--- a/src/DateUtils.js
+++ b/src/DateUtils.js
@@ -15,7 +15,7 @@ limitations under the License.
*/
'use strict';
-import _t from 'counterpart-riot';
+import { _t } from './languageHandler';
function getDaysArray() {
var days = [];
diff --git a/src/Lifecycle.js b/src/Lifecycle.js
index 56af920609..0e3e52fe40 100644
--- a/src/Lifecycle.js
+++ b/src/Lifecycle.js
@@ -27,7 +27,7 @@ import DMRoomMap from './utils/DMRoomMap';
import RtsClient from './RtsClient';
import Modal from './Modal';
import sdk from './index';
-import _t from 'counterpart-riot';
+import { _t } from './languageHandler';
/**
* Called at startup, to attempt to build a logged-in Matrix session. It tries
diff --git a/src/Notifier.js b/src/Notifier.js
index 9d3e114b14..8ab28e667e 100644
--- a/src/Notifier.js
+++ b/src/Notifier.js
@@ -21,7 +21,7 @@ import TextForEvent from './TextForEvent';
import Avatar from './Avatar';
import dis from './dispatcher';
import sdk from './index';
-import _t from 'counterpart-riot';
+import { _t } from './languageHandler';
import Modal from './Modal';
/*
diff --git a/src/PasswordReset.js b/src/PasswordReset.js
index d5992e3e0a..668b8f67da 100644
--- a/src/PasswordReset.js
+++ b/src/PasswordReset.js
@@ -15,7 +15,7 @@ limitations under the License.
*/
var Matrix = require("matrix-js-sdk");
-import _t from 'counterpart-riot';
+import { _t } from './languageHandler';
/**
* Allows a user to reset their password on a homeserver.
diff --git a/src/Roles.js b/src/Roles.js
index 0c7463c48a..5af8be76df 100644
--- a/src/Roles.js
+++ b/src/Roles.js
@@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
-import _t from 'counterpart-riot';
+import { _t } from './languageHandler';
export function levelRoleMap() {
const LEVEL_ROLE_MAP = {};
diff --git a/src/ScalarMessaging.js b/src/ScalarMessaging.js
index abe39056e7..b120125e0e 100644
--- a/src/ScalarMessaging.js
+++ b/src/ScalarMessaging.js
@@ -125,7 +125,7 @@ const SdkConfig = require('./SdkConfig');
const MatrixClientPeg = require("./MatrixClientPeg");
const MatrixEvent = require("matrix-js-sdk").MatrixEvent;
const dis = require("./dispatcher");
-import _t from 'counterpart-riot';
+import { _t } from './languageHandler';
function sendResponse(event, res) {
const data = JSON.parse(JSON.stringify(event.data));
diff --git a/src/SlashCommands.js b/src/SlashCommands.js
index e2c69ad46a..645ca6e8fe 100644
--- a/src/SlashCommands.js
+++ b/src/SlashCommands.js
@@ -18,7 +18,7 @@ import MatrixClientPeg from "./MatrixClientPeg";
import dis from "./dispatcher";
import Tinter from "./Tinter";
import sdk from './index';
-import _t from 'counterpart-riot';
+import { _t } from './languageHandler';
import Modal from './Modal';
diff --git a/src/TextForEvent.js b/src/TextForEvent.js
index 36a6eff45f..24d79ac3ce 100644
--- a/src/TextForEvent.js
+++ b/src/TextForEvent.js
@@ -16,7 +16,7 @@ limitations under the License.
var MatrixClientPeg = require("./MatrixClientPeg");
var CallHandler = require("./CallHandler");
-import _t from 'counterpart-riot';
+import { _t } from './languageHandler';
import * as Roles from './Roles';
function textForMemberEvent(ev) {
diff --git a/src/WhoIsTyping.js b/src/WhoIsTyping.js
index b2eef42f5a..f3d89f0ff2 100644
--- a/src/WhoIsTyping.js
+++ b/src/WhoIsTyping.js
@@ -15,7 +15,7 @@ limitations under the License.
*/
var MatrixClientPeg = require("./MatrixClientPeg");
-import _t from 'counterpart-riot';
+import { _t } from './languageHandler';
module.exports = {
usersTypingApartFromMe: function(room) {
diff --git a/src/async-components/views/dialogs/EncryptedEventDialog.js b/src/async-components/views/dialogs/EncryptedEventDialog.js
index 279f6bbda1..3a6ca4e6b7 100644
--- a/src/async-components/views/dialogs/EncryptedEventDialog.js
+++ b/src/async-components/views/dialogs/EncryptedEventDialog.js
@@ -15,7 +15,7 @@ limitations under the License.
*/
var React = require("react");
-import _t from 'counterpart-riot';
+import { _t } from '../../../languageHandler';
var sdk = require('../../../index');
var MatrixClientPeg = require("../../../MatrixClientPeg");
diff --git a/src/autocomplete/CommandProvider.js b/src/autocomplete/CommandProvider.js
index d72c8d1c07..34d0110131 100644
--- a/src/autocomplete/CommandProvider.js
+++ b/src/autocomplete/CommandProvider.js
@@ -1,5 +1,5 @@
import React from 'react';
-import _t from 'counterpart-riot';
+import { _t } from '../languageHandler';
import AutocompleteProvider from './AutocompleteProvider';
import Fuse from 'fuse.js';
import {TextualCompletion} from './Components';
diff --git a/src/autocomplete/EmojiProvider.js b/src/autocomplete/EmojiProvider.js
index d0675e9ffc..7b9e8fb669 100644
--- a/src/autocomplete/EmojiProvider.js
+++ b/src/autocomplete/EmojiProvider.js
@@ -1,5 +1,5 @@
import React from 'react';
-import _t from 'counterpart-riot';
+import { _t } from '../languageHandler';
import AutocompleteProvider from './AutocompleteProvider';
import {emojioneList, shortnameToImage, shortnameToUnicode} from 'emojione';
import Fuse from 'fuse.js';
diff --git a/src/autocomplete/RoomProvider.js b/src/autocomplete/RoomProvider.js
index 175aaf2691..4a2dd09eb5 100644
--- a/src/autocomplete/RoomProvider.js
+++ b/src/autocomplete/RoomProvider.js
@@ -1,5 +1,5 @@
import React from 'react';
-import _t from 'counterpart-riot';
+import { _t } from '../languageHandler';
import AutocompleteProvider from './AutocompleteProvider';
import MatrixClientPeg from '../MatrixClientPeg';
import Fuse from 'fuse.js';
diff --git a/src/autocomplete/UserProvider.js b/src/autocomplete/UserProvider.js
index c2fccbc418..281b6fd40d 100644
--- a/src/autocomplete/UserProvider.js
+++ b/src/autocomplete/UserProvider.js
@@ -1,5 +1,5 @@
import React from 'react';
-import _t from 'counterpart-riot';
+import { _t } from '../languageHandler';
import AutocompleteProvider from './AutocompleteProvider';
import Q from 'q';
import Fuse from 'fuse.js';
diff --git a/src/components/structures/CreateRoom.js b/src/components/structures/CreateRoom.js
index 48ce4e274e..8b3d035dc1 100644
--- a/src/components/structures/CreateRoom.js
+++ b/src/components/structures/CreateRoom.js
@@ -18,7 +18,7 @@ limitations under the License.
import React from 'react';
import q from 'q';
-import _t from 'counterpart-riot';
+import { _t } from '../../languageHandler';
import sdk from '../../index';
import MatrixClientPeg from '../../MatrixClientPeg';
const PresetValues = {
diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index c8cb721041..a00d9c22ce 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -36,7 +36,7 @@ import PageTypes from '../../PageTypes';
import createRoom from "../../createRoom";
import * as UDEHandler from '../../UnknownDeviceErrorHandler';
-import _t from 'counterpart-riot';
+import { _t } from '../../languageHandler';
module.exports = React.createClass({
displayName: 'MatrixChat',
diff --git a/src/components/structures/NotificationPanel.js b/src/components/structures/NotificationPanel.js
index 214bb79862..21eccbdff6 100644
--- a/src/components/structures/NotificationPanel.js
+++ b/src/components/structures/NotificationPanel.js
@@ -16,7 +16,7 @@ limitations under the License.
var React = require('react');
var ReactDOM = require("react-dom");
-import _t from 'counterpart-riot';
+import { _t } from '../../languageHandler';
var Matrix = require("matrix-js-sdk");
var sdk = require('../../index');
var MatrixClientPeg = require("../../MatrixClientPeg");
diff --git a/src/components/structures/RoomStatusBar.js b/src/components/structures/RoomStatusBar.js
index 8e1fe971a4..5a9592243a 100644
--- a/src/components/structures/RoomStatusBar.js
+++ b/src/components/structures/RoomStatusBar.js
@@ -15,7 +15,7 @@ limitations under the License.
*/
import React from 'react';
-import _t from 'counterpart-riot';
+import { _t } from '../../languageHandler';
import sdk from '../../index';
import dis from '../../dispatcher';
import WhoIsTyping from '../../WhoIsTyping';
diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js
index 2b7e7acc3d..f0b6160012 100644
--- a/src/components/structures/RoomView.js
+++ b/src/components/structures/RoomView.js
@@ -25,7 +25,7 @@ var ReactDOM = require("react-dom");
var q = require("q");
var classNames = require("classnames");
var Matrix = require("matrix-js-sdk");
-import _t from 'counterpart-riot';
+import { _t } from '../../languageHandler';
var UserSettingsStore = require('../../UserSettingsStore');
var MatrixClientPeg = require("../../MatrixClientPeg");
diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js
index 8b180077e0..5719964df5 100644
--- a/src/components/structures/TimelinePanel.js
+++ b/src/components/structures/TimelinePanel.js
@@ -23,7 +23,7 @@ var Matrix = require("matrix-js-sdk");
var EventTimeline = Matrix.EventTimeline;
var sdk = require('../../index');
-import _t from 'counterpart-riot';
+import { _t } from '../../languageHandler';
var MatrixClientPeg = require("../../MatrixClientPeg");
var dis = require("../../dispatcher");
var ObjectUtils = require('../../ObjectUtils');
diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js
index e0302931f5..a39c06f907 100644
--- a/src/components/structures/UserSettings.js
+++ b/src/components/structures/UserSettings.js
@@ -29,7 +29,7 @@ const Email = require('../../email');
const AddThreepid = require('../../AddThreepid');
const SdkConfig = require('../../SdkConfig');
import AccessibleButton from '../views/elements/AccessibleButton';
-import _t from 'counterpart-riot';
+import { _t } from '../../languageHandler';
import * as languageHandler from '../../languageHandler';
import * as FormattingUtils from '../../utils/FormattingUtils';
diff --git a/src/components/structures/login/ForgotPassword.js b/src/components/structures/login/ForgotPassword.js
index 6a250eb531..5438b123c3 100644
--- a/src/components/structures/login/ForgotPassword.js
+++ b/src/components/structures/login/ForgotPassword.js
@@ -17,7 +17,7 @@ limitations under the License.
'use strict';
var React = require('react');
-import _t from 'counterpart-riot';
+import { _t } from '../../../languageHandler';
var sdk = require('../../../index');
var Modal = require("../../../Modal");
var MatrixClientPeg = require('../../../MatrixClientPeg');
diff --git a/src/components/structures/login/Login.js b/src/components/structures/login/Login.js
index 17d9833340..78f515804a 100644
--- a/src/components/structures/login/Login.js
+++ b/src/components/structures/login/Login.js
@@ -18,7 +18,7 @@ limitations under the License.
'use strict';
import React from 'react';
-import _t from 'counterpart-riot';
+import { _t } from '../../../languageHandler';
import ReactDOM from 'react-dom';
import url from 'url';
import sdk from '../../../index';
diff --git a/src/components/structures/login/PostRegistration.js b/src/components/structures/login/PostRegistration.js
index c3425cf68b..86ff519783 100644
--- a/src/components/structures/login/PostRegistration.js
+++ b/src/components/structures/login/PostRegistration.js
@@ -19,7 +19,7 @@ limitations under the License.
import React from 'react';
import sdk from '../../../index';
import MatrixClientPeg from '../../../MatrixClientPeg';
-import _t from 'counterpart-riot';
+import { _t } from '../../../languageHandler';
module.exports = React.createClass({
displayName: 'PostRegistration',
diff --git a/src/components/structures/login/Registration.js b/src/components/structures/login/Registration.js
index b78f0ec80a..6dfd700d13 100644
--- a/src/components/structures/login/Registration.js
+++ b/src/components/structures/login/Registration.js
@@ -27,7 +27,7 @@ import MatrixClientPeg from '../../../MatrixClientPeg';
import RegistrationForm from '../../views/login/RegistrationForm';
import CaptchaForm from '../../views/login/CaptchaForm';
import RtsClient from '../../../RtsClient';
-import _t from 'counterpart-riot';
+import { _t } from '../../../languageHandler';
const MIN_PASSWORD_LENGTH = 6;
diff --git a/src/components/views/dialogs/ChatInviteDialog.js b/src/components/views/dialogs/ChatInviteDialog.js
index 032a705a0e..81d032683e 100644
--- a/src/components/views/dialogs/ChatInviteDialog.js
+++ b/src/components/views/dialogs/ChatInviteDialog.js
@@ -16,7 +16,7 @@ limitations under the License.
import React from 'react';
import classNames from 'classnames';
-import _t from 'counterpart-riot';
+import { _t } from '../../../languageHandler';
import sdk from '../../../index';
import { getAddressType, inviteMultipleToRoom } from '../../../Invite';
import createRoom from '../../../createRoom';
diff --git a/src/components/views/dialogs/ConfirmUserActionDialog.js b/src/components/views/dialogs/ConfirmUserActionDialog.js
index aded0dabb9..b10df3ccef 100644
--- a/src/components/views/dialogs/ConfirmUserActionDialog.js
+++ b/src/components/views/dialogs/ConfirmUserActionDialog.js
@@ -16,7 +16,7 @@ limitations under the License.
import React from 'react';
import sdk from '../../../index';
-import _t from 'counterpart-riot';
+import { _t } from '../../../languageHandler';
import classnames from 'classnames';
/*
diff --git a/src/components/views/dialogs/TextInputDialog.js b/src/components/views/dialogs/TextInputDialog.js
index 080e6ac1b6..673be42030 100644
--- a/src/components/views/dialogs/TextInputDialog.js
+++ b/src/components/views/dialogs/TextInputDialog.js
@@ -16,7 +16,7 @@ limitations under the License.
import React from 'react';
import sdk from '../../../index';
-import _t from 'counterpart-riot';
+import { _t } from '../../../languageHandler';
export default React.createClass({
displayName: 'TextInputDialog',
diff --git a/src/components/views/elements/LanguageDropdown.js b/src/components/views/elements/LanguageDropdown.js
index 764657830c..39458b506c 100644
--- a/src/components/views/elements/LanguageDropdown.js
+++ b/src/components/views/elements/LanguageDropdown.js
@@ -18,7 +18,7 @@ import React from 'react';
import sdk from '../../../index';
import UserSettingsStore from '../../../UserSettingsStore';
-import _t from 'counterpart-riot';
+import { _t } from '../../../languageHandler';
import * as languageHandler from '../../../languageHandler';
function languageMatchesSearchQuery(query, language) {
diff --git a/src/components/views/elements/MemberEventListSummary.js b/src/components/views/elements/MemberEventListSummary.js
index 747e5019a8..2999af42d6 100644
--- a/src/components/views/elements/MemberEventListSummary.js
+++ b/src/components/views/elements/MemberEventListSummary.js
@@ -15,7 +15,7 @@ limitations under the License.
*/
import React from 'react';
const MemberAvatar = require('../avatars/MemberAvatar.js');
-import _t from 'counterpart-riot';
+import { _t } from '../../../languageHandler';
module.exports = React.createClass({
displayName: 'MemberEventListSummary',
diff --git a/src/components/views/login/PasswordLogin.js b/src/components/views/login/PasswordLogin.js
index 6530df83e4..7360b343e5 100644
--- a/src/components/views/login/PasswordLogin.js
+++ b/src/components/views/login/PasswordLogin.js
@@ -19,7 +19,7 @@ import React from 'react';
import ReactDOM from 'react-dom';
import classNames from 'classnames';
import sdk from '../../../index';
-import _t from 'counterpart-riot';
+import { _t } from '../../../languageHandler';
import {field_input_incorrect} from '../../../UiEffects';
diff --git a/src/components/views/messages/MFileBody.js b/src/components/views/messages/MFileBody.js
index d7609eae15..83b5793c9a 100644
--- a/src/components/views/messages/MFileBody.js
+++ b/src/components/views/messages/MFileBody.js
@@ -20,7 +20,7 @@ import React from 'react';
import filesize from 'filesize';
import MatrixClientPeg from '../../../MatrixClientPeg';
import sdk from '../../../index';
-import _t from 'counterpart-riot';
+import { _t } from '../../../languageHandler';
import {decryptFile} from '../../../utils/DecryptFile';
import Tinter from '../../../Tinter';
import request from 'browser-request';
diff --git a/src/components/views/room_settings/AliasSettings.js b/src/components/views/room_settings/AliasSettings.js
index a6a9302190..d8766c637d 100644
--- a/src/components/views/room_settings/AliasSettings.js
+++ b/src/components/views/room_settings/AliasSettings.js
@@ -19,7 +19,7 @@ var React = require('react');
var ObjectUtils = require("../../../ObjectUtils");
var MatrixClientPeg = require('../../../MatrixClientPeg');
var sdk = require("../../../index");
-import _t from 'counterpart-riot';
+import { _t } from '../../../languageHandler';
var Modal = require("../../../Modal");
module.exports = React.createClass({
diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js
index 3e2c3f5de0..6760a3e894 100644
--- a/src/components/views/rooms/EventTile.js
+++ b/src/components/views/rooms/EventTile.js
@@ -18,7 +18,7 @@ limitations under the License.
var React = require('react');
var classNames = require("classnames");
-import _t from 'counterpart-riot';
+import { _t } from '../../../languageHandler';
var Modal = require('../../../Modal');
var sdk = require('../../../index');
diff --git a/src/components/views/rooms/MemberInfo.js b/src/components/views/rooms/MemberInfo.js
index fed307c216..e76d5387d6 100644
--- a/src/components/views/rooms/MemberInfo.js
+++ b/src/components/views/rooms/MemberInfo.js
@@ -31,7 +31,7 @@ import classNames from 'classnames';
import dis from '../../../dispatcher';
import Modal from '../../../Modal';
import sdk from '../../../index';
-import _t from 'counterpart-riot';
+import { _t } from '../../../languageHandler';
import createRoom from '../../../createRoom';
import DMRoomMap from '../../../utils/DMRoomMap';
import Unread from '../../../Unread';
diff --git a/src/components/views/rooms/MemberList.js b/src/components/views/rooms/MemberList.js
index 6fe1ad1b79..20a0851bd4 100644
--- a/src/components/views/rooms/MemberList.js
+++ b/src/components/views/rooms/MemberList.js
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
var React = require('react');
-import _t from 'counterpart-riot';
+import { _t } from '../../../languageHandler';
var classNames = require('classnames');
var Matrix = require("matrix-js-sdk");
var q = require('q');
diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js
index a9b65d4611..488d3cc287 100644
--- a/src/components/views/rooms/MessageComposer.js
+++ b/src/components/views/rooms/MessageComposer.js
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
var React = require('react');
-import _t from 'counterpart-riot';
+import { _t } from '../../../languageHandler';
var CallHandler = require('../../../CallHandler');
var MatrixClientPeg = require('../../../MatrixClientPeg');
var Modal = require('../../../Modal');
diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js
index adb2a4c6aa..c52f9724b0 100644
--- a/src/components/views/rooms/MessageComposerInput.js
+++ b/src/components/views/rooms/MessageComposerInput.js
@@ -30,7 +30,7 @@ import type {MatrixClient} from 'matrix-js-sdk/lib/matrix';
import SlashCommands from '../../../SlashCommands';
import Modal from '../../../Modal';
import sdk from '../../../index';
-import _t from 'counterpart-riot';
+import { _t } from '../../../languageHandler';
import dis from '../../../dispatcher';
import KeyCode from '../../../KeyCode';
diff --git a/src/components/views/rooms/MessageComposerInputOld.js b/src/components/views/rooms/MessageComposerInputOld.js
index 2bbb1cbb87..9b4efa272c 100644
--- a/src/components/views/rooms/MessageComposerInputOld.js
+++ b/src/components/views/rooms/MessageComposerInputOld.js
@@ -20,7 +20,7 @@ var SlashCommands = require("../../../SlashCommands");
var Modal = require("../../../Modal");
var MemberEntry = require("../../../TabCompleteEntries").MemberEntry;
var sdk = require('../../../index');
-import _t from 'counterpart-riot';
+import { _t } from '../../../languageHandler';
import UserSettingsStore from "../../../UserSettingsStore";
var dis = require("../../../dispatcher");
diff --git a/src/components/views/rooms/RoomHeader.js b/src/components/views/rooms/RoomHeader.js
index f9779d088e..38bd63e019 100644
--- a/src/components/views/rooms/RoomHeader.js
+++ b/src/components/views/rooms/RoomHeader.js
@@ -19,7 +19,7 @@ limitations under the License.
var React = require('react');
var classNames = require('classnames');
var sdk = require('../../../index');
-import _t from 'counterpart-riot';
+import { _t } from '../../../languageHandler';
var MatrixClientPeg = require('../../../MatrixClientPeg');
var Modal = require("../../../Modal");
var dis = require("../../../dispatcher");
diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js
index b463928482..f02f70b88b 100644
--- a/src/components/views/rooms/RoomList.js
+++ b/src/components/views/rooms/RoomList.js
@@ -17,7 +17,7 @@ limitations under the License.
'use strict';
var React = require("react");
var ReactDOM = require("react-dom");
-import _t from 'counterpart-riot';
+import { _t } from '../../../languageHandler';
var GeminiScrollbar = require('react-gemini-scrollbar');
var MatrixClientPeg = require("../../../MatrixClientPeg");
var CallHandler = require('../../../CallHandler');
diff --git a/src/components/views/rooms/RoomPreviewBar.js b/src/components/views/rooms/RoomPreviewBar.js
index 54857845ce..2cf9ade847 100644
--- a/src/components/views/rooms/RoomPreviewBar.js
+++ b/src/components/views/rooms/RoomPreviewBar.js
@@ -21,7 +21,7 @@ var React = require('react');
var sdk = require('../../../index');
var MatrixClientPeg = require('../../../MatrixClientPeg');
-import _t from 'counterpart-riot';
+import { _t } from '../../../languageHandler';
module.exports = React.createClass({
displayName: 'RoomPreviewBar',
diff --git a/src/components/views/rooms/RoomSettings.js b/src/components/views/rooms/RoomSettings.js
index 02efd439a4..df70513b7a 100644
--- a/src/components/views/rooms/RoomSettings.js
+++ b/src/components/views/rooms/RoomSettings.js
@@ -17,7 +17,7 @@ limitations under the License.
import q from 'q';
import React from 'react';
-import _t from 'counterpart-riot';
+import { _t } from '../../../languageHandler';
import MatrixClientPeg from '../../../MatrixClientPeg';
import SdkConfig from '../../../SdkConfig';
import sdk from '../../../index';
diff --git a/src/components/views/rooms/SearchableEntityList.js b/src/components/views/rooms/SearchableEntityList.js
index 1c3ddea705..4412fc539f 100644
--- a/src/components/views/rooms/SearchableEntityList.js
+++ b/src/components/views/rooms/SearchableEntityList.js
@@ -17,7 +17,7 @@ var React = require('react');
var MatrixClientPeg = require("../../../MatrixClientPeg");
var Modal = require("../../../Modal");
var sdk = require("../../../index");
-import _t from 'counterpart-riot';
+import { _t } from '../../../languageHandler';
var GeminiScrollbar = require('react-gemini-scrollbar');
// A list capable of displaying entities which conform to the SearchableEntity
diff --git a/src/components/views/rooms/TopUnreadMessagesBar.js b/src/components/views/rooms/TopUnreadMessagesBar.js
index 0d68b0dc0d..6cb6475a8f 100644
--- a/src/components/views/rooms/TopUnreadMessagesBar.js
+++ b/src/components/views/rooms/TopUnreadMessagesBar.js
@@ -18,7 +18,7 @@ limitations under the License.
'use strict';
var React = require('react');
-import _t from 'counterpart-riot';
+import { _t } from '../../../languageHandler';
var sdk = require('../../../index');
module.exports = React.createClass({
diff --git a/src/components/views/settings/AddPhoneNumber.js b/src/components/views/settings/AddPhoneNumber.js
index bdb67bc5ff..697a539bcd 100644
--- a/src/components/views/settings/AddPhoneNumber.js
+++ b/src/components/views/settings/AddPhoneNumber.js
@@ -15,7 +15,7 @@ limitations under the License.
*/
import React from 'react';
-import _t from 'counterpart-riot';
+import { _t } from '../../../languageHandler';
import sdk from '../../../index';
import AddThreepid from '../../../AddThreepid';
diff --git a/src/components/views/settings/ChangePassword.js b/src/components/views/settings/ChangePassword.js
index 71356fc5f6..e0fe6ce56a 100644
--- a/src/components/views/settings/ChangePassword.js
+++ b/src/components/views/settings/ChangePassword.js
@@ -21,7 +21,7 @@ var MatrixClientPeg = require("../../../MatrixClientPeg");
var Modal = require("../../../Modal");
var sdk = require("../../../index");
import AccessibleButton from '../elements/AccessibleButton';
-import _t from 'counterpart-riot';
+import { _t } from '../../../languageHandler';
module.exports = React.createClass({
displayName: 'ChangePassword',
diff --git a/src/components/views/settings/DevicesPanelEntry.js b/src/components/views/settings/DevicesPanelEntry.js
index c076c5f670..f295a7c2d5 100644
--- a/src/components/views/settings/DevicesPanelEntry.js
+++ b/src/components/views/settings/DevicesPanelEntry.js
@@ -17,7 +17,7 @@ limitations under the License.
import React from 'react';
import sdk from '../../../index';
-import _t from 'counterpart-riot';
+import { _t } from '../../../languageHandler';
import MatrixClientPeg from '../../../MatrixClientPeg';
import Modal from '../../../Modal';
import DateUtils from '../../../DateUtils';
diff --git a/src/createRoom.js b/src/createRoom.js
index 78e7485237..fe148f0408 100644
--- a/src/createRoom.js
+++ b/src/createRoom.js
@@ -17,7 +17,7 @@ limitations under the License.
var MatrixClientPeg = require('./MatrixClientPeg');
var Modal = require('./Modal');
var sdk = require('./index');
-import _t from 'counterpart-riot';
+import { _t } from './languageHandler';
var dis = require("./dispatcher");
var Rooms = require("./Rooms");
From 107024efa8da99fd08267fb65758ad0d4c8d2b50 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Thu, 25 May 2017 11:42:19 +0100
Subject: [PATCH 0181/1016] Unnecessary whitespace
---
README.md | 1 -
1 file changed, 1 deletion(-)
diff --git a/README.md b/README.md
index 56965c5f86..0f5ef73365 100644
--- a/README.md
+++ b/README.md
@@ -28,7 +28,6 @@ Translation Status
==================
[](https://translate.nordgedanken.de/engage/riot-web/?utm_source=widget)
-
Developer Guide
===============
From c7776915f7801ba6cd9d53cf882f4bb0aa3a9281 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Thu, 25 May 2017 11:45:36 +0100
Subject: [PATCH 0182/1016] Kill off more tabs
---
src/CallHandler.js | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/src/CallHandler.js b/src/CallHandler.js
index a557c735a2..5edeba7625 100644
--- a/src/CallHandler.js
+++ b/src/CallHandler.js
@@ -256,7 +256,7 @@ function _onAction(payload) {
Modal.createDialog(ErrorDialog, {
description: _t('You cannot place a call with yourself') + '.',
title: _t("Error"),
- button: _t("OK"),
+ button: _t("OK"),
});
return;
}
@@ -284,7 +284,7 @@ function _onAction(payload) {
Modal.createDialog(ErrorDialog, {
description: _t('Conference calls are not supported in this client'),
title: _t("Error"),
- button: _t("OK"),
+ button: _t("OK"),
});
}
else if (!MatrixClientPeg.get().supportsVoip()) {
@@ -292,7 +292,7 @@ function _onAction(payload) {
Modal.createDialog(ErrorDialog, {
title: _t('VoIP is unsupported'),
description: _t('You cannot place VoIP calls in this browser') + '.',
- button: _t("OK"),
+ button: _t("OK"),
});
}
else if (MatrixClientPeg.get().isRoomEncrypted(payload.room_id)) {
@@ -304,8 +304,8 @@ function _onAction(payload) {
// Therefore we disable conference calling in E2E rooms.
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
- title: _t("Error"),
- button: _t("OK"),
+ title: _t("Error"),
+ button: _t("OK"),
description: _t('Conference calls are not supported in encrypted rooms'),
});
}
@@ -314,7 +314,7 @@ function _onAction(payload) {
Modal.createDialog(QuestionDialog, {
title: _t('Warning') + '!',
description: _t('Conference calling is in development and may not be reliable') + '.',
- button: _t("OK"),
+ button: _t("OK"),
onFinished: confirm=>{
if (confirm) {
ConferenceHandler.createNewMatrixCall(
@@ -327,7 +327,7 @@ function _onAction(payload) {
Modal.createDialog(ErrorDialog, {
title: _t('Failed to set up conference call'),
description: _t('Conference call failed') + '. ' + ((err && err.message) ? err.message : ''),
- button: _t("OK"),
+ button: _t("OK"),
});
});
}
From d6d9a89453805cf8686fabce958dad76c6b111cb Mon Sep 17 00:00:00 2001
From: David Baker
Date: Fri, 28 Apr 2017 11:20:29 +0100
Subject: [PATCH 0183/1016] Other empty sections no longer need to be greyed
---
src/components/views/rooms/RoomList.js | 13 +------------
1 file changed, 1 insertion(+), 12 deletions(-)
diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js
index 9dfa99fb44..e285c1841e 100644
--- a/src/components/views/rooms/RoomList.js
+++ b/src/components/views/rooms/RoomList.js
@@ -510,14 +510,9 @@ module.exports = React.createClass({
},
_getEmptyContent: function(section) {
- let greyed = false;
if (this.state.totalRoomCount === 0) {
const TintableSvg = sdk.getComponent('elements.TintableSvg');
switch (section) {
- case 'm.favourite':
- case 'm.lowpriority':
- greyed = true;
- break;
case 'im.vector.fake.direct':
return
@@ -543,13 +538,7 @@ module.exports = React.createClass({
const labelText = 'Drop here to ' + (VERBS[section] || 'tag ' + section);
- let label;
- if (greyed) {
- label =
{labelText} ;
- } else {
- label = labelText;
- }
- return
;
+ return
;
},
render: function() {
From bff0577cb61bfb8095c23254f618d9bb9a42a131 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Thu, 4 May 2017 13:55:52 +0100
Subject: [PATCH 0184/1016] Add buttons to room sub list headers
Conflicts:
src/component-index.js
src/components/views/rooms/RoomList.js
cherry-picking commit ce119a6.
---
src/components/views/elements/RoleButton.js | 75 +++++++++++++++++++++
src/components/views/rooms/RoomList.js | 40 +++++++----
2 files changed, 102 insertions(+), 13 deletions(-)
create mode 100644 src/components/views/elements/RoleButton.js
diff --git a/src/components/views/elements/RoleButton.js b/src/components/views/elements/RoleButton.js
new file mode 100644
index 0000000000..06006a5779
--- /dev/null
+++ b/src/components/views/elements/RoleButton.js
@@ -0,0 +1,75 @@
+/*
+Copyright Vector Creations Ltd
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import React from 'react';
+import PropTypes from 'prop-types';
+import AccessibleButton from './AccessibleButton';
+import dis from '../../../dispatcher';
+import sdk from '../../../index';
+
+export default React.createClass({
+ displayName: 'RoleButton',
+
+ propTypes: {
+ role: PropTypes.string.isRequired,
+ size: PropTypes.string,
+ },
+
+ getDefaultProps: function() {
+ return {
+ size: 25,
+ };
+ },
+
+ _onClick: function(ev) {
+ ev.stopPropagation();
+
+ let action;
+ switch(this.props.role) {
+ case 'start_chat':
+ action = 'view_create_chat';
+ break;
+ case 'room_directory':
+ action = 'view_room_directory';
+ break;
+ case 'create_room':
+ action = 'view_create_room';
+ break;
+ }
+ if (action) dis.dispatch({action: action});
+ },
+
+ _getIconPath() {
+ switch(this.props.role) {
+ case 'start_chat':
+ return 'img/icons-people.svg';
+ case 'room_directory':
+ return 'img/icons-directory.svg';
+ case 'create_room':
+ return 'img/icons-create-room.svg';
+ }
+ },
+
+ render: function() {
+ const TintableSvg = sdk.getComponent("elements.TintableSvg");
+
+ return (
+
+
+
+ );
+ }
+});
diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js
index e285c1841e..9a64c16239 100644
--- a/src/components/views/rooms/RoomList.js
+++ b/src/components/views/rooms/RoomList.js
@@ -1,5 +1,6 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
+Copyright Vector Creations Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -510,27 +511,23 @@ module.exports = React.createClass({
},
_getEmptyContent: function(section) {
+ const RoleButton = sdk.getComponent('elements.RoleButton');
if (this.state.totalRoomCount === 0) {
const TintableSvg = sdk.getComponent('elements.TintableSvg');
switch (section) {
case 'im.vector.fake.direct':
return
-
-
-
- Use the button below to chat with someone!
+ Press
+
+ to start a chat with someone
;
case 'im.vector.fake.recent':
return
-
-
-
- Use the button below to browse the room directory
-
-
-
-
- or this button to start a new one!
+ You're not in any rooms yet! Press
+
+ to make a room or
+
+ to browse the directory
;
}
}
@@ -541,6 +538,21 @@ module.exports = React.createClass({
return ;
},
+ _getHeaderItems: function(section) {
+ const RoleButton = sdk.getComponent('elements.RoleButton');
+ switch (section) {
+ case 'im.vector.fake.direct':
+ return
+
+ ;
+ case 'im.vector.fake.recent':
+ return
+
+
+ ;
+ }
+ },
+
render: function() {
var RoomSubList = sdk.getComponent('structures.RoomSubList');
var self = this;
@@ -577,6 +589,7 @@ module.exports = React.createClass({
label="People"
tagName="im.vector.fake.direct"
emptyContent={this._getEmptyContent('im.vector.fake.direct')}
+ headerItems={this._getHeaderItems('im.vector.fake.direct')}
editable={ true }
order="recent"
selectedRoom={ self.props.selectedRoom }
@@ -591,6 +604,7 @@ module.exports = React.createClass({
label="Rooms"
editable={ true }
emptyContent={this._getEmptyContent('im.vector.fake.recent')}
+ headerItems={this._getHeaderItems('im.vector.fake.recent')}
order="recent"
selectedRoom={ self.props.selectedRoom }
incomingCall={ self.state.incomingCall }
From 9fa6e8b1e23ca6cb60812a23b521c7d4629fe68a Mon Sep 17 00:00:00 2001
From: David Baker
Date: Thu, 25 May 2017 12:02:05 +0100
Subject: [PATCH 0185/1016] Add us to copyright
As I've now added code to this file
---
src/components/views/elements/LanguageDropdown.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/components/views/elements/LanguageDropdown.js b/src/components/views/elements/LanguageDropdown.js
index 39458b506c..25a920d2e0 100644
--- a/src/components/views/elements/LanguageDropdown.js
+++ b/src/components/views/elements/LanguageDropdown.js
@@ -1,5 +1,6 @@
/*
Copyright 2017 Marcel Radzio (MTRNord)
+Copyright 2017 Vector Creations Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
From 54af06e8e12bfe86f11b4a3131ea155b8e161f3c Mon Sep 17 00:00:00 2001
From: David Baker
Date: Thu, 4 May 2017 15:02:21 +0100
Subject: [PATCH 0186/1016] What year is it? Who's the president?
---
src/components/views/elements/RoleButton.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/views/elements/RoleButton.js b/src/components/views/elements/RoleButton.js
index 06006a5779..f20b4c6b88 100644
--- a/src/components/views/elements/RoleButton.js
+++ b/src/components/views/elements/RoleButton.js
@@ -1,5 +1,5 @@
/*
-Copyright Vector Creations Ltd
+Copyright 2017 Vector Creations Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
From a996f52ea34c0d4c7cc072c7cf068baf3b9cde1b Mon Sep 17 00:00:00 2001
From: David Baker
Date: Thu, 4 May 2017 15:38:09 +0100
Subject: [PATCH 0187/1016] Make bottom left menu buttons use RoleButton too
---
src/components/views/elements/RoleButton.js | 53 ++++++++++++++++++++-
1 file changed, 52 insertions(+), 1 deletion(-)
diff --git a/src/components/views/elements/RoleButton.js b/src/components/views/elements/RoleButton.js
index f20b4c6b88..60f227a067 100644
--- a/src/components/views/elements/RoleButton.js
+++ b/src/components/views/elements/RoleButton.js
@@ -31,6 +31,13 @@ export default React.createClass({
getDefaultProps: function() {
return {
size: 25,
+ tooltip: false,
+ };
+ },
+
+ getInitialState: function() {
+ return {
+ showTooltip: false,
};
},
@@ -48,10 +55,39 @@ export default React.createClass({
case 'create_room':
action = 'view_create_room';
break;
+ case 'home_page':
+ action = 'view_home_page';
+ break;
+ case 'settings':
+ action = 'view_user_settings';
+ break;
}
if (action) dis.dispatch({action: action});
},
+ _onMouseEnter: function() {
+ if (this.props.tooltip) this.setState({showTooltip: true});
+ },
+
+ _onMouseLeave: function() {
+ this.setState({showTooltip: false});
+ },
+
+ _getLabel() {
+ switch(this.props.role) {
+ case 'start_chat':
+ return 'Start chat';
+ case 'room_directory':
+ return 'Room directory';
+ case 'create_room':
+ return 'Create new room';
+ case 'home_page':
+ return 'Welcome page';
+ case 'settings':
+ return 'Settings';
+ }
+ },
+
_getIconPath() {
switch(this.props.role) {
case 'start_chat':
@@ -60,15 +96,30 @@ export default React.createClass({
return 'img/icons-directory.svg';
case 'create_room':
return 'img/icons-create-room.svg';
+ case 'home_page':
+ return 'img/icons-home.svg';
+ case 'settings':
+ return 'img/icons-settings.svg';
}
},
render: function() {
const TintableSvg = sdk.getComponent("elements.TintableSvg");
+ let tooltip;
+ if (this.state.showTooltip) {
+ const RoomTooltip = sdk.getComponent("rooms.RoomTooltip");
+ tooltip = ;
+ }
+
return (
-
+
+ {tooltip}
);
}
From 3d3d89202e01100f1162d30542b59c8826c19e29 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Thu, 4 May 2017 15:46:24 +0100
Subject: [PATCH 0188/1016] Year
---
src/components/views/rooms/RoomList.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js
index 9a64c16239..ecb178d145 100644
--- a/src/components/views/rooms/RoomList.js
+++ b/src/components/views/rooms/RoomList.js
@@ -1,6 +1,6 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
-Copyright Vector Creations Ltd
+Copyright 2017 Vector Creations Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
From 99efbbee5e23913221d95bc9d8457bed24a4e0ae Mon Sep 17 00:00:00 2001
From: David Baker
Date: Thu, 4 May 2017 16:22:06 +0100
Subject: [PATCH 0189/1016] Depend on prop-types module
So we can start writing code compatible with new React
---
package.json | 1 +
1 file changed, 1 insertion(+)
diff --git a/package.json b/package.json
index 059fdd390f..572dcddeb5 100644
--- a/package.json
+++ b/package.json
@@ -65,6 +65,7 @@
"lodash": "^4.13.1",
"matrix-js-sdk": "0.7.8",
"optimist": "^0.6.1",
+ "prop-types": "^15.5.8",
"q": "^1.4.1",
"react": "^15.4.0",
"react-addons-css-transition-group": "15.3.2",
From dc2274df54896b48f836854cf46cd10b525d41c8 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Thu, 4 May 2017 18:08:04 +0100
Subject: [PATCH 0190/1016] Hide empty tips if collapsed
---
src/components/views/rooms/RoomList.js | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js
index ecb178d145..2dce02cc78 100644
--- a/src/components/views/rooms/RoomList.js
+++ b/src/components/views/rooms/RoomList.js
@@ -511,6 +511,12 @@ module.exports = React.createClass({
},
_getEmptyContent: function(section) {
+ const RoomDropTarget = sdk.getComponent('rooms.RoomDropTarget');
+
+ if (this.props.collapsed) {
+ return ;
+ }
+
const RoleButton = sdk.getComponent('elements.RoleButton');
if (this.state.totalRoomCount === 0) {
const TintableSvg = sdk.getComponent('elements.TintableSvg');
@@ -531,7 +537,6 @@ module.exports = React.createClass({
;
}
}
- const RoomDropTarget = sdk.getComponent('rooms.RoomDropTarget');
const labelText = 'Drop here to ' + (VERBS[section] || 'tag ' + section);
From 913723b76bef38f8c91e57bb4aa49ba4f2bda57c Mon Sep 17 00:00:00 2001
From: David Baker
Date: Thu, 25 May 2017 12:08:14 +0100
Subject: [PATCH 0191/1016] Remove extCounterpart now we only use one instance
---
src/languageHandler.js | 11 +----------
1 file changed, 1 insertion(+), 10 deletions(-)
diff --git a/src/languageHandler.js b/src/languageHandler.js
index 25c380a53c..5e7c7869b4 100644
--- a/src/languageHandler.js
+++ b/src/languageHandler.js
@@ -37,7 +37,7 @@ export function _t(...args) {
return counterpart.translate(...args);
}
-export function setLanguage(languages, extCounterpart=null) {
+export function setLanguage(languages) {
if (!languages || !Array.isArray(languages)) {
const languages = this.getNormalizedLanguageKeys(this.getLanguageFromBrowser());
console.log("no language found. Got from browser: " + JSON.stringify(languages));
@@ -81,9 +81,6 @@ export function setLanguage(languages, extCounterpart=null) {
});
return;
} else {
- if (extCounterpart) {
- extCounterpart.registerTranslations(langCode, langJson);
- }
counterpart.registerTranslations(langCode, langJson);
}
}
@@ -106,9 +103,6 @@ export function setLanguage(languages, extCounterpart=null) {
if ((isValidFirstLanguage) || (languages.length==2 && languageFiles.hasOwnProperty(languages[1]))) {
validLanguageKey = (isValidFirstLanguage) ? languages[0] : languages[1];
getLanguage(i18nFolder + languageFiles[validLanguageKey], validLanguageKey, registerTranslations);
- if (extCounterpart) {
- extCounterpart.setLocale(validLanguageKey);
- }
counterpart.setLocale(validLanguageKey);
UserSettingsStore.setLocalSetting('language', validLanguageKey);
console.log("set language to "+validLanguageKey);
@@ -120,9 +114,6 @@ export function setLanguage(languages, extCounterpart=null) {
if (validLanguageKey!="en") {
getLanguage(i18nFolder + languageFiles['en'], 'en', registerTranslations);
}
- if (extCounterpart) {
- extCounterpart.setFallbackLocale('en');
- }
counterpart.setFallbackLocale('en');
});
};
From 9337158a470e2d23d16cce0054a931e5f97a3b0d Mon Sep 17 00:00:00 2001
From: David Baker
Date: Fri, 5 May 2017 14:25:18 +0100
Subject: [PATCH 0192/1016] Separate classes for the different buttons
Also rename RoleButton to ActionButton because it's not being
given a Role any more.
Conflicts:
src/component-index.js
cherry-picking commit 4a5821e.
---
.../{RoleButton.js => ActionButton.js} | 60 +++----------------
.../views/elements/CreateRoomButton.js | 37 ++++++++++++
src/components/views/elements/HomeButton.js | 37 ++++++++++++
.../views/elements/RoomDirectoryButton.js | 37 ++++++++++++
.../views/elements/SettingsButton.js | 37 ++++++++++++
.../views/elements/StartChatButton.js | 37 ++++++++++++
src/components/views/rooms/RoomList.js | 20 ++++---
7 files changed, 204 insertions(+), 61 deletions(-)
rename src/components/views/elements/{RoleButton.js => ActionButton.js} (54%)
create mode 100644 src/components/views/elements/CreateRoomButton.js
create mode 100644 src/components/views/elements/HomeButton.js
create mode 100644 src/components/views/elements/RoomDirectoryButton.js
create mode 100644 src/components/views/elements/SettingsButton.js
create mode 100644 src/components/views/elements/StartChatButton.js
diff --git a/src/components/views/elements/RoleButton.js b/src/components/views/elements/ActionButton.js
similarity index 54%
rename from src/components/views/elements/RoleButton.js
rename to src/components/views/elements/ActionButton.js
index 60f227a067..6d6289ddab 100644
--- a/src/components/views/elements/RoleButton.js
+++ b/src/components/views/elements/ActionButton.js
@@ -24,8 +24,11 @@ export default React.createClass({
displayName: 'RoleButton',
propTypes: {
- role: PropTypes.string.isRequired,
size: PropTypes.string,
+ tooltip: PropTypes.bool,
+ action: PropTypes.string.isRequired,
+ label: PropTypes.string.isRequired,
+ iconPath: PropTypes.string.isRequired,
},
getDefaultProps: function() {
@@ -43,26 +46,7 @@ export default React.createClass({
_onClick: function(ev) {
ev.stopPropagation();
-
- let action;
- switch(this.props.role) {
- case 'start_chat':
- action = 'view_create_chat';
- break;
- case 'room_directory':
- action = 'view_room_directory';
- break;
- case 'create_room':
- action = 'view_create_room';
- break;
- case 'home_page':
- action = 'view_home_page';
- break;
- case 'settings':
- action = 'view_user_settings';
- break;
- }
- if (action) dis.dispatch({action: action});
+ dis.dispatch({action: this.props.action});
},
_onMouseEnter: function() {
@@ -73,43 +57,13 @@ export default React.createClass({
this.setState({showTooltip: false});
},
- _getLabel() {
- switch(this.props.role) {
- case 'start_chat':
- return 'Start chat';
- case 'room_directory':
- return 'Room directory';
- case 'create_room':
- return 'Create new room';
- case 'home_page':
- return 'Welcome page';
- case 'settings':
- return 'Settings';
- }
- },
-
- _getIconPath() {
- switch(this.props.role) {
- case 'start_chat':
- return 'img/icons-people.svg';
- case 'room_directory':
- return 'img/icons-directory.svg';
- case 'create_room':
- return 'img/icons-create-room.svg';
- case 'home_page':
- return 'img/icons-home.svg';
- case 'settings':
- return 'img/icons-settings.svg';
- }
- },
-
render: function() {
const TintableSvg = sdk.getComponent("elements.TintableSvg");
let tooltip;
if (this.state.showTooltip) {
const RoomTooltip = sdk.getComponent("rooms.RoomTooltip");
- tooltip = ;
+ tooltip = ;
}
return (
@@ -118,7 +72,7 @@ export default React.createClass({
onMouseEnter={this._onMouseEnter}
onMouseLeave={this._onMouseLeave}
>
-
+
{tooltip}
);
diff --git a/src/components/views/elements/CreateRoomButton.js b/src/components/views/elements/CreateRoomButton.js
new file mode 100644
index 0000000000..d6b6526d6c
--- /dev/null
+++ b/src/components/views/elements/CreateRoomButton.js
@@ -0,0 +1,37 @@
+/*
+Copyright 2017 Vector Creations Ltd
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import sdk from '../../../index';
+import PropTypes from 'prop-types';
+
+const CreateRoomButton = function(props) {
+ const ActionButton = sdk.getComponent('elements.ActionButton');
+ return (
+
+ );
+};
+
+CreateRoomButton.propTypes = {
+ size: PropTypes.string,
+ tooltip: PropTypes.bool,
+};
+
+export default CreateRoomButton;
diff --git a/src/components/views/elements/HomeButton.js b/src/components/views/elements/HomeButton.js
new file mode 100644
index 0000000000..4c7f295c87
--- /dev/null
+++ b/src/components/views/elements/HomeButton.js
@@ -0,0 +1,37 @@
+/*
+Copyright 2017 Vector Creations Ltd
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import sdk from '../../../index';
+import PropTypes from 'prop-types';
+
+const HomeButton = function(props) {
+ const ActionButton = sdk.getComponent('elements.ActionButton');
+ return (
+
+ );
+};
+
+HomeButton.propTypes = {
+ size: PropTypes.string,
+ tooltip: PropTypes.bool,
+};
+
+export default HomeButton;
diff --git a/src/components/views/elements/RoomDirectoryButton.js b/src/components/views/elements/RoomDirectoryButton.js
new file mode 100644
index 0000000000..651dd8edd0
--- /dev/null
+++ b/src/components/views/elements/RoomDirectoryButton.js
@@ -0,0 +1,37 @@
+/*
+Copyright 2017 Vector Creations Ltd
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import sdk from '../../../index';
+import PropTypes from 'prop-types';
+
+const RoomDirectoryButton = function(props) {
+ const ActionButton = sdk.getComponent('elements.ActionButton');
+ return (
+
+ );
+};
+
+RoomDirectoryButton.propTypes = {
+ size: PropTypes.string,
+ tooltip: PropTypes.bool,
+};
+
+export default RoomDirectoryButton;
diff --git a/src/components/views/elements/SettingsButton.js b/src/components/views/elements/SettingsButton.js
new file mode 100644
index 0000000000..51da6e3fd1
--- /dev/null
+++ b/src/components/views/elements/SettingsButton.js
@@ -0,0 +1,37 @@
+/*
+Copyright 2017 Vector Creations Ltd
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import sdk from '../../../index';
+import PropTypes from 'prop-types';
+
+const SettingsButton = function(props) {
+ const ActionButton = sdk.getComponent('elements.ActionButton');
+ return (
+
+ );
+};
+
+SettingsButton.propTypes = {
+ size: PropTypes.string,
+ tooltip: PropTypes.bool,
+};
+
+export default SettingsButton;
diff --git a/src/components/views/elements/StartChatButton.js b/src/components/views/elements/StartChatButton.js
new file mode 100644
index 0000000000..66cd911754
--- /dev/null
+++ b/src/components/views/elements/StartChatButton.js
@@ -0,0 +1,37 @@
+/*
+Copyright 2017 Vector Creations Ltd
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import sdk from '../../../index';
+import PropTypes from 'prop-types';
+
+const StartChatButton = function(props) {
+ const ActionButton = sdk.getComponent('elements.ActionButton');
+ return (
+
+ );
+};
+
+StartChatButton.propTypes = {
+ size: PropTypes.string,
+ tooltip: PropTypes.bool,
+};
+
+export default StartChatButton;
diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js
index 2dce02cc78..8c8fd3ea86 100644
--- a/src/components/views/rooms/RoomList.js
+++ b/src/components/views/rooms/RoomList.js
@@ -517,22 +517,24 @@ module.exports = React.createClass({
return ;
}
- const RoleButton = sdk.getComponent('elements.RoleButton');
+ const StartChatButton = sdk.getComponent('elements.StartChatButton');
+ const RoomDirectoryButton = sdk.getComponent('elements.RoomDirectoryButton');
+ const CreateRoomButton = sdk.getComponent('elements.CreateRoomButton');
if (this.state.totalRoomCount === 0) {
const TintableSvg = sdk.getComponent('elements.TintableSvg');
switch (section) {
case 'im.vector.fake.direct':
return
Press
-
+
to start a chat with someone
;
case 'im.vector.fake.recent':
return
You're not in any rooms yet! Press
-
+
to make a room or
-
+
to browse the directory
;
}
@@ -544,16 +546,18 @@ module.exports = React.createClass({
},
_getHeaderItems: function(section) {
- const RoleButton = sdk.getComponent('elements.RoleButton');
+ const StartChatButton = sdk.getComponent('elements.StartChatButton');
+ const RoomDirectoryButton = sdk.getComponent('elements.RoomDirectoryButton');
+ const CreateRoomButton = sdk.getComponent('elements.CreateRoomButton');
switch (section) {
case 'im.vector.fake.direct':
return
-
+
;
case 'im.vector.fake.recent':
return
-
-
+
+
;
}
},
From 5e855e6fee3c5cf068263967baa6d0c544c2a32c Mon Sep 17 00:00:00 2001
From: David Baker
Date: Fri, 5 May 2017 14:56:26 +0100
Subject: [PATCH 0193/1016] Size is a string, import react
React gets put in by the JSX transpile
---
src/components/views/elements/ActionButton.js | 2 +-
src/components/views/elements/CreateRoomButton.js | 1 +
src/components/views/elements/HomeButton.js | 1 +
src/components/views/elements/RoomDirectoryButton.js | 1 +
src/components/views/elements/SettingsButton.js | 1 +
src/components/views/elements/StartChatButton.js | 1 +
6 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/components/views/elements/ActionButton.js b/src/components/views/elements/ActionButton.js
index 6d6289ddab..267388daf6 100644
--- a/src/components/views/elements/ActionButton.js
+++ b/src/components/views/elements/ActionButton.js
@@ -33,7 +33,7 @@ export default React.createClass({
getDefaultProps: function() {
return {
- size: 25,
+ size: "25",
tooltip: false,
};
},
diff --git a/src/components/views/elements/CreateRoomButton.js b/src/components/views/elements/CreateRoomButton.js
index d6b6526d6c..e7e526d36b 100644
--- a/src/components/views/elements/CreateRoomButton.js
+++ b/src/components/views/elements/CreateRoomButton.js
@@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
+import React from 'react';
import sdk from '../../../index';
import PropTypes from 'prop-types';
diff --git a/src/components/views/elements/HomeButton.js b/src/components/views/elements/HomeButton.js
index 4c7f295c87..5c446f24c9 100644
--- a/src/components/views/elements/HomeButton.js
+++ b/src/components/views/elements/HomeButton.js
@@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
+import React from 'react';
import sdk from '../../../index';
import PropTypes from 'prop-types';
diff --git a/src/components/views/elements/RoomDirectoryButton.js b/src/components/views/elements/RoomDirectoryButton.js
index 651dd8edd0..5e68776a15 100644
--- a/src/components/views/elements/RoomDirectoryButton.js
+++ b/src/components/views/elements/RoomDirectoryButton.js
@@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
+import React from 'react';
import sdk from '../../../index';
import PropTypes from 'prop-types';
diff --git a/src/components/views/elements/SettingsButton.js b/src/components/views/elements/SettingsButton.js
index 51da6e3fd1..c6438da277 100644
--- a/src/components/views/elements/SettingsButton.js
+++ b/src/components/views/elements/SettingsButton.js
@@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
+import React from 'react';
import sdk from '../../../index';
import PropTypes from 'prop-types';
diff --git a/src/components/views/elements/StartChatButton.js b/src/components/views/elements/StartChatButton.js
index 66cd911754..02d5677a7c 100644
--- a/src/components/views/elements/StartChatButton.js
+++ b/src/components/views/elements/StartChatButton.js
@@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
+import React from 'react';
import sdk from '../../../index';
import PropTypes from 'prop-types';
From 548f319816d94955fc45fc68f51da5f6dcc2787e Mon Sep 17 00:00:00 2001
From: David Baker
Date: Fri, 5 May 2017 17:51:14 +0100
Subject: [PATCH 0194/1016] Remove redundant role elements
---
src/components/views/rooms/RoomList.js | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js
index 8c8fd3ea86..cde2bec7da 100644
--- a/src/components/views/rooms/RoomList.js
+++ b/src/components/views/rooms/RoomList.js
@@ -552,12 +552,12 @@ module.exports = React.createClass({
switch (section) {
case 'im.vector.fake.direct':
return
-
+
;
case 'im.vector.fake.recent':
return
-
-
+
+
;
}
},
From db45e9953649eef76de62caedce2060d8cd2bd4e Mon Sep 17 00:00:00 2001
From: David Baker
Date: Thu, 25 May 2017 12:09:48 +0100
Subject: [PATCH 0195/1016] Remove defaulting here
We already do it in the place this function gets called
---
src/languageHandler.js | 5 -----
1 file changed, 5 deletions(-)
diff --git a/src/languageHandler.js b/src/languageHandler.js
index 5e7c7869b4..ae34e9c764 100644
--- a/src/languageHandler.js
+++ b/src/languageHandler.js
@@ -38,11 +38,6 @@ export function _t(...args) {
}
export function setLanguage(languages) {
- if (!languages || !Array.isArray(languages)) {
- const languages = this.getNormalizedLanguageKeys(this.getLanguageFromBrowser());
- console.log("no language found. Got from browser: " + JSON.stringify(languages));
- }
-
request(i18nFolder + 'languages.json', function(err, response, body) {
function getLanguage(langPath, langCode, callback) {
let response_return = {};
From 3185d3ed41d376d52d65583247a08a74a12f1983 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Thu, 25 May 2017 13:54:59 +0100
Subject: [PATCH 0196/1016] Re-add bouncing/callout animation to action buttons
---
src/components/views/elements/ActionButton.js | 4 ++++
src/components/views/elements/CreateRoomButton.js | 1 +
src/components/views/elements/RoomDirectoryButton.js | 1 +
src/components/views/elements/StartChatButton.js | 3 ++-
src/components/views/rooms/RoomList.js | 6 +++---
5 files changed, 11 insertions(+), 4 deletions(-)
diff --git a/src/components/views/elements/ActionButton.js b/src/components/views/elements/ActionButton.js
index 267388daf6..08fb6faa1d 100644
--- a/src/components/views/elements/ActionButton.js
+++ b/src/components/views/elements/ActionButton.js
@@ -27,6 +27,7 @@ export default React.createClass({
size: PropTypes.string,
tooltip: PropTypes.bool,
action: PropTypes.string.isRequired,
+ mouseOverAction: PropTypes.string,
label: PropTypes.string.isRequired,
iconPath: PropTypes.string.isRequired,
},
@@ -51,6 +52,9 @@ export default React.createClass({
_onMouseEnter: function() {
if (this.props.tooltip) this.setState({showTooltip: true});
+ if (this.props.mouseOverAction) {
+ dis.dispatch({action: this.props.mouseOverAction});
+ }
},
_onMouseLeave: function() {
diff --git a/src/components/views/elements/CreateRoomButton.js b/src/components/views/elements/CreateRoomButton.js
index e7e526d36b..82643559b3 100644
--- a/src/components/views/elements/CreateRoomButton.js
+++ b/src/components/views/elements/CreateRoomButton.js
@@ -22,6 +22,7 @@ const CreateRoomButton = function(props) {
const ActionButton = sdk.getComponent('elements.ActionButton');
return (
Press
-
+
to start a chat with someone
;
case 'im.vector.fake.recent':
return
You're not in any rooms yet! Press
-
+
to make a room or
-
+
to browse the directory
;
}
From 7900bf1c7de0d170b3ca055332d4bebb3f10f4ef Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Thu, 25 May 2017 13:55:37 +0100
Subject: [PATCH 0197/1016] Don't show "Drop to ..." if total rooms = 0
---
src/components/views/rooms/RoomList.js | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js
index 98a5229d6a..216dd972cf 100644
--- a/src/components/views/rooms/RoomList.js
+++ b/src/components/views/rooms/RoomList.js
@@ -540,6 +540,10 @@ module.exports = React.createClass({
}
}
+ if (this.state.totalRoomCount === 0) {
+ return null;
+ }
+
const labelText = 'Drop here to ' + (VERBS[section] || 'tag ' + section);
return ;
From 51c8ee6db23f5eacaa348a58324df69704b06a15 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Thu, 25 May 2017 14:38:12 +0100
Subject: [PATCH 0198/1016] Allow teamServerConfig to be missing
---
src/components/structures/LoggedInView.js | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js
index 4687afbee1..8cd2bf8a71 100644
--- a/src/components/structures/LoggedInView.js
+++ b/src/components/structures/LoggedInView.js
@@ -232,9 +232,15 @@ export default React.createClass({
break;
case PageTypes.HomePage:
+ // If team server config is present, pass the teamServerURL. props.teamToken
+ // must also be set for the team page to be displayed, otherwise the
+ // welcomePageUrl is used (which might be undefined).
+ const teamServerUrl = this.props.config.teamServerConfig ?
+ this.props.config.teamServerConfig.teamServerURL : null;
+
page_element = ;
From 2265b59287b44c0ad8d473c98e59a84209141df2 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Thu, 25 May 2017 14:54:28 +0100
Subject: [PATCH 0199/1016] Remove warm-fuzzy after setting mxid
---
src/components/views/dialogs/SetMxIdDialog.js | 39 +------------------
1 file changed, 1 insertion(+), 38 deletions(-)
diff --git a/src/components/views/dialogs/SetMxIdDialog.js b/src/components/views/dialogs/SetMxIdDialog.js
index c371fdd35a..86b5fccbc2 100644
--- a/src/components/views/dialogs/SetMxIdDialog.js
+++ b/src/components/views/dialogs/SetMxIdDialog.js
@@ -53,9 +53,6 @@ export default React.createClass({
doingUIAuth: false,
// Indicate error with auth
authError: '',
-
- // Indicate success of setting mxid
- success: false,
};
},
@@ -98,10 +95,6 @@ export default React.createClass({
});
},
- onSuccessContinue: function() {
- this.props.onFinished(true, this._registeredCreds);
- },
-
_doUsernameCheck: function() {
// Check if username is available
return this._matrixClient.isUsernameAvailable(this.state.username).then(
@@ -169,7 +162,7 @@ export default React.createClass({
// XXX Implement RTS /register here
const teamToken = null;
- this._registeredCreds = {
+ this.props.onFinished(true, {
userId: response.user_id,
deviceId: response.device_id,
homeserverUrl: this._matrixClient.getHomeserverUrl(),
@@ -177,11 +170,6 @@ export default React.createClass({
accessToken: response.access_token,
password: this._generatedPassword,
teamToken: teamToken,
- };
-
- // Before continuing, show a warm-fuzzy success and only submit onSuccessContinue
- this.setState({
- success: true,
});
},
@@ -231,31 +219,6 @@ export default React.createClass({
!this.state.usernameError &&
!this.state.usernameBusy;
- if (this.state.success) {
- // XXX BaseDialog needs an onFinished
- return (
-
-
-
- You have successfully
- picked { this.state.username } as your
- username and you now have access to the full
- set of features on Riot.
-
-
-
-
-
-
- );
- }
-
return (
Date: Thu, 25 May 2017 15:20:02 +0100
Subject: [PATCH 0200/1016] Unbreak the roomlist
---
src/components/views/rooms/RoomList.js | 10 ----------
1 file changed, 10 deletions(-)
diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js
index 216dd972cf..efadda08ac 100644
--- a/src/components/views/rooms/RoomList.js
+++ b/src/components/views/rooms/RoomList.js
@@ -72,9 +72,6 @@ module.exports = React.createClass({
cli.on("RoomMember.name", this.onRoomMemberName);
cli.on("accountData", this.onAccountData);
- // lookup for which lists a given roomId is currently in.
- this.listsForRoomId = {};
-
this.refreshRoomList();
// order of the sublists
@@ -260,7 +257,6 @@ module.exports = React.createClass({
// ", prevMembership = " + me.events.member.getPrevContent().membership);
if (me.membership == "invite") {
- self.listsForRoomId[room.roomId].push("im.vector.fake.invite");
lists["im.vector.fake.invite"].push(room);
}
else if (HIDE_CONFERENCE_CHANS && Rooms.isConfCallRoom(room, me, self.props.ConferenceHandler)) {
@@ -277,22 +273,18 @@ module.exports = React.createClass({
var tagName = tagNames[i];
lists[tagName] = lists[tagName] || [];
lists[tagName].push(room);
- self.listsForRoomId[room.roomId].push(tagName);
otherTagNames[tagName] = 1;
}
}
else if (dmRoomMap.getUserIdForRoomId(room.roomId)) {
// "Direct Message" rooms (that we're still in and that aren't otherwise tagged)
- self.listsForRoomId[room.roomId].push("im.vector.fake.direct");
lists["im.vector.fake.direct"].push(room);
}
else {
- self.listsForRoomId[room.roomId].push("im.vector.fake.recent");
lists["im.vector.fake.recent"].push(room);
}
}
else if (me.membership === "leave") {
- self.listsForRoomId[room.roomId].push("im.vector.fake.archived");
lists["im.vector.fake.archived"].push(room);
}
else {
@@ -313,10 +305,8 @@ module.exports = React.createClass({
const me = room.getMember(MatrixClientPeg.get().credentials.userId);
if (me && Rooms.looksLikeDirectMessageRoom(room, me)) {
- self.listsForRoomId[room.roomId].push("im.vector.fake.direct");
lists["im.vector.fake.direct"].push(room);
} else {
- self.listsForRoomId[room.roomId].push("im.vector.fake.recent");
lists["im.vector.fake.recent"].push(room);
}
}
From 11799b4c71330f8dd0636a386e7e6c2782e20f52 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Thu, 25 May 2017 15:27:54 +0100
Subject: [PATCH 0201/1016] Show "Password" instead of "New Password" when the
existing password has been cached
---
src/components/views/settings/ChangePassword.js | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/components/views/settings/ChangePassword.js b/src/components/views/settings/ChangePassword.js
index bfc9ac264e..25af0e389f 100644
--- a/src/components/views/settings/ChangePassword.js
+++ b/src/components/views/settings/ChangePassword.js
@@ -193,12 +193,14 @@ module.exports = React.createClass({
switch (this.state.phase) {
case this.Phases.Edit:
+ const passwordLabel = this.state.cachedPassword ?
+ 'Password' : 'New Password';
return (
{ currentPassword }
- New password
+ { passwordLabel }
From 39dbc4c6e1cc16bb0978930a5382e59d8b141b03 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Thu, 25 May 2017 16:45:32 +0100
Subject: [PATCH 0202/1016] Lots of changes to languageHandler
* Replace callbacks with promises
* Move plain functions to top level
* De-duplicate bits that fetched languages.json
* Take full language preference list from the browser if we
can get it, rather than just the first.
---
src/languageHandler.js | 176 +++++++++++++++++++----------------------
1 file changed, 80 insertions(+), 96 deletions(-)
diff --git a/src/languageHandler.js b/src/languageHandler.js
index ae34e9c764..4b49c95fc9 100644
--- a/src/languageHandler.js
+++ b/src/languageHandler.js
@@ -37,131 +37,115 @@ export function _t(...args) {
return counterpart.translate(...args);
}
-export function setLanguage(languages) {
- request(i18nFolder + 'languages.json', function(err, response, body) {
- function getLanguage(langPath, langCode, callback) {
- let response_return = {};
- let resp_raw = {};
- request(
- { method: "GET", url: langPath },
- (err, response, body) => {
- if (err || response.status < 200 || response.status >= 300) {
- if (response) {
- if (response.status == 404 || (response.status == 0 && body == '')) {
- resp_raw = {};
- }
- }
- const resp = {err: err, response: resp_raw};
- err = resp['err'];
- const response_cb = resp['response'];
- callback(err, response_cb, langCode);
- return;
- }
+export function setLanguage(preferredLangs) {
+ if (!Array.isArray(preferredLangs)) {
+ preferredLangs = [preferredLangs];
+ }
- response_return = JSON.parse(body);
- callback(null, response_return, langCode);
- return;
- }
- );
- return;
- }
+ let langToUse;
+ let availLangs;
+ return getLangsJson().then((result) => {
+ availLangs = result;
- function registerTranslations(err, langJson, langCode){
- if (err !== null) {
- var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
- Modal.createDialog(ErrorDialog, {
- title: counterpart.translate('Error changing language'),
- description: counterpart.translate('Riot was unable to find the correct Data for the selected Language.'),
- button: counterpart.translate("OK"),
- });
- return;
- } else {
- counterpart.registerTranslations(langCode, langJson);
+ for (let i = 0; i < preferredLangs.length; ++i) {
+ if (availLangs.hasOwnProperty(preferredLangs[i])) {
+ langToUse = preferredLangs[i];
}
}
-
- let languageFiles = {};
- if(err){
- console.error(err);
- return;
- } else {
- if (body) {
- languageFiles = JSON.parse(body);
- } else {
- languageFiles = JSON.parse('{"en": "en_EN.json"}');
- }
+ if (!langToUse) {
+ throw new Error("Unable to find an appropriate language");
}
+ return getLanguage(i18nFolder + availLangs[langToUse]);
+ }).then((langData) => {
+ counterpart.registerTranslations(langToUse, langData);
+ counterpart.setLocale(langToUse);
+ UserSettingsStore.setLocalSetting('language', langToUse);
+ console.log("set language to " + langToUse);
- const isValidFirstLanguage = (languageFiles.hasOwnProperty(languages[0]));
- var validLanguageKey = "";
- if ((isValidFirstLanguage) || (languages.length==2 && languageFiles.hasOwnProperty(languages[1]))) {
- validLanguageKey = (isValidFirstLanguage) ? languages[0] : languages[1];
- getLanguage(i18nFolder + languageFiles[validLanguageKey], validLanguageKey, registerTranslations);
- counterpart.setLocale(validLanguageKey);
- UserSettingsStore.setLocalSetting('language', validLanguageKey);
- console.log("set language to "+validLanguageKey);
- } else {
- console.log("didnt find any language file");
+ // Set 'en' as fallback language:
+ if (langToUse != "en") {
+ return getLanguage(i18nFolder + availLangs['en']);
}
-
- //Set 'en' as fallback language:
- if (validLanguageKey!="en") {
- getLanguage(i18nFolder + languageFiles['en'], 'en', registerTranslations);
- }
- counterpart.setFallbackLocale('en');
+ }).then((langData) => {
+ if (langData) counterpart.registerTranslations('en', langData);
});
};
export function getAllLanguageKeysFromJson() {
- let deferred = q.defer();
-
- request(
- { method: "GET", url: i18nFolder + 'languages.json' },
- (err, response, body) => {
- if (err || response.status < 200 || response.status >= 300) {
- if (response) {
- if (response.status == 404 || (response.status == 0 && body == '')) {
- deferred.resolve({});
- }
- }
- deferred.reject({err: err, response: response});
- return;
- }
- var languages = JSON.parse(body);
- // If no language is found, fallback to 'en':
- if (!languages) {
- languages = [{"en": "en_EN.json"}];
- }
- const languageKeys = Object.keys(languages);
- deferred.resolve(languageKeys);
- }
- );
- return deferred.promise;
+ return getLangsJson().then((langs) => {
+ return Object.keys(langs);
+ });
}
-export function getLanguageFromBrowser() {
- return navigator.languages[0] || navigator.language || navigator.userLanguage;
+export function getLanguagesFromBrowser() {
+ if (navigator.languages) return navigator.languages;
+ if (navigator.language) return [navigator.language]
+ return [navigator.userLanguage];
};
+/**
+ * Turns a language string, normalises it,
+ * (see normalizeLanguageKey) into an array of language strings
+ * with fallback to generic languages
+ * (eg. 'pt-BR' => ['pt-br', 'pt'])
+ *
+ * @param {string} language The input language string
+ * @return {string[]} List of normalised languages
+ */
export function getNormalizedLanguageKeys(language) {
- if (!language) {
- return;
- }
const languageKeys = [];
const normalizedLanguage = this.normalizeLanguageKey(language);
const languageParts = normalizedLanguage.split('-');
- if (languageParts.length==2 && languageParts[0]==languageParts[1]) {
+ if (languageParts.length == 2 && languageParts[0] == languageParts[1]) {
languageKeys.push(languageParts[0]);
} else {
languageKeys.push(normalizedLanguage);
- if (languageParts.length==2) {
+ if (languageParts.length == 2) {
languageKeys.push(languageParts[0]);
}
}
return languageKeys;
};
+/**
+ * Returns a language string with underscores replaced with
+ * hyphens, and lowercased.
+ */
export function normalizeLanguageKey(language) {
return language.toLowerCase().replace("_","-");
};
+
+function getLangsJson() {
+ const deferred = q.defer();
+
+ request(
+ { method: "GET", url: i18nFolder + 'languages.json' },
+ (err, response, body) => {
+ if (err || response.status < 200 || response.status >= 300) {
+ deferred.reject({err: err, response: response});
+ return;
+ }
+ deferred.resolve(JSON.parse(body));
+ }
+ );
+ return deferred.promise;
+}
+
+function getLanguage(langPath) {
+ const deferred = q.defer();
+
+ let response_return = {};
+ request(
+ { method: "GET", url: langPath },
+ (err, response, body) => {
+ if (err || response.status < 200 || response.status >= 300) {
+ deferred.reject({err: err, response: response});
+ return;
+ }
+
+ deferred.resolve(JSON.parse(body));
+ }
+ );
+ return deferred.promise;
+}
From 3a379d09de62bf3282d693dfd73fe612499b5179 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Thu, 25 May 2017 16:52:15 +0100
Subject: [PATCH 0203/1016] Update translations
Actually translate the language dropdown label & add the string.
Remove some unused ones.
---
src/components/structures/UserSettings.js | 2 +-
src/i18n/strings/en_EN.json | 3 +--
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js
index a39c06f907..279920eeb7 100644
--- a/src/components/structures/UserSettings.js
+++ b/src/components/structures/UserSettings.js
@@ -567,7 +567,7 @@ module.exports = React.createClass({
_renderLanguageSetting: function () {
const LanguageDropdown = sdk.getComponent('views.elements.LanguageDropdown');
return
-
Interface Language
+
{_t('Interface Language')}
Date: Thu, 25 May 2017 16:55:24 +0100
Subject: [PATCH 0204/1016] More tabs
---
src/Roles.js | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/Roles.js b/src/Roles.js
index 5af8be76df..cdfc156423 100644
--- a/src/Roles.js
+++ b/src/Roles.js
@@ -16,8 +16,8 @@ limitations under the License.
import { _t } from './languageHandler';
export function levelRoleMap() {
- const LEVEL_ROLE_MAP = {};
- LEVEL_ROLE_MAP[undefined] = _t('Default');
+ const LEVEL_ROLE_MAP = {};
+ LEVEL_ROLE_MAP[undefined] = _t('Default');
LEVEL_ROLE_MAP[0] = _t('User');
LEVEL_ROLE_MAP[50] = _t('Moderator');
LEVEL_ROLE_MAP[100] = _t('Admin');
@@ -25,7 +25,7 @@ export function levelRoleMap() {
}
export function textualPowerLevel(level, userDefault) {
- const LEVEL_ROLE_MAP = this.levelRoleMap();
+ const LEVEL_ROLE_MAP = this.levelRoleMap();
if (LEVEL_ROLE_MAP[level]) {
return LEVEL_ROLE_MAP[level] + (level !== undefined ? ` (${level})` : ` (${userDefault})`);
} else {
From 91edc064416c202de31bf18ccfcc14b499ce6ee0 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Thu, 25 May 2017 17:04:42 +0100
Subject: [PATCH 0205/1016] Use RVS to indicate "joining" when setting a mxid
This prevents RoomView from doing any peeking whilst the join/registration is in progress, causing weirdness with TimelinePanel getPendingEventList (which throws an error if called when peeking).
---
src/components/structures/RoomView.js | 14 ++++++++++----
src/stores/RoomViewStore.js | 11 ++++++++++-
2 files changed, 20 insertions(+), 5 deletions(-)
diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js
index e5e38a33d8..6e2a7df5ac 100644
--- a/src/components/structures/RoomView.js
+++ b/src/components/structures/RoomView.js
@@ -671,10 +671,6 @@ module.exports = React.createClass({
// compatability workaround, let's not bother.
Rooms.setDMRoom(this.state.room.roomId, me.events.member.getSender()).done();
}
-
- this.setState({
- joining: false
- });
}
}, 500),
@@ -762,12 +758,22 @@ module.exports = React.createClass({
},
});
+ // Don't peek whilst registering otherwise getPendingEventList complains
+ // Do this by indicating our intention to join
+ dis.dispatch({
+ action: 'will_join',
+ });
+
const SetMxIdDialog = sdk.getComponent('views.dialogs.SetMxIdDialog');
const close = Modal.createDialog(SetMxIdDialog, {
homeserverUrl: cli.getHomeserverUrl(),
onFinished: (submitted, credentials) => {
if (submitted) {
this.props.onRegistered(credentials);
+ } else {
+ dis.dispatch({
+ action: 'cancel_join',
+ });
}
},
onDifferentServerClicked: (ev) => {
diff --git a/src/stores/RoomViewStore.js b/src/stores/RoomViewStore.js
index fe57079859..1ceef551a8 100644
--- a/src/stores/RoomViewStore.js
+++ b/src/stores/RoomViewStore.js
@@ -58,7 +58,16 @@ class RoomViewStore extends Store {
case 'view_room':
this._viewRoom(payload);
break;
-
+ case 'will_join':
+ this._setState({
+ joining: true,
+ });
+ break;
+ case 'cancel_join':
+ this._setState({
+ joining: false,
+ });
+ break;
// join_room:
// - opts: options for joinRoom
case 'join_room':
From 0849b0e20527ae3c2abf563a0232609ae78550cc Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Thu, 25 May 2017 17:10:49 +0100
Subject: [PATCH 0206/1016] Fix view_next_room, view_previous_room and
view_indexed_room
These must now make a dispatch to RoomViewStore instead of calling `viewRoom` directly on MatrixChat. This will call both `viewRoom` of MatrixChat _and_ the logic in RVS so there is some redundancy here. It'd be best to move as much as possible of viewRoom out to the RVS itself.
But for now, this fixes a bug that occures when leaving (the viewed room would not change).
---
src/components/structures/MatrixChat.js | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index a6f2ee820f..2e4a3b90ad 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -553,6 +553,7 @@ module.exports = React.createClass({
this.notifyNewScreen('register');
},
+ // TODO: Move to RoomViewStore
_viewNextRoom: function(roomIndexDelta) {
const allRooms = RoomListSorter.mostRecentActivityFirst(
MatrixClientPeg.get().getRooms(),
@@ -566,15 +567,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,
+ });
}
},
From 263a51938d894896306fab155737aecc7c74b04a Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Thu, 25 May 2017 17:16:16 +0100
Subject: [PATCH 0207/1016] Reset store state when logging out
This prevents leaking of state that we do not want to share with the next user
---
src/stores/LifecycleStore.js | 5 +++++
src/stores/RoomViewStore.js | 3 +++
src/stores/SessionStore.js | 5 +++++
3 files changed, 13 insertions(+)
diff --git a/src/stores/LifecycleStore.js b/src/stores/LifecycleStore.js
index 43e2de9d52..5dfe82500a 100644
--- a/src/stores/LifecycleStore.js
+++ b/src/stores/LifecycleStore.js
@@ -61,6 +61,11 @@ class LifecycleStore extends Store {
});
dis.dispatch(deferredAction);
break;
+ case 'on_logged_out':
+ this._state = {
+ deferred_action: null,
+ };
+ break;
}
}
}
diff --git a/src/stores/RoomViewStore.js b/src/stores/RoomViewStore.js
index 1ceef551a8..d893318af7 100644
--- a/src/stores/RoomViewStore.js
+++ b/src/stores/RoomViewStore.js
@@ -73,6 +73,9 @@ class RoomViewStore extends Store {
case 'join_room':
this._joinRoom(payload);
break;
+ case 'on_logged_out':
+ this.reset();
+ break;
}
}
diff --git a/src/stores/SessionStore.js b/src/stores/SessionStore.js
index 2fd35ce40a..5713e4d321 100644
--- a/src/stores/SessionStore.js
+++ b/src/stores/SessionStore.js
@@ -66,6 +66,11 @@ class SessionStore extends Store {
cachedPassword: null,
});
break;
+ case 'on_logged_out':
+ this._state = {
+ cachedPassword: null,
+ };
+ break;
}
}
From 85756c78b475c9af29d8a36e4c4d5804b8e17798 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Thu, 25 May 2017 17:17:37 +0100
Subject: [PATCH 0208/1016] Change to object literal
---
src/Roles.js | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/Roles.js b/src/Roles.js
index cdfc156423..8c1f711bbe 100644
--- a/src/Roles.js
+++ b/src/Roles.js
@@ -16,12 +16,12 @@ limitations under the License.
import { _t } from './languageHandler';
export function levelRoleMap() {
- const LEVEL_ROLE_MAP = {};
- LEVEL_ROLE_MAP[undefined] = _t('Default');
- LEVEL_ROLE_MAP[0] = _t('User');
- LEVEL_ROLE_MAP[50] = _t('Moderator');
- LEVEL_ROLE_MAP[100] = _t('Admin');
- return LEVEL_ROLE_MAP;
+ return {
+ undefined: _t('Default'),
+ 0: _t('User'),
+ 50: _t('Moderator'),
+ 100: _t('Admin'),
+ };
}
export function textualPowerLevel(level, userDefault) {
From 6d67655f6648627f78709520017a98646ebd6fd2 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Thu, 25 May 2017 17:22:04 +0100
Subject: [PATCH 0209/1016] Add german for Change password
---
src/i18n/strings/de_DE.json | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json
index 1c6b3eb5f3..bf55379d6d 100644
--- a/src/i18n/strings/de_DE.json
+++ b/src/i18n/strings/de_DE.json
@@ -37,6 +37,7 @@
"Joins room with given alias": "Betrete Raum mit angegebenen Alias",
"Kicks user with given id": "Kickt Benutzer mit angegebener ID",
"Changes your display nickname": "Ändert deinen angezeigten Nicknamen",
+ "Change Password": "Passwort ändern",
"Searches DuckDuckGo for results": "Suche in DuckDuckGo nach Ergebnissen",
"Commands": "Kommandos",
"Emoji": "Smileys",
From 9112fc231af6cf9d7bb913b321bacf70fda87e5a Mon Sep 17 00:00:00 2001
From: David Baker
Date: Thu, 25 May 2017 17:35:18 +0100
Subject: [PATCH 0210/1016] More translation string fixups
---
src/components/structures/UserSettings.js | 12 ++++++------
src/i18n/strings/en_EN.json | 10 +++++++---
2 files changed, 13 insertions(+), 9 deletions(-)
diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js
index 279920eeb7..b669898cd7 100644
--- a/src/components/structures/UserSettings.js
+++ b/src/components/structures/UserSettings.js
@@ -724,7 +724,7 @@ module.exports = React.createClass({
{ _t("Found a bug?") }
Report it
+ onClick={this._onBugReportClicked}>{_t('Report it')}
@@ -1031,10 +1031,10 @@ module.exports = React.createClass({
- { _t("Logged in as") } {this._me}
+ { _t("Logged in as:") } {this._me}
- Access Token: <{ _t("click to reveal") }>
+ {_t('Access Token:')} <{ _t("click to reveal") }>
{ _t("Homeserver is") } { MatrixClientPeg.get().getHomeserverUrl() }
@@ -1043,15 +1043,15 @@ module.exports = React.createClass({
{ _t("Identity Server is") } { MatrixClientPeg.get().getIdentityServerUrl() }
- matrix-react-sdk version: {(REACT_SDK_VERSION !== '')
+ {_t('matrix-react-sdk version:')} {(REACT_SDK_VERSION !== '')
? gHVersionLabel('matrix-org/matrix-react-sdk', REACT_SDK_VERSION)
: REACT_SDK_VERSION
}
- riot-web version: {(this.state.vectorVersion !== undefined)
+ {_t('riot-web version:')} {(this.state.vectorVersion !== undefined)
? gHVersionLabel('vector-im/riot-web', this.state.vectorVersion)
: 'unknown'
}
- { _t("olm version: ") } {olmVersionString}
+ { _t("olm version:") } {olmVersionString}
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index fcbd3d737f..e8fffbe80d 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -126,6 +126,7 @@
"%(targetName)s accepted an invitation": "%(targetName)s accepted an invitation",
"%(targetName)s accepted the invitation for %(displayName)s": "%(targetName)s accepted the invitation for %(displayName)s",
"Account": "Account",
+ "Access Token:": "Access Token:",
"Add email address": "Add email address",
"Add phone number": "Add phone number",
"Admin": "Admin",
@@ -283,7 +284,7 @@
"I have verified my email address": "I have verified my email address",
"Import E2E room keys": "Import E2E room keys",
"Incorrect verification code": "Incorrect verification code",
- "Interface language": "Interface language",
+ "Interface Language": "Interface Language",
"Invalid alias format": "Invalid alias format",
"Invalid address format": "Invalid address format",
"Invalid Email Address": "Invalid Email Address",
@@ -311,7 +312,7 @@
"%(targetName)s left the room": "%(targetName)s left the room",
"Level": "Level",
"Local addresses for this room:": "Local addresses for this room:",
- "Logged in as": "Logged in as",
+ "Logged in as:": "Logged in as:",
"Login as guest": "Login as guest",
"Logout": "Logout",
"Low priority": "Low priority",
@@ -319,6 +320,7 @@
"Manage Integrations": "Manage Integrations",
"Markdown is disabled": "Markdown is disabled",
"Markdown is enabled": "Markdown is enabled",
+ "matrix-react-sdk version:": "matrix-react-sdk version:",
"Members only": "Members only",
"Message not sent due to unknown devices being present": "Message not sent due to unknown devices being present",
"Missing room_id in request": "Missing room_id in request",
@@ -348,7 +350,7 @@
"No results": "No results",
"No users have specific privileges in this room": "No users have specific privileges in this room",
"OK": "OK",
- "olm version": "olm version",
+ "olm version:": "olm version:",
"Once encryption is enabled for a room it cannot be turned off again (for now)": "Once encryption is enabled for a room it cannot be turned off again (for now)",
"Once you've followed the link it contains, click below": "Once you've followed the link it contains, click below",
"Only people who have been invited": "Only people who have been invited",
@@ -377,12 +379,14 @@
"%(senderName)s removed their profile picture": "%(senderName)s removed their profile picture",
"Remove": "Remove",
"%(senderName)s requested a VoIP conference": "%(senderName)s requested a VoIP conference",
+ "Report it": "Report it",
"Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved ": "Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved ",
"restore": "restore",
"Return to app": "Return to app",
"Return to login screen": "Return to login screen",
"Riot does not have permission to send you notifications - please check your browser settings": "Riot does not have permission to send you notifications - please check your browser settings",
"Riot was not given permission to send notifications - please try again": "Riot was not given permission to send notifications - please try again",
+ "riot-web version:": "riot-web version:",
"Room %(roomId)s not visible": "Room %(roomId)s not visible",
"Room Colour": "Room Colour",
"Room name (optional)": "Room name (optional)",
From 443ab1add73390176478fc7ecd1a334aa157e833 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Thu, 25 May 2017 18:20:48 +0100
Subject: [PATCH 0211/1016] Put back default strings on dialogs
But make them work by calling _t in render rather than
getDefaultProps().
Also sort out some 'Warning!' strings
---
src/CallHandler.js | 15 +--------------
src/ContentMessages.js | 1 -
src/SlashCommands.js | 1 -
src/components/structures/MatrixChat.js | 3 ---
src/components/structures/RoomView.js | 9 +--------
src/components/structures/TimelinePanel.js | 1 -
src/components/structures/UserSettings.js | 8 --------
src/components/structures/login/ForgotPassword.js | 3 +--
src/components/views/dialogs/ChatInviteDialog.js | 4 ----
src/components/views/dialogs/ErrorDialog.js | 10 +++++++---
.../views/dialogs/InteractiveAuthDialog.js | 3 ++-
.../views/dialogs/NeedToRegisterDialog.js | 5 +++--
src/components/views/dialogs/QuestionDialog.js | 3 ++-
src/components/views/login/RegistrationForm.js | 2 +-
.../views/room_settings/AliasSettings.js | 2 --
src/components/views/rooms/MemberInfo.js | 7 +------
src/components/views/rooms/MessageComposer.js | 1 -
.../views/rooms/MessageComposerInput.js | 2 --
.../views/rooms/MessageComposerInputOld.js | 3 ---
src/components/views/rooms/RoomHeader.js | 1 -
src/components/views/rooms/RoomSettings.js | 5 +----
src/components/views/settings/AddPhoneNumber.js | 1 -
src/components/views/settings/ChangePassword.js | 2 +-
src/createRoom.js | 1 -
src/i18n/strings/da.json | 2 +-
src/i18n/strings/de_DE.json | 2 +-
src/i18n/strings/en_EN.json | 6 +++++-
src/i18n/strings/pt.json | 2 +-
src/i18n/strings/pt_BR.json | 2 +-
src/i18n/strings/ru.json | 2 +-
30 files changed, 31 insertions(+), 78 deletions(-)
diff --git a/src/CallHandler.js b/src/CallHandler.js
index 5edeba7625..c36ca382f2 100644
--- a/src/CallHandler.js
+++ b/src/CallHandler.js
@@ -145,7 +145,6 @@ function _setCallListeners(call) {
Modal.createDialog(ErrorDialog, {
title: _t('Call Timeout'),
description: _t('The remote side failed to pick up') + '.',
- button: _t("OK"),
});
}
else if (oldState === "invite_sent") {
@@ -207,7 +206,6 @@ function _onAction(payload) {
Modal.createDialog(ErrorDialog, {
title: _t('Unable to capture screen'),
description: screenCapErrorString,
- button: _t("OK"),
});
return;
}
@@ -228,7 +226,6 @@ function _onAction(payload) {
Modal.createDialog(ErrorDialog, {
title: _t('Existing Call'),
description: _t('You are already in a call') + '.',
- button: _t("OK"),
});
return; // don't allow >1 call to be placed.
}
@@ -239,7 +236,6 @@ function _onAction(payload) {
Modal.createDialog(ErrorDialog, {
title: _t('VoIP is unsupported'),
description: _t('You cannot place VoIP calls in this browser') + '.',
- button: _t("OK"),
});
return;
}
@@ -255,8 +251,6 @@ function _onAction(payload) {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
description: _t('You cannot place a call with yourself') + '.',
- title: _t("Error"),
- button: _t("OK"),
});
return;
}
@@ -283,8 +277,6 @@ function _onAction(payload) {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
description: _t('Conference calls are not supported in this client'),
- title: _t("Error"),
- button: _t("OK"),
});
}
else if (!MatrixClientPeg.get().supportsVoip()) {
@@ -292,7 +284,6 @@ function _onAction(payload) {
Modal.createDialog(ErrorDialog, {
title: _t('VoIP is unsupported'),
description: _t('You cannot place VoIP calls in this browser') + '.',
- button: _t("OK"),
});
}
else if (MatrixClientPeg.get().isRoomEncrypted(payload.room_id)) {
@@ -304,17 +295,14 @@ function _onAction(payload) {
// Therefore we disable conference calling in E2E rooms.
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
- title: _t("Error"),
- button: _t("OK"),
description: _t('Conference calls are not supported in encrypted rooms'),
});
}
else {
var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createDialog(QuestionDialog, {
- title: _t('Warning') + '!',
+ title: _t('Warning!'),
description: _t('Conference calling is in development and may not be reliable') + '.',
- button: _t("OK"),
onFinished: confirm=>{
if (confirm) {
ConferenceHandler.createNewMatrixCall(
@@ -327,7 +315,6 @@ function _onAction(payload) {
Modal.createDialog(ErrorDialog, {
title: _t('Failed to set up conference call'),
description: _t('Conference call failed') + '. ' + ((err && err.message) ? err.message : ''),
- button: _t("OK"),
});
});
}
diff --git a/src/ContentMessages.js b/src/ContentMessages.js
index d4f8cae276..315c312b9f 100644
--- a/src/ContentMessages.js
+++ b/src/ContentMessages.js
@@ -356,7 +356,6 @@ class ContentMessages {
Modal.createDialog(ErrorDialog, {
title: _t('Upload Failed'),
description: desc,
- button: _t("OK"),
});
}
}).finally(() => {
diff --git a/src/SlashCommands.js b/src/SlashCommands.js
index 645ca6e8fe..366604eaee 100644
--- a/src/SlashCommands.js
+++ b/src/SlashCommands.js
@@ -71,7 +71,6 @@ const commands = {
Modal.createDialog(ErrorDialog, {
title: _t('/ddg is not a command'),
description: _t('To use it, just wait for autocomplete results to load and tab through them') + '.',
- button: _t("OK"),
});
return success();
}),
diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index a00d9c22ce..2ba1506551 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -378,7 +378,6 @@ module.exports = React.createClass({
Modal.createDialog(QuestionDialog, {
title: _t('Reject invitation'),
description: _t('Are you sure you want to reject the invitation?'),
- button: "OK",
onFinished: (confirm) => {
if (confirm) {
// FIXME: controller shouldn't be loading a view :(
@@ -395,7 +394,6 @@ module.exports = React.createClass({
Modal.createDialog(ErrorDialog, {
title: _t('Failed to reject invitation'),
description: err.toString(),
- button: _t("OK"),
});
});
}
@@ -895,7 +893,6 @@ module.exports = React.createClass({
Modal.createDialog(ErrorDialog, {
title: _t('Signed Out'),
description: _t('For security, this session has been signed out. Please sign in again.'),
- button: _t("OK"),
});
dis.dispatch({
action: 'logout',
diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js
index f0b6160012..badc7cd01d 100644
--- a/src/components/structures/RoomView.js
+++ b/src/components/structures/RoomView.js
@@ -531,7 +531,7 @@ module.exports = React.createClass({
if (!userHasUsedEncryption) {
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createDialog(QuestionDialog, {
- title: _t("Warning") + "!",
+ title: _t("Warning!"),
hasCancelButton: false,
description: (
@@ -541,7 +541,6 @@ module.exports = React.createClass({
{ _t("Encrypted messages will not be visible on clients that do not yet implement encryption") }.
),
- button: "OK",
});
}
if (localStorage) {
@@ -882,7 +881,6 @@ module.exports = React.createClass({
Modal.createDialog(ErrorDialog, {
title: _t("Failed to join room"),
description: msg,
- button: _t("OK"),
});
}
}).done();
@@ -964,7 +962,6 @@ module.exports = React.createClass({
Modal.createDialog(ErrorDialog, {
title: _t('Failed to upload file'),
description: ((error && error.message) ? error.message : _t("Server may be unavailable, overloaded, or the file too big")),
- button: _t("OK"),
});
});
},
@@ -1052,7 +1049,6 @@ module.exports = React.createClass({
Modal.createDialog(ErrorDialog, {
title: _t("Search failed"),
description: ((error && error.message) ? error.message : _t("Server may be unavailable, overloaded, or search timed out :(")),
- button: _t("OK"),
});
}).finally(function() {
self.setState({
@@ -1180,7 +1176,6 @@ module.exports = React.createClass({
Modal.createDialog(ErrorDialog, {
title: _t("Failed to save settings"),
description: fails.map(function(result) { return result.reason; }).join("\n"),
- button: _t("OK"),
});
// still editing room settings
}
@@ -1220,7 +1215,6 @@ module.exports = React.createClass({
Modal.createDialog(ErrorDialog, {
title: _t("Error"),
description: _t("Failed to forget room %(errCode)s", { errCode: errCode }),
- button: _t("OK"),
});
});
},
@@ -1243,7 +1237,6 @@ module.exports = React.createClass({
Modal.createDialog(ErrorDialog, {
title: _t("Failed to reject invite"),
description: msg,
- button: _t("OK"),
});
self.setState({
diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js
index 5719964df5..60c8c25114 100644
--- a/src/components/structures/TimelinePanel.js
+++ b/src/components/structures/TimelinePanel.js
@@ -914,7 +914,6 @@ var TimelinePanel = React.createClass({
Modal.createDialog(ErrorDialog, {
title: _t("Failed to load timeline position"),
description: message,
- button: _t("OK"),
onFinished: onFinished,
});
};
diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js
index b669898cd7..b2bf693cfe 100644
--- a/src/components/structures/UserSettings.js
+++ b/src/components/structures/UserSettings.js
@@ -249,7 +249,6 @@ module.exports = React.createClass({
Modal.createDialog(ErrorDialog, {
title: _t("Can't load user settings"),
description: ((error && error.message) ? error.message : _t("Server may be unavailable or overloaded")),
- button: _t("OK"),
});
});
},
@@ -292,7 +291,6 @@ module.exports = React.createClass({
Modal.createDialog(ErrorDialog, {
title: _t("Failed to set avatar"),
description: ((err && err.message) ? err.message : _t("Operation failed")),
- button: _t("OK")
});
});
},
@@ -335,7 +333,6 @@ module.exports = React.createClass({
Modal.createDialog(ErrorDialog, {
title: _t("Error"),
description: errMsg,
- button: _t("OK"),
});
},
@@ -344,7 +341,6 @@ module.exports = React.createClass({
Modal.createDialog(ErrorDialog, {
title: _t("Success"),
description: _t("Your password was successfully changed. You will not receive push notifications on other devices until you log back in to them") + ".",
- button: _t("OK"),
});
},
@@ -372,7 +368,6 @@ module.exports = React.createClass({
Modal.createDialog(ErrorDialog, {
title: _t("Invalid Email Address"),
description: _t("This doesn't appear to be a valid email address"),
- button: _t("OK"),
});
return;
}
@@ -392,7 +387,6 @@ module.exports = React.createClass({
Modal.createDialog(ErrorDialog, {
title: _t("Unable to add email address"),
description: ((err && err.message) ? err.message : _t("Operation failed")),
- button: _t("OK"),
});
});
ReactDOM.findDOMNode(this.refs.add_email_input).blur();
@@ -418,7 +412,6 @@ module.exports = React.createClass({
Modal.createDialog(ErrorDialog, {
title: _t("Unable to remove contact information"),
description: ((err && err.message) ? err.message : _t("Operation failed")),
- button: _t("OK"),
});
}).done();
}
@@ -460,7 +453,6 @@ module.exports = React.createClass({
Modal.createDialog(ErrorDialog, {
title: _t("Unable to verify email address"),
description: ((err && err.message) ? err.message : _t("Operation failed")),
- button: _t("OK"),
});
}
});
diff --git a/src/components/structures/login/ForgotPassword.js b/src/components/structures/login/ForgotPassword.js
index 5438b123c3..c7dc35c1a8 100644
--- a/src/components/structures/login/ForgotPassword.js
+++ b/src/components/structures/login/ForgotPassword.js
@@ -90,7 +90,7 @@ module.exports = React.createClass({
else {
var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createDialog(QuestionDialog, {
- title: _t('Warning'),
+ title: _t('Warning!'),
description:
{ _t(
@@ -157,7 +157,6 @@ module.exports = React.createClass({
Modal.createDialog(ErrorDialog, {
title: title,
description: body,
- button: _t("OK"),
});
},
diff --git a/src/components/views/dialogs/ChatInviteDialog.js b/src/components/views/dialogs/ChatInviteDialog.js
index 81d032683e..ca0f10bc6b 100644
--- a/src/components/views/dialogs/ChatInviteDialog.js
+++ b/src/components/views/dialogs/ChatInviteDialog.js
@@ -307,7 +307,6 @@ module.exports = React.createClass({
Modal.createDialog(ErrorDialog, {
title: "Failed to invite",
description: ((err && err.message) ? err.message : "Operation failed"),
- button: _t("OK"),
});
return null;
})
@@ -321,7 +320,6 @@ module.exports = React.createClass({
Modal.createDialog(ErrorDialog, {
title: "Failed to invite user",
description: ((err && err.message) ? err.message : "Operation failed"),
- button: _t("OK"),
});
return null;
})
@@ -343,7 +341,6 @@ module.exports = React.createClass({
Modal.createDialog(ErrorDialog, {
title: "Failed to invite",
description: ((err && err.message) ? err.message : "Operation failed"),
- button: _t("OK"),
});
return null;
})
@@ -403,7 +400,6 @@ module.exports = React.createClass({
Modal.createDialog(ErrorDialog, {
title: "Failed to invite the following users to the " + room.name + " room:",
description: errorList.join(", "),
- button: _t("OK"),
});
}
return addrs;
diff --git a/src/components/views/dialogs/ErrorDialog.js b/src/components/views/dialogs/ErrorDialog.js
index fb7286e304..bf48d1757b 100644
--- a/src/components/views/dialogs/ErrorDialog.js
+++ b/src/components/views/dialogs/ErrorDialog.js
@@ -27,6 +27,7 @@ limitations under the License.
import React from 'react';
import sdk from '../../../index';
+import { _t } from '../../../languageHandler';
export default React.createClass({
displayName: 'ErrorDialog',
@@ -44,6 +45,9 @@ export default React.createClass({
getDefaultProps: function() {
return {
focus: true,
+ title: null,
+ description: null,
+ button: null,
};
},
@@ -57,13 +61,13 @@ export default React.createClass({
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
return (
+ title={this.props.title || _t('Error')}>
- {this.props.description}
+ {this.props.description || _t('An error has occurred.')}
- {this.props.button}
+ {this.props.button || _t('OK')}
diff --git a/src/components/views/dialogs/InteractiveAuthDialog.js b/src/components/views/dialogs/InteractiveAuthDialog.js
index f0eeead29a..51561270c4 100644
--- a/src/components/views/dialogs/InteractiveAuthDialog.js
+++ b/src/components/views/dialogs/InteractiveAuthDialog.js
@@ -20,6 +20,7 @@ import Matrix from 'matrix-js-sdk';
import React from 'react';
import sdk from '../../../index';
+import { _t } from '../../../languageHandler';
import AccessibleButton from '../elements/AccessibleButton';
@@ -99,7 +100,7 @@ export default React.createClass({
return (
{content}
diff --git a/src/components/views/dialogs/NeedToRegisterDialog.js b/src/components/views/dialogs/NeedToRegisterDialog.js
index 875af6a7fd..4a07a65529 100644
--- a/src/components/views/dialogs/NeedToRegisterDialog.js
+++ b/src/components/views/dialogs/NeedToRegisterDialog.js
@@ -26,6 +26,7 @@ limitations under the License.
import React from 'react';
import dis from '../../../dispatcher';
import sdk from '../../../index';
+import { _t } from '../../../languageHandler';
module.exports = React.createClass({
displayName: 'NeedToRegisterDialog',
@@ -52,10 +53,10 @@ module.exports = React.createClass({
return (
- {this.props.description}
+ {this.props.description || _t('A registered account is required for this action')}
diff --git a/src/components/views/dialogs/QuestionDialog.js b/src/components/views/dialogs/QuestionDialog.js
index e3d38922ee..bd7046232b 100644
--- a/src/components/views/dialogs/QuestionDialog.js
+++ b/src/components/views/dialogs/QuestionDialog.js
@@ -16,6 +16,7 @@ limitations under the License.
import React from 'react';
import sdk from '../../../index';
+import { _t } from '../../../languageHandler';
export default React.createClass({
displayName: 'QuestionDialog',
@@ -63,7 +64,7 @@ export default React.createClass({
- {this.props.button}
+ {this.props.button || _t('OK')}
{this.props.extraButtons}
{cancelButton}
diff --git a/src/components/views/login/RegistrationForm.js b/src/components/views/login/RegistrationForm.js
index e55a224531..7ca5de72cd 100644
--- a/src/components/views/login/RegistrationForm.js
+++ b/src/components/views/login/RegistrationForm.js
@@ -100,7 +100,7 @@ module.exports = React.createClass({
if (this.refs.email.value == '') {
var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createDialog(QuestionDialog, {
- title: "Warning",
+ title: "Warning!",
description:
If you don't specify an email address, you won't be able to reset your password.
diff --git a/src/components/views/room_settings/AliasSettings.js b/src/components/views/room_settings/AliasSettings.js
index d8766c637d..b5ef1d8309 100644
--- a/src/components/views/room_settings/AliasSettings.js
+++ b/src/components/views/room_settings/AliasSettings.js
@@ -157,7 +157,6 @@ module.exports = React.createClass({
Modal.createDialog(ErrorDialog, {
title: _t('Invalid alias format'),
description: _t('"%(alias)s" is not a valid format for an alias', { alias: alias }),
- button: _t('OK'),
});
}
},
@@ -174,7 +173,6 @@ module.exports = React.createClass({
Modal.createDialog(ErrorDialog, {
title: _t('Invalid address format'),
description: _t('"%(alias)s" is not a valid format for an address', { alias: alias }),
- button: _t('OK'),
});
}
},
diff --git a/src/components/views/rooms/MemberInfo.js b/src/components/views/rooms/MemberInfo.js
index e76d5387d6..86d39f91ec 100644
--- a/src/components/views/rooms/MemberInfo.js
+++ b/src/components/views/rooms/MemberInfo.js
@@ -244,7 +244,6 @@ module.exports = WithMatrixClient(React.createClass({
Modal.createDialog(ErrorDialog, {
title: _t("Failed to kick"),
description: ((err && err.message) ? err.message : "Operation failed"),
- button: _t("OK"),
});
}
).finally(()=>{
@@ -287,7 +286,6 @@ module.exports = WithMatrixClient(React.createClass({
Modal.createDialog(ErrorDialog, {
title: _t("Error"),
description: _t("Failed to ban user"),
- button: _t("OK"),
});
}
).finally(()=>{
@@ -338,7 +336,6 @@ module.exports = WithMatrixClient(React.createClass({
Modal.createDialog(ErrorDialog, {
title: _t("Error"),
description: _t("Failed to mute user"),
- button: _t("OK"),
});
}
).finally(()=>{
@@ -388,7 +385,6 @@ module.exports = WithMatrixClient(React.createClass({
Modal.createDialog(ErrorDialog, {
title: _t("Error"),
description: _t("Failed to toggle moderator status"),
- button: _t("OK"),
});
}
}
@@ -410,7 +406,6 @@ module.exports = WithMatrixClient(React.createClass({
Modal.createDialog(ErrorDialog, {
title: _t("Error"),
description: _t("Failed to change power level"),
- button: _t("OK"),
});
}
).finally(()=>{
@@ -438,7 +433,7 @@ module.exports = WithMatrixClient(React.createClass({
if (parseInt(myPower) === parseInt(powerLevel)) {
var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createDialog(QuestionDialog, {
- title: _t("Warning"),
+ title: _t("Warning!"),
description:
{ _t("You will not be able to undo this change as you are promoting the user to have the same power level as yourself") }.
diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js
index 488d3cc287..99b16413b5 100644
--- a/src/components/views/rooms/MessageComposer.js
+++ b/src/components/views/rooms/MessageComposer.js
@@ -127,7 +127,6 @@ export default class MessageComposer extends React.Component {
),
- button: _t("OK"),
onFinished: (shouldUpload) => {
if(shouldUpload) {
// MessageComposer shouldn't have to rely on its parent passing in a callback to upload a file
diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js
index c52f9724b0..a79f71c92d 100644
--- a/src/components/views/rooms/MessageComposerInput.js
+++ b/src/components/views/rooms/MessageComposerInput.js
@@ -507,7 +507,6 @@ export default class MessageComposerInput extends React.Component {
Modal.createDialog(ErrorDialog, {
title: _t("Server error"),
description: ((err && err.message) ? err.message : _t("Server unavailable, overloaded, or something else went wrong") + "."),
- button: _t("OK"),
});
});
}
@@ -517,7 +516,6 @@ export default class MessageComposerInput extends React.Component {
Modal.createDialog(ErrorDialog, {
title: _t("Command error"),
description: cmd.error,
- button: _t("OK"),
});
}
return true;
diff --git a/src/components/views/rooms/MessageComposerInputOld.js b/src/components/views/rooms/MessageComposerInputOld.js
index 9b4efa272c..b664ef4bd3 100644
--- a/src/components/views/rooms/MessageComposerInputOld.js
+++ b/src/components/views/rooms/MessageComposerInputOld.js
@@ -297,7 +297,6 @@ export default React.createClass({
Modal.createDialog(ErrorDialog, {
title: _t("Unknown command"),
description: _t("Usage") + ": /markdown on|off",
- button: _t("OK"),
});
}
return;
@@ -318,7 +317,6 @@ export default React.createClass({
Modal.createDialog(ErrorDialog, {
title: _t("Server error"),
description: ((err && err.message) ? err.message : _t("Server unavailable, overloaded, or something else went wrong") + "."),
- button: _t("OK"),
});
});
}
@@ -328,7 +326,6 @@ export default React.createClass({
Modal.createDialog(ErrorDialog, {
title: _t("Command error"),
description: cmd.error,
- button: _t("OK"),
});
}
return;
diff --git a/src/components/views/rooms/RoomHeader.js b/src/components/views/rooms/RoomHeader.js
index 38bd63e019..c5e3a56922 100644
--- a/src/components/views/rooms/RoomHeader.js
+++ b/src/components/views/rooms/RoomHeader.js
@@ -122,7 +122,6 @@ module.exports = React.createClass({
Modal.createDialog(ErrorDialog, {
title: _t("Error"),
description: _t("Failed to set avatar") + ".",
- button: _t("OK"),
});
}).done();
},
diff --git a/src/components/views/rooms/RoomSettings.js b/src/components/views/rooms/RoomSettings.js
index df70513b7a..bff668ac85 100644
--- a/src/components/views/rooms/RoomSettings.js
+++ b/src/components/views/rooms/RoomSettings.js
@@ -59,7 +59,6 @@ const BannedUser = React.createClass({
Modal.createDialog(ErrorDialog, {
title: _t('Error'),
description: _t('Failed to unban'),
- button: _t("OK"),
});
}).done();
},
@@ -530,7 +529,6 @@ module.exports = React.createClass({
Modal.createDialog(ErrorDialog, {
title: _t('Error'),
description: _t("Failed to forget room %(errCode)s", { errCode: errCode }),
- button: _t("OK"),
});
});
},
@@ -540,7 +538,7 @@ module.exports = React.createClass({
var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createDialog(QuestionDialog, {
- title: _t('Warning') + '!',
+ title: _t('Warning!'),
description: (
{ _t('End-to-end encryption is in beta and may not be reliable') }.
@@ -550,7 +548,6 @@ module.exports = React.createClass({
{ _t('Encrypted messages will not be visible on clients that do not yet implement encryption') }.
),
- button: "OK",
onFinished: confirm=>{
if (!confirm) {
this.refs.encrypt.checked = false;
diff --git a/src/components/views/settings/AddPhoneNumber.js b/src/components/views/settings/AddPhoneNumber.js
index 697a539bcd..1fdd06cf00 100644
--- a/src/components/views/settings/AddPhoneNumber.js
+++ b/src/components/views/settings/AddPhoneNumber.js
@@ -85,7 +85,6 @@ export default WithMatrixClient(React.createClass({
Modal.createDialog(ErrorDialog, {
title: _t("Error"),
description: msg,
- button: _t("OK"),
});
}).finally(() => {
if (this._unmounted) return;
diff --git a/src/components/views/settings/ChangePassword.js b/src/components/views/settings/ChangePassword.js
index e0fe6ce56a..33091f9c71 100644
--- a/src/components/views/settings/ChangePassword.js
+++ b/src/components/views/settings/ChangePassword.js
@@ -70,7 +70,7 @@ module.exports = React.createClass({
var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createDialog(QuestionDialog, {
- title: _t("Warning"),
+ title: _t("Warning!"),
description:
{ _t(
diff --git a/src/createRoom.js b/src/createRoom.js
index fe148f0408..0a21f68208 100644
--- a/src/createRoom.js
+++ b/src/createRoom.js
@@ -107,7 +107,6 @@ function createRoom(opts) {
Modal.createDialog(ErrorDialog, {
title: _t("Failure to create room"),
description: _t("Server may be unavailable, overloaded, or you hit a bug") + ".",
- button: _t("OK"),
});
return null;
});
diff --git a/src/i18n/strings/da.json b/src/i18n/strings/da.json
index 26889c3bb6..2b30593a4c 100644
--- a/src/i18n/strings/da.json
+++ b/src/i18n/strings/da.json
@@ -48,7 +48,7 @@
"Create a new account": "Oprette en ny bruger",
"Send an encrypted message": "Send en krypteret meddelelse",
"Send a message (unencrypted)": "Send en meddelelse (ukrypteret)",
- "Warning": "Advarsel",
+ "Warning!": "Advarsel!",
"accept": "acceptere",
"accepted an invitation": "Godkendt en invitation",
"accepted the invitation for": "Accepteret invitationen til",
diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json
index bf55379d6d..421a8e1f41 100644
--- a/src/i18n/strings/de_DE.json
+++ b/src/i18n/strings/de_DE.json
@@ -48,7 +48,7 @@
"Create a new account": "Erstelle einen neuen Benutzer",
"Send an encrypted message": "Sende eine verschlüsselte Nachricht",
"Send a message (unencrypted)": "Sende eine Nachricht (unverschlüsselt)",
- "Warning": "Warnung",
+ "Warning!": "Warnung!",
"Direct Chat": "Privater Chat",
"Error": "Fehler",
"accept": "akzeptiere",
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index e8fffbe80d..8f0a8018b0 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -121,6 +121,7 @@
"zh-sg":"Chinese (Singapore)",
"zh-tw":"Chinese (Taiwan)",
"zu":"Zulu",
+ "A registered account is required for this action": "A registered account is required for this action",
"A text message has been sent to +%(msisdn)s. Please enter the verification code it contains": "A text message has been sent to +%(msisdn)s. Please enter the verification code it contains",
"accept": "accept",
"%(targetName)s accepted an invitation": "%(targetName)s accepted an invitation",
@@ -132,6 +133,7 @@
"Admin": "Admin",
"Advanced": "Advanced",
"Algorithm": "Algorithm",
+ "Authentication": "Authentication",
"all room members": "all room members",
"all room members, from the point they are invited": "all room members, from the point they are invited",
"all room members, from the point they joined": "all room members, from the point they joined",
@@ -149,6 +151,7 @@
"A new password must be entered.": "A new password must be entered.",
"%(senderName)s answered the call": "%(senderName)s answered the call",
"anyone.": "anyone",
+ "An error has occurred.": "An error has occurred.",
"Anyone who knows the room's link, apart from guests": "Anyone who knows the room's link, apart from guests",
"Anyone who knows the room's link, including guests": "Anyone who knows the room's link, including guests",
"Are you sure?": "Are you sure?",
@@ -371,6 +374,7 @@
"Reason": "Reason",
"Revoke Moderator": "Revoke Moderator",
"Refer a friend to Riot": "Refer a friend to Riot",
+ "Registration required": "Registration required",
"rejected": "rejected",
"%(targetName)s rejected the invitation": "%(targetName)s rejected the invitation",
"Reject invitation": "Reject invitation",
@@ -513,7 +517,7 @@
"VoIP conference started": "VoIP conference started",
"VoIP is unsupported": "VoIP is unsupported",
"(warning: cannot be disabled again!)": "(warning: cannot be disabled again!)",
- "Warning": "Warning",
+ "Warning!": "Warning!",
"Who can access this room?": "Who can access this room?",
"Who can read history?": "Who can read history?",
"Who would you like to add to this room?": "Who would you like to add to this room?",
diff --git a/src/i18n/strings/pt.json b/src/i18n/strings/pt.json
index 60ab244401..e19d261997 100644
--- a/src/i18n/strings/pt.json
+++ b/src/i18n/strings/pt.json
@@ -257,7 +257,7 @@
"VoIP conference finished": "Conferência VoIP encerrada",
"VoIP conference started": "Conferência VoIP iniciada",
"(warning: cannot be disabled again!)": "(atenção: esta operação não poderá ser desfeita depois!)",
- "Warning": "Atenção",
+ "Warning!": "Atenção!",
"was banned": "banida/o",
"was invited": "convidada/o",
"was kicked": "retirada/o da sala",
diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json
index 3bd74ce14f..47d9271232 100644
--- a/src/i18n/strings/pt_BR.json
+++ b/src/i18n/strings/pt_BR.json
@@ -262,7 +262,7 @@
"VoIP conference finished": "Conferência VoIP encerrada",
"VoIP conference started": "Conferência VoIP iniciada",
"(warning: cannot be disabled again!)": "(atenção: esta operação não poderá ser desfeita depois!)",
- "Warning": "Atenção",
+ "Warning": "Atenção!",
"was banned": "banida/o",
"was invited": "convidada/o",
"was kicked": "retirada/o da sala",
diff --git a/src/i18n/strings/ru.json b/src/i18n/strings/ru.json
index 001a5aca6a..6bd3773861 100644
--- a/src/i18n/strings/ru.json
+++ b/src/i18n/strings/ru.json
@@ -191,7 +191,7 @@
"VoIP conference finished": "VoIP конференция закончилась",
"VoIP conference started": "VoIP Конференция стартовала",
"(warning: cannot be disabled again!)": "(предупреждение: не может быть снова отключен!)",
- "Warning": "Предупреждение",
+ "Warning!": "Предупреждение!",
"was banned": "запрещен",
"was invited": "приглашенный",
"was kicked": "выброшен",
From 6c41be3c140673e4f59243fa4f20455860c6f3e7 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Thu, 25 May 2017 18:23:15 +0100
Subject: [PATCH 0212/1016] Missed OKs
---
src/Notifier.js | 1 -
src/components/views/messages/MFileBody.js | 1 -
2 files changed, 2 deletions(-)
diff --git a/src/Notifier.js b/src/Notifier.js
index 8ab28e667e..eeedbcf365 100644
--- a/src/Notifier.js
+++ b/src/Notifier.js
@@ -141,7 +141,6 @@ const Notifier = {
Modal.createDialog(ErrorDialog, {
title: _t('Unable to enable Notifications'),
description,
- button: _t("OK"),
});
return;
}
diff --git a/src/components/views/messages/MFileBody.js b/src/components/views/messages/MFileBody.js
index 83b5793c9a..1bb9464999 100644
--- a/src/components/views/messages/MFileBody.js
+++ b/src/components/views/messages/MFileBody.js
@@ -286,7 +286,6 @@ module.exports = React.createClass({
Modal.createDialog(ErrorDialog, {
title: _t("Error"),
description: _t("Error decrypting attachment"),
- button: _t("OK"),
});
}).finally(() => {
decrypting = false;
From c3c291644965203e08b6887fb054ff1bb6919e4d Mon Sep 17 00:00:00 2001
From: David Baker
Date: Thu, 25 May 2017 18:26:42 +0100
Subject: [PATCH 0213/1016] Use array literals
---
src/DateUtils.js | 47 ++++++++++++++++++++++++-----------------------
1 file changed, 24 insertions(+), 23 deletions(-)
diff --git a/src/DateUtils.js b/src/DateUtils.js
index be5f432d06..46dda6aeb4 100644
--- a/src/DateUtils.js
+++ b/src/DateUtils.js
@@ -1,5 +1,6 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
+Copyright 2017 Vector Creations Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -18,32 +19,32 @@ limitations under the License.
import { _t } from './languageHandler';
function getDaysArray() {
- var days = [];
- days.push(_t('Sun'));
- days.push(_t('Mon'));
- days.push(_t('Tue'));
- days.push(_t('Wed'));
- days.push(_t('Thu'));
- days.push(_t('Fri'));
- days.push(_t('Sat'));
- return days;
+ return [
+ _t('Sun'),
+ _t('Mon'),
+ _t('Tue'),
+ _t('Wed'),
+ _t('Thu'),
+ _t('Fri'),
+ _t('Sat'),
+ ];
}
function getMonthsArray() {
- var months = [];
- months.push(_t('Jan'));
- months.push(_t('Feb'));
- months.push(_t('Mar'));
- months.push(_t('Apr'));
- months.push(_t('May'));
- months.push(_t('Jun'));
- months.push(_t('Jul'));
- months.push(_t('Aug'));
- months.push(_t('Sep'));
- months.push(_t('Oct'));
- months.push(_t('Nov'));
- months.push(_t('Dec'));
- return months;
+ return [
+ _t('Jan'),
+ _t('Feb'),
+ _t('Mar'),
+ _t('Apr'),
+ _t('May'),
+ _t('Jun'),
+ _t('Jul'),
+ _t('Aug'),
+ _t('Sep'),
+ _t('Oct'),
+ _t('Nov'),
+ _t('Dec'),
+ ];
}
function pad(n) {
From 43d8ccf1287f0efa6bf80d7e8a2f05a5f78ad357 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Thu, 25 May 2017 19:21:18 +0100
Subject: [PATCH 0214/1016] Hopefully sort out strings with appended '.'s
---
src/ScalarMessaging.js | 22 ++++++++++----------
src/SlashCommands.js | 2 +-
src/TextForEvent.js | 39 +++++++++++++++++++++++-------------
src/i18n/strings/da.json | 4 ++--
src/i18n/strings/de_DE.json | 40 ++++++++++++++++++-------------------
src/i18n/strings/en_EN.json | 40 ++++++++++++++++++-------------------
src/i18n/strings/fr.json | 2 +-
src/i18n/strings/pt.json | 38 +++++++++++++++++------------------
src/i18n/strings/pt_BR.json | 40 ++++++++++++++++++-------------------
src/i18n/strings/ru.json | 26 ++++++++++++------------
10 files changed, 132 insertions(+), 121 deletions(-)
diff --git a/src/ScalarMessaging.js b/src/ScalarMessaging.js
index b120125e0e..8c591f7cb2 100644
--- a/src/ScalarMessaging.js
+++ b/src/ScalarMessaging.js
@@ -151,7 +151,7 @@ function inviteUser(event, roomId, userId) {
console.log(`Received request to invite ${userId} into room ${roomId}`);
const client = MatrixClientPeg.get();
if (!client) {
- sendError(event, _t('You need to be logged in') + '.');
+ sendError(event, _t('You need to be logged in.'));
return;
}
const room = client.getRoom(roomId);
@@ -171,7 +171,7 @@ function inviteUser(event, roomId, userId) {
success: true,
});
}, function(err) {
- sendError(event, _t('You need to be able to invite users to do that') + '.', err);
+ sendError(event, _t('You need to be able to invite users to do that.'), err);
});
}
@@ -182,7 +182,7 @@ function setPlumbingState(event, roomId, status) {
console.log(`Received request to set plumbing state to status "${status}" in room ${roomId}`);
const client = MatrixClientPeg.get();
if (!client) {
- sendError(event, _t('You need to be logged in') + '.');
+ sendError(event, _t('You need to be logged in.'));
return;
}
client.sendStateEvent(roomId, "m.room.plumbing", { status : status }).done(() => {
@@ -190,7 +190,7 @@ function setPlumbingState(event, roomId, status) {
success: true,
});
}, (err) => {
- sendError(event, err.message ? err.message : _t('Failed to send request') + '.', err);
+ sendError(event, err.message ? err.message : _t('Failed to send request.'), err);
});
}
@@ -198,7 +198,7 @@ function setBotOptions(event, roomId, userId) {
console.log(`Received request to set options for bot ${userId} in room ${roomId}`);
const client = MatrixClientPeg.get();
if (!client) {
- sendError(event, _t('You need to be logged in') + '.');
+ sendError(event, _t('You need to be logged in.'));
return;
}
client.sendStateEvent(roomId, "m.room.bot.options", event.data.content, "_" + userId).done(() => {
@@ -206,20 +206,20 @@ function setBotOptions(event, roomId, userId) {
success: true,
});
}, (err) => {
- sendError(event, err.message ? err.message : _t('Failed to send request') + '.', err);
+ sendError(event, err.message ? err.message : _t('Failed to send request.'), err);
});
}
function setBotPower(event, roomId, userId, level) {
if (!(Number.isInteger(level) && level >= 0)) {
- sendError(event, _t('Power level must be positive integer') + '.');
+ sendError(event, _t('Power level must be positive integer.'));
return;
}
console.log(`Received request to set power level to ${level} for bot ${userId} in room ${roomId}.`);
const client = MatrixClientPeg.get();
if (!client) {
- sendError(event, _t('You need to be logged in') + '.');
+ sendError(event, _t('You need to be logged in.'));
return;
}
@@ -236,7 +236,7 @@ function setBotPower(event, roomId, userId, level) {
success: true,
});
}, (err) => {
- sendError(event, err.message ? err.message : _t('Failed to send request') + '.', err);
+ sendError(event, err.message ? err.message : _t('Failed to send request.'), err);
});
});
}
@@ -259,12 +259,12 @@ function botOptions(event, roomId, userId) {
function returnStateEvent(event, roomId, eventType, stateKey) {
const client = MatrixClientPeg.get();
if (!client) {
- sendError(event, _t('You need to be logged in') + '.');
+ sendError(event, _t('You need to be logged in.'));
return;
}
const room = client.getRoom(roomId);
if (!room) {
- sendError(event, _t('This room is not recognised') + '.');
+ sendError(event, _t('This room is not recognised.'));
return;
}
const stateEvent = room.currentState.getStateEvents(eventType, stateKey);
diff --git a/src/SlashCommands.js b/src/SlashCommands.js
index 366604eaee..d2c0eda3ff 100644
--- a/src/SlashCommands.js
+++ b/src/SlashCommands.js
@@ -70,7 +70,7 @@ const commands = {
// TODO Don't explain this away, actually show a search UI here.
Modal.createDialog(ErrorDialog, {
title: _t('/ddg is not a command'),
- description: _t('To use it, just wait for autocomplete results to load and tab through them') + '.',
+ description: _t('To use it, just wait for autocomplete results to load and tab through them.'),
});
return success();
}),
diff --git a/src/TextForEvent.js b/src/TextForEvent.js
index 24d79ac3ce..d5a54927a0 100644
--- a/src/TextForEvent.js
+++ b/src/TextForEvent.js
@@ -32,9 +32,9 @@ function textForMemberEvent(ev) {
var threePidContent = ev.getContent().third_party_invite;
if (threePidContent) {
if (threePidContent.display_name) {
- return _t('%(targetName)s accepted the invitation for %(displayName)s', {targetName: targetName, displayName: threePidContent.display_name}) + ".";
+ return _t('%(targetName)s accepted the invitation for %(displayName)s.', {targetName: targetName, displayName: threePidContent.display_name});
} else {
- return _t('%(targetName)s accepted an invitation', {targetName: targetName}) + '.';
+ return _t('%(targetName)s accepted an invitation.', {targetName: targetName});
}
}
else {
@@ -42,11 +42,14 @@ function textForMemberEvent(ev) {
return _t('%(senderName)s requested a VoIP conference', {senderName: senderName});
}
else {
- return _t('%(senderName)s invited %(targetName)s', {senderName: senderName, targetName: targetName}) + '.';
+ return _t('%(senderName)s invited %(targetName)s.', {senderName: senderName, targetName: targetName});
}
}
case 'ban':
- return _t('%(senderName)s banned %(targetName)s', {senderName: senderName, targetName: targetName}) + '. ' + reason;
+ return _t(
+ '%(senderName)s banned %(targetName)s. %(reason)s.',
+ {senderName: senderName, targetName: targetName, reason: reason}
+ );
case 'join':
if (ev.getPrevContent() && ev.getPrevContent().membership == 'join') {
if (ev.getPrevContent().displayname && ev.getContent().displayname && ev.getPrevContent().displayname != ev.getContent().displayname) {
@@ -71,7 +74,7 @@ function textForMemberEvent(ev) {
return _t('VoIP conference started');
}
else {
- return _t('%(targetName)s joined the room', {targetName: targetName}) + '.';
+ return _t('%(targetName)s joined the room.', {targetName: targetName});
}
}
case 'leave':
@@ -80,23 +83,29 @@ function textForMemberEvent(ev) {
return _t('VoIP conference finished');
}
else if (ev.getPrevContent().membership === "invite") {
- return _t('%(targetName)s rejected the invitation', {targetName: targetName}) + '.';
+ return _t('%(targetName)s rejected the invitation.', {targetName: targetName});
}
else {
- return _t('%(targetName)s left the room', {targetName: targetName}) + '.';
+ return _t('%(targetName)s left the room.', {targetName: targetName});
}
}
else if (ev.getPrevContent().membership === "ban") {
- return _t('%(senderName)s unbanned %(targetName)s', {senderName: senderName, targetName: targetName}) + '.';
+ return _t('%(senderName)s unbanned %(targetName)s.', {senderName: senderName, targetName: targetName}) + '.';
}
else if (ev.getPrevContent().membership === "join") {
- return _t('%(senderName)s kicked %(targetName)s', {senderName: senderName, targetName: targetName}) + '. ' + reason;
+ return _t(
+ '%(senderName)s kicked %(targetName)s. %(reason)s',
+ {senderName: senderName, targetName: targetName, reason}
+ );
}
else if (ev.getPrevContent().membership === "invite") {
- return _t('%(senderName)s withdrew %(targetName)s\'s inivitation', {senderName: senderName, targetName: targetName}) + '. ' + reason;
+ return _t(
+ '%(senderName)s withdrew %(targetName)s\'s inivitation. %(reason)s',
+ {senderName: senderName, targetName: targetName, reason: reason}
+ );
}
else {
- return _t('%(targetName)s left the room', {targetName: targetName}) + '.';
+ return _t('%(targetName)s left the room.', {targetName: targetName});
}
}
}
@@ -118,7 +127,7 @@ function textForMessageEvent(ev) {
if (ev.getContent().msgtype === "m.emote") {
message = "* " + senderDisplayName + " " + message;
} else if (ev.getContent().msgtype === "m.image") {
- message = _t('%(senderDisplayName)s sent an image', {senderDisplayName: senderDisplayName}) + '.';
+ message = _t('%(senderDisplayName)s sent an image.', {senderDisplayName: senderDisplayName});
}
return message;
}
@@ -144,17 +153,18 @@ function textForCallInviteEvent(event) {
type = "video";
}
var supported = MatrixClientPeg.get().supportsVoip() ? "" : _t('(not supported by this browser)');
- return _t('%(senderName)s placed a %(callType)s call', {senderName: senderName, callType: type}) + '. ' + supported;
+ return _t('%(senderName)s placed a %(callType)s call.', {senderName: senderName, callType: type}) + '. ' + supported;
}
function textForThreePidInviteEvent(event) {
var senderName = event.sender ? event.sender.name : event.getSender();
- return _t('%(senderName)s sent an invitation to %(targetDisplayName)s to join the room', {senderName: senderName, targetDisplayName: event.getContent().display_name}) + ".";
+ return _t('%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.', {senderName: senderName, targetDisplayName: event.getContent().display_name});
}
function textForHistoryVisibilityEvent(event) {
var senderName = event.sender ? event.sender.name : event.getSender();
var vis = event.getContent().history_visibility;
+ // XXX: This i18n just isn't going to work for languages with different sentence structure.
var text = _t('%(senderName)s made future room history visible to', {senderName: senderName}) + ' ';
if (vis === "invited") {
text += _t('all room members, from the point they are invited') + '.';
@@ -199,6 +209,7 @@ function textForPowerEvent(event) {
}
);
let diff = [];
+ // XXX: This is also surely broken for i18n
users.forEach((userId) => {
// Previous power level
const from = event.getPrevContent().users[userId];
diff --git a/src/i18n/strings/da.json b/src/i18n/strings/da.json
index 2b30593a4c..13491eb0b9 100644
--- a/src/i18n/strings/da.json
+++ b/src/i18n/strings/da.json
@@ -133,11 +133,11 @@
"da": "Dansk",
"ru": "Russisk",
"%(targetName)s accepted an invitation": "%(targetName)s accepterede en invitation",
- "%(targetName)s accepted the invitation for %(displayName)s": "%(targetName)s accepteret invitationen til %(displayName)s",
+ "%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s accepteret invitationen til %(displayName)s.",
"%(names)s and %(lastPerson)s are typing": "%(names)s og %(lastPerson)s er ved at skrive",
"%(names)s and one other are typing": "%(names)s og den anden skriver",
"%(names)s and %(count)s others are typing": "%(names)s og %(count)s andre skriver",
- "%(senderName)s answered the call": "%(senderName)s besvarede opkaldet",
+ "%(senderName)s answered the call.": "%(senderName)s besvarede opkaldet.",
"af": "Afrikaans",
"ar-eg": "Arabisk (Egypten)",
"ar-ma": "Arabisk (Marokko)",
diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json
index 421a8e1f41..90395a08c0 100644
--- a/src/i18n/strings/de_DE.json
+++ b/src/i18n/strings/de_DE.json
@@ -349,54 +349,54 @@
"To send events of type": "Zum Senden von Ereignissen mit Typ",
"%(names)s and %(lastPerson)s are typing": "%(names)s und %(lastPerson)s schreiben",
"%(targetName)s accepted an invitation": "%(targetName)s akzeptierte eine Einladung",
- "%(targetName)s accepted the invitation for %(displayName)s": "%(targetName)s akzeptierte eine Einladung für %(displayName)s",
+ "%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s akzeptierte eine Einladung für %(displayName)s.",
"%(names)s and one other are typing": "%(names)s und eine weitere Person tippen",
"%(names)s and %(count)s others are typing": "%(names)s und %(count)s weitere Personen tippen",
- "%(senderName)s answered the call": "%(senderName)s beantwortete den Anruf",
- "%(senderName)s banned %(targetName)s": "%(senderName)s bannte %(targetName)s",
+ "%(senderName)s answered the call.": "%(senderName)s beantwortete den Anruf.",
+ "%(senderName)s banned %(targetName)s.": "%(senderName)s bannte %(targetName)s.",
"%(senderName)s changed their display name from %(oldDisplayName)s to %(displayName)s": "%(senderName)s änderte den Anzeigenamen von %(oldDisplayName)s zu %(displayName)s",
"%(senderName)s changed their profile picture": "%(senderName)s änderte das Profilbild",
"%(senderName)s changed the power level of %(powerLevelDiffText)s": "%(senderName)s änderte das Berechtigungslevel von %(powerLevelDiffText)s",
"%(senderDisplayName)s changed the room name to %(roomName)s": "%(senderDisplayName)s änderte den Raumnamen zu %(roomName)s",
"%(senderDisplayName)s changed the topic to %(topic)s": "%(senderDisplayName)s änderte das Thema zu %(topic)s",
"/ddg is not a command": "/ddg ist kein Kommando",
- "%(senderName)s ended the call": "%(senderName)s beendete den Anruf",
+ "%(senderName)s ended the call.": "%(senderName)s beendete den Anruf.",
"Failed to lookup current room": "Aktuellen Raum nachzuschlagen schlug fehl",
- "Failed to send request": "Anfrage zu senden schlug fehl",
+ "Failed to send request.": "Anfrage zu senden schlug fehl.",
"%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s von %(fromPowerLevel)s zu %(toPowerLevel)s",
- "%(senderName)s invited %(targetName)s": "%(senderName)s lud %(targetName)s ein",
+ "%(senderName)s invited %(targetName)s.": "%(senderName)s lud %(targetName)s ein.",
"%(displayName)s is typing": "%(displayName)s tippt",
- "%(targetName)s joined the room": "%(targetName)s trat dem Raum bei",
- "%(senderName)s kicked %(targetName)s": "%(senderName)s kickte %(targetName)s",
- "%(targetName)s left the room": "%(targetName)s verließ den Raum",
+ "%(targetName)s joined the room.": "%(targetName)s trat dem Raum bei.",
+ "%(senderName)s kicked %(targetName)s.": "%(senderName)s kickte %(targetName)s.",
+ "%(targetName)s left the room.": "%(targetName)s verließ den Raum.",
"%(senderName)s made future room history visible to": "%(senderName)s machte die zukünftige Raumhistorie sichtbar für",
"Missing room_id in request": "Fehlende room_id in Anfrage",
"Missing user_id in request": "Fehlende user_id in Anfrage",
"Must be viewing a room": "Muss einen Raum ansehen",
"New Composer & Autocomplete": "Neuer Eingabeverarbeiter & Autovervollständigung",
"(not supported by this browser)": "(nicht von diesem Browser unterstützt)",
- "%(senderName)s placed a %(callType)s call": "%(senderName)s startete einen %(callType)s-Anruf",
- "Power level must be positive integer": "Berechtigungslevel muss eine positive Zahl sein",
+ "%(senderName)s placed a %(callType)s call.": "%(senderName)s startete einen %(callType)s-Anruf.",
+ "Power level must be positive integer.": "Berechtigungslevel muss eine positive Zahl sein.",
"Reason": "Grund",
- "%(targetName)s rejected the invitation": "%(targetName)s lehnte die Einladung ab",
+ "%(targetName)s rejected the invitation.": "%(targetName)s lehnte die Einladung ab.",
"%(senderName)s removed their display name (%(oldDisplayName)s)": "%(senderName)s löschte den Anzeigenamen (%(oldDisplayName)s)",
"%(senderName)s removed their profile picture": "%(senderName)s löschte das Profilbild",
"%(senderName)s requested a VoIP conference": "%(senderName)s fragte nach einer VoIP-Konferenz",
"Room %(roomId)s not visible": "Raum %(roomId)s ist nicht sichtbar",
- "%(senderDisplayName)s sent an image": "%(senderDisplayName)s hat ein Bild gesendet",
- "%(senderName)s sent an invitation to %(targetDisplayName)s to join the room": "%(senderName)s sandte eine Einladung an %(targetDisplayName)s um diesem Raum beizutreten",
+ "%(senderDisplayName)s sent an image.": "%(senderDisplayName)s hat ein Bild gesendet.",
+ "%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.": "%(senderName)s sandte eine Einladung an %(targetDisplayName)s um diesem Raum beizutreten.",
"%(senderName)s set a profile picture": "%(senderName)s setzte ein Profilbild",
"%(senderName)s set their display name to %(displayName)s": "%(senderName)s setzte den Anzeigenamen zu %(displayName)s",
- "This room is not recognised": "Dieser Raum wurde nicht erkannt",
+ "This room is not recognised.": "Dieser Raum wurde nicht erkannt.",
"These are experimental features that may break in unexpected ways": "Dies sind experimentelle Funktionen, die in unerwarteter Weise Fehler verursachen können",
- "To use it, just wait for autocomplete results to load and tab through them": "Um dies zu nutzen, warte auf die Autovervollständigungsergebnisse und benutze die TAB Taste",
+ "To use it, just wait for autocomplete results to load and tab through them.": "Um dies zu nutzen, warte auf die Autovervollständigungsergebnisse und benutze die TAB Taste.",
"%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s)": "%(senderName)s schaltete Ende-zu-Ende-Verschlüsselung ein (Algorithmus: %(algorithm)s)",
- "%(senderName)s unbanned %(targetName)s": "%(senderName)s zog Bann für %(targetName)s zurück",
+ "%(senderName)s unbanned %(targetName)s.": "%(senderName)s zog Bann für %(targetName)s zurück.",
"Usage": "Verwendung",
"Use with caution": "Mit Vorsicht benutzen",
- "%(senderName)s withdrew %(targetName)s's inivitation": "%(senderName)s zog die Einladung für %(targetName)s zurück",
- "You need to be able to invite users to do that": "Du musst in der Lage sein Nutzer einzuladen um dies zu tun",
- "You need to be logged in": "Du musst angemeldet sein",
+ "%(senderName)s withdrew %(targetName)s's inivitation.": "%(senderName)s zog die Einladung für %(targetName)s zurück.",
+ "You need to be able to invite users to do that.": "Du musst in der Lage sein Nutzer einzuladen um dies zu tun.",
+ "You need to be logged in.": "Du musst angemeldet sein.",
"There are no visible files in this room": "Es gibt keine sichtbaren Dateien in diesem Raum",
"Error changing language": "Fehler beim Ändern der Sprache",
"Riot was unable to find the correct Data for the selected Language.": "Riot war nicht in der Lage die korrekten Daten für die ausgewählte Sprache zu finden.",
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index 8f0a8018b0..9c69233692 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -125,7 +125,7 @@
"A text message has been sent to +%(msisdn)s. Please enter the verification code it contains": "A text message has been sent to +%(msisdn)s. Please enter the verification code it contains",
"accept": "accept",
"%(targetName)s accepted an invitation": "%(targetName)s accepted an invitation",
- "%(targetName)s accepted the invitation for %(displayName)s": "%(targetName)s accepted the invitation for %(displayName)s",
+ "%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s accepted the invitation for %(displayName)s.",
"Account": "Account",
"Access Token:": "Access Token:",
"Add email address": "Add email address",
@@ -149,7 +149,7 @@
"%(names)s and %(count)s others are typing": "%(names)s and %(count)s others are typing",
"An email has been sent to": "An email has been sent to",
"A new password must be entered.": "A new password must be entered.",
- "%(senderName)s answered the call": "%(senderName)s answered the call",
+ "%(senderName)s answered the call.": "%(senderName)s answered the call.",
"anyone.": "anyone",
"An error has occurred.": "An error has occurred.",
"Anyone who knows the room's link, apart from guests": "Anyone who knows the room's link, apart from guests",
@@ -159,7 +159,7 @@
"Are you sure you want upload the following files?": "Are you sure you want upload the following files?",
"Attachment": "Attachment",
"Autoplay GIFs and videos": "Autoplay GIFs and videos",
- "%(senderName)s banned %(targetName)s": "%(senderName)s banned %(targetName)s",
+ "%(senderName)s banned %(targetName)s.": "%(senderName)s banned %(targetName)s.",
"Ban": "Ban",
"Banned users": "Banned users",
"Bans user with given id": "Bans user with given id",
@@ -233,7 +233,7 @@
"Enable encryption": "Enable encryption",
"Encrypted messages will not be visible on clients that do not yet implement encryption": "Encrypted messages will not be visible on clients that do not yet implement encryption",
"Encrypted room": "Encrypted room",
- "%(senderName)s ended the call": "%(senderName)s ended the call",
+ "%(senderName)s ended the call.": "%(senderName)s ended the call.",
"End-to-end encryption information": "End-to-end encryption information",
"End-to-end encryption is in beta and may not be reliable": "End-to-end encryption is in beta and may not be reliable",
"Enter Code": "Enter Code",
@@ -257,7 +257,7 @@
"Failed to reject invitation": "Failed to reject invitation",
"Failed to save settings": "Failed to save settings",
"Failed to send email": "Failed to send email",
- "Failed to send request": "Failed to send request",
+ "Failed to send request.": "Failed to send request.",
"Failed to set display name": "Failed to set display name",
"Failed to set up conference call": "Failed to set up conference call",
"Failed to toggle moderator status": "Failed to toggle moderator status",
@@ -291,7 +291,7 @@
"Invalid alias format": "Invalid alias format",
"Invalid address format": "Invalid address format",
"Invalid Email Address": "Invalid Email Address",
- "%(senderName)s invited %(targetName)s": "%(senderName)s invited %(targetName)s",
+ "%(senderName)s invited %(targetName)s.": "%(senderName)s invited %(targetName)s.",
"Invite new room members": "Invite new room members",
"Invites": "Invites",
"Invites user with given id to current room": "Invites user with given id to current room",
@@ -303,16 +303,16 @@
"Join Room": "Join Room",
"joined and left": "joined and left",
"joined": "joined",
- "%(targetName)s joined the room": "%(targetName)s joined the room",
+ "%(targetName)s joined the room.": "%(targetName)s joined the room.",
"Joins room with given alias": "Joins room with given alias",
- "%(senderName)s kicked %(targetName)s": "%(senderName)s kicked %(targetName)s",
+ "%(senderName)s kicked %(targetName)s.": "%(senderName)s kicked %(targetName)s.",
"Kick": "Kick",
"Kicks user with given id": "Kicks user with given id",
"Labs": "Labs",
"Leave room": "Leave room",
"left and rejoined": "left and rejoined",
"left": "left",
- "%(targetName)s left the room": "%(targetName)s left the room",
+ "%(targetName)s left the room.": "%(targetName)s left the room.",
"Level": "Level",
"Local addresses for this room:": "Local addresses for this room:",
"Logged in as:": "Logged in as:",
@@ -363,10 +363,10 @@
"People": "People",
"Permissions": "Permissions",
"Phone": "Phone",
- "%(senderName)s placed a %(callType)s call": "%(senderName)s placed a %(callType)s call",
+ "%(senderName)s placed a %(callType)s call.": "%(senderName)s placed a %(callType)s call.",
"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.",
"Please Register": "Please Register",
- "Power level must be positive integer": "Power level must be positive integer",
+ "Power level must be positive integer.": "Power level must be positive integer.",
"Press": "Press",
"Privacy warning": "Privacy warning",
"Privileged Users": "Privileged Users",
@@ -376,7 +376,7 @@
"Refer a friend to Riot": "Refer a friend to Riot",
"Registration required": "Registration required",
"rejected": "rejected",
- "%(targetName)s rejected the invitation": "%(targetName)s rejected the invitation",
+ "%(targetName)s rejected the invitation.": "%(targetName)s rejected the invitation.",
"Reject invitation": "Reject invitation",
"Remove Contact Information?": "Remove Contact Information?",
"%(senderName)s removed their display name (%(oldDisplayName)s)": "%(senderName)s removed their display name (%(oldDisplayName)s)",
@@ -405,8 +405,8 @@
"Sender device information": "Sender device information",
"Send Invites": "Send Invites",
"Send Reset Email": "Send Reset Email",
- "%(senderDisplayName)s sent an image": "%(senderDisplayName)s sent an image",
- "%(senderName)s sent an invitation to %(targetDisplayName)s to join the room": "%(senderName)s sent an invitation to %(targetDisplayName)s to join the room",
+ "%(senderDisplayName)s sent an image.": "%(senderDisplayName)s sent an image.",
+ "%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.": "%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.",
"sent a video": "sent a video",
"Server error": "Server error",
"Server may be unavailable or overloaded": "Server may be unavailable or overloaded",
@@ -445,7 +445,7 @@
"The file '%(fileName)s' failed to upload": "The file '%(fileName)s' failed to upload",
"The remote side failed to pick up": "The remote side failed to pick up",
"This room has no local addresses": "This room has no local addresses",
- "This room is not recognised": "This room is not recognised",
+ "This room is not recognised.": "This room is not recognised.",
"This room is private or inaccessible to guests. You may be able to join if you register": "This room is private or inaccessible to guests. You may be able to join if you register",
"These are experimental features that may break in unexpected ways": "These are experimental features that may break in unexpected ways",
"The visibility of existing history will be unchanged": "The visibility of existing history will be unchanged",
@@ -474,7 +474,7 @@
"to start a chat with someone": "to start a chat with someone",
"to tag as %(tagName)s": "to tag as %(tagName)s",
"to tag direct chat": "to tag direct chat",
- "To use it, just wait for autocomplete results to load and tab through them": "To use it, just wait for autocomplete results to load and tab through them",
+ "To use it, just wait for autocomplete results to load and tab through them.": "To use it, just wait for autocomplete results to load and tab through them.",
"Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question": "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question",
"Tried to load a specific point in this room's timeline, but was unable to find it": "Tried to load a specific point in this room's timeline, but was unable to find it",
"Turn Markdown off": "Turn Markdown off",
@@ -485,7 +485,7 @@
"Unable to restore previous session": "Unable to restore previous session",
"Unable to verify email address": "Unable to verify email address",
"Unban": "Unban",
- "%(senderName)s unbanned %(targetName)s": "%(senderName)s unbanned %(targetName)s",
+ "%(senderName)s unbanned %(targetName)s.": "%(senderName)s unbanned %(targetName)s.",
"Unable to capture screen": "Unable to capture screen",
"Unable to enable Notifications": "Unable to enable Notifications",
"Unable to load device list": "Unable to load device list",
@@ -522,7 +522,7 @@
"Who can read history?": "Who can read history?",
"Who would you like to add to this room?": "Who would you like to add to this room?",
"Who would you like to communicate with?": "Who would you like to communicate with?",
- "%(senderName)s withdrew %(targetName)s's inivitation": "%(senderName)s withdrew %(targetName)s's inivitation",
+ "%(senderName)s withdrew %(targetName)s's inivitation.": "%(senderName)s withdrew %(targetName)s's inivitation.",
"Would you like to": "Would you like to",
"You are already in a call": "You are already in a call",
"You're not in any rooms yet! Press": "You're not in any rooms yet! Press",
@@ -534,8 +534,8 @@
"You have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, sign in again on each device": "You have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, sign in again on each device",
"You have no visible notifications": "You have no visible notifications",
"you must be a": "you must be a",
- "You need to be able to invite users to do that": "You need to be able to invite users to do that",
- "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.",
+ "You need to be logged in.": "You need to be logged in.",
"You need to log back in to generate end-to-end encryption keys for this device and submit the public key to your homeserver. This is a once off; sorry for the inconvenience": "You need to log back in to generate end-to-end encryption keys for this device and submit the public key to your homeserver. This is a once off; sorry for the inconvenience",
"Your email address does not appear to be associated with a Matrix ID on this Homeserver": "Your email address does not appear to be associated with a Matrix ID on this Homeserver",
"Your password has been reset": "Your password has been reset",
diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json
index e393d27bda..eba5b5a0fc 100644
--- a/src/i18n/strings/fr.json
+++ b/src/i18n/strings/fr.json
@@ -137,7 +137,7 @@
"Enable encryption": "Enable encryption",
"Encrypted messages will not be visible on clients that do not yet implement encryption": "Encrypted messages will not be visible on clients that do not yet implement encryption",
"Encrypted room": "Encrypted room",
- "%(senderName)s ended the call": "%(senderName)s ended the call",
+ "%(senderName)s ended the call.": "%(senderName)s ended the call.",
"End-to-end encryption information": "End-to-end encryption information",
"End-to-end encryption is in beta and may not be reliable": "End-to-end encryption is in beta and may not be reliable",
"Enter Code": "Enter Code",
diff --git a/src/i18n/strings/pt.json b/src/i18n/strings/pt.json
index e19d261997..d166b3a273 100644
--- a/src/i18n/strings/pt.json
+++ b/src/i18n/strings/pt.json
@@ -304,16 +304,16 @@
"de": "Alemão",
"da": "Dinamarquês",
"ru": "Russo",
- "%(targetName)s accepted an invitation": "%(targetName)s aceitou um convite",
- "%(targetName)s accepted the invitation for %(displayName)s": "%(targetName)s aceitou o convite para %(displayName)s",
+ "%(targetName)s accepted an invitation": "%(targetName)s aceitou um convite.",
+ "%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s aceitou o convite para %(displayName)s.",
"all room members, from the point they are invited": "todas/os as/os integrantes da sala, a partir do momento em que foram convidadas/os",
"all room members, from the point they joined": "todas/os as/os integrantes da sala, a partir do momento em que entraram na sala",
"%(names)s and %(lastPerson)s are typing": "%(names)s e %(lastPerson)s estão escrevendo",
"%(names)s and one other are typing": "%(names)s e uma outra pessoa estão escrevendo",
"%(names)s and %(count)s others are typing": "%(names)s e %(count)s outras pessoas estão escrevendo",
- "%(senderName)s answered the call": "%(senderName)s atendeu à chamada",
+ "%(senderName)s answered the call.": "%(senderName)s atendeu à chamada.",
"anyone.": "qualquer pessoa",
- "%(senderName)s banned %(targetName)s": "%(senderName)s removeu %(targetName)s da sala",
+ "%(senderName)s banned %(targetName)s.": "%(senderName)s removeu %(targetName)s da sala.",
"Call Timeout": "Tempo esgotado. Chamada encerrada",
"%(senderName)s changed their display name from %(oldDisplayName)s to %(displayName)s": "%(senderName)s mudou seu nome público de %(oldDisplayName)s para %(displayName)s",
"%(senderName)s changed their profile picture": "%(senderName)s alterou sua imagem de perfil",
@@ -332,16 +332,16 @@
"Existing Call": "Chamada em andamento",
"Failed to lookup current room": "Não foi possível buscar na sala atual",
"Failed to send email": "Não foi possível enviar email",
- "Failed to send request": "Não foi possível mandar requisição",
+ "Failed to send request.": "Não foi possível mandar requisição.",
"Failed to set up conference call": "Não foi possível montar a chamada de conferência",
"Failed to verify email address: make sure you clicked the link in the email": "Não foi possível verificar o endereço de email: verifique se você realmente clicou no link que está no seu email",
"Failure to create room": "Não foi possível criar a sala",
"%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s de %(fromPowerLevel)s para %(toPowerLevel)s",
"Guest users can't create new rooms. Please register to create room and start a chat": "Visitantes não podem criar novas salas. Por favor, registre-se para criar uma sala e iniciar uma conversa",
- "%(senderName)s invited %(targetName)s": "%(senderName)s convidou %(targetName)s",
+ "%(senderName)s invited %(targetName)s.": "%(senderName)s convidou %(targetName)s.",
"%(displayName)s is typing": "%(displayName)s está escrevendo",
- "%(targetName)s joined the room": "%(targetName)s entrou na sala",
- "%(senderName)s kicked %(targetName)s": "%(senderName)s removeu %(targetName)s da sala",
+ "%(targetName)s joined the room.": "%(targetName)s entrou na sala.",
+ "%(senderName)s kicked %(targetName)s.": "%(senderName)s removeu %(targetName)s da sala.",
"%(targetName)s left the room": "%(targetName)s saiu da sala",
"%(senderName)s made future room history visible to": "%(senderName)s deixou o histórico futuro da sala visível para",
"Missing room_id in request": "Faltou o id da sala na requisição",
@@ -350,20 +350,20 @@
"New Composer & Autocomplete": "Nova ferramenta de formatação de mensagens e autocompletar",
"(not supported by this browser)": "(não é compatível com este navegador)",
"olm version": "versão olm",
- "%(senderName)s placed a %(callType)s call": "%(senderName)s fez uma chamada de %(callType)s",
- "Power level must be positive integer": "O nível de permissões tem que ser um número inteiro e positivo",
+ "%(senderName)s placed a %(callType)s call.": "%(senderName)s fez uma chamada de %(callType)s.",
+ "Power level must be positive integer.": "O nível de permissões tem que ser um número inteiro e positivo.",
"Press": "Aperte",
"Reason": "Razão",
"Refer a friend to Riot": "Recomende Riot a um/a amigo/a",
- "%(targetName)s rejected the invitation": "%(targetName)s recusou o convite",
+ "%(targetName)s rejected the invitation.": "%(targetName)s recusou o convite.",
"%(senderName)s removed their display name (%(oldDisplayName)s)": "%(senderName)s removeu o seu nome público (%(oldDisplayName)s)",
"%(senderName)s removed their profile picture": "%(senderName)s removeu sua imagem de perfil",
"%(senderName)s requested a VoIP conference": "%(senderName)s está solicitando uma conferência de voz",
"Riot does not have permission to send you notifications - please check your browser settings": "Riot não tem permissões para enviar notificações a você - por favor, verifique as configurações do seu navegador",
"Riot was not given permission to send notifications - please try again": "Riot não tem permissões para enviar notificações a você - por favor, tente novamente",
"Room %(roomId)s not visible": "A sala %(roomId)s não está visível",
- "%(senderDisplayName)s sent an image": "%(senderDisplayName)s enviou uma imagem",
- "%(senderName)s sent an invitation to %(targetDisplayName)s to join the room": "%(senderName)s enviou um convite para %(targetDisplayName)s entrar na sala",
+ "%(senderDisplayName)s sent an image.": "%(senderDisplayName)s enviou uma imagem.",
+ "%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.": "%(senderName)s enviou um convite para %(targetDisplayName)s entrar na sala.",
"%(senderName)s set a profile picture": "%(senderName)s definiu uma imagem de perfil",
"%(senderName)s set their display name to %(displayName)s": "%(senderName)s definiu seu nome público para %(displayName)s",
"tag as %(tagName)s": "marcar como %(tagName)s",
@@ -372,7 +372,7 @@
"The file '%(fileName)s' exceeds this home server's size limit for uploads": "O arquivo '%(fileName)s' ultrapassa o limite de tamanho que nosso servidor permite enviar",
"The file '%(fileName)s' failed to upload": "Não foi possível enviar o arquivo '%(fileName)s",
"The remote side failed to pick up": "Houve alguma falha que não permitiu a outra pessoa atender à chamada",
- "This room is not recognised": "Esta sala não é reconhecida",
+ "This room is not recognised.": "Esta sala não é reconhecida.",
"These are experimental features that may break in unexpected ways": "Estas são funcionalidades experimentais que podem apresentar falhas",
"This phone number is already in use": "Este número de telefone já está sendo usado",
"to browse the directory": "para navegar na lista pública de salas",
@@ -383,24 +383,24 @@
"to restore": "para restaurar",
"to start a chat with someone": "para iniciar uma conversa com alguém",
"to tag direct chat": "para marcar a conversa como pessoal",
- "To use it, just wait for autocomplete results to load and tab through them": "Para usar esta funcionalidade, espere o carregamento dos resultados de autocompletar e então escolha entre as opções",
+ "To use it, just wait for autocomplete results to load and tab through them.": "Para usar esta funcionalidade, espere o carregamento dos resultados de autocompletar e então escolha entre as opções.",
"%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s)": "%(senderName)s ativou criptografia ponta a ponta (algoritmo %(algorithm)s)",
"Unable to restore previous session": "Não foi possível restaurar a sessão anterior",
- "%(senderName)s unbanned %(targetName)s": "%(senderName)s desfez o banimento de %(targetName)s",
+ "%(senderName)s unbanned %(targetName)s.": "%(senderName)s desfez o banimento de %(targetName)s.",
"Unable to capture screen": "Não foi possível capturar a imagem da tela",
"Unable to enable Notifications": "Não foi possível ativar as notificações",
"Upload Failed": "O envio falhou",
"Usage": "Uso",
"Use with caution": "Use com cautela",
"VoIP is unsupported": "Chamada de voz não permitida",
- "%(senderName)s withdrew %(targetName)s's inivitation": "%(senderName)s desfez o convite a %(targetName)s's",
+ "%(senderName)s withdrew %(targetName)s's inivitation.": "%(senderName)s desfez o convite a %(targetName)s's.",
"You are already in a call": "Você já está em uma chamada",
"You're not in any rooms yet! Press": "Você ainda não está em nenhuma sala! Pressione",
"You are trying to access %(roomName)s": "Você está tentando acessar a sala %(roomName)s",
"You cannot place a call with yourself": "Você não pode iniciar uma chamada",
"You cannot place VoIP calls in this browser": "Você não pode fazer chamadas de voz neste navegador",
- "You need to be able to invite users to do that": "Para fazer isso, você tem que ter permissão para convidar outras pessoas",
- "You need to be logged in": "Você tem que estar logado",
+ "You need to be able to invite users to do that.": "Para fazer isso, você tem que ter permissão para convidar outras pessoas.",
+ "You need to be logged in.": "Você tem que estar logado.",
"You need to log back in to generate end-to-end encryption keys for this device and submit the public key to your homeserver. This is a once off; sorry for the inconvenience": "É necessário que você faça login novamente para poder gerar as chaves de criptografia ponta-a-ponta para este dispositivo e então enviar sua chave pública para o servidor. Pedimos desculpas pela inconveniência, é preciso fazer isso apenas única uma vez",
"Your email address does not appear to be associated with a Matrix ID on this Homeserver": "O seu endereço de email não parece estar associado a uma conta de usuária/o Matrix neste servidor",
"Set a display name:": "Defina um nome público para você:",
diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json
index 47d9271232..5194f4cc6c 100644
--- a/src/i18n/strings/pt_BR.json
+++ b/src/i18n/strings/pt_BR.json
@@ -310,15 +310,15 @@
"da": "Dinamarquês",
"ru": "Russo",
"%(targetName)s accepted an invitation": "%(targetName)s aceitou um convite",
- "%(targetName)s accepted the invitation for %(displayName)s": "%(targetName)s aceitou o convite para %(displayName)s",
+ "%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s aceitou o convite para %(displayName)s.",
"all room members, from the point they are invited": "todas/os as/os integrantes da sala, a partir do momento em que foram convidadas/os",
"all room members, from the point they joined": "todas/os as/os integrantes da sala, a partir do momento em que entraram na sala",
"%(names)s and %(lastPerson)s are typing": "%(names)s e %(lastPerson)s estão escrevendo",
"%(names)s and one other are typing": "%(names)s e uma outra pessoa estão escrevendo",
"%(names)s and %(count)s others are typing": "%(names)s e %(count)s outras pessoas estão escrevendo",
- "%(senderName)s answered the call": "%(senderName)s atendeu à chamada",
+ "%(senderName)s answered the call.": "%(senderName)s atendeu à chamada.",
"anyone.": "qualquer pessoa",
- "%(senderName)s banned %(targetName)s": "%(senderName)s removeu %(targetName)s da sala",
+ "%(senderName)s banned %(targetName)s.": "%(senderName)s removeu %(targetName)s da sala.",
"Call Timeout": "Tempo esgotado. Chamada encerrada",
"%(senderName)s changed their display name from %(oldDisplayName)s to %(displayName)s": "%(senderName)s mudou seu nome público de %(oldDisplayName)s para %(displayName)s",
"%(senderName)s changed their profile picture": "%(senderName)s alterou sua imagem de perfil",
@@ -333,21 +333,21 @@
"/ddg is not a command": "/ddg não é um comando",
"Drop here %(toAction)s": "Arraste aqui %(toAction)s",
"Drop here to tag %(section)s": "Arraste aqui para marcar como %(section)s",
- "%(senderName)s ended the call": "%(senderName)s finalizou a chamada",
+ "%(senderName)s ended the call.": "%(senderName)s finalizou a chamada.",
"Existing Call": "Chamada em andamento",
"Failed to lookup current room": "Não foi possível buscar na sala atual",
"Failed to send email": "Não foi possível enviar email",
- "Failed to send request": "Não foi possível mandar requisição",
+ "Failed to send request.": "Não foi possível mandar requisição.",
"Failed to set up conference call": "Não foi possível montar a chamada de conferência",
"Failed to verify email address: make sure you clicked the link in the email": "Não foi possível verificar o endereço de email: verifique se você realmente clicou no link que está no seu email",
"Failure to create room": "Não foi possível criar a sala",
"%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s de %(fromPowerLevel)s para %(toPowerLevel)s",
"Guest users can't create new rooms. Please register to create room and start a chat": "Visitantes não podem criar novas salas. Por favor, registre-se para criar uma sala e iniciar uma conversa",
- "%(senderName)s invited %(targetName)s": "%(senderName)s convidou %(targetName)s",
+ "%(senderName)s invited %(targetName)s.": "%(senderName)s convidou %(targetName)s.",
"%(displayName)s is typing": "%(displayName)s está escrevendo",
- "%(targetName)s joined the room": "%(targetName)s entrou na sala",
- "%(senderName)s kicked %(targetName)s": "%(senderName)s removeu %(targetName)s da sala",
- "%(targetName)s left the room": "%(targetName)s saiu da sala",
+ "%(targetName)s joined the room.": "%(targetName)s entrou na sala.",
+ "%(senderName)s kicked %(targetName)s.": "%(senderName)s removeu %(targetName)s da sala.",
+ "%(targetName)s left the room.": "%(targetName)s saiu da sala.",
"%(senderName)s made future room history visible to": "%(senderName)s deixou o histórico futuro da sala visível para",
"Missing room_id in request": "Faltou o id da sala na requisição",
"Missing user_id in request": "Faltou o id de usuário na requisição",
@@ -355,20 +355,20 @@
"New Composer & Autocomplete": "Nova ferramenta de formatação de mensagens e autocompletar",
"(not supported by this browser)": "(não é compatível com este navegador)",
"olm version": "versão olm",
- "%(senderName)s placed a %(callType)s call": "%(senderName)s fez uma chamada de %(callType)s",
- "Power level must be positive integer": "O nível de permissões tem que ser um número inteiro e positivo",
+ "%(senderName)s placed a %(callType)s call.": "%(senderName)s fez uma chamada de %(callType)s.",
+ "Power level must be positive integer.": "O nível de permissões tem que ser um número inteiro e positivo.",
"Press": "Aperte",
"Reason": "Razão",
"Refer a friend to Riot": "Recomende Riot a um/a amigo/a",
- "%(targetName)s rejected the invitation": "%(targetName)s recusou o convite",
+ "%(targetName)s rejected the invitation.": "%(targetName)s recusou o convite.",
"%(senderName)s removed their display name (%(oldDisplayName)s)": "%(senderName)s removeu o seu nome público (%(oldDisplayName)s)",
"%(senderName)s removed their profile picture": "%(senderName)s removeu sua imagem de perfil",
"%(senderName)s requested a VoIP conference": "%(senderName)s está solicitando uma conferência de voz",
"Riot does not have permission to send you notifications - please check your browser settings": "Riot não tem permissões para enviar notificações a você - por favor, verifique as configurações do seu navegador",
"Riot was not given permission to send notifications - please try again": "Riot não tem permissões para enviar notificações a você - por favor, tente novamente",
"Room %(roomId)s not visible": "A sala %(roomId)s não está visível",
- "%(senderDisplayName)s sent an image": "%(senderDisplayName)s enviou uma imagem",
- "%(senderName)s sent an invitation to %(targetDisplayName)s to join the room": "%(senderName)s enviou um convite para %(targetDisplayName)s entrar na sala",
+ "%(senderDisplayName)s sent an image.": "%(senderDisplayName)s enviou uma imagem.",
+ "%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.": "%(senderName)s enviou um convite para %(targetDisplayName)s entrar na sala.",
"%(senderName)s set a profile picture": "%(senderName)s definiu uma imagem de perfil",
"%(senderName)s set their display name to %(displayName)s": "%(senderName)s definiu seu nome público para %(displayName)s",
"tag as %(tagName)s": "marcar como %(tagName)s",
@@ -377,7 +377,7 @@
"The file '%(fileName)s' exceeds this home server's size limit for uploads": "O arquivo '%(fileName)s' ultrapassa o limite de tamanho que nosso servidor permite enviar",
"The file '%(fileName)s' failed to upload": "Não foi possível enviar o arquivo '%(fileName)s",
"The remote side failed to pick up": "Houve alguma falha que não permitiu a outra pessoa atender à chamada",
- "This room is not recognised": "Esta sala não é reconhecida",
+ "This room is not recognised.": "Esta sala não é reconhecida.",
"These are experimental features that may break in unexpected ways": "Estas são funcionalidades experimentais que podem apresentar falhas",
"This phone number is already in use": "Este número de telefone já está sendo usado",
"to browse the directory": "para navegar na lista pública de salas",
@@ -389,24 +389,24 @@
"to start a chat with someone": "para iniciar uma conversa com alguém",
"to tag as %(tagName)s": "para marcar como %(tagName)s",
"to tag direct chat": "para marcar a conversa como pessoal",
- "To use it, just wait for autocomplete results to load and tab through them": "Para usar esta funcionalidade, espere o carregamento dos resultados de autocompletar e então escolha entre as opções",
+ "To use it, just wait for autocomplete results to load and tab through them.": "Para usar esta funcionalidade, espere o carregamento dos resultados de autocompletar e então escolha entre as opções.",
"%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s)": "%(senderName)s ativou criptografia ponta a ponta (algoritmo %(algorithm)s)",
"Unable to restore previous session": "Não foi possível restaurar a sessão anterior",
- "%(senderName)s unbanned %(targetName)s": "%(senderName)s desfez o banimento de %(targetName)s",
+ "%(senderName)s unbanned %(targetName)s.": "%(senderName)s desfez o banimento de %(targetName)s.",
"Unable to capture screen": "Não foi possível capturar a imagem da tela",
"Unable to enable Notifications": "Não foi possível ativar as notificações",
"Upload Failed": "O envio falhou",
"Usage": "Uso",
"Use with caution": "Use com cautela",
"VoIP is unsupported": "Chamada de voz não permitida",
- "%(senderName)s withdrew %(targetName)s's inivitation": "%(senderName)s desfez o convite a %(targetName)s",
+ "%(senderName)s withdrew %(targetName)s's inivitation.": "%(senderName)s desfez o convite a %(targetName)s.",
"You are already in a call": "Você já está em uma chamada",
"You're not in any rooms yet! Press": "Você ainda não está em nenhuma sala! Pressione",
"You are trying to access %(roomName)s": "Você está tentando acessar a sala %(roomName)s",
"You cannot place a call with yourself": "Você não pode iniciar uma chamada",
"You cannot place VoIP calls in this browser": "Você não pode fazer chamadas de voz neste navegador",
- "You need to be able to invite users to do that": "Para fazer isso, você tem que ter permissão para convidar outras pessoas",
- "You need to be logged in": "Você tem que estar logado",
+ "You need to be able to invite users to do that.": "Para fazer isso, você tem que ter permissão para convidar outras pessoas.",
+ "You need to be logged in.": "Você tem que estar logado.",
"You need to log back in to generate end-to-end encryption keys for this device and submit the public key to your homeserver. This is a once off; sorry for the inconvenience": "É necessário que você faça login novamente para poder gerar as chaves de criptografia ponta-a-ponta para este dispositivo e então enviar sua chave pública para o servidor. Pedimos desculpas pela inconveniência, é preciso fazer isso apenas única uma vez",
"Your email address does not appear to be associated with a Matrix ID on this Homeserver": "O seu endereço de email não parece estar associado a uma conta de usuária/o Matrix neste servidor",
"Set a display name:": "Defina um nome público para você:",
diff --git a/src/i18n/strings/ru.json b/src/i18n/strings/ru.json
index 6bd3773861..ea23d16d04 100644
--- a/src/i18n/strings/ru.json
+++ b/src/i18n/strings/ru.json
@@ -217,15 +217,15 @@
"da": "Датский",
"ru": "Русский",
"%(targetName)s accepted an invitation": "%(targetName)s принятое приглашение",
- "%(targetName)s accepted the invitation for %(displayName)s": "%(targetName)s принятое приглашение от %(displayName)s",
+ "%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s принятое приглашение от %(displayName)s.",
"Resend all": "Переслать снова всем",
"cancel all": "отменить всем",
"Active call": "Активный звонок",
"%(names)s and %(lastPerson)s are typing": "%(names)s и %(lastPerson)s печатает",
"%(names)s and one other are typing": "%(names)s и другой печатают",
"%(names)s and %(count)s others are typing": "%(names)s и %(count)s другие печатают",
- "%(senderName)s answered the call": "%(senderName)s ответил на звонок",
- "%(senderName)s banned %(targetName)s": "%(senderName)s запрещенный %(targetName)s",
+ "%(senderName)s answered the call.": "%(senderName)s ответил на звонок.",
+ "%(senderName)s banned %(targetName)s.": "%(senderName)s запрещенный %(targetName)s.",
"Call Timeout": "Время ожидания вызова",
"%(senderName)s changed their display name from %(oldDisplayName)s to %(displayName)s": "%(senderName)s их имя измененное с %(oldDisplayName)s на %(displayName)s",
"%(senderName)s changed their profile picture": "%(senderName)s измененное ихнее фото профиля",
@@ -239,21 +239,21 @@
"/ddg is not a command": "/ddg не команда",
"Drop here %(toAction)s": "Вставить здесь %(toAction)s",
"Drop here to tag %(section)s": "Вставить здесь для тега %(section)s",
- "%(senderName)s ended the call": "%(senderName)s прекратил звонок",
+ "%(senderName)s ended the call.": "%(senderName)s прекратил звонок.",
"Existing Call": "Существующий вызов",
"Failed to lookup current room": "Не удалось выполнить поиск текущий комнаты",
- "Failed to send request": "Не удалось выслать запрос",
+ "Failed to send request.": "Не удалось выслать запрос.",
"Failed to set up conference call": "Не удалось установить конференц-вызов",
"Failed to verify email address: make sure you clicked the link in the email": "Не удалось подтвердить email-адрес: убедитесь что вы щелкнули по ссылке электронной почты",
"Failure to create room": "Не удалось создать комнату",
"%(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": "Гостевые пользователи не могут создавать новые комнаты. Зарегистрируйтесь для создания комнаты и чата",
"click to reveal": "нажать для открытия",
- "%(senderName)s invited %(targetName)s": "%(senderName)s приглашает %(targetName)s",
+ "%(senderName)s invited %(targetName)s.": "%(senderName)s приглашает %(targetName)s.",
"%(displayName)s is typing": "%(displayName)s вводит текст",
"%(targetName)s joined the room": "%(targetName)s присоединенный к комнате",
- "%(senderName)s kicked %(targetName)s": "%(senderName)s выкинул %(targetName)s",
- "%(targetName)s left the room": "%(targetName)s покинул комнату",
+ "%(senderName)s kicked %(targetName)s.": "%(senderName)s выкинул %(targetName)s.",
+ "%(targetName)s left the room.": "%(targetName)s покинул комнату.",
"%(senderName)s made future room history visible to": "%(senderName)s история сделаной будущей комнаты, видимая для",
"Missing room_id in request": "Отсутствует room_id в запросе",
"Missing user_id in request": "Отсутствует user_id в запросе",
@@ -357,14 +357,14 @@
"Sunday": "Воскресенье",
"%(weekDayName)s %(time)s": "%(weekDayName)s %(time)s",
"Upload an avatar:": "Загрузить аватар",
- "You need to be logged in": "Вы должны быть зарегистрированы",
- "You need to be able to invite users to do that": "Вам необходимо пригласить пользователей чтобы сделать это",
+ "You need to be logged in.": "Вы должны быть зарегистрированы",
+ "You need to be able to invite users to do that.": "Вам необходимо пригласить пользователей чтобы сделать это.",
"You cannot place VoIP calls in this browser": "Вы не можете сделать вызовы VoIP с этим браузером",
"You are already in a call": "Вы уже находитесь в разговоре",
"You're not in any rooms yet! Press": "Вы еще не находитесь ни в каких комнатах! Нажать",
"You are trying to access %(roomName)s": "Вы пытаетесь получить доступ %(roomName)s",
"You cannot place a call with yourself": "Вы не можете позвонить самим себе",
- "%(senderName)s withdrew %(targetName)s's inivitation": "%(senderName)s анулировал %(targetName)s's преглашение",
+ "%(senderName)s withdrew %(targetName)s's inivitation.": "%(senderName)s анулировал %(targetName)s's преглашение.",
"Sep": "Сен.",
"Jan": "Янв.",
"Feb": "Фев.",
@@ -389,10 +389,10 @@
"Your email address does not appear to be associated with a Matrix ID on this Homeserver": "Ваш адрес электронной почты, кажется, не связан с Matrix ID на этом Homeserver",
"to start a chat with someone": "Начать чат с кем-то",
"to tag direct chat": "Пометить прямой чат",
- "To use it, just wait for autocomplete results to load and tab through them": "Для его использования, просто подождите результатов автозаполнения для загрузки на вкладке и через них",
+ "To use it, just wait for autocomplete results to load and tab through them.": "Для его использования, просто подождите результатов автозаполнения для загрузки на вкладке и через них.",
"%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s)": "%(senderName)s включил сквозное шифрование (algorithm %(algorithm)s)",
"Unable to restore previous session": "Невозможно востановить предыдущий сеанс",
- "%(senderName)s unbanned %(targetName)s": "%(senderName)s запрет отменен %(targetName)s",
+ "%(senderName)s unbanned %(targetName)s.": "%(senderName)s запрет отменен %(targetName)s.",
"Unable to capture screen": "Невозможно записать снимок экрана",
"Unable to enable Notifications": "Невозможно включить уведомления",
"Upload Failed": "Неудавшаяся загрузка",
From dac6a3360b38da1269b833689015114849551fb2 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Thu, 25 May 2017 19:25:06 +0100
Subject: [PATCH 0215/1016] Oops, don't change these reason strings.
---
src/TextForEvent.js | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/TextForEvent.js b/src/TextForEvent.js
index d5a54927a0..88872807e3 100644
--- a/src/TextForEvent.js
+++ b/src/TextForEvent.js
@@ -94,15 +94,15 @@ function textForMemberEvent(ev) {
}
else if (ev.getPrevContent().membership === "join") {
return _t(
- '%(senderName)s kicked %(targetName)s. %(reason)s',
- {senderName: senderName, targetName: targetName, reason}
- );
+ '%(senderName)s kicked %(targetName)s.',
+ {senderName: senderName, targetName: targetName}
+ ) + '. ' + reason;
}
else if (ev.getPrevContent().membership === "invite") {
return _t(
- '%(senderName)s withdrew %(targetName)s\'s inivitation. %(reason)s',
- {senderName: senderName, targetName: targetName, reason: reason}
- );
+ '%(senderName)s withdrew %(targetName)s\'s inivitation.',
+ {senderName: senderName, targetName: targetName}
+ ) + '. ' + reason;
}
else {
return _t('%(targetName)s left the room.', {targetName: targetName});
From be4944a4b68a58e2976cef472a1b9f3e04239de9 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Thu, 25 May 2017 19:43:34 +0100
Subject: [PATCH 0216/1016] Hopefully get translations with '.'s sorted
attempt 2
---
src/TextForEvent.js | 18 +++++++++---------
src/i18n/strings/de_DE.json | 2 +-
src/i18n/strings/en_EN.json | 2 +-
src/i18n/strings/fr.json | 2 +-
src/i18n/strings/pt_BR.json | 2 +-
src/i18n/strings/ru.json | 2 +-
6 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/src/TextForEvent.js b/src/TextForEvent.js
index 88872807e3..2d53c5db9f 100644
--- a/src/TextForEvent.js
+++ b/src/TextForEvent.js
@@ -47,9 +47,9 @@ function textForMemberEvent(ev) {
}
case 'ban':
return _t(
- '%(senderName)s banned %(targetName)s. %(reason)s.',
- {senderName: senderName, targetName: targetName, reason: reason}
- );
+ '%(senderName)s banned %(targetName)s.',
+ {senderName: senderName, targetName: targetName}
+ ) + ' ' + reason;
case 'join':
if (ev.getPrevContent() && ev.getPrevContent().membership == 'join') {
if (ev.getPrevContent().displayname && ev.getContent().displayname && ev.getPrevContent().displayname != ev.getContent().displayname) {
@@ -90,19 +90,19 @@ function textForMemberEvent(ev) {
}
}
else if (ev.getPrevContent().membership === "ban") {
- return _t('%(senderName)s unbanned %(targetName)s.', {senderName: senderName, targetName: targetName}) + '.';
+ return _t('%(senderName)s unbanned %(targetName)s.', {senderName: senderName, targetName: targetName});
}
else if (ev.getPrevContent().membership === "join") {
return _t(
'%(senderName)s kicked %(targetName)s.',
{senderName: senderName, targetName: targetName}
- ) + '. ' + reason;
+ ) + ' ' + reason;
}
else if (ev.getPrevContent().membership === "invite") {
return _t(
'%(senderName)s withdrew %(targetName)s\'s inivitation.',
{senderName: senderName, targetName: targetName}
- ) + '. ' + reason;
+ ) + ' ' + reason;
}
else {
return _t('%(targetName)s left the room.', {targetName: targetName});
@@ -135,13 +135,13 @@ function textForMessageEvent(ev) {
function textForCallAnswerEvent(event) {
var senderName = event.sender ? event.sender.name : _t('Someone');
var supported = MatrixClientPeg.get().supportsVoip() ? "" : _t('(not supported by this browser)');
- return _t('%(senderName)s answered the call', {senderName: senderName}) + '. ' + supported;
+ return _t('%(senderName)s answered the call', {senderName: senderName}) + ' ' + supported;
}
function textForCallHangupEvent(event) {
var senderName = event.sender ? event.sender.name : _t('Someone');
var supported = MatrixClientPeg.get().supportsVoip() ? "" : _t('(not supported by this browser)');
- return _t('%(senderName)s ended the call', {senderName: senderName}) + '. ' + supported;
+ return _t('%(senderName)s ended the call', {senderName: senderName}) + ' ' + supported;
}
function textForCallInviteEvent(event) {
@@ -153,7 +153,7 @@ function textForCallInviteEvent(event) {
type = "video";
}
var supported = MatrixClientPeg.get().supportsVoip() ? "" : _t('(not supported by this browser)');
- return _t('%(senderName)s placed a %(callType)s call.', {senderName: senderName, callType: type}) + '. ' + supported;
+ return _t('%(senderName)s placed a %(callType)s call.', {senderName: senderName, callType: type}) + ' ' + supported;
}
function textForThreePidInviteEvent(event) {
diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json
index 90395a08c0..dc1cc1dbb6 100644
--- a/src/i18n/strings/de_DE.json
+++ b/src/i18n/strings/de_DE.json
@@ -360,7 +360,7 @@
"%(senderDisplayName)s changed the room name to %(roomName)s": "%(senderDisplayName)s änderte den Raumnamen zu %(roomName)s",
"%(senderDisplayName)s changed the topic to %(topic)s": "%(senderDisplayName)s änderte das Thema zu %(topic)s",
"/ddg is not a command": "/ddg ist kein Kommando",
- "%(senderName)s ended the call.": "%(senderName)s beendete den Anruf.",
+ "%(senderName)s ended the call": "%(senderName)s beendete den Anruf",
"Failed to lookup current room": "Aktuellen Raum nachzuschlagen schlug fehl",
"Failed to send request.": "Anfrage zu senden schlug fehl.",
"%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s von %(fromPowerLevel)s zu %(toPowerLevel)s",
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index 9c69233692..13a65e6a9c 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -233,7 +233,7 @@
"Enable encryption": "Enable encryption",
"Encrypted messages will not be visible on clients that do not yet implement encryption": "Encrypted messages will not be visible on clients that do not yet implement encryption",
"Encrypted room": "Encrypted room",
- "%(senderName)s ended the call.": "%(senderName)s ended the call.",
+ "%(senderName)s ended the call": "%(senderName)s ended the call",
"End-to-end encryption information": "End-to-end encryption information",
"End-to-end encryption is in beta and may not be reliable": "End-to-end encryption is in beta and may not be reliable",
"Enter Code": "Enter Code",
diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json
index eba5b5a0fc..e393d27bda 100644
--- a/src/i18n/strings/fr.json
+++ b/src/i18n/strings/fr.json
@@ -137,7 +137,7 @@
"Enable encryption": "Enable encryption",
"Encrypted messages will not be visible on clients that do not yet implement encryption": "Encrypted messages will not be visible on clients that do not yet implement encryption",
"Encrypted room": "Encrypted room",
- "%(senderName)s ended the call.": "%(senderName)s ended the call.",
+ "%(senderName)s ended the call": "%(senderName)s ended the call",
"End-to-end encryption information": "End-to-end encryption information",
"End-to-end encryption is in beta and may not be reliable": "End-to-end encryption is in beta and may not be reliable",
"Enter Code": "Enter Code",
diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json
index 5194f4cc6c..2aaca8b9d7 100644
--- a/src/i18n/strings/pt_BR.json
+++ b/src/i18n/strings/pt_BR.json
@@ -333,7 +333,7 @@
"/ddg is not a command": "/ddg não é um comando",
"Drop here %(toAction)s": "Arraste aqui %(toAction)s",
"Drop here to tag %(section)s": "Arraste aqui para marcar como %(section)s",
- "%(senderName)s ended the call.": "%(senderName)s finalizou a chamada.",
+ "%(senderName)s ended the call": "%(senderName)s finalizou a chamada",
"Existing Call": "Chamada em andamento",
"Failed to lookup current room": "Não foi possível buscar na sala atual",
"Failed to send email": "Não foi possível enviar email",
diff --git a/src/i18n/strings/ru.json b/src/i18n/strings/ru.json
index ea23d16d04..c211bace3b 100644
--- a/src/i18n/strings/ru.json
+++ b/src/i18n/strings/ru.json
@@ -239,7 +239,7 @@
"/ddg is not a command": "/ddg не команда",
"Drop here %(toAction)s": "Вставить здесь %(toAction)s",
"Drop here to tag %(section)s": "Вставить здесь для тега %(section)s",
- "%(senderName)s ended the call.": "%(senderName)s прекратил звонок.",
+ "%(senderName)s ended the call": "%(senderName)s прекратил звонок",
"Existing Call": "Существующий вызов",
"Failed to lookup current room": "Не удалось выполнить поиск текущий комнаты",
"Failed to send request.": "Не удалось выслать запрос.",
From e7fbf889fae4daf89b75fc67af0e7435f0a35ed3 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Thu, 25 May 2017 19:53:27 +0100
Subject: [PATCH 0217/1016] Make current language more accurate
Make it reflect reality if it's been taken from the browser langs
by just getting what was saet in counterpart rather than the saved
setting.
Also fix style.
---
src/components/structures/UserSettings.js | 18 ++++++------------
src/languageHandler.js | 4 ++++
2 files changed, 10 insertions(+), 12 deletions(-)
diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js
index b2bf693cfe..c9a9c58939 100644
--- a/src/components/structures/UserSettings.js
+++ b/src/components/structures/UserSettings.js
@@ -202,16 +202,10 @@ module.exports = React.createClass({
this._syncedSettings = syncedSettings;
this._localSettings = UserSettingsStore.getLocalSettings();
- if (!this._localSettings.hasOwnProperty('language')) {
- const language = languageHandler.normalizeLanguageKey(languageHandler.getLanguageFromBrowser());
- this.setState({
- Language: language
- });
- } else {
- this.setState({
- Language: this._localSettings.language
- });
- }
+
+ this.setState({
+ language: languageHandler.getCurrentLanguage(),
+ });
},
componentDidMount: function() {
@@ -551,7 +545,7 @@ module.exports = React.createClass({
onLanguageChange: function(l) {
UserSettingsStore.setLocalSetting('language', l);
this.setState({
- Language: l,
+ language: l,
});
PlatformPeg.get().reload();
},
@@ -562,7 +556,7 @@ module.exports = React.createClass({
{_t('Interface Language')}
;
},
diff --git a/src/languageHandler.js b/src/languageHandler.js
index 4b49c95fc9..6734c99582 100644
--- a/src/languageHandler.js
+++ b/src/languageHandler.js
@@ -116,6 +116,10 @@ export function normalizeLanguageKey(language) {
return language.toLowerCase().replace("_","-");
};
+export function getCurrentLanguage() {
+ return counterpart.getLocale();
+}
+
function getLangsJson() {
const deferred = q.defer();
From ab92cc2254f0fb9c45f959cc9ff7c9b5e0c00831 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Thu, 25 May 2017 19:55:54 +0100
Subject: [PATCH 0218/1016] Question marks do not internationalise
---
src/components/structures/UserSettings.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js
index c9a9c58939..f441525f96 100644
--- a/src/components/structures/UserSettings.js
+++ b/src/components/structures/UserSettings.js
@@ -292,7 +292,7 @@ module.exports = React.createClass({
onLogoutClicked: function(ev) {
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createDialog(QuestionDialog, {
- title: _t("Sign out") + "?",
+ title: _t("Sign out"),
description:
{ _t("For security, logging out will delete any end-to-end encryption keys from this browser. If you want to be able to decrypt your conversation history from future Riot sessions, please export your room keys for safe-keeping") }.
From 2d6bf5fa910011405d4bc0b7dc316b866a6c88ba Mon Sep 17 00:00:00 2001
From: David Baker
Date: Thu, 25 May 2017 20:04:28 +0100
Subject: [PATCH 0219/1016] Remove messages with HTML formatting
The translations strings are not HTML and it gets escaped.
---
src/components/structures/RoomView.js | 4 ++--
src/components/views/rooms/RoomSettings.js | 6 +++---
src/i18n/strings/da.json | 2 +-
src/i18n/strings/de_DE.json | 8 ++++----
src/i18n/strings/en_EN.json | 8 ++++----
src/i18n/strings/pt.json | 8 ++++----
src/i18n/strings/pt_BR.json | 8 ++++----
src/i18n/strings/ru.json | 6 +++---
8 files changed, 25 insertions(+), 25 deletions(-)
diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js
index badc7cd01d..4b3b09f1fa 100644
--- a/src/components/structures/RoomView.js
+++ b/src/components/structures/RoomView.js
@@ -536,8 +536,8 @@ module.exports = React.createClass({
description: (
{ _t("End-to-end encryption is in beta and may not be reliable") }.
-
{ _t("You should not yet trust it to secure data") }.
-
{ _t("Devices will not yet be able to decrypt history from before they joined the room") }.
+
{ _t("You should not yet trust it to secure data") }.
+
{ _t("Devices will not yet be able to decrypt history from before they joined the room") }.
{ _t("Encrypted messages will not be visible on clients that do not yet implement encryption") }.
),
diff --git a/src/components/views/rooms/RoomSettings.js b/src/components/views/rooms/RoomSettings.js
index bff668ac85..e50bcdde46 100644
--- a/src/components/views/rooms/RoomSettings.js
+++ b/src/components/views/rooms/RoomSettings.js
@@ -542,9 +542,9 @@ module.exports = React.createClass({
description: (
{ _t('End-to-end encryption is in beta and may not be reliable') }.
-
{ _t('You should not yet trust it to secure data') }.
-
{ _t('Devices will not yet be able to decrypt history from before they joined the room') }.
-
{ _t('Once encryption is enabled for a room it cannot be turned off again (for now)') }.
+
{ _t('You should not yet trust it to secure data') }.
+
{ _t('Devices will not yet be able to decrypt history from before they joined the room') }.
+
{ _t('Once encryption is enabled for a room it cannot be turned off again (for now)') }.
{ _t('Encrypted messages will not be visible on clients that do not yet implement encryption') }.
),
diff --git a/src/i18n/strings/da.json b/src/i18n/strings/da.json
index 13491eb0b9..72e0ae60c2 100644
--- a/src/i18n/strings/da.json
+++ b/src/i18n/strings/da.json
@@ -100,7 +100,7 @@
"decline": "nedgang",
"Default": "Standard",
"demote": "degradere",
- "Devices will not yet be able to decrypt history from before they joined the room": "Enhederne vil ikke være i stand til at dekryptere historikken fra, før de kom til rummet",
+ "Devices will not yet be able to decrypt history from before they joined the room": "Enhederne vil ikke være i stand til at dekryptere historikken fra, før de kom til rummet",
"Direct Chat": "Personligt Chat",
"Disable inline URL previews by default": "Deaktiver forrige weblinks forhåndsvisninger som standard",
"Display name": "Visningsnavn",
diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json
index dc1cc1dbb6..ffe46f5723 100644
--- a/src/i18n/strings/de_DE.json
+++ b/src/i18n/strings/de_DE.json
@@ -84,7 +84,7 @@
"Deactivate Account": "Deaktiviere Account",
"Deactivate my account": "Deaktiviere meinen Account",
"decline": "Ablehnen",
- "Devices will not yet be able to decrypt history from before they joined the room": "Geräte werden nicht in der Lage sein, die Historie vor dem Beitritt in den Raum zu entschlüsseln",
+ "Devices will not yet be able to decrypt history from before they joined the room": "Geräte werden nicht in der Lage sein, die Historie vor dem Beitritt in den Raum zu entschlüsseln",
"Display name": "Anzeigename",
"Email Address": "E-Mail-Adresse",
"Email, name or matrix ID": "E-Mail, Name oder Matrix-ID",
@@ -163,7 +163,7 @@
"": "",
"No users have specific privileges in this room": "Keine Nutzer haben in diesem Raum besondere Berechtigungen",
"olm version": "OLM-Version",
- "Once encryption is enabled for a room it cannot be turned off again (for now)": "Sobald Verschlüsselung für einen Raum aktiviert wird, kann diese (aktuell noch) nicht wieder deaktiviert werden",
+ "Once encryption is enabled for a room it cannot be turned off again (for now)": "Sobald Verschlüsselung für einen Raum aktiviert wird, kann diese (aktuell noch) nicht wieder deaktiviert werden",
"Only people who have been invited": "Nur Personen die eingeladen wurden",
"or": "oder",
"other": "weiteres",
@@ -186,7 +186,7 @@
"removed their display name": "löschte den eigenen Anzeigenamen",
"Remove": "Entferne",
"requested a VoIP conference": "hat eine VoIP-Konferenz angefordert",
- "Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved ": "Eine Passwortänderung sorgt aktuell dafür, dass alle Ende-zu-Ende-Schlüssel von allen Geräten zurückgesetzt werden. Dadurch wird die verschlüsselte Chat-Historie unlesbar, es sei denn Sie exportieren vorher Ihre Raum-Schlüssel und importieren sie nachher wieder. In Zukunft wird dies verbessert ",
+ "Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved": "Eine Passwortänderung sorgt aktuell dafür, dass alle Ende-zu-Ende-Schlüssel von allen Geräten zurückgesetzt werden. Dadurch wird die verschlüsselte Chat-Historie unlesbar, es sei denn Sie exportieren vorher Ihre Raum-Schlüssel und importieren sie nachher wieder. In Zukunft wird dies verbessert",
"restore": "Zum zurücksetzen",
"Return to login screen": "Zur Anmeldung zurückkehren",
"Room Colour": "Raumfarbe",
@@ -277,7 +277,7 @@
"You have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, sign in again on each device": "Du wurdest von allen Geräten ausgeloggt und wirst keine Push-Benachrichtigungen mehr bekommen. Um Benachrichtigungen zu reaktivieren melde dich auf jedem Gerät neu an",
"you must be a": "nötige Rolle",
"Your password has been reset": "Dein Passwort wurde zurückgesetzt",
- "You should not yet trust it to secure data": "Du solltest nicht darauf vertrauen um deine Daten abzusichern",
+ "You should not yet trust it to secure data": "Du solltest nicht darauf vertrauen um deine Daten abzusichern",
"removed their profile picture": "löschte das eigene Profilbild",
"times": "mal",
"Bulk Options": "Bulk-Optionen",
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index 13a65e6a9c..9afe796086 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -215,7 +215,7 @@
"Deops user with given id": "Deops user with given id",
"Device ID": "Device ID",
"Devices": "Devices",
- "Devices will not yet be able to decrypt history from before they joined the room": "Devices will not yet be able to decrypt history from before they joined the room",
+ "Devices will not yet be able to decrypt history from before they joined the room": "Devices will not yet be able to decrypt history from before they joined the room",
"Direct Chat": "Direct Chat",
"Direct chats": "Direct chats",
"Disable inline URL previews by default": "Disable inline URL previews by default",
@@ -354,7 +354,7 @@
"No users have specific privileges in this room": "No users have specific privileges in this room",
"OK": "OK",
"olm version:": "olm version:",
- "Once encryption is enabled for a room it cannot be turned off again (for now)": "Once encryption is enabled for a room it cannot be turned off again (for now)",
+ "Once encryption is enabled for a room it cannot be turned off again (for now)": "Once encryption is enabled for a room it cannot be turned off again (for now)",
"Once you've followed the link it contains, click below": "Once you've followed the link it contains, click below",
"Only people who have been invited": "Only people who have been invited",
"or": "or",
@@ -384,7 +384,7 @@
"Remove": "Remove",
"%(senderName)s requested a VoIP conference": "%(senderName)s requested a VoIP conference",
"Report it": "Report it",
- "Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved ": "Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved ",
+ "Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved": "Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved",
"restore": "restore",
"Return to app": "Return to app",
"Return to login screen": "Return to login screen",
@@ -542,7 +542,7 @@
"Your password was successfully changed. You will not receive push notifications on other devices until you log back in to them": "Your password was successfully changed. You will not receive push notifications on other devices until you log back in to them",
"You seem to be in a call, are you sure you want to quit?": "You seem to be in a call, are you sure you want to quit?",
"You seem to be uploading files, are you sure you want to quit?": "You seem to be uploading files, are you sure you want to quit?",
- "You should not yet trust it to secure data": "You should not yet trust it to secure data",
+ "You should not yet trust it to secure data": "You should not yet trust it to secure data",
"You will not be able to undo this change as you are promoting the user to have the same power level as yourself": "You will not be able to undo this change as you are promoting the user to have the same power level as yourself",
"Sun": "Sun",
"Mon": "Mon",
diff --git a/src/i18n/strings/pt.json b/src/i18n/strings/pt.json
index d166b3a273..f14f2b31f4 100644
--- a/src/i18n/strings/pt.json
+++ b/src/i18n/strings/pt.json
@@ -60,7 +60,7 @@
"demote": "reduzir prioridade",
"Deops user with given id": "Retirar função de moderador do usuário com o identificador informado",
"Device ID": "Identificador do dispositivo",
- "Devices will not yet be able to decrypt history from before they joined the room": "Os dispositivos não serão ainda capazes de descriptografar o histórico anterior à sua entrada na sala",
+ "Devices will not yet be able to decrypt history from before they joined the room": "Os dispositivos não serão ainda capazes de descriptografar o histórico anterior à sua entrada na sala",
"Direct Chat": "Conversa pessoal",
"Disable inline URL previews by default": "Desabilitar visualizações prévias por padrão",
"Display name": "Nome",
@@ -146,7 +146,7 @@
"NOT verified": "NÃO verificado",
"No users have specific privileges in this room": "Nenhum/a usuário/a possui privilégios específicos nesta sala",
"olm version: ": "Versão do olm: ",
- "Once encryption is enabled for a room it cannot be turned off again (for now)": "Assim que a criptografia é ativada para uma sala, ela não poderá ser desativada novamente (ainda)",
+ "Once encryption is enabled for a room it cannot be turned off again (for now)": "Assim que a criptografia é ativada para uma sala, ela não poderá ser desativada novamente (ainda)",
"Once you've followed the link it contains, click below": "Quando você tiver clicado no link que está no email, clique o botão abaixo",
"Only people who have been invited": "Apenas pessoas que tenham sido convidadas",
"or": "ou",
@@ -171,7 +171,7 @@
"removed their profile picture": "removeu sua foto de perfil",
"Remove": "Remover",
"requested a VoIP conference": "requisitou uma conferência VoIP",
- "Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved ": "Atualmente, ao alterar sua senha, você também zera todas as chaves de criptografia ponta-a-ponta em todos os dipositivos, fazendo com que o histórico de conversas da sala não possa mais ser lido, a não ser que você antes exporte suas chaves de sala e as reimporte após a alteração da senha. No futuro, isso será melhorado ",
+ "Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved": "Atualmente, ao alterar sua senha, você também zera todas as chaves de criptografia ponta-a-ponta em todos os dipositivos, fazendo com que o histórico de conversas da sala não possa mais ser lido, a não ser que você antes exporte suas chaves de sala e as reimporte após a alteração da senha. No futuro, isso será melhorado",
"restore": "restaurar",
"Return to app": "Retornar ao aplicativo",
"Return to login screen": "Retornar à tela de login",
@@ -277,7 +277,7 @@
"you must be a": "você precisa ser",
"Your password has been reset": "Sua senha foi redefinida",
"Your password was successfully changed. You will not receive push notifications on other devices until you log back in to them": "Sua senha foi alterada com sucesso. Você não receberá notificações em outros dispositivos até que você logue novamente por eles",
- "You should not yet trust it to secure data": "Você não deve confiar nela ainda para preservar seus dados",
+ "You should not yet trust it to secure data": "Você não deve confiar nela ainda para preservar seus dados",
"Sun": "Dom",
"Mon": "Seg",
"Tue": "Ter",
diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json
index 2aaca8b9d7..1aebaab694 100644
--- a/src/i18n/strings/pt_BR.json
+++ b/src/i18n/strings/pt_BR.json
@@ -63,7 +63,7 @@
"demote": "reduzir prioridade",
"Deops user with given id": "Retirar função de moderador do usuário com o identificador informado",
"Device ID": "Identificador do dispositivo",
- "Devices will not yet be able to decrypt history from before they joined the room": "Os dispositivos não serão ainda capazes de descriptografar o histórico anterior à sua entrada na sala",
+ "Devices will not yet be able to decrypt history from before they joined the room": "Os dispositivos não serão ainda capazes de descriptografar o histórico anterior à sua entrada na sala",
"Direct Chat": "Conversa pessoal",
"Disable inline URL previews by default": "Desabilitar visualizações prévias por padrão",
"Display name": "Nome",
@@ -149,7 +149,7 @@
"NOT verified": "NÃO verificado",
"No users have specific privileges in this room": "Nenhum/a usuário/a possui privilégios específicos nesta sala",
"olm version: ": "Versão do olm: ",
- "Once encryption is enabled for a room it cannot be turned off again (for now)": "Assim que a criptografia é ativada para uma sala, ela não poderá ser desativada novamente (ainda)",
+ "Once encryption is enabled for a room it cannot be turned off again (for now)": "Assim que a criptografia é ativada para uma sala, ela não poderá ser desativada novamente (ainda)",
"Once you've followed the link it contains, click below": "Quando você tiver clicado no link que está no email, clique o botão abaixo",
"Only people who have been invited": "Apenas pessoas que tenham sido convidadas",
"or": "ou",
@@ -175,7 +175,7 @@
"removed their profile picture": "removeu sua foto de perfil",
"Remove": "Remover",
"requested a VoIP conference": "requisitou uma conferência VoIP",
- "Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved ": "Atualmente, ao alterar sua senha, você também zera todas as chaves de criptografia ponta-a-ponta em todos os dipositivos, fazendo com que o histórico de conversas da sala não possa mais ser lido, a não ser que você antes exporte suas chaves de sala e as reimporte após a alteração da senha. No futuro, isso será melhorado ",
+ "Resetting password will currently reset any end-to-end encryption keys on all devices, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved": "Atualmente, ao alterar sua senha, você também zera todas as chaves de criptografia ponta-a-ponta em todos os dipositivos, fazendo com que o histórico de conversas da sala não possa mais ser lido, a não ser que você antes exporte suas chaves de sala e as reimporte após a alteração da senha. No futuro, isso será melhorado",
"restore": "restaurar",
"Return to app": "Retornar ao aplicativo",
"Return to login screen": "Retornar à tela de login",
@@ -282,7 +282,7 @@
"you must be a": "você precisa ser",
"Your password has been reset": "Sua senha foi redefinida",
"Your password was successfully changed. You will not receive push notifications on other devices until you log back in to them": "Sua senha foi alterada com sucesso. Você não receberá notificações em outros dispositivos até que você logue novamente por eles",
- "You should not yet trust it to secure data": "Você não deve confiar nela ainda para preservar seus dados",
+ "You should not yet trust it to secure data": "Você não deve confiar nela ainda para preservar seus dados",
"Sun": "Dom",
"Mon": "Seg",
"Tue": "Ter",
diff --git a/src/i18n/strings/ru.json b/src/i18n/strings/ru.json
index c211bace3b..2455317292 100644
--- a/src/i18n/strings/ru.json
+++ b/src/i18n/strings/ru.json
@@ -58,7 +58,7 @@
"demote": "понижать",
"Deops user with given id": "Deops пользователь с данным id",
"Device ID": "Устройство ID",
- "Devices will not yet be able to decrypt history from before they joined the room": "Устройство еще не будет в состоянии дешифровать историю, до присоединения к комнате",
+ "Devices will not yet be able to decrypt history from before they joined the room": "Устройство еще не будет в состоянии дешифровать историю, до присоединения к комнате",
"Direct Chat": "Персональное сообщение",
"Disable inline URL previews by default": "Отключить встроенные предварительные просмотры URL по умолчанию",
"Display name": "Отображаемое имя",
@@ -136,7 +136,7 @@
"NOT verified": "НЕ проверенный",
"No users have specific privileges in this room": "Ни у каких пользователей нет специальных полномочий в этой комнате",
"olm version": "olm версия",
- "Once encryption is enabled for a room it cannot be turned off again (for now)": "Как только шифрование включено для комнаты, оно не может быть выключено снова (на данный момент)",
+ "Once encryption is enabled for a room it cannot be turned off again (for now)": "Как только шифрование включено для комнаты, оно не может быть выключено снова (на данный момент)",
"or": "или",
"other": "другой",
"others": "другие",
@@ -210,7 +210,7 @@
"you must be a": "Вы должны быть",
"Your password has been reset": "Ваш пароль был изменен",
"Your password was successfully changed. You will not receive push notifications on other devices until you log back in to them": "Ваш пароль был успешно изменен. Вы не будете получать Push-уведомления о других устройствах, пока не войдете обратно на них",
- "You should not yet trust it to secure data": "Вы еще не должны доверять этому защиту данных",
+ "You should not yet trust it to secure data": "Вы еще не должны доверять этому защиту данных",
"en": "Английский",
"pt-br": "Португальский Бразилия",
"de": "Немецкий",
From ba8e37a84ef52c6d6173d655df20f6eaba0baf26 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Thu, 25 May 2017 23:13:49 +0100
Subject: [PATCH 0220/1016] Don't include src in the test resolve root
Don't include src in resolve root for the karma test, as otherwise
modules from react sdk get pulled in instead of npm libraries like
'extend' which breaks everything in really subtle ways.
---
karma.conf.js | 1 -
test/components/views/dialogs/InteractiveAuthDialog-test.js | 2 +-
test/components/views/rooms/MessageComposerInput-test.js | 2 +-
test/utils/MegolmExportEncryption-test.js | 2 +-
4 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/karma.conf.js b/karma.conf.js
index 3495a981be..13e88350c1 100644
--- a/karma.conf.js
+++ b/karma.conf.js
@@ -166,7 +166,6 @@ module.exports = function (config) {
'sinon': 'sinon/pkg/sinon.js',
},
root: [
- path.resolve('./src'),
path.resolve('./test'),
],
},
diff --git a/test/components/views/dialogs/InteractiveAuthDialog-test.js b/test/components/views/dialogs/InteractiveAuthDialog-test.js
index b8a8e49769..c8f16225de 100644
--- a/test/components/views/dialogs/InteractiveAuthDialog-test.js
+++ b/test/components/views/dialogs/InteractiveAuthDialog-test.js
@@ -22,7 +22,7 @@ import ReactTestUtils from 'react-addons-test-utils';
import sinon from 'sinon';
import sdk from 'matrix-react-sdk';
-import MatrixClientPeg from 'MatrixClientPeg';
+import MatrixClientPeg from '../../../../src/MatrixClientPeg';
import * as test_utils from '../../../test-utils';
diff --git a/test/components/views/rooms/MessageComposerInput-test.js b/test/components/views/rooms/MessageComposerInput-test.js
index ca2bbba2eb..67e788e2eb 100644
--- a/test/components/views/rooms/MessageComposerInput-test.js
+++ b/test/components/views/rooms/MessageComposerInput-test.js
@@ -8,7 +8,7 @@ import * as testUtils from '../../../test-utils';
import sdk from 'matrix-react-sdk';
import UserSettingsStore from '../../../../src/UserSettingsStore';
const MessageComposerInput = sdk.getComponent('views.rooms.MessageComposerInput');
-import MatrixClientPeg from 'MatrixClientPeg';
+import MatrixClientPeg from '../../../../src/MatrixClientPeg';
function addTextToDraft(text) {
const components = document.getElementsByClassName('public-DraftEditor-content');
diff --git a/test/utils/MegolmExportEncryption-test.js b/test/utils/MegolmExportEncryption-test.js
index 0c49fd48d1..1c90c7a030 100644
--- a/test/utils/MegolmExportEncryption-test.js
+++ b/test/utils/MegolmExportEncryption-test.js
@@ -16,7 +16,7 @@ limitations under the License.
"use strict";
-import * as MegolmExportEncryption from 'utils/MegolmExportEncryption';
+import * as MegolmExportEncryption from '../../src/utils/MegolmExportEncryption';
import * as testUtils from '../test-utils';
import expect from 'expect';
From b5b157a0fb0bae097fc0cb62af5169b59eae31f4 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Fri, 26 May 2017 10:34:36 +0100
Subject: [PATCH 0221/1016] Don't show notif nag bar if guest
---
src/components/structures/LoggedInView.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js
index 8cd2bf8a71..e7c1e00008 100644
--- a/src/components/structures/LoggedInView.js
+++ b/src/components/structures/LoggedInView.js
@@ -253,6 +253,7 @@ export default React.createClass({
break;
}
+ const isGuest = this.props.matrixClient.isGuest();
var topBar;
if (this.props.hasNewVersion) {
topBar = ;
} else if (this.state.userHasGeneratedPassword) {
topBar = ;
- } else if (Notifier.supportsDesktopNotifications() && !Notifier.isEnabled() && !Notifier.isToolbarHidden()) {
+ } else if (!isGuest && Notifier.supportsDesktopNotifications() && !Notifier.isEnabled() && !Notifier.isToolbarHidden()) {
topBar = ;
}
From 2dcc03960a5b9fb362e3fd6a140cc28a61c85344 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Fri, 26 May 2017 11:46:33 +0100
Subject: [PATCH 0222/1016] Set the displayname to the mxid once PWLU
---
src/components/structures/MatrixChat.js | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index a6f2ee820f..6d827b3ef0 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -805,6 +805,12 @@ module.exports = React.createClass({
dis.dispatch({action: 'view_home_page'});
} else if (this._is_registered) {
this._is_registered = false;
+
+ // Set the display name = entered username
+ MatrixClientPeg.get().setDisplayName(
+ MatrixClientPeg.get().getUserIdLocalpart()
+ );
+
if (this.props.config.welcomeUserId) {
createRoom({
dmUserId: this.props.config.welcomeUserId,
From c0f43a14fd4c5b61bfe05e820a0091d4afa951d8 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Fri, 26 May 2017 11:47:55 +0100
Subject: [PATCH 0223/1016] Improve comment
---
src/components/structures/MatrixChat.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index 6d827b3ef0..3c23f649e3 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -806,7 +806,7 @@ module.exports = React.createClass({
} else if (this._is_registered) {
this._is_registered = false;
- // Set the display name = entered username
+ // Set the display name = user ID localpart
MatrixClientPeg.get().setDisplayName(
MatrixClientPeg.get().getUserIdLocalpart()
);
From 2400efa92bc2f3622f02a0492102182ff935b613 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Fri, 26 May 2017 11:48:38 +0100
Subject: [PATCH 0224/1016] Correct LifecycleStore docs
---
src/stores/LifecycleStore.js | 7 -------
1 file changed, 7 deletions(-)
diff --git a/src/stores/LifecycleStore.js b/src/stores/LifecycleStore.js
index 43e2de9d52..3ed2e5c045 100644
--- a/src/stores/LifecycleStore.js
+++ b/src/stores/LifecycleStore.js
@@ -20,13 +20,6 @@ import {Store} from 'flux/utils';
* A class for storing application state to do with login/registration. This is a simple
* flux store that listens for actions and updates its state accordingly, informing any
* listeners (views) of state changes.
- *
- * Usage:
- * ```
- * lifecycleStore.addListener(() => {
- * this.setState({ cachedPassword: lifecycleStore.getCachedPassword() })
- * })
- * ```
*/
class LifecycleStore extends Store {
constructor() {
From ad3373789fe094d5790119aaf5002205f1d92eb1 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Fri, 26 May 2017 11:50:32 +0100
Subject: [PATCH 0225/1016] Warn about LifecycleStore not explicitly being used
---
src/components/structures/MatrixChat.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index a6f2ee820f..ef2882fe13 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -32,6 +32,7 @@ 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';
From e44f3cc7096a8370a3dff464e14c8f9cf26df65e Mon Sep 17 00:00:00 2001
From: David Baker
Date: Fri, 26 May 2017 11:58:45 +0100
Subject: [PATCH 0226/1016] Fix tests
* Serve translation files from the karma server
* Port UserSettingsStore to ES6 exports because the test runner
gets confused by ES6 importing a commonjs module
* Remove extra spaces in translations strings for MELS
* Fix 'his/her' back to be 'their'
* Change test to expect singular 'invitation' for a single person
(there may be multiple invitations, but IMO this should be
'rejected n invitations' and we can play with the wording later,
I don't think the singular is any worse than the plural).
* set language in the MELS tests (and wait for it to complete)
* Don't bother setting lang in other tests for now
---
karma.conf.js | 9 ++-
src/UserSettingsStore.js | 3 +-
.../views/elements/MemberEventListSummary.js | 64 +++++++++---------
src/i18n/strings/de_DE.json | 66 +++++++++----------
src/i18n/strings/en_EN.json | 64 +++++++++---------
src/i18n/strings/pt_BR.json | 64 +++++++++---------
.../elements/MemberEventListSummary-test.js | 13 ++--
test/test-utils.js | 6 --
8 files changed, 146 insertions(+), 143 deletions(-)
diff --git a/karma.conf.js b/karma.conf.js
index 13e88350c1..4ad72b4927 100644
--- a/karma.conf.js
+++ b/karma.conf.js
@@ -55,11 +55,18 @@ module.exports = function (config) {
// some images to reduce noise from the tests
{pattern: 'test/img/*', watched: false, included: false,
served: true, nocache: false},
+ // translation files
+ {pattern: 'src/i18n/strings/*', watcheed: false, included: false, served: true},
+ {pattern: 'test/i18n/*', watched: false, included: false, served: true},
],
- // redirect img links to the karma server
proxies: {
+ // redirect img links to the karma server
"/img/": "/base/test/img/",
+ // special languages.json file for the tests
+ "/i18n/languages.json": "/base/test/i18n/languages.json",
+ // and redirect i18n requests
+ "/i18n/": "/base/src/i18n/strings/",
},
// list of files to exclude
diff --git a/src/UserSettingsStore.js b/src/UserSettingsStore.js
index fd5ccb0de3..5b96692dc9 100644
--- a/src/UserSettingsStore.js
+++ b/src/UserSettingsStore.js
@@ -14,7 +14,6 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-'use strict';
import q from 'q';
import MatrixClientPeg from './MatrixClientPeg';
import Notifier from './Notifier';
@@ -27,7 +26,7 @@ import Notifier from './Notifier';
* TODO: Find a way to translate the names of LABS_FEATURES. In other words, guarantee that languages were already loaded before building this array.
*/
-module.exports = {
+export default {
LABS_FEATURES: [
{
name: "New Composer & Autocomplete",
diff --git a/src/components/views/elements/MemberEventListSummary.js b/src/components/views/elements/MemberEventListSummary.js
index 2999af42d6..fdfd46e9e5 100644
--- a/src/components/views/elements/MemberEventListSummary.js
+++ b/src/components/views/elements/MemberEventListSummary.js
@@ -215,67 +215,67 @@ module.exports = React.createClass({
case "joined":
if (repeats > 1) {
res = (plural)
- ? _t("%(severalUsers)s joined %(repeats)s times", { severalUsers: "", repeats: repeats })
- : _t("%(oneUser)s joined %(repeats)s times", { oneUser: "", repeats: repeats });
+ ? _t("%(severalUsers)sjoined %(repeats)s times", { severalUsers: "", repeats: repeats })
+ : _t("%(oneUser)sjoined %(repeats)s times", { oneUser: "", repeats: repeats });
} else {
res = (plural)
- ? _t("%(severalUsers)s joined", { severalUsers: "" })
- : _t("%(oneUser)s joined", { oneUser: "" });
+ ? _t("%(severalUsers)sjoined", { severalUsers: "" })
+ : _t("%(oneUser)sjoined", { oneUser: "" });
}
break;
case "left":
if (repeats > 1) {
res = (plural)
- ? _t("%(severalUsers)s left %(repeats)s times", { severalUsers: "", repeats: repeats })
- : _t("%(oneUser)s left %(repeats)s times", { oneUser: "", repeats: repeats });
+ ? _t("%(severalUsers)sleft %(repeats)s times", { severalUsers: "", repeats: repeats })
+ : _t("%(oneUser)sleft %(repeats)s times", { oneUser: "", repeats: repeats });
} else {
res = (plural)
- ? _t("%(severalUsers)s left", { severalUsers: "" })
- : _t("%(oneUser)s left", { oneUser: "" });
+ ? _t("%(severalUsers)sleft", { severalUsers: "" })
+ : _t("%(oneUser)sleft", { oneUser: "" });
} break;
case "joined_and_left":
if (repeats > 1) {
res = (plural)
- ? _t("%(severalUsers)s joined and left %(repeats)s times", { severalUsers: "", repeats: repeats })
- : _t("%(oneUser)s joined and left %(repeats)s times", { oneUser: "", repeats: repeats });
+ ? _t("%(severalUsers)sjoined and left %(repeats)s times", { severalUsers: "", repeats: repeats })
+ : _t("%(oneUser)sjoined and left %(repeats)s times", { oneUser: "", repeats: repeats });
} else {
res = (plural)
- ? _t("%(severalUsers)s joined and left", { severalUsers: "" })
- : _t("%(oneUser)s joined and left", { oneUser: "" });
+ ? _t("%(severalUsers)sjoined and left", { severalUsers: "" })
+ : _t("%(oneUser)sjoined and left", { oneUser: "" });
}
break;
case "left_and_joined":
if (repeats > 1) {
res = (plural)
- ? _t("%(severalUsers)s left and rejoined %(repeats)s times", { severalUsers: "", repeats: repeats })
- : _t("%(oneUser)s left and rejoined %(repeats)s times", { oneUser: "", repeats: repeats });
+ ? _t("%(severalUsers)sleft and rejoined %(repeats)s times", { severalUsers: "", repeats: repeats })
+ : _t("%(oneUser)sleft and rejoined %(repeats)s times", { oneUser: "", repeats: repeats });
} else {
res = (plural)
- ? _t("%(severalUsers)s left and rejoined", { severalUsers: "" })
- : _t("%(oneUser)s left and rejoined", { oneUser: "" });
+ ? _t("%(severalUsers)sleft and rejoined", { severalUsers: "" })
+ : _t("%(oneUser)sleft and rejoined", { oneUser: "" });
} break;
break;
case "invite_reject":
if (repeats > 1) {
res = (plural)
- ? _t("%(severalUsers)s rejected their invitations %(repeats)s times", { severalUsers: "", repeats: repeats })
- : _t("%(oneUser)s rejected his/her invitation %(repeats)s times", { oneUser: "", repeats: repeats });
+ ? _t("%(severalUsers)srejected their invitations %(repeats)s times", { severalUsers: "", repeats: repeats })
+ : _t("%(oneUser)srejected their invitation %(repeats)s times", { oneUser: "", repeats: repeats });
} else {
res = (plural)
- ? _t("%(severalUsers)s rejected their invitations", { severalUsers: "" })
- : _t("%(oneUser)s rejected his/her invitation", { oneUser: "" });
+ ? _t("%(severalUsers)srejected their invitations", { severalUsers: "" })
+ : _t("%(oneUser)srejected their invitation", { oneUser: "" });
}
break;
case "invite_withdrawal":
if (repeats > 1) {
res = (plural)
- ? _t("%(severalUsers)s had their invitations withdrawn %(repeats)s times", { severalUsers: "", repeats: repeats })
- : _t("%(oneUser)s had his/her invitation withdrawn %(repeats)s times", { oneUser: "", repeats: repeats });
+ ? _t("%(severalUsers)shad their invitations withdrawn %(repeats)s times", { severalUsers: "", repeats: repeats })
+ : _t("%(oneUser)shad their invitation withdrawn %(repeats)s times", { oneUser: "", repeats: repeats });
} else {
res = (plural)
- ? _t("%(severalUsers)s had their invitations withdrawn", { severalUsers: "" })
- : _t("%(oneUser)s had his/her invitation withdrawn", { oneUser: "" });
+ ? _t("%(severalUsers)shad their invitations withdrawn", { severalUsers: "" })
+ : _t("%(oneUser)shad their invitation withdrawn", { oneUser: "" });
}
break;
case "invited":
@@ -325,23 +325,23 @@ module.exports = React.createClass({
case "changed_name":
if (repeats > 1) {
res = (plural)
- ? _t("%(severalUsers)s changed their name %(repeats)s times", { severalUsers: "", repeats: repeats })
- : _t("%(oneUser)s changed his/her name %(repeats)s times", { oneUser: "", repeats: repeats });
+ ? _t("%(severalUsers)schanged their name %(repeats)s times", { severalUsers: "", repeats: repeats })
+ : _t("%(oneUser)schanged their name %(repeats)s times", { oneUser: "", repeats: repeats });
} else {
res = (plural)
- ? _t("%(severalUsers)s changed their name", { severalUsers: "" })
- : _t("%(oneUser)s changed his/her name", { oneUser: "" });
+ ? _t("%(severalUsers)schanged their name", { severalUsers: "" })
+ : _t("%(oneUser)schanged their name", { oneUser: "" });
}
break;
case "changed_avatar":
if (repeats > 1) {
res = (plural)
- ? _t("%(severalUsers)s changed their avatar %(repeats)s times", { severalUsers: "", repeats: repeats })
- : _t("%(oneUser)s changed his/her avatar %(repeats)s times", { oneUser: "", repeats: repeats });
+ ? _t("%(severalUsers)schanged their avatar %(repeats)s times", { severalUsers: "", repeats: repeats })
+ : _t("%(oneUser)schanged their avatar %(repeats)s times", { oneUser: "", repeats: repeats });
} else {
res = (plural)
- ? _t("%(severalUsers)s changed their avatar", { severalUsers: "" })
- : _t("%(oneUser)s changed his/her avatar", { oneUser: "" });
+ ? _t("%(severalUsers)schanged their avatar", { severalUsers: "" })
+ : _t("%(oneUser)schanged their avatar", { oneUser: "" });
}
break;
}
diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json
index ffe46f5723..c161dd3ed4 100644
--- a/src/i18n/strings/de_DE.json
+++ b/src/i18n/strings/de_DE.json
@@ -654,30 +654,30 @@
"%(items)s and %(remaining)s others": "%(items)s und %(remaining)s weitere",
"%(items)s and one other": "%(items)s und ein(e) weitere(r)",
"%(items)s and %(lastItem)s": "%(items)s und %(lastItem)s",
- "%(severalUsers)s joined %(repeats)s times": "%(severalUsers)s trat(en) %(repeats)s mal bei",
- "%(oneUser)s joined %(repeats)s times": "%(oneUser)s trat %(repeats)s mal bei",
- "%(severalUsers)s joined": "%(severalUsers)s traten bei",
- "%(oneUser)s joined": "%(oneUser)s trat bei",
- "%(severalUsers)s left %(repeats)s times": "%(severalUsers)s gingen %(repeats)s mal",
- "%(oneUser)s left %(repeats)s times": "%(oneUser)s ging %(repeats)s mal",
- "%(severalUsers)s left": "%(severalUsers)s gingen",
- "%(oneUser)s left": "%(oneUser)s ging",
- "%(severalUsers)s joined and left %(repeats)s times": "%(severalUsers)s traten bei und gingen %(repeats)s mal",
- "%(oneUser)s joined and left %(repeats)s times": "%(oneUser)s trat bei und ging %(repeats)s mal",
- "%(severalUsers)s joined and left": "%(severalUsers)s traten bei und gingen",
- "%(oneUser)s joined and left": "%(oneUser)s trat bei und ging",
- "%(severalUsers)s left and rejoined %(repeats)s times": "%(severalUsers)s gingen und traten erneut bei - %(repeats)s mal",
- "%(oneUser)s left and rejoined %(repeats)s times": "%(oneUser)s ging und trat erneut bei - %(repeats)s mal",
- "%(severalUsers)s left and rejoined": "%(severalUsers)s gingen und traten erneut bei",
- "%(oneUser)s left left and rejoined": "%(oneUser)s ging und trat erneut bei",
- "%(severalUsers)s rejected their invitations %(repeats)s times": "%(severalUsers)s lehnten %(repeats)s mal ihre Einladung ab",
- "%(oneUser)s rejected his/her invitation %(repeats)s times": "%(oneUser)s lehnte seine/ihre Einladung %(repeats)s mal ab",
- "%(severalUsers)s rejected their invitations": "%(severalUsers)s lehnten ihre Einladung ab",
- "%(oneUser)s rejected his/her invitation": "%(oneUser)s lehnte seine/ihre Einladung ab",
- "%(severalUsers)s had their invitations withdrawn %(repeats)s times": "%(severalUsers)s zogen ihre Einladungen %(repeats)s mal zurück",
- "%(oneUser)s had his/her invitation withdrawn %(repeats)s times": "%(oneUser)s zog seine/ihre Einladung %(repeats)s mal zurück",
- "%(severalUsers)s had their invitations withdrawn": "%(severalUsers)s zogen ihre Einladungen zurück",
- "%(oneUser)s had his/her invitation withdrawn": "%(oneUser)s zog seine/ihre Einladung zurück",
+ "%(severalUsers)sjoined %(repeats)s times": "%(severalUsers)strat(en) %(repeats)s mal bei",
+ "%(oneUser)sjoined %(repeats)s times": "%(oneUser)strat %(repeats)s mal bei",
+ "%(severalUsers)sjoined": "%(severalUsers)straten bei",
+ "%(oneUser)sjoined": "%(oneUser)strat bei",
+ "%(severalUsers)sleft %(repeats)s times": "%(severalUsers)sgingen %(repeats)s mal",
+ "%(oneUser)sleft %(repeats)s times": "%(oneUser)sging %(repeats)s mal",
+ "%(severalUsers)sleft": "%(severalUsers)sgingen",
+ "%(oneUser)sleft": "%(oneUser)sging",
+ "%(severalUsers)sjoined and left %(repeats)s times": "%(severalUsers)straten bei und gingen %(repeats)s mal",
+ "%(oneUser)sjoined and left %(repeats)s times": "%(oneUser)strat bei und ging %(repeats)s mal",
+ "%(severalUsers)sjoined and left": "%(severalUsers)straten bei und gingen",
+ "%(oneUser)sjoined and left": "%(oneUser)strat bei und ging",
+ "%(severalUsers)sleft and rejoined %(repeats)s times": "%(severalUsers)sgingen und traten erneut bei - %(repeats)s mal",
+ "%(oneUser)sleft and rejoined %(repeats)s times": "%(oneUser)sging und trat erneut bei - %(repeats)s mal",
+ "%(severalUsers)sleft and rejoined": "%(severalUsers)sgingen und traten erneut bei",
+ "%(oneUser)sleft left and rejoined": "%(oneUser)sging und trat erneut bei",
+ "%(severalUsers)srejected their invitations %(repeats)s times": "%(severalUsers)slehnten %(repeats)s mal ihre Einladung ab",
+ "%(oneUser)srejected their invitation %(repeats)s times": "%(oneUser)slehnte seine/ihre Einladung %(repeats)s mal ab",
+ "%(severalUsers)srejected their invitations": "%(severalUsers)slehnten ihre Einladung ab",
+ "%(oneUser)srejected their invitation": "%(oneUser)slehnte seine/ihre Einladung ab",
+ "%(severalUsers)shad their invitations withdrawn %(repeats)s times": "%(severalUsers)szogen ihre Einladungen %(repeats)s mal zurück",
+ "%(oneUser)shad their invitation withdrawn %(repeats)s times": "%(oneUser)szog seine/ihre Einladung %(repeats)s mal zurück",
+ "%(severalUsers)shad their invitations withdrawn": "%(severalUsers)szogen ihre Einladungen zurück",
+ "%(oneUser)shad their invitation withdrawn": "%(oneUser)szog seine/ihre Einladung zurück",
"were invited %(repeats)s times": "wurden %(repeats)s mal eingeladen",
"was invited %(repeats)s times": "wurde %(repeats)s mal eingeladen",
"were invited": "wurden eingeladen",
@@ -690,14 +690,14 @@
"were kicked %(repeats)s times": "wurden %(repeats)s mal gekickt",
"was kicked %(repeats)s times": "wurde %(repeats)s mal gekickt",
"were kicked": "wurden gekickt",
- "%(severalUsers)s changed their name %(repeats)s times": "%(severalUsers)s änderten %(repeats)s mal ihre Namen",
- "%(oneUser)s changed his/her name %(repeats)s times": "%(oneUser)s änderte %(repeats)s mal seinen/ihren Namen",
- "%(severalUsers)s changed their name": "%(severalUsers)s änderten ihre Namen",
- "%(oneUser)s changed his/her name": "%(oneUser)s änderte seinen/ihren Namen",
- "%(severalUsers)s changed their avatar %(repeats)s times": "%(severalUsers)s änderten %(repeats)s mal ihren Avatar",
- "%(oneUser)s changed his/her avatar %(repeats)s times": "%(oneUser)s änderte %(repeats)s mal seinen/ihren Avatar",
- "%(severalUsers)s changed their avatar": "%(severalUsers)s änderten ihre Avatare",
- "%(oneUser)s changed his/her avatar": "%(severalUsers)s änderte seinen/ihren Avatar",
+ "%(severalUsers)schanged their name %(repeats)s times": "%(severalUsers)sänderten %(repeats)s mal ihre Namen",
+ "%(oneUser)schanged their name %(repeats)s times": "%(oneUser)sänderte %(repeats)s mal seinen/ihren Namen",
+ "%(severalUsers)schanged their name": "%(severalUsers)sänderten ihre Namen",
+ "%(oneUser)schanged their name": "%(oneUser)sänderte seinen/ihren Namen",
+ "%(severalUsers)schanged their avatar %(repeats)s times": "%(severalUsers)sänderten %(repeats)s mal ihren Avatar",
+ "%(oneUser)schanged their avatar %(repeats)s times": "%(oneUser)sänderte %(repeats)s mal seinen/ihren Avatar",
+ "%(severalUsers)schanged their avatar": "%(severalUsers)sänderten ihre Avatare",
+ "%(oneUser)schanged their avatar": "%(severalUsers)sänderte seinen/ihren Avatar",
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s, %(day)s. %(monthName)s %(fullYear)s %(time)s",
- "%(oneUser)s left and rejoined": "%(oneUser)s ging und trat erneut bei"
+ "%(oneUser)sleft and rejoined": "%(oneUser)sging und trat erneut bei"
}
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index 9afe796086..2a934d5f2f 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -612,30 +612,30 @@
"quote":"quote",
"bullet":"bullet",
"numbullet":"numbullet",
- "%(severalUsers)s joined %(repeats)s times": "%(severalUsers)s joined %(repeats)s times",
- "%(oneUser)s joined %(repeats)s times": "%(oneUser)s joined %(repeats)s times",
- "%(severalUsers)s joined": "%(severalUsers)s joined",
- "%(oneUser)s joined": "%(oneUser)s joined",
- "%(severalUsers)s left %(repeats)s times": "%(severalUsers)s left %(repeats)s times",
- "%(oneUser)s left %(repeats)s times": "%(oneUser)s left %(repeats)s times",
- "%(severalUsers)s left": "%(severalUsers)s left",
- "%(oneUser)s left": "%(oneUser)s left",
- "%(severalUsers)s joined and left %(repeats)s times": "%(severalUsers)s joined and left %(repeats)s times",
- "%(oneUser)s joined and left %(repeats)s times": "%(oneUser)s joined and left %(repeats)s times",
- "%(severalUsers)s joined and left": "%(severalUsers)s joined and left",
- "%(oneUser)s joined and left": "%(oneUser)s joined and left",
- "%(severalUsers)s left and rejoined %(repeats)s times": "%(severalUsers)s left and rejoined %(repeats)s times",
- "%(oneUser)s left and rejoined %(repeats)s times": "%(oneUser)s left and rejoined %(repeats)s times",
- "%(severalUsers)s left and rejoined": "%(severalUsers)s left and rejoined",
- "%(oneUser)s left and rejoined": "%(oneUser)s left and rejoined",
- "%(severalUsers)s rejected their invitations %(repeats)s times": "%(severalUsers)s rejected their invitations %(repeats)s times",
- "%(oneUser)s rejected his/her invitation %(repeats)s times": "%(oneUser)s rejected his/her invitation %(repeats)s times",
- "%(severalUsers)s rejected their invitations": "%(severalUsers)s rejected their invitations",
- "%(oneUser)s rejected his/her invitation": "%(oneUser)s rejected his/her invitation",
- "%(severalUsers)s had their invitations withdrawn %(repeats)s times": "%(severalUsers)s had their invitations withdrawn %(repeats)s times",
- "%(oneUser)s had his/her invitation withdrawn %(repeats)s times": "%(oneUser)s had his/her invitation withdrawn %(repeats)s times",
- "%(severalUsers)s had their invitations withdrawn": "%(severalUsers)s had their invitations withdrawn",
- "%(oneUser)s had his/her invitation withdrawn": "%(oneUser)s had his/her invitation withdrawn",
+ "%(severalUsers)sjoined %(repeats)s times": "%(severalUsers)sjoined %(repeats)s times",
+ "%(oneUser)sjoined %(repeats)s times": "%(oneUser)sjoined %(repeats)s times",
+ "%(severalUsers)sjoined": "%(severalUsers)sjoined",
+ "%(oneUser)sjoined": "%(oneUser)sjoined",
+ "%(severalUsers)sleft %(repeats)s times": "%(severalUsers)sleft %(repeats)s times",
+ "%(oneUser)sleft %(repeats)s times": "%(oneUser)sleft %(repeats)s times",
+ "%(severalUsers)sleft": "%(severalUsers)sleft",
+ "%(oneUser)sleft": "%(oneUser)sleft",
+ "%(severalUsers)sjoined and left %(repeats)s times": "%(severalUsers)sjoined and left %(repeats)s times",
+ "%(oneUser)sjoined and left %(repeats)s times": "%(oneUser)sjoined and left %(repeats)s times",
+ "%(severalUsers)sjoined and left": "%(severalUsers)sjoined and left",
+ "%(oneUser)sjoined and left": "%(oneUser)sjoined and left",
+ "%(severalUsers)sleft and rejoined %(repeats)s times": "%(severalUsers)sleft and rejoined %(repeats)s times",
+ "%(oneUser)sleft and rejoined %(repeats)s times": "%(oneUser)sleft and rejoined %(repeats)s times",
+ "%(severalUsers)sleft and rejoined": "%(severalUsers)sleft and rejoined",
+ "%(oneUser)sleft and rejoined": "%(oneUser)sleft and rejoined",
+ "%(severalUsers)srejected their invitations %(repeats)s times": "%(severalUsers)srejected their invitations %(repeats)s times",
+ "%(oneUser)srejected their invitation %(repeats)s times": "%(oneUser)srejected their invitation %(repeats)s times",
+ "%(severalUsers)srejected their invitations": "%(severalUsers)srejected their invitations",
+ "%(oneUser)srejected their invitation": "%(oneUser)srejected their invitation",
+ "%(severalUsers)shad their invitations withdrawn %(repeats)s times": "%(severalUsers)shad their invitations withdrawn %(repeats)s times",
+ "%(oneUser)shad their invitation withdrawn %(repeats)s times": "%(oneUser)shad their invitation withdrawn %(repeats)s times",
+ "%(severalUsers)shad their invitations withdrawn": "%(severalUsers)shad their invitations withdrawn",
+ "%(oneUser)shad their invitation withdrawn": "%(oneUser)shad their invitation withdrawn",
"were invited %(repeats)s times": "were invited %(repeats)s times",
"was invited %(repeats)s times": "was invited %(repeats)s times",
"were invited": "were invited",
@@ -652,12 +652,12 @@
"was kicked %(repeats)s times": "was kicked %(repeats)s times",
"were kicked": "were kicked",
"was kicked": "was kicked",
- "%(severalUsers)s changed their name %(repeats)s times": "%(severalUsers)s changed their name %(repeats)s times",
- "%(oneUser)s changed his/her name %(repeats)s times": "%(oneUser)s changed his/her name %(repeats)s times",
- "%(severalUsers)s changed their name": "%(severalUsers)s changed their name",
- "%(oneUser)s changed his/her name": "%(oneUser)s changed his/her name",
- "%(severalUsers)s changed their avatar %(repeats)s times": "%(severalUsers)s changed their avatar %(repeats)s times",
- "%(oneUser)s changed his/her avatar %(repeats)s times": "%(oneUser)s changed his/her avatar %(repeats)s times",
- "%(severalUsers)s changed their avatar": "%(severalUsers)s changed their avatar",
- "%(oneUser)s changed his/her avatar": "%(oneUser)s changed his/her avatar"
+ "%(severalUsers)schanged their name %(repeats)s times": "%(severalUsers)schanged their name %(repeats)s times",
+ "%(oneUser)schanged their name %(repeats)s times": "%(oneUser)schanged their name %(repeats)s times",
+ "%(severalUsers)schanged their name": "%(severalUsers)schanged their name",
+ "%(oneUser)schanged their name": "%(oneUser)schanged their name",
+ "%(severalUsers)schanged their avatar %(repeats)s times": "%(severalUsers)schanged their avatar %(repeats)s times",
+ "%(oneUser)schanged their avatar %(repeats)s times": "%(oneUser)schanged their avatar %(repeats)s times",
+ "%(severalUsers)schanged their avatar": "%(severalUsers)schanged their avatar",
+ "%(oneUser)schanged their avatar": "%(oneUser)schanged their avatar"
}
diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json
index 1aebaab694..87570228df 100644
--- a/src/i18n/strings/pt_BR.json
+++ b/src/i18n/strings/pt_BR.json
@@ -536,7 +536,7 @@
"es-mx": "Espanhol (México)",
"es-ni": "Espanhol (Nicarágua)",
"es-pa": "Espanhol (Panamá)",
- "%(oneUser)s changed his/her avatar": "%(oneUser)s alterou sua imagem pública",
+ "%(oneUser)schanged their avatar": "%(oneUser)salterou sua imagem pública",
"es-pe": "Espanhol (Peru)",
"es-pr": "Espanhol (Porto Rico)",
"es-sv": "Espanhol (El Salvador)",
@@ -659,30 +659,30 @@
"quote": "citação",
"bullet": "marcador de lista",
"numbullet": "marcador de numeração",
- "%(severalUsers)s joined %(repeats)s times": "%(severalUsers)s ingressaram %(repeats)s vezes",
- "%(oneUser)s joined %(repeats)s times": "%(oneUser)s ingressou %(repeats)s vezes",
- "%(severalUsers)s joined": "%(severalUsers)s ingressaram",
- "%(oneUser)s joined": "%(oneUser)s ingressou",
- "%(severalUsers)s left %(repeats)s times": "%(severalUsers)s saíram %(repeats)s vezes",
- "%(oneUser)s left %(repeats)s times": "%(oneUser)s saiu %(repeats)s vezes",
- "%(severalUsers)s left": "%(severalUsers)s saíram",
- "%(oneUser)s left": "%(oneUser)s saiu",
- "%(severalUsers)s joined and left %(repeats)s times": "%(severalUsers)s ingressaram e saíram %(repeats)s vezes",
- "%(oneUser)s joined and left %(repeats)s times": "%(oneUser)s ingressou e saiu %(repeats)s vezes",
- "%(severalUsers)s joined and left": "%(severalUsers)s ingressaram e saíram",
- "%(oneUser)s joined and left": "%(oneUser)s ingressou e saiu",
- "%(severalUsers)s left and rejoined %(repeats)s times": "%(severalUsers)s saíram e entraram novamente %(repeats)s vezes",
- "%(oneUser)s left and rejoined %(repeats)s times": "%(oneUser)s saiu e entrou novamente %(repeats)s vezes",
- "%(severalUsers)s left and rejoined": "%(severalUsers)s saíram e entraram novamente",
- "%(oneUser)s left and rejoined": "%(oneUser)s saiu e entrou novamente",
- "%(severalUsers)s rejected their invitations %(repeats)s times": "%(severalUsers)s rejeitaram seus convites %(repeats)s vezes",
- "%(oneUser)s rejected his/her invitation %(repeats)s times": "%(oneUser)s rejeitou seu convite %(repeats)s vezes",
- "%(severalUsers)s rejected their invitations": "%(severalUsers)s rejeitaram seus convites",
- "%(oneUser)s rejected his/her invitation": "%(oneUser)s rejeitou seu convite",
- "%(severalUsers)s had their invitations withdrawn %(repeats)s times": "%(severalUsers)s tiveram seus convites desfeitos %(repeats)s vezes",
- "%(oneUser)s had his/her invitation withdrawn %(repeats)s times": "%(oneUser)s teve seu convite desfeito %(repeats)s vezes",
- "%(severalUsers)s had their invitations withdrawn": "%(severalUsers)s tiveram seus convites desfeitos",
- "%(oneUser)s had his/her invitation withdrawn": "%(oneUser)s teve seu convite desfeito",
+ "%(severalUsers)sjoined %(repeats)s times": "%(severalUsers)singressaram %(repeats)s vezes",
+ "%(oneUser)sjoined %(repeats)s times": "%(oneUser)singressou %(repeats)s vezes",
+ "%(severalUsers)sjoined": "%(severalUsers)singressaram",
+ "%(oneUser)sjoined": "%(oneUser)singressou",
+ "%(severalUsers)sleft %(repeats)s times": "%(severalUsers)ssaíram %(repeats)s vezes",
+ "%(oneUser)sleft %(repeats)s times": "%(oneUser)ssaiu %(repeats)s vezes",
+ "%(severalUsers)sleft": "%(severalUsers)ssaíram",
+ "%(oneUser)sleft": "%(oneUser)ssaiu",
+ "%(severalUsers)sjoined and left %(repeats)s times": "%(severalUsers)singressaram e saíram %(repeats)s vezes",
+ "%(oneUser)sjoined and left %(repeats)s times": "%(oneUser)singressou e saiu %(repeats)s vezes",
+ "%(severalUsers)sjoined and left": "%(severalUsers)singressaram e saíram",
+ "%(oneUser)sjoined and left": "%(oneUser)singressou e saiu",
+ "%(severalUsers)sleft and rejoined %(repeats)s times": "%(severalUsers)ssaíram e entraram novamente %(repeats)s vezes",
+ "%(oneUser)sleft and rejoined %(repeats)s times": "%(oneUser)ssaiu e entrou novamente %(repeats)s vezes",
+ "%(severalUsers)sleft and rejoined": "%(severalUsers)ssaíram e entraram novamente",
+ "%(oneUser)sleft and rejoined": "%(oneUser)ssaiu e entrou novamente",
+ "%(severalUsers)srejected their invitations %(repeats)s times": "%(severalUsers)srejeitaram seus convites %(repeats)s vezes",
+ "%(oneUser)srejected their invitation %(repeats)s times": "%(oneUser)srejeitou seu convite %(repeats)s vezes",
+ "%(severalUsers)srejected their invitations": "%(severalUsers)srejeitaram seus convites",
+ "%(oneUser)srejected their invitation": "%(oneUser)srejeitou seu convite",
+ "%(severalUsers)shad their invitations withdrawn %(repeats)s times": "%(severalUsers)stiveram seus convites desfeitos %(repeats)s vezes",
+ "%(oneUser)shad their invitation withdrawn %(repeats)s times": "%(oneUser)steve seu convite desfeito %(repeats)s vezes",
+ "%(severalUsers)shad their invitations withdrawn": "%(severalUsers)stiveram seus convites desfeitos",
+ "%(oneUser)shad their invitation withdrawn": "%(oneUser)steve seu convite desfeito",
"were invited %(repeats)s times": "foram convidadas(os) %(repeats)s vezes",
"was invited %(repeats)s times": "foi convidada(o) %(repeats)s vezes",
"were invited": "foram convidadas(os)",
@@ -695,12 +695,12 @@
"were kicked %(repeats)s times": "foram expulsas(os) %(repeats)s vezes",
"was kicked %(repeats)s times": "foi expulsa(o) %(repeats)s vezes",
"were kicked": "foram expulsas(os)",
- "%(severalUsers)s changed their name %(repeats)s times": "%(severalUsers)s alteraram seu nome %(repeats)s vezes",
- "%(oneUser)s changed his/her name %(repeats)s times": "%(oneUser)s alterou seu nome %(repeats)s vezes",
- "%(severalUsers)s changed their name": "%(severalUsers)s alteraram seus nomes",
- "%(oneUser)s changed his/her name": "%(oneUser)s alterou seu nome",
- "%(severalUsers)s changed their avatar %(repeats)s times": "%(severalUsers)s alteraram sua imagem pública %(repeats)s vezes",
- "%(oneUser)s changed his/her avatar %(repeats)s times": "%(oneUser)s alterou sua imagem pública %(repeats)s vezes",
- "%(severalUsers)s changed their avatar": "%(severalUsers)s alteraram sua imagem pública",
+ "%(severalUsers)schanged their name %(repeats)s times": "%(severalUsers)salteraram seu nome %(repeats)s vezes",
+ "%(oneUser)schanged their name %(repeats)s times": "%(oneUser)salterou seu nome %(repeats)s vezes",
+ "%(severalUsers)schanged their name": "%(severalUsers)salteraram seus nomes",
+ "%(oneUser)schanged their name": "%(oneUser)salterou seu nome",
+ "%(severalUsers)schanged their avatar %(repeats)s times": "%(severalUsers)salteraram sua imagem pública %(repeats)s vezes",
+ "%(oneUser)schanged their avatar %(repeats)s times": "%(oneUser)salterou sua imagem pública %(repeats)s vezes",
+ "%(severalUsers)schanged their avatar": "%(severalUsers)salteraram sua imagem pública",
"Ban": "Banir"
}
diff --git a/test/components/views/elements/MemberEventListSummary-test.js b/test/components/views/elements/MemberEventListSummary-test.js
index d01d705040..96fb4cc2e5 100644
--- a/test/components/views/elements/MemberEventListSummary-test.js
+++ b/test/components/views/elements/MemberEventListSummary-test.js
@@ -4,6 +4,7 @@ const ReactDOM = require("react-dom");
const ReactTestUtils = require('react-addons-test-utils');
const sdk = require('matrix-react-sdk');
const MemberEventListSummary = sdk.getComponent('views.elements.MemberEventListSummary');
+import * as languageHandler from '../../../../src/languageHandler';
const testUtils = require('../../../test-utils');
describe('MemberEventListSummary', function() {
@@ -82,9 +83,11 @@ describe('MemberEventListSummary', function() {
return eventsForUsers;
};
- beforeEach(function() {
+ beforeEach(function(done) {
testUtils.beforeEach(this);
sandbox = testUtils.stubClient();
+
+ languageHandler.setLanguage('en').done(done);
});
afterEach(function() {
@@ -356,7 +359,7 @@ describe('MemberEventListSummary', function() {
const summaryText = summary.innerText;
expect(summaryText).toBe(
- "user_1 and 1 other were unbanned, joined and left 2 times and were banned"
+ "user_1 and one other were unbanned, joined and left 2 times and were banned"
);
});
@@ -559,7 +562,7 @@ describe('MemberEventListSummary', function() {
const summaryText = summary.innerText;
expect(summaryText).toBe(
- "user_1 and 1 other rejected their invitations and " +
+ "user_1 and one other rejected their invitations and " +
"had their invitations withdrawn"
);
});
@@ -595,7 +598,7 @@ describe('MemberEventListSummary', function() {
const summaryText = summary.innerText;
expect(summaryText).toBe(
- "user_1 rejected their invitations 2 times"
+ "user_1 rejected their invitation 2 times"
);
});
@@ -650,7 +653,7 @@ describe('MemberEventListSummary', function() {
const summaryText = summary.innerText;
expect(summaryText).toBe(
- "user_1, user_2 and 1 other joined"
+ "user_1, user_2 and one other joined"
);
});
diff --git a/test/test-utils.js b/test/test-utils.js
index 40be715abd..5209465362 100644
--- a/test/test-utils.js
+++ b/test/test-utils.js
@@ -3,8 +3,6 @@
import sinon from 'sinon';
import q from 'q';
import ReactTestUtils from 'react-addons-test-utils';
-import * as counterpart from 'counterpart-riot';
-var languageHandler = require("../src/languageHandler");
import peg from '../src/MatrixClientPeg.js';
import jssdk from 'matrix-js-sdk';
@@ -67,10 +65,6 @@ export function beforeEach(context) {
console.log(desc);
console.log(new Array(1 + desc.length).join("="));
-
- const languages = languageHandler.getNormalizedLanguageKeys('en');
- languageHandler.setLanguage(languages, counterpart);
-
};
From 0de0b8954d9eb44f2cfc812ba6dbff56d85b2ef7 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Fri, 26 May 2017 12:03:36 +0100
Subject: [PATCH 0227/1016] Trailing whitespace
---
test/components/views/elements/MemberEventListSummary-test.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/components/views/elements/MemberEventListSummary-test.js b/test/components/views/elements/MemberEventListSummary-test.js
index 96fb4cc2e5..a93e38b50c 100644
--- a/test/components/views/elements/MemberEventListSummary-test.js
+++ b/test/components/views/elements/MemberEventListSummary-test.js
@@ -86,7 +86,7 @@ describe('MemberEventListSummary', function() {
beforeEach(function(done) {
testUtils.beforeEach(this);
sandbox = testUtils.stubClient();
-
+
languageHandler.setLanguage('en').done(done);
});
From bb344740dda0a15f7cf1565f13bfec94a8ce4cd4 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Fri, 26 May 2017 13:09:29 +0100
Subject: [PATCH 0228/1016] Add test languages file
---
test/i18n/languages.json | 3 +++
1 file changed, 3 insertions(+)
create mode 100644 test/i18n/languages.json
diff --git a/test/i18n/languages.json b/test/i18n/languages.json
new file mode 100644
index 0000000000..bdb46584b9
--- /dev/null
+++ b/test/i18n/languages.json
@@ -0,0 +1,3 @@
+{
+ "en": "en_EN.json"
+}
From 28094a9a6634e5ec827bcf8481448b733bf44430 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Fri, 26 May 2017 13:13:57 +0100
Subject: [PATCH 0229/1016] Show "Something went wrong!" when errcode undefined
---
src/components/views/dialogs/SetMxIdDialog.js | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/components/views/dialogs/SetMxIdDialog.js b/src/components/views/dialogs/SetMxIdDialog.js
index 86b5fccbc2..72599a6b21 100644
--- a/src/components/views/dialogs/SetMxIdDialog.js
+++ b/src/components/views/dialogs/SetMxIdDialog.js
@@ -108,6 +108,7 @@ export default React.createClass({
const newState = {
usernameCheckSupport: err.errcode !== "M_UNRECOGNIZED",
};
+ console.error('Error whilst checking username availability: ', err);
switch (err.errcode) {
case "M_USER_IN_USE":
newState.usernameError = 'Username not available';
@@ -120,8 +121,11 @@ export default React.createClass({
// fine and rely on the error appearing in registration step.
newState.usernameError = '';
break;
+ case undefined:
+ newState.usernameError = 'Something went wrong!';
+ break;
default:
- newState.usernameError = 'An error occurred' + err.message;
+ newState.usernameError = 'An error occurred: ' + err.message;
break;
}
this.setState(newState);
From 5e136863b0558d1edc83b1413abbef56b4a19742 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Fri, 26 May 2017 13:18:44 +0100
Subject: [PATCH 0230/1016] Block user settings with view_set_mxid
---
src/components/structures/MatrixChat.js | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index ef2882fe13..a03273c1c6 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -433,6 +433,10 @@ module.exports = React.createClass({
this._viewIndexedRoom(payload.roomIndex);
break;
case 'view_user_settings':
+ if (MatrixClientPeg.get().isGuest()) {
+ dis.dispatch({action: 'view_set_mxid'});
+ break;
+ }
this._setPage(PageTypes.UserSettings);
this.notifyNewScreen('settings');
break;
@@ -441,7 +445,6 @@ module.exports = React.createClass({
dis.dispatch({action: 'view_set_mxid'});
break;
}
-
Modal.createDialog(TextInputDialog, {
title: "Create Room",
description: "Room name (optional)",
From e3e848d09e368a0ec35eae84bcd374fac8314919 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Fri, 26 May 2017 13:19:46 +0100
Subject: [PATCH 0231/1016] Fix missing full stop on translation key
---
src/components/structures/UserSettings.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js
index f441525f96..fe896c8a1d 100644
--- a/src/components/structures/UserSettings.js
+++ b/src/components/structures/UserSettings.js
@@ -295,7 +295,7 @@ module.exports = React.createClass({
title: _t("Sign out"),
description:
- { _t("For security, logging out will delete any end-to-end encryption keys from this browser. If you want to be able to decrypt your conversation history from future Riot sessions, please export your room keys for safe-keeping") }.
+ { _t("For security, logging out will delete any end-to-end encryption keys from this browser. If you want to be able to decrypt your conversation history from future Riot sessions, please export your room keys for safe-keeping.") }.
,
button: _t("Sign out"),
extraButtons: [
From 0f8e560247834f978afa7706ca9b231962786280 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Fri, 26 May 2017 15:29:11 +0100
Subject: [PATCH 0232/1016] Add function to display i18n keys in UI
Avoids having to load translations in riot-web tests
---
src/languageHandler.js | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/languageHandler.js b/src/languageHandler.js
index 6734c99582..cddc70da56 100644
--- a/src/languageHandler.js
+++ b/src/languageHandler.js
@@ -37,6 +37,13 @@ export function _t(...args) {
return counterpart.translate(...args);
}
+// Allow overriding the text displayed when no translation exists
+// Currently only use din unit tests to avoid having to load
+// the translations in riot-web
+export function setMissingEntryGenerator(f) {
+ counterpart.setMissingEntryGenerator(f);
+}
+
export function setLanguage(preferredLangs) {
if (!Array.isArray(preferredLangs)) {
preferredLangs = [preferredLangs];
From 84cef00f1f4efe29c7686a1e08662374a4d68bbe Mon Sep 17 00:00:00 2001
From: David Baker
Date: Fri, 26 May 2017 17:16:23 +0100
Subject: [PATCH 0233/1016] Mismatched oneUser / severalUsers
---
src/i18n/strings/de_DE.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json
index c161dd3ed4..f09ff9f99b 100644
--- a/src/i18n/strings/de_DE.json
+++ b/src/i18n/strings/de_DE.json
@@ -697,7 +697,7 @@
"%(severalUsers)schanged their avatar %(repeats)s times": "%(severalUsers)sänderten %(repeats)s mal ihren Avatar",
"%(oneUser)schanged their avatar %(repeats)s times": "%(oneUser)sänderte %(repeats)s mal seinen/ihren Avatar",
"%(severalUsers)schanged their avatar": "%(severalUsers)sänderten ihre Avatare",
- "%(oneUser)schanged their avatar": "%(severalUsers)sänderte seinen/ihren Avatar",
+ "%(oneUser)schanged their avatar": "%(oneUser)sänderte seinen/ihren Avatar",
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s, %(day)s. %(monthName)s %(fullYear)s %(time)s",
"%(oneUser)sleft and rejoined": "%(oneUser)sging und trat erneut bei"
}
From 9311b9012a4177c501a61c34a5d7d08b48f054f5 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Fri, 26 May 2017 17:23:02 +0100
Subject: [PATCH 0234/1016] Use the same `.reset` as RoomViewStore
---
src/stores/LifecycleStore.js | 16 ++++++++++------
src/stores/SessionStore.js | 16 ++++++++++------
2 files changed, 20 insertions(+), 12 deletions(-)
diff --git a/src/stores/LifecycleStore.js b/src/stores/LifecycleStore.js
index 5dfe82500a..f7e3ff9dbb 100644
--- a/src/stores/LifecycleStore.js
+++ b/src/stores/LifecycleStore.js
@@ -16,6 +16,10 @@ limitations under the License.
import dis from '../dispatcher';
import {Store} from 'flux/utils';
+const INITIAL_STATE = {
+ deferred_action: null,
+};
+
/**
* A class for storing application state to do with login/registration. This is a simple
* flux store that listens for actions and updates its state accordingly, informing any
@@ -33,9 +37,7 @@ class LifecycleStore extends Store {
super(dis);
// Initialise state
- this._state = {
- deferred_action: null,
- };
+ this._state = INITIAL_STATE;
}
_setState(newState) {
@@ -62,12 +64,14 @@ class LifecycleStore extends Store {
dis.dispatch(deferredAction);
break;
case 'on_logged_out':
- this._state = {
- deferred_action: null,
- };
+ this.reset();
break;
}
}
+
+ reset() {
+ this._state = Object.assign({}, INITIAL_STATE);
+ }
}
let singletonLifecycleStore = null;
diff --git a/src/stores/SessionStore.js b/src/stores/SessionStore.js
index 5713e4d321..a4b49d9cea 100644
--- a/src/stores/SessionStore.js
+++ b/src/stores/SessionStore.js
@@ -16,6 +16,10 @@ limitations under the License.
import dis from '../dispatcher';
import {Store} from 'flux/utils';
+const INITIAL_STATE = {
+ cachedPassword: localStorage.getItem('mx_pass'),
+};
+
/**
* A class for storing application state to do with the session. This is a simple flux
* store that listens for actions and updates its state accordingly, informing any
@@ -33,9 +37,7 @@ class SessionStore extends Store {
super(dis);
// Initialise state
- this._state = {
- cachedPassword: localStorage.getItem('mx_pass'),
- };
+ this._state = INITIAL_STATE;
}
_update() {
@@ -67,11 +69,13 @@ class SessionStore extends Store {
});
break;
case 'on_logged_out':
- this._state = {
- cachedPassword: null,
- };
+ this.reset();
break;
}
+
+ reset() {
+ this._state = Object.assign({}, INITIAL_STATE);
+ }
}
getCachedPassword() {
From ac44151e2a2d8c9b158d8b8315ddb939430291f6 Mon Sep 17 00:00:00 2001
From: Luke Barnard
Date: Fri, 26 May 2017 17:27:31 +0100
Subject: [PATCH 0235/1016] Put the reset method in the right scope...
---
src/stores/SessionStore.js | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/stores/SessionStore.js b/src/stores/SessionStore.js
index a4b49d9cea..62868e4fe4 100644
--- a/src/stores/SessionStore.js
+++ b/src/stores/SessionStore.js
@@ -72,10 +72,10 @@ class SessionStore extends Store {
this.reset();
break;
}
+ }
- reset() {
- this._state = Object.assign({}, INITIAL_STATE);
- }
+ reset() {
+ this._state = Object.assign({}, INITIAL_STATE);
}
getCachedPassword() {
From 40680d55e09980af4b783780bba153ab8ecdfd20 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Fri, 26 May 2017 17:55:48 +0100
Subject: [PATCH 0236/1016] Fix merge fail
---
src/DateUtils.js | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/DateUtils.js b/src/DateUtils.js
index bc8594c671..8b7a2f3f20 100644
--- a/src/DateUtils.js
+++ b/src/DateUtils.js
@@ -62,6 +62,8 @@ function twelveHourTime(date) {
module.exports = {
formatDate: function(date) {
var now = new Date();
+ const days = getDaysArray();
+ const months = getMonthsArray();
if (date.toDateString() === now.toDateString()) {
return this.formatTime(date);
}
From 23fbce66a292509d2418c8aa3d68dc33cae23818 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Fri, 26 May 2017 17:56:51 +0100
Subject: [PATCH 0237/1016] Translate 'email' threepid label
---
src/components/structures/UserSettings.js | 1 +
src/i18n/strings/en_EN.json | 1 +
2 files changed, 2 insertions(+)
diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js
index 862d2480ce..a861e8ebd1 100644
--- a/src/components/structures/UserSettings.js
+++ b/src/components/structures/UserSettings.js
@@ -826,6 +826,7 @@ module.exports = React.createClass({
nameForMedium: function(medium) {
if (medium === 'msisdn') return _t('Phone');
+ if (medium === 'email') return _t('Email');
return medium[0].toUpperCase() + medium.slice(1);
},
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index 2a934d5f2f..f98423d67c 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -227,6 +227,7 @@
"Drop here %(toAction)s": "Drop here %(toAction)s",
"Drop here to tag %(section)s": "Drop here to tag %(section)s",
"Ed25519 fingerprint": "Ed25519 fingerprint",
+ "Email": "Email",
"Email Address": "Email Address",
"Email, name or matrix ID": "Email, name or matrix ID",
"Emoji": "Emoji",
From cf0073a631bacc608dbafe75ee9d97fef401c606 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Fri, 26 May 2017 17:59:19 +0100
Subject: [PATCH 0238/1016] Missing translations in User Interface section
---
src/i18n/strings/en_EN.json | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index f98423d67c..73ae8c5515 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -133,6 +133,7 @@
"Admin": "Admin",
"Advanced": "Advanced",
"Algorithm": "Algorithm",
+ "Always show message timestamps": "Always show message timestamps",
"Authentication": "Authentication",
"all room members": "all room members",
"all room members, from the point they are invited": "all room members, from the point they are invited",
@@ -420,6 +421,7 @@
"%(senderName)s set their display name to %(displayName)s": "%(senderName)s set their display name to %(displayName)s",
"Settings": "Settings",
"Show panel": "Show panel",
+ "Show timestamps in 12 hour format (e.g. 2:30pm)": "Show timestamps in 12 hour format (e.g. 2:30pm)",
"Signed Out": "Signed Out",
"Sign in": "Sign in",
"Sign out": "Sign out",
From 62ffe929e49256d48401f6ec1acb42cc0d698cc5 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Fri, 26 May 2017 18:03:24 +0100
Subject: [PATCH 0239/1016] Translate Add Threepid labels
---
src/components/structures/UserSettings.js | 2 +-
src/components/views/settings/AddPhoneNumber.js | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js
index a861e8ebd1..59b8f5a385 100644
--- a/src/components/structures/UserSettings.js
+++ b/src/components/structures/UserSettings.js
@@ -887,7 +887,7 @@ module.exports = React.createClass({
addEmailSection = (
- Email
+ {_t('Email')}
- Phone
+ {_t('Phone')}
From 647c1b61699e8ebb6be73b9481df669aa129c289 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Fri, 26 May 2017 18:19:32 +0100
Subject: [PATCH 0240/1016] Once more, with braces
---
src/components/views/login/PasswordLogin.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/views/login/PasswordLogin.js b/src/components/views/login/PasswordLogin.js
index 51042a59b3..fcaf05a8c8 100644
--- a/src/components/views/login/PasswordLogin.js
+++ b/src/components/views/login/PasswordLogin.js
@@ -128,7 +128,7 @@ class PasswordLogin extends React.Component {
type="text"
name="username" // make it a little easier for browser's remember-password
onChange={this.onUsernameChanged}
- placeholder=_t('username')
+ placeholder={_t('username')}
value={this.state.username}
autoFocus
/>;
From 563b85ceb831f353a30509dbe0cef0e0ef2a6243 Mon Sep 17 00:00:00 2001
From: David Baker
Date: Fri, 26 May 2017 18:53:23 +0100
Subject: [PATCH 0241/1016] Merge
https://github.com/MTRNord/matrix-react-sdk/pull/115
---
src/i18n/strings/fr.json | 151 +++++++++++++++++++++++++++++++++------
1 file changed, 129 insertions(+), 22 deletions(-)
diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json
index e393d27bda..9967d70bc2 100644
--- a/src/i18n/strings/fr.json
+++ b/src/i18n/strings/fr.json
@@ -120,27 +120,27 @@
"zh-tw": "Chinese (Taiwan)",
"zu": "Zulu",
"anyone.": "anyone",
- "Direct Chat": "Chat direct",
- "Direct chats": "Direct chats",
- "Disable inline URL previews by default": "Disable inline URL previews by default",
- "Disinvite": "Disinvite",
- "Display name": "Display name",
- "Displays action": "Displays action",
- "Don't send typing notifications": "Don't send typing notifications",
- "Download %(text)s": "Download %(text)s",
- "Drop here %(toAction)s": "Drop here %(toAction)s",
- "Drop here to tag %(section)s": "Drop here to tag %(section)s",
- "Ed25519 fingerprint": "Ed25519 fingerprint",
- "Email Address": "Email Address",
- "Email, name or matrix ID": "Email, name or matrix ID",
- "Emoji": "Emoji",
- "Enable encryption": "Enable encryption",
- "Encrypted messages will not be visible on clients that do not yet implement encryption": "Encrypted messages will not be visible on clients that do not yet implement encryption",
- "Encrypted room": "Encrypted room",
- "%(senderName)s ended the call": "%(senderName)s ended the call",
- "End-to-end encryption information": "End-to-end encryption information",
- "End-to-end encryption is in beta and may not be reliable": "End-to-end encryption is in beta and may not be reliable",
- "Enter Code": "Enter Code",
+ "Direct Chat": "Conversation Directe",
+ "Direct chats": "Conversations directes",
+ "Disable inline URL previews by default": "Désactiver l’aperçu des URLs",
+ "Disinvite": "Désinviter",
+ "Display name": "Nom d'affichage",
+ "Displays action": "Affiche l'action",
+ "Don't send typing notifications": "Ne pas envoyer les notifications de saisie",
+ "Download %(text)s": "Télécharger %(text)s",
+ "Drop here %(toAction)s": "Déposer ici %(toAction)s",
+ "Drop here to tag %(section)s": "Déposer ici pour marque comme %(section)s",
+ "Ed25519 fingerprint": "Empreinte Ed25519",
+ "Email Address": "Adresse e-mail",
+ "Email, name or matrix ID": "E-mail, nom or identifiant Matrix",
+ "Emoji": "Emoticône",
+ "Enable encryption": "Activer l'encryption",
+ "Encrypted messages will not be visible on clients that do not yet implement encryption": "Les messages encryptés ne seront pas visibles dans les clients qui n’implémentent pas encore l’encryption",
+ "Encrypted room": "Salon encrypté",
+ "%(senderName)s ended the call": "%(senderName)s a terminé l’appel",
+ "End-to-end encryption information": "Information sur l'encryption bout-en-bout",
+ "End-to-end encryption is in beta and may not be reliable": "L’encryption bout-en-bout est en béta et risque de ne pas être fiable",
+ "Enter Code": "Entrer le code",
"Error": "Erreur",
"Event information": "Event information",
"Existing Call": "Existing Call",
@@ -166,5 +166,112 @@
"bold": "gras",
"italic": "italique",
"strike": "barré",
- "underline": "souligné"
+ "underline": "souligné",
+ "Favourite": "Favoris",
+ "Notifications": "Notifications",
+ "Settings": "Paramètres",
+ "Failed to join the room": "Échec de l'adhésion au salon",
+ "A text message has been sent to +%(msisdn)s. Please enter the verification code it contains": "Un message texte a été envoyé à +%(msisdn)s. Merci d'entrer le code de vérification qu'il contient",
+ "accept": "Accepter",
+ "%(targetName)s accepted an invitation": "%(targetName)s a accepté une invitation",
+ "%(targetName)s accepted the invitation for %(displayName)s": "%(targetName)s a accepté une invitation de %(displayName)s",
+ "Account": "Compte",
+ "Add email address": "Ajouter une adresse e-mail",
+ "Add phone number": "Ajouter un numéro de téléphone",
+ "Admin": "Admin",
+ "Advanced": "Avancé",
+ "Algorithm": "Algorithme",
+ "all room members": "tous les membres du salon",
+ "all room members, from the point they are invited": "tous les membres du salon, depuis le moment où ils ont été invités",
+ "all room members, from the point they joined": "tous les membres du salon, depuis le moment où ils ont joint",
+ "an address": "une adresse",
+ "and": "et",
+ "%(items)s and %(remaining)s others": "%(items)s et %(remaining)s autres",
+ "%(items)s and one other": "%(items)s et un autre",
+ "%(items)s and %(lastItem)s": "%(items)s et %(lastItem)s",
+ "and %(overflowCount)s others...": "et %(overflowCount)s autres...",
+ "and one other...": "et un autre...",
+ "%(names)s and %(lastPerson)s are typing": "%(names)s et %(lastPerson)s sont en train de taper",
+ "%(names)s and one other are typing": "%(names)s et un autre sont en train de taper",
+ "%(names)s and %(count)s others are typing": "%(names)s et %(count)s d'autres sont en train de taper",
+ "An email has been sent to": "Un e-mail a été envoyé à",
+ "A new password must be entered.": "Un nouveau mot de passe doit être entré.",
+ "%(senderName)s answered the call": "%(senderName)s a répondu à l’appel",
+ "Anyone who knows the room's link, apart from guests": "Tout ceux qui connaissent le lien du salon, à part les invités",
+ "Anyone who knows the room's link, including guests": "Tout ceux qui connaissent le lien du salon, y compris les invités",
+ "Are you sure?": "Êtes-vous sûr ?",
+ "Are you sure you want to reject the invitation?": "Êtes-vous sûr de vouloir rejeter l'invitation ?",
+ "Are you sure you want upload the following files?": "Êtes-vous sûr de vouloir télécharger les fichiers suivants ?",
+ "Attachment": "Pièce jointe",
+ "Autoplay GIFs and videos": "Jouer automatiquement les GIFs et vidéos",
+ "%(senderName)s banned %(targetName)s": "%(senderName)s a banni %(targetName)s",
+ "Ban": "Bannir",
+ "Banned users": "Utilisateurs bannis",
+ "Bans user with given id": "Utilisateurs bannis avec un identifiant donné",
+ "Blacklisted": "Sur liste noire",
+ "Bug Report": "Rapport d'erreur",
+ "Call Timeout": "Délai d’appel expiré",
+ "Can't connect to homeserver - please check your connectivity and ensure your %(urlStart)s homeserver's SSL certificate %(urlEnd)s is trusted": "Connexion au Home Server impossible - merci de vérifier votre connectivité et que le %(urlStart)s certificat SSL de votre Home Server %(urlEnd)s est de confiance",
+ "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or %(urlStart)s enable unsafe scripts %(urlEnd)s": "Impossible de se connecter au homeserver en HTTP si l'URL dans la barre de votre explorateur est en HTTPS. Utilisez HTTPS ou %(urlStart)s activez le support des scripts non-vérifiés %(urlEnd)s",
+ "Can't load user settings": "Impossible de charger les paramètres utilisateur",
+ "Change Password": "Changer le mot de passe",
+ "%(senderName)s changed their display name from %(oldDisplayName)s to %(displayName)s": "%(senderName)s a changé son nom d’affichage de %(oldDisplayName)s en %(displayName)s",
+ "%(senderName)s changed their profile picture": "%(senderName)s a changé sa photo de profil",
+ "%(senderName)s changed the power level of %(powerLevelDiffText)s": "%(senderName)s a changé le niveau de pouvoir de %(powerLevelDiffText)s",
+ "%(senderDisplayName)s changed the room name to %(roomName)s": "%(senderDisplayName)s a changé le nom du salon en %(roomName)s",
+ "%(senderDisplayName)s changed the topic to \"%(topic)s\"": "%(senderDisplayName)s a changé le sujet du salon en \"%(topic)s\"",
+ "Changes to who can read history will only apply to future messages in this room": "Les changements de visibilité de l’historique de ce salon ne s’appliquent qu’aux messages futurs",
+ "Changes your display nickname": "Change votre nom d'affichage",
+ "Claimed Ed25519 fingerprint key": "Clé empreinte Ed25519 revendiquée",
+ "Clear Cache and Reload": "Vider le cache et recharger",
+ "Clear Cache": "Vider le cache",
+ "Click here": "Cliquer ici",
+ "Click here to fix": "Cliquer ici pour réparer",
+ "Click to mute audio": "Cliquer pour couper le son",
+ "Click to mute video": "Cliquer ici pour couper la vidéo",
+ "click to reveal": "cliquer pour dévoiler",
+ "Click to unmute video": "Cliquer pour rétablir la vidéo",
+ "Click to unmute audio": "Cliquer pour rétablir le son",
+ "Command error": "Erreur de commande",
+ "Commands": "Commandes",
+ "Conference call failed": "Échec de la conférence",
+ "Conference calling is in development and may not be reliable": "Les appels en conférence sont encore en développement et sont potentiellement peu fiables",
+ "Conference calls are not supported in encrypted rooms": "Les appels en conférence ne sont pas supportés dans les salons encryptés",
+ "Conference calls are not supported in this client": "Les appels en conférence ne sont pas supportés avec ce client",
+ "Confirm password": "Confirmer le mot de passe",
+ "Confirm your new password": "Confirmer votre nouveau mot de passe",
+ "Continue": "Continuer",
+ "Could not connect to the integration server": "Impossible de se connecter au serveur d'intégration",
+ "Create an account": "Créer un compte",
+ "Create Room": "Créer un salon",
+ "Cryptography": "Encryption",
+ "Current password": "Mot de passe actuel",
+ "Curve25519 identity key": "Clé d’identité Curve25519",
+ "/ddg is not a command": "/ddg n'est pas une commande",
+ "Deactivate Account": "Désactiver le compte",
+ "Deactivate my account": "Désactiver mon compte",
+ "decline": "décliner",
+ "Decrypt %(text)s": "Décrypter %(text)s",
+ "Decryption error": "Erreur de décryptage",
+ "Delete": "Supprimer",
+ "demote": "rétrograder",
+ "Deops user with given id": "Retire les privilèges d’opérateur d’un utilisateur avec un ID donné",
+ "Device ID": "ID de l'appareil",
+ "Devices": "Appareils",
+ "Devices will not yet be able to decrypt history from before they joined the room": "Les appareils ne seront pas capables de décrypter l’historique précédant leur adhésion au salon",
+ "ml": "Malayalam",
+ "Failed to join room": "Échec lors de l’adhésion au salon",
+ "Failed to kick": "Échec lors de l'expulsion",
+ "Failed to leave room": "Échec du départ",
+ "Failed to load timeline position": "Erreur lors du chargement de la position dans la chronologie",
+ "Failed to lookup current room": "Échec lors de la recherche du salon actuel",
+ "Failed to mute user": "Échec lors de l'interruption de l'utilisateur",
+ "Failed to reject invite": "Échec lors du rejet de l'invitation",
+ "Failed to reject invitation": "Échec lors du rejet de l'invitation",
+ "Failed to save settings": "Échec lors de la sauvegarde des paramètres",
+ "Failed to send email": "Échec lors de l’envoi de l’e-mail",
+ "Failed to send request": "Échec lors de l’envoi de la requête",
+ "Failed to set display name": "Échec lors de l'enregistrement du nom d'affichage",
+ "Failed to set up conference call": "Échec lors de l’établissement de l’appel",
+ "Failed to toggle moderator status": "Échec lors de l’établissement du statut de modérateur"
}
From cddfe7e6ef34387a61e792ecefb03396b1ee85bb Mon Sep 17 00:00:00 2001
From: David Baker
Date: Fri, 26 May 2017 19:00:44 +0100
Subject: [PATCH 0242/1016] Fix inconsistent quoting in topic messages
---
src/i18n/strings/de_DE.json | 2 +-
src/i18n/strings/pt.json | 2 +-
src/i18n/strings/pt_BR.json | 1 -
src/i18n/strings/ru.json | 2 +-
4 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json
index f09ff9f99b..d927de847c 100644
--- a/src/i18n/strings/de_DE.json
+++ b/src/i18n/strings/de_DE.json
@@ -358,7 +358,7 @@
"%(senderName)s changed their profile picture": "%(senderName)s änderte das Profilbild",
"%(senderName)s changed the power level of %(powerLevelDiffText)s": "%(senderName)s änderte das Berechtigungslevel von %(powerLevelDiffText)s",
"%(senderDisplayName)s changed the room name to %(roomName)s": "%(senderDisplayName)s änderte den Raumnamen zu %(roomName)s",
- "%(senderDisplayName)s changed the topic to %(topic)s": "%(senderDisplayName)s änderte das Thema zu %(topic)s",
+ "%(senderDisplayName)s changed the topic to \"%(topic)s\"": "%(senderDisplayName)s änderte das Thema zu \"%(topic)s\"",
"/ddg is not a command": "/ddg ist kein Kommando",
"%(senderName)s ended the call": "%(senderName)s beendete den Anruf",
"Failed to lookup current room": "Aktuellen Raum nachzuschlagen schlug fehl",
diff --git a/src/i18n/strings/pt.json b/src/i18n/strings/pt.json
index f14f2b31f4..5beb530768 100644
--- a/src/i18n/strings/pt.json
+++ b/src/i18n/strings/pt.json
@@ -319,7 +319,7 @@
"%(senderName)s changed their profile picture": "%(senderName)s alterou sua imagem de perfil",
"%(senderName)s changed the power level of %(powerLevelDiffText)s": "%(senderName)s alterou o nível de permissões de %(powerLevelDiffText)s",
"%(senderDisplayName)s changed the room name to %(roomName)s": "%(senderDisplayName)s alterou o nome da sala para %(roomName)s",
- "%(senderDisplayName)s changed the topic to %(topic)s": "%(senderDisplayName)s alterou o tópico para %(topic)s",
+ "%(senderDisplayName)s changed the topic to \"%(topic)s\"": "%(senderDisplayName)s alterou o tópico para \"%(topic)s\"",
"click to reveal": "clique para ver",
"Conference call failed": "Chamada de conferência falhou",
"Conference calling is in development and may not be reliable": "Chamadas de conferência estão em desenvolvimento e portanto podem não funcionar",
diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json
index 87570228df..5722997165 100644
--- a/src/i18n/strings/pt_BR.json
+++ b/src/i18n/strings/pt_BR.json
@@ -324,7 +324,6 @@
"%(senderName)s changed their profile picture": "%(senderName)s alterou sua imagem de perfil",
"%(senderName)s changed the power level of %(powerLevelDiffText)s": "%(senderName)s alterou o nível de permissões de %(powerLevelDiffText)s",
"%(senderDisplayName)s changed the room name to %(roomName)s": "%(senderDisplayName)s alterou o nome da sala para %(roomName)s",
- "%(senderDisplayName)s changed the topic to %(topic)s": "%(senderDisplayName)s alterou o tópico para %(topic)s",
"click to reveal": "clique para ver",
"Conference call failed": "Chamada de conferência falhou",
"Conference calling is in development and may not be reliable": "Chamadas de conferência estão em desenvolvimento e portanto podem não funcionar",
diff --git a/src/i18n/strings/ru.json b/src/i18n/strings/ru.json
index 2455317292..b22f2d0fa7 100644
--- a/src/i18n/strings/ru.json
+++ b/src/i18n/strings/ru.json
@@ -231,7 +231,7 @@
"%(senderName)s changed their profile picture": "%(senderName)s измененное ихнее фото профиля",
"%(senderName)s changed the power level of %(powerLevelDiffText)s": "%(senderName)s уровень мощности изменен на %(powerLevelDiffText)s",
"%(senderDisplayName)s changed the room name to %(roomName)s": "%(senderDisplayName)s имя комнаты измененно на %(roomName)s",
- "%(senderDisplayName)s changed the topic to %(topic)s": "%(senderDisplayName)s измененная тема на %(topic)s",
+ "%(senderDisplayName)s changed the topic to \"%(topic)s\"": "%(senderDisplayName)s измененная тема на %(topic)s",
"Conference call failed": "Конференц-вызов прервался",
"Conference calling is in development and may not be reliable": "Конференц-вызов находится в процессе и может не быть надежным",
"Conference calls are not supported in encrypted rooms": "Конференц-вызовы не поддерживаются в зашифрованных комнатах",
From 1edad35c9810c4958ba54073dc9fab8dde5455b3 Mon Sep 17 00:00:00 2001
From: Krombel