From 8308555489448b956236dde3bcbedcd32be3b5d2 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 21 Apr 2017 12:12:37 +0100 Subject: [PATCH 0001/1016] Mark sync param as optional so that my IDE will stop complaining. Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/dispatcher.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dispatcher.js b/src/dispatcher.js index 9864cb3807..f3ebed8357 100644 --- a/src/dispatcher.js +++ b/src/dispatcher.js @@ -16,13 +16,13 @@ limitations under the License. 'use strict'; -var flux = require("flux"); +const flux = require("flux"); class MatrixDispatcher extends flux.Dispatcher { /** * @param {Object} payload Required. The payload to dispatch. * Must contain at least an 'action' key. - * @param {boolean} sync Optional. Pass true to dispatch + * @param {boolean=} sync Optional. Pass true to dispatch * synchronously. This is useful for anything triggering * an operation that the browser requires user interaction * for. From f200e349c9f7b23b28441871abf94bcfe0d08f94 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 21 Apr 2017 12:50:36 +0100 Subject: [PATCH 0002/1016] Add .idea to .gitignore file so I don't accidentally upload my IDE config Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 5139d614ad..7006c02403 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,5 @@ npm-debug.log # test reports created by karma /karma-reports + +/.idea From b6ca16fc2f73704536bf93b05db2eeaef040bc98 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 21 Apr 2017 12:56:59 +0100 Subject: [PATCH 0003/1016] add RoomView state for message being forwarded add RoomView action handler for message forward clear forwardingMessage onCancelClick RoomView change var into const in render RoomView load ForwardMessage from rooms.ForwardMessage if there is a messageForwarding object in state show panel in aux Create ForwardMessage class Modify RoomHeader so that it shows the cancel button more greedily reskindex Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/component-index.js | 2 + src/components/structures/RoomView.js | 48 ++++++---- src/components/views/rooms/ForwardMessage.js | 95 ++++++++++++++++++++ src/components/views/rooms/RoomHeader.js | 3 + 4 files changed, 129 insertions(+), 19 deletions(-) create mode 100644 src/components/views/rooms/ForwardMessage.js diff --git a/src/component-index.js b/src/component-index.js index d6873c6dfd..b9f358467e 100644 --- a/src/component-index.js +++ b/src/component-index.js @@ -183,6 +183,8 @@ 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$ForwardMessage from './components/views/rooms/ForwardMessage'; +views$rooms$ForwardMessage && (module.exports.components['views.rooms.ForwardMessage'] = views$rooms$ForwardMessage); 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'; diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index b09b101b8a..ea221e98b7 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -123,6 +123,8 @@ module.exports = React.createClass({ room: null, roomId: null, roomLoading: true, + + forwardingMessage: null, editingRoomSettings: false, uploadingRoomSettings: false, numUnreadMessages: 0, @@ -437,6 +439,11 @@ module.exports = React.createClass({ callState: callState }); + break; + case 'forward_message': + this.setState({ + forwardingMessage: payload.content, + }); break; } }, @@ -1180,7 +1187,10 @@ module.exports = React.createClass({ onCancelClick: function() { console.log("updateTint from onCancelClick"); this.updateTint(); - this.setState({editingRoomSettings: false}); + this.setState({ + editingRoomSettings: false, + forwardingMessage: null, + }); }, onLeaveClick: function() { @@ -1462,16 +1472,17 @@ module.exports = React.createClass({ }, render: function() { - var RoomHeader = sdk.getComponent('rooms.RoomHeader'); - var MessageComposer = sdk.getComponent('rooms.MessageComposer'); - var RoomSettings = sdk.getComponent("rooms.RoomSettings"); - var AuxPanel = sdk.getComponent("rooms.AuxPanel"); - var SearchBar = sdk.getComponent("rooms.SearchBar"); - var ScrollPanel = sdk.getComponent("structures.ScrollPanel"); - var TintableSvg = sdk.getComponent("elements.TintableSvg"); - var RoomPreviewBar = sdk.getComponent("rooms.RoomPreviewBar"); - var Loader = sdk.getComponent("elements.Spinner"); - var TimelinePanel = sdk.getComponent("structures.TimelinePanel"); + const RoomHeader = sdk.getComponent('rooms.RoomHeader'); + const MessageComposer = sdk.getComponent('rooms.MessageComposer'); + const ForwardMessage = sdk.getComponent("rooms.ForwardMessage"); + const RoomSettings = sdk.getComponent("rooms.RoomSettings"); + const AuxPanel = sdk.getComponent("rooms.AuxPanel"); + const SearchBar = sdk.getComponent("rooms.SearchBar"); + const ScrollPanel = sdk.getComponent("structures.ScrollPanel"); + const TintableSvg = sdk.getComponent("elements.TintableSvg"); + const RoomPreviewBar = sdk.getComponent("rooms.RoomPreviewBar"); + const Loader = sdk.getComponent("elements.Spinner"); + const TimelinePanel = sdk.getComponent("structures.TimelinePanel"); if (!this.state.room) { if (this.state.roomLoading) { @@ -1599,17 +1610,16 @@ module.exports = React.createClass({ />; } - var aux = null; - if (this.state.editingRoomSettings) { + let aux = null; + if (this.state.forwardingMessage !== null) { + aux = ; + } else if (this.state.editingRoomSettings) { aux = ; - } - else if (this.state.uploadingRoomSettings) { + } else if (this.state.uploadingRoomSettings) { aux = ; - } - else if (this.state.searching) { + } else if (this.state.searching) { aux = ; - } - else if (!myMember || myMember.membership !== "join") { + } else if (!myMember || myMember.membership !== "join") { // We do have a room object for this room, but we're not currently in it. // We may have a 3rd party invite to it. var inviterName = undefined; diff --git a/src/components/views/rooms/ForwardMessage.js b/src/components/views/rooms/ForwardMessage.js new file mode 100644 index 0000000000..58aac4edd1 --- /dev/null +++ b/src/components/views/rooms/ForwardMessage.js @@ -0,0 +1,95 @@ +/* + Copyright 2017 Vector Creations Ltd + Copyright 2017 Michael Telatynski + + 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 dis from '../../../dispatcher'; +import KeyCode from "../../../KeyCode"; + + +module.exports = React.createClass({ + displayName: 'ForwardMessage', + + propTypes: { + content: React.PropTypes.object.isRequired, + + // true if RightPanel is collapsed + collapsedRhs: React.PropTypes.bool, + onCancelClick: React.PropTypes.func.isRequired, + }, + + componentWillMount: function() { + this._unmounted = false; + + if (!this.props.collapsedRhs) { + dis.dispatch({ + action: 'hide_right_panel', + }); + } + + dis.dispatch({ + action: 'ui_opacity', + sideOpacity: 1.0, + middleOpacity: 0.3, + }); + }, + + componentDidMount: function() { + this.dispatcherRef = dis.register(this.onAction); + document.addEventListener('keydown', this._onKeyDown); + }, + + componentWillUnmount: function() { + this._unmounted = true; + dis.dispatch({ + action: 'show_right_panel', + }); + dis.dispatch({ + action: 'ui_opacity', + sideOpacity: 1.0, + middleOpacity: 1.0, + }); + dis.unregister(this.dispatcherRef); + document.removeEventListener('keydown', this._onKeyDown); + }, + + onAction: function(payload) { + if (payload.action === 'view_room') { + MatrixClientPeg.get().sendMessage(payload.room_id, this.props.content); + } + }, + + _onKeyDown: function(ev) { + switch (ev.keyCode) { + case KeyCode.ESCAPE: + this.props.onCancelClick(); + dis.dispatch({action: 'focus_composer'}); + break; + } + }, + + render: function() { + return ( +
+ +

Select a room to send the message to

+

Use the left sidebar Room List to select forwarding target

+ +
+ ); + }, +}); diff --git a/src/components/views/rooms/RoomHeader.js b/src/components/views/rooms/RoomHeader.js index 94f2691f2c..3d2386070c 100644 --- a/src/components/views/rooms/RoomHeader.js +++ b/src/components/views/rooms/RoomHeader.js @@ -186,6 +186,9 @@ module.exports = React.createClass({ ); save_button = Save; + } + + if (this.props.onCancelClick) { cancel_button = ; } From 64112da25ccb25797dd1cb9a1727fe5ae6c0a468 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 21 Apr 2017 14:01:04 +0100 Subject: [PATCH 0004/1016] only re-show right panel if it was visible before we were mounted Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/rooms/ForwardMessage.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/components/views/rooms/ForwardMessage.js b/src/components/views/rooms/ForwardMessage.js index 58aac4edd1..efecd1381f 100644 --- a/src/components/views/rooms/ForwardMessage.js +++ b/src/components/views/rooms/ForwardMessage.js @@ -55,9 +55,13 @@ module.exports = React.createClass({ componentWillUnmount: function() { this._unmounted = true; - dis.dispatch({ - action: 'show_right_panel', - }); + + if (!this.props.collapsedRhs) { + dis.dispatch({ + action: 'show_right_panel', + }); + } + dis.dispatch({ action: 'ui_opacity', sideOpacity: 1.0, From 589d41e3b9f3e17bb960c67540ac235038ae785c Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 21 Apr 2017 18:50:29 +0100 Subject: [PATCH 0005/1016] DRY the code a little bit in anticipation of #813 which sends a `focus_composer` onCancelClick() Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/rooms/ForwardMessage.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/views/rooms/ForwardMessage.js b/src/components/views/rooms/ForwardMessage.js index efecd1381f..42e2465037 100644 --- a/src/components/views/rooms/ForwardMessage.js +++ b/src/components/views/rooms/ForwardMessage.js @@ -81,7 +81,6 @@ module.exports = React.createClass({ switch (ev.keyCode) { case KeyCode.ESCAPE: this.props.onCancelClick(); - dis.dispatch({action: 'focus_composer'}); break; } }, From e6fd380947207a161a9ac8edb336eddd21bc6533 Mon Sep 17 00:00:00 2001 From: turt2live Date: Mon, 24 Apr 2017 12:49:09 -0600 Subject: [PATCH 0006/1016] Change redact -> remove for clarity Addresses vector-im/riot-web#2814 Non-technical users may not understand what 'redact' means and can more easily understand what 'Remove' does. See discussion on vector-im/riot-web#2814 for more information. Signed-off-by: Travis Ralston --- src/components/views/dialogs/ConfirmRedactDialog.js | 8 ++++---- src/components/views/messages/UnknownBody.js | 2 +- src/components/views/rooms/RoomSettings.js | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/views/dialogs/ConfirmRedactDialog.js b/src/components/views/dialogs/ConfirmRedactDialog.js index fc9e55f666..db5197e338 100644 --- a/src/components/views/dialogs/ConfirmRedactDialog.js +++ b/src/components/views/dialogs/ConfirmRedactDialog.js @@ -42,7 +42,7 @@ export default React.createClass({ render: function() { const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); - const title = "Confirm Redaction"; + const title = "Confirm Removal"; const confirmButtonClass = classnames({ 'mx_Dialog_primary': true, @@ -55,12 +55,12 @@ export default React.createClass({ title={title} >
- Are you sure you wish to redact (delete) this event? - Note that if you redact a room name or topic change, it could undo the change. + Are you sure you wish to remove (delete) this event? + Note that if you delete a room name or topic change, it could undo the change.
- To redact messages, you must be a + To remove messages, you must be a
From fbca0e0d0d320d970826d6112b2b793b1a093530 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 24 Apr 2017 20:03:07 +0100 Subject: [PATCH 0007/1016] correct cancel appearing when it shouldn't Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/RoomView.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 9cd5070ad1..91d5e01f78 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -1778,7 +1778,7 @@ module.exports = React.createClass({ onSearchClick={this.onSearchClick} onSettingsClick={this.onSettingsClick} onSaveClick={this.onSettingsSaveClick} - onCancelClick={this.onCancelClick} + onCancelClick={aux ? this.onCancelClick : null} onForgetClick={ (myMember && myMember.membership === "leave") ? this.onForgetClick : null } From ee560a969a17591ea39590a61bc0f3327e9df8ab Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 24 Apr 2017 20:17:29 +0100 Subject: [PATCH 0008/1016] upon forwarding message to current room, explicitly remove clear from aux Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/RoomView.js | 2 +- src/components/views/rooms/ForwardMessage.js | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 91d5e01f78..e941c6b1a8 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -1614,7 +1614,7 @@ module.exports = React.createClass({ let aux = null; if (this.state.forwardingMessage !== null) { - aux = ; + aux = ; } else if (this.state.editingRoomSettings) { aux = ; } else if (this.state.uploadingRoomSettings) { diff --git a/src/components/views/rooms/ForwardMessage.js b/src/components/views/rooms/ForwardMessage.js index 42e2465037..bb401dde69 100644 --- a/src/components/views/rooms/ForwardMessage.js +++ b/src/components/views/rooms/ForwardMessage.js @@ -25,6 +25,7 @@ module.exports = React.createClass({ displayName: 'ForwardMessage', propTypes: { + currentRoomId: React.PropTypes.string.isRequired, content: React.PropTypes.object.isRequired, // true if RightPanel is collapsed @@ -74,6 +75,7 @@ module.exports = React.createClass({ onAction: function(payload) { if (payload.action === 'view_room') { MatrixClientPeg.get().sendMessage(payload.room_id, this.props.content); + if (this.props.currentRoomId === payload.room_id) this.props.onCancelClick(); } }, From 4cf9e0c1ae46d22baef4463376b33c7e577f62cc Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 24 Apr 2017 22:00:59 +0100 Subject: [PATCH 0009/1016] Create a way to restore last state of the rhs panel. Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/MatrixChat.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 9b8aa3426a..6df8d99c3a 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -303,6 +303,7 @@ module.exports = React.createClass({ componentDidUpdate: function() { if (this.focusComposer) { + console.log('is this the shitty duplicate?'); dis.dispatch({action: 'focus_composer'}); this.focusComposer = false; } @@ -547,15 +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': + this.setState({ + collapse_rhs: this.was_rhs_collapsed, + }); + break; case 'ui_opacity': this.setState({ sideOpacity: payload.sideOpacity, From bfba25f3dac7a0279f19300023065032ad251fc3 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 24 Apr 2017 22:10:14 +0100 Subject: [PATCH 0010/1016] we don't care about rhs state anymore as we can just restore it sanely Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/RoomView.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index e941c6b1a8..3e43d6984a 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -1614,7 +1614,7 @@ module.exports = React.createClass({ let aux = null; if (this.state.forwardingMessage !== null) { - aux = ; + aux = ; } else if (this.state.editingRoomSettings) { aux = ; } else if (this.state.uploadingRoomSettings) { From 28a2266f70ee5aaf18d0eb06333e19a6b9e558cc Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 24 Apr 2017 22:11:20 +0100 Subject: [PATCH 0011/1016] tidy up UDE handler Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/UnknownDeviceErrorHandler.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/UnknownDeviceErrorHandler.js b/src/UnknownDeviceErrorHandler.js index 2aa0573e22..2b1cf23380 100644 --- a/src/UnknownDeviceErrorHandler.js +++ b/src/UnknownDeviceErrorHandler.js @@ -22,7 +22,7 @@ let isDialogOpen = false; const onAction = function(payload) { if (payload.action === 'unknown_device_error' && !isDialogOpen) { - var UnknownDeviceDialog = sdk.getComponent("dialogs.UnknownDeviceDialog"); + const UnknownDeviceDialog = sdk.getComponent('dialogs.UnknownDeviceDialog'); isDialogOpen = true; Modal.createDialog(UnknownDeviceDialog, { devices: payload.err.devices, @@ -33,17 +33,17 @@ const onAction = function(payload) { // https://github.com/vector-im/riot-web/issues/3148 console.log('UnknownDeviceDialog closed with '+r); }, - }, "mx_Dialog_unknownDevice"); + }, 'mx_Dialog_unknownDevice'); } -} +}; let ref = null; -export function startListening () { +export function startListening() { ref = dis.register(onAction); } -export function stopListening () { +export function stopListening() { if (ref) { dis.unregister(ref); ref = null; From 9ae9aeea0746e5190865491f6602346af6fb1327 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 24 Apr 2017 22:14:45 +0100 Subject: [PATCH 0012/1016] lets improve forwarding :D ditch double quotes stop caring about rhs state always call hide_right_panel, nop if already hidden use new restore_right_panel to bring it back if it was visible pre-us actually tell things that we sent a message or failed in doing so now the UDE works :D Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/rooms/ForwardMessage.js | 32 ++++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/components/views/rooms/ForwardMessage.js b/src/components/views/rooms/ForwardMessage.js index bb401dde69..e5be89b2e0 100644 --- a/src/components/views/rooms/ForwardMessage.js +++ b/src/components/views/rooms/ForwardMessage.js @@ -18,7 +18,7 @@ import React from 'react'; import MatrixClientPeg from '../../../MatrixClientPeg'; import dis from '../../../dispatcher'; -import KeyCode from "../../../KeyCode"; +import KeyCode from '../../../KeyCode'; module.exports = React.createClass({ @@ -28,20 +28,13 @@ module.exports = React.createClass({ currentRoomId: React.PropTypes.string.isRequired, content: React.PropTypes.object.isRequired, - // true if RightPanel is collapsed - collapsedRhs: React.PropTypes.bool, onCancelClick: React.PropTypes.func.isRequired, }, componentWillMount: function() { this._unmounted = false; - if (!this.props.collapsedRhs) { - dis.dispatch({ - action: 'hide_right_panel', - }); - } - + dis.dispatch({action: 'hide_right_panel'}); dis.dispatch({ action: 'ui_opacity', sideOpacity: 1.0, @@ -57,12 +50,7 @@ module.exports = React.createClass({ componentWillUnmount: function() { this._unmounted = true; - if (!this.props.collapsedRhs) { - dis.dispatch({ - action: 'show_right_panel', - }); - } - + dis.dispatch({action: 'restore_right_panel'}); dis.dispatch({ action: 'ui_opacity', sideOpacity: 1.0, @@ -74,7 +62,19 @@ module.exports = React.createClass({ onAction: function(payload) { if (payload.action === 'view_room') { - MatrixClientPeg.get().sendMessage(payload.room_id, this.props.content); + const Client = MatrixClientPeg.get(); + Client.sendMessage(payload.room_id, this.props.content).done(() => { + dis.dispatch({action: 'message_sent'}); + }, (err) => { + if (err.name === "UnknownDeviceError") { + dis.dispatch({ + action: 'unknown_device_error', + err: err, + room: Client.getRoom(payload.room_id), + }); + } + dis.dispatch({action: 'message_send_failed'}); + }); if (this.props.currentRoomId === payload.room_id) this.props.onCancelClick(); } }, From 3997974f0f4036af92187ad1d0d50198ad58710a Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 24 Apr 2017 22:16:15 +0100 Subject: [PATCH 0013/1016] remove debug console log (ignore its content pls) Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- 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 6df8d99c3a..25ec644787 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -303,7 +303,6 @@ module.exports = React.createClass({ componentDidUpdate: function() { if (this.focusComposer) { - console.log('is this the shitty duplicate?'); dis.dispatch({action: 'focus_composer'}); this.focusComposer = false; } From 8774100508cc36b5b0b96a4d78001543cdbe2f9f Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Fri, 28 Apr 2017 13:22:55 +0100 Subject: [PATCH 0014/1016] Initial implementation: SetDisplayName -> SetMxIdDialog - Replaces SetDisplayNameDialog with SetMxIdDialog. This new dialog will use InteractiveAuth to authenticate a user with their chosen mxid. De-scoped: - style tweaks for the InteractiveAuth in the dialog (capcha) and error message. - checking for mxid availability --- src/component-index.js | 4 +- src/components/structures/LoggedInView.js | 5 + src/components/structures/MatrixChat.js | 1 + src/components/structures/RoomView.js | 53 +++--- .../views/dialogs/SetDisplayNameDialog.js | 87 ---------- src/components/views/dialogs/SetMxIdDialog.js | 153 ++++++++++++++++++ 6 files changed, 182 insertions(+), 121 deletions(-) delete mode 100644 src/components/views/dialogs/SetDisplayNameDialog.js create mode 100644 src/components/views/dialogs/SetMxIdDialog.js diff --git a/src/component-index.js b/src/component-index.js index d6873c6dfd..2e55ad14cc 100644 --- a/src/component-index.js +++ b/src/component-index.js @@ -95,8 +95,8 @@ import views$dialogs$QuestionDialog from './components/views/dialogs/QuestionDia 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$SetMxIdDialog from './components/views/dialogs/SetMxIdDialog'; +views$dialogs$SetMxIdDialog && (module.exports.components['views.dialogs.SetMxIdDialog'] = views$dialogs$SetMxIdDialog); 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'; diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js index c4eeb03d5f..6514b32f79 100644 --- a/src/components/structures/LoggedInView.js +++ b/src/components/structures/LoggedInView.js @@ -43,6 +43,10 @@ export default React.createClass({ onRoomCreated: React.PropTypes.func, onUserSettingsClose: React.PropTypes.func, + // Called with the credentials of a registered user (if they were a ROU that + // transitioned to PWLU) + onRegistered: React.PropTypes.func, + teamToken: React.PropTypes.string, // and lots and lots of other stuff. @@ -184,6 +188,7 @@ export default React.createClass({ roomAddress={this.props.currentRoomAlias || this.props.currentRoomId} autoJoin={this.props.autoJoin} onRoomIdResolved={this.props.onRoomIdResolved} + onRegistered={this.props.onRegistered} eventId={this.props.initialEventId} thirdPartyInvite={this.props.thirdPartyInvite} oobData={this.props.roomOobData} diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 9b8aa3426a..0fe2d6a52f 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -1166,6 +1166,7 @@ module.exports = React.createClass({ onRoomIdResolved={this.onRoomIdResolved} onRoomCreated={this.onRoomCreated} onUserSettingsClose={this.onUserSettingsClose} + onRegistered={this.onRegistered} teamToken={this._teamToken} {...this.props} {...this.state} diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index a0c36374b6..83b446597c 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -69,6 +69,10 @@ module.exports = React.createClass({ // once it has been resolved. onRoomIdResolved: React.PropTypes.func, + // Called with the credentials of a registered user (if they were a ROU that + // transitioned to PWLU) + onRegistered: React.PropTypes.func, + // An object representing a third party invite to join this room // Fields: // * inviteSignUrl (string) The URL used to join this room from an email invite @@ -764,38 +768,27 @@ module.exports = React.createClass({ var self = this; var cli = MatrixClientPeg.get(); - var display_name_promise = q(); - // if this is the first room we're joining, check the user has a display name - // and if they don't, prompt them to set one. - // NB. This unfortunately does not re-use the ChangeDisplayName component because - // it doesn't behave quite as desired here (we want an input field here rather than - // content-editable, and we want a default). - if (cli.getRooms().filter((r) => { - return r.hasMembershipState(cli.credentials.userId, "join"); - })) { - display_name_promise = cli.getProfileInfo(cli.credentials.userId).then((result) => { - if (!result.displayname) { - var SetDisplayNameDialog = sdk.getComponent('views.dialogs.SetDisplayNameDialog'); - var dialog_defer = q.defer(); - Modal.createDialog(SetDisplayNameDialog, { - currentDisplayName: result.displayname, - onFinished: (submitted, newDisplayName) => { - if (submitted) { - cli.setDisplayName(newDisplayName).done(() => { - dialog_defer.resolve(); - }); - } - else { - dialog_defer.reject(); - } - } - }); - return dialog_defer.promise; + var mxIdPromise = q(); + + // If the user is a ROU, allow them to transition to a PWLU + if (cli.isGuest()) { + const SetMxIdDialog = sdk.getComponent('views.dialogs.SetMxIdDialog'); + mxIdPromise = q.defer(); + Modal.createDialog(SetMxIdDialog, { + onFinished: (submitted, credentials) => { + if (!submitted) { + mxIdPromise.reject(); + } + this.props.onRegistered(credentials); + mxIdPromise.resolve(); } }); } - display_name_promise.then(() => { + mxIdPromise.then(() => { + this.setState({ + joining: true + }); // 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); @@ -870,10 +863,6 @@ module.exports = React.createClass({ }); } }).done(); - - this.setState({ - joining: true - }); }, onMessageListScroll: function(ev) { diff --git a/src/components/views/dialogs/SetDisplayNameDialog.js b/src/components/views/dialogs/SetDisplayNameDialog.js deleted file mode 100644 index 1047e05c26..0000000000 --- a/src/components/views/dialogs/SetDisplayNameDialog.js +++ /dev/null @@ -1,87 +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. -*/ - -import React from 'react'; -import sdk from '../../../index'; -import MatrixClientPeg from '../../../MatrixClientPeg'; - -/** - * Prompt the user to set a display name. - * - * On success, `onFinished(true, newDisplayName)` is called. - */ -export default React.createClass({ - displayName: 'SetDisplayNameDialog', - propTypes: { - onFinished: React.PropTypes.func.isRequired, - currentDisplayName: React.PropTypes.string, - }, - - getInitialState: function() { - if (this.props.currentDisplayName) { - return { value: this.props.currentDisplayName }; - } - - if (MatrixClientPeg.get().isGuest()) { - return { value : "Guest " + MatrixClientPeg.get().getUserIdLocalpart() }; - } - else { - return { value : MatrixClientPeg.get().getUserIdLocalpart() }; - } - }, - - componentDidMount: function() { - this.refs.input_value.select(); - }, - - onValueChange: function(ev) { - this.setState({ - value: ev.target.value - }); - }, - - onFormSubmit: function(ev) { - ev.preventDefault(); - this.props.onFinished(true, this.state.value); - return false; - }, - - render: function() { - const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); - return ( - -
- Your display name is how you'll appear to others when you speak in rooms.
- What would you like it to be? -
-
-
- -
-
- -
-
-
- ); - }, -}); diff --git a/src/components/views/dialogs/SetMxIdDialog.js b/src/components/views/dialogs/SetMxIdDialog.js new file mode 100644 index 0000000000..78737e4ac4 --- /dev/null +++ b/src/components/views/dialogs/SetMxIdDialog.js @@ -0,0 +1,153 @@ +/* +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. +*/ + +import q from 'q'; +import React from 'react'; +import sdk from '../../../index'; +import MatrixClientPeg from '../../../MatrixClientPeg'; + +/** + * Prompt the user to set a display name. + * + * On success, `onFinished(true, newDisplayName)` is called. + */ +export default React.createClass({ + displayName: 'SetMxIdDialog', + propTypes: { + onFinished: React.PropTypes.func.isRequired, + }, + + getInitialState: function() { + return { + username : '', + doingUIAuth: false, + } + }, + + componentDidMount: function() { + this.refs.input_value.select(); + + this._matrixClient = MatrixClientPeg.get(); + }, + + onValueChange: function(ev) { + this.setState({ + username: ev.target.value + }); + }, + + onSubmit: function(ev) { + this.setState({ + doingUIAuth: true, + }); + }, + + _generateAndCachePassword: function() { + const pass = Math.random().toString(36).slice(2); + if (localStorage) { + localStorage.setItem('mx_pass', pass); + } + return pass; + }, + + _makeRegisterRequest: function(auth) { + // Not upgrading - changing mxids + const guestAccessToken = null; + + return this._matrixClient.register( + this.state.username, + this._generateAndCachePassword(), + undefined, // session id: included in the auth dict already + auth, + {}, + guestAccessToken, + ); + }, + + _onUIAuthFinished: function(success, response) { + this.setState({ + doingUIAuth: false, + }); + console.info('Auth Finsihed', arguments); + + if (!success) { + this.setState({ errorText : response.message }); + return; + } + + // XXX Implement RTS /register here + const teamToken = null; + + this.props.onFinished(true, { + userId: response.user_id, + deviceId: response.device_id, + homeserverUrl: this._matrixClient.getHomeserverUrl(), + identityServerUrl: this._matrixClient.getIdentityServerUrl(), + accessToken: response.access_token, + teamToken: teamToken, + }); + }, + + render: function() { + 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 = ; + } + return ( + +
+

+ Beyond this point you're going to need to pick a username - your + unique identifire 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 } +
+
+
+ +
+
+ ); + }, +}); From 6dff4a44157bd8006a4ee6ccc97efa534bbf5b36 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Fri, 28 Apr 2017 13:28:34 +0100 Subject: [PATCH 0015/1016] Return early after cancelled mxid dialog --- src/components/structures/RoomView.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 83b446597c..d06c2c2226 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -778,6 +778,7 @@ module.exports = React.createClass({ onFinished: (submitted, credentials) => { if (!submitted) { mxIdPromise.reject(); + return; } this.props.onRegistered(credentials); mxIdPromise.resolve(); From d12b1903f2b528426931271c6256aa016082c00d Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Fri, 28 Apr 2017 13:29:30 +0100 Subject: [PATCH 0016/1016] Fix defer promise logic --- src/components/structures/RoomView.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index d06c2c2226..569c4e46f0 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -773,15 +773,16 @@ module.exports = React.createClass({ // If the user is a ROU, allow them to transition to a PWLU if (cli.isGuest()) { const SetMxIdDialog = sdk.getComponent('views.dialogs.SetMxIdDialog'); - mxIdPromise = q.defer(); + const defered = q.defer(); + mxIdPromise = defered.promise; Modal.createDialog(SetMxIdDialog, { onFinished: (submitted, credentials) => { if (!submitted) { - mxIdPromise.reject(); + defered.reject(); return; } this.props.onRegistered(credentials); - mxIdPromise.resolve(); + defered.resolve(); } }); } From 5a5768a4ecc9033f64e98e146ad8628a61cf079c Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Fri, 28 Apr 2017 13:38:35 +0100 Subject: [PATCH 0017/1016] Try to fix tests --- src/components/structures/RoomView.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 569c4e46f0..848a8cc7ba 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -771,7 +771,7 @@ module.exports = React.createClass({ var mxIdPromise = q(); // If the user is a ROU, allow them to transition to a PWLU - if (cli.isGuest()) { + if (cli && cli.isGuest()) { const SetMxIdDialog = sdk.getComponent('views.dialogs.SetMxIdDialog'); const defered = q.defer(); mxIdPromise = defered.promise; From c6262d62a63fee8aad8546141e5898c400f73284 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 28 Apr 2017 18:21:22 +0100 Subject: [PATCH 0018/1016] webrtc config electron init on LoggedInView mounting configurable via UserSettings new class: CallMediaHandler Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/CallMediaHandler.js | 63 ++++++++++++++++++ src/components/structures/LoggedInView.js | 5 ++ src/components/structures/UserSettings.js | 78 ++++++++++++++++++++++- 3 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 src/CallMediaHandler.js diff --git a/src/CallMediaHandler.js b/src/CallMediaHandler.js new file mode 100644 index 0000000000..9133a6548d --- /dev/null +++ b/src/CallMediaHandler.js @@ -0,0 +1,63 @@ +/* + Copyright 2017 Michael Telatynski <7t3chguy@gmail.com> + + 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 UserSettingsStore from './UserSettingsStore'; +import * as Matrix from 'matrix-js-sdk'; +import q from 'q'; + +export default { + getDevices: function() { + // Only needed for Electron atm, though should work in modern browsers + // once permission has been granted to the webapp + return navigator.mediaDevices.enumerateDevices().then(function(devices) { + const audioIn = {}; + const videoIn = {}; + + devices.forEach((device) => { + switch (device.kind) { + case 'audioinput': audioIn[device.deviceId] = device.label; break; + case 'videoinput': videoIn[device.deviceId] = device.label; break; + } + }); + + // console.log("Loaded WebRTC Devices", mediaDevices); + return { + audioinput: audioIn, + videoinput: videoIn, + }; + }, (error) => { console.log('Unable to refresh WebRTC Devices: ', error); }); + }, + + loadDevices: function() { + // this.getDevices().then((devices) => { + const localSettings = UserSettingsStore.getLocalSettings(); + // // if deviceId is not found, automatic fallback is in spec + // // recall previously stored inputs if any + Matrix.setMatrixCallAudioInput(localSettings['webrtc_audioinput']); + Matrix.setMatrixCallVideoInput(localSettings['webrtc_videoinput']); + // }); + }, + + setAudioInput: function(deviceId) { + UserSettingsStore.setLocalSetting('webrtc_audioinput', deviceId); + Matrix.setMatrixCallAudioInput(deviceId); + }, + + setVideoInput: function(deviceId) { + UserSettingsStore.setLocalSetting('webrtc_videoinput', deviceId); + Matrix.setMatrixCallVideoInput(deviceId); + }, +}; diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js index 9f01b0082b..8d18e92a0d 100644 --- a/src/components/structures/LoggedInView.js +++ b/src/components/structures/LoggedInView.js @@ -21,6 +21,7 @@ import React from 'react'; import KeyCode from '../../KeyCode'; import Notifier from '../../Notifier'; import PageTypes from '../../PageTypes'; +import CallMediaHandler from '../../CallMediaHandler'; import sdk from '../../index'; import dis from '../../dispatcher'; @@ -71,6 +72,10 @@ 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(); + document.addEventListener('keydown', this._onKeyDown); }, diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index ba5d5780b4..05410e866f 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -24,6 +24,7 @@ var dis = require("../../dispatcher"); var q = require('q'); var package_json = require('../../../package.json'); var UserSettingsStore = require('../../UserSettingsStore'); +var CallMediaHandler = require('../../CallMediaHandler'); var GeminiScrollbar = require('react-gemini-scrollbar'); var Email = require('../../email'); var AddThreepid = require('../../AddThreepid'); @@ -109,7 +110,6 @@ const THEMES = [ } ]; - module.exports = React.createClass({ displayName: 'UserSettings', @@ -147,6 +147,7 @@ module.exports = React.createClass({ email_add_pending: false, vectorVersion: null, rejectingInvites: false, + mediaDevices: null, }; }, @@ -167,6 +168,18 @@ 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', + }); + }); + // Bulk rejecting invites: // /sync won't have had time to return when UserSettings re-renders from state changes, so getRooms() // will still return rooms with invites. To get around this, add a listener for @@ -187,6 +200,8 @@ 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() { @@ -775,6 +790,66 @@ module.exports = React.createClass({ ; }, + _mapWebRtcDevicesToSpans: function(devices) { + return Object.keys(devices).map( + (deviceId) => {devices[deviceId]} + ); + }, + + _setAudioInput: function(deviceId) { + this.setState({activeAudioInput: deviceId}); + CallMediaHandler.setAudioInput(deviceId); + }, + + _setVideoInput: function(deviceId) { + this.setState({activeVideoInput: deviceId}); + CallMediaHandler.setVideoInput(deviceId); + }, + + _renderWebRtcSettings: function() { + if (!(window && window.process && window.process.type) + || !this.state.mediaDevices) return; + + const Dropdown = sdk.getComponent('elements.Dropdown'); + + let microphoneDropdown =
No Microphones detected
; + let webcamDropdown =
No Webcams detected
; + + const audioInputs = this.state.mediaDevices.audioinput; + if ('default' in audioInputs) { + microphoneDropdown =
+

Microphone

+ + {this._mapWebRtcDevicesToSpans(audioInputs)} + +
; + } + + const videoInputs = this.state.mediaDevices.videoinput; + if ('default' in videoInputs) { + webcamDropdown =
+

Cameras

+ + {this._mapWebRtcDevicesToSpans(videoInputs)} + +
; + } + + return
+

WebRTC

+
+ {microphoneDropdown} + {webcamDropdown} +
+
; + }, + _showSpoiler: function(event) { const target = event.target; const hidden = target.getAttribute('data-spoiler'); @@ -973,6 +1048,7 @@ module.exports = React.createClass({ {this._renderUserInterfaceSettings()} {this._renderLabs()} + {this._renderWebRtcSettings()} {this._renderDevicesPanel()} {this._renderCryptoInfo()} {this._renderBulkOptions()} From a887af9f92c8f9c9b58bcff8190eb42f1e7f6bb3 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Tue, 2 May 2017 09:56:14 +0100 Subject: [PATCH 0019/1016] copyright --- src/component-index.js | 1 + src/components/views/dialogs/SetMxIdDialog.js | 1 + 2 files changed, 2 insertions(+) diff --git a/src/component-index.js b/src/component-index.js index 2e55ad14cc..aa65cabbec 100644 --- a/src/component-index.js +++ b/src/component-index.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. diff --git a/src/components/views/dialogs/SetMxIdDialog.js b/src/components/views/dialogs/SetMxIdDialog.js index 78737e4ac4..c8083371de 100644 --- a/src/components/views/dialogs/SetMxIdDialog.js +++ b/src/components/views/dialogs/SetMxIdDialog.js @@ -1,5 +1,6 @@ /* 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. From 4f71f4c331bb36d19ccdd92d2d92425bc7cf3138 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Tue, 2 May 2017 10:07:06 +0100 Subject: [PATCH 0020/1016] Store mx_pass at the same point as access_token So that we don't overwrite the existing one every time we try to register. --- src/Lifecycle.js | 6 ++++++ src/components/views/dialogs/SetMxIdDialog.js | 12 +++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/Lifecycle.js b/src/Lifecycle.js index f20716cae6..7fba0a0298 100644 --- a/src/Lifecycle.js +++ b/src/Lifecycle.js @@ -303,6 +303,12 @@ export function setLoggedIn(credentials) { localStorage.setItem("mx_device_id", credentials.deviceId); } + // 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); + } + console.log("Session persisted for %s", credentials.userId); } catch (e) { console.warn("Error using local storage: can't persist session!", e); diff --git a/src/components/views/dialogs/SetMxIdDialog.js b/src/components/views/dialogs/SetMxIdDialog.js index c8083371de..db88c3304b 100644 --- a/src/components/views/dialogs/SetMxIdDialog.js +++ b/src/components/views/dialogs/SetMxIdDialog.js @@ -56,21 +56,18 @@ export default React.createClass({ }); }, - _generateAndCachePassword: function() { - const pass = Math.random().toString(36).slice(2); - if (localStorage) { - localStorage.setItem('mx_pass', pass); - } - return pass; + _generatePassword: function() { + return Math.random().toString(36).slice(2); }, _makeRegisterRequest: function(auth) { // Not upgrading - changing mxids const guestAccessToken = null; + this._generatedPassword = this._generatePassword(); return this._matrixClient.register( this.state.username, - this._generateAndCachePassword(), + this._generatedPassword, undefined, // session id: included in the auth dict already auth, {}, @@ -98,6 +95,7 @@ export default React.createClass({ homeserverUrl: this._matrixClient.getHomeserverUrl(), identityServerUrl: this._matrixClient.getIdentityServerUrl(), accessToken: response.access_token, + password: this._generatedPassword, teamToken: teamToken, }); }, From 13d37e43ff85605ba74a06c146a0ab11086421d8 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Tue, 2 May 2017 10:14:54 +0100 Subject: [PATCH 0021/1016] Mock isGuest --- test/test-utils.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test-utils.js b/test/test-utils.js index 5209465362..9f404f98eb 100644 --- a/test/test-utils.js +++ b/test/test-utils.js @@ -133,6 +133,7 @@ export function createTestClient() { sendHtmlMessage: () => q({}), getSyncState: () => "SYNCING", generateClientSecret: () => "t35tcl1Ent5ECr3T", + isGuest: () => false, }; } From 18ba5d3e49319c4c47d70a8c0ec1ec9ca84a5fd9 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@googlemail.com> Date: Wed, 3 May 2017 12:39:24 +0100 Subject: [PATCH 0022/1016] fix typo made in #849 --- src/components/views/dialogs/SetMxIdDialog.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/dialogs/SetMxIdDialog.js b/src/components/views/dialogs/SetMxIdDialog.js index db88c3304b..d139a4ae3b 100644 --- a/src/components/views/dialogs/SetMxIdDialog.js +++ b/src/components/views/dialogs/SetMxIdDialog.js @@ -122,7 +122,7 @@ export default React.createClass({

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 =
+
+ +
+
+ +
+
; + } switch (this.state.phase) { case this.Phases.Edit: return (
-
-
- -
-
- -
-
+ { currentPassword }
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 =
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 = Encrypted by verified device; } // real padlocks else if (this.props.mxEvent.isEncrypted() || (e2eEnabled && this.props.eventSendStatus)) { if (this.props.mxEvent.getContent().msgtype === 'm.bad.encrypted') { - e2e = ; + e2e = Undecryptable; } else if (this.state.verified == true || (e2eEnabled && this.props.eventSendStatus)) { - e2e = ; + e2e = Encrypted by verified device; } else { - e2e = ; + e2e = Encrypted by unverified device; } } else if (e2eEnabled) { - e2e = ; + e2e = Unencrypted message; } 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.
- Close 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({
Level:
+
+ +
{ 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 = (
+
+
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} -
-
- - -
-
- ); - }, -}); 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 `
+ 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.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 (
- + , ); - var error; - var addressSelector; + let error; + let addressSelector; if (this.state.error) { error =
{_t("You have entered an invalid contact. Try using their Matrix ID or email address.")}
; + } else if ( + this.state.query.length > 0 && + this.state.queryList.length === 0 && + !this.state.busy + ) { + error =
{_t("No results")}
; } else { const addressSelectorHeader =
- Searching known users + { _t("Searching known users") }
; addressSelector = ( {this.addressSelector = ref;}} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index c87ffb8cdf..eb331c24a9 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -599,6 +599,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?", + "Searching known users": "Searching known users", "%(senderName)s withdrew %(targetName)s's invitation.": "%(senderName)s withdrew %(targetName)s's invitation.", "Would you like to": "Would you like to", "You are already in a call.": "You are already in a call.", From 49b22fb6c6f42fd9366cb19c261b1b03359a6aed Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Wed, 7 Jun 2017 16:13:40 +0100 Subject: [PATCH 0630/1016] Use an arrow function to allow `this` This was causing `TypeError: Cannot read property '_getEmptyContent' of undefined` for those with custom tags. --- 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 bf270b1148..f90d89c8c1 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -578,7 +578,7 @@ module.exports = React.createClass({ onHeaderClick={ self.onSubListHeaderClick } onShowMoreRooms={ self.onShowMoreRooms } /> - { Object.keys(self.state.lists).map(function(tagName) { + { Object.keys(self.state.lists).map((tagName) => { if (!tagName.match(/^(m\.(favourite|lowpriority)|im\.vector\.fake\.(invite|recent|direct|archived))$/)) { return Date: Wed, 7 Jun 2017 17:33:15 +0100 Subject: [PATCH 0631/1016] Use then, catch, done and display any errors that occur during search --- .../views/dialogs/ChatInviteDialog.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/components/views/dialogs/ChatInviteDialog.js b/src/components/views/dialogs/ChatInviteDialog.js index 867a3b423c..0c65f02525 100644 --- a/src/components/views/dialogs/ChatInviteDialog.js +++ b/src/components/views/dialogs/ChatInviteDialog.js @@ -61,6 +61,8 @@ module.exports = React.createClass({ // Whether a search is ongoing busy: false, + // An error message generated during the user directory search + searchError: null, // The query being searched for query: "", // List of AddressTile.InviteAddressType objects representing @@ -181,6 +183,7 @@ module.exports = React.createClass({ this.setState({ busy: true, query, + searchError: null, }); MatrixClientPeg.get().searchUserDirectory({ term: query, @@ -222,7 +225,12 @@ module.exports = React.createClass({ }, () => { this.addressSelector.moveSelectionTop(); }); - }).finally(() => { + }).catch((err) => { + console.error('Error whilst searching user directory: ', err); + this.setState({ + searchError: err.errcode ? err.message : _t('Something went wrong!'), + }); + }).done(() => { this.setState({ busy: false, }); @@ -232,6 +240,7 @@ module.exports = React.createClass({ this.setState({ queryList: [], query: "", + searchError: null, }); } }, @@ -494,6 +503,8 @@ module.exports = React.createClass({ let addressSelector; if (this.state.error) { error =
{_t("You have entered an invalid contact. Try using their Matrix ID or email address.")}
; + } else if (this.state.searchError) { + error =
{this.state.searchError}
; } else if ( this.state.query.length > 0 && this.state.queryList.length === 0 && @@ -501,15 +512,11 @@ module.exports = React.createClass({ ) { error =
{_t("No results")}
; } else { - const addressSelectorHeader =
- { _t("Searching known users") } -
; addressSelector = ( {this.addressSelector = ref;}} addressList={ this.state.queryList } onSelected={ this.onSelected } truncateAt={ TRUNCATE_QUERY_LIST } - header={ addressSelectorHeader } /> ); } From 3db286f8f007f10ad99a81cf2924ca4179b9123b Mon Sep 17 00:00:00 2001 From: Krombel Date: Wed, 7 Jun 2017 16:45:34 +0000 Subject: [PATCH 0632/1016] Translated using Weblate (German) Currently translated at 100.0% (850 of 850 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/de/ --- src/i18n/strings/de_DE.json | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index b6011ccf2d..79603ad361 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -111,7 +111,7 @@ "Default": "Standard", "demote": "Berechtigungslevel herabstufen", "Export E2E room keys": "E2E-Raum-Schlüssel exportieren", - "Failed to change password. Is your password correct?": "Passwort-Änderung schlug fehl. Ist dein Passwort korrekt?", + "Failed to change password. Is your password correct?": "Passwortänderung fehlgeschlagen. Ist dein Passwort richtig?", "Failed to forget room": "Vergessen des Raums schlug fehl", "Failed to leave room": "Verlassen des Raums fehlgeschlagen", "Failed to reject invitation": "Fehler beim Abweisen der Einladung", @@ -890,5 +890,35 @@ "%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s hat das Raum-Bild für %(roomName)s geändert", "Hide removed messages": "Gelöschte Nachrichten verbergen", "Start new chat": "Neuen Chat starten", - "Disable markdown formatting": "Deaktiviere Markdown-Formatierung" + "Disable markdown formatting": "Deaktiviere Markdown-Formatierung", + "Add": "Hinzufügen", + "%(count)s new messages.one": "%(count)s neue Nachricht", + "%(count)s new messages.other": "%(count)s neue Nachrichten", + "Error: Problem communicating with the given homeserver.": "Fehler: Problem beim kommunizieren mit dem angegebenen Heimserver.", + "Failed to fetch avatar URL": "Fehler beim holen der Avatar-URL", + "Some of your messages have not been sent.": "Einige deiner Nachrichten wurden nicht gesendet.", + "The phone number entered looks invalid": "Die Telefonnummer, die eingegeben wurde, sieht ungültig aus", + "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 betretbar. Du kannst evtl. beitreten 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.": "Es wurde versucht einen spezifischen Punkt in der Chat-Historie zu laden, aber du hast keine Berechtigung diese Nachricht zu sehen.", + "Tried to load a specific point in this room's timeline, but was unable to find it.": "Es wurde versucht einen spezifischen Punkt in der Chat-Historie zu laden, aber er konnte nicht gefunden werden.", + "Uploading %(filename)s and %(count)s others.zero": "%(filename)s wird hochgeladen", + "Uploading %(filename)s and %(count)s others.one": "%(filename)s und %(count)s weitere werden hochgeladen", + "Uploading %(filename)s and %(count)s others.other": "%(filename)s und %(count)s weitere werden hochgeladen", + "You must register to use this functionality": "Du musst dich registrieren um diese Funktionalität zu nutzen", + "Resend all or cancel all now. You can also select individual messages to resend or cancel.": "Sende erneut oder breche alles ab. Du kannst auch auch individuelle Nachrichten erneut senden or abbrechen.", + "Create new room": "Erstelle neuen Raum", + "Welcome page": "Willkommensseite", + "Room directory": "Raum-Verzeichnis", + "Start chat": "Starte Chat", + "New Password": "Neues Passwort", + "Start chatting": "Starte plaudern", + "Start Chatting": "Starte Gespräche", + "Click on the button below to start chatting!": "Klicke den Button unten um das Plaudern zu beginnen!", + "Create a new chat or reuse an existing one": "Erstelle einen neuen Chat oder nutze einen existierenden", + "You already have existing direct chats with this user:": "Du hast bereits direkte Chats mit diesem Nutzer:", + "Username available": "Nutzername verfügbar", + "Username not available": "Nutzername nicht verfügbar", + "Something went wrong!": "Etwas ging schief!", + "This will be your account name on the homeserver, or you can pick a different server.": "Dies wird dein Konto-Name auf dem Heimserver, oder du kannst einen anderen Server auswählen.", + "If you already have a Matrix account you can log in instead.": "Wenn du bereits ein Matrix-Konto hast, kannst du ansonsten auch anmelden." } From b2a17fea08acba34ee3de3b444b2d8e9de6ca98d Mon Sep 17 00:00:00 2001 From: Amandine Date: Wed, 7 Jun 2017 16:28:38 +0000 Subject: [PATCH 0633/1016] Translated using Weblate (French) Currently translated at 96.5% (821 of 850 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/fr/ --- src/i18n/strings/fr.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index 0ae02be82f..c4a606b9bc 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -827,5 +827,8 @@ "You have disabled URL previews by default.": "Vous avez désactivé les aperçus d’URL par défaut.", "You have enabled URL previews by default.": "Vous avez activé les aperçus d’URL par défaut.", "You have entered an invalid contact. Try using their Matrix ID or email address.": "Vous avez entré un contact invalide. Essayez d’utiliser leur identifiant Matrix ou leur adresse email.", - "Hide removed messages": "Cacher les messages supprimés" + "Hide removed messages": "Cacher les messages supprimés", + "Add": "Ajouter", + "%(count)s new messages.one": "%(count)s nouveau message", + "%(count)s new messages.other": "%(count)s nouveaux messages" } From c11fd8c3b18f7d6b7ea01af9ea09047d03df3720 Mon Sep 17 00:00:00 2001 From: Pitchaya Boonsarngsuk Date: Wed, 7 Jun 2017 16:45:41 +0000 Subject: [PATCH 0634/1016] Translated using Weblate (Thai) Currently translated at 39.0% (332 of 850 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/th/ --- src/i18n/strings/th.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/th.json b/src/i18n/strings/th.json index 2fa93e02fc..a05f5c8f68 100644 --- a/src/i18n/strings/th.json +++ b/src/i18n/strings/th.json @@ -327,5 +327,9 @@ "%(oneUser)schanged their name": "%(oneUser)sเปลี่ยนชื่อของเขาแล้ว", "%(severalUsers)schanged their name %(repeats)s times": "%(severalUsers)sเปลี่ยนชื่อของพวกเขา %(repeats)s ครั้ง", "%(oneUser)schanged their name %(repeats)s times": "%(oneUser)sเปลี่ยนชื่อของเขา %(repeats)s ครั้ง", - "%(severalUsers)schanged their name": "%(severalUsers)sเปลี่ยนชื่อของพวกเขาแล้ว" + "%(severalUsers)schanged their name": "%(severalUsers)sเปลี่ยนชื่อของพวกเขาแล้ว", + "Create new room": "สร้างห้องใหม่", + "Room directory": "ไดเรกทอรีห้อง", + "Start chat": "เริ่มแชท", + "Welcome page": "หน้าต้อนรับ" } From 29da25529c903a5d621032271cf17979c36a27cc Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Wed, 7 Jun 2017 17:40:09 +0100 Subject: [PATCH 0635/1016] Make it backwards compatible Hit the user_directory API and if M_UNRECOGNIZED, do a local search and continue to search locally therein. --- .../views/dialogs/ChatInviteDialog.js | 154 +++++++++++------- 1 file changed, 99 insertions(+), 55 deletions(-) diff --git a/src/components/views/dialogs/ChatInviteDialog.js b/src/components/views/dialogs/ChatInviteDialog.js index 0c65f02525..2f635fd670 100644 --- a/src/components/views/dialogs/ChatInviteDialog.js +++ b/src/components/views/dialogs/ChatInviteDialog.js @@ -63,6 +63,8 @@ module.exports = React.createClass({ busy: false, // An error message generated during the user directory search searchError: null, + // Whether the server supports the user_directory API + serverSupportsUserDirectory: true, // The query being searched for query: "", // List of AddressTile.InviteAddressType objects representing @@ -180,61 +182,11 @@ module.exports = React.createClass({ // Only do search if there is something to search if (query.length > 0 && query != '@' && query.length >= 2) { this.queryChangedDebouncer = setTimeout(() => { - this.setState({ - busy: true, - query, - searchError: null, - }); - MatrixClientPeg.get().searchUserDirectory({ - term: query, - }).then((resp) => { - const queryList = []; - resp.results.forEach((user) => { - if (user.user_id === MatrixClientPeg.get().credentials.userId) { - return; - } - // Return objects, structure of which is defined - // by InviteAddressType - queryList.push({ - addressType: 'mx', - address: user.user_id, - displayName: user.display_name, - avatarMxc: user.avatar_url, - isKnown: true, - }); - }); - - // If the query is a valid address, add an entry for that - // This is important, otherwise there's no way to invite - // a perfectly valid address if there are close matches. - const addrType = getAddressType(query); - if (addrType !== null) { - queryList.unshift({ - addressType: addrType, - address: query, - isKnown: false, - }); - if (this._cancelThreepidLookup) this._cancelThreepidLookup(); - if (addrType == 'email') { - this._lookupThreepid(addrType, query).done(); - } - } - this.setState({ - queryList, - error: false, - }, () => { - this.addressSelector.moveSelectionTop(); - }); - }).catch((err) => { - console.error('Error whilst searching user directory: ', err); - this.setState({ - searchError: err.errcode ? err.message : _t('Something went wrong!'), - }); - }).done(() => { - this.setState({ - busy: false, - }); - }); + if (this.state.serverSupportsUserDirectory) { + this._doUserDirectorySearch(query); + } else { + this._doLocalSearch(query); + } }, QUERY_USER_DIRECTORY_DEBOUNCE_MS); } else { this.setState({ @@ -277,6 +229,98 @@ module.exports = React.createClass({ if (this._cancelThreepidLookup) this._cancelThreepidLookup(); }, + _doUserDirectorySearch: function(query) { + this.setState({ + busy: true, + query, + searchError: null, + }); + MatrixClientPeg.get().searchUserDirectory({ + term: query, + }).then((resp) => { + this._processResults(resp.results, query); + }).catch((err) => { + console.error('Error whilst searching user directory: ', err); + this.setState({ + searchError: err.errcode ? err.message : _t('Something went wrong!'), + }); + if (err.errcode === 'M_UNRECOGNIZED') { + this.setState({ + serverSupportsUserDirectory: false, + }); + // Do a local search immediately + this._doLocalSearch(query); + } + }).done(() => { + this.setState({ + busy: false, + }); + }); + }, + + _doLocalSearch: function(query) { + this.setState({ + query, + searchError: null, + }); + const results = []; + MatrixClientPeg.get().getUsers().forEach((user) => { + if (user.userId.toLowerCase().indexOf(query) === -1 && + user.displayName.toLowerCase().indexOf(query) === -1 + ) { + return; + } + + // Put results in the format of the new API + results.push({ + user_id: user.userId, + display_name: user.displayName, + avatar_url: user.avatarUrl, + }); + }); + this._processResults(results, query); + }, + + _processResults: function(results, query) { + const queryList = []; + results.forEach((user) => { + if (user.user_id === MatrixClientPeg.get().credentials.userId) { + return; + } + // Return objects, structure of which is defined + // by InviteAddressType + queryList.push({ + addressType: 'mx', + address: user.user_id, + displayName: user.display_name, + avatarMxc: user.avatar_url, + isKnown: true, + }); + }); + + // If the query is a valid address, add an entry for that + // This is important, otherwise there's no way to invite + // a perfectly valid address if there are close matches. + const addrType = getAddressType(query); + if (addrType !== null) { + queryList.unshift({ + addressType: addrType, + address: query, + isKnown: false, + }); + if (this._cancelThreepidLookup) this._cancelThreepidLookup(); + if (addrType == 'email') { + this._lookupThreepid(addrType, query).done(); + } + } + this.setState({ + queryList, + error: false, + }, () => { + if (this.addressSelector) this.addressSelector.moveSelectionTop(); + }); + }, + _getDirectMessageRooms: function(addr) { const dmRoomMap = new DMRoomMap(MatrixClientPeg.get()); const dmRooms = dmRoomMap.getDMRoomsForUserId(addr); From 74d680a9524aa1eb73d0b4f276c78ce80b5985f9 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Wed, 7 Jun 2017 18:15:13 +0100 Subject: [PATCH 0636/1016] sync fullstops everywhere --- src/i18n/strings/de_DE.json | 28 ++++++++++++++-------------- src/i18n/strings/el.json | 4 ++-- src/i18n/strings/en_US.json | 10 +++++----- src/i18n/strings/es.json | 2 +- src/i18n/strings/fr.json | 10 +++++----- src/i18n/strings/pt.json | 10 +++++----- src/i18n/strings/pt_BR.json | 10 +++++----- src/i18n/strings/ru.json | 6 +++--- 8 files changed, 40 insertions(+), 40 deletions(-) diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index 15375b8f90..513faea017 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -227,7 +227,7 @@ "to join the discussion": "um an der Diskussion teilzunehmen", "To kick users": "Um Nutzer zu entfernen", "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", + "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.", "Could not connect to the integration server": "Konnte keine Verbindung zum Integrations-Server herstellen", "Disable inline URL previews by default": "URL-Vorschau im Chat standardmäßig deaktivieren", "Guests can't use labs features. Please register.": "Gäste können keine Labor-Funktionen nutzen. Bitte registrieren.", @@ -280,15 +280,15 @@ "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 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 werden in verschlüsselten Räumen nicht unterstützt", "Conference calls are not supported in this client": "Konferenzgespräche werden von diesem Client nicht unterstützt", "Existing Call": "Bereits bestehender Anruf", "Failed to set up conference call": "Konferenzgespräch konnte nicht gestartet werden", "Failed to verify email address: make sure you clicked the link in the email": "Verifizierung der E-Mail-Adresse fehlgeschlagen: Bitte stelle sicher, dass du den Link in der E-Mail angeklickt hast", "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", + "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", @@ -302,11 +302,11 @@ "Unable to enable Notifications": "Benachrichtigungen konnten nicht aktiviert werden", "Upload Failed": "Upload fehlgeschlagen", "VoIP is unsupported": "VoIP wird 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 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üsselungs-Schlüssel für dieses Gerät zu generieren und um den öffentlichen Schlüssel auf deinem Homeserver zu hinterlegen. Dies muss nur einmal durchgeführt werden, bitte entschuldige die Unannehmlichkeiten.", - "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", + "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", @@ -581,7 +581,7 @@ "Failed to save settings": "Einstellungen konnten nicht gespeichert werden", "Failed to set display name": "Anzeigename konnte nicht gesetzt werden", "Fill screen": "Fülle Bildschirm", - "Guest users can't upload files. Please register to upload": "Gäste können keine Dateien hochladen. Bitte zunächst registrieren", + "Guest users can't upload files. Please register to upload.": "Gäste können keine Dateien hochladen. Bitte zunächst registrieren.", "Hide Text Formatting Toolbar": "Verberge Text-Formatierungs-Toolbar", "Incorrect verification code": "Falscher Verifizierungscode", "Invalid alias format": "Ungültiges Alias-Format", @@ -608,16 +608,16 @@ "Server error": "Server-Fehler", "Server may be unavailable, overloaded, or search timed out :(": "Der Server ist entweder nicht verfügbar, überlastet oder die Suche wurde wegen Zeitüberschreitung abgebrochen :(", "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": "Der Server ist entweder nicht verfügbar, überlastet oder es liegt ein anderweitiger Fehler vor", - "Some of your messages have not been sent": "Einige deiner Nachrichten wurden noch nicht gesendet", + "Server unavailable, overloaded, or something else went wrong.": "Der Server ist entweder nicht verfügbar, überlastet oder es liegt ein anderweitiger Fehler vor.", + "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 zugänglich. Du kannst jedoch eventuell beitreten, 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": "Der Versuch, einen spezifischen Punkt im Chatverlauf zu laden, ist fehlgeschlagen. Der Punkt konnte nicht gefunden werden", + "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 zugänglich. Du kannst jedoch eventuell beitreten, 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.": "Der Versuch, einen spezifischen Punkt im Chatverlauf zu laden, ist fehlgeschlagen. Der Punkt konnte nicht gefunden werden.", "Turn Markdown off": "Markdown abschalten", "Turn Markdown on": "Markdown einschalten", "Unable to load device list": "Geräteliste konnte nicht geladen werden", diff --git a/src/i18n/strings/el.json b/src/i18n/strings/el.json index ab2129dc81..d8729202d5 100644 --- a/src/i18n/strings/el.json +++ b/src/i18n/strings/el.json @@ -48,7 +48,7 @@ "Authentication": "Πιστοποίηση", "and": "και", "An email has been sent to": "Ένα email στάλθηκε σε", - "A new password must be entered.": "Ο νέος κωδικός πρέπει να εισαχθεί", + "A new password must be entered.": "Ο νέος κωδικός πρέπει να εισαχθεί.", "%(senderName)s answered the call.": "Ο χρήστης %(senderName)s απάντησε.", "An error has occurred.": "Ένα σφάλμα προέκυψε", "Anyone": "Oποιοσδήποτε", @@ -265,7 +265,7 @@ "For security, this session has been signed out. Please sign in again.": "Για λόγους ασφαλείας, αυτή η συνεδρία έχει τερματιστεί. Παρακαλώ συνδεθείτε ξανά.", "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.": "Για λόγους ασφαλείας, τα κλειδιά κρυπτογράφησης θα διαγράφονται από τον φυλλομετρητή κατά την αποσύνδεση σας. Εάν επιθυμείτε να αποκρυπτογραφήσετε τις συνομιλίες σας στο μέλλον, εξάγετε τα κλειδιά σας και κρατήστε τα ασφαλή.", "Found a bug?": "Βρήκατε κάποιο πρόβλημα;", - "Guest users can't upload files. Please register to upload": "Οι επισκέπτες δεν μπορούν να ανεβάσουν αρχεία. Παρακαλώ εγγραφείτε πρώτα", + "Guest users can't upload files. Please register to upload.": "Οι επισκέπτες δεν μπορούν να ανεβάσουν αρχεία. Παρακαλώ εγγραφείτε πρώτα.", "had": "είχε", "Hangup": "Κλείσε", "Historical": "Ιστορικό", diff --git a/src/i18n/strings/en_US.json b/src/i18n/strings/en_US.json index 2ad228254b..19b854e936 100644 --- a/src/i18n/strings/en_US.json +++ b/src/i18n/strings/en_US.json @@ -308,7 +308,7 @@ "Guest access is disabled on this Home Server.": "Guest access is disabled on this Home Server.", "Guests can't set avatars. Please register.": "Guests can't set avatars. Please register.", "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", + "Guest users can't upload files. Please register to upload.": "Guest users can't upload files. Please register to upload.", "Guests can't use labs features. Please register.": "Guests can't use labs features. Please register.", "Guests cannot join this room even if explicitly invited.": "Guests cannot join this room even if explicitly invited.", "had": "had", @@ -476,7 +476,7 @@ "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", + "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 recognized ", "Start a chat": "Start a chat", @@ -501,7 +501,7 @@ "There was a problem logging in.": "There was a problem logging in.", "This room has no local addresses": "This room has no local addresses", "This room is not recognised.": "This room is not recognized.", - "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", + "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", @@ -530,8 +530,8 @@ "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", + "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).", diff --git a/src/i18n/strings/es.json b/src/i18n/strings/es.json index 9a07710570..125a929b9f 100644 --- a/src/i18n/strings/es.json +++ b/src/i18n/strings/es.json @@ -282,7 +282,7 @@ "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s de %(fromPowerLevel)s a %(toPowerLevel)s", "Guests can't set avatars. Please register.": "Invitados no puedes establecer avatares. Por favor regístrate.", "Guest users can't create new rooms. Please register to create room and start a chat.": "Usuarios invitados no pueden crear nuevas salas. Por favor regístrate para crear la sala y iniciar la conversación.", - "Guest users can't upload files. Please register to upload": "Usuarios invitados no puedes subir archivos. Por favor regístrate para subir tus archivos", + "Guest users can't upload files. Please register to upload.": "Usuarios invitados no puedes subir archivos. Por favor regístrate para subir tus archivos.", "Guests can't use labs features. Please register.": "Invitados no puedes usar las características en desarrollo. Por favor regístrate.", "Guests cannot join this room even if explicitly invited.": "Invitados no pueden unirse a esta sala aun cuando han sido invitados explícitamente.", "had": "tuvo", diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index 0ae02be82f..80d2479c48 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -294,7 +294,7 @@ "Found a bug?": "Trouvé un problème ?", "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s de %(fromPowerLevel)s à %(toPowerLevel)s", "Guest users can't create new rooms. Please register to create room and start a chat.": "Les visiteurs ne peuvent créer de nouveaux salons. Merci de vous enregistrer pour commencer une discussion.", - "Guest users can't upload files. Please register to upload": "Les visiteurs ne peuvent telécharger de fichiers. Merci de vous enregistrer pour télécharger", + "Guest users can't upload files. Please register to upload.": "Les visiteurs ne peuvent telécharger de fichiers. Merci de vous enregistrer pour télécharger.", "had": "avait", "Hangup": "Raccrocher", "Hide read receipts": "Cacher les accusés de réception", @@ -457,7 +457,7 @@ "since the point in time of selecting this option": "depuis le moment où cette option a été sélectionnée", "since they joined": "depuis qu’ils ont rejoint le salon", "since they were invited": "depuis qu’ils ont été invités", - "Some of your messages have not been sent": "Certains de vos messages n’ont pas été envoyés", + "Some of your messages have not been sent.": "Certains de vos messages n’ont pas été envoyés.", "Someone": "Quelqu'un", "Sorry, this homeserver is using a login which is not recognised ": "Désolé, ce homeserver utilise un identifiant qui n’est pas reconnu ", "Start a chat": "Démarrer une conversation", @@ -478,7 +478,7 @@ "The remote side failed to pick up": "Le correspondant n’a pas décroché", "This room has no local addresses": "Ce salon n'a pas d'adresse locale", "This room is not recognised.": "Ce salon n'a pas été reconnu.", - "This room is private or inaccessible to guests. You may be able to join if you register": "Ce salon est privé ou non autorisé aux visiteurs. Vous devriez pouvoir le rejoindre si vous vous enregistrez", + "This room is private or inaccessible to guests. You may be able to join if you register.": "Ce salon est privé ou non autorisé aux visiteurs. Vous devriez pouvoir le rejoindre si vous vous enregistrez.", "These are experimental features that may break in unexpected ways": "Ces fonctionnalités sont expérimentales et risquent de mal fonctionner", "The visibility of existing history will be unchanged": "La visibilité de l’historique existant sera inchangée", "This doesn't appear to be a valid email address": "Cette adresse n’a pas l’air d’être valide", @@ -507,8 +507,8 @@ "to tag as %(tagName)s": "pour marquer comme %(tagName)s", "to tag direct chat": "pour marquer comme conversation directe", "To use it, just wait for autocomplete results to load and tab through them.": "Pour l’utiliser, attendez simplement que les résultats de l’auto-complétion s’affichent et défilez avec la touche Tab.", - "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question": "Une tentative de chargement d’un point donné dans la chronologie de ce salon a été effectuée, mais vous n’avez pas la permission de voir le message en question", - "Tried to load a specific point in this room's timeline, but was unable to find it": "Une tentative de chargement d’un point donné dans la chronologie de ce salon a été effectuée, mais il n’a pas été trouvé", + "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.": "Une tentative de chargement d’un point donné dans la chronologie de ce salon a été effectuée, mais vous n’avez pas la permission de voir le message en question.", + "Tried to load a specific point in this room's timeline, but was unable to find it.": "Une tentative de chargement d’un point donné dans la chronologie de ce salon a été effectuée, mais il n’a pas été trouvé.", "Turn Markdown off": "Désactiver le formatage ’Markdown’", "Turn Markdown on": "Activer le formatage ’Markdown’", "%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s).": "%(senderName)s a activé l’encryption bout-en-bout (algorithme %(algorithm)s).", diff --git a/src/i18n/strings/pt.json b/src/i18n/strings/pt.json index fb90423c3a..47260b4909 100644 --- a/src/i18n/strings/pt.json +++ b/src/i18n/strings/pt.json @@ -101,7 +101,7 @@ "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", + "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", @@ -627,15 +627,15 @@ "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", + "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", + "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", diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json index fc942c2ed8..13c9ec0737 100644 --- a/src/i18n/strings/pt_BR.json +++ b/src/i18n/strings/pt_BR.json @@ -101,7 +101,7 @@ "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", + "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", @@ -627,15 +627,15 @@ "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", + "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", + "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", diff --git a/src/i18n/strings/ru.json b/src/i18n/strings/ru.json index a0788a15dc..cf9bd78ffd 100644 --- a/src/i18n/strings/ru.json +++ b/src/i18n/strings/ru.json @@ -498,7 +498,7 @@ "Failed to set display name": "Не удалось установить отображаемое имя", "Failed to toggle moderator status": "Не удалось изменить статус модератора", "Fill screen": "Заполнить экран", - "Guest users can't upload files. Please register to upload": "Гости не могут посылать файлы. Пожалуйста, зарегистрируйтесь для отправки", + "Guest users can't upload files. Please register to upload.": "Гости не могут посылать файлы. Пожалуйста, зарегистрируйтесь для отправки.", "Hide read receipts": "Скрыть отметки о прочтении", "Hide Text Formatting Toolbar": "Скрыть панель форматирования текста", "Incorrect verification code": "Неверный код подтверждения", @@ -567,7 +567,7 @@ "since the point in time of selecting this option": "с момента выбора этой настройки", "since they joined": "с момента входа", "since they were invited": "с момента приглашения", - "Some of your messages have not been sent": "Некоторые из ваших сообщений не были отправлены", + "Some of your messages have not been sent.": "Некоторые из ваших сообщений не были отправлены.", "Someone": "Кто-то", "Submit": "Отправить", "Success": "Успех", @@ -583,7 +583,7 @@ "The remote side failed to pick up": "Удалённая сторона не смогла ответить", "This room has no local addresses": "Эта комната не имеет местного адреса", "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": "Это экспериментальные функции, которые могут неожиданным образом вызывать ошибки", "This doesn't appear to be a valid email address": "Не похоже, что это правильный адрес электронной почты", "This is a preview of this room. Room interactions have been disabled": "Это просмотр данной комнаты. Взаимодействия с ней были отключены.", From fe487232ade5ebe30e3e1b7b60b08574d7729f0e Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Wed, 7 Jun 2017 18:15:48 +0100 Subject: [PATCH 0637/1016] sync fullstops everywhere --- scripts/fix-i18n.pl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/fix-i18n.pl b/scripts/fix-i18n.pl index 247b2b663f..ea19d710df 100755 --- a/scripts/fix-i18n.pl +++ b/scripts/fix-i18n.pl @@ -61,6 +61,11 @@ You are already in a call. You cannot place VoIP calls in this browser. You cannot place a call with yourself. Your email address does not appear to be associated with a Matrix ID on this Homeserver. +Guest users can't upload files. Please register to upload. +Some of your messages have not been sent. +This room is private or inaccessible to guests. You may be able to join if you register. +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 you do not have permission to view the message in question. EOT )]; } From ef2fedc3a9ead6c38888276ad93aed361ef6d6eb Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Wed, 7 Jun 2017 18:24:35 +0100 Subject: [PATCH 0638/1016] fix missing translations and typos in i18n --- src/components/structures/CreateRoom.js | 2 +- src/components/views/dialogs/SetMxIdDialog.js | 6 +++--- src/i18n/strings/de_DE.json | 2 +- src/i18n/strings/en_EN.json | 4 +++- src/i18n/strings/en_US.json | 2 +- src/i18n/strings/fr.json | 2 +- src/i18n/strings/pt.json | 2 +- src/i18n/strings/pt_BR.json | 2 +- src/i18n/strings/ru.json | 2 +- 9 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/components/structures/CreateRoom.js b/src/components/structures/CreateRoom.js index 3e291dfd94..7ecc315ba7 100644 --- a/src/components/structures/CreateRoom.js +++ b/src/components/structures/CreateRoom.js @@ -231,7 +231,7 @@ module.exports = React.createClass({ if (curr_phase == this.phases.ERROR) { error_box = (
- {_t('An error occured: %(error_string)s', {error_string: this.state.error_string})} + {_t('An error occurred: %(error_string)s', {error_string: this.state.error_string})}
); } diff --git a/src/components/views/dialogs/SetMxIdDialog.js b/src/components/views/dialogs/SetMxIdDialog.js index 78576eb1e4..2081701c6b 100644 --- a/src/components/views/dialogs/SetMxIdDialog.js +++ b/src/components/views/dialogs/SetMxIdDialog.js @@ -125,8 +125,8 @@ export default React.createClass({ break; case "M_INVALID_USERNAME": newState.usernameError = _t( - 'Username invalid: %(errMessage)', - { errMessage: err.message}, + 'Username invalid: %(error_message)s', + { error_message: err.message}, ); break; case "M_UNRECOGNIZED": @@ -139,7 +139,7 @@ export default React.createClass({ break; default: newState.usernameError = _t( - 'An error occurred: %(errMessage)', + 'An error occurred: %(errMessage)s', { errMessage: err.message }, ); break; diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index 513faea017..798540900c 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -339,7 +339,7 @@ "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", + "An error occurred: %(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": "Bisherigen Chatverlauf mit neuen Nutzern teilen", diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index eb331c24a9..11af1c03df 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -268,6 +268,7 @@ "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", + "Enter passphrase": "Enter passphrase", "Error": "Error", "Error decrypting attachment": "Error decrypting attachment", "Error: Problem communicating with the given homeserver.": "Error: Problem communicating with the given homeserver.", @@ -580,6 +581,7 @@ "User ID": "User ID", "User Interface": "User Interface", "User name": "User name", + "Username invalid: %(errMessage)s": "Username invalid: %(errMessage)s", "Users": "Users", "User": "User", "Verification Pending": "Verification Pending", @@ -661,7 +663,7 @@ "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", + "An error occurred: %(error_string)s": "An error occurred: %(error_string)s", "Topic": "Topic", "Make Moderator": "Make Moderator", "Make this room private": "Make this room private", diff --git a/src/i18n/strings/en_US.json b/src/i18n/strings/en_US.json index 19b854e936..96f2243f63 100644 --- a/src/i18n/strings/en_US.json +++ b/src/i18n/strings/en_US.json @@ -645,7 +645,7 @@ "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", + "An error occurred: %(error_string)s": "An error occurred: %(error_string)s", "Topic": "Topic", "Make Moderator": "Make Moderator", "Make this room private": "Make this room private", diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index 80d2479c48..ff6cb57ab5 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -611,7 +611,7 @@ "User names may only contain letters, numbers, dots, hyphens and underscores.": "Les noms d’utilisateurs ne peuvent contenir que des lettres, chiffres, points et tirets hauts ou bas.", "An unknown error occurred.": "Une erreur inconnue est survenue.", "I already have an account": "J’ai déjà un compte", - "An error occured: %(error_string)s": "Une erreur est survenue : %(error_string)s", + "An error occurred: %(error_string)s": "Une erreur est survenue : %(error_string)s", "Topic": "Sujet", "Make Moderator": "Nommer modérateur", "Make this room private": "Rendre ce salon privé", diff --git a/src/i18n/strings/pt.json b/src/i18n/strings/pt.json index 47260b4909..b76cf6a274 100644 --- a/src/i18n/strings/pt.json +++ b/src/i18n/strings/pt.json @@ -417,7 +417,7 @@ "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", + "An error occurred: %(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", diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json index 13c9ec0737..f77ef74799 100644 --- a/src/i18n/strings/pt_BR.json +++ b/src/i18n/strings/pt_BR.json @@ -417,7 +417,7 @@ "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", + "An error occurred: %(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", diff --git a/src/i18n/strings/ru.json b/src/i18n/strings/ru.json index cf9bd78ffd..61c82e6448 100644 --- a/src/i18n/strings/ru.json +++ b/src/i18n/strings/ru.json @@ -336,7 +336,7 @@ "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", + "An error occurred: %(error_string)s": "Произошла ошибка: %(error_string)s", "Topic": "Тема", "Make this room private": "Сделать эту комнату частной", "Share message history with new users": "Поделись историей сообщений с новыми учасниками", From 8228db88d2f8ea3f2acca0709dd431976c59192c Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Wed, 7 Jun 2017 18:28:49 +0100 Subject: [PATCH 0639/1016] oops, fix more i18n var name typos --- src/components/views/dialogs/SetMxIdDialog.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/views/dialogs/SetMxIdDialog.js b/src/components/views/dialogs/SetMxIdDialog.js index 2081701c6b..d428223ad6 100644 --- a/src/components/views/dialogs/SetMxIdDialog.js +++ b/src/components/views/dialogs/SetMxIdDialog.js @@ -125,8 +125,8 @@ export default React.createClass({ break; case "M_INVALID_USERNAME": newState.usernameError = _t( - 'Username invalid: %(error_message)s', - { error_message: err.message}, + 'Username invalid: %(errMessage)s', + { errMessage: err.message}, ); break; case "M_UNRECOGNIZED": @@ -139,8 +139,8 @@ export default React.createClass({ break; default: newState.usernameError = _t( - 'An error occurred: %(errMessage)s', - { errMessage: err.message }, + 'An error occurred: %(error_string)s', + { error_string: err.message }, ); break; } From f7ab6a574ccda7e6469ae1ec962a6aa0520c9991 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 7 Jun 2017 18:32:32 +0100 Subject: [PATCH 0640/1016] Remove unused string --- src/i18n/strings/de_DE.json | 1 - src/i18n/strings/en_EN.json | 1 - src/i18n/strings/en_US.json | 1 - src/i18n/strings/fr.json | 1 - src/i18n/strings/pt.json | 1 - src/i18n/strings/pt_BR.json | 1 - src/i18n/strings/ru.json | 1 - src/i18n/strings/th.json | 1 - 8 files changed, 8 deletions(-) diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index 798540900c..6d3abef3e4 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -615,7 +615,6 @@ "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 zugänglich. Du kannst jedoch eventuell beitreten, 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.": "Der Versuch, einen spezifischen Punkt im Chatverlauf zu laden, ist fehlgeschlagen. Der Punkt konnte nicht gefunden werden.", "Turn Markdown off": "Markdown abschalten", diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 11af1c03df..6a7f01e9df 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -511,7 +511,6 @@ "There was a problem logging in.": "There was a problem logging in.", "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", diff --git a/src/i18n/strings/en_US.json b/src/i18n/strings/en_US.json index 96f2243f63..6736e39276 100644 --- a/src/i18n/strings/en_US.json +++ b/src/i18n/strings/en_US.json @@ -501,7 +501,6 @@ "There was a problem logging in.": "There was a problem logging in.", "This room has no local addresses": "This room has no local addresses", "This room is not recognised.": "This room is not recognized.", - "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", diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index ff6cb57ab5..bf05173356 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -478,7 +478,6 @@ "The remote side failed to pick up": "Le correspondant n’a pas décroché", "This room has no local addresses": "Ce salon n'a pas d'adresse locale", "This room is not recognised.": "Ce salon n'a pas été reconnu.", - "This room is private or inaccessible to guests. You may be able to join if you register.": "Ce salon est privé ou non autorisé aux visiteurs. Vous devriez pouvoir le rejoindre si vous vous enregistrez.", "These are experimental features that may break in unexpected ways": "Ces fonctionnalités sont expérimentales et risquent de mal fonctionner", "The visibility of existing history will be unchanged": "La visibilité de l’historique existant sera inchangée", "This doesn't appear to be a valid email address": "Cette adresse n’a pas l’air d’être valide", diff --git a/src/i18n/strings/pt.json b/src/i18n/strings/pt.json index b76cf6a274..27d257de9e 100644 --- a/src/i18n/strings/pt.json +++ b/src/i18n/strings/pt.json @@ -633,7 +633,6 @@ "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'", diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json index f77ef74799..99175c874e 100644 --- a/src/i18n/strings/pt_BR.json +++ b/src/i18n/strings/pt_BR.json @@ -633,7 +633,6 @@ "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'", diff --git a/src/i18n/strings/ru.json b/src/i18n/strings/ru.json index 61c82e6448..58f8e0291c 100644 --- a/src/i18n/strings/ru.json +++ b/src/i18n/strings/ru.json @@ -583,7 +583,6 @@ "The remote side failed to pick up": "Удалённая сторона не смогла ответить", "This room has no local addresses": "Эта комната не имеет местного адреса", "This room is not recognised.": "Эта комната не опознана.", - "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": "Это экспериментальные функции, которые могут неожиданным образом вызывать ошибки", "This doesn't appear to be a valid email address": "Не похоже, что это правильный адрес электронной почты", "This is a preview of this room. Room interactions have been disabled": "Это просмотр данной комнаты. Взаимодействия с ней были отключены.", diff --git a/src/i18n/strings/th.json b/src/i18n/strings/th.json index db67539b38..5bafdc97cd 100644 --- a/src/i18n/strings/th.json +++ b/src/i18n/strings/th.json @@ -318,7 +318,6 @@ "The file '%(fileName)s' failed to upload": "การอัปโหลดไฟล์ '%(fileName)s' ล้มเหลว", "This Home Server does not support login using email address.": "เซิร์ฟเวอร์บ้านนี้ไม่รองรับการลงชื่อเข้าใช้ด้วยที่อยู่อีเมล", "There was a problem logging in.": "มีปัญหาในการลงชื่อเข้าใช้", - "This room is private or inaccessible to guests. You may be able to join if you register": "ห้องนี้เป็นส่วนตัวหรือไม่อนุญาตให้แขกเข้าถึง คุณอาจเข้าร่วมได้หากคุณลงทะเบียน", "this invitation?": "คำเชิญนี้?", "This is a preview of this room. Room interactions have been disabled": "นี่คือตัวอย่างของห้อง การตอบสนองภายในห้องถูกปิดใช้งาน", "This phone number is already in use": "หมายเลขโทรศัพท์นี้ถูกใช้งานแล้ว", From 0170a12ad6d558ab03b9c4a2b4e53c6f65317839 Mon Sep 17 00:00:00 2001 From: Amandine Date: Wed, 7 Jun 2017 17:08:15 +0000 Subject: [PATCH 0641/1016] Translated using Weblate (French) Currently translated at 97.0% (825 of 850 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/fr/ --- src/i18n/strings/fr.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index c4a606b9bc..c7bff8977b 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -830,5 +830,9 @@ "Hide removed messages": "Cacher les messages supprimés", "Add": "Ajouter", "%(count)s new messages.one": "%(count)s nouveau message", - "%(count)s new messages.other": "%(count)s nouveaux messages" + "%(count)s new messages.other": "%(count)s nouveaux messages", + "Disable markdown formatting": "Désactiver le formattage markdown", + "Error: Problem communicating with the given homeserver.": "Erreur: Problème de communication avec le homeserveur.", + "Failed to fetch avatar URL": "Échec lors de la récupération de l’URL de l’avatar", + "The phone number entered looks invalid": "Le numéro de téléphone entré semble être invalide" } From ca5215a28841a7123ade7ddc0e8015f2310248e1 Mon Sep 17 00:00:00 2001 From: Bamstam Date: Wed, 7 Jun 2017 17:34:00 +0000 Subject: [PATCH 0642/1016] Translated using Weblate (German) Currently translated at 100.0% (850 of 850 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/de/ --- src/i18n/strings/de_DE.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index 79603ad361..0d952ffcd9 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -114,7 +114,7 @@ "Failed to change password. Is your password correct?": "Passwortänderung fehlgeschlagen. Ist dein Passwort richtig?", "Failed to forget room": "Vergessen des Raums schlug fehl", "Failed to leave room": "Verlassen des Raums fehlgeschlagen", - "Failed to reject invitation": "Fehler beim Abweisen der Einladung", + "Failed to reject invitation": "Einladung konnte nicht abgelehnt werden", "Failed to set avatar.": "Fehler beim Setzen des Profilbilds.", "Failed to unban": "Entbannen fehlgeschlagen", "Failed to upload file": "Datei-Upload fehlgeschlagen", @@ -418,7 +418,7 @@ "Press": "Drücke", "tag as %(tagName)s": "als %(tagName)s taggen", "to browse the directory": "um das Raum-Verzeichnis zu durchsuchen", - "to demote": "um die Priorität herabzusetzen", + "to demote": "um das Berechtigungslevel herabzusetzen", "to favourite": "zum Favorisieren", "to make a room or": "um einen Raum zu erstellen, oder", "to restore": "zum wiederherstellen", @@ -618,7 +618,7 @@ "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 zugänglich. Du kannst jedoch eventuell beitreten, 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": "Der Versuch, einen spezifischen Punkt im Chatverlauf zu laden, ist fehlgeschlagen. Der Punkt konnte nicht gefunden werden", - "Turn Markdown off": "Markdown abschalten", + "Turn Markdown off": "Markdown deaktiveren", "Turn Markdown on": "Markdown einschalten", "Unable to load device list": "Geräteliste konnte nicht geladen werden", "Unknown command": "Unbekannter Befehl", @@ -786,7 +786,7 @@ "This image cannot be displayed.": "Dieses Bild kann nicht angezeigt werden.", "Error decrypting video": "Video-Entschlüsselung fehlgeschlagen", "Import room keys": "Importiere Raum-Schlüssel", - "File to import": "Datei zum Importieren", + "File to import": "Zu importierende Datei", "Failed to invite the following users to the %(roomName)s room:": "Das Einladen der folgenden Nutzer in den Raum \"%(roomName)s\" ist fehlgeschlagen:", "Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change.": "Bist du sicher, dass du dieses Ereignis entfernen (löschen) möchtest? Wenn du die Änderung eines Raum-Namens oder eines Raum-Themas löscht, kann dies dazu führen, dass die ursprüngliche Änderung rückgängig gemacht wird.", "This process allows you to export the keys for messages you have received in encrypted rooms to a local file. You will then be able to import the file into another Matrix client in the future, so that client will also be able to decrypt these messages.": "Dieser Prozess erlaubt es dir, die Schlüssel für in verschlüsselten Räumen empfangene Nachrichten in eine lokale Datei zu exportieren. In Zukunft wird es möglich sein, diese Datei in einen anderen Matrix-Client zu importieren, sodass dieser Client ebenfalls diese Nachrichten entschlüsseln kann.", @@ -828,7 +828,7 @@ "Invited": "Eingeladen", "Set a Display Name": "Setze einen Anzeigenamen", "for %(amount)ss": "für %(amount)ss", - "for %(amount)sm": "für %(amount)sm", + "for %(amount)sm": "seit %(amount)smin", "for %(amount)sh": "für %(amount)sh", "for %(amount)sd": "für %(amount)sd", "%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s hat das Raum-Bild entfernt.", @@ -906,7 +906,7 @@ "Uploading %(filename)s and %(count)s others.other": "%(filename)s und %(count)s weitere werden hochgeladen", "You must register to use this functionality": "Du musst dich registrieren um diese Funktionalität zu nutzen", "Resend all or cancel all now. You can also select individual messages to resend or cancel.": "Sende erneut oder breche alles ab. Du kannst auch auch individuelle Nachrichten erneut senden or abbrechen.", - "Create new room": "Erstelle neuen Raum", + "Create new room": "Neuen Raum erstellen", "Welcome page": "Willkommensseite", "Room directory": "Raum-Verzeichnis", "Start chat": "Starte Chat", From 8d7f40ed075a550ce289f4fadc333fe1a2c589bf Mon Sep 17 00:00:00 2001 From: Pitchaya Boonsarngsuk Date: Wed, 7 Jun 2017 17:33:48 +0000 Subject: [PATCH 0643/1016] Translated using Weblate (Thai) Currently translated at 40.8% (347 of 850 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/th/ --- src/i18n/strings/th.json | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/th.json b/src/i18n/strings/th.json index a05f5c8f68..2f64cdaf71 100644 --- a/src/i18n/strings/th.json +++ b/src/i18n/strings/th.json @@ -323,7 +323,7 @@ "This is a preview of this room. Room interactions have been disabled": "นี่คือตัวอย่างของห้อง การตอบสนองภายในห้องถูกปิดใช้งาน", "This phone number is already in use": "หมายเลขโทรศัพท์นี้ถูกใช้งานแล้ว", "This room's internal ID is": "ID ภายในของห้องนี้คือ", - "times": "เวลา", + "times": "ครั้ง", "%(oneUser)schanged their name": "%(oneUser)sเปลี่ยนชื่อของเขาแล้ว", "%(severalUsers)schanged their name %(repeats)s times": "%(severalUsers)sเปลี่ยนชื่อของพวกเขา %(repeats)s ครั้ง", "%(oneUser)schanged their name %(repeats)s times": "%(oneUser)sเปลี่ยนชื่อของเขา %(repeats)s ครั้ง", @@ -331,5 +331,20 @@ "Create new room": "สร้างห้องใหม่", "Room directory": "ไดเรกทอรีห้อง", "Start chat": "เริ่มแชท", - "Welcome page": "หน้าต้อนรับ" + "Welcome page": "หน้าต้อนรับ", + "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or enable unsafe scripts.": "ไม่สามารถเชื่อมต่อไปยังเซิร์ฟเวอร์บ้านผ่านทาง HTTP ได้เนื่องจาก URL ที่อยู่บนเบราว์เซอร์เป็น HTTPS กรุณาใช้ HTTPS หรือเปิดใช้งานสคริปต์ที่ไม่ปลอดภัย.", + "%(count)s new messages.one": "มี %(count)s ข้อความใหม่", + "%(count)s new messages.other": "มี %(count)s ข้อความใหม่", + "Disable inline URL previews by default": "ตั้งค่าเริ่มต้นให้ไม่แสดงตัวอย่าง URL ในแชท", + "Disable markdown formatting": "ปิดใช้งานการจัดรูปแบบ markdown", + "End-to-end encryption information": "ข้อมูลการเข้ารหัสจากปลายทางถึงปลายทาง", + "End-to-end encryption is in beta and may not be reliable": "การเข้ารหัสจากปลายทางถึงปลายทางยังอยู่ในเบต้า และอาจพึ่งพาไม่ได้", + "Error: Problem communicating with the given homeserver.": "ข้อผิดพลาด: มีปัญหาในการติดต่อกับเซิร์ฟเวอร์บ้านที่กำหนด", + "Export E2E room keys": "ส่งออกกุญแจถอดรหัส E2E", + "Failed to change power level": "การเปลี่ยนระดับอำนาจล้มเหลว", + "Import E2E room keys": "นำเข้ากุญแจถอดรหัส E2E", + "to favourite": "ไปยังรายการโปรด", + "to demote": "เพื่อลดขั้น", + "The default role for new room members is": "บทบาทเริ่มต้นของสมาชิกใหม่คือ", + "The phone number entered looks invalid": "ดูเหมือนว่าหมายเลขโทรศัพท์ที่กรอกรมาไม่ถูกต้อง" } From 5cf44454d2b463fe5543a439992a270398aec3f1 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Wed, 7 Jun 2017 18:41:18 +0100 Subject: [PATCH 0644/1016] fix more punctuation fails --- src/i18n/strings/th.json | 1 + src/i18n/strings/zh_Hans.json | 4 ++-- src/i18n/strings/zh_Hant.json | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/i18n/strings/th.json b/src/i18n/strings/th.json index 5bafdc97cd..f5564415fc 100644 --- a/src/i18n/strings/th.json +++ b/src/i18n/strings/th.json @@ -318,6 +318,7 @@ "The file '%(fileName)s' failed to upload": "การอัปโหลดไฟล์ '%(fileName)s' ล้มเหลว", "This Home Server does not support login using email address.": "เซิร์ฟเวอร์บ้านนี้ไม่รองรับการลงชื่อเข้าใช้ด้วยที่อยู่อีเมล", "There was a problem logging in.": "มีปัญหาในการลงชื่อเข้าใช้", + "This room is private or inaccessible to guests. You may be able to join if you register.": "ห้องนี้เป็นส่วนตัวหรือไม่อนุญาตให้แขกเข้าถึง คุณอาจเข้าร่วมได้หากคุณลงทะเบียน", "this invitation?": "คำเชิญนี้?", "This is a preview of this room. Room interactions have been disabled": "นี่คือตัวอย่างของห้อง การตอบสนองภายในห้องถูกปิดใช้งาน", "This phone number is already in use": "หมายเลขโทรศัพท์นี้ถูกใช้งานแล้ว", diff --git a/src/i18n/strings/zh_Hans.json b/src/i18n/strings/zh_Hans.json index 54a6886b35..e0db2b525b 100644 --- a/src/i18n/strings/zh_Hans.json +++ b/src/i18n/strings/zh_Hans.json @@ -76,7 +76,7 @@ "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s 从 %(fromPowerLevel)s 变为 %(toPowerLevel)s", "Guests can't set avatars. Please register.": "游客不能设置头像。请注册。.", "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.": "游客不能上传文件。请注册以上传文件", "Guests can't use labs features. Please register.": "游客不能使用实验性功能。请注册。.", "Guests cannot join this room even if explicitly invited.": "游客不能加入此聊天室,即使有人主动邀请。.", "had": "已经", @@ -138,7 +138,7 @@ "since the point in time of selecting this option": "从选择此选项起", "since they joined": "从他们加入时起", "since they were invited": "从他们被邀请时起", - "Some of your messages have not been sent": "部分消息发送失败", + "Some of your messages have not been sent.": "部分消息发送失败", "Someone": "某个用户", "Sorry, this homeserver is using a login which is not recognised ": "很抱歉,无法识别此主服务器使用的登录方式 ", "Start a chat": "创建聊天", diff --git a/src/i18n/strings/zh_Hant.json b/src/i18n/strings/zh_Hant.json index 250dc23ef3..17901789e9 100644 --- a/src/i18n/strings/zh_Hant.json +++ b/src/i18n/strings/zh_Hant.json @@ -194,7 +194,7 @@ "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s 從 %(fromPowerLevel)s 變為 %(toPowerLevel)s", "Guests can't set avatars. Please register.": "游客不能設置頭像。請注冊。.", "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.": "游客不能上傳文件。請注冊以上傳文件", "Guests can't use labs features. Please register.": "游客不能使用實驗性功能。請注冊。.", "Guests cannot join this room even if explicitly invited.": "游客不能加入此聊天室,即使有人主動邀請。.", "had": "已經", @@ -265,7 +265,7 @@ "since the point in time of selecting this option": "從選擇此選項起", "since they joined": "從他們加入時起", "since they were invited": "從他們被邀請時起", - "Some of your messages have not been sent": "部分消息發送失敗", + "Some of your messages have not been sent.": "部分消息發送失敗", "Someone": "某個用戶", "Sorry, this homeserver is using a login which is not recognised ": "很抱歉,無法識別此主伺服器使用的登錄方式 ", "Start a chat": "創建聊天", From ca1c0e42b0559b2095b2a99783a0f946f020dc89 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Wed, 7 Jun 2017 18:42:34 +0100 Subject: [PATCH 0645/1016] oops, merge correctly --- src/i18n/strings/th.json | 1 - 1 file changed, 1 deletion(-) diff --git a/src/i18n/strings/th.json b/src/i18n/strings/th.json index f5564415fc..5bafdc97cd 100644 --- a/src/i18n/strings/th.json +++ b/src/i18n/strings/th.json @@ -318,7 +318,6 @@ "The file '%(fileName)s' failed to upload": "การอัปโหลดไฟล์ '%(fileName)s' ล้มเหลว", "This Home Server does not support login using email address.": "เซิร์ฟเวอร์บ้านนี้ไม่รองรับการลงชื่อเข้าใช้ด้วยที่อยู่อีเมล", "There was a problem logging in.": "มีปัญหาในการลงชื่อเข้าใช้", - "This room is private or inaccessible to guests. You may be able to join if you register.": "ห้องนี้เป็นส่วนตัวหรือไม่อนุญาตให้แขกเข้าถึง คุณอาจเข้าร่วมได้หากคุณลงทะเบียน", "this invitation?": "คำเชิญนี้?", "This is a preview of this room. Room interactions have been disabled": "นี่คือตัวอย่างของห้อง การตอบสนองภายในห้องถูกปิดใช้งาน", "This phone number is already in use": "หมายเลขโทรศัพท์นี้ถูกใช้งานแล้ว", From ea02d8841c991e1b595a8e87f166170ddc61b2bb Mon Sep 17 00:00:00 2001 From: RiotTranslate Date: Wed, 7 Jun 2017 19:48:48 +0200 Subject: [PATCH 0646/1016] Update from Weblate. (#1052) * Added translation using Weblate (Thai) * Translated using Weblate (Thai) Currently translated at 2.6% (22 of 827 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/th/ * Translated using Weblate (Thai) Currently translated at 8.9% (74 of 827 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/th/ * Translated using Weblate (German) Currently translated at 99.8% (826 of 827 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/de/ * Translated using Weblate (Swedish) Currently translated at 35.4% (293 of 827 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/sv/ * Translated using Weblate (Thai) Currently translated at 9.7% (81 of 827 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/th/ * Translated using Weblate (German) Currently translated at 100.0% (827 of 827 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/de/ * Translated using Weblate (Russian) Currently translated at 76.9% (636 of 827 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/ru/ * Translated using Weblate (Hungarian) Currently translated at 0.1% (1 of 827 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/hu/ * Translated using Weblate (Thai) Currently translated at 39.2% (325 of 827 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/th/ * Translated using Weblate (German) Currently translated at 99.8% (827 of 828 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/de/ * Translated using Weblate (German) Currently translated at 99.8% (827 of 828 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/de/ * Translated using Weblate (Thai) Currently translated at 39.7% (329 of 828 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/th/ * Translated using Weblate (German) Currently translated at 99.8% (828 of 828 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/de/ * Translated using Weblate (Hungarian) Currently translated at 3.1% (26 of 828 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/hu/ * Translated using Weblate (Russian) Currently translated at 77.4% (641 of 828 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/ru/ * Translated using Weblate (Thai) Currently translated at 39.7% (329 of 828 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/th/ * Translated using Weblate (Russian) Currently translated at 100.0% (828 of 828 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/ru/ * Translated using Weblate (German) Currently translated at 100.0% (828 of 828 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/de/ * Translated using Weblate (Russian) Currently translated at 100.0% (828 of 828 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/ru/ * Translated using Weblate (German) Currently translated at 100.0% (850 of 850 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/de/ * Translated using Weblate (French) Currently translated at 96.5% (821 of 850 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/fr/ * Translated using Weblate (Thai) Currently translated at 39.0% (332 of 850 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/th/ --- src/i18n/strings/de_DE.json | 109 +++++++++++------ src/i18n/strings/fr.json | 5 +- src/i18n/strings/hu.json | 27 ++++- src/i18n/strings/ru.json | 228 +++++++++++++++++++++++++++++++++--- src/i18n/strings/th.json | 16 ++- 5 files changed, 322 insertions(+), 63 deletions(-) diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index 6d3abef3e4..f7b3d16ebc 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -6,7 +6,7 @@ "People": "Direkt-Chats", "Rooms": "Räume", "Low priority": "Niedrige Priorität", - "Historical": "Historisch", + "Historical": "Archiv", "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.", @@ -42,7 +42,7 @@ "Commands": "Kommandos", "Emoji": "Emoji", "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", + "Login as guest": "Als Gast anmelden", "Return to app": "Zurück zur Anwendung", "Sign in": "Anmelden", "Create a new account": "Erstelle einen neuen Benutzer", @@ -75,7 +75,7 @@ "changed the topic to": "änderte das Thema zu", "Changes to who can read history will only apply to future messages in this room": "Änderungen, die bestimmen, wer den Chatverlauf lesen kann, gelten nur für zukünftige Nachrichten in diesem Raum", "Clear Cache and Reload": "Cache leeren und neu laden", - "Click here": "Klicke hier", + "Click here": "Hier klicken,", "Confirm your new password": "Neues Passwort bestätigen", "Continue": "Fortfahren", "Create an account": "Erstelle einen Account", @@ -96,7 +96,7 @@ "End-to-end encryption is in beta and may not be reliable": "Die Ende-zu-Ende-Verschlüsselung befindet sich aktuell im Beta-Stadium und ist eventuell noch nicht hundertprozentig zuverlässig", "Failed to send email": "Fehler beim Senden der E-Mail", "Account": "Konto", - "Add phone number": "Füge Telefonnummer hinzu", + "Add phone number": "Telefonnummer hinzufügen", "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 erst Benachrichtigungen auf anderen Geräten empfangen können, wenn du dich dort erneut anmeldest", "all room members": "Alle Raum-Mitglieder", @@ -111,7 +111,7 @@ "Default": "Standard", "demote": "Berechtigungslevel herabstufen", "Export E2E room keys": "E2E-Raum-Schlüssel exportieren", - "Failed to change password. Is your password correct?": "Passwort-Änderung schlug fehl. Ist dein Passwort korrekt?", + "Failed to change password. Is your password correct?": "Passwortänderung fehlgeschlagen. Ist dein Passwort richtig?", "Failed to forget room": "Vergessen des Raums schlug fehl", "Failed to leave room": "Verlassen des Raums fehlgeschlagen", "Failed to reject invitation": "Fehler beim Abweisen der Einladung", @@ -140,12 +140,12 @@ "is a": "ist ein", "is trusted": "wird vertraut", "Sign in with": "Ich möchte mich anmelden mit", - "joined and left": "trat bei und ging", - "joined": "trat bei", + "joined and left": "hat den Raum betreten und wieder verlassen", + "joined": "hat den Raum betreten", "joined the room": "trat dem Raum bei", "Leave room": "Verlasse Raum", "left and rejoined": "ging(en) und trat(en) erneut bei", - "left": "ging", + "left": "hat den Raum verlassen", "left the room": "verließ den Raum", "Logged in as": "Angemeldet als", "Logout": "Abmelden", @@ -204,7 +204,7 @@ "Signed Out": "Abgemeldet", "Sign out": "Abmelden", "since the point in time of selecting this option": "ab dem Zeitpunkt, an dem diese Option gewählt wird", - "since they joined": "seitdem sie beitraten", + "since they joined": "ab dem Zeitpunkt, an dem sie beigetreten sind", "since they were invited": "seitdem sie eingeladen wurden", "Someone": "Jemand", "Start a chat": "Starte einen Chat", @@ -336,8 +336,8 @@ "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 scheint keine gültige E-Mail-Adresse zu sein.", "This doesn't look like a valid phone number.": "Dies scheint keine gültige Telefonnummer zu sein.", - "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.", + "User names may only contain letters, numbers, dots, hyphens and underscores.": "Benutzernamen dürfen nur Buchstaben, Nummern, Punkte, Binde- und Unterstriche enthalten.", + "An unknown error occurred.": "Ein unbekannter Fehler ist aufgetreten.", "I already have an account": "Ich habe bereits einen Account", "An error occurred: %(error_string)s": "Ein Fehler trat auf: %(error_string)s", "Topic": "Thema", @@ -352,7 +352,7 @@ "%(names)s and %(count)s others are typing": "%(names)s und %(count)s weitere Personen schreiben", "%(senderName)s answered the call.": "%(senderName)s hat den Anruf angenommen.", "%(senderName)s banned %(targetName)s.": "%(senderName)s hat %(targetName)s aus dem Raum verbannt.", - "%(senderName)s changed their display name from %(oldDisplayName)s to %(displayName)s.": "%(senderName)s hat den Anzeigenamen von %(oldDisplayName)s auf %(displayName)s geändert.", + "%(senderName)s changed their display name from %(oldDisplayName)s to %(displayName)s.": "%(senderName)s hat den Anzeigenamen von \"%(oldDisplayName)s\" auf \"%(displayName)s\" geändert.", "%(senderName)s changed their profile picture.": "%(senderName)s hat das Profilbild geändert.", "%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s hat das Berechtigungslevel von %(powerLevelDiffText)s geändert.", "%(senderDisplayName)s changed the room name to %(roomName)s.": "%(senderDisplayName)s änderte den Raumnamen zu %(roomName)s.", @@ -364,7 +364,7 @@ "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s von %(fromPowerLevel)s zu %(toPowerLevel)s", "%(senderName)s invited %(targetName)s.": "%(senderName)s hat %(targetName)s eingeladen.", "%(displayName)s is typing": "%(displayName)s schreibt", - "%(targetName)s joined the room.": "%(targetName)s trat dem Raum bei.", + "%(targetName)s joined the room.": "%(targetName)s hat den Raum betreten.", "%(senderName)s kicked %(targetName)s.": "%(senderName)s kickte %(targetName)s.", "%(targetName)s left the room.": "%(targetName)s hat den Raum verlassen.", "%(senderName)s made future room history visible to": "%(senderName)s machte die zukünftige Raumhistorie sichtbar für", @@ -378,12 +378,12 @@ "Reason": "Grund", "%(targetName)s rejected the invitation.": "%(targetName)s hat die Einladung abgelehnt.", "%(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 removed their profile picture.": "%(senderName)s hat das Profilbild gelöscht.", "%(senderName)s requested a VoIP conference.": "%(senderName)s möchte eine VoIP-Konferenz beginnen.", "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 hat %(targetDisplayName)s in diesen Raum eingeladen.", - "%(senderName)s set a profile picture.": "%(senderName)s setzte ein Profilbild.", + "%(senderName)s set a profile picture.": "%(senderName)s hat ein Profilbild gesetzt.", "%(senderName)s set their display name to %(displayName)s.": "%(senderName)s hat den Anzeigenamen geändert in %(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", @@ -398,7 +398,7 @@ "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.", + "Connectivity to the server has been lost.": "Verbindung zum Server wurde unterbrochen.", "Sent messages will be stored until your connection has returned.": "Gesendete Nachrichten werden gespeichert, bis die Internetverbindung wiederhergestellt wurde.", "Auto-complete": "Autovervollständigung", "Resend all": "Alle erneut senden", @@ -427,7 +427,7 @@ "You're not in any rooms yet! Press": "Du bist noch keinem Raum beigetreten! Drücke", "click to reveal": "Klicke zum anzeigen", "To remove other users' messages": "Um Nachrichten anderer Nutzer zu verbergen", - "You are trying to access %(roomName)s": "Du versuchst auf %(roomName)s zuzugreifen", + "You are trying to access %(roomName)s": "Du versuchst, auf den Raum \"%(roomName)s\" zuzugreifen", "af": "Afrikaans", "ar-ae": "Arabisch (VAE)", "ar-bh": "Arabisch (Bahrain)", @@ -558,7 +558,7 @@ "Are you sure?": "Bist du sicher?", "Attachment": "Anhang", "Ban": "Verbannen", - "Can't connect to homeserver - please check your connectivity and ensure your homeserver's SSL certificate is trusted.": "Kann nicht zum Heimserver verbinden - bitte checke eine Verbindung und stelle sicher, dass dem SSL-Zertifikat deines Heimservers vertraut wird.", + "Can't connect to homeserver - please check your connectivity and ensure your homeserver's SSL certificate is trusted.": "Verbindungsaufbau zum Heimserver nicht möglich - bitte Internetverbindung überprüfen und sicherstellen, ob das SSL-Zertifikat des Heimservers vertrauenswürdig ist.", "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or enable unsafe scripts.": "Kann nicht zum Heimserver via HTTP verbinden, wenn eine HTTPS-Url in deiner Adresszeile steht. Nutzer HTTPS oder aktiviere unsichere Skripte.", "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", @@ -590,7 +590,7 @@ "'%(alias)s' is not a valid format for an alias": "'%(alias)s' hat kein valides Aliasformat", "Join Room": "Dem Raum beitreten", "Kick": "Kicke", - "Level": "Level", + "Level": "Berechtigungslevel", "Local addresses for this room:": "Lokale Adressen dieses Raumes:", "Markdown is disabled": "Markdown ist deaktiviert", "Markdown is enabled": "Markdown ist aktiviert", @@ -652,25 +652,25 @@ "%(items)s and one other": "%(items)s und ein(e) weitere(r)", "%(items)s and %(lastItem)s": "%(items)s und %(lastItem)s", "%(severalUsers)sjoined %(repeats)s times": "%(severalUsers)ssind dem Raum %(repeats)s mal beigetreten", - "%(oneUser)sjoined %(repeats)s times": "%(oneUser)strat %(repeats)s mal bei", - "%(severalUsers)sjoined": "%(severalUsers)straten bei", - "%(oneUser)sjoined": "%(oneUser)strat bei", + "%(oneUser)sjoined %(repeats)s times": "%(oneUser)shat den Raum %(repeats)s mal betreten", + "%(severalUsers)sjoined": "%(severalUsers)shaben den Raum betreten", + "%(oneUser)sjoined": "%(oneUser)shat den Raum betreten", "%(severalUsers)sleft %(repeats)s times": "%(severalUsers)sverließen %(repeats)s mal den Raum", "%(oneUser)sleft %(repeats)s times": "%(oneUser)sging %(repeats)s mal", "%(severalUsers)sleft": "%(severalUsers)shaben den Raum verlassen", "%(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)sjoined and left %(repeats)s times": "%(severalUsers)shaben den Raum %(repeats)s mal betreten und wieder verlassen", + "%(oneUser)sjoined and left %(repeats)s times": "%(oneUser)shat den Raum %(repeats)s mal betreten und wieder verlassen", + "%(severalUsers)sjoined and left": "%(severalUsers)shaben den Raum betreten und wieder verlassen", + "%(oneUser)sjoined and left": "%(oneUser)shat den Raum betreten und wieder verlassen", "%(severalUsers)sleft and rejoined %(repeats)s times": "%(severalUsers)shaben den Raum verlassen und %(repeats)s mal neu betreten", - "%(oneUser)sleft and rejoined %(repeats)s times": "%(oneUser)sging und trat %(repeats)s mal erneut bei", - "%(severalUsers)sleft and rejoined": "%(severalUsers)s gingen und traten erneut bei", + "%(oneUser)sleft and rejoined %(repeats)s times": "%(oneUser)shat den Raum %(repeats)s mal verlassen und wieder neu betreten", + "%(severalUsers)sleft and rejoined": "%(severalUsers)shaben den Raum verlassen und wieder neu betreten", "%(oneUser)sleft left and rejoined": "%(oneUser)sging und trat erneut bei", - "%(severalUsers)srejected their invitations %(repeats)s times": "%(severalUsers)s lehnten %(repeats)s mal ihre Einladung ab", + "%(severalUsers)srejected their invitations %(repeats)s times": "%(severalUsers)shaben ihre Einladung %(repeats)s mal abgelehnt", "%(oneUser)srejected their invitation %(repeats)s times": "%(oneUser)shat die Einladung %(repeats)s mal abgelehnt", - "%(severalUsers)srejected their invitations": "%(severalUsers)slehnten ihre Einladung ab", - "%(oneUser)srejected their invitation": "%(oneUser)slehnte seine/ihre Einladung ab", + "%(severalUsers)srejected their invitations": "%(severalUsers)shaben ihre Einladung abgelehnt", + "%(oneUser)srejected their invitation": "%(oneUser)shat die Einladung abgelehnt", "%(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", @@ -687,16 +687,16 @@ "were kicked %(repeats)s times": "wurden %(repeats)s mal gekickt", "was kicked %(repeats)s times": "wurde %(repeats)s mal gekickt", "were kicked": "wurden aus dem Raum entfernt", - "%(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 %(repeats)s times": "%(severalUsers)shaben ihren Namen %(repeats)s mal geändert", + "%(oneUser)schanged their name %(repeats)s times": "%(oneUser)shat den Namen %(repeats)s mal geändert", "%(severalUsers)schanged their name": "%(severalUsers)shaben ihre Namen geändert", - "%(oneUser)schanged their name": "%(oneUser)sänderte seinen/ihren Namen", + "%(oneUser)schanged their name": "%(oneUser)shat den Namen geändert", "%(severalUsers)schanged their avatar %(repeats)s times": "%(severalUsers)shaben %(repeats)s mal ihr Profilbild geändert", "%(oneUser)schanged their avatar %(repeats)s times": "%(oneUser)shat %(repeats)s mal das Profilbild geändert", "%(severalUsers)schanged their avatar": "%(severalUsers)shaben ihr Profilbild geändert", "%(oneUser)schanged their avatar": "%(oneUser)shat das Profilbild geändert", "%(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)sverließ den Raum und trat erneut bei", + "%(oneUser)sleft and rejoined": "%(oneUser)shat den Raum verlassen und wieder neu betreten", "A registered account is required for this action": "Für diese Aktion ist ein registrierter Account notwendig", "Access Token:": "Zugangs-Token:", "Always show message timestamps": "Nachrichten-Zeitstempel immer anzeigen", @@ -727,7 +727,7 @@ "Invalid file%(extra)s": "Ungültige Datei%(extra)s", "Remove %(threePid)s?": "Entferne %(threePid)s?", "Please select the destination room for this message": "Bitte den Raum auswählen, an den diese Nachricht gesendet werden soll", - "%(senderDisplayName)s removed the room name.": "%(senderDisplayName)s löschte den Raumnamen.", + "%(senderDisplayName)s removed the room name.": "%(senderDisplayName)s hat den Raum-Namen gelöscht.", "Passphrases must match": "Passphrase muss übereinstimmen", "Passphrase must not be empty": "Passphrase darf nicht leer sein", "Export room keys": "Raum-Schlüssel exportieren", @@ -861,8 +861,8 @@ "device id: ": "Geräte-ID: ", "Device key:": "Geräte-Schlüssel:", "Email address (optional)": "E-Mail-Adresse (optional)", - "List this room in %(domain)s's room directory?": "Liste diesen Raum in %(domain)s's Raumverzeichnis?", - "Mobile phone number (optional)": "Handynummer (optional)", + "List this room in %(domain)s's room directory?": "Diesen Raum zum Raum-Verzeichnis von %(domain)s hinzufügen?", + "Mobile phone number (optional)": "Mobilfunknummer (optional)", "Password:": "Passwort:", "Register": "Registrieren", "Save": "Speichern", @@ -888,5 +888,36 @@ "$senderDisplayName changed the room avatar to ": "$senderDisplayName hat das Raum-Bild geändert zu ", "%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s hat das Raum-Bild für %(roomName)s geändert", "Hide removed messages": "Gelöschte Nachrichten verbergen", - "Start new chat": "Neuen Chat starten" + "Start new chat": "Neuen Chat starten", + "Disable markdown formatting": "Deaktiviere Markdown-Formatierung", + "Add": "Hinzufügen", + "%(count)s new messages.one": "%(count)s neue Nachricht", + "%(count)s new messages.other": "%(count)s neue Nachrichten", + "Error: Problem communicating with the given homeserver.": "Fehler: Problem beim kommunizieren mit dem angegebenen Heimserver.", + "Failed to fetch avatar URL": "Fehler beim holen der Avatar-URL", + "Some of your messages have not been sent.": "Einige deiner Nachrichten wurden nicht gesendet.", + "The phone number entered looks invalid": "Die Telefonnummer, die eingegeben wurde, sieht ungültig aus", + "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 betretbar. Du kannst evtl. beitreten 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.": "Es wurde versucht einen spezifischen Punkt in der Chat-Historie zu laden, aber du hast keine Berechtigung diese Nachricht zu sehen.", + "Tried to load a specific point in this room's timeline, but was unable to find it.": "Es wurde versucht einen spezifischen Punkt in der Chat-Historie zu laden, aber er konnte nicht gefunden werden.", + "Uploading %(filename)s and %(count)s others.zero": "%(filename)s wird hochgeladen", + "Uploading %(filename)s and %(count)s others.one": "%(filename)s und %(count)s weitere werden hochgeladen", + "Uploading %(filename)s and %(count)s others.other": "%(filename)s und %(count)s weitere werden hochgeladen", + "You must register to use this functionality": "Du musst dich registrieren um diese Funktionalität zu nutzen", + "Resend all or cancel all now. You can also select individual messages to resend or cancel.": "Sende erneut oder breche alles ab. Du kannst auch auch individuelle Nachrichten erneut senden or abbrechen.", + "Create new room": "Erstelle neuen Raum", + "Welcome page": "Willkommensseite", + "Room directory": "Raum-Verzeichnis", + "Start chat": "Starte Chat", + "New Password": "Neues Passwort", + "Start chatting": "Starte plaudern", + "Start Chatting": "Starte Gespräche", + "Click on the button below to start chatting!": "Klicke den Button unten um das Plaudern zu beginnen!", + "Create a new chat or reuse an existing one": "Erstelle einen neuen Chat oder nutze einen existierenden", + "You already have existing direct chats with this user:": "Du hast bereits direkte Chats mit diesem Nutzer:", + "Username available": "Nutzername verfügbar", + "Username not available": "Nutzername nicht verfügbar", + "Something went wrong!": "Etwas ging schief!", + "This will be your account name on the homeserver, or you can pick a different server.": "Dies wird dein Konto-Name auf dem Heimserver, oder du kannst einen anderen Server auswählen.", + "If you already have a Matrix account you can log in instead.": "Wenn du bereits ein Matrix-Konto hast, kannst du ansonsten auch anmelden." } diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index bf05173356..ecbb20fbb7 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -826,5 +826,8 @@ "You have disabled URL previews by default.": "Vous avez désactivé les aperçus d’URL par défaut.", "You have enabled URL previews by default.": "Vous avez activé les aperçus d’URL par défaut.", "You have entered an invalid contact. Try using their Matrix ID or email address.": "Vous avez entré un contact invalide. Essayez d’utiliser leur identifiant Matrix ou leur adresse email.", - "Hide removed messages": "Cacher les messages supprimés" + "Hide removed messages": "Cacher les messages supprimés", + "Add": "Ajouter", + "%(count)s new messages.one": "%(count)s nouveau message", + "%(count)s new messages.other": "%(count)s nouveaux messages" } diff --git a/src/i18n/strings/hu.json b/src/i18n/strings/hu.json index 5430737f15..748810f76e 100644 --- a/src/i18n/strings/hu.json +++ b/src/i18n/strings/hu.json @@ -1,3 +1,28 @@ { - "Cancel": "Mégse" + "Cancel": "Mégse", + "Search": "Keresés", + "OK": "Rendben", + "Custom Server Options": "Egyedi szerver beállítások", + "Direct Chat": "Közvetlen csevegés", + "Dismiss": "Eltűntet", + "Drop here %(toAction)s": "%(toAction)s -t húzd ide", + "Error": "Hiba", + "Failed to forget room %(errCode)s": "Nem lehet eltávolítani a szobát: %(errCode)s", + "Failed to join the room": "Nem lehet csatlakozni a szobához", + "Favourite": "Kedvenc", + "Mute": "Elnémít", + "Notifications": "Értesítések", + "Operation failed": "Művelet sikertelen", + "Please Register": "Regisztrálj", + "powered by Matrix": "Matrixon alapul", + "Remove": "Töröl", + "Settings": "Beállítások", + "unknown error code": "ismeretlen hiba kód", + "Sunday": "Vasárnap", + "Monday": "Hétfő", + "Tuesday": "Kedd", + "Wednesday": "Szerda", + "Thursday": "Csütörtök", + "Friday": "Péntek", + "Saturday": "Szombat" } diff --git a/src/i18n/strings/ru.json b/src/i18n/strings/ru.json index 58f8e0291c..85b7a6a7f5 100644 --- a/src/i18n/strings/ru.json +++ b/src/i18n/strings/ru.json @@ -84,11 +84,11 @@ "Failed to upload file": "Не удалось закачать файл", "Favourite": "Избранное", "favourite": "фаворит", - "Favourites": "Фавориты", + "Favourites": "Избранное", "Filter room members": "Фильтр участников комнаты", "Forget room": "Забыть комнату", "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?": "Нашли ошибку?", "had": "имеет", "Hangup": "Отключение", @@ -153,7 +153,7 @@ "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 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", @@ -228,7 +228,7 @@ "%(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 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.", @@ -254,7 +254,7 @@ "%(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 история сделаной будущей комнаты, видимая для", + "%(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": "Комната должна быть посищена", @@ -356,8 +356,8 @@ "Saturday": "Суббота", "Sunday": "Воскресенье", "%(weekDayName)s %(time)s": "%(weekDayName)s %(time)s", - "Upload an avatar:": "Загрузить аватар", - "You need to be logged in.": "Вы должны быть зарегистрированы", + "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.": "Вы уже находитесь в разговоре.", @@ -377,7 +377,7 @@ "Oct": "Окт.", "Nov": "Ноя.", "Dec": "Дек.", - "%(weekDayName)s, %(monthName)s %(day)s %(time)s": "%(weekDayName)s, %(monthName)s %(day)s %(time)s", + "%(weekDayName)s, %(monthName)s %(day)s %(time)s": "%(weekDayName)s, %(day)s %(monthName)s %(time)s", "Mon": "Пн", "Sun": "Вс", "Tue": "Вт", @@ -388,8 +388,8 @@ "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.": "Для его использования, просто подождите результатов автозаполнения для загрузки на вкладке и через них.", + "to tag direct chat": "отметить прямой чат", + "To use it, just wait for autocomplete results to load and tab through them.": "Для его использования просто подождите загрузки результатов автозаполнения и нажимайте Tab для навигации.", "%(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.", @@ -478,7 +478,7 @@ "Always show message timestamps": "Всегда показывать время сообщения", "Authentication": "Авторизация", "olm version:": "версия olm:", - "%(items)s and %(remaining)s others": "%(items)s и %(remaining)s другие", + "%(items)s and %(remaining)s others": "%(items)s и другие %(remaining)s", "%(items)s and one other": "%(items)s и ещё один", "%(items)s and %(lastItem)s": "%(items)s и %(lastItem)s", "and one other...": "и ещё один...", @@ -491,7 +491,7 @@ "Current password": "Текущий пароль", "Email": "Электронная почта", "Failed to kick": "Не удалось выгнать", - "Failed to load timeline position": "Не удалось узнать место во времени", + "Failed to load timeline position": "Не удалось загрузить позицию таймлайна", "Failed to mute user": "Не удалось заглушить", "Failed to reject invite": "Не удалось отклонить приглашение", "Failed to save settings": "Не удалось сохранить настройки", @@ -519,7 +519,7 @@ "New passwords don't match": "Пароли не совпадают", "not set": "не установлено", "not specified": "не указано", - "No devices with registered encryption keys": "Нет устройств с записанными ключами шифрования", + "No devices with registered encryption keys": "Нет устройств с зарегистрированными ключами шифрования", "No more results": "Нет больше результатов", "No results": "Нет результатов", "OK": "ОК", @@ -535,7 +535,7 @@ "rejected": "отклонено", "%(targetName)s rejected the invitation.": "%(targetName)s отклонил приглашение.", "Reject invitation": "Отклонить приглашение", - "Remove Contact Information?": "Убрать контактную информацию?", + "Remove Contact Information?": "Удалить контактную информацию?", "%(senderName)s removed their display name (%(oldDisplayName)s).": "%(senderName)s убрал своё отображаемое имя (%(oldDisplayName)s).", "%(senderName)s removed their profile picture.": "%(senderName)s убрал своё изображение.", "%(senderName)s requested a VoIP conference.": "%(senderName)s запросил голосовую конференц-связь.", @@ -585,7 +585,7 @@ "This room is not recognised.": "Эта комната не опознана.", "These are experimental features that may break in unexpected ways": "Это экспериментальные функции, которые могут неожиданным образом вызывать ошибки", "This doesn't appear to be a valid email address": "Не похоже, что это правильный адрес электронной почты", - "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 room's internal ID is": "Внутренний ID этой комнаты", "times": "раз", @@ -598,7 +598,7 @@ "Unknown room %(roomId)s": "Неизвестная комната %(roomId)s", "You have been invited to join this room by %(inviterName)s": "Вы были приглашены войти в эту комнату от %(inviterName)s", "You seem to be uploading files, are you sure you want to quit?": "Похоже вы передаёте файлы, вы уверены, что хотите выйти?", - "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)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, %(day)s %(monthName)s %(fullYear)s %(time)s", "Make Moderator": "Сделать модератором", "Room": "Комната", "Cancel": "Отмена", @@ -606,7 +606,7 @@ "italic": "наклонный", "strike": "перечёркнутый", "underline": "подчёркнутый", - "code": "текст", + "code": "код", "quote": "цитата", "bullet": "пункт", "numbullet": "нумерация", @@ -672,5 +672,197 @@ "Logged in as:": "Зарегестрирован как:", "Default Device": "Стандартное устройство", "No Webcams detected": "Веб-камера не обнаружена", - "VoIP": "VoIP" + "VoIP": "VoIP", + "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.": "Для обеспечения безопасности выход из системы удалит все ключи шифроования из этого браузера. Если вы хотите иметь возможность расшифровать переписку в будущем - вы должны экспортирвать ключи вручную.", + "Guest access is disabled on this Home Server.": "Гостевой доступ отключен на этом сервере.", + "Guests can't set avatars. Please register.": "Гости не могут устанавливать аватар. Пожалуйста, зарегистрируйтесь.", + "Guests can't use labs features. Please register.": "Гости не могут использовать экспериментальные возможности. Пожалуйста, зарегистрируйтесь.", + "Guests cannot join this room even if explicitly invited.": "Гости не могут заходить в эту комнату если не были приглашены.", + "Missing Media Permissions, click here to request.": "Отсутствуют разрешения, нажмите для запроса.", + "No media permissions": "Нет медиа разрешений", + "You may need to manually permit Riot to access your microphone/webcam": "Вам необходимо предоставить Riot доступ к микрофону или веб-камере вручную", + "Anyone": "Все", + "Are you sure you want to leave the room '%(roomName)s'?": "Вы уверены, что хотите покинуть '%(roomName)s'?", + "%(senderDisplayName)s removed the room name.": "%(senderDisplayName)s удалил имя комнаты.", + "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.": "Смена пароля также сбросит все ключи шифрования на всех устройствах, сделав зашифрованную историю недоступной, если только вы сначала не экспортируете ключи шифрования и не импортируете их потом. В будущем это будет исправлено.", + "Custom level": "Пользовательский уровень", + "(default: %(userName)s)": "(по-умолчанию: %(userName)s)", + "Device already verified!": "Устройство уже верифицировано!", + "Device ID:": "ID устройства:", + "device id: ": "id устройства: ", + "Device key:": "Ключ устройства:", + "disabled": "отключено", + "Disable markdown formatting": "Отключить форматирование Markdown", + "Email address": "Адрес email", + "Email address (optional)": "Адрем email (не обязательно)", + "enabled": "включено", + "Error decrypting attachment": "Ошибка расшифровки файла", + "Export": "Экспорт", + "Failed to register as guest:": "Ошибка регистрации как гостя:", + "Failed to set avatar.": "Не удалось установить аватар.", + "Import": "Импорт", + "Incorrect username and/or password.": "Неверное имя пользователя и/или пароль.", + "Invalid file%(extra)s": "Неправильный файл%(extra)s", + "Invited": "Приглашен", + "Jump to first unread message.": "Перейти к первому непрочитанному сообщению.", + "List this room in %(domain)s's room directory?": "Показывать эту комнату в списке комнат %(domain)s?", + "Message not sent due to unknown devices being present": "Сообщение не было отправлено из-за присутствия неизвестного устройства", + "Mobile phone number (optional)": "Номер мобильного телефона (не обязательно)", + "Once you've followed the link it contains, click below": "Как только вы пройдете по ссылке, нажмите на кнопку ниже", + "Password:": "Пароль:", + "Privacy warning": "Предупреждение приватности", + "Privileged Users": "Привилегированные пользователи", + "Revoke Moderator": "Снять модераторские права", + "Refer a friend to Riot:": "Расскажите другу о Riot:", + "Register": "Регистрация", + "Remote addresses for this room:": "Удаленные адреса для этой комнаты:", + "Remove %(threePid)s?": "Удалить %(threePid)s?", + "Results from DuckDuckGo": "Результаты от DuckDuckGo", + "Save": "Сохранить", + "Searches DuckDuckGo for results": "Ищет результаты через DuckDuckGo", + "Server error": "Ошибка сервера", + "Server may be unavailable or overloaded": "Сервер может быть недоступен или перегружен", + "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 you hit a bug.": "Сервер может быть недоступен, перегружен или вы нашли баг.", + "Server unavailable, overloaded, or something else went wrong.": "Сервер может быть недоступен, перегружен или произошло что-то страшное.", + "Session ID": "ID сессии", + "%(senderName)s set a profile picture.": "%(senderName)s установил картинку профиля.", + "%(senderName)s set their display name to %(displayName)s.": "%(senderName)s установил отображаемое имя %(displayName)s.", + "Setting a user name will create a fresh account": "Установка имени пользователя создаст новую учетную запись", + "Signed Out": "Вышли", + "Sorry, this homeserver is using a login which is not recognised ": "Извините, этот Home Server использует логин, который не удалось распознать ", + "Tagged as: ": "Теги: ", + "The signing key you provided matches the signing key you received from %(userId)s's device %(deviceId)s. Device marked as verified.": "Ключ, предоставленный вами, совпадает с ключем, полученным от устройства %(userId)s с ID %(deviceId)s. Устройство помечено как верифицированное.", + "%(actionVerb)s this person?": "%(actionVerb)s этого пользователя?", + "The file '%(fileName)s' exceeds this home server's size limit for uploads": "Файл '%(fileName)s' превышает ограничение размера загрузок на этом Home Server'е", + "This Home Server does not support login using email address.": "Этот Home Server не поддерживает вход по адресу email.", + "There was a problem logging in.": "Возникла проблема входа в учетную запись.", + "The visibility of existing history will be unchanged": "Видимость текущей истории не будет изменена", + "this invitation?": "это приглашение?", + "This room is not accessible by remote Matrix servers": "Это комната закрыта для других серверов Matrix", + "To ban users": "Забанить пользователей", + "to browse the directory": "просматривать директорию", + "To configure the room": "Конфигурировать комнату", + "To invite users into the room": "Приглашать пользователей в комнату", + "to join the discussion": "присоединиться к дискуссии", + "To kick users": "Выгонять пользователей", + "To link to a room it must have": "Для создания ссылки на комнату она должна иметь", + "to make a room or": "создать комнату или", + "To remove other users' messages": "Удалять сообщения других пользователей", + "To reset your password, enter the email address linked to your account": "Чтобы сбросить ваш пароль введите адрес email, который используется аккаунтом", + "to tag as %(tagName)s": "отметить как %(tagName)s", + "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": "Вы попытались загрузить указанное сообщение в комнате, однако сервер не смог его найти", + "Unable to load device list": "Невозможно загрузить список устройств", + "Unknown (user, device) pair:": "Неизвестная пара пользователь-устройство:", + "Unmute": "Разглушить", + "Unrecognised command:": "Неизвестная команда:", + "Unrecognised room alias:": "Неизвестный псевдоним комнаты:", + "Verified key": "Верифицированный ключ", + "WARNING: Device already verified, but keys do NOT MATCH!": "ВНИМАНИЕ: устройство уже было верифицировано, однако ключи НЕ СОВПАДАЮТ!", + "WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and device %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!": "ВНИМАНИЕ: ОШИБКА ВЕРИФИКАЦИИ КЛЮЧЕЙ! Ключ для подписки устройства %(deviceId)s пользователя %(userId)s: \"%(fprint)s\", однако он не совпадает с предоставленным ключем \"%(fingerprint)s\". Это может означать перехват вашего канала коммуникации!", + "You have disabled URL previews by default.": "Предпросмотр ссылок отключен по-умолчанию.", + "You have enabled URL previews by default.": "Предпросмотр ссылок включен по-умолчанию.", + "You have entered an invalid contact. Try using their Matrix ID or email address.": "Вы ввели неправильный адрес. Попробуйте использовать Matrix ID или адрес email.", + "You need to enter a user name.": "Необходимо ввести имя пользователя.", + "You seem to be in a call, are you sure you want to quit?": "Вы учавствуете в звонке, вы уверены, что хотите выйти?", + "You will not be able to undo this change as you are promoting the user to have the same power level as yourself": "Вы не сможете отменить это действие так как даете пользователю такой же уровень доступа как и у вас", + "Set a Display Name": "Установить отображаемое имя", + "(~%(searchCount)s results)": "(~%(searchCount)s результатов)", + "%(severalUsers)shad their invitations withdrawn %(repeats)s times": "%(severalUsers)s отозвали свои приглашения %(repeats)s раз", + "%(oneUser)shad their invitation withdrawn %(repeats)s times": "%(oneUser)s отозвал свои приглашения %(repeats)s раз", + "%(severalUsers)shad their invitations withdrawn": "%(severalUsers)s отозвали свои приглашения", + "%(oneUser)shad their invitation withdrawn": "%(oneUser)s отозвал свое приглашение", + "Please select the destination room for this message": "Выберите комнату назначения для этого сообщения", + "Options": "Настройки", + "Passphrases must match": "Пароли должны совпадать", + "Passphrase must not be empty": "Пароль не должен быть пустым", + "Export room keys": "Экспортировать ключи", + "Enter passphrase": "Введите пароль", + "Confirm passphrase": "Подтвердите пароль", + "Import room keys": "Импортировать ключи", + "File to import": "Файл для импорта", + "This process allows you to export the keys for messages you have received in encrypted rooms to a local file. You will then be able to import the file into another Matrix client in the future, so that client will also be able to decrypt these messages.": "Этот процесс позволяет вам экспортировать ключи для сообщений, которые вы получили в комнатах с шифрованием, в локальный файл. Вы сможете импортировать эти ключи в другой клиент Matrix чтобы расшифровать эти сообщения.", + "The exported file will allow anyone who can read it to decrypt any encrypted messages that you can see, so you should be careful to keep it secure. To help with this, you should enter a passphrase below, which will be used to encrypt the exported data. It will only be possible to import the data by using the same passphrase.": "Экспортированный файл позволит любому пользователю расшифровать и зашифровать сообщения, которые вы видите, поэтому вы должны быть крайне осторожны и держать файл в надежном месте. Чтобы поспособствовать этому вы должны ввести пароль, который будет использоваться для шифрования ключей. Вы сможете импоортировать ключи только зная этот пароль.", + "This process allows you to import encryption keys that you had previously exported from another Matrix client. You will then be able to decrypt any messages that the other client could decrypt.": "Этот процесс позволяем вам импортировать ключи шифрования, которые вы экспортировали ранее из клиента Matrix. После импорта вы сможете читать зашифрованную переписку и отправлять шифрованные сообщения.", + "The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.": "Экспортированный файл защищен паролем. Вы должны ввести этот пароль для расшифровки.", + "You must join the room to see its files": "Вы должны зайти в комнату для просмотра файлов", + "Reject all %(invitedRooms)s invites": "Отклонить все %(invitedRooms)s приглашения", + "Start new chat": "Начать новый чат", + "Guest users can't invite users. Please register.": "Гости не могут приглашать пользователей. Пожалуйста, зарегистрируйтесь.", + "Failed to invite": "Ошибка приглашения", + "Failed to invite user": "Ошибка приглашения пользователя", + "Failed to invite the following users to the %(roomName)s room:": "Ошибка приглашения следующих пользователей в %(roomName)s:", + "Confirm Removal": "Подтвердите удаление", + "Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change.": "Вы уверены, что хотите удалить этот эвент? Обратите внимание, что если это смена имени комнаты или топика, то удаление отменит это изменение.", + "Unknown error": "Неизвестная ошибка", + "Incorrect password": "Неправильный пароль", + "This will make your account permanently unusable. You will not be able to re-register the same user ID.": "Это сделает вашу учетную запись нерабочей. Вы не сможете зарегистрироваться снова с тем же ID.", + "This action is irreversible.": "Это действие необратимо.", + "To continue, please enter your password.": "Для продолжения введите ваш пароль.", + "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": "Имя устройства", + "Device key": "Ключ устройства", + "If it matches, press the verify button below. If it doesn't, 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.": "В будущем процесс верификации будет усложнен.", + "Verify device": "Верифицировать устройство", + "I verify that the keys match": "Я верифицирую - ключи совпадают", + "We encountered an error trying to restore your previous session. If you continue, you will need to log in again, and encrypted chat history will be unreadable.": "Обнаружена ошибка при восстановлении вашей предыдущей сессии. Вам необходимо зайти снова, шифрованные сообщения будут нечитаемы.", + "Unable to restore session": "Невозможно восстановить сессию", + "If you have previously used a more recent version of Riot, your session may be incompatible with this version. Close this window and return to the more recent version.": "Если вы использовали более новую версию Riot, то ваша сессия может быть несовместима с текущей. Закройте это окно и вернитесь к использованию более новой версии.", + "Continue anyway": "Все равно продолжить", + "Your display name is how you'll appear to others when you speak in rooms. What would you like it to be?": "Отображаемое имя - это то, как вы отображаетесь в чате. Какое имя вы хотите?", + "You are currently blacklisting unverified devices; to send messages to these devices you must verify them.": "Пока что вы вносите неверифицированные устройства в черный список автоматически. Для отправки сообщений на эти устройства вам необходимо их верифицировать.", + "We recommend you go through the verification process for each device to confirm they belong to their legitimate owner, but you can resend the message without verifying if you prefer.": "Рекомендуется сначала верифицировать устройства для подтверждения их владения правильным пользователем, но вы можете отправить сообщение без верификации, если хотите.", + "\"%(RoomName)s\" contains devices that you haven't seen before.": "\"%(RoomName)s\" содержит неизвестные прежде устройства.", + "Unknown Address": "Неизвестный адрес", + "Unblacklist": "Удалить из черного списка", + "Blacklist": "Добавить в черный список", + "Unverify": "Убрать верификацию", + "Verify...": "Верифицировать...", + "ex. @bob:example.com": "например @bob:example.com", + "Add User": "Добавить пользователя", + "This Home Server would like to make sure you are not a robot": "Этот Home Server хочет удостовериться что вы не робот", + "Sign in with CAS": "Войти с помощью CAS", + "You can use the custom server options to sign into other Matrix servers by specifying a different Home server URL.": "Вы можете использовать пользовательские настройки сервера для использования другого Home Server'а при помощи указания его URL.", + "This allows you to use this app with an existing Matrix account on a different home server.": "Это позволяет использовать это приложение с существующей учетной записью на другом Home Server'е.", + "You can also set a custom identity server but this will typically prevent interaction with users based on email address.": "Вы также можете указать пользовательский сервер идентификации, но это обычно ломает возможность общаться с пользователями с помощью адреса email.", + "Please check your email to continue registration.": "Проверьте свою почту для продолжения регистрации.", + "Token incorrect": "Неправильный токен", + "A text message has been sent to": "Текстовое сообщение было отправлено", + "Please enter the code it contains:": "Введите содержащийся код:", + "If you don't specify an email address, you won't be able to reset your password. Are you sure?": "Если вы не укажете адрес email, то вы не сможете сбросить свой пароль в будущем. Вы уверены?", + "You are registering with %(SelectedTeamName)s": "Вы регистрируетесь на %(SelectedTeamName)s", + "Default server": "Сервер по-умолчанию", + "Custom server": "Пользовательский сервер", + "Home server URL": "URL Home Server'а", + "Identity server URL": "URL сервера идентификации", + "What does this mean?": "Что это значит?", + "Error decrypting audio": "Ошибка расшифровки аудио", + "Error decrypting image": "Ошибка расшифровки изображения", + "Image '%(Body)s' cannot be displayed.": "Изображение '%(Body)s' не может быть отображено.", + "This image cannot be displayed.": "Это изображение не может быть отображено.", + "Error decrypting video": "Ошибка расшифровки видео", + "Add an Integration": "Добавить интеграцию", + "You are about to be taken to a third-party site so you can authenticate your account for use with %(integrationsUrl)s. Do you wish to continue?": "Вы будете перенаправлены на внешний сайт, где вы сможете аутентифицировать свою учетную запись для использования с %(integrationsUrl)s. Вы хотите продолжить?", + "Removed or unknown message type": "Удаленный или неизвестный тип сообщения", + "Disable URL previews by default for participants in this room": "Отключить предпросмотр URL для участников этой комнаты по-умолчанию", + "URL previews are %(globalDisableUrlPreview)s by default for participants in this room.": "Предпросмотр URL %(globalDisableUrlPreview)s по-умолчанию для участников этой комнаты.", + "URL Previews": "Предпросмотр URL", + "Enable URL previews for this room (affects only you)": "Включить предпросмотр URL в этой комнате (только для вас)", + "Drop file here to upload": "Перетащите файл сюда для загрузки", + " (unsupported)": " (не поддерживается)", + "Ongoing conference call%(supportedText)s. %(joinText)s": "Идет конференц-звонок%(supportedText)s. %(joinText)s", + "for %(amount)ss": "для %(amount)s", + "for %(amount)sm": "для %(amount)s", + "for %(amount)sh": "для %(amount)s", + "for %(amount)sd": "для %(amount)s", + "Online": "В сети", + "Idle": "Отошел", + "Offline": "Не в сети", + "Disable URL previews for this room (affects only you)": "Отключить предпросмотр URL в этой комнате (только для вас)", + "$senderDisplayName changed the room avatar to ": "$senderDisplayName сменил аватар комнаты на ", + "%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s удалил аватар комнаты.", + "%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s сменил аватар для %(roomName)s" } diff --git a/src/i18n/strings/th.json b/src/i18n/strings/th.json index 5bafdc97cd..6608219faa 100644 --- a/src/i18n/strings/th.json +++ b/src/i18n/strings/th.json @@ -25,7 +25,7 @@ "(default: %(userName)s)": "(ค่าเริ่มต้น: %(userName)s)", "Default Device": "อุปกรณ์เริ่มต้น", "%(senderName)s banned %(targetName)s.": "%(senderName)s แบน %(targetName)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\"", "Decrypt %(text)s": "ถอดรหัส %(text)s", "Device ID": "ID อุปกรณ์", "Device ID:": "ID อุปกรณ์:", @@ -96,8 +96,8 @@ "all room members, from the point they are invited": "สมาชิกทั้งหมด นับตั้งแต่เมื่อได้รับคำเชิญ", "all room members, from the point they joined": "สมาชิกทั้งหมด นับตั้งแต่เมื่อเข้าร่วมห้อง", "an address": "ที่อยู่", - "%(items)s and %(remaining)s others": "%(items)s และอีก %(remaining)s อย่าง", - "%(items)s and one other": "%(items)s และอีกหนึ่งอย่าง", + "%(items)s and %(remaining)s others": "%(items)s และอีก %(remaining)s ผู้ใช้", + "%(items)s and one other": "%(items)s และอีกหนึ่งผู้ใช้", "%(items)s and %(lastItem)s": "%(items)s และ %(lastItem)s", "and %(overflowCount)s others...": "และอีก %(overflowCount)s ผู้ใช้...", "and one other...": "และอีกหนึ่งผู้ใช้...", @@ -322,5 +322,13 @@ "This is a preview of this room. Room interactions have been disabled": "นี่คือตัวอย่างของห้อง การตอบสนองภายในห้องถูกปิดใช้งาน", "This phone number is already in use": "หมายเลขโทรศัพท์นี้ถูกใช้งานแล้ว", "This room's internal ID is": "ID ภายในของห้องนี้คือ", - "times": "เวลา" + "times": "เวลา", + "%(oneUser)schanged their name": "%(oneUser)sเปลี่ยนชื่อของเขาแล้ว", + "%(severalUsers)schanged their name %(repeats)s times": "%(severalUsers)sเปลี่ยนชื่อของพวกเขา %(repeats)s ครั้ง", + "%(oneUser)schanged their name %(repeats)s times": "%(oneUser)sเปลี่ยนชื่อของเขา %(repeats)s ครั้ง", + "%(severalUsers)schanged their name": "%(severalUsers)sเปลี่ยนชื่อของพวกเขาแล้ว", + "Create new room": "สร้างห้องใหม่", + "Room directory": "ไดเรกทอรีห้อง", + "Start chat": "เริ่มแชท", + "Welcome page": "หน้าต้อนรับ" } From 93fcd0aa4047db9edaa3dd435d9aa5e05fce70d0 Mon Sep 17 00:00:00 2001 From: Amandine Date: Wed, 7 Jun 2017 18:02:20 +0000 Subject: [PATCH 0647/1016] Translated using Weblate (French) Currently translated at 100.0% (850 of 850 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/fr/ --- src/i18n/strings/fr.json | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index c7bff8977b..4d01492815 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -834,5 +834,30 @@ "Disable markdown formatting": "Désactiver le formattage markdown", "Error: Problem communicating with the given homeserver.": "Erreur: Problème de communication avec le homeserveur.", "Failed to fetch avatar URL": "Échec lors de la récupération de l’URL de l’avatar", - "The phone number entered looks invalid": "Le numéro de téléphone entré semble être invalide" + "The phone number entered looks invalid": "Le numéro de téléphone entré semble être invalide", + "Guest users can't upload files. Please register to upload.": "Les visiteurs ne peuvent pas télécharger de fichier. Veuillez vous enregistrer pour télécharger.", + "Some of your messages have not been sent.": "Certains de vos messages n’ont pas été envoyés.", + "This room is private or inaccessible to guests. You may be able to join if you register.": "Ce salon est privé ou interdits aux visiteurs. Vous pourrez peut-être le joindre si vous vous enregistrez.", + "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.": "Un instant donné de la chronologie n’a pu être chargé car vous n’avez pas la permission de le visualiser.", + "Tried to load a specific point in this room's timeline, but was unable to find it.": "Un instant donné de la chronologie n’a pu être chargé car il n’a pas pu être trouvé.", + "Uploading %(filename)s and %(count)s others.zero": "Téléchargement de %(filename)s", + "Uploading %(filename)s and %(count)s others.one": "Téléchargement de %(filename)s et %(count)s autre", + "Uploading %(filename)s and %(count)s others.other": "Téléchargement de %(filename)s et %(count)s autres", + "You must register to use this functionality": "Vous devez vous inscrire pour utiliser cette fonctionnalité", + "Resend all or cancel all now. You can also select individual messages to resend or cancel.": "Tout renvoyer or tout annuler maintenant. Vous pouvez aussi sélectionner des messages individuels à envoyer ou annuler.", + "Create new room": "Créer un nouveau salon", + "Welcome page": "Page d'accueil", + "Room directory": "Répertoire des salons", + "Start chat": "Démarrer une discussion", + "New Password": "Nouveau mot de passe", + "Start chatting": "Démarrer une discussion", + "Start Chatting": "Démarrer une discussion", + "Click on the button below to start chatting!": "Cliquer sur le bouton ci-dessous pour commencer une discussion !", + "Create a new chat or reuse an existing one": "Démarrer une nouvelle discussion ou en réutiliser une existante", + "You already have existing direct chats with this user:": "Vous avez déjà des discussions en cours avec cet utilisateur :", + "Username available": "Nom d'utilisateur disponible", + "Username not available": "Nom d'utilisateur indisponible", + "Something went wrong!": "Quelque chose s’est mal passé !", + "This will be your account name on the homeserver, or you can pick a different server.": "Cela sera le nom de votre compte sur le serveur , ou vous pouvez sélectionner un autre serveur.", + "If you already have a Matrix account you can log in instead.": "Si vous avez déjà un compte Matrix vous pouvez vous identifier à la place." } From 8833dc64b7267aa7491c407e3115762431456030 Mon Sep 17 00:00:00 2001 From: Bamstam Date: Wed, 7 Jun 2017 18:02:34 +0000 Subject: [PATCH 0648/1016] Translated using Weblate (German) Currently translated at 100.0% (850 of 850 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/de/ --- src/i18n/strings/de_DE.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index 0d952ffcd9..abf1eea088 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -182,7 +182,7 @@ "Once you've followed the link it contains, click below": "Nachdem du dem darin enthaltenen Link gefolgt bist, klicke unten", "rejected the invitation.": "lehnte die Einladung ab.", "Reject invitation": "Einladung ablehnen", - "Remove Contact Information?": "Kontakt-Informationen löschen?", + "Remove Contact Information?": "Kontakt-Informationen entfernen?", "removed their display name": "löschte den eigenen Anzeigenamen", "Remove": "Entfernen", "requested a VoIP conference": "hat eine VoIP-Konferenz angefordert", @@ -271,7 +271,7 @@ "Who would you like to communicate with?": "Mit wem möchtest du kommunizieren?", "Would you like to": "Möchtest du", "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 von %(inviterName)s in diesen Raum eingeladen", + "You have been invited to join this room by %(inviterName)s": "%(inviterName)s hat dich in diesen Raum eingeladen", "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 auf allen Geräten abgemeldet und wirst keine Push-Benachrichtigungen mehr erhalten. Um die Benachrichtigungen zu reaktivieren, musst du dich auf jedem Gerät neu anmelden", "you must be a": "nötige Rolle", "Your password has been reset": "Dein Passwort wurde zurückgesetzt", @@ -377,7 +377,7 @@ "Power level must be positive integer.": "Berechtigungslevel muss eine positive ganze Zahl sein.", "Reason": "Grund", "%(targetName)s rejected the invitation.": "%(targetName)s hat die Einladung abgelehnt.", - "%(senderName)s removed their display name (%(oldDisplayName)s).": "%(senderName)s löschte den Anzeigenamen (%(oldDisplayName)s).", + "%(senderName)s removed their display name (%(oldDisplayName)s).": "%(senderName)s hat den Anzeigenamen entfernt (%(oldDisplayName)s).", "%(senderName)s removed their profile picture.": "%(senderName)s hat das Profilbild gelöscht.", "%(senderName)s requested a VoIP conference.": "%(senderName)s möchte eine VoIP-Konferenz beginnen.", "Room %(roomId)s not visible": "Raum %(roomId)s ist nicht sichtbar", @@ -902,8 +902,8 @@ "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.": "Es wurde versucht einen spezifischen Punkt in der Chat-Historie zu laden, aber du hast keine Berechtigung diese Nachricht zu sehen.", "Tried to load a specific point in this room's timeline, but was unable to find it.": "Es wurde versucht einen spezifischen Punkt in der Chat-Historie zu laden, aber er konnte nicht gefunden werden.", "Uploading %(filename)s and %(count)s others.zero": "%(filename)s wird hochgeladen", - "Uploading %(filename)s and %(count)s others.one": "%(filename)s und %(count)s weitere werden hochgeladen", - "Uploading %(filename)s and %(count)s others.other": "%(filename)s und %(count)s weitere werden hochgeladen", + "Uploading %(filename)s and %(count)s others.one": "%(filename)s und %(count)s weitere Dateien werden hochgeladen", + "Uploading %(filename)s and %(count)s others.other": "%(filename)s und %(count)s weitere Dateien werden hochgeladen", "You must register to use this functionality": "Du musst dich registrieren um diese Funktionalität zu nutzen", "Resend all or cancel all now. You can also select individual messages to resend or cancel.": "Sende erneut oder breche alles ab. Du kannst auch auch individuelle Nachrichten erneut senden or abbrechen.", "Create new room": "Neuen Raum erstellen", @@ -920,5 +920,5 @@ "Username not available": "Nutzername nicht verfügbar", "Something went wrong!": "Etwas ging schief!", "This will be your account name on the homeserver, or you can pick a different server.": "Dies wird dein Konto-Name auf dem Heimserver, oder du kannst einen anderen Server auswählen.", - "If you already have a Matrix account you can log in instead.": "Wenn du bereits ein Matrix-Konto hast, kannst du ansonsten auch anmelden." + "If you already have a Matrix account you can log in instead.": "Wenn du bereits ein Matrix-Benutzerkonto hast, kannst du dich stattdessen auch direkt anmelden." } From 6ae8172fa2a91f8ba90142d1f74eb0593e0ee5a1 Mon Sep 17 00:00:00 2001 From: Pitchaya Boonsarngsuk Date: Wed, 7 Jun 2017 18:03:05 +0000 Subject: [PATCH 0649/1016] Translated using Weblate (Thai) Currently translated at 49.5% (421 of 850 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/th/ --- src/i18n/strings/th.json | 76 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/th.json b/src/i18n/strings/th.json index 2f64cdaf71..716cdb34e2 100644 --- a/src/i18n/strings/th.json +++ b/src/i18n/strings/th.json @@ -346,5 +346,79 @@ "to favourite": "ไปยังรายการโปรด", "to demote": "เพื่อลดขั้น", "The default role for new room members is": "บทบาทเริ่มต้นของสมาชิกใหม่คือ", - "The phone number entered looks invalid": "ดูเหมือนว่าหมายเลขโทรศัพท์ที่กรอกรมาไม่ถูกต้อง" + "The phone number entered looks invalid": "ดูเหมือนว่าหมายเลขโทรศัพท์ที่กรอกรมาไม่ถูกต้อง", + "The email address linked to your account must be entered.": "กรุณากรอกที่อยู่อีเมลที่เชื่อมกับบัญชีของคุณ", + "The file '%(fileName)s' exceeds this home server's size limit for uploads": "ไฟล์ '%(fileName)s' มีขนาดใหญ่เกินจำกัดของเซิร์ฟเวอร์บ้าน", + "To send messages": "เพื่อส่งข้อความ", + "to start a chat with someone": "เพื่อเริ่มแชทกับผู้อื่น", + "to tag as %(tagName)s": "เพื่อแท็กว่า %(tagName)s", + "to tag direct chat": "เพื่อแทกว่าแชทตรง", + "Turn Markdown off": "ปิด markdown", + "Turn Markdown on": "เปิด markdown", + "%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s).": "%(senderName)s ได้เปิดใช้งานการเข้ารหัสจากปลายทางถึงปลายทาง (อัลกอริทึม%(algorithm)s).", + "Unable to add email address": "ไมาสามารถเพิ่มที่อยู่อีเมล", + "Unable to verify email address.": "ไม่สามารถยืนยันที่อยู่อีเมล", + "Unban": "ปลดแบน", + "%(senderName)s unbanned %(targetName)s.": "%(senderName)s ปลดแบน %(targetName)s แล้ว", + "Unable to capture screen": "ไม่สามารถจับภาพหน้าจอ", + "Unable to enable Notifications": "ไม่สามารถเปิดใช้งานการแจ้งเตือน", + "Unable to load device list": "ไม่สามารถโหลดรายชื่ออุปกรณ์", + "Unencrypted room": "ห้องที่ไม่เข้ารหัส", + "unencrypted": "ยังไม่ได้เข้ารหัส", + "Unknown command": "คำสั่งที่ไม่รู้จัก", + "unknown device": "อุปกรณ์ที่ไม่รู้จัก", + "Unknown room %(roomId)s": "ห้องที่ไม่รู้จัก %(roomId)s", + "Unknown (user, device) pair:": "คู่ (ผู้ใช้, อุปกรณ์) ที่ไม่รู้จัก:", + "unknown": "ไม่รู้จัก", + "Unrecognised command:": "คำสั่งที่ไม่รู้จัก:", + "Unrecognised room alias:": "นามแฝงห้องที่ไม่รู้จัก:", + "Uploading %(filename)s and %(count)s others.zero": "กำลังอัปโหลด %(filename)s", + "Uploading %(filename)s and %(count)s others.one": "กำลังอัปโหลด %(filename)s และอีก %(count)s ไฟล์", + "Uploading %(filename)s and %(count)s others.other": "กำลังอัปโหลด %(filename)s และอีก %(count)s ไฟล์", + "uploaded a file": "อัปโหลดไฟล์", + "Upload Failed": "การอัปโหลดล้มเหลว", + "Upload Files": "อัปโหลดไฟล์", + "Upload file": "อัปโหลดไฟล์", + "Usage": "การใช้งาน", + "User ID": "ID ผู้ใช้", + "User Interface": "อินเตอร์เฟสผู้ใช้", + "User name": "ชื่อผู้ใช้", + "User": "ผู้ใช้", + "Warning!": "คำเตือน!", + "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?": "คุณต้องการสื่อสารกับใคร?", + "You're not in any rooms yet! Press": "คุณยังไม่ได้อยู่ในห้องใดเลย! กด", + "You are trying to access %(roomName)s": "คุณกำลังพยายามเข้าสู่ %(roomName)s", + "You have disabled URL previews by default.": "ค่าเริ่มต้นของคุณปิดใช้งานตัวอย่าง URL เอาไว้", + "You have enabled URL previews by default.": "ค่าเริ่มต้นของคุณเปิดใช้งานตัวอย่าง URL เอาไว้", + "you must be a": "คุณต้องเป็น", + "You must register to use this functionality": "คุณต้องลงทะเบียนเพื่อใช้ฟังก์ชันนี้", + "You need to be logged in.": "คุณต้องเข้าสู่ระบบก่อน", + "You need to enter a user name.": "คุณต้องกรอกชื่อผู้ใช้ก่อน", + "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": "การเปลี่ยนรหัสผ่านเสร็จสมบูณณ์ คุณจะไม่ได้รับการแจ้งเตือนบนอุปกรณ์อื่น ๆ จนกว่าคุณจะกลับเข้าสู่ระบบในอุปกรณ์เหล่านั้น", + "Sun": "อา.", + "Mon": "จ.", + "Tue": "อ.", + "Wed": "พ.", + "Thu": "พฤ.", + "Fri": "ศ.", + "Sat": "ส.", + "Jan": "ม.ค.", + "Feb": "ก.พ.", + "Mar": "มี.ค.", + "Apr": "เม.ย.", + "May": "พ.ค.", + "Jun": "มิ.ย.", + "Jul": "ก.ค.", + "Aug": "ส.ค.", + "Sep": "ก.ย.", + "Oct": "ต.ค.", + "Nov": "พ.ย.", + "Dec": "ธ.ค.", + "%(weekDayName)s, %(monthName)s %(day)s %(time)s": "%(weekDayName)s %(day)s %(monthName)s %(time)s", + "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "วัน%(weekDayName)sที่ %(day)s %(monthName)s %(fullYear)s เวลา %(time)s", + "%(weekDayName)s %(time)s": "%(weekDayName)s %(time)s" } From e21a8d384b5b4b9440da12766fe52473e7951216 Mon Sep 17 00:00:00 2001 From: Pitchaya Boonsarngsuk Date: Wed, 7 Jun 2017 18:36:25 +0000 Subject: [PATCH 0650/1016] Translated using Weblate (Thai) Currently translated at 56.0% (476 of 850 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/th/ --- src/i18n/strings/th.json | 61 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 3 deletions(-) diff --git a/src/i18n/strings/th.json b/src/i18n/strings/th.json index 716cdb34e2..f7ed3aa550 100644 --- a/src/i18n/strings/th.json +++ b/src/i18n/strings/th.json @@ -201,7 +201,7 @@ "Hangup": "วางสาย", "Historical": "ประวัติแชทเก่า", "Homeserver is": "เซิร์ฟเวอร์บ้านคือ", - "Identity Server is": "เซิร์ฟเวอร์ยืนยันตัวตนคือ", + "Identity Server is": "เซิร์ฟเวอร์ระบุตัวตนคือ", "I have verified my email address": "ฉันยืนยันที่อยู่อีเมลแล้ว", "Import": "นำเข้า", "Incorrect username and/or password.": "ชื่อผู้ใช้และ/หรือรหัสผ่านไม่ถูกต้อง", @@ -419,6 +419,61 @@ "Nov": "พ.ย.", "Dec": "ธ.ค.", "%(weekDayName)s, %(monthName)s %(day)s %(time)s": "%(weekDayName)s %(day)s %(monthName)s %(time)s", - "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "วัน%(weekDayName)sที่ %(day)s %(monthName)s %(fullYear)s เวลา %(time)s", - "%(weekDayName)s %(time)s": "%(weekDayName)s %(time)s" + "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s %(day)s %(monthName)s %(fullYear)s %(time)s", + "%(weekDayName)s %(time)s": "%(weekDayName)s %(time)s", + "Set a display name:": "ตั้งชื่อที่แสดง:", + "Set a Display Name": "ตั้งชื่อที่แสดง", + "Passwords don't match.": "รหัสผ่านไม่ตรงกัน", + "Password too short (min %(MIN_PASSWORD_LENGTH)s).": "รหัสผ่านสั้นเกินไป (ขึ้นต่ำ %(MIN_PASSWORD_LENGTH)s ตัวอักษร)", + "An unknown error occurred.": "เกิดข้อผิดพลาดที่ไม่รู้จัก", + "I already have an account": "ฉันมีบัญชีอยู่แล้ว", + "An error occured: %(error_string)s": "เกิดข้อผิดพลาด: %(error_string)s", + "Topic": "หัวข้อ", + "Make Moderator": "เลื่อนขั้นเป็นผู้ช่วยดูแล", + "Make this room private": "ทำให้ห้องนี้เป็นส่วนตัว", + "Share message history with new users": "แบ่งประวัติแชทให้ผู้ใช้ใหม่", + "Encrypt room": "เข้ารหัสห้อง", + "Room": "ห้อง", + "(~%(searchCount)s results)": "(~%(searchCount)s ผลลัพธ์)", + "or": "หรือ", + "bold": "หนา", + "italic": "เอียง", + "strike": "ขีดทับ", + "underline": "ขีดเส้นใต้", + "code": "โค๊ด", + "quote": "อ้างอิง", + "were kicked %(repeats)s times": "ถูกเตะ %(repeats)s ครั้ง", + "was kicked %(repeats)s times": "ถูกเตะ %(repeats)s ครั้ง", + "were kicked": "ถูกเตะ", + "was kicked": "ถูกเตะ", + "New Password": "รหัสผ่านใหม่", + "Options": "ตัวเลือก", + "Export room keys": "ส่งออกกุณแจห้อง", + "Confirm passphrase": "ยืนยันรหัสผ่าน", + "Import room keys": "นำเข้ากุณแจห้อง", + "File to import": "ไฟล์ที่จะนำเข้า", + "Start new chat": "เริ่มแชทใหม่", + "Failed to invite": "การเชิญล้มเหลว", + "Failed to invite user": "การเชิญผู้ใช้ล้มเหลว", + "Failed to invite the following users to the %(roomName)s room:": "การเชิญผู้ใช้เหล่านี้เข้าสู่ห้อง %(roomName)s ล้มเหลว:", + "Confirm Removal": "ยืนยันการลบ", + "Unknown error": "ข้อผิดพลาดที่ไม่รู้จัก", + "Incorrect password": "รหัสผ่านไม่ถูกต้อง", + "Device name": "ชื่ออุปกรณ์", + "Device key": "Key อุปกรณ์", + "Unknown devices": "อุปกรณ์ที่ไม่รู้จัก", + "Unknown Address": "ที่อยู่ที่ไม่รู้จัก", + "Unblacklist": "ถอดบัญชีดำ", + "Blacklist": "ขึ้นบัญชีดำ", + "ex. @bob:example.com": "เช่น @bob:example.com", + "Add User": "เพิ่มผู้ใช้", + "This Home Server would like to make sure you are not a robot": "เซิร์ฟเวอร์บ้านต้องการยืนยันว่าคุณไม่ใช่หุ่นยนต์", + "Sign in with CAS": "เข้าสู่ระบบด้วย CAS", + "You can use the custom server options to sign into other Matrix servers by specifying a different Home server URL.": "คุณสามารถกำหนดเซิร์ฟเวอร์บ้านเองได้โดยใส่ URL ของเซิร์ฟเวอร์นั้น เพื่อเข้าสู่ระบบของเซิร์ฟเวอร์ Matrix อื่น", + "This allows you to use this app with an existing Matrix account on a different home server.": "ทั้งนี่เพื่อให้คุณสามารถใช้ Riot กับบัญชี Matrix ที่มีอยู่แล้วบนเซิร์ฟเวอร์บ้านอื่น ๆ ได้", + "You can also set a custom identity server but this will typically prevent interaction with users based on email address.": "คุณอาจเลือกเซิร์ฟเวอร์ระบุตัวตนเองด้วยก็ได้ แต่คุณจะไม่สามารถเชิญผู้ใช้อื่นด้วยที่อยู่อีเมล หรือรับคำเชิญจากผู้ใช้อื่นทางที่อยู่อีเมลได้", + "Default server": "เซิร์ฟเวอร์เริ่มต้น", + "Custom server": "เซิร์ฟเวอร์ที่กำหนดเอง", + "Home server URL": "URL เซิร์ฟเวอร์บ้าน", + "Identity server URL": "URL เซิร์ฟเวอร์ระบุตัวตน" } From 566fb2caebc604c90c3073a97482fa6873043e7b Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Wed, 7 Jun 2017 22:41:02 +0100 Subject: [PATCH 0651/1016] Add some logging around switching rooms ... which I wish had been there when I was staring at a rageshake. --- src/components/structures/MatrixChat.js | 11 +++++++++++ src/components/structures/TimelinePanel.js | 3 +++ 2 files changed, 14 insertions(+) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index efb2b38d6e..d11e100f26 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -644,6 +644,17 @@ module.exports = React.createClass({ } } + if (roomInfo.room_alias) { + console.log( + `Switching to room alias ${roomInfo.room_alias} at event ` + + newState.initialEventId, + ); + } else { + console.log(`Switching to room id ${roomInfo.room_id} at event ` + + newState.initialEventId, + ); + } + // Wait for the first sync to complete so that if a room does have an alias, // it would have been retrieved. let waitFor = q(null); diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js index f0e6810b6b..928e2405aa 100644 --- a/src/components/structures/TimelinePanel.js +++ b/src/components/structures/TimelinePanel.js @@ -902,6 +902,9 @@ var TimelinePanel = React.createClass({ var onError = (error) => { this.setState({timelineLoading: false}); + console.error( + `Error loading timeline panel at ${eventId}: ${error}`, + ); var msg = error.message ? error.message : JSON.stringify(error); var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); From 0f4028da07f05682950ac47489d7f5bafebc2f0f Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Thu, 8 Jun 2017 09:44:58 +0100 Subject: [PATCH 0652/1016] Add more translations.. --- src/components/views/create_room/Presets.js | 7 ++++--- src/components/views/create_room/RoomAlias.js | 3 ++- src/components/views/dialogs/ChatCreateOrReuseDialog.js | 4 ++-- src/components/views/dialogs/ChatInviteDialog.js | 2 +- src/components/views/dialogs/DeactivateAccountDialog.js | 2 +- src/i18n/strings/en_EN.json | 7 +++++++ 6 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/components/views/create_room/Presets.js b/src/components/views/create_room/Presets.js index 6d40be9d32..c4d7ca0cdb 100644 --- a/src/components/views/create_room/Presets.js +++ b/src/components/views/create_room/Presets.js @@ -17,6 +17,7 @@ limitations under the License. 'use strict'; var React = require('react'); +import { _t } from '../../../languageHandler'; var Presets = { PrivateChat: "private_chat", @@ -46,9 +47,9 @@ module.exports = React.createClass({ render: function() { return ( ); } diff --git a/src/components/views/create_room/RoomAlias.js b/src/components/views/create_room/RoomAlias.js index 6d311db593..e1cb7e4094 100644 --- a/src/components/views/create_room/RoomAlias.js +++ b/src/components/views/create_room/RoomAlias.js @@ -15,6 +15,7 @@ limitations under the License. */ var React = require('react'); +import { _t } from '../../../languageHandler'; module.exports = React.createClass({ displayName: 'RoomAlias', @@ -94,7 +95,7 @@ module.exports = React.createClass({ render: function() { return ( - ); diff --git a/src/components/views/dialogs/ChatCreateOrReuseDialog.js b/src/components/views/dialogs/ChatCreateOrReuseDialog.js index 55caa52299..e83333556d 100644 --- a/src/components/views/dialogs/ChatCreateOrReuseDialog.js +++ b/src/components/views/dialogs/ChatCreateOrReuseDialog.js @@ -96,10 +96,10 @@ export default class ChatCreateOrReuseDialog extends React.Component { onFinished={() => { this.props.onFinished(false) }} - title='Create a new chat or reuse an existing one' + title={_t('Create a new chat or reuse an existing one')} >
- You already have existing direct chats with this user: + {_t("You already have existing direct chats with this user:")}
{tiles} {startNewChat} diff --git a/src/components/views/dialogs/ChatInviteDialog.js b/src/components/views/dialogs/ChatInviteDialog.js index cd294a123d..c411c42392 100644 --- a/src/components/views/dialogs/ChatInviteDialog.js +++ b/src/components/views/dialogs/ChatInviteDialog.js @@ -490,7 +490,7 @@ module.exports = React.createClass({ error =
{_t("You have entered an invalid contact. Try using their Matrix ID or email address.")}
; } else { const addressSelectorHeader =
- Searching known users + {_t("Searching known users")}
; addressSelector = ( {this.addressSelector = ref;}} diff --git a/src/components/views/dialogs/DeactivateAccountDialog.js b/src/components/views/dialogs/DeactivateAccountDialog.js index da74e6b716..e3b7cca078 100644 --- a/src/components/views/dialogs/DeactivateAccountDialog.js +++ b/src/components/views/dialogs/DeactivateAccountDialog.js @@ -86,7 +86,7 @@ export default class DeactivateAccountDialog extends React.Component { passwordBoxClass = 'error'; } - const okLabel = this.state.busy ? : 'Deactivate Account'; + const okLabel = this.state.busy ? : _t('Deactivate Account'); const okEnabled = this.state.confirmButtonEnabled && !this.state.busy; let cancelButton = null; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 56f38cc87c..36d220dca5 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -145,6 +145,7 @@ "Hide removed messages": "Hide removed messages", "Always show message timestamps": "Always show message timestamps", "Authentication": "Authentication", + "Alias (optional)": "Alias (optional)", "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", @@ -218,11 +219,13 @@ "one": "%(count)s new message", "other": "%(count)s new messages" }, + "Create a new chat or reuse an existing one": "Create a new chat or reuse an existing one", "Create an account": "Create an account", "Create Room": "Create Room", "Cryptography": "Cryptography", "Current password": "Current password", "Curve25519 identity key": "Curve25519 identity key", + "Custom": "Custom", "Custom level": "Custom level", "/ddg is not a command": "/ddg is not a command", "Deactivate Account": "Deactivate Account", @@ -420,8 +423,10 @@ "Power level must be positive integer.": "Power level must be positive integer.", "Press": "Press", "Privacy warning": "Privacy warning", + "Private Chat": "Private Chat", "Privileged Users": "Privileged Users", "Profile": "Profile", + "Public Chat": "Public Chat", "Reason": "Reason", "Revoke Moderator": "Revoke Moderator", "Refer a friend to Riot:": "Refer a friend to Riot:", @@ -456,6 +461,7 @@ "Search": "Search", "Search failed": "Search failed", "Searches DuckDuckGo for results": "Searches DuckDuckGo for results", + "Searching known users": "Searching known users", "Send a message (unencrypted)": "Send a message (unencrypted)", "Send an encrypted message": "Send an encrypted message", "Sender device information": "Sender device information", @@ -601,6 +607,7 @@ "Who would you like to communicate with?": "Who would you like to communicate with?", "%(senderName)s withdrew %(targetName)s's invitation.": "%(senderName)s withdrew %(targetName)s's invitation.", "Would you like to": "Would you like to", + "You already have existing direct chats with this user:": "You already have existing direct chats with this user:", "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", From bc0eb4294c7f4c45754ef02cc19d1444743653a4 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Thu, 8 Jun 2017 10:27:35 +0100 Subject: [PATCH 0653/1016] rename welcome page to home --- src/components/views/elements/HomeButton.js | 2 +- src/i18n/strings/en_EN.json | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/views/elements/HomeButton.js b/src/components/views/elements/HomeButton.js index 80c64c6a60..4877f5dd43 100644 --- a/src/components/views/elements/HomeButton.js +++ b/src/components/views/elements/HomeButton.js @@ -23,7 +23,7 @@ const HomeButton = function(props) { const ActionButton = sdk.getComponent('elements.ActionButton'); return ( Date: Thu, 8 Jun 2017 09:56:30 +0000 Subject: [PATCH 0654/1016] Translated using Weblate (Russian) Currently translated at 100.0% (852 of 852 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/ru/ --- src/i18n/strings/ru.json | 46 +++++++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/src/i18n/strings/ru.json b/src/i18n/strings/ru.json index 85b7a6a7f5..647fbfe1d5 100644 --- a/src/i18n/strings/ru.json +++ b/src/i18n/strings/ru.json @@ -92,14 +92,14 @@ "Found a bug?": "Нашли ошибку?", "had": "имеет", "Hangup": "Отключение", - "Historical": "Исторический", + "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": "Прегласить новых учасников комнаты", + "Invite new room members": "Пригласить новых учасников в комнату", "Invites": "Приглашать", "Invites user with given id to current room": "Пригласить пользователя с данным id в текущую комнату", "is a": "является", @@ -119,13 +119,13 @@ "Logout": "Выход из системы", "Low priority": "Низкий приоритет", "made future room history visible to": "made future room history visible to", - "Manage Integrations": "Управление интеграций", + "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 from this device": "Никогда не отправлять зашифрованные сообщения на неверифицированные устроства с этого устройства", "Never send encrypted messages to unverified devices in this room from this device": "Никогда не отправляйте зашифрованные сообщения в непроверенные устройства в этой комнате из этого устройства", "New password": "Новый пароль", "New passwords must match each other.": "Новые пароли должны соответствовать друг другу.", @@ -190,7 +190,7 @@ "Voice call": "Голосовой вызов", "VoIP conference finished.": "VoIP конференция закончилась.", "VoIP conference started.": "VoIP Конференция стартовала.", - "(warning: cannot be disabled again!)": "(предупреждение: не может быть снова отключен!)", + "(warning: cannot be disabled again!)": "(предупреждение: не может быть отключено!)", "Warning!": "Предупреждение!", "was banned": "запрещен", "was invited": "приглашенный", @@ -200,7 +200,7 @@ "were": "быть", "Who can access this room?": "Кто может получить доступ к этой комнате?", "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?": "С кем хотели бы Вы связываться?", "withdrawn": "уходить", "Would you like to": "Хотели бы Вы", @@ -658,7 +658,7 @@ "powered by Matrix": "управляемый с Matrix", "Add a topic": "Добавить тему", "Show timestamps in 12 hour format (e.g. 2:30pm)": "Времея отображать в 12 часовом формате (напр. 2:30pm)", - "Use compact timeline layout": "Используйте компактным указанием времени", + "Use compact timeline layout": "Компактное отображение", "Hide removed messages": "Скрыть удаленное сообщение", "No Microphones detected": "Микрофоны не обнаружены", "Unknown devices": "Незнакомое устройство", @@ -864,5 +864,35 @@ "Disable URL previews for this room (affects only you)": "Отключить предпросмотр URL в этой комнате (только для вас)", "$senderDisplayName changed the room avatar to ": "$senderDisplayName сменил аватар комнаты на ", "%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s удалил аватар комнаты.", - "%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s сменил аватар для %(roomName)s" + "%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s сменил аватар для %(roomName)s", + "Create new room": "Создать комнату", + "Room directory": "Каталог комнат", + "Start chat": "Начать чат", + "Welcome page": "Домашняя страница", + "Add": "Добавить", + "%(count)s new messages.one": "%(count)s новое сообщение", + "%(count)s new messages.other": "%(count)s новых сообщений", + "Error: Problem communicating with the given homeserver.": "Ошибка: проблема коммуникаций с указанным Home Server'ом.", + "Failed to fetch avatar URL": "Ошибка получения аватара", + "The phone number entered looks invalid": "Введенный номер телефона выглядит неправильным", + "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.": "Ошибка загрузки истории комнаты: запрошенный элемент не найден.", + "Uploading %(filename)s and %(count)s others.zero": "Загрузка %(filename)s", + "Uploading %(filename)s and %(count)s others.one": "Загрузка %(filename)s и %(count)s другой файл", + "Uploading %(filename)s and %(count)s others.other": "Загрузка %(filename)s и %(count)s других файлов", + "Username invalid: %(errMessage)s": "Неверное имя пользователя: %(errMessage)s", + "Searching known users": "Искать известных пользователей", + "You must register to use this functionality": "Вы должны зарегистрироваться для использования этой функции", + "Resend all or cancel all now. You can also select individual messages to resend or cancel.": "Отослать снова или отменить отправку. Вы также можете выбрать на отправку или отмену отдельные сообщения.", + "New Password": "Новый пароль", + "Start chatting": "Начать общение", + "Start Chatting": "Начать общение", + "Click on the button below to start chatting!": "Нажмите на кнопку ниже для того, чтобы начать общение!", + "Create a new chat or reuse an existing one": "Создать новый чат или использовать уже существующий", + "You already have existing direct chats with this user:": "У вас уже есть существующие приватные чаты с этим пользователем:", + "Username available": "Имя пользователя доступно", + "Username not available": "Имя пользователя недоступно", + "Something went wrong!": "Что-то пошло не так!", + "This will be your account name on the homeserver, or you can pick a different server.": "Это будет ваше имя пользователя на , или вы можете выбрать другой сервер.", + "If you already have a Matrix account you can log in instead.": "Если вы уже имеете учетную запись Matrix, то вы можете войти." } From 96e471adc6ae522b58442bbe6307de7b1bff3ce3 Mon Sep 17 00:00:00 2001 From: Bamstam Date: Wed, 7 Jun 2017 22:03:11 +0000 Subject: [PATCH 0655/1016] Translated using Weblate (German) Currently translated at 99.7% (850 of 852 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/de/ --- src/i18n/strings/de_DE.json | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index ee8478c0c8..2845a5021c 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -127,7 +127,7 @@ "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 wurden.", "Guests can't set avatars. Please register.": "Gäste können kein Profilbild setzen. Bitte registrieren.", - "Guest users can't upload files. Please register to upload.": "Gäste können keine Dateien hochladen. Bitte registrieren um hochzuladen.", + "Guest users can't upload files. Please register to upload.": "Gäste können keine Dateien hochladen. Bitte zunächst registrieren.", "had": "hatte", "Hangup": "Auflegen", "Homeserver is": "Der Homeserver ist", @@ -227,7 +227,7 @@ "to join the discussion": "um an der Diskussion teilzunehmen", "To kick users": "Um Nutzer zu entfernen", "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.", + "Server may be unavailable, overloaded, or you hit a bug.": "Server ist nicht verfügbar, überlastet oder du bist auf einen Fehler gestoßen.", "Could not connect to the integration server": "Konnte keine Verbindung zum Integrations-Server herstellen", "Disable inline URL previews by default": "URL-Vorschau im Chat standardmäßig deaktivieren", "Guests can't use labs features. Please register.": "Gäste können keine Labor-Funktionen nutzen. Bitte registrieren.", @@ -281,14 +281,14 @@ "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 calling is in development and may not be reliable.": "Konferenzgespräche befinden sich noch in der Entwicklungsphase und sind möglicherweise nicht zuverlässig nutzbar.", "Conference calls are not supported in encrypted rooms": "Konferenzgespräche werden in verschlüsselten Räumen nicht unterstützt", "Conference calls are not supported in this client": "Konferenzgespräche werden von diesem Client nicht unterstützt", "Existing Call": "Bereits bestehender Anruf", "Failed to set up conference call": "Konferenzgespräch konnte nicht gestartet werden", "Failed to verify email address: make sure you clicked the link in the email": "Verifizierung der E-Mail-Adresse fehlgeschlagen: Bitte stelle sicher, dass du den Link in der E-Mail angeklickt hast", "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.", + "Guest users can't create new rooms. Please register to create room and start a chat.": "Gastnutzer können keine neuen Räume erstellen. Bitte registriere dich um Räume zu erstellen und Chats 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", @@ -302,11 +302,11 @@ "Unable to enable Notifications": "Benachrichtigungen konnten nicht aktiviert werden", "Upload Failed": "Upload fehlgeschlagen", "VoIP is unsupported": "VoIP wird nicht unterstützt", - "You are already in a call.": "Du bist bereits bei einem Anruf.", + "You are already in a call.": "Du bist bereits in einem Gespräch.", "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 cannot place VoIP calls in this browser.": "Du kannst keine VoIP-Gespräche 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üsselungs-Schlüssel für dieses Gerät zu generieren und um den öffentlichen Schlüssel auf deinem Homeserver zu hinterlegen. Dies muss nur einmal durchgeführt werden, bitte entschuldige die Unannehmlichkeiten.", - "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.", + "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 Heimserver verbunden zu sein.", "Sun": "So", "Mon": "Mo", "Tue": "Di", @@ -581,7 +581,6 @@ "Failed to save settings": "Einstellungen konnten nicht gespeichert werden", "Failed to set display name": "Anzeigename konnte nicht gesetzt werden", "Fill screen": "Fülle Bildschirm", - "Guest users can't upload files. Please register to upload.": "Gäste können keine Dateien hochladen. Bitte zunächst registrieren.", "Hide Text Formatting Toolbar": "Verberge Text-Formatierungs-Toolbar", "Incorrect verification code": "Falscher Verifizierungscode", "Invalid alias format": "Ungültiges Alias-Format", @@ -608,8 +607,8 @@ "Server error": "Server-Fehler", "Server may be unavailable, overloaded, or search timed out :(": "Der Server ist entweder nicht verfügbar, überlastet oder die Suche wurde wegen Zeitüberschreitung abgebrochen :(", "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.": "Der Server ist entweder nicht verfügbar, überlastet oder es liegt ein anderweitiger Fehler vor.", - "Some of your messages have not been sent.": "Einige deiner Nachrichten wurden noch nicht gesendet.", + "Server unavailable, overloaded, or something else went wrong.": "Server nicht verfügbar, überlastet oder etwas anderes lief falsch.", + "Some of your messages have not been sent.": "Einige deiner Nachrichten wurden 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", @@ -726,7 +725,7 @@ "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.": "Eine Änderung des Passworts setzt derzeit alle Schlüssel für die E2E-Verschlüsselung auf allen verwendeten Geräten zurück. Bereits verschlüsselte Chat-Inhalte sind somit nur noch lesbar, wenn du zunächst alle Schlüssel exportierst und später wieder importierst. Wir arbeiten an einer Verbesserung dieser momentan noch notwendigen Vorgehensweise.", "Unmute": "Stummschalten aufheben", "Invalid file%(extra)s": "Ungültige Datei%(extra)s", - "Remove %(threePid)s?": "Entferne %(threePid)s?", + "Remove %(threePid)s?": "%(threePid)s entfernen?", "Please select the destination room for this message": "Bitte den Raum auswählen, an den diese Nachricht gesendet werden soll", "%(senderDisplayName)s removed the room name.": "%(senderDisplayName)s hat den Raum-Namen gelöscht.", "Passphrases must match": "Passphrase muss übereinstimmen", @@ -736,7 +735,6 @@ "Confirm passphrase": "Bestätige Passphrase", "The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.": "Die Export-Datei wird mit einer Passphrase geschützt sein. Du solltest die Passphrase hier eingeben um die Datei zu entschlüsseln.", "You must join the room to see its files": "Du musst dem Raum beitreten, um die Raum-Dateien sehen zu können", - "Server may be unavailable, overloaded, or you hit a bug.": "Server ist nicht verfügbar, überlastet oder du bist auf einen Fehler gestoßen.", "Reject all %(invitedRooms)s invites": "Alle %(invitedRooms)s Einladungen ablehnen", "Start new Chat": "Starte neuen Chat", "Guest users can't invite users. Please register.": "Gäste können keine Nutzer einladen. Bitte registrieren.", @@ -841,17 +839,13 @@ "Default Device": "Standard-Gerät", "Microphone": "Mikrofon", "Camera": "Kamera", - "Conference call failed.": "Konferenzgespräch fehlgeschlagen.", - "Conference calling is in development and may not be reliable.": "Konferenzgespräche befinden sich noch in der Entwicklungsphase und sind möglicherweise nicht zuverlässig nutzbar.", "Device already verified!": "Gerät bereits verifiziert!", "Export": "Export", "Failed to register as guest:": "Registrieren als Gast schlug fehl:", "Guest access is disabled on this Home Server.": "Gastzugang ist auf diesem Heimserver deaktivert.", - "Guest users can't create new rooms. Please register to create room and start a chat.": "Gastnutzer können keine neuen Räume erstellen. Bitte registriere dich um Räume zu erstellen und Chats zu starten.", "Import": "Import", "Incorrect username and/or password.": "Inkorrekter Nutzername und/oder Passwort.", "Results from DuckDuckGo": "Ergebnisse von DuckDuckGo", - "Server unavailable, overloaded, or something else went wrong.": "Server nicht verfügbar, überlastet oder etwas anderes lief falsch.", "The signing key you provided matches the signing key you received from %(userId)s's device %(deviceId)s. Device marked as verified.": "Den Signaturschlüssel den du bereitstellst stimmt mit dem Schlüssel den du von %(userId)s's Gerät %(deviceId)s empfangen hast überein. Gerät als verifiziert markiert.", "Add a topic": "Thema hinzufügen", "Anyone": "Jeder", @@ -879,13 +873,9 @@ "Verified key": "Verifizierter Schlüssel", "WARNING: Device already verified, but keys do NOT MATCH!": "WARNUNG: Gerät bereits verifiziert, aber Schlüssel sind NICHT GLEICH!", "WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and device %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!": "WARNUNG: SCHLÜSSEL-VERIFIZIERUNG FEHLGESCHLAGEN! Der Signatur-Schlüssel für %(userId)s und Gerät %(deviceId)s ist \"%(fprint)s\" welche nicht dem bereitgestellten Schlüssel \"%(fingerprint)s\" übereinstimmen. Dies kann bedeuten, dass deine Kommunikation abgefangen wird!", - "You are already in a call.": "Du bist bereits in einem Gespräch.", - "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 keine VoIP-Gespräche in diesem Browser starten.", "You have disabled URL previews by default.": "Du hast die URL-Vorschau standardmäßig deaktiviert.", "You have enabled URL previews by default.": "Du hast die URL-Vorschau standardmäßig aktiviert.", "You have entered an invalid contact. Try using their Matrix ID or email address.": "Du hast einen ungültigen Kontakt eingegeben. Versuche es mit der Matrix-ID oder der E-Mail-Adresse.", - "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 Heimserver verbunden zu sein.", "$senderDisplayName changed the room avatar to ": "$senderDisplayName hat das Raum-Bild geändert zu ", "%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s hat das Raum-Bild für %(roomName)s geändert", "Hide removed messages": "Gelöschte Nachrichten verbergen", @@ -896,7 +886,6 @@ "%(count)s new messages.other": "%(count)s neue Nachrichten", "Error: Problem communicating with the given homeserver.": "Fehler: Problem beim kommunizieren mit dem angegebenen Heimserver.", "Failed to fetch avatar URL": "Fehler beim holen der Avatar-URL", - "Some of your messages have not been sent.": "Einige deiner Nachrichten wurden nicht gesendet.", "The phone number entered looks invalid": "Die Telefonnummer, die eingegeben wurde, sieht ungültig aus", "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 betretbar. Du kannst evtl. beitreten 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.": "Es wurde versucht einen spezifischen Punkt in der Chat-Historie zu laden, aber du hast keine Berechtigung diese Nachricht zu sehen.", From db647fa6678128d11ea7e5cf81d3c061dba8302e Mon Sep 17 00:00:00 2001 From: Pitchaya Boonsarngsuk Date: Wed, 7 Jun 2017 22:05:27 +0000 Subject: [PATCH 0656/1016] Translated using Weblate (Thai) Currently translated at 56.2% (479 of 852 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/th/ --- src/i18n/strings/th.json | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/th.json b/src/i18n/strings/th.json index 877a177c17..f97b6b8d50 100644 --- a/src/i18n/strings/th.json +++ b/src/i18n/strings/th.json @@ -229,7 +229,7 @@ "Leave room": "ออกจากห้อง", "left and rejoined": "ออกแล้วกลับเข้าร่วมอีกครั้ง", "left": "ออกไปแล้ว", - "%(targetName)s left the room.": "%(targetName)s ออกจากห้องไปแล้ว", + "%(targetName)s left the room.": "%(targetName)s ออกจากห้องแล้ว", "List this room in %(domain)s's room directory?": "แสดงห้องนี้ในไดเรกทอรีห้องของ %(domain)s?", "Logged in as:": "เข้าสู่ระบบในชื่อ:", "Login as guest": "เข้าสู่ระบบในฐานะแขก", @@ -474,5 +474,9 @@ "Default server": "เซิร์ฟเวอร์เริ่มต้น", "Custom server": "เซิร์ฟเวอร์ที่กำหนดเอง", "Home server URL": "URL เซิร์ฟเวอร์บ้าน", - "Identity server URL": "URL เซิร์ฟเวอร์ระบุตัวตน" + "Identity server URL": "URL เซิร์ฟเวอร์ระบุตัวตน", + "%(severalUsers)sleft %(repeats)s times": "%(targetName)sออกจากห้อง %(repeats)s ครั้ง", + "%(oneUser)sleft %(repeats)s times": "%(oneUser)sออกจากห้อง %(repeats)s ครั้ง", + "%(severalUsers)sleft": "%(severalUsers)sออกจากห้องแล้ว", + "%(oneUser)sleft": "%(oneUser)sออกจากห้องแล้ว" } From 2bed5bae174eeb045f81f12f3616c9b5cac23f4f Mon Sep 17 00:00:00 2001 From: Bamstam Date: Wed, 7 Jun 2017 22:03:11 +0000 Subject: [PATCH 0657/1016] Translated using Weblate (German) Currently translated at 99.7% (851 of 853 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/de/ --- src/i18n/strings/de_DE.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index 2845a5021c..7013ccabb6 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -909,5 +909,6 @@ "Username not available": "Nutzername nicht verfügbar", "Something went wrong!": "Etwas ging schief!", "This will be your account name on the homeserver, or you can pick a different server.": "Dies wird dein Konto-Name auf dem Heimserver, oder du kannst einen anderen Server auswählen.", - "If you already have a Matrix account you can log in instead.": "Wenn du bereits ein Matrix-Benutzerkonto hast, kannst du dich stattdessen auch direkt anmelden." + "If you already have a Matrix account you can log in instead.": "Wenn du bereits ein Matrix-Benutzerkonto hast, kannst du dich stattdessen auch direkt anmelden.", + "Home": "Start" } From 5686eb75d46eb7629ad8f4083d21470dfe91c35d Mon Sep 17 00:00:00 2001 From: Bamstam Date: Wed, 7 Jun 2017 22:03:11 +0000 Subject: [PATCH 0658/1016] Translated using Weblate (German) Currently translated at 99.7% (851 of 853 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/de/ --- src/i18n/strings/de_DE.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index 7013ccabb6..b81b3ef1e4 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -910,5 +910,6 @@ "Something went wrong!": "Etwas ging schief!", "This will be your account name on the homeserver, or you can pick a different server.": "Dies wird dein Konto-Name auf dem Heimserver, oder du kannst einen anderen Server auswählen.", "If you already have a Matrix account you can log in instead.": "Wenn du bereits ein Matrix-Benutzerkonto hast, kannst du dich stattdessen auch direkt anmelden.", - "Home": "Start" + "Home": "Start", + "Username invalid: %(errMessage)s": "Nutzername falsch:" } From 811901097d77ff50d75b4427cc7d6fd5a1249b09 Mon Sep 17 00:00:00 2001 From: Bamstam Date: Wed, 7 Jun 2017 22:03:11 +0000 Subject: [PATCH 0659/1016] Translated using Weblate (German) Currently translated at 100.0% (853 of 853 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/de/ --- 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 b81b3ef1e4..4102d7be56 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -911,5 +911,5 @@ "This will be your account name on the homeserver, or you can pick a different server.": "Dies wird dein Konto-Name auf dem Heimserver, oder du kannst einen anderen Server auswählen.", "If you already have a Matrix account you can log in instead.": "Wenn du bereits ein Matrix-Benutzerkonto hast, kannst du dich stattdessen auch direkt anmelden.", "Home": "Start", - "Username invalid: %(errMessage)s": "Nutzername falsch:" + "Username invalid: %(errMessage)s": "Nutzername falsch: %(errMessage)s" } From ec9a436625d70e4af616a4fadb19d16d9577bdef Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 8 Jun 2017 11:56:48 +0100 Subject: [PATCH 0660/1016] Formatting --- src/Login.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Login.js b/src/Login.js index 87731744e9..659fdb92bb 100644 --- a/src/Login.js +++ b/src/Login.js @@ -161,8 +161,7 @@ export default class Login { error.friendlyText = ( _t('This Home Server does not support login using email address.') ); - } - else if (error.httpStatus === 403) { + } else if (error.httpStatus === 403) { error.friendlyText = ( _t('Incorrect username and/or password.') ); @@ -185,8 +184,7 @@ export default class Login { throw error; }); } - } - else { + } else { error.friendlyText = ( _t("There was a problem logging in.") + ' (HTTP ' + error.httpStatus + ")" ); From c9f723b16052d2e215ba716ab23196dbd7c9ae70 Mon Sep 17 00:00:00 2001 From: RiotTranslate Date: Thu, 8 Jun 2017 13:05:02 +0200 Subject: [PATCH 0661/1016] Update from Weblate. (#1053) * Added translation using Weblate (Thai) * Translated using Weblate (Thai) Currently translated at 2.6% (22 of 827 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/th/ * Translated using Weblate (Thai) Currently translated at 8.9% (74 of 827 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/th/ * Translated using Weblate (German) Currently translated at 99.8% (826 of 827 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/de/ * Translated using Weblate (Swedish) Currently translated at 35.4% (293 of 827 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/sv/ * Translated using Weblate (Thai) Currently translated at 9.7% (81 of 827 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/th/ * Translated using Weblate (German) Currently translated at 100.0% (827 of 827 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/de/ * Translated using Weblate (Russian) Currently translated at 76.9% (636 of 827 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/ru/ * Translated using Weblate (Hungarian) Currently translated at 0.1% (1 of 827 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/hu/ * Translated using Weblate (Thai) Currently translated at 39.2% (325 of 827 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/th/ * Translated using Weblate (German) Currently translated at 99.8% (827 of 828 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/de/ * Translated using Weblate (German) Currently translated at 99.8% (827 of 828 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/de/ * Translated using Weblate (Thai) Currently translated at 39.7% (329 of 828 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/th/ * Translated using Weblate (German) Currently translated at 99.8% (828 of 828 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/de/ * Translated using Weblate (Hungarian) Currently translated at 3.1% (26 of 828 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/hu/ * Translated using Weblate (Russian) Currently translated at 77.4% (641 of 828 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/ru/ * Translated using Weblate (Thai) Currently translated at 39.7% (329 of 828 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/th/ * Translated using Weblate (Russian) Currently translated at 100.0% (828 of 828 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/ru/ * Translated using Weblate (German) Currently translated at 100.0% (828 of 828 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/de/ * Translated using Weblate (Russian) Currently translated at 100.0% (828 of 828 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/ru/ * Translated using Weblate (German) Currently translated at 100.0% (850 of 850 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/de/ * Translated using Weblate (French) Currently translated at 96.5% (821 of 850 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/fr/ * Translated using Weblate (Thai) Currently translated at 39.0% (332 of 850 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/th/ * Translated using Weblate (French) Currently translated at 97.0% (825 of 850 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/fr/ * Translated using Weblate (German) Currently translated at 100.0% (850 of 850 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/de/ * Translated using Weblate (Thai) Currently translated at 40.8% (347 of 850 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/th/ * Translated using Weblate (French) Currently translated at 100.0% (850 of 850 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/fr/ * Translated using Weblate (German) Currently translated at 100.0% (850 of 850 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/de/ * Translated using Weblate (Thai) Currently translated at 49.5% (421 of 850 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/th/ * Translated using Weblate (Thai) Currently translated at 56.0% (476 of 850 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/th/ * Translated using Weblate (Russian) Currently translated at 100.0% (852 of 852 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/ru/ * Translated using Weblate (German) Currently translated at 99.7% (850 of 852 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/de/ * Translated using Weblate (Thai) Currently translated at 56.2% (479 of 852 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/th/ * Translated using Weblate (German) Currently translated at 99.7% (851 of 853 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/de/ * Translated using Weblate (German) Currently translated at 99.7% (851 of 853 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/de/ * Translated using Weblate (German) Currently translated at 100.0% (853 of 853 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/de/ --- src/i18n/strings/de_DE.json | 62 +++++++------- src/i18n/strings/fr.json | 31 ++++++- src/i18n/strings/ru.json | 46 +++++++++-- src/i18n/strings/th.json | 156 +++++++++++++++++++++++++++++++++++- 4 files changed, 247 insertions(+), 48 deletions(-) diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index f7b3d16ebc..4102d7be56 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -114,7 +114,7 @@ "Failed to change password. Is your password correct?": "Passwortänderung fehlgeschlagen. Ist dein Passwort richtig?", "Failed to forget room": "Vergessen des Raums schlug fehl", "Failed to leave room": "Verlassen des Raums fehlgeschlagen", - "Failed to reject invitation": "Fehler beim Abweisen der Einladung", + "Failed to reject invitation": "Einladung konnte nicht abgelehnt werden", "Failed to set avatar.": "Fehler beim Setzen des Profilbilds.", "Failed to unban": "Entbannen fehlgeschlagen", "Failed to upload file": "Datei-Upload fehlgeschlagen", @@ -127,7 +127,7 @@ "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 wurden.", "Guests can't set avatars. Please register.": "Gäste können kein Profilbild setzen. Bitte registrieren.", - "Guest users can't upload files. Please register to upload.": "Gäste können keine Dateien hochladen. Bitte registrieren um hochzuladen.", + "Guest users can't upload files. Please register to upload.": "Gäste können keine Dateien hochladen. Bitte zunächst registrieren.", "had": "hatte", "Hangup": "Auflegen", "Homeserver is": "Der Homeserver ist", @@ -182,7 +182,7 @@ "Once you've followed the link it contains, click below": "Nachdem du dem darin enthaltenen Link gefolgt bist, klicke unten", "rejected the invitation.": "lehnte die Einladung ab.", "Reject invitation": "Einladung ablehnen", - "Remove Contact Information?": "Kontakt-Informationen löschen?", + "Remove Contact Information?": "Kontakt-Informationen entfernen?", "removed their display name": "löschte den eigenen Anzeigenamen", "Remove": "Entfernen", "requested a VoIP conference": "hat eine VoIP-Konferenz angefordert", @@ -227,7 +227,7 @@ "to join the discussion": "um an der Diskussion teilzunehmen", "To kick users": "Um Nutzer zu entfernen", "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.", + "Server may be unavailable, overloaded, or you hit a bug.": "Server ist nicht verfügbar, überlastet oder du bist auf einen Fehler gestoßen.", "Could not connect to the integration server": "Konnte keine Verbindung zum Integrations-Server herstellen", "Disable inline URL previews by default": "URL-Vorschau im Chat standardmäßig deaktivieren", "Guests can't use labs features. Please register.": "Gäste können keine Labor-Funktionen nutzen. Bitte registrieren.", @@ -271,7 +271,7 @@ "Who would you like to communicate with?": "Mit wem möchtest du kommunizieren?", "Would you like to": "Möchtest du", "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 von %(inviterName)s in diesen Raum eingeladen", + "You have been invited to join this room by %(inviterName)s": "%(inviterName)s hat dich in diesen Raum eingeladen", "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 auf allen Geräten abgemeldet und wirst keine Push-Benachrichtigungen mehr erhalten. Um die Benachrichtigungen zu reaktivieren, musst du dich auf jedem Gerät neu anmelden", "you must be a": "nötige Rolle", "Your password has been reset": "Dein Passwort wurde zurückgesetzt", @@ -281,14 +281,14 @@ "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 calling is in development and may not be reliable.": "Konferenzgespräche befinden sich noch in der Entwicklungsphase und sind möglicherweise nicht zuverlässig nutzbar.", "Conference calls are not supported in encrypted rooms": "Konferenzgespräche werden in verschlüsselten Räumen nicht unterstützt", "Conference calls are not supported in this client": "Konferenzgespräche werden von diesem Client nicht unterstützt", "Existing Call": "Bereits bestehender Anruf", "Failed to set up conference call": "Konferenzgespräch konnte nicht gestartet werden", "Failed to verify email address: make sure you clicked the link in the email": "Verifizierung der E-Mail-Adresse fehlgeschlagen: Bitte stelle sicher, dass du den Link in der E-Mail angeklickt hast", "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.", + "Guest users can't create new rooms. Please register to create room and start a chat.": "Gastnutzer können keine neuen Räume erstellen. Bitte registriere dich um Räume zu erstellen und Chats 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", @@ -302,11 +302,11 @@ "Unable to enable Notifications": "Benachrichtigungen konnten nicht aktiviert werden", "Upload Failed": "Upload fehlgeschlagen", "VoIP is unsupported": "VoIP wird nicht unterstützt", - "You are already in a call.": "Du bist bereits bei einem Anruf.", + "You are already in a call.": "Du bist bereits in einem Gespräch.", "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 cannot place VoIP calls in this browser.": "Du kannst keine VoIP-Gespräche 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üsselungs-Schlüssel für dieses Gerät zu generieren und um den öffentlichen Schlüssel auf deinem Homeserver zu hinterlegen. Dies muss nur einmal durchgeführt werden, bitte entschuldige die Unannehmlichkeiten.", - "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.", + "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 Heimserver verbunden zu sein.", "Sun": "So", "Mon": "Mo", "Tue": "Di", @@ -377,7 +377,7 @@ "Power level must be positive integer.": "Berechtigungslevel muss eine positive ganze Zahl sein.", "Reason": "Grund", "%(targetName)s rejected the invitation.": "%(targetName)s hat die Einladung abgelehnt.", - "%(senderName)s removed their display name (%(oldDisplayName)s).": "%(senderName)s löschte den Anzeigenamen (%(oldDisplayName)s).", + "%(senderName)s removed their display name (%(oldDisplayName)s).": "%(senderName)s hat den Anzeigenamen entfernt (%(oldDisplayName)s).", "%(senderName)s removed their profile picture.": "%(senderName)s hat das Profilbild gelöscht.", "%(senderName)s requested a VoIP conference.": "%(senderName)s möchte eine VoIP-Konferenz beginnen.", "Room %(roomId)s not visible": "Raum %(roomId)s ist nicht sichtbar", @@ -418,7 +418,7 @@ "Press": "Drücke", "tag as %(tagName)s": "als %(tagName)s taggen", "to browse the directory": "um das Raum-Verzeichnis zu durchsuchen", - "to demote": "um die Priorität herabzusetzen", + "to demote": "um das Berechtigungslevel herabzusetzen", "to favourite": "zum Favorisieren", "to make a room or": "um einen Raum zu erstellen, oder", "to restore": "zum wiederherstellen", @@ -581,7 +581,6 @@ "Failed to save settings": "Einstellungen konnten nicht gespeichert werden", "Failed to set display name": "Anzeigename konnte nicht gesetzt werden", "Fill screen": "Fülle Bildschirm", - "Guest users can't upload files. Please register to upload.": "Gäste können keine Dateien hochladen. Bitte zunächst registrieren.", "Hide Text Formatting Toolbar": "Verberge Text-Formatierungs-Toolbar", "Incorrect verification code": "Falscher Verifizierungscode", "Invalid alias format": "Ungültiges Alias-Format", @@ -608,16 +607,17 @@ "Server error": "Server-Fehler", "Server may be unavailable, overloaded, or search timed out :(": "Der Server ist entweder nicht verfügbar, überlastet oder die Suche wurde wegen Zeitüberschreitung abgebrochen :(", "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.": "Der Server ist entweder nicht verfügbar, überlastet oder es liegt ein anderweitiger Fehler vor.", - "Some of your messages have not been sent.": "Einige deiner Nachrichten wurden noch nicht gesendet.", + "Server unavailable, overloaded, or something else went wrong.": "Server nicht verfügbar, überlastet oder etwas anderes lief falsch.", + "Some of your messages have not been sent.": "Einige deiner Nachrichten wurden 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", - "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.": "Der Versuch, einen spezifischen Punkt im Chatverlauf zu laden, ist fehlgeschlagen. Der Punkt konnte nicht gefunden werden.", - "Turn Markdown off": "Markdown abschalten", + "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 zugänglich. Du kannst jedoch eventuell beitreten, 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": "Der Versuch, einen spezifischen Punkt im Chatverlauf zu laden, ist fehlgeschlagen. Der Punkt konnte nicht gefunden werden", + "Turn Markdown off": "Markdown deaktiveren", "Turn Markdown on": "Markdown einschalten", "Unable to load device list": "Geräteliste konnte nicht geladen werden", "Unknown command": "Unbekannter Befehl", @@ -725,7 +725,7 @@ "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.": "Eine Änderung des Passworts setzt derzeit alle Schlüssel für die E2E-Verschlüsselung auf allen verwendeten Geräten zurück. Bereits verschlüsselte Chat-Inhalte sind somit nur noch lesbar, wenn du zunächst alle Schlüssel exportierst und später wieder importierst. Wir arbeiten an einer Verbesserung dieser momentan noch notwendigen Vorgehensweise.", "Unmute": "Stummschalten aufheben", "Invalid file%(extra)s": "Ungültige Datei%(extra)s", - "Remove %(threePid)s?": "Entferne %(threePid)s?", + "Remove %(threePid)s?": "%(threePid)s entfernen?", "Please select the destination room for this message": "Bitte den Raum auswählen, an den diese Nachricht gesendet werden soll", "%(senderDisplayName)s removed the room name.": "%(senderDisplayName)s hat den Raum-Namen gelöscht.", "Passphrases must match": "Passphrase muss übereinstimmen", @@ -735,7 +735,6 @@ "Confirm passphrase": "Bestätige Passphrase", "The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.": "Die Export-Datei wird mit einer Passphrase geschützt sein. Du solltest die Passphrase hier eingeben um die Datei zu entschlüsseln.", "You must join the room to see its files": "Du musst dem Raum beitreten, um die Raum-Dateien sehen zu können", - "Server may be unavailable, overloaded, or you hit a bug.": "Server ist nicht verfügbar, überlastet oder du bist auf einen Fehler gestoßen.", "Reject all %(invitedRooms)s invites": "Alle %(invitedRooms)s Einladungen ablehnen", "Start new Chat": "Starte neuen Chat", "Guest users can't invite users. Please register.": "Gäste können keine Nutzer einladen. Bitte registrieren.", @@ -785,7 +784,7 @@ "This image cannot be displayed.": "Dieses Bild kann nicht angezeigt werden.", "Error decrypting video": "Video-Entschlüsselung fehlgeschlagen", "Import room keys": "Importiere Raum-Schlüssel", - "File to import": "Datei zum Importieren", + "File to import": "Zu importierende Datei", "Failed to invite the following users to the %(roomName)s room:": "Das Einladen der folgenden Nutzer in den Raum \"%(roomName)s\" ist fehlgeschlagen:", "Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change.": "Bist du sicher, dass du dieses Ereignis entfernen (löschen) möchtest? Wenn du die Änderung eines Raum-Namens oder eines Raum-Themas löscht, kann dies dazu führen, dass die ursprüngliche Änderung rückgängig gemacht wird.", "This process allows you to export the keys for messages you have received in encrypted rooms to a local file. You will then be able to import the file into another Matrix client in the future, so that client will also be able to decrypt these messages.": "Dieser Prozess erlaubt es dir, die Schlüssel für in verschlüsselten Räumen empfangene Nachrichten in eine lokale Datei zu exportieren. In Zukunft wird es möglich sein, diese Datei in einen anderen Matrix-Client zu importieren, sodass dieser Client ebenfalls diese Nachrichten entschlüsseln kann.", @@ -827,7 +826,7 @@ "Invited": "Eingeladen", "Set a Display Name": "Setze einen Anzeigenamen", "for %(amount)ss": "für %(amount)ss", - "for %(amount)sm": "für %(amount)sm", + "for %(amount)sm": "seit %(amount)smin", "for %(amount)sh": "für %(amount)sh", "for %(amount)sd": "für %(amount)sd", "%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s hat das Raum-Bild entfernt.", @@ -840,17 +839,13 @@ "Default Device": "Standard-Gerät", "Microphone": "Mikrofon", "Camera": "Kamera", - "Conference call failed.": "Konferenzgespräch fehlgeschlagen.", - "Conference calling is in development and may not be reliable.": "Konferenzgespräche befinden sich noch in der Entwicklungsphase und sind möglicherweise nicht zuverlässig nutzbar.", "Device already verified!": "Gerät bereits verifiziert!", "Export": "Export", "Failed to register as guest:": "Registrieren als Gast schlug fehl:", "Guest access is disabled on this Home Server.": "Gastzugang ist auf diesem Heimserver deaktivert.", - "Guest users can't create new rooms. Please register to create room and start a chat.": "Gastnutzer können keine neuen Räume erstellen. Bitte registriere dich um Räume zu erstellen und Chats zu starten.", "Import": "Import", "Incorrect username and/or password.": "Inkorrekter Nutzername und/oder Passwort.", "Results from DuckDuckGo": "Ergebnisse von DuckDuckGo", - "Server unavailable, overloaded, or something else went wrong.": "Server nicht verfügbar, überlastet oder etwas anderes lief falsch.", "The signing key you provided matches the signing key you received from %(userId)s's device %(deviceId)s. Device marked as verified.": "Den Signaturschlüssel den du bereitstellst stimmt mit dem Schlüssel den du von %(userId)s's Gerät %(deviceId)s empfangen hast überein. Gerät als verifiziert markiert.", "Add a topic": "Thema hinzufügen", "Anyone": "Jeder", @@ -878,13 +873,9 @@ "Verified key": "Verifizierter Schlüssel", "WARNING: Device already verified, but keys do NOT MATCH!": "WARNUNG: Gerät bereits verifiziert, aber Schlüssel sind NICHT GLEICH!", "WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and device %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!": "WARNUNG: SCHLÜSSEL-VERIFIZIERUNG FEHLGESCHLAGEN! Der Signatur-Schlüssel für %(userId)s und Gerät %(deviceId)s ist \"%(fprint)s\" welche nicht dem bereitgestellten Schlüssel \"%(fingerprint)s\" übereinstimmen. Dies kann bedeuten, dass deine Kommunikation abgefangen wird!", - "You are already in a call.": "Du bist bereits in einem Gespräch.", - "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 keine VoIP-Gespräche in diesem Browser starten.", "You have disabled URL previews by default.": "Du hast die URL-Vorschau standardmäßig deaktiviert.", "You have enabled URL previews by default.": "Du hast die URL-Vorschau standardmäßig aktiviert.", "You have entered an invalid contact. Try using their Matrix ID or email address.": "Du hast einen ungültigen Kontakt eingegeben. Versuche es mit der Matrix-ID oder der E-Mail-Adresse.", - "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 Heimserver verbunden zu sein.", "$senderDisplayName changed the room avatar to ": "$senderDisplayName hat das Raum-Bild geändert zu ", "%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s hat das Raum-Bild für %(roomName)s geändert", "Hide removed messages": "Gelöschte Nachrichten verbergen", @@ -895,17 +886,16 @@ "%(count)s new messages.other": "%(count)s neue Nachrichten", "Error: Problem communicating with the given homeserver.": "Fehler: Problem beim kommunizieren mit dem angegebenen Heimserver.", "Failed to fetch avatar URL": "Fehler beim holen der Avatar-URL", - "Some of your messages have not been sent.": "Einige deiner Nachrichten wurden nicht gesendet.", "The phone number entered looks invalid": "Die Telefonnummer, die eingegeben wurde, sieht ungültig aus", "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 betretbar. Du kannst evtl. beitreten 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.": "Es wurde versucht einen spezifischen Punkt in der Chat-Historie zu laden, aber du hast keine Berechtigung diese Nachricht zu sehen.", "Tried to load a specific point in this room's timeline, but was unable to find it.": "Es wurde versucht einen spezifischen Punkt in der Chat-Historie zu laden, aber er konnte nicht gefunden werden.", "Uploading %(filename)s and %(count)s others.zero": "%(filename)s wird hochgeladen", - "Uploading %(filename)s and %(count)s others.one": "%(filename)s und %(count)s weitere werden hochgeladen", - "Uploading %(filename)s and %(count)s others.other": "%(filename)s und %(count)s weitere werden hochgeladen", + "Uploading %(filename)s and %(count)s others.one": "%(filename)s und %(count)s weitere Dateien werden hochgeladen", + "Uploading %(filename)s and %(count)s others.other": "%(filename)s und %(count)s weitere Dateien werden hochgeladen", "You must register to use this functionality": "Du musst dich registrieren um diese Funktionalität zu nutzen", "Resend all or cancel all now. You can also select individual messages to resend or cancel.": "Sende erneut oder breche alles ab. Du kannst auch auch individuelle Nachrichten erneut senden or abbrechen.", - "Create new room": "Erstelle neuen Raum", + "Create new room": "Neuen Raum erstellen", "Welcome page": "Willkommensseite", "Room directory": "Raum-Verzeichnis", "Start chat": "Starte Chat", @@ -919,5 +909,7 @@ "Username not available": "Nutzername nicht verfügbar", "Something went wrong!": "Etwas ging schief!", "This will be your account name on the homeserver, or you can pick a different server.": "Dies wird dein Konto-Name auf dem Heimserver, oder du kannst einen anderen Server auswählen.", - "If you already have a Matrix account you can log in instead.": "Wenn du bereits ein Matrix-Konto hast, kannst du ansonsten auch anmelden." + "If you already have a Matrix account you can log in instead.": "Wenn du bereits ein Matrix-Benutzerkonto hast, kannst du dich stattdessen auch direkt anmelden.", + "Home": "Start", + "Username invalid: %(errMessage)s": "Nutzername falsch: %(errMessage)s" } diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index ecbb20fbb7..ffd1ef5885 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -829,5 +829,34 @@ "Hide removed messages": "Cacher les messages supprimés", "Add": "Ajouter", "%(count)s new messages.one": "%(count)s nouveau message", - "%(count)s new messages.other": "%(count)s nouveaux messages" + "%(count)s new messages.other": "%(count)s nouveaux messages", + "Disable markdown formatting": "Désactiver le formattage markdown", + "Error: Problem communicating with the given homeserver.": "Erreur: Problème de communication avec le homeserveur.", + "Failed to fetch avatar URL": "Échec lors de la récupération de l’URL de l’avatar", + "The phone number entered looks invalid": "Le numéro de téléphone entré semble être invalide", + "Guest users can't upload files. Please register to upload.": "Les visiteurs ne peuvent pas télécharger de fichier. Veuillez vous enregistrer pour télécharger.", + "Some of your messages have not been sent.": "Certains de vos messages n’ont pas été envoyés.", + "This room is private or inaccessible to guests. You may be able to join if you register.": "Ce salon est privé ou interdits aux visiteurs. Vous pourrez peut-être le joindre si vous vous enregistrez.", + "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.": "Un instant donné de la chronologie n’a pu être chargé car vous n’avez pas la permission de le visualiser.", + "Tried to load a specific point in this room's timeline, but was unable to find it.": "Un instant donné de la chronologie n’a pu être chargé car il n’a pas pu être trouvé.", + "Uploading %(filename)s and %(count)s others.zero": "Téléchargement de %(filename)s", + "Uploading %(filename)s and %(count)s others.one": "Téléchargement de %(filename)s et %(count)s autre", + "Uploading %(filename)s and %(count)s others.other": "Téléchargement de %(filename)s et %(count)s autres", + "You must register to use this functionality": "Vous devez vous inscrire pour utiliser cette fonctionnalité", + "Resend all or cancel all now. You can also select individual messages to resend or cancel.": "Tout renvoyer or tout annuler maintenant. Vous pouvez aussi sélectionner des messages individuels à envoyer ou annuler.", + "Create new room": "Créer un nouveau salon", + "Welcome page": "Page d'accueil", + "Room directory": "Répertoire des salons", + "Start chat": "Démarrer une discussion", + "New Password": "Nouveau mot de passe", + "Start chatting": "Démarrer une discussion", + "Start Chatting": "Démarrer une discussion", + "Click on the button below to start chatting!": "Cliquer sur le bouton ci-dessous pour commencer une discussion !", + "Create a new chat or reuse an existing one": "Démarrer une nouvelle discussion ou en réutiliser une existante", + "You already have existing direct chats with this user:": "Vous avez déjà des discussions en cours avec cet utilisateur :", + "Username available": "Nom d'utilisateur disponible", + "Username not available": "Nom d'utilisateur indisponible", + "Something went wrong!": "Quelque chose s’est mal passé !", + "This will be your account name on the homeserver, or you can pick a different server.": "Cela sera le nom de votre compte sur le serveur , ou vous pouvez sélectionner un autre serveur.", + "If you already have a Matrix account you can log in instead.": "Si vous avez déjà un compte Matrix vous pouvez vous identifier à la place." } diff --git a/src/i18n/strings/ru.json b/src/i18n/strings/ru.json index 85b7a6a7f5..647fbfe1d5 100644 --- a/src/i18n/strings/ru.json +++ b/src/i18n/strings/ru.json @@ -92,14 +92,14 @@ "Found a bug?": "Нашли ошибку?", "had": "имеет", "Hangup": "Отключение", - "Historical": "Исторический", + "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": "Прегласить новых учасников комнаты", + "Invite new room members": "Пригласить новых учасников в комнату", "Invites": "Приглашать", "Invites user with given id to current room": "Пригласить пользователя с данным id в текущую комнату", "is a": "является", @@ -119,13 +119,13 @@ "Logout": "Выход из системы", "Low priority": "Низкий приоритет", "made future room history visible to": "made future room history visible to", - "Manage Integrations": "Управление интеграций", + "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 from this device": "Никогда не отправлять зашифрованные сообщения на неверифицированные устроства с этого устройства", "Never send encrypted messages to unverified devices in this room from this device": "Никогда не отправляйте зашифрованные сообщения в непроверенные устройства в этой комнате из этого устройства", "New password": "Новый пароль", "New passwords must match each other.": "Новые пароли должны соответствовать друг другу.", @@ -190,7 +190,7 @@ "Voice call": "Голосовой вызов", "VoIP conference finished.": "VoIP конференция закончилась.", "VoIP conference started.": "VoIP Конференция стартовала.", - "(warning: cannot be disabled again!)": "(предупреждение: не может быть снова отключен!)", + "(warning: cannot be disabled again!)": "(предупреждение: не может быть отключено!)", "Warning!": "Предупреждение!", "was banned": "запрещен", "was invited": "приглашенный", @@ -200,7 +200,7 @@ "were": "быть", "Who can access this room?": "Кто может получить доступ к этой комнате?", "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?": "С кем хотели бы Вы связываться?", "withdrawn": "уходить", "Would you like to": "Хотели бы Вы", @@ -658,7 +658,7 @@ "powered by Matrix": "управляемый с Matrix", "Add a topic": "Добавить тему", "Show timestamps in 12 hour format (e.g. 2:30pm)": "Времея отображать в 12 часовом формате (напр. 2:30pm)", - "Use compact timeline layout": "Используйте компактным указанием времени", + "Use compact timeline layout": "Компактное отображение", "Hide removed messages": "Скрыть удаленное сообщение", "No Microphones detected": "Микрофоны не обнаружены", "Unknown devices": "Незнакомое устройство", @@ -864,5 +864,35 @@ "Disable URL previews for this room (affects only you)": "Отключить предпросмотр URL в этой комнате (только для вас)", "$senderDisplayName changed the room avatar to ": "$senderDisplayName сменил аватар комнаты на ", "%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s удалил аватар комнаты.", - "%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s сменил аватар для %(roomName)s" + "%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s сменил аватар для %(roomName)s", + "Create new room": "Создать комнату", + "Room directory": "Каталог комнат", + "Start chat": "Начать чат", + "Welcome page": "Домашняя страница", + "Add": "Добавить", + "%(count)s new messages.one": "%(count)s новое сообщение", + "%(count)s new messages.other": "%(count)s новых сообщений", + "Error: Problem communicating with the given homeserver.": "Ошибка: проблема коммуникаций с указанным Home Server'ом.", + "Failed to fetch avatar URL": "Ошибка получения аватара", + "The phone number entered looks invalid": "Введенный номер телефона выглядит неправильным", + "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.": "Ошибка загрузки истории комнаты: запрошенный элемент не найден.", + "Uploading %(filename)s and %(count)s others.zero": "Загрузка %(filename)s", + "Uploading %(filename)s and %(count)s others.one": "Загрузка %(filename)s и %(count)s другой файл", + "Uploading %(filename)s and %(count)s others.other": "Загрузка %(filename)s и %(count)s других файлов", + "Username invalid: %(errMessage)s": "Неверное имя пользователя: %(errMessage)s", + "Searching known users": "Искать известных пользователей", + "You must register to use this functionality": "Вы должны зарегистрироваться для использования этой функции", + "Resend all or cancel all now. You can also select individual messages to resend or cancel.": "Отослать снова или отменить отправку. Вы также можете выбрать на отправку или отмену отдельные сообщения.", + "New Password": "Новый пароль", + "Start chatting": "Начать общение", + "Start Chatting": "Начать общение", + "Click on the button below to start chatting!": "Нажмите на кнопку ниже для того, чтобы начать общение!", + "Create a new chat or reuse an existing one": "Создать новый чат или использовать уже существующий", + "You already have existing direct chats with this user:": "У вас уже есть существующие приватные чаты с этим пользователем:", + "Username available": "Имя пользователя доступно", + "Username not available": "Имя пользователя недоступно", + "Something went wrong!": "Что-то пошло не так!", + "This will be your account name on the homeserver, or you can pick a different server.": "Это будет ваше имя пользователя на , или вы можете выбрать другой сервер.", + "If you already have a Matrix account you can log in instead.": "Если вы уже имеете учетную запись Matrix, то вы можете войти." } diff --git a/src/i18n/strings/th.json b/src/i18n/strings/th.json index 6608219faa..f97b6b8d50 100644 --- a/src/i18n/strings/th.json +++ b/src/i18n/strings/th.json @@ -201,7 +201,7 @@ "Hangup": "วางสาย", "Historical": "ประวัติแชทเก่า", "Homeserver is": "เซิร์ฟเวอร์บ้านคือ", - "Identity Server is": "เซิร์ฟเวอร์ยืนยันตัวตนคือ", + "Identity Server is": "เซิร์ฟเวอร์ระบุตัวตนคือ", "I have verified my email address": "ฉันยืนยันที่อยู่อีเมลแล้ว", "Import": "นำเข้า", "Incorrect username and/or password.": "ชื่อผู้ใช้และ/หรือรหัสผ่านไม่ถูกต้อง", @@ -229,7 +229,7 @@ "Leave room": "ออกจากห้อง", "left and rejoined": "ออกแล้วกลับเข้าร่วมอีกครั้ง", "left": "ออกไปแล้ว", - "%(targetName)s left the room.": "%(targetName)s ออกจากห้องไปแล้ว", + "%(targetName)s left the room.": "%(targetName)s ออกจากห้องแล้ว", "List this room in %(domain)s's room directory?": "แสดงห้องนี้ในไดเรกทอรีห้องของ %(domain)s?", "Logged in as:": "เข้าสู่ระบบในชื่อ:", "Login as guest": "เข้าสู่ระบบในฐานะแขก", @@ -322,7 +322,7 @@ "This is a preview of this room. Room interactions have been disabled": "นี่คือตัวอย่างของห้อง การตอบสนองภายในห้องถูกปิดใช้งาน", "This phone number is already in use": "หมายเลขโทรศัพท์นี้ถูกใช้งานแล้ว", "This room's internal ID is": "ID ภายในของห้องนี้คือ", - "times": "เวลา", + "times": "ครั้ง", "%(oneUser)schanged their name": "%(oneUser)sเปลี่ยนชื่อของเขาแล้ว", "%(severalUsers)schanged their name %(repeats)s times": "%(severalUsers)sเปลี่ยนชื่อของพวกเขา %(repeats)s ครั้ง", "%(oneUser)schanged their name %(repeats)s times": "%(oneUser)sเปลี่ยนชื่อของเขา %(repeats)s ครั้ง", @@ -330,5 +330,153 @@ "Create new room": "สร้างห้องใหม่", "Room directory": "ไดเรกทอรีห้อง", "Start chat": "เริ่มแชท", - "Welcome page": "หน้าต้อนรับ" + "Welcome page": "หน้าต้อนรับ", + "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or enable unsafe scripts.": "ไม่สามารถเชื่อมต่อไปยังเซิร์ฟเวอร์บ้านผ่านทาง HTTP ได้เนื่องจาก URL ที่อยู่บนเบราว์เซอร์เป็น HTTPS กรุณาใช้ HTTPS หรือเปิดใช้งานสคริปต์ที่ไม่ปลอดภัย.", + "%(count)s new messages.one": "มี %(count)s ข้อความใหม่", + "%(count)s new messages.other": "มี %(count)s ข้อความใหม่", + "Disable inline URL previews by default": "ตั้งค่าเริ่มต้นให้ไม่แสดงตัวอย่าง URL ในแชท", + "Disable markdown formatting": "ปิดใช้งานการจัดรูปแบบ markdown", + "End-to-end encryption information": "ข้อมูลการเข้ารหัสจากปลายทางถึงปลายทาง", + "End-to-end encryption is in beta and may not be reliable": "การเข้ารหัสจากปลายทางถึงปลายทางยังอยู่ในเบต้า และอาจพึ่งพาไม่ได้", + "Error: Problem communicating with the given homeserver.": "ข้อผิดพลาด: มีปัญหาในการติดต่อกับเซิร์ฟเวอร์บ้านที่กำหนด", + "Export E2E room keys": "ส่งออกกุญแจถอดรหัส E2E", + "Failed to change power level": "การเปลี่ยนระดับอำนาจล้มเหลว", + "Import E2E room keys": "นำเข้ากุญแจถอดรหัส E2E", + "to favourite": "ไปยังรายการโปรด", + "to demote": "เพื่อลดขั้น", + "The default role for new room members is": "บทบาทเริ่มต้นของสมาชิกใหม่คือ", + "The phone number entered looks invalid": "ดูเหมือนว่าหมายเลขโทรศัพท์ที่กรอกรมาไม่ถูกต้อง", + "The email address linked to your account must be entered.": "กรุณากรอกที่อยู่อีเมลที่เชื่อมกับบัญชีของคุณ", + "The file '%(fileName)s' exceeds this home server's size limit for uploads": "ไฟล์ '%(fileName)s' มีขนาดใหญ่เกินจำกัดของเซิร์ฟเวอร์บ้าน", + "To send messages": "เพื่อส่งข้อความ", + "to start a chat with someone": "เพื่อเริ่มแชทกับผู้อื่น", + "to tag as %(tagName)s": "เพื่อแท็กว่า %(tagName)s", + "to tag direct chat": "เพื่อแทกว่าแชทตรง", + "Turn Markdown off": "ปิด markdown", + "Turn Markdown on": "เปิด markdown", + "%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s).": "%(senderName)s ได้เปิดใช้งานการเข้ารหัสจากปลายทางถึงปลายทาง (อัลกอริทึม%(algorithm)s).", + "Unable to add email address": "ไมาสามารถเพิ่มที่อยู่อีเมล", + "Unable to verify email address.": "ไม่สามารถยืนยันที่อยู่อีเมล", + "Unban": "ปลดแบน", + "%(senderName)s unbanned %(targetName)s.": "%(senderName)s ปลดแบน %(targetName)s แล้ว", + "Unable to capture screen": "ไม่สามารถจับภาพหน้าจอ", + "Unable to enable Notifications": "ไม่สามารถเปิดใช้งานการแจ้งเตือน", + "Unable to load device list": "ไม่สามารถโหลดรายชื่ออุปกรณ์", + "Unencrypted room": "ห้องที่ไม่เข้ารหัส", + "unencrypted": "ยังไม่ได้เข้ารหัส", + "Unknown command": "คำสั่งที่ไม่รู้จัก", + "unknown device": "อุปกรณ์ที่ไม่รู้จัก", + "Unknown room %(roomId)s": "ห้องที่ไม่รู้จัก %(roomId)s", + "Unknown (user, device) pair:": "คู่ (ผู้ใช้, อุปกรณ์) ที่ไม่รู้จัก:", + "unknown": "ไม่รู้จัก", + "Unrecognised command:": "คำสั่งที่ไม่รู้จัก:", + "Unrecognised room alias:": "นามแฝงห้องที่ไม่รู้จัก:", + "Uploading %(filename)s and %(count)s others.zero": "กำลังอัปโหลด %(filename)s", + "Uploading %(filename)s and %(count)s others.one": "กำลังอัปโหลด %(filename)s และอีก %(count)s ไฟล์", + "Uploading %(filename)s and %(count)s others.other": "กำลังอัปโหลด %(filename)s และอีก %(count)s ไฟล์", + "uploaded a file": "อัปโหลดไฟล์", + "Upload Failed": "การอัปโหลดล้มเหลว", + "Upload Files": "อัปโหลดไฟล์", + "Upload file": "อัปโหลดไฟล์", + "Usage": "การใช้งาน", + "User ID": "ID ผู้ใช้", + "User Interface": "อินเตอร์เฟสผู้ใช้", + "User name": "ชื่อผู้ใช้", + "User": "ผู้ใช้", + "Warning!": "คำเตือน!", + "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?": "คุณต้องการสื่อสารกับใคร?", + "You're not in any rooms yet! Press": "คุณยังไม่ได้อยู่ในห้องใดเลย! กด", + "You are trying to access %(roomName)s": "คุณกำลังพยายามเข้าสู่ %(roomName)s", + "You have disabled URL previews by default.": "ค่าเริ่มต้นของคุณปิดใช้งานตัวอย่าง URL เอาไว้", + "You have enabled URL previews by default.": "ค่าเริ่มต้นของคุณเปิดใช้งานตัวอย่าง URL เอาไว้", + "you must be a": "คุณต้องเป็น", + "You must register to use this functionality": "คุณต้องลงทะเบียนเพื่อใช้ฟังก์ชันนี้", + "You need to be logged in.": "คุณต้องเข้าสู่ระบบก่อน", + "You need to enter a user name.": "คุณต้องกรอกชื่อผู้ใช้ก่อน", + "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": "การเปลี่ยนรหัสผ่านเสร็จสมบูณณ์ คุณจะไม่ได้รับการแจ้งเตือนบนอุปกรณ์อื่น ๆ จนกว่าคุณจะกลับเข้าสู่ระบบในอุปกรณ์เหล่านั้น", + "Sun": "อา.", + "Mon": "จ.", + "Tue": "อ.", + "Wed": "พ.", + "Thu": "พฤ.", + "Fri": "ศ.", + "Sat": "ส.", + "Jan": "ม.ค.", + "Feb": "ก.พ.", + "Mar": "มี.ค.", + "Apr": "เม.ย.", + "May": "พ.ค.", + "Jun": "มิ.ย.", + "Jul": "ก.ค.", + "Aug": "ส.ค.", + "Sep": "ก.ย.", + "Oct": "ต.ค.", + "Nov": "พ.ย.", + "Dec": "ธ.ค.", + "%(weekDayName)s, %(monthName)s %(day)s %(time)s": "%(weekDayName)s %(day)s %(monthName)s %(time)s", + "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s %(day)s %(monthName)s %(fullYear)s %(time)s", + "%(weekDayName)s %(time)s": "%(weekDayName)s %(time)s", + "Set a display name:": "ตั้งชื่อที่แสดง:", + "Set a Display Name": "ตั้งชื่อที่แสดง", + "Passwords don't match.": "รหัสผ่านไม่ตรงกัน", + "Password too short (min %(MIN_PASSWORD_LENGTH)s).": "รหัสผ่านสั้นเกินไป (ขึ้นต่ำ %(MIN_PASSWORD_LENGTH)s ตัวอักษร)", + "An unknown error occurred.": "เกิดข้อผิดพลาดที่ไม่รู้จัก", + "I already have an account": "ฉันมีบัญชีอยู่แล้ว", + "An error occured: %(error_string)s": "เกิดข้อผิดพลาด: %(error_string)s", + "Topic": "หัวข้อ", + "Make Moderator": "เลื่อนขั้นเป็นผู้ช่วยดูแล", + "Make this room private": "ทำให้ห้องนี้เป็นส่วนตัว", + "Share message history with new users": "แบ่งประวัติแชทให้ผู้ใช้ใหม่", + "Encrypt room": "เข้ารหัสห้อง", + "Room": "ห้อง", + "(~%(searchCount)s results)": "(~%(searchCount)s ผลลัพธ์)", + "or": "หรือ", + "bold": "หนา", + "italic": "เอียง", + "strike": "ขีดทับ", + "underline": "ขีดเส้นใต้", + "code": "โค๊ด", + "quote": "อ้างอิง", + "were kicked %(repeats)s times": "ถูกเตะ %(repeats)s ครั้ง", + "was kicked %(repeats)s times": "ถูกเตะ %(repeats)s ครั้ง", + "were kicked": "ถูกเตะ", + "was kicked": "ถูกเตะ", + "New Password": "รหัสผ่านใหม่", + "Options": "ตัวเลือก", + "Export room keys": "ส่งออกกุณแจห้อง", + "Confirm passphrase": "ยืนยันรหัสผ่าน", + "Import room keys": "นำเข้ากุณแจห้อง", + "File to import": "ไฟล์ที่จะนำเข้า", + "Start new chat": "เริ่มแชทใหม่", + "Failed to invite": "การเชิญล้มเหลว", + "Failed to invite user": "การเชิญผู้ใช้ล้มเหลว", + "Failed to invite the following users to the %(roomName)s room:": "การเชิญผู้ใช้เหล่านี้เข้าสู่ห้อง %(roomName)s ล้มเหลว:", + "Confirm Removal": "ยืนยันการลบ", + "Unknown error": "ข้อผิดพลาดที่ไม่รู้จัก", + "Incorrect password": "รหัสผ่านไม่ถูกต้อง", + "Device name": "ชื่ออุปกรณ์", + "Device key": "Key อุปกรณ์", + "Unknown devices": "อุปกรณ์ที่ไม่รู้จัก", + "Unknown Address": "ที่อยู่ที่ไม่รู้จัก", + "Unblacklist": "ถอดบัญชีดำ", + "Blacklist": "ขึ้นบัญชีดำ", + "ex. @bob:example.com": "เช่น @bob:example.com", + "Add User": "เพิ่มผู้ใช้", + "This Home Server would like to make sure you are not a robot": "เซิร์ฟเวอร์บ้านต้องการยืนยันว่าคุณไม่ใช่หุ่นยนต์", + "Sign in with CAS": "เข้าสู่ระบบด้วย CAS", + "You can use the custom server options to sign into other Matrix servers by specifying a different Home server URL.": "คุณสามารถกำหนดเซิร์ฟเวอร์บ้านเองได้โดยใส่ URL ของเซิร์ฟเวอร์นั้น เพื่อเข้าสู่ระบบของเซิร์ฟเวอร์ Matrix อื่น", + "This allows you to use this app with an existing Matrix account on a different home server.": "ทั้งนี่เพื่อให้คุณสามารถใช้ Riot กับบัญชี Matrix ที่มีอยู่แล้วบนเซิร์ฟเวอร์บ้านอื่น ๆ ได้", + "You can also set a custom identity server but this will typically prevent interaction with users based on email address.": "คุณอาจเลือกเซิร์ฟเวอร์ระบุตัวตนเองด้วยก็ได้ แต่คุณจะไม่สามารถเชิญผู้ใช้อื่นด้วยที่อยู่อีเมล หรือรับคำเชิญจากผู้ใช้อื่นทางที่อยู่อีเมลได้", + "Default server": "เซิร์ฟเวอร์เริ่มต้น", + "Custom server": "เซิร์ฟเวอร์ที่กำหนดเอง", + "Home server URL": "URL เซิร์ฟเวอร์บ้าน", + "Identity server URL": "URL เซิร์ฟเวอร์ระบุตัวตน", + "%(severalUsers)sleft %(repeats)s times": "%(targetName)sออกจากห้อง %(repeats)s ครั้ง", + "%(oneUser)sleft %(repeats)s times": "%(oneUser)sออกจากห้อง %(repeats)s ครั้ง", + "%(severalUsers)sleft": "%(severalUsers)sออกจากห้องแล้ว", + "%(oneUser)sleft": "%(oneUser)sออกจากห้องแล้ว" } From 1c58a9cd5e1ef9625fb4a2b753b6639ec00027ce Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Thu, 8 Jun 2017 12:33:29 +0100 Subject: [PATCH 0662/1016] Everything but src/components/views/rooms --- .../views/dialogs/InteractiveAuthDialog.js | 2 +- .../dialogs/SessionRestoreErrorDialog.js | 9 +++++--- .../views/dialogs/SetDisplayNameDialog.js | 2 +- .../views/dialogs/UnknownDeviceDialog.js | 4 ++-- .../views/elements/TruncatedList.js | 3 ++- .../login/InteractiveAuthEntryComponents.js | 2 +- .../views/login/RegistrationForm.js | 4 ++-- .../views/room_settings/ColorSettings.js | 5 +++-- .../views/settings/AddPhoneNumber.js | 2 +- src/components/views/settings/ChangeAvatar.js | 5 +++-- .../views/settings/ChangeDisplayName.js | 3 ++- .../views/settings/ChangePassword.js | 2 +- src/components/views/settings/DevicesPanel.js | 11 +++++----- .../settings/EnableNotificationsButton.js | 5 +++-- src/components/views/voip/CallView.js | 7 +++++- src/components/views/voip/IncomingCallBox.js | 22 +++++++++++++++---- src/i18n/strings/en_EN.json | 22 +++++++++++++++++++ 17 files changed, 80 insertions(+), 30 deletions(-) diff --git a/src/components/views/dialogs/InteractiveAuthDialog.js b/src/components/views/dialogs/InteractiveAuthDialog.js index 945e0a9d69..363ce89b57 100644 --- a/src/components/views/dialogs/InteractiveAuthDialog.js +++ b/src/components/views/dialogs/InteractiveAuthDialog.js @@ -78,7 +78,7 @@ export default React.createClass({ - Dismiss + {_t("Dismiss")}
); diff --git a/src/components/views/dialogs/SessionRestoreErrorDialog.js b/src/components/views/dialogs/SessionRestoreErrorDialog.js index e7f6e19db7..51651b4aaa 100644 --- a/src/components/views/dialogs/SessionRestoreErrorDialog.js +++ b/src/components/views/dialogs/SessionRestoreErrorDialog.js @@ -18,7 +18,7 @@ import React from 'react'; import sdk from '../../../index'; import SdkConfig from '../../../SdkConfig'; import Modal from '../../../Modal'; -import { _t } from '../../../languageHandler'; +import { _t, _tJsx } from '../../../languageHandler'; export default React.createClass({ @@ -44,8 +44,11 @@ export default React.createClass({ if (SdkConfig.get().bug_report_endpoint_url) { bugreport = ( -

Otherwise, - click here to send a bug report. +

+ {_tJsx( + "Otherwise, click here to send a bug report.", + /(.*?)<\/a>/, (sub) => {sub} + )}

); } diff --git a/src/components/views/dialogs/SetDisplayNameDialog.js b/src/components/views/dialogs/SetDisplayNameDialog.js index 1134b1f8cd..0d6f486e0e 100644 --- a/src/components/views/dialogs/SetDisplayNameDialog.js +++ b/src/components/views/dialogs/SetDisplayNameDialog.js @@ -80,7 +80,7 @@ export default React.createClass({ />
- +
diff --git a/src/components/views/dialogs/UnknownDeviceDialog.js b/src/components/views/dialogs/UnknownDeviceDialog.js index 8f91b84519..6ebd0c3efc 100644 --- a/src/components/views/dialogs/UnknownDeviceDialog.js +++ b/src/components/views/dialogs/UnknownDeviceDialog.js @@ -145,7 +145,7 @@ export default React.createClass({ console.log("UnknownDeviceDialog closed by escape"); this.props.onFinished(); }} - title='Room contains unknown devices' + title={_t('Room contains unknown devices')} >

@@ -162,7 +162,7 @@ export default React.createClass({ this.props.onFinished(); Resend.resendUnsentEvents(this.props.room); }}> - Send anyway + {_t("Send anyway")} ); } else { return ( ); } diff --git a/src/components/views/voip/CallView.js b/src/components/views/voip/CallView.js index 6cab7531b7..b53794637f 100644 --- a/src/components/views/voip/CallView.js +++ b/src/components/views/voip/CallView.js @@ -18,6 +18,7 @@ var dis = require("../../../dispatcher"); var CallHandler = require("../../../CallHandler"); var sdk = require('../../../index'); var MatrixClientPeg = require("../../../MatrixClientPeg"); +import { _t } from '../../../languageHandler'; module.exports = React.createClass({ displayName: 'CallView', @@ -130,7 +131,11 @@ module.exports = React.createClass({ var voice; if (this.state.call && this.state.call.type === "voice" && this.props.showVoice) { var callRoom = MatrixClientPeg.get().getRoom(this.state.call.roomId); - voice =
Active call ({ callRoom.name })
; + voice = ( +
+ {_t("Active call (%(roomName)s)", {roomName: callRoom.name})} +
+ ); } return ( diff --git a/src/components/views/voip/IncomingCallBox.js b/src/components/views/voip/IncomingCallBox.js index 6cd19538f0..1b806fc5b3 100644 --- a/src/components/views/voip/IncomingCallBox.js +++ b/src/components/views/voip/IncomingCallBox.js @@ -17,6 +17,7 @@ var React = require('react'); var MatrixClientPeg = require('../../../MatrixClientPeg'); var dis = require("../../../dispatcher"); var CallHandler = require("../../../CallHandler"); +import { _t } from '../../../languageHandler'; module.exports = React.createClass({ displayName: 'IncomingCallBox', @@ -45,23 +46,36 @@ module.exports = React.createClass({ room = MatrixClientPeg.get().getRoom(this.props.incomingCall.roomId); } - var caller = room ? room.name : "unknown"; + var caller = room ? room.name : _t("unknown caller"); + + let incomingCallText = null; + if (this.props.incomingCall) { + if (this.props.incomingCall.type === "voice") { + incomingCallText = _t("Incoming voice call from %(name)s", {name: caller}); + } + else if (this.props.incomingCall.type === "video") { + incomingCallText = _t("Incoming video call from %(name)s", {name: caller}); + } + else { + incomingCallText = _t("Incoming call from %(name)s", {name: caller}); + } + } return (
- Incoming { this.props.incomingCall ? this.props.incomingCall.type : '' } call from { caller } + {incomingCallText}
- Decline + {_t("Decline")}
- Accept + {_t("Accept")}
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 36d220dca5..de1adc539d 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -122,15 +122,18 @@ "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", + "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", "Access Token:": "Access Token:", + "Active call (%(roomName)s)": "Active call (%(roomName)s)", "Add": "Add", "Add a topic": "Add a topic", "Add email address": "Add email address", "Add phone number": "Add phone number", "Admin": "Admin", + "And %(count)s more...": "And %(count)s more...", "VoIP": "VoIP", "Missing Media Permissions, click here to request.": "Missing Media Permissions, click here to request.", "No Microphones detected": "No Microphones detected", @@ -230,6 +233,7 @@ "/ddg is not a command": "/ddg is not a command", "Deactivate Account": "Deactivate Account", "Deactivate my account": "Deactivate my account", + "Decline": "Decline", "decline": "decline", "Decrypt %(text)s": "Decrypt %(text)s", "Decryption error": "Decryption error", @@ -247,6 +251,7 @@ "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 Notifications": "Disable Notifications", "disabled": "disabled", "Disable inline URL previews by default": "Disable inline URL previews by default", "Disable markdown formatting": "Disable markdown formatting", @@ -264,6 +269,7 @@ "Email, name or matrix ID": "Email, name or matrix ID", "Emoji": "Emoji", "Enable encryption": "Enable encryption", + "Enable Notifications": "Enable Notifications", "enabled": "enabled", "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", @@ -303,6 +309,7 @@ "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 upload profile picture!": "Failed to upload profile picture!", "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", @@ -332,6 +339,9 @@ "I have verified my email address": "I have verified my email address", "Import": "Import", "Import E2E room keys": "Import E2E room keys", + "Incoming call from %(name)s": "Incoming call from %(name)s", + "Incoming video call from %(name)s": "Incoming video call from %(name)s", + "Incoming voice call from %(name)s": "Incoming voice call from %(name)s", "Incorrect username and/or password.": "Incorrect username and/or password.", "Incorrect verification code": "Incorrect verification code", "Interface Language": "Interface Language", @@ -359,6 +369,7 @@ "Kick": "Kick", "Kicks user with given id": "Kicks user with given id", "Labs": "Labs", + "Last seen": "Last seen", "Leave room": "Leave room", "left and rejoined": "left and rejoined", "left": "left", @@ -402,6 +413,7 @@ "": "", "NOT verified": "NOT verified", "No devices with registered encryption keys": "No devices with registered encryption keys", + "No display name": "No display name", "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", @@ -411,6 +423,7 @@ "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", "Operation failed": "Operation failed", + "Otherwise, click here to send a bug report.": "Otherwise, click here to send a bug report.", "Password": "Password", "Password:": "Password:", "Passwords can't be empty": "Passwords can't be empty", @@ -453,9 +466,11 @@ "riot-web version:": "riot-web version:", "Room %(roomId)s not visible": "Room %(roomId)s not visible", "Room Colour": "Room Colour", + "Room contains unknown devices": "Room contains unknown devices", "Room name (optional)": "Room name (optional)", "Rooms": "Rooms", "Save": "Save", + "Saving room color settings is only available to registered users": "Saving room color settings is only available to registered users", "Scroll to bottom of page": "Scroll to bottom of page", "Scroll to unread messages": "Scroll to unread messages", "Search": "Search", @@ -464,6 +479,7 @@ "Searching known users": "Searching known users", "Send a message (unencrypted)": "Send a message (unencrypted)", "Send an encrypted message": "Send an encrypted message", + "Send anyway": "Send anyway", "Sender device information": "Sender device information", "Send Invites": "Send Invites", "Send Reset Email": "Send Reset Email", @@ -480,6 +496,7 @@ "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.", + "Set": "Set", "Setting a user name will create a fresh account": "Setting a user name will create a fresh account", "Settings": "Settings", "Show panel": "Show panel", @@ -494,6 +511,7 @@ "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 authentication": "Start authentication", "Start Chat": "Start Chat", "Submit": "Submit", "Success": "Success", @@ -561,6 +579,7 @@ "Unable to load device list": "Unable to load device list", "Unencrypted room": "Unencrypted room", "unencrypted": "unencrypted", + "unknown caller": "unknown caller", "Unknown command": "Unknown command", "unknown device": "unknown device", "unknown error code": "unknown error code", @@ -580,6 +599,7 @@ "Upload Failed": "Upload Failed", "Upload Files": "Upload Files", "Upload file": "Upload file", + "Upload new:": "Upload new:", "Usage": "Usage", "Use compact timeline layout": "Use compact timeline layout", "Use with caution": "Use with caution", @@ -633,6 +653,7 @@ "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", + "Your home server does not support device management.": "Your home server does not support device management.", "Sun": "Sun", "Mon": "Mon", "Tue": "Tue", @@ -780,6 +801,7 @@ "To continue, please enter your password.": "To continue, please enter your password.", "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:": "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": "Device name", + "Device Name": "Device Name", "Device key": "Device key", "If it matches, press the verify button below. If it doesn't, then someone else is intercepting this device and you probably want to press the blacklist button instead.": "If it matches, press the verify button below. If it doesn't, 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.": "In future this verification process will be more sophisticated.", From 7e7b5a6dc03c36a831dcbad40744fe669f22de6c Mon Sep 17 00:00:00 2001 From: Vladi Date: Thu, 8 Jun 2017 11:46:42 +0000 Subject: [PATCH 0663/1016] Translated using Weblate (Russian) Currently translated at 100.0% (853 of 853 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/ru/ --- src/i18n/strings/ru.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/ru.json b/src/i18n/strings/ru.json index 647fbfe1d5..4f1939ffcc 100644 --- a/src/i18n/strings/ru.json +++ b/src/i18n/strings/ru.json @@ -667,7 +667,7 @@ "Desktop specific": "Специфический десктоп", "Start automatically after system login": "Автостарт после входа в систему", "Analytics": "Аналитика", - "Riot collects anonymous analytics to allow us to improve the application.": "Riot собирет анонимные данные, чтобы улутшыть эту програму.", + "Riot collects anonymous analytics to allow us to improve the application.": "Riot собирает анонимные данные, чтобы улучшить эту программу.", "Opt out of analytics": "Подтвердить отказ передачи аналитических данных", "Logged in as:": "Зарегестрирован как:", "Default Device": "Стандартное устройство", @@ -894,5 +894,6 @@ "Username not available": "Имя пользователя недоступно", "Something went wrong!": "Что-то пошло не так!", "This will be your account name on the homeserver, or you can pick a different server.": "Это будет ваше имя пользователя на , или вы можете выбрать другой сервер.", - "If you already have a Matrix account you can log in instead.": "Если вы уже имеете учетную запись Matrix, то вы можете войти." + "If you already have a Matrix account you can log in instead.": "Если вы уже имеете учетную запись Matrix, то вы можете войти.", + "Home": "Старт" } From 9bda212cb189939066f389ec7d2d7d3677d4219c Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Thu, 8 Jun 2017 13:52:52 +0100 Subject: [PATCH 0664/1016] Make travis/jenkins check for lint on files which are clean to start with --- .eslintignore.errorfiles | 187 ++++++++++++++++++++++ .travis.yml | 4 +- jenkins.sh | 5 + scripts/generate-eslint-error-ignore-file | 21 +++ scripts/travis.sh | 11 ++ 5 files changed, 225 insertions(+), 3 deletions(-) create mode 100644 .eslintignore.errorfiles create mode 100755 scripts/generate-eslint-error-ignore-file create mode 100755 scripts/travis.sh diff --git a/.eslintignore.errorfiles b/.eslintignore.errorfiles new file mode 100644 index 0000000000..f1b63d7367 --- /dev/null +++ b/.eslintignore.errorfiles @@ -0,0 +1,187 @@ +# autogenerated file: run scripts/generate-eslint-error-ignore-file to update. + +src/AddThreepid.js +src/async-components/views/dialogs/EncryptedEventDialog.js +src/autocomplete/AutocompleteProvider.js +src/autocomplete/Autocompleter.js +src/autocomplete/Components.js +src/autocomplete/DuckDuckGoProvider.js +src/autocomplete/EmojiProvider.js +src/autocomplete/RoomProvider.js +src/autocomplete/UserProvider.js +src/Avatar.js +src/BasePlatform.js +src/CallHandler.js +src/component-index.js +src/components/structures/ContextualMenu.js +src/components/structures/CreateRoom.js +src/components/structures/FilePanel.js +src/components/structures/InteractiveAuth.js +src/components/structures/LoggedInView.js +src/components/structures/login/ForgotPassword.js +src/components/structures/login/Login.js +src/components/structures/login/PostRegistration.js +src/components/structures/login/Registration.js +src/components/structures/MatrixChat.js +src/components/structures/MessagePanel.js +src/components/structures/NotificationPanel.js +src/components/structures/RoomStatusBar.js +src/components/structures/RoomView.js +src/components/structures/ScrollPanel.js +src/components/structures/TimelinePanel.js +src/components/structures/UploadBar.js +src/components/structures/UserSettings.js +src/components/views/avatars/BaseAvatar.js +src/components/views/avatars/MemberAvatar.js +src/components/views/avatars/RoomAvatar.js +src/components/views/create_room/CreateRoomButton.js +src/components/views/create_room/Presets.js +src/components/views/create_room/RoomAlias.js +src/components/views/dialogs/ChatCreateOrReuseDialog.js +src/components/views/dialogs/ChatInviteDialog.js +src/components/views/dialogs/DeactivateAccountDialog.js +src/components/views/dialogs/InteractiveAuthDialog.js +src/components/views/dialogs/SetMxIdDialog.js +src/components/views/dialogs/UnknownDeviceDialog.js +src/components/views/elements/AccessibleButton.js +src/components/views/elements/ActionButton.js +src/components/views/elements/AddressSelector.js +src/components/views/elements/AddressTile.js +src/components/views/elements/CreateRoomButton.js +src/components/views/elements/DeviceVerifyButtons.js +src/components/views/elements/DirectorySearchBox.js +src/components/views/elements/Dropdown.js +src/components/views/elements/EditableText.js +src/components/views/elements/EditableTextContainer.js +src/components/views/elements/HomeButton.js +src/components/views/elements/LanguageDropdown.js +src/components/views/elements/MemberEventListSummary.js +src/components/views/elements/PowerSelector.js +src/components/views/elements/ProgressBar.js +src/components/views/elements/RoomDirectoryButton.js +src/components/views/elements/SettingsButton.js +src/components/views/elements/StartChatButton.js +src/components/views/elements/TintableSvg.js +src/components/views/elements/TruncatedList.js +src/components/views/elements/UserSelector.js +src/components/views/login/CaptchaForm.js +src/components/views/login/CasLogin.js +src/components/views/login/CountryDropdown.js +src/components/views/login/CustomServerDialog.js +src/components/views/login/InteractiveAuthEntryComponents.js +src/components/views/login/LoginHeader.js +src/components/views/login/PasswordLogin.js +src/components/views/login/RegistrationForm.js +src/components/views/login/ServerConfig.js +src/components/views/messages/MAudioBody.js +src/components/views/messages/MessageEvent.js +src/components/views/messages/MFileBody.js +src/components/views/messages/MImageBody.js +src/components/views/messages/MVideoBody.js +src/components/views/messages/RoomAvatarEvent.js +src/components/views/messages/TextualBody.js +src/components/views/messages/TextualEvent.js +src/components/views/room_settings/AliasSettings.js +src/components/views/room_settings/ColorSettings.js +src/components/views/room_settings/UrlPreviewSettings.js +src/components/views/rooms/Autocomplete.js +src/components/views/rooms/AuxPanel.js +src/components/views/rooms/EntityTile.js +src/components/views/rooms/EventTile.js +src/components/views/rooms/LinkPreviewWidget.js +src/components/views/rooms/MemberDeviceInfo.js +src/components/views/rooms/MemberInfo.js +src/components/views/rooms/MemberList.js +src/components/views/rooms/MemberTile.js +src/components/views/rooms/MessageComposer.js +src/components/views/rooms/MessageComposerInput.js +src/components/views/rooms/MessageComposerInputOld.js +src/components/views/rooms/PresenceLabel.js +src/components/views/rooms/ReadReceiptMarker.js +src/components/views/rooms/RoomHeader.js +src/components/views/rooms/RoomList.js +src/components/views/rooms/RoomNameEditor.js +src/components/views/rooms/RoomPreviewBar.js +src/components/views/rooms/RoomSettings.js +src/components/views/rooms/RoomTile.js +src/components/views/rooms/RoomTopicEditor.js +src/components/views/rooms/SearchableEntityList.js +src/components/views/rooms/SearchResultTile.js +src/components/views/rooms/TabCompleteBar.js +src/components/views/rooms/TopUnreadMessagesBar.js +src/components/views/rooms/UserTile.js +src/components/views/settings/AddPhoneNumber.js +src/components/views/settings/ChangeAvatar.js +src/components/views/settings/ChangeDisplayName.js +src/components/views/settings/ChangePassword.js +src/components/views/settings/DevicesPanel.js +src/components/views/settings/DevicesPanelEntry.js +src/components/views/settings/EnableNotificationsButton.js +src/components/views/voip/CallView.js +src/components/views/voip/IncomingCallBox.js +src/components/views/voip/VideoFeed.js +src/components/views/voip/VideoView.js +src/ContentMessages.js +src/createRoom.js +src/DateUtils.js +src/email.js +src/Entities.js +src/extend.js +src/HtmlUtils.js +src/ImageUtils.js +src/Invite.js +src/languageHandler.js +src/linkify-matrix.js +src/Login.js +src/Markdown.js +src/MatrixClientPeg.js +src/Modal.js +src/Notifier.js +src/ObjectUtils.js +src/PasswordReset.js +src/PlatformPeg.js +src/Presence.js +src/ratelimitedfunc.js +src/Resend.js +src/RichText.js +src/Roles.js +src/RoomListSorter.js +src/RoomNotifs.js +src/Rooms.js +src/RtsClient.js +src/ScalarAuthClient.js +src/ScalarMessaging.js +src/SdkConfig.js +src/Skinner.js +src/SlashCommands.js +src/stores/LifecycleStore.js +src/TabComplete.js +src/TabCompleteEntries.js +src/TextForEvent.js +src/Tinter.js +src/UiEffects.js +src/Unread.js +src/UserActivity.js +src/utils/DecryptFile.js +src/utils/DMRoomMap.js +src/utils/FormattingUtils.js +src/utils/MultiInviter.js +src/utils/Receipt.js +src/Velociraptor.js +src/VelocityBounce.js +src/WhoIsTyping.js +src/wrappers/WithMatrixClient.js +test/all-tests.js +test/components/structures/login/Registration-test.js +test/components/structures/MessagePanel-test.js +test/components/structures/ScrollPanel-test.js +test/components/structures/TimelinePanel-test.js +test/components/stub-component.js +test/components/views/dialogs/InteractiveAuthDialog-test.js +test/components/views/elements/MemberEventListSummary-test.js +test/components/views/login/RegistrationForm-test.js +test/components/views/rooms/MessageComposerInput-test.js +test/mock-clock.js +test/skinned-sdk.js +test/stores/RoomViewStore-test.js +test/test-utils.js diff --git a/.travis.yml b/.travis.yml index a405b9ef35..918cec696b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,4 @@ install: - npm install - (cd node_modules/matrix-js-sdk && npm install) script: - # don't run the riot tests unless the react-sdk tests pass, otherwise - # the output is confusing. - - npm run test && ./.travis-test-riot.sh + ./scripts/travis.sh diff --git a/jenkins.sh b/jenkins.sh index 6a77911c27..d9bb62855b 100755 --- a/jenkins.sh +++ b/jenkins.sh @@ -21,6 +21,11 @@ npm run test # run eslint npm run lintall -- -f checkstyle -o eslint.xml || true +# re-run the linter, excluding any files known to have errors or warnings. +./node_modules/.bin/eslint --max-warnings 0 \ + --ignore-path .eslintignore.errorfiles \ + src test + # delete the old tarball, if it exists rm -f matrix-react-sdk-*.tgz diff --git a/scripts/generate-eslint-error-ignore-file b/scripts/generate-eslint-error-ignore-file new file mode 100755 index 0000000000..3a635f5a7d --- /dev/null +++ b/scripts/generate-eslint-error-ignore-file @@ -0,0 +1,21 @@ +#!/bin/sh +# +# generates .eslintignore.errorfiles to list the files which have errors in, +# so that they can be ignored in future automated linting. + +out=.eslintignore.errorfiles + +cd `dirname $0`/.. + +echo "generating $out" + +{ + cat < 0) | .filePath' | + sed -e 's/.*matrix-react-sdk\///'; +} > "$out" diff --git a/scripts/travis.sh b/scripts/travis.sh new file mode 100755 index 0000000000..f349b06ad5 --- /dev/null +++ b/scripts/travis.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +set -ex + +npm run test +./.travis-test-riot.sh + +# run the linter, but exclude any files known to have errors or warnings. +./node_modules/.bin/eslint --max-warnings 0 \ + --ignore-path .eslintignore.errorfiles \ + src test From 00df956ca24b39134641573ea9456bbdd7d63565 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Thu, 8 Jun 2017 14:08:51 +0100 Subject: [PATCH 0665/1016] Add remaining translations! Every file has now been manually vetted by me. Due to the extent of the changes, I've been unable to test all scenarios to make sure this all works. :( --- src/components/views/rooms/AuxPanel.js | 15 +++-- src/components/views/rooms/EntityTile.js | 5 +- src/components/views/rooms/EventTile.js | 10 ++-- .../views/rooms/MemberDeviceInfo.js | 6 +- src/components/views/rooms/MemberInfo.js | 8 +-- src/components/views/rooms/MemberTile.js | 3 +- src/components/views/rooms/MessageComposer.js | 6 +- .../views/rooms/ReadReceiptMarker.js | 7 ++- src/components/views/rooms/RoomHeader.js | 4 +- src/components/views/rooms/RoomNameEditor.js | 5 +- src/components/views/rooms/RoomPreviewBar.js | 58 +++++++++++++------ src/components/views/rooms/RoomSettings.js | 14 +++-- .../views/rooms/SimpleRoomHeader.js | 3 +- .../views/rooms/TopUnreadMessagesBar.js | 2 +- src/i18n/strings/en_EN.json | 53 ++++++++++++----- 15 files changed, 130 insertions(+), 69 deletions(-) diff --git a/src/components/views/rooms/AuxPanel.js b/src/components/views/rooms/AuxPanel.js index f53512e684..7e7a670aea 100644 --- a/src/components/views/rooms/AuxPanel.js +++ b/src/components/views/rooms/AuxPanel.js @@ -19,7 +19,7 @@ import MatrixClientPeg from "../../../MatrixClientPeg"; import sdk from '../../../index'; import dis from "../../../dispatcher"; import ObjectUtils from '../../../ObjectUtils'; -import { _t } from '../../../languageHandler'; +import { _t, _tJsx} from '../../../languageHandler'; module.exports = React.createClass({ @@ -78,7 +78,7 @@ module.exports = React.createClass({ fileDropTarget = (
+ title={_t("Drop File Here")}>
{_t("Drop file here to upload")} @@ -95,9 +95,14 @@ module.exports = React.createClass({ } else { joinText = ( - Join as { this.onConferenceNotificationClick(event, 'voice');}} - href="#">voice or { this.onConferenceNotificationClick(event, 'video'); }} - href="#">video. + {_tJsx( + "Join as voice or video.", + [/(.*?)<\/a>/, /(.*?)<\/a>/], + [ + (sub) => { this.onConferenceNotificationClick(event, 'voice');}} href="#">{sub}, + (sub) => { this.onConferenceNotificationClick(event, 'video');}} href="#">{sub}, + ] + )} ); } diff --git a/src/components/views/rooms/EntityTile.js b/src/components/views/rooms/EntityTile.js index 71e8fb0be7..015b7b4603 100644 --- a/src/components/views/rooms/EntityTile.js +++ b/src/components/views/rooms/EntityTile.js @@ -21,6 +21,7 @@ var React = require('react'); var MatrixClientPeg = require('../../../MatrixClientPeg'); var sdk = require('../../../index'); import AccessibleButton from '../elements/AccessibleButton'; +import { _t } from '../../../languageHandler'; var PRESENCE_CLASS = { @@ -140,10 +141,10 @@ module.exports = React.createClass({ var power; var powerLevel = this.props.powerLevel; if (powerLevel >= 50 && powerLevel < 99) { - power = Mod; + power = {_t("Moderator")}/; } if (powerLevel >= 99) { - power = Admin; + power = {_t("Admin")}/; } diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index e527f53170..80f062aef7 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -487,22 +487,22 @@ module.exports = WithMatrixClient(React.createClass({ let e2e; // cosmetic padlocks: if ((e2eEnabled && this.props.eventSendStatus) || this.props.mxEvent.getType() === 'm.room.encryption') { - e2e = Encrypted by verified device; + e2e = {_t("Encrypted; } // real padlocks else if (this.props.mxEvent.isEncrypted() || (e2eEnabled && this.props.eventSendStatus)) { if (this.props.mxEvent.getContent().msgtype === 'm.bad.encrypted') { - e2e = Undecryptable; + e2e = {_t("Undecryptable")}; } else if (this.state.verified == true || (e2eEnabled && this.props.eventSendStatus)) { - e2e = Encrypted by verified device; + e2e = {_t("Encrypted; } else { - e2e = Encrypted by unverified device; + e2e = {_t("Encrypted; } } else if (e2eEnabled) { - e2e = Unencrypted message; + e2e = {_t("Unencrypted; } const timestamp = this.props.mxEvent.getTs() ? : null; diff --git a/src/components/views/rooms/MemberDeviceInfo.js b/src/components/views/rooms/MemberDeviceInfo.js index af44bf7f99..33b919835c 100644 --- a/src/components/views/rooms/MemberDeviceInfo.js +++ b/src/components/views/rooms/MemberDeviceInfo.js @@ -26,19 +26,19 @@ export default class MemberDeviceInfo extends React.Component { if (this.props.device.isBlocked()) { indicator = (
- Blacklisted + {_t("Blacklisted")}/
); } else if (this.props.device.isVerified()) { indicator = (
- Verified + {_t("Verified")}/
); } else { indicator = (
- Unverified + {_t("Unverified")}/
); } diff --git a/src/components/views/rooms/MemberInfo.js b/src/components/views/rooms/MemberInfo.js index 86d39f91ec..0c66dd4f11 100644 --- a/src/components/views/rooms/MemberInfo.js +++ b/src/components/views/rooms/MemberInfo.js @@ -378,7 +378,7 @@ module.exports = WithMatrixClient(React.createClass({ var NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog"); Modal.createDialog(NeedToRegisterDialog, { title: _t("Please Register"), - description: _t("This action cannot be performed by a guest user. Please register to be able to do this") + ".", + 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); @@ -436,7 +436,7 @@ module.exports = WithMatrixClient(React.createClass({ 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") }.
+ { _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: _t("Continue"), @@ -705,7 +705,7 @@ module.exports = WithMatrixClient(React.createClass({ if (kickButton || banButton || muteButton || giveModButton) { adminTools =
-

Admin tools

+

{_t("Admin tools")}

{muteButton} @@ -743,7 +743,7 @@ module.exports = WithMatrixClient(React.createClass({ { this.props.member.userId }
- { _t("Level") }: + { _t("Level:") }
- {files[i].name || 'Attachment'} + {files[i].name || _t('Attachment')} ); } @@ -291,7 +291,7 @@ export default class MessageComposer extends React.Component { const formattingButton = (  { _t("(~%(searchCount)s results)", { searchCount: this.props.searchInfo.searchCount }) }
; + searchStatus =
 { _t("(~%(count)s results)", { count: this.props.searchInfo.searchCount }) }
; } // XXX: this is a bit inefficient - we could just compare room.name for 'Empty room'... @@ -288,7 +288,7 @@ module.exports = React.createClass({ var settings_button; if (this.props.onSettingsClick) { settings_button = - + ; } diff --git a/src/components/views/rooms/RoomNameEditor.js b/src/components/views/rooms/RoomNameEditor.js index 773b713584..0fc2d1669c 100644 --- a/src/components/views/rooms/RoomNameEditor.js +++ b/src/components/views/rooms/RoomNameEditor.js @@ -19,6 +19,7 @@ limitations under the License. var React = require('react'); var sdk = require('../../../index'); var MatrixClientPeg = require('../../../MatrixClientPeg'); +import { _t } from '../../../languageHandler'; module.exports = React.createClass({ displayName: 'RoomNameEditor', @@ -35,8 +36,8 @@ module.exports = React.createClass({ this._initialName = name ? name.getContent().name : ''; - this._placeholderName = "Unnamed Room"; - if (defaultName && defaultName !== 'Empty room') { + this._placeholderName = _t("Unnamed Room"); + if (defaultName && defaultName !== 'Empty room') { // default name from JS SDK, needs no translation as we don't ever show it. this._placeholderName += " (" + defaultName + ")"; } }, diff --git a/src/components/views/rooms/RoomPreviewBar.js b/src/components/views/rooms/RoomPreviewBar.js index b20b2ef58e..8aa1f353a6 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 '../../../languageHandler'; +import { _t, _tJsx } from '../../../languageHandler'; module.exports = React.createClass({ displayName: 'RoomPreviewBar', @@ -84,7 +84,7 @@ module.exports = React.createClass({ }, _roomNameElement: function(fallback) { - fallback = fallback || 'a room'; + fallback = fallback || _t('a room'); const name = this.props.room ? this.props.room.name : (this.props.room_alias || ""); return name ? name : fallback; }, @@ -114,8 +114,7 @@ module.exports = React.createClass({ if (this.props.invitedEmail) { if (this.state.threePidFetchError) { emailMatchBlock =
- Unable to ascertain that the address this invite was - sent to matches one associated with your account. + {_t("Unable to ascertain that the address this invite was sent to matches one associated with your account.")}
; } else if (this.state.invitedEmailMxid != MatrixClientPeg.get().credentials.userId) { emailMatchBlock = @@ -124,28 +123,35 @@ module.exports = React.createClass({ /!\\
- This invitation was sent to {this.props.invitedEmail}, which is not associated with this account.
- You may wish to login with a different account, or add this email to this account. + {_t("This invitation was sent to an email address which is not associated with this account:")} + {this.props.invitedEmail} +
+ {_t("You may wish to login with a different account, or add this email to this account.")}
; } } - // TODO: find a way to respect HTML in counterpart! joinBlock = (
{ _t('You have been invited to join this room by %(inviterName)s', {inviterName: this.props.inviterName}) }
- { _t('Would you like to') } { _t('accept') } { _t('or') } { _t('decline') } { _t('this invitation?') } + { _tJsx( + 'Would you like to accept or decline this invitation?', + [/(.*?)<\/a>/, /(.*?)<\/a>/], + [ + (sub) => {sub}, + (sub) => {sub} + ] + )}
{emailMatchBlock}
); } else if (kicked || banned) { - const verb = kicked ? 'kicked' : 'banned'; - const roomName = this._roomNameElement('this room'); + const roomName = this._roomNameElement(_t('This room')); const kickerMember = this.props.room.currentState.getMember( myMember.events.member.getSender() ); @@ -153,29 +159,39 @@ module.exports = React.createClass({ kickerMember.name : myMember.events.member.getSender(); let reason; if (myMember.events.member.getContent().reason) { - reason =
Reason: {myMember.events.member.getContent().reason}
+ reason =
{_t("Reason: %(reasonText)s", {reasonText: myMember.events.member.getContent().reason})}
} let rejoinBlock; if (!banned) { - rejoinBlock = ; + rejoinBlock = ; } + + let actionText; + if (kicked) { + actionText = _t("You have been kicked from %(roomName)s by %(userName)s.", {roomName: roomName, userName: kickerName}); + } + else if (banned) { + actionText = _t("You have been banned from %(roomName)s by %(userName)s.", {roomName: roomName, userName: kickerName}); + } // no other options possible due to the kicked || banned check above. + joinBlock = (
- You have been {verb} from {roomName} by {kickerName}.
+ {actionText} +
{reason} {rejoinBlock} - Forget + {_t("Forget room")}
); } else if (this.props.error) { - var name = this.props.roomAlias || "This room"; + var name = this.props.roomAlias || _t("This room"); var error; if (this.props.error.errcode == 'M_NOT_FOUND') { - error = name + " does not exist"; + error = _t("%(roomName)s does not exist.", {roomName: name}); } else { - error = name + " is not accessible at this time"; + error = _t("%(roomName)s is not accessible at this time.", {roomName: name}); } joinBlock = (
@@ -189,8 +205,12 @@ module.exports = React.createClass({ joinBlock = (
- { _t('You are trying to access %(roomName)s', {roomName: name}) }.
- { _t('Click here') } { _t('to join the discussion') }! + { _t('You are trying to access %(roomName)s.', {roomName: name}) } +
+ { _tJsx("Click here to join the discussion!", + /(.*?)<\/a>/, + (sub) => {sub} + )}
); diff --git a/src/components/views/rooms/RoomSettings.js b/src/components/views/rooms/RoomSettings.js index 205f614ba2..f31fc68afc 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 '../../../languageHandler'; +import { _t, _tJsx } from '../../../languageHandler'; import MatrixClientPeg from '../../../MatrixClientPeg'; import SdkConfig from '../../../SdkConfig'; import sdk from '../../../index'; @@ -46,7 +46,7 @@ const BannedUser = React.createClass({ const ConfirmUserActionDialog = sdk.getComponent("dialogs.ConfirmUserActionDialog"); Modal.createDialog(ConfirmUserActionDialog, { member: this.props.member, - action: 'Unban', + action: _t('Unban'), danger: false, onFinished: (proceed) => { if (!proceed) return; @@ -597,7 +597,7 @@ module.exports = React.createClass({ ? : } - { isEncrypted ? "Encryption is enabled in this room" : "Encryption is not enabled in this room" }. + { isEncrypted ? _t("Encryption is enabled in this room") : _t("Encryption is not enabled in this room") }. { settings }
@@ -653,7 +653,7 @@ module.exports = React.createClass({ {Object.keys(user_levels).map(function(user, i) { return (
  • - { user } { _t('is a') } + { _t("%(user)s is a", {user: user}) }
  • ); })} @@ -754,7 +754,11 @@ module.exports = React.createClass({ if (this.state.join_rule === "public" && aliasCount == 0) { addressWarning =
    - { _t('To link to a room it must have') } { _t('an address') }. + { _tJsx( + 'To link to a room it must have an address.', + /(.*?)<\/a>/, + (sub) => {sub} + )}
    ; } diff --git a/src/components/views/rooms/SimpleRoomHeader.js b/src/components/views/rooms/SimpleRoomHeader.js index a6f342af86..44ec7c29aa 100644 --- a/src/components/views/rooms/SimpleRoomHeader.js +++ b/src/components/views/rooms/SimpleRoomHeader.js @@ -20,6 +20,7 @@ import React from 'react'; import dis from '../../../dispatcher'; import AccessibleButton from '../elements/AccessibleButton'; import sdk from '../../../index'; +import { _t } from '../../../languageHandler'; // cancel button which is shared between room header and simple room header export function CancelButton(props) { @@ -28,7 +29,7 @@ export function CancelButton(props) { return ( Cancel + width="18" height="18" alt={_t("Cancel")}/> ); } diff --git a/src/components/views/rooms/TopUnreadMessagesBar.js b/src/components/views/rooms/TopUnreadMessagesBar.js index 45aba66633..aa1f86483e 100644 --- a/src/components/views/rooms/TopUnreadMessagesBar.js +++ b/src/components/views/rooms/TopUnreadMessagesBar.js @@ -41,7 +41,7 @@ module.exports = React.createClass({
    Close
    ); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index de1adc539d..6026298cd6 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -120,8 +120,8 @@ "zh-tw":"Chinese (Taiwan)", "zu":"Zulu", "A registered account is required for this action": "A registered account is required for this action", + "a room": "a room", "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", "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.", @@ -133,6 +133,7 @@ "Add email address": "Add email address", "Add phone number": "Add phone number", "Admin": "Admin", + "Admin tools": "Admin tools", "And %(count)s more...": "And %(count)s more...", "VoIP": "VoIP", "Missing Media Permissions, click here to request.": "Missing Media Permissions, click here to request.", @@ -152,7 +153,6 @@ "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", @@ -201,13 +201,14 @@ "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 join the discussion!": "Click here to join the discussion!", "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", + "Close": "Close", "Command error": "Command error", "Commands": "Commands", "Conference call failed.": "Conference call failed.", @@ -234,7 +235,6 @@ "Deactivate Account": "Deactivate Account", "Deactivate my account": "Deactivate my account", "Decline": "Decline", - "decline": "decline", "Decrypt %(text)s": "Decrypt %(text)s", "Decryption error": "Decryption error", "(default: %(userName)s)": "(default: %(userName)s)", @@ -260,6 +260,7 @@ "Displays action": "Displays action", "Don't send typing notifications": "Don't send typing notifications", "Download %(text)s": "Download %(text)s", + "Drop File Here": "Drop File Here", "Drop here %(toAction)s": "Drop here %(toAction)s", "Drop here to tag %(section)s": "Drop here to tag %(section)s", "Ed25519 fingerprint": "Ed25519 fingerprint", @@ -271,8 +272,12 @@ "Enable encryption": "Enable encryption", "Enable Notifications": "Enable Notifications", "enabled": "enabled", + "Encrypted by a verified device": "Encrypted by a verified device", + "Encrypted by an unverified device": "Encrypted by an unverified device", "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", + "Encryption is enabled in this room": "Encryption is enabled in this room", + "Encryption is not enabled in this room": "Encryption is not enabled in this 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", @@ -354,11 +359,11 @@ "Invited": "Invited", "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", "Sign in with": "Sign in with", + "Join as voice or video.": "Join as voice or video.", "Join Room": "Join Room", "joined and left": "joined and left", "joined": "joined", @@ -374,7 +379,7 @@ "left and rejoined": "left and rejoined", "left": "left", "%(targetName)s left the room.": "%(targetName)s left the room.", - "Level": "Level", + "Level:": "Level:", "List this room in %(domain)s's room directory?": "List this room in %(domain)s's room directory?", "Local addresses for this room:": "Local addresses for this room:", "Logged in as:": "Logged in as:", @@ -441,6 +446,7 @@ "Profile": "Profile", "Public Chat": "Public Chat", "Reason": "Reason", + "Reason: %(reasonText)s": "Reason: %(reasonText)s", "Revoke Moderator": "Revoke Moderator", "Refer a friend to Riot:": "Refer a friend to Riot:", "Register": "Register", @@ -448,6 +454,7 @@ "rejected": "rejected", "%(targetName)s rejected the invitation.": "%(targetName)s rejected the invitation.", "Reject invitation": "Reject invitation", + "Rejoin": "Rejoin", "Remote addresses for this room:": "Remote addresses for this room:", "Remove Contact Information?": "Remove Contact Information?", "%(senderName)s removed their display name (%(oldDisplayName)s).": "%(senderName)s removed their display name (%(oldDisplayName)s).", @@ -468,6 +475,8 @@ "Room Colour": "Room Colour", "Room contains unknown devices": "Room contains unknown devices", "Room name (optional)": "Room name (optional)", + "%(roomName)s does not exist.": "%(roomName)s does not exist.", + "%(roomName)s is not accessible at this time.": "%(roomName)s is not accessible at this time.", "Rooms": "Rooms", "Save": "Save", "Saving room color settings is only available to registered users": "Saving room color settings is only available to registered users", @@ -477,6 +486,7 @@ "Search failed": "Search failed", "Searches DuckDuckGo for results": "Searches DuckDuckGo for results", "Searching known users": "Searching known users", + "Seen by %(userName)s at %(dateTime)s": "Seen by %(userName)s at %(dateTime)s", "Send a message (unencrypted)": "Send a message (unencrypted)", "Send an encrypted message": "Send an encrypted message", "Send anyway": "Send anyway", @@ -500,6 +510,7 @@ "Setting a user name will create a fresh account": "Setting a user name will create a fresh account", "Settings": "Settings", "Show panel": "Show panel", + "Show Text Formatting Toolbar": "Show Text Formatting Toolbar", "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", @@ -522,7 +533,7 @@ "The main address for this room is": "The main address for this room is", "The phone number entered looks invalid": "The phone number entered looks invalid", "The signing key you provided matches the signing key you received from %(userId)s's device %(deviceId)s. Device marked as verified.": "The signing key you provided matches the signing key you received from %(userId)s's device %(deviceId)s. Device marked as verified.", - "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 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?", @@ -531,6 +542,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 Home Server does not support login using email address.": "This Home Server does not support login using email address.", + "This invitation was sent to an email address which is not associated with this account:": "This invitation was sent to an email address which is not associated with this account:", "There was a problem logging in.": "There was a problem logging in.", "This room has no local addresses": "This room has no local addresses", "This room is not recognised.": "This room is not recognised.", @@ -538,9 +550,9 @@ "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": "This room", "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", @@ -550,9 +562,8 @@ "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 link to a room it must have an address.": "To link to a room it must have an address.", "to make a room or": "to make a room or", "To remove other users' messages": "To remove 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", @@ -574,11 +585,14 @@ "Unable to verify email address.": "Unable to verify email address.", "Unban": "Unban", "%(senderName)s unbanned %(targetName)s.": "%(senderName)s unbanned %(targetName)s.", + "Unable to ascertain that the address this invite was sent to matches one associated with your account.": "Unable to ascertain that the address this invite was sent to matches one associated with your account.", "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", + "Undecryptable": "Undecryptable", "Unencrypted room": "Unencrypted room", "unencrypted": "unencrypted", + "Unencrypted message": "Unencrypted message", "unknown caller": "unknown caller", "Unknown command": "Unknown command", "unknown device": "unknown device", @@ -587,8 +601,10 @@ "Unknown (user, device) pair:": "Unknown (user, device) pair:", "unknown": "unknown", "Unmute": "Unmute", + "Unnamed Room": "Unnamed Room", "Unrecognised command:": "Unrecognised command:", "Unrecognised room alias:": "Unrecognised room alias:", + "Unverified": "Unverified", "Uploading %(filename)s and %(count)s others": { "zero": "Uploading %(filename)s", "one": "Uploading %(filename)s and %(count)s other", @@ -605,12 +621,15 @@ "Use with caution": "Use with caution", "User ID": "User ID", "User Interface": "User Interface", + "%(user)s is a": "%(user)s is a", "User name": "User name", + "%(userName)s (power %(powerLevelNumber)s)": "%(userName)s (power %(powerLevelNumber)s)", "Users": "Users", "User": "User", "Verification Pending": "Verification Pending", "Verification": "Verification", "verified": "verified", + "Verified": "Verified", "Verified key": "Verified key", "Video call": "Video call", "Voice call": "Voice call", @@ -626,20 +645,23 @@ "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 invitation.": "%(senderName)s withdrew %(targetName)s's invitation.", - "Would you like to": "Would you like to", + "Would you like to accept or decline this invitation?": "Would you like to accept or decline this invitation?". "You already have existing direct chats with this user:": "You already have existing direct chats with this user:", "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 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 banned from %(roomName)s by %(userName)s.": "You have been banned from %(roomName)s by %(userName)s.", "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 kicked from %(roomName)s by %(userName)s.": "You have been kicked from %(roomName)s by %(userName)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 disabled URL previews by default.": "You have disabled URL previews by default.", "You have enabled URL previews by default.": "You have enabled URL previews by default.", "You have entered an invalid contact. Try using their Matrix ID or email address.": "You have entered an invalid contact. Try using their Matrix ID or email address.", "You have no visible notifications": "You have no visible notifications", + "You may wish to login with a different account, or add this email to this account.": "You may wish to login with a different account, or add this email to this account.", "you must be a": "you must be a", "You must register to use this functionality": "You must register to use this functionality", "You need to be able to invite users to do that.": "You need to be able to invite users to do that.", @@ -652,7 +674,7 @@ "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", + "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.", "Your home server does not support device management.": "Your home server does not support device management.", "Sun": "Sun", "Mon": "Mon", @@ -700,7 +722,10 @@ "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 or cancel all now. You can also select individual messages to resend or cancel.": "Resend all or cancel all now. You can also select individual messages to resend or cancel.", - "(~%(searchCount)s results)": "(~%(searchCount)s results)", + "(~%(count)s results)": { + "one": "(~%(count)s result)", + "other": "(~%(count)s results)" + }, "Cancel": "Cancel", "or": "or", "Active call": "Active call", From 8479a8f5ffc5e3aeaa2547fe007ebcaab3916437 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Thu, 8 Jun 2017 14:11:34 +0100 Subject: [PATCH 0666/1016] Update .eslintignore.errorfiles --- .eslintignore.errorfiles | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.eslintignore.errorfiles b/.eslintignore.errorfiles index f1b63d7367..dedde7e07d 100644 --- a/.eslintignore.errorfiles +++ b/.eslintignore.errorfiles @@ -2,6 +2,8 @@ src/AddThreepid.js src/async-components/views/dialogs/EncryptedEventDialog.js +src/async-components/views/dialogs/ExportE2eKeysDialog.js +src/async-components/views/dialogs/ImportE2eKeysDialog.js src/autocomplete/AutocompleteProvider.js src/autocomplete/Autocompleter.js src/autocomplete/Components.js @@ -165,6 +167,7 @@ src/UserActivity.js src/utils/DecryptFile.js src/utils/DMRoomMap.js src/utils/FormattingUtils.js +src/utils/MegolmExportEncryption.js src/utils/MultiInviter.js src/utils/Receipt.js src/Velociraptor.js @@ -185,3 +188,4 @@ test/mock-clock.js test/skinned-sdk.js test/stores/RoomViewStore-test.js test/test-utils.js +test/utils/MegolmExportEncryption-test.js From 6d1fa775a04c24b37bf81524f1eb03516176e62e Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 8 Jun 2017 14:14:45 +0100 Subject: [PATCH 0667/1016] Formatting --- src/components/structures/login/Login.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/components/structures/login/Login.js b/src/components/structures/login/Login.js index 840f0874a7..be440febdc 100644 --- a/src/components/structures/login/Login.js +++ b/src/components/structures/login/Login.js @@ -219,8 +219,8 @@ module.exports = React.createClass({ if (err.cors === 'rejected') { if (window.location.protocol === 'https:' && (this.state.enteredHomeserverUrl.startsWith("http:") || - !this.state.enteredHomeserverUrl.startsWith("http"))) - { + !this.state.enteredHomeserverUrl.startsWith("http")) + ) { errorText = { _tJsx("Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. " + "Either use HTTPS or enable unsafe scripts.", @@ -228,8 +228,7 @@ module.exports = React.createClass({ (sub) => { return { sub }; } )} ; - } - else { + } else { errorText = { _tJsx("Can't connect to homeserver - please check your connectivity and ensure your homeserver's SSL certificate is trusted.", /(.*?)<\/a>/, From d3cf78ff5abd3297cb1f51a3017d5236db114af5 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 8 Jun 2017 14:17:49 +0100 Subject: [PATCH 0668/1016] Control currently viewied event via RoomViewStore Fix for https://github.com/vector-im/riot-web/issues/4224 Due to the way `MatrixChat` does a state update when the `view_room` dispatch fires and a second update when `RoomViewStore` sends an update, the current event ID and room ID were becoming out of sync. The solution devised was to have the event ID managed by the `RoomViewStore` itself and do any defaulting there (for when we revisit a room that we saved scroll state for previously). This required a few changes: - The addition of `update_scroll_state` in `RoomViewStore` allows the `RoomView` to save scroll state for a room before swapping to another one. Previously the caching of scroll state was done in `RoomView`. - The `view_room` dispatch now accepts an `event_id`, which dictates which event is supposed to be scrolled to in the `MessagePanel` when a new room is viewed. It also accepts `event_offset`, but currently, this isn't passed in by a dispatch in the app, but it is clobbered when loading the default position when an `event_id` isn't specified. Finally, `highlighted` was added to distinguish whether the initial event being scrolled to is also highlighted. This flag is also used by `viewRoom` in `MatrixChat` in order to decide whether to `notifyNewScreen` with the specified `event_id`. --- src/components/structures/LoggedInView.js | 2 - src/components/structures/MatrixChat.js | 19 +---- src/components/structures/RoomView.js | 93 +++++++++-------------- src/components/views/rooms/EventTile.js | 1 + src/stores/RoomViewStore.js | 72 +++++++++++++++++- 5 files changed, 109 insertions(+), 78 deletions(-) diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js index a201a0bea7..8fa35e84d7 100644 --- a/src/components/structures/LoggedInView.js +++ b/src/components/structures/LoggedInView.js @@ -223,10 +223,8 @@ export default React.createClass({ ref='roomView' autoJoin={this.props.autoJoin} onRegistered={this.props.onRegistered} - eventId={this.props.initialEventId} thirdPartyInvite={this.props.thirdPartyInvite} oobData={this.props.roomOobData} - highlightedEventId={this.props.highlightedEventId} eventPixelOffset={this.props.initialEventPixelOffset} key={this.props.currentRoomId || 'roomview'} opacity={this.props.middleOpacity} diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index efb2b38d6e..b65fa0be1c 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -618,9 +618,6 @@ module.exports = React.createClass({ this.focusComposer = true; const newState = { - initialEventId: roomInfo.event_id, - highlightedEventId: roomInfo.event_id, - initialEventPixelOffset: undefined, page_type: PageTypes.RoomView, thirdPartyInvite: roomInfo.third_party_invite, roomOobData: roomInfo.oob_data, @@ -632,18 +629,6 @@ module.exports = React.createClass({ newState.currentRoomId = roomInfo.room_id; } - // if we aren't given an explicit event id, look for one in the - // scrollStateMap. - // - // TODO: do this in RoomView rather than here - if (!roomInfo.event_id && this.refs.loggedInView) { - const scrollState = this.refs.loggedInView.getScrollStateForRoom(roomInfo.room_id); - if (scrollState) { - newState.initialEventId = scrollState.focussedEvent; - newState.initialEventPixelOffset = scrollState.pixelOffset; - } - } - // Wait for the first sync to complete so that if a room does have an alias, // it would have been retrieved. let waitFor = q(null); @@ -669,7 +654,7 @@ module.exports = React.createClass({ } } - if (roomInfo.event_id) { + if (roomInfo.event_id && roomInfo.highlighted) { presentedId += "/" + roomInfo.event_id; } this.notifyNewScreen('room/' + presentedId); @@ -1124,8 +1109,10 @@ module.exports = React.createClass({ }; const payload = { + id: '#mylovelyid', action: 'view_room', event_id: eventId, + highlighted: Boolean(eventId), third_party_invite: thirdPartyInvite, oob_data: oobData, }; diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index df21715d75..a4c589187b 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -83,36 +83,8 @@ module.exports = React.createClass({ // * invited us tovthe room oobData: React.PropTypes.object, - // id of an event to jump to. If not given, will go to the end of the - // live timeline. - eventId: React.PropTypes.string, - - // where to position the event given by eventId, in pixels from the - // bottom of the viewport. If not given, will try to put the event - // 1/3 of the way down the viewport. - eventPixelOffset: React.PropTypes.number, - - // ID of an event to highlight. If undefined, no event will be highlighted. - // Typically this will either be the same as 'eventId', or undefined. - highlightedEventId: React.PropTypes.string, - // is the RightPanel collapsed? collapsedRhs: React.PropTypes.bool, - - // a map from room id to scroll state, which will be updated on unmount. - // - // If there is no special scroll state (ie, we are following the live - // timeline), the scroll state is null. Otherwise, it is an object with - // the following properties: - // - // focussedEvent: the ID of the 'focussed' event. Typically this is - // the last event fully visible in the viewport, though if we - // have done an explicit scroll to an explicit event, it will be - // that event. - // - // pixelOffset: the number of pixels the window is scrolled down - // from the focussedEvent. - scrollStateMap: React.PropTypes.object, }, getInitialState: function() { @@ -180,13 +152,28 @@ module.exports = React.createClass({ if (this.unmounted) { return; } - this.setState({ + const newState = { roomId: RoomViewStore.getRoomId(), roomAlias: RoomViewStore.getRoomAlias(), roomLoading: RoomViewStore.isRoomLoading(), roomLoadError: RoomViewStore.getRoomLoadError(), joining: RoomViewStore.isJoining(), - }, () => { + eventId: RoomViewStore.getEventId(), + eventPixelOffset: RoomViewStore.getEventPixelOffset(), + isEventHighlighted: RoomViewStore.isEventHighlighted(), + }; + + if (this.state.eventId !== newState.eventId) { + newState.searchResults = null; + } + + // Store the scroll state for the previous room so that we can return to this + // position when viewing this room in future. + if (this.state.roomId !== newState.roomId) { + this._updateScrollMap(this.state.roomId); + } + + this.setState(newState, () => { this._onHaveRoom(); this.onRoom(MatrixClientPeg.get().getRoom(this.state.roomId)); }); @@ -287,13 +274,6 @@ module.exports = React.createClass({ } }, - componentWillReceiveProps: function(newProps) { - if (newProps.eventId != this.props.eventId) { - // when we change focussed event id, hide the search results. - this.setState({searchResults: null}); - } - }, - shouldComponentUpdate: function(nextProps, nextState) { return (!ObjectUtils.shallowEqual(this.props, nextProps) || !ObjectUtils.shallowEqual(this.state, nextState)); @@ -319,7 +299,7 @@ module.exports = React.createClass({ this.unmounted = true; // update the scroll map before we get unmounted - this._updateScrollMap(); + this._updateScrollMap(this.state.roomId); if (this.refs.roomView) { // disconnect the D&D event listeners from the room view. This @@ -598,6 +578,14 @@ module.exports = React.createClass({ }); }, + _updateScrollMap(roomId) { + dis.dispatch({ + action: 'update_scroll_state', + room_id: roomId, + scroll_state: this._getScrollState(), + }); + }, + onRoom: function(room) { if (!room || room.roomId !== this.state.roomId) { return; @@ -1240,21 +1228,6 @@ module.exports = React.createClass({ } }, - // update scrollStateMap on unmount - _updateScrollMap: function() { - if (!this.state.room) { - // we were instantiated on a room alias and haven't yet joined the room. - return; - } - if (!this.props.scrollStateMap) return; - - var roomId = this.state.room.roomId; - - var state = this._getScrollState(); - this.props.scrollStateMap[roomId] = state; - }, - - // get the current scroll position of the room, so that it can be // restored when we switch back to it. // @@ -1677,6 +1650,14 @@ module.exports = React.createClass({ hideMessagePanel = true; } + const shouldHighlight = this.state.isEventHighlighted; + let highlightedEventId = null; + if (this.state.forwardingEvent) { + highlightedEventId = this.state.forwardingEvent.getId(); + } else if (shouldHighlight) { + highlightedEventId = this.state.eventId; + } + // console.log("ShowUrlPreview for %s is %s", this.state.room.roomId, this.state.showUrlPreview); var messagePanel = (
    diff --git a/src/async-components/views/dialogs/ImportE2eKeysDialog.js b/src/async-components/views/dialogs/ImportE2eKeysDialog.js index 61d2aeec74..91010d33b9 100644 --- a/src/async-components/views/dialogs/ImportE2eKeysDialog.js +++ b/src/async-components/views/dialogs/ImportE2eKeysDialog.js @@ -122,13 +122,13 @@ export default React.createClass({ 'This process allows you to import encryption keys ' + 'that you had previously exported from another Matrix ' + 'client. You will then be able to decrypt any ' + - 'messages that the other client could decrypt.' + 'messages that the other client could decrypt.', ) }

    { _t( 'The export file will be protected with a passphrase. ' + - 'You should enter the passphrase here, to decrypt the file.' + 'You should enter the passphrase here, to decrypt the file.', ) }

    diff --git a/src/utils/MegolmExportEncryption.js b/src/utils/MegolmExportEncryption.js index c98c467e1c..de39ea71cc 100644 --- a/src/utils/MegolmExportEncryption.js +++ b/src/utils/MegolmExportEncryption.js @@ -32,7 +32,7 @@ const subtleCrypto = window.crypto.subtle || window.crypto.webkitSubtle; /** * Decrypt a megolm key file * - * @param {ArrayBuffer} file + * @param {ArrayBuffer} data file to decrypt * @param {String} password * @return {Promise} promise for decrypted output */ @@ -61,12 +61,12 @@ export function decryptMegolmKeyFile(data, password) { const hmac = body.subarray(-32); return deriveKeys(salt, iterations, password).then((keys) => { - const [aes_key, hmac_key] = keys; + const [aesKey, hmacKey] = keys; const toVerify = body.subarray(0, -32); return subtleCrypto.verify( {name: 'HMAC'}, - hmac_key, + hmacKey, hmac, toVerify, ).then((isValid) => { @@ -80,7 +80,7 @@ export function decryptMegolmKeyFile(data, password) { counter: iv, length: 64, }, - aes_key, + aesKey, ciphertext, ); }); @@ -102,7 +102,7 @@ export function decryptMegolmKeyFile(data, password) { */ export function encryptMegolmKeyFile(data, password, options) { options = options || {}; - const kdf_rounds = options.kdf_rounds || 500000; + const kdfRounds = options.kdf_rounds || 500000; const salt = new Uint8Array(16); window.crypto.getRandomValues(salt); @@ -115,8 +115,8 @@ export function encryptMegolmKeyFile(data, password, options) { // of a single bit of iv is a price we have to pay. iv[9] &= 0x7f; - return deriveKeys(salt, kdf_rounds, password).then((keys) => { - const [aes_key, hmac_key] = keys; + return deriveKeys(salt, kdfRounds, password).then((keys) => { + const [aesKey, hmacKey] = keys; return subtleCrypto.encrypt( { @@ -124,7 +124,7 @@ export function encryptMegolmKeyFile(data, password, options) { counter: iv, length: 64, }, - aes_key, + aesKey, new TextEncoder().encode(data), ).then((ciphertext) => { const cipherArray = new Uint8Array(ciphertext); @@ -134,17 +134,17 @@ export function encryptMegolmKeyFile(data, password, options) { resultBuffer[idx++] = 1; // version resultBuffer.set(salt, idx); idx += salt.length; resultBuffer.set(iv, idx); idx += iv.length; - resultBuffer[idx++] = kdf_rounds >> 24; - resultBuffer[idx++] = (kdf_rounds >> 16) & 0xff; - resultBuffer[idx++] = (kdf_rounds >> 8) & 0xff; - resultBuffer[idx++] = kdf_rounds & 0xff; + resultBuffer[idx++] = kdfRounds >> 24; + resultBuffer[idx++] = (kdfRounds >> 16) & 0xff; + resultBuffer[idx++] = (kdfRounds >> 8) & 0xff; + resultBuffer[idx++] = kdfRounds & 0xff; resultBuffer.set(cipherArray, idx); idx += cipherArray.length; const toSign = resultBuffer.subarray(0, idx); return subtleCrypto.sign( {name: 'HMAC'}, - hmac_key, + hmacKey, toSign, ).then((hmac) => { hmac = new Uint8Array(hmac); @@ -170,7 +170,7 @@ function deriveKeys(salt, iterations, password) { new TextEncoder().encode(password), {name: 'PBKDF2'}, false, - ['deriveBits'] + ['deriveBits'], ).then((key) => { return subtleCrypto.deriveBits( { @@ -180,33 +180,33 @@ function deriveKeys(salt, iterations, password) { hash: 'SHA-512', }, key, - 512 + 512, ); }).then((keybits) => { const now = new Date(); console.log("E2e import/export: deriveKeys took " + (now - start) + "ms"); - const aes_key = keybits.slice(0, 32); - const hmac_key = keybits.slice(32); + const aesKey = keybits.slice(0, 32); + const hmacKey = keybits.slice(32); - const aes_prom = subtleCrypto.importKey( + const aesProm = subtleCrypto.importKey( 'raw', - aes_key, + aesKey, {name: 'AES-CTR'}, false, - ['encrypt', 'decrypt'] + ['encrypt', 'decrypt'], ); - const hmac_prom = subtleCrypto.importKey( + const hmacProm = subtleCrypto.importKey( 'raw', - hmac_key, + hmacKey, { name: 'HMAC', hash: {name: 'SHA-256'}, }, false, - ['sign', 'verify'] + ['sign', 'verify'], ); - return Promise.all([aes_prom, hmac_prom]); + return Promise.all([aesProm, hmacProm]); }); } @@ -301,7 +301,7 @@ function packMegolmKeyFile(data) { function encodeBase64(uint8Array) { // Misinterpt the Uint8Array as Latin-1. // window.btoa expects a unicode string with codepoints in the range 0-255. - var latin1String = String.fromCharCode.apply(null, uint8Array); + const latin1String = String.fromCharCode.apply(null, uint8Array); // Use the builtin base64 encoder. return window.btoa(latin1String); } @@ -313,10 +313,10 @@ function encodeBase64(uint8Array) { */ function decodeBase64(base64) { // window.atob returns a unicode string with codepoints in the range 0-255. - var latin1String = window.atob(base64); + const latin1String = window.atob(base64); // Encode the string as a Uint8Array - var uint8Array = new Uint8Array(latin1String.length); - for (var i = 0; i < latin1String.length; i++) { + const uint8Array = new Uint8Array(latin1String.length); + for (let i = 0; i < latin1String.length; i++) { uint8Array[i] = latin1String.charCodeAt(i); } return uint8Array; diff --git a/test/.eslintrc.js b/test/.eslintrc.js index 4cc4659d7d..537f989b62 100644 --- a/test/.eslintrc.js +++ b/test/.eslintrc.js @@ -2,4 +2,9 @@ module.exports = { env: { mocha: true, }, -} + + // mocha defines a 'this' + rules: { + "babel/no-invalid-this": "off", + }, +}; diff --git a/test/utils/MegolmExportEncryption-test.js b/test/utils/MegolmExportEncryption-test.js index 1c90c7a030..2637097837 100644 --- a/test/utils/MegolmExportEncryption-test.js +++ b/test/utils/MegolmExportEncryption-test.js @@ -25,23 +25,40 @@ const TEST_VECTORS=[ [ "plain", "password", - "-----BEGIN MEGOLM SESSION DATA-----\nAXNhbHRzYWx0c2FsdHNhbHSIiIiIiIiIiIiIiIiIiIiIAAAACmIRUW2OjZ3L2l6j9h0lHlV3M2dx\ncissyYBxjsfsAndErh065A8=\n-----END MEGOLM SESSION DATA-----" + "-----BEGIN MEGOLM SESSION DATA-----\n" + + "AXNhbHRzYWx0c2FsdHNhbHSIiIiIiIiIiIiIiIiIiIiIAAAACmIRUW2OjZ3L2l6j9h0lHlV3M2dx\n" + + "cissyYBxjsfsAndErh065A8=\n" + + "-----END MEGOLM SESSION DATA-----", ], [ "Hello, World", "betterpassword", - "-----BEGIN MEGOLM SESSION DATA-----\nAW1vcmVzYWx0bW9yZXNhbHT//////////wAAAAAAAAAAAAAD6KyBpe1Niv5M5NPm4ZATsJo5nghk\nKYu63a0YQ5DRhUWEKk7CcMkrKnAUiZny\n-----END MEGOLM SESSION DATA-----" + "-----BEGIN MEGOLM SESSION DATA-----\n" + + "AW1vcmVzYWx0bW9yZXNhbHT//////////wAAAAAAAAAAAAAD6KyBpe1Niv5M5NPm4ZATsJo5nghk\n" + + "KYu63a0YQ5DRhUWEKk7CcMkrKnAUiZny\n" + + "-----END MEGOLM SESSION DATA-----", ], [ "alphanumericallyalphanumericallyalphanumericallyalphanumerically", "SWORDFISH", - "-----BEGIN MEGOLM SESSION DATA-----\nAXllc3NhbHR5Z29vZG5lc3P//////////wAAAAAAAAAAAAAD6OIW+Je7gwvjd4kYrb+49gKCfExw\nMgJBMD4mrhLkmgAngwR1pHjbWXaoGybtiAYr0moQ93GrBQsCzPbvl82rZhaXO3iH5uHo/RCEpOqp\nPgg29363BGR+/Ripq/VCLKGNbw==\n-----END MEGOLM SESSION DATA-----" + "-----BEGIN MEGOLM SESSION DATA-----\n" + + "AXllc3NhbHR5Z29vZG5lc3P//////////wAAAAAAAAAAAAAD6OIW+Je7gwvjd4kYrb+49gKCfExw\n" + + "MgJBMD4mrhLkmgAngwR1pHjbWXaoGybtiAYr0moQ93GrBQsCzPbvl82rZhaXO3iH5uHo/RCEpOqp\n" + + "Pgg29363BGR+/Ripq/VCLKGNbw==\n" + + "-----END MEGOLM SESSION DATA-----", ], [ "alphanumericallyalphanumericallyalphanumericallyalphanumerically", - "passwordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpassword", - "-----BEGIN MEGOLM SESSION DATA-----\nAf//////////////////////////////////////////AAAD6IAZJy7IQ7Y0idqSw/bmpngEEVVh\ngsH+8ptgqxw6ZVWQnohr8JsuwH9SwGtiebZuBu5smPCO+RFVWH2cQYslZijXv/BEH/txvhUrrtCd\nbWnSXS9oymiqwUIGs08sXI33ZA==\n-----END MEGOLM SESSION DATA-----" - ] + "passwordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpassword" + + "passwordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpassword" + + "passwordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpassword" + + "passwordpasswordpasswordpasswordpassword", + "-----BEGIN MEGOLM SESSION DATA-----\n" + + "Af//////////////////////////////////////////AAAD6IAZJy7IQ7Y0idqSw/bmpngEEVVh\n" + + "gsH+8ptgqxw6ZVWQnohr8JsuwH9SwGtiebZuBu5smPCO+RFVWH2cQYslZijXv/BEH/txvhUrrtCd\n" + + "bWnSXS9oymiqwUIGs08sXI33ZA==\n" + + "-----END MEGOLM SESSION DATA-----", + ], ] ; @@ -55,7 +72,7 @@ describe('MegolmExportEncryption', function() { if (!window.crypto.subtle && !window.crypto.webkitSubtle) { this.skip(); } - }) + }); beforeEach(function() { testUtils.beforeEach(this); @@ -64,14 +81,14 @@ describe('MegolmExportEncryption', function() { describe('decrypt', function() { it('should handle missing header', function() { const input=stringToArray(`-----`); - expect(()=>{MegolmExportEncryption.decryptMegolmKeyFile(input, '')}) + expect(()=>MegolmExportEncryption.decryptMegolmKeyFile(input, '')) .toThrow('Header line not found'); }); it('should handle missing trailer', function() { const input=stringToArray(`-----BEGIN MEGOLM SESSION DATA----- -----`); - expect(()=>{MegolmExportEncryption.decryptMegolmKeyFile(input, '')}) + expect(()=>MegolmExportEncryption.decryptMegolmKeyFile(input, '')) .toThrow('Trailer line not found'); }); @@ -81,7 +98,7 @@ AXNhbHRzYWx0c2FsdHNhbHSIiIiIiIiIiIiIiIiIiIiIAAAACmIRUW2OjZ3L2l6j9h0lHlV3M2dx cissyYBxjsfsAn -----END MEGOLM SESSION DATA----- `); - expect(()=>{MegolmExportEncryption.decryptMegolmKeyFile(input, '')}) + expect(()=>MegolmExportEncryption.decryptMegolmKeyFile(input, '')) .toThrow('Invalid file: too short'); }); @@ -94,12 +111,12 @@ cissyYBxjsfsAn const [plain, password, input] = TEST_VECTORS[i]; return MegolmExportEncryption.decryptMegolmKeyFile( - stringToArray(input), password + stringToArray(input), password, ).then((decrypted) => { expect(decrypted).toEqual(plain); return next(i+1); - }) - }; + }); + } return next(0).catch(done); }); }); @@ -115,7 +132,7 @@ cissyYBxjsfsAn input, password, {kdf_rounds: 1000}, ).then((ciphertext) => { return MegolmExportEncryption.decryptMegolmKeyFile( - ciphertext, password + ciphertext, password, ); }).then((plaintext) => { expect(plaintext).toEqual(input); From f320f7d475465f0fa1fae3e15f2c666a2d14c678 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 8 Jun 2017 14:30:45 +0100 Subject: [PATCH 0671/1016] Remove redundant action id --- 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 b65fa0be1c..1f9d2f6bb8 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -1109,7 +1109,6 @@ module.exports = React.createClass({ }; const payload = { - id: '#mylovelyid', action: 'view_room', event_id: eventId, highlighted: Boolean(eventId), From f6051f663df00e4c05a9483e6760450a67ba699d Mon Sep 17 00:00:00 2001 From: dtygel Date: Thu, 8 Jun 2017 13:32:21 +0000 Subject: [PATCH 0672/1016] Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (853 of 853 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/pt_BR/ --- src/i18n/strings/pt_BR.json | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json index 99175c874e..231c6a464d 100644 --- a/src/i18n/strings/pt_BR.json +++ b/src/i18n/strings/pt_BR.json @@ -876,5 +876,35 @@ "You have disabled URL previews by default.": "Você desabilitou pré-visualizações de links por padrão.", "You have enabled URL previews by default.": "Você habilitou pré-visualizações de links por padrão.", "You have entered an invalid contact. Try using their Matrix ID or email address.": "Você inseriu um contato inválido. Tente usar o ID Matrix ou endereço de e-mail da pessoa que está buscando.", - "Hide removed messages": "Ocultar mensagens removidas" + "Hide removed messages": "Ocultar mensagens removidas", + "Add": "Adicionar", + "%(count)s new messages.one": "%(count)s nova mensagem", + "%(count)s new messages.other": "%(count)s novas mensagens", + "Disable markdown formatting": "Desabilitar formatação MarkDown", + "Error: Problem communicating with the given homeserver.": "Erro: problema de comunicação com o Servidor de Base fornecido.", + "Failed to fetch avatar URL": "Falha ao obter a URL da imagem de perfil", + "Home": "Início", + "The phone number entered looks invalid": "O número de telefone inserido parece ser inválido", + "Uploading %(filename)s and %(count)s others.zero": "Enviando o arquivo %(filename)s", + "Uploading %(filename)s and %(count)s others.one": "Enviando o arquivo %(filename)s e %(count)s outros arquivos", + "Uploading %(filename)s and %(count)s others.other": "Enviando o arquivo %(filename)s e %(count)s outros arquivos", + "Username invalid: %(errMessage)s": "Nome de usuária(o) inválido: %(errMessage)s", + "Searching known users": "Buscando pessoas conhecidas", + "You must register to use this functionality": "Você deve se registrar para poder usar esta funcionalidade", + "Resend all or cancel all now. You can also select individual messages to resend or cancel.": "Reenviar todas ou cancelar todas agora. Você também pode selecionar mensagens individuais que queira reenviar ou cancelar.", + "Create new room": "Criar nova sala", + "Welcome page": "Página de boas vindas", + "Room directory": "Lista pública de salas", + "Start chat": "Iniciar conversa pessoal", + "New Password": "Nova senha", + "Start chatting": "Iniciar a conversa", + "Start Chatting": "Iniciar a conversa", + "Click on the button below to start chatting!": "Clique no botão abaixo para começar a conversar!", + "Create a new chat or reuse an existing one": "Criar uma nova conversa ou reutilizar alguma já existente", + "You already have existing direct chats with this user:": "Você já tem conversas pessoais com esta pessoa:", + "Username available": "Nome de usuária(o) disponível", + "Username not available": "Nome de usuária(o) indisponível", + "Something went wrong!": "Algo deu errado!", + "This will be your account name on the homeserver, or you can pick a different server.": "Este será seu nome de conta no Servidor de Base , ou então você pode escolher um servidor diferente.", + "If you already have a Matrix account you can log in instead.": "Se você já tem uma conta Matrix, pode também fazer login." } From e3ac78bd2804f8d6f76c99d24175bd31dcf3735a Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Thu, 8 Jun 2017 14:36:20 +0100 Subject: [PATCH 0673/1016] Self-review --- src/components/views/room_settings/ColorSettings.js | 1 - src/i18n/strings/en_EN.json | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/views/room_settings/ColorSettings.js b/src/components/views/room_settings/ColorSettings.js index 2c18cb74f9..5fc845a541 100644 --- a/src/components/views/room_settings/ColorSettings.js +++ b/src/components/views/room_settings/ColorSettings.js @@ -20,7 +20,6 @@ var sdk = require('../../../index'); var Tinter = require('../../../Tinter'); var MatrixClientPeg = require("../../../MatrixClientPeg"); var Modal = require("../../../Modal"); -import { _t } from '../../../languageHandler'; import dis from '../../../dispatcher'; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 0ecbf3da55..3619424d19 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -481,7 +481,6 @@ "%(roomName)s is not accessible at this time.": "%(roomName)s is not accessible at this time.", "Rooms": "Rooms", "Save": "Save", - "Saving room color settings is only available to registered users": "Saving room color settings is only available to registered users", "Scroll to bottom of page": "Scroll to bottom of page", "Scroll to unread messages": "Scroll to unread messages", "Search": "Search", @@ -648,7 +647,7 @@ "Who would you like to communicate with?": "Who would you like to communicate with?", "Searching known users": "Searching known users", "%(senderName)s withdrew %(targetName)s's invitation.": "%(senderName)s withdrew %(targetName)s's invitation.", - "Would you like to accept or decline this invitation?": "Would you like to accept or decline this invitation?". + "Would you like to accept or decline this invitation?": "Would you like to accept or decline this invitation?", "You already have existing direct chats with this user:": "You already have existing direct chats with this user:", "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", From b16e652acc1c9876606879458483c5bf9e9b75f2 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Thu, 8 Jun 2017 07:54:47 +0100 Subject: [PATCH 0674/1016] rewrite MegolmExportEncryption using async/await ... to make it easier to add exception handling --- src/utils/MegolmExportEncryption.js | 193 +++++++++++----------- test/utils/MegolmExportEncryption-test.js | 24 ++- 2 files changed, 112 insertions(+), 105 deletions(-) diff --git a/src/utils/MegolmExportEncryption.js b/src/utils/MegolmExportEncryption.js index de39ea71cc..e91dc06b04 100644 --- a/src/utils/MegolmExportEncryption.js +++ b/src/utils/MegolmExportEncryption.js @@ -36,7 +36,7 @@ const subtleCrypto = window.crypto.subtle || window.crypto.webkitSubtle; * @param {String} password * @return {Promise} promise for decrypted output */ -export function decryptMegolmKeyFile(data, password) { +export async function decryptMegolmKeyFile(data, password) { const body = unpackMegolmKeyFile(data); // check we have a version byte @@ -60,33 +60,30 @@ export function decryptMegolmKeyFile(data, password) { const ciphertext = body.subarray(37, 37+ciphertextLength); const hmac = body.subarray(-32); - return deriveKeys(salt, iterations, password).then((keys) => { - const [aesKey, hmacKey] = keys; + const [aesKey, hmacKey] = await deriveKeys(salt, iterations, password); - const toVerify = body.subarray(0, -32); - return subtleCrypto.verify( - {name: 'HMAC'}, - hmacKey, - hmac, - toVerify, - ).then((isValid) => { - if (!isValid) { - throw new Error('Authentication check failed: incorrect password?'); - } + const toVerify = body.subarray(0, -32); + const isValid = await subtleCrypto.verify( + {name: 'HMAC'}, + hmacKey, + hmac, + toVerify, + ); + if (!isValid) { + throw new Error('Authentication check failed: incorrect password?'); + } - return subtleCrypto.decrypt( - { - name: "AES-CTR", - counter: iv, - length: 64, - }, - aesKey, - ciphertext, - ); - }); - }).then((plaintext) => { - return new TextDecoder().decode(new Uint8Array(plaintext)); - }); + const plaintext = await subtleCrypto.decrypt( + { + name: "AES-CTR", + counter: iv, + length: 64, + }, + aesKey, + ciphertext, + ); + + return new TextDecoder().decode(new Uint8Array(plaintext)); } @@ -100,7 +97,7 @@ export function decryptMegolmKeyFile(data, password) { * key-derivation function. * @return {Promise} promise for encrypted output */ -export function encryptMegolmKeyFile(data, password, options) { +export async function encryptMegolmKeyFile(data, password, options) { options = options || {}; const kdfRounds = options.kdf_rounds || 500000; @@ -115,44 +112,42 @@ export function encryptMegolmKeyFile(data, password, options) { // of a single bit of iv is a price we have to pay. iv[9] &= 0x7f; - return deriveKeys(salt, kdfRounds, password).then((keys) => { - const [aesKey, hmacKey] = keys; + const [aesKey, hmacKey] = await deriveKeys(salt, kdfRounds, password); - return subtleCrypto.encrypt( - { - name: "AES-CTR", - counter: iv, - length: 64, - }, - aesKey, - new TextEncoder().encode(data), - ).then((ciphertext) => { - const cipherArray = new Uint8Array(ciphertext); - const bodyLength = (1+salt.length+iv.length+4+cipherArray.length+32); - const resultBuffer = new Uint8Array(bodyLength); - let idx = 0; - resultBuffer[idx++] = 1; // version - resultBuffer.set(salt, idx); idx += salt.length; - resultBuffer.set(iv, idx); idx += iv.length; - resultBuffer[idx++] = kdfRounds >> 24; - resultBuffer[idx++] = (kdfRounds >> 16) & 0xff; - resultBuffer[idx++] = (kdfRounds >> 8) & 0xff; - resultBuffer[idx++] = kdfRounds & 0xff; - resultBuffer.set(cipherArray, idx); idx += cipherArray.length; + const ciphertext = await subtleCrypto.encrypt( + { + name: "AES-CTR", + counter: iv, + length: 64, + }, + aesKey, + new TextEncoder().encode(data), + ); - const toSign = resultBuffer.subarray(0, idx); + const cipherArray = new Uint8Array(ciphertext); + const bodyLength = (1+salt.length+iv.length+4+cipherArray.length+32); + const resultBuffer = new Uint8Array(bodyLength); + let idx = 0; + resultBuffer[idx++] = 1; // version + resultBuffer.set(salt, idx); idx += salt.length; + resultBuffer.set(iv, idx); idx += iv.length; + resultBuffer[idx++] = kdfRounds >> 24; + resultBuffer[idx++] = (kdfRounds >> 16) & 0xff; + resultBuffer[idx++] = (kdfRounds >> 8) & 0xff; + resultBuffer[idx++] = kdfRounds & 0xff; + resultBuffer.set(cipherArray, idx); idx += cipherArray.length; - return subtleCrypto.sign( - {name: 'HMAC'}, - hmacKey, - toSign, - ).then((hmac) => { - hmac = new Uint8Array(hmac); - resultBuffer.set(hmac, idx); - return packMegolmKeyFile(resultBuffer); - }); - }); - }); + const toSign = resultBuffer.subarray(0, idx); + + const hmac = await subtleCrypto.sign( + {name: 'HMAC'}, + hmacKey, + toSign, + ); + + const hmacArray = new Uint8Array(hmac); + resultBuffer.set(hmacArray, idx); + return packMegolmKeyFile(resultBuffer); } /** @@ -163,51 +158,51 @@ export function encryptMegolmKeyFile(data, password, options) { * @param {String} password password * @return {Promise<[CryptoKey, CryptoKey]>} promise for [aes key, hmac key] */ -function deriveKeys(salt, iterations, password) { +async function deriveKeys(salt, iterations, password) { const start = new Date(); - return subtleCrypto.importKey( + const key = await subtleCrypto.importKey( 'raw', new TextEncoder().encode(password), {name: 'PBKDF2'}, false, ['deriveBits'], - ).then((key) => { - return subtleCrypto.deriveBits( - { - name: 'PBKDF2', - salt: salt, - iterations: iterations, - hash: 'SHA-512', - }, - key, - 512, - ); - }).then((keybits) => { - const now = new Date(); - console.log("E2e import/export: deriveKeys took " + (now - start) + "ms"); + ); - const aesKey = keybits.slice(0, 32); - const hmacKey = keybits.slice(32); + const keybits = await subtleCrypto.deriveBits( + { + name: 'PBKDF2', + salt: salt, + iterations: iterations, + hash: 'SHA-512', + }, + key, + 512, + ); - const aesProm = subtleCrypto.importKey( - 'raw', - aesKey, - {name: 'AES-CTR'}, - false, - ['encrypt', 'decrypt'], - ); - const hmacProm = subtleCrypto.importKey( - 'raw', - hmacKey, - { - name: 'HMAC', - hash: {name: 'SHA-256'}, - }, - false, - ['sign', 'verify'], - ); - return Promise.all([aesProm, hmacProm]); - }); + const now = new Date(); + console.log("E2e import/export: deriveKeys took " + (now - start) + "ms"); + + const aesKey = keybits.slice(0, 32); + const hmacKey = keybits.slice(32); + + const aesProm = subtleCrypto.importKey( + 'raw', + aesKey, + {name: 'AES-CTR'}, + false, + ['encrypt', 'decrypt'], + ); + const hmacProm = subtleCrypto.importKey( + 'raw', + hmacKey, + { + name: 'HMAC', + hash: {name: 'SHA-256'}, + }, + false, + ['sign', 'verify'], + ); + return await Promise.all([aesProm, hmacProm]); } const HEADER_LINE = '-----BEGIN MEGOLM SESSION DATA-----'; diff --git a/test/utils/MegolmExportEncryption-test.js b/test/utils/MegolmExportEncryption-test.js index 2637097837..fbd945ced6 100644 --- a/test/utils/MegolmExportEncryption-test.js +++ b/test/utils/MegolmExportEncryption-test.js @@ -81,15 +81,23 @@ describe('MegolmExportEncryption', function() { describe('decrypt', function() { it('should handle missing header', function() { const input=stringToArray(`-----`); - expect(()=>MegolmExportEncryption.decryptMegolmKeyFile(input, '')) - .toThrow('Header line not found'); + return MegolmExportEncryption.decryptMegolmKeyFile(input, '') + .then((res) => { + throw new Error('expected to throw'); + }, (error) => { + expect(error.message).toEqual('Header line not found'); + }); }); it('should handle missing trailer', function() { const input=stringToArray(`-----BEGIN MEGOLM SESSION DATA----- -----`); - expect(()=>MegolmExportEncryption.decryptMegolmKeyFile(input, '')) - .toThrow('Trailer line not found'); + return MegolmExportEncryption.decryptMegolmKeyFile(input, '') + .then((res) => { + throw new Error('expected to throw'); + }, (error) => { + expect(error.message).toEqual('Trailer line not found'); + }); }); it('should handle a too-short body', function() { @@ -98,8 +106,12 @@ AXNhbHRzYWx0c2FsdHNhbHSIiIiIiIiIiIiIiIiIiIiIAAAACmIRUW2OjZ3L2l6j9h0lHlV3M2dx cissyYBxjsfsAn -----END MEGOLM SESSION DATA----- `); - expect(()=>MegolmExportEncryption.decryptMegolmKeyFile(input, '')) - .toThrow('Invalid file: too short'); + return MegolmExportEncryption.decryptMegolmKeyFile(input, '') + .then((res) => { + throw new Error('expected to throw'); + }, (error) => { + expect(error.message).toEqual('Invalid file: too short'); + }); }); it('should decrypt a range of inputs', function(done) { From f2efdf8a4f4fbf0122dbd9aa266bde4ed713ccd5 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Thu, 8 Jun 2017 14:45:59 +0100 Subject: [PATCH 0675/1016] Review comments --- src/components/views/rooms/AuxPanel.js | 4 ++-- src/components/views/rooms/RoomPreviewBar.js | 4 ++-- src/components/views/rooms/RoomSettings.js | 2 +- src/i18n/strings/en_EN.json | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/views/rooms/AuxPanel.js b/src/components/views/rooms/AuxPanel.js index 7e7a670aea..55b68d1eb1 100644 --- a/src/components/views/rooms/AuxPanel.js +++ b/src/components/views/rooms/AuxPanel.js @@ -96,8 +96,8 @@ module.exports = React.createClass({ else { joinText = ( {_tJsx( - "Join as voice or video.", - [/(.*?)<\/a>/, /(.*?)<\/a>/], + "Join as voice or video.", + [/(.*?)<\/voiceText>/, /(.*?)<\/videoText>/], [ (sub) => { this.onConferenceNotificationClick(event, 'voice');}} href="#">{sub}, (sub) => { this.onConferenceNotificationClick(event, 'video');}} href="#">{sub}, diff --git a/src/components/views/rooms/RoomPreviewBar.js b/src/components/views/rooms/RoomPreviewBar.js index 8aa1f353a6..34166452cb 100644 --- a/src/components/views/rooms/RoomPreviewBar.js +++ b/src/components/views/rooms/RoomPreviewBar.js @@ -138,8 +138,8 @@ module.exports = React.createClass({
    { _tJsx( - 'Would you like to accept or decline this invitation?', - [/(.*?)<\/a>/, /(.*?)<\/a>/], + 'Would you like to accept or decline this invitation?', + [/(.*?)<\/acceptText>/, /(.*?)<\/declineText>/], [ (sub) => {sub}, (sub) => {sub} diff --git a/src/components/views/rooms/RoomSettings.js b/src/components/views/rooms/RoomSettings.js index f31fc68afc..997c451aa0 100644 --- a/src/components/views/rooms/RoomSettings.js +++ b/src/components/views/rooms/RoomSettings.js @@ -754,7 +754,7 @@ module.exports = React.createClass({ if (this.state.join_rule === "public" && aliasCount == 0) { addressWarning =
    - { _tJsx( + { _tJsx( 'To link to a room it must have an address.', /(.*?)<\/a>/, (sub) => {sub} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 3619424d19..d29c26aa28 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -365,7 +365,7 @@ "'%(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", "Sign in with": "Sign in with", - "Join as voice or video.": "Join as voice or video.", + "Join as voice or video.": "Join as voice or video.", "Join Room": "Join Room", "joined and left": "joined and left", "joined": "joined", @@ -647,7 +647,7 @@ "Who would you like to communicate with?": "Who would you like to communicate with?", "Searching known users": "Searching known users", "%(senderName)s withdrew %(targetName)s's invitation.": "%(senderName)s withdrew %(targetName)s's invitation.", - "Would you like to accept or decline this invitation?": "Would you like to accept or decline this invitation?", + "Would you like to accept or decline this invitation?": "Would you like to accept or decline this invitation?", "You already have existing direct chats with this user:": "You already have existing direct chats with this user:", "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", From 71eb40585991da54b42961f9a970fc2107caee38 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 8 Jun 2017 14:53:21 +0100 Subject: [PATCH 0676/1016] Add comments & remove redundant check --- src/HtmlUtils.js | 5 +++++ src/components/views/messages/TextualBody.js | 13 ++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js index 5e8cfc3755..aec32092ed 100644 --- a/src/HtmlUtils.js +++ b/src/HtmlUtils.js @@ -365,6 +365,11 @@ export function bodyToHtml(content, highlights, opts) { } function addCodeCopyButton(safeBody) { + // Adds 'copy' buttons to pre blocks + // Note that this only manipulates the markup to add the buttons: + // we need to add the event handlers once the nodes are in the DOM + // since we can't save functions in the markup. + // This is done in TextualBody const el = document.createElement("div"); el.innerHTML = safeBody; const codeBlocks = Array.from(el.getElementsByTagName("pre")); diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js index 45fca566b9..d5a1977cdd 100644 --- a/src/components/views/messages/TextualBody.js +++ b/src/components/views/messages/TextualBody.js @@ -94,14 +94,13 @@ module.exports = React.createClass({ } }, 10); } + // add event handlers to the 'copy code' buttons const buttons = ReactDOM.findDOMNode(this).getElementsByClassName("mx_EventTile_copyButton"); - if (buttons.length > 0) { - for (let i = 0; i < buttons.length; i++) { - buttons[i].onclick = (e) => { - const copyCode = buttons[i].parentNode.getElementsByTagName("code")[0]; - this.copyToClipboard(copyCode.textContent); - }; - } + for (let i = 0; i < buttons.length; i++) { + buttons[i].onclick = (e) => { + const copyCode = buttons[i].parentNode.getElementsByTagName("code")[0]; + this.copyToClipboard(copyCode.textContent); + }; } } }, From 9baaf5499f30c80f8018279c69a742f572483805 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Thu, 8 Jun 2017 14:58:54 +0100 Subject: [PATCH 0677/1016] Linting --- src/components/views/dialogs/SessionRestoreErrorDialog.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/dialogs/SessionRestoreErrorDialog.js b/src/components/views/dialogs/SessionRestoreErrorDialog.js index 51651b4aaa..a3eb7c6962 100644 --- a/src/components/views/dialogs/SessionRestoreErrorDialog.js +++ b/src/components/views/dialogs/SessionRestoreErrorDialog.js @@ -47,7 +47,7 @@ export default React.createClass({

    {_tJsx( "Otherwise, click here to send a bug report.", - /(.*?)<\/a>/, (sub) => {sub} + /(.*?)<\/a>/, (sub) => {sub}, )}

    ); From 614006a256c725804fcb7d7f031e9bbc15fb1107 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Thu, 8 Jun 2017 15:09:23 +0100 Subject: [PATCH 0678/1016] remove duplicate keys from en_EN --- src/i18n/strings/en_EN.json | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index d29c26aa28..3540feddee 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -645,7 +645,6 @@ "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?", - "Searching known users": "Searching known users", "%(senderName)s withdrew %(targetName)s's invitation.": "%(senderName)s withdrew %(targetName)s's invitation.", "Would you like to accept or decline this invitation?": "Would you like to accept or decline this invitation?", "You already have existing direct chats with this user:": "You already have existing direct chats with this user:", @@ -817,7 +816,6 @@ "This process allows you to import encryption keys that you had previously exported from another Matrix client. You will then be able to decrypt any messages that the other client could decrypt.": "This process allows you to import encryption keys that you had previously exported from another Matrix client. You will then be able to decrypt any messages that the other client could decrypt.", "The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.": "The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.", "You must join the room to see its files": "You must join the room to see its files", - "Server may be unavailable, overloaded, or you hit a bug.": "Server may be unavailable, overloaded, or you hit a bug.", "Reject all %(invitedRooms)s invites": "Reject all %(invitedRooms)s invites", "Start new chat": "Start new chat", "Guest users can't invite users. Please register.": "Guest users can't invite users. Please register.", @@ -897,15 +895,9 @@ "Online": "Online", "Idle": "Idle", "Offline": "Offline", - "disabled": "disabled", - "enabled": "enabled", "Start chatting": "Start chatting", "Start Chatting": "Start Chatting", "Click on the button below to start chatting!": "Click on the button below to start chatting!", - "Create a new chat or reuse an existing one": "Create a new chat or reuse an existing one", - "You already have existing direct chats with this user:": "You already have existing direct chats with this user:", - "Start new chat": "Start new chat", - "Disable URL previews for this room (affects only you)": "Disable URL previews for this room (affects only you)", "$senderDisplayName changed the room avatar to ": "$senderDisplayName changed the room avatar to ", "%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s removed the room avatar.", "%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s changed the avatar for %(roomName)s", From 74e426055699b2b0415710cc4af3661f297592f5 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Thu, 8 Jun 2017 15:14:46 +0100 Subject: [PATCH 0679/1016] fix fullstops --- scripts/fix-i18n.pl | 7 ++++++- src/i18n/strings/de_DE.json | 12 ++++++------ src/i18n/strings/en_US.json | 6 +++--- src/i18n/strings/fr.json | 6 +++--- src/i18n/strings/pt.json | 6 +++--- src/i18n/strings/pt_BR.json | 6 +++--- src/i18n/strings/ru.json | 10 +++++----- src/i18n/strings/th.json | 2 +- src/i18n/strings/zh_Hans.json | 2 +- src/i18n/strings/zh_Hant.json | 4 ++-- 10 files changed, 33 insertions(+), 28 deletions(-) diff --git a/scripts/fix-i18n.pl b/scripts/fix-i18n.pl index ea19d710df..def352463d 100755 --- a/scripts/fix-i18n.pl +++ b/scripts/fix-i18n.pl @@ -66,6 +66,11 @@ Some of your messages have not been sent. This room is private or inaccessible to guests. You may be able to join if you register. 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 you do not have permission to view the message in question. +This action cannot be performed by a guest user. Please register to be able to do this. +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 you do not have permission to view the message in question. +You are trying to access %(roomName)s. +You will not be able to undo this change as you are promoting the user to have the same power level as yourself. EOT )]; } @@ -89,7 +94,7 @@ if ($_ =~ m/^(\s+)"(.*?)"(: *)"(.*?)"(,?)$/) { $sub = 1; } - if ($src eq $fixup && $dst !~ /\.$/) { + if ($ARGV !~ /(zh_Hans|zh_Hant|th)\.json$/ && $src eq $fixup && $dst !~ /\.$/) { print STDERR "fixing up dst: $dst\n"; $dst .= '.'; $sub = 1; diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index 4102d7be56..384e27027b 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -427,7 +427,7 @@ "You're not in any rooms yet! Press": "Du bist noch keinem Raum beigetreten! Drücke", "click to reveal": "Klicke zum anzeigen", "To remove other users' messages": "Um Nachrichten anderer Nutzer zu verbergen", - "You are trying to access %(roomName)s": "Du versuchst, auf den Raum \"%(roomName)s\" zuzugreifen", + "You are trying to access %(roomName)s.": "Du versuchst, auf den Raum \"%(roomName)s\" zuzugreifen.", "af": "Afrikaans", "ar-ae": "Arabisch (VAE)", "ar-bh": "Arabisch (Bahrain)", @@ -611,12 +611,12 @@ "Some of your messages have not been sent.": "Einige deiner Nachrichten wurden 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", + "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 zugänglich. Du kannst jedoch eventuell beitreten, 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": "Der Versuch, einen spezifischen Punkt im Chatverlauf zu laden, ist fehlgeschlagen. Der Punkt konnte nicht gefunden werden", + "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 zugänglich. Du kannst jedoch eventuell beitreten, 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.": "Der Versuch, einen spezifischen Punkt im Chatverlauf zu laden, ist fehlgeschlagen. Der Punkt konnte nicht gefunden werden.", "Turn Markdown off": "Markdown deaktiveren", "Turn Markdown on": "Markdown einschalten", "Unable to load device list": "Geräteliste konnte nicht geladen werden", @@ -625,7 +625,7 @@ "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 diese Änderung nicht rückgängig machen können, da der Nutzer dasselbe Berechtigungslevel wie du selbst erhalten wird", + "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 diese Änderung nicht rückgängig machen können, da der Nutzer dasselbe Berechtigungslevel wie du selbst erhalten wird.", "Make Moderator": "Zum Moderator machen", "Room": "Raum", "(~%(searchCount)s results)": "(~%(searchCount)s Ergebnisse)", diff --git a/src/i18n/strings/en_US.json b/src/i18n/strings/en_US.json index 6736e39276..5cdfd65d27 100644 --- a/src/i18n/strings/en_US.json +++ b/src/i18n/strings/en_US.json @@ -489,7 +489,7 @@ "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", "The signing key you provided matches the signing key you received from %(userId)s's device %(deviceId)s. Device marked as verified.": "The signing key you provided matches the signing key you received from %(userId)s's device %(deviceId)s. Device marked as verified.", - "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 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?", @@ -588,7 +588,7 @@ "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 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", @@ -609,7 +609,7 @@ "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", + "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", diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index ffd1ef5885..1058db4aca 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -468,7 +468,7 @@ "tag direct chat": "marquer comme conversation directe", "The default role for new room members is": "Le rôle par défaut des nouveaux membres est", "The main address for this room is": "L'adresse principale pour ce salon est", - "This action cannot be performed by a guest user. Please register to be able to do this": "Cette action ne peut être effectuée par un visiteur. Merci de vous enregistrer afin de pouvoir effectuer cette action", + "This action cannot be performed by a guest user. Please register to be able to do this.": "Cette action ne peut être effectuée par un visiteur. Merci de vous enregistrer afin de pouvoir effectuer cette action.", "This email address is already in use": "Cette adresse e-mail est déjà utilisée", "This email address was not found": "Cette adresse e-mail n’a pas été trouvée", "%(actionVerb)s this person?": "%(actionVerb)s cette personne ?", @@ -557,7 +557,7 @@ "Would you like to": "Voulez-vous", "You are already in a call.": "Vous êtes déjà dans un appel.", "You're not in any rooms yet! Press": "Vous n’êtes dans aucun salon ! Cliquez", - "You are trying to access %(roomName)s": "Vous essayez d'accéder à %(roomName)s", + "You are trying to access %(roomName)s.": "Vous essayez d'accéder à %(roomName)s.", "You cannot place a call with yourself.": "Vous ne pouvez pas passer d'appel avec vous-même.", "You cannot place VoIP calls in this browser.": "Vous ne pouvez pas passer d'appel voix dans cet explorateur.", "You do not have permission to post to this room": "Vous n’avez pas la permission de poster dans ce salon", @@ -576,7 +576,7 @@ "You seem to be uploading files, are you sure you want to quit?": "Vous semblez être en train de télécharger des fichiers, êtes-vous sûr(e) de vouloir quitter ?", "You should not yet trust it to secure data": "Vous ne pouvez pas encore lui faire confiance pour sécuriser vos données", "changing room on a RoomView is not supported": "changer de salon sur un RoomView n'est pas supporté", - "You will not be able to undo this change as you are promoting the user to have the same power level as yourself": "Vous ne pourrez pas défaire ce changement car vous promouvez l’utilisateur aux mêmes pouvoirs que vous", + "You will not be able to undo this change as you are promoting the user to have the same power level as yourself.": "Vous ne pourrez pas défaire ce changement car vous promouvez l’utilisateur aux mêmes pouvoirs que vous.", "Sun": "Dim", "Mon": "Lun", "Tue": "Mar", diff --git a/src/i18n/strings/pt.json b/src/i18n/strings/pt.json index 27d257de9e..850e95f61f 100644 --- a/src/i18n/strings/pt.json +++ b/src/i18n/strings/pt.json @@ -399,7 +399,7 @@ "%(senderName)s withdrew %(targetName)s's invitation.": "%(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 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.", @@ -630,7 +630,7 @@ "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", + "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", "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.", @@ -643,7 +643,7 @@ "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ê", + "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)", diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json index 99175c874e..9e218d5c36 100644 --- a/src/i18n/strings/pt_BR.json +++ b/src/i18n/strings/pt_BR.json @@ -399,7 +399,7 @@ "%(senderName)s withdrew %(targetName)s's invitation.": "%(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 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.", @@ -630,7 +630,7 @@ "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", + "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", "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.", @@ -643,7 +643,7 @@ "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ê", + "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)", diff --git a/src/i18n/strings/ru.json b/src/i18n/strings/ru.json index 647fbfe1d5..ce3d0d973d 100644 --- a/src/i18n/strings/ru.json +++ b/src/i18n/strings/ru.json @@ -362,7 +362,7 @@ "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 are trying to access %(roomName)s.": "Вы пытаетесь получить доступ %(roomName)s.", "You cannot place a call with yourself.": "Вы не можете позвонить самим себе.", "%(senderName)s withdrew %(targetName)s's invitation.": "%(senderName)s анулировал %(targetName)s's преглашение.", "Sep": "Сен.", @@ -575,7 +575,7 @@ "tag direct chat": "отметить прямое общение", "The default role for new room members 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 was not found": "Этот адрес электронной почты не найден", "The email address linked to your account must be entered.": "Необходимо ввести адрес электронной почты, связанный с вашей учётной записью.", @@ -752,8 +752,8 @@ "To remove other users' messages": "Удалять сообщения других пользователей", "To reset your password, enter the email address linked to your account": "Чтобы сбросить ваш пароль введите адрес email, который используется аккаунтом", "to tag as %(tagName)s": "отметить как %(tagName)s", - "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 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.": "Вы попытались загрузить указанное сообщение в комнате, однако сервер не смог его найти.", "Unable to load device list": "Невозможно загрузить список устройств", "Unknown (user, device) pair:": "Неизвестная пара пользователь-устройство:", "Unmute": "Разглушить", @@ -767,7 +767,7 @@ "You have entered an invalid contact. Try using their Matrix ID or email address.": "Вы ввели неправильный адрес. Попробуйте использовать Matrix ID или адрес email.", "You need to enter a user name.": "Необходимо ввести имя пользователя.", "You seem to be in a call, are you sure you want to quit?": "Вы учавствуете в звонке, вы уверены, что хотите выйти?", - "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.": "Вы не сможете отменить это действие так как даете пользователю такой же уровень доступа как и у вас.", "Set a Display Name": "Установить отображаемое имя", "(~%(searchCount)s results)": "(~%(searchCount)s результатов)", "%(severalUsers)shad their invitations withdrawn %(repeats)s times": "%(severalUsers)s отозвали свои приглашения %(repeats)s раз", diff --git a/src/i18n/strings/th.json b/src/i18n/strings/th.json index f97b6b8d50..7ec5b606fc 100644 --- a/src/i18n/strings/th.json +++ b/src/i18n/strings/th.json @@ -389,7 +389,7 @@ "Who would you like to add to this room?": "คุณต้องการเพิ่มใครเข้าห้องนี้?", "Who would you like to communicate with?": "คุณต้องการสื่อสารกับใคร?", "You're not in any rooms yet! Press": "คุณยังไม่ได้อยู่ในห้องใดเลย! กด", - "You are trying to access %(roomName)s": "คุณกำลังพยายามเข้าสู่ %(roomName)s", + "You are trying to access %(roomName)s.": "คุณกำลังพยายามเข้าสู่ %(roomName)s", "You have disabled URL previews by default.": "ค่าเริ่มต้นของคุณปิดใช้งานตัวอย่าง URL เอาไว้", "You have enabled URL previews by default.": "ค่าเริ่มต้นของคุณเปิดใช้งานตัวอย่าง URL เอาไว้", "you must be a": "คุณต้องเป็น", diff --git a/src/i18n/strings/zh_Hans.json b/src/i18n/strings/zh_Hans.json index e0db2b525b..e6eb5f632b 100644 --- a/src/i18n/strings/zh_Hans.json +++ b/src/i18n/strings/zh_Hans.json @@ -147,7 +147,7 @@ "Success": "成功", "The default role for new room members 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 was not found": "未找到此邮箱地址", "%(actionVerb)s this person?": "%(actionVerb)s 这个用户?", diff --git a/src/i18n/strings/zh_Hant.json b/src/i18n/strings/zh_Hant.json index 17901789e9..56cc8f6add 100644 --- a/src/i18n/strings/zh_Hant.json +++ b/src/i18n/strings/zh_Hant.json @@ -274,7 +274,7 @@ "Success": "成功", "The default role for new room members 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 was not found": "未找到此郵箱地址", "%(actionVerb)s this person?": "%(actionVerb)s 這個用戶?", @@ -290,7 +290,7 @@ "Would you like to": "你要", "You are already in a call.": "你已在電話通話中", "You're not in any rooms yet! Press": "你尚未加入任何聊天室!請按", - "You are trying to access %(roomName)s": "你將進入 %(roomName)聊天室", + "You are trying to access %(roomName)s.": "你將進入 %(roomName)聊天室", "You cannot place a call with yourself.": "你不能打電話給自已", "You cannot place VoIP calls in this browser.": "在此瀏覽器下無法置入網路電話通話", "Sun": "星期日", From aa20ad706e0f6110801a6e30c884f21be46d76a1 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 8 Jun 2017 15:30:43 +0100 Subject: [PATCH 0680/1016] Remove redundant `currentRoomAlias` --- 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 a115e89062..0493456e0e 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -621,7 +621,6 @@ module.exports = React.createClass({ page_type: PageTypes.RoomView, thirdPartyInvite: roomInfo.third_party_invite, roomOobData: roomInfo.oob_data, - currentRoomAlias: roomInfo.room_alias, autoJoin: roomInfo.auto_join, }; From 8ddc38c9b107ddd3d143e0269f7609c2444e4bbd Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 8 Jun 2017 15:32:31 +0100 Subject: [PATCH 0681/1016] Doc _viewRoom highlighted --- 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 0493456e0e..f34cc8b0ef 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -607,6 +607,7 @@ module.exports = React.createClass({ // @param {boolean=} roomInfo.show_settings Makes RoomView show the room settings dialog. // @param {string=} roomInfo.event_id ID of the event in this room to show: this will cause a switch to the // context of that particular event. + // @param {boolean=} roomInfo.highlighted If true, add event_id to the hash of the URL // @param {Object=} roomInfo.third_party_invite Object containing data about the third party // we received to join the room, if any. // @param {string=} roomInfo.third_party_invite.inviteSignUrl 3pid invite sign URL From 2e82bf40fb7996f909b68ab8ad4fdc94ef489c47 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 8 Jun 2017 15:34:20 +0100 Subject: [PATCH 0682/1016] Explain setting of highlighted in room/$eventId showScreen handling. --- 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 f34cc8b0ef..5f13f53d0b 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -1118,6 +1118,7 @@ module.exports = React.createClass({ const payload = { action: 'view_room', event_id: eventId, + // If an event ID is set (truthy), mark it as highlighted highlighted: Boolean(eventId), third_party_invite: thirdPartyInvite, oob_data: oobData, From dca2be632591c64914421329a3a9743232019ec6 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 8 Jun 2017 15:37:30 +0100 Subject: [PATCH 0683/1016] Add RoomView initial state and docs --- src/components/structures/RoomView.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index a4c589187b..f5db36a581 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -94,6 +94,13 @@ module.exports = React.createClass({ roomLoading: true, peekLoading: false, + // The event to be scrolled to initially + eventId: null, + // The offset in pixels from the event with which to scroll vertically + eventPixelOffset: null, + // Whether to highlight the event scrolled to + isEventHighlighted: null, + forwardingEvent: null, editingRoomSettings: false, uploadingRoomSettings: false, From e58e4db628c17e54d396570f4d6cf7e216eb11d6 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 8 Jun 2017 15:38:58 +0100 Subject: [PATCH 0684/1016] Explain why search results are cleared when eventId changes --- src/components/structures/RoomView.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index f5db36a581..180e6ebc89 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -170,6 +170,8 @@ module.exports = React.createClass({ isEventHighlighted: RoomViewStore.isEventHighlighted(), }; + // Clear the search results when clicking a search result (which changes the + // currently scrolled to event, this.state.eventId). if (this.state.eventId !== newState.eventId) { newState.searchResults = null; } From 59e649f326aaff78253ae40cb824f78969b3b611 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 8 Jun 2017 15:43:45 +0100 Subject: [PATCH 0685/1016] Add comment about roomId potentially not being resolved following an update from RoomViewStore --- src/components/structures/RoomView.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 180e6ebc89..cfa4038114 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -183,6 +183,8 @@ module.exports = React.createClass({ } this.setState(newState, () => { + // At this point, this.state.roomId could be null (e.g. the alias might not + // have been resolved yet) so anything called here must handle this case. this._onHaveRoom(); this.onRoom(MatrixClientPeg.get().getRoom(this.state.roomId)); }); From 1792fa45f4732aae292f85a85903d1199be87a65 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 8 Jun 2017 15:45:50 +0100 Subject: [PATCH 0686/1016] Null-guard roomId before sending a dispatch to update scroll state Otherwise we pointlessly assign the null key to something --- src/components/structures/RoomView.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index cfa4038114..06e65589d1 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -590,6 +590,10 @@ module.exports = React.createClass({ }, _updateScrollMap(roomId) { + // No point updating scroll state if the room ID hasn't been resolved yet + if (!roomId) { + return; + } dis.dispatch({ action: 'update_scroll_state', room_id: roomId, From d691c891e7353e95d309474f92bf2d1ff7cc6546 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 8 Jun 2017 15:49:48 +0100 Subject: [PATCH 0687/1016] Move all login error string generation into view This makes all the various hits done by login report the same useful error messages and gets rid of the broken ones like printing the http status code even if it was undefined. Also add text for the case of overzealous browser extensions because lots of people get bitten by it. --- src/Login.js | 18 +-------- src/components/structures/login/Login.js | 51 ++++++++++++++++-------- src/i18n/strings/en_EN.json | 2 +- 3 files changed, 36 insertions(+), 35 deletions(-) diff --git a/src/Login.js b/src/Login.js index 659fdb92bb..8db6e99b89 100644 --- a/src/Login.js +++ b/src/Login.js @@ -97,11 +97,6 @@ export default class Login { guest: true }; }, (error) => { - if (error.httpStatus === 403) { - error.friendlyText = _t("Guest access is disabled on this Home Server."); - } else { - error.friendlyText = _t("Failed to register as guest:") + ' ' + error.data; - } throw error; }); } @@ -157,14 +152,7 @@ export default class Login { accessToken: data.access_token }); }, function(error) { - if (error.httpStatus == 400 && loginParams.medium) { - error.friendlyText = ( - _t('This Home Server does not support login using email address.') - ); - } else if (error.httpStatus === 403) { - error.friendlyText = ( - _t('Incorrect username and/or password.') - ); + if (error.httpStatus === 403) { if (self._fallbackHsUrl) { var fbClient = Matrix.createClient({ baseUrl: self._fallbackHsUrl, @@ -184,10 +172,6 @@ export default class Login { throw error; }); } - } else { - error.friendlyText = ( - _t("There was a problem logging in.") + ' (HTTP ' + error.httpStatus + ")" - ); } throw error; }); diff --git a/src/components/structures/login/Login.js b/src/components/structures/login/Login.js index be440febdc..7d283e0e36 100644 --- a/src/components/structures/login/Login.js +++ b/src/components/structures/login/Login.js @@ -87,7 +87,24 @@ module.exports = React.createClass({ ).then((data) => { this.props.onLoggedIn(data); }, (error) => { - this._setStateFromError(error, true); + let errorText; + + // Some error strings only apply for logging in + const usingEmail = username.indexOf("@"); + if (error.httpStatus == 400 && usingEmail) { + errorText = _t('This Home Server does not support login using email address.'); + } else if (error.httpStatus == 401 || error.httpStatus === 403) { + errorText = _t('Incorrect username and/or password.'); + } else { + // other errors, not specific to doing a password login + errorText = this._errorTextFromError(error); + } + + this.setState({ + errorText: errorText, + // https://matrix.org/jira/browse/SYN-744 + loginIncorrect: error.httpStatus == 401 || error.httpStatus == 403 + }); }).finally(() => { this.setState({ busy: false @@ -110,7 +127,16 @@ module.exports = React.createClass({ this._loginLogic.loginAsGuest().then(function(data) { self.props.onLoggedIn(data); }, function(error) { - self._setStateFromError(error, true); + let errorText; + if (error.httpStatus === 403) { + errorText = _t("Guest access is disabled on this Home Server."); + } else { + errorText = self._errorTextFromError(error); + } + self.setState({ + errorText: errorText, + loginIncorrect: false, + }); }).finally(function() { self.setState({ busy: false @@ -183,31 +209,22 @@ module.exports = React.createClass({ currentFlow: self._getCurrentFlowStep(), }); }, function(err) { - self._setStateFromError(err, false); + self.setState({ + errorText: self._errorTextFromError(err), + loginIncorrect: false, + }); }).finally(function() { self.setState({ busy: false, }); - }); + }).done(); }, _getCurrentFlowStep: function() { return this._loginLogic ? this._loginLogic.getCurrentFlowStep() : null; }, - _setStateFromError: function(err, isLoginAttempt) { - this.setState({ - errorText: this._errorTextFromError(err), - // https://matrix.org/jira/browse/SYN-744 - loginIncorrect: isLoginAttempt && (err.httpStatus == 401 || err.httpStatus == 403) - }); - }, - _errorTextFromError(err) { - if (err.friendlyText) { - return err.friendlyText; - } - let errCode = err.errcode; if (!errCode && err.httpStatus) { errCode = "HTTP " + err.httpStatus; @@ -230,7 +247,7 @@ module.exports = React.createClass({ ; } else { errorText = - { _tJsx("Can't connect to homeserver - please check your connectivity and ensure your homeserver's SSL certificate is trusted.", + { _tJsx("Can't connect to homeserver - please check your connectivity and ensure your homeserver's SSL certificate is trusted and that a browser extension is not blocking requests.", /(.*?)<\/a>/, (sub) => { return { sub }; } )} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 8dbbb98423..6984d46ed4 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -180,7 +180,7 @@ "Bug Report": "Bug Report", "Bulk Options": "Bulk Options", "Call Timeout": "Call Timeout", - "Can't connect to homeserver - please check your connectivity and ensure your homeserver's SSL certificate is trusted.": "Can't connect to homeserver - please check your connectivity and ensure your homeserver's SSL certificate is trusted.", + "Can't connect to homeserver - please check your connectivity and ensure your homeserver's SSL certificate is trusted and that a browser extension is not blocking requests.": "Can't connect to homeserver - please check your connectivity and ensure your homeserver's SSL certificate is trusted and that a browser extension is not blocking requests.", "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or enable unsafe scripts.": "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or enable unsafe scripts.", "Can't load user settings": "Can't load user settings", "Change Password": "Change Password", From 000a045e3523ed7154fb3817e06d3ee5cf343439 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 8 Jun 2017 15:47:41 +0100 Subject: [PATCH 0688/1016] Rename RVS state (event -> initialEvent) and redocument --- src/components/structures/RoomView.js | 18 ++++++------- src/stores/RoomViewStore.js | 38 ++++++++++++++------------- 2 files changed, 29 insertions(+), 27 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 06e65589d1..942e3c8e07 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -165,14 +165,14 @@ module.exports = React.createClass({ roomLoading: RoomViewStore.isRoomLoading(), roomLoadError: RoomViewStore.getRoomLoadError(), joining: RoomViewStore.isJoining(), - eventId: RoomViewStore.getEventId(), - eventPixelOffset: RoomViewStore.getEventPixelOffset(), - isEventHighlighted: RoomViewStore.isEventHighlighted(), + initialEventId: RoomViewStore.getInitialEventId(), + initialEventPixelOffset: RoomViewStore.getInitialEventPixelOffset(), + isInitialEventHighlighted: RoomViewStore.isInitialEventHighlighted(), }; // Clear the search results when clicking a search result (which changes the - // currently scrolled to event, this.state.eventId). - if (this.state.eventId !== newState.eventId) { + // currently scrolled to event, this.state.initialEventId). + if (this.state.initialEventId !== newState.initialEventId) { newState.searchResults = null; } @@ -1665,12 +1665,12 @@ module.exports = React.createClass({ hideMessagePanel = true; } - const shouldHighlight = this.state.isEventHighlighted; + const shouldHighlight = this.state.isInitialEventHighlighted; let highlightedEventId = null; if (this.state.forwardingEvent) { highlightedEventId = this.state.forwardingEvent.getId(); } else if (shouldHighlight) { - highlightedEventId = this.state.eventId; + highlightedEventId = this.state.initialEventId; } // console.log("ShowUrlPreview for %s is %s", this.state.room.roomId, this.state.showUrlPreview); @@ -1681,8 +1681,8 @@ module.exports = React.createClass({ manageReadMarkers={true} hidden={hideMessagePanel} highlightedEventId={highlightedEventId} - eventId={this.state.eventId} - eventPixelOffset={this.state.eventPixelOffset} + eventId={this.state.initialEventId} + eventPixelOffset={this.state.initialEventPixelOffset} onScroll={ this.onMessageListScroll } onReadMarkerUpdated={ this._updateTopUnreadMessagesBar } showUrlPreview = { this.state.showUrlPreview } diff --git a/src/stores/RoomViewStore.js b/src/stores/RoomViewStore.js index 0529824a4b..8277aeda97 100644 --- a/src/stores/RoomViewStore.js +++ b/src/stores/RoomViewStore.js @@ -27,12 +27,14 @@ const INITIAL_STATE = { joinError: null, // The room ID of the room roomId: null, - // The event being viewed - eventId: null, - // The offset to display the event at (see scrollStateMap) - eventPixelOffset: null, - // Whether to highlight the event - isEventHighlighted: false, + + // The event to scroll to initially + initialEventId: null, + // The offset to display the initial event at (see scrollStateMap) + initialEventPixelOffset: null, + // Whether to highlight the initial event + isInitialEventHighlighted: false, + // The room alias of the room (or null if not originally specified in view_room) roomAlias: null, // Whether the current room is loading @@ -131,9 +133,9 @@ class RoomViewStore extends Store { this._setState({ roomId: payload.room_id, - eventId: payload.event_id, - eventPixelOffset: payload.event_offset, - isEventHighlighted: payload.highlighted, + initialEventId: payload.event_id, + initialEventPixelOffset: payload.event_offset, + isInitialEventHighlighted: payload.highlighted, roomLoading: false, roomLoadError: null, }); @@ -141,9 +143,9 @@ class RoomViewStore extends Store { // Resolve the alias and then do a second dispatch with the room ID acquired this._setState({ roomId: null, - eventId: null, - eventPixelOffset: null, - isEventHighlighted: null, + initialEventId: null, + initialEventPixelOffset: null, + isInitialEventHighlighted: null, roomAlias: payload.room_alias, roomLoading: true, roomLoadError: null, @@ -229,16 +231,16 @@ class RoomViewStore extends Store { return this._state.roomId; } - getEventId() { - return this._state.eventId; + getInitialEventId() { + return this._state.initialEventId; } - getEventPixelOffset() { - return this._state.eventPixelOffset; + getInitialEventPixelOffset() { + return this._state.initialEventPixelOffset; } - isEventHighlighted() { - return this._state.isEventHighlighted; + isInitialEventHighlighted() { + return this._state.isInitialEventHighlighted; } getRoomAlias() { From 36f72cccb51d4be536509b8879321660b604d90c Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 8 Jun 2017 15:54:23 +0100 Subject: [PATCH 0689/1016] Don't alter members of payload (when defaulting an event to scroll to), alter a copy --- src/stores/RoomViewStore.js | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/stores/RoomViewStore.js b/src/stores/RoomViewStore.js index 8277aeda97..a0c9ccc33f 100644 --- a/src/stores/RoomViewStore.js +++ b/src/stores/RoomViewStore.js @@ -121,24 +121,26 @@ class RoomViewStore extends Store { _viewRoom(payload) { if (payload.room_id) { - // If an event ID wasn't specified, default to the one saved for this room - // via update_scroll_state. Also assume event_offset should be set. - if (!payload.event_id) { - const roomScrollState = this._state.scrollStateMap[payload.room_id]; - if (roomScrollState) { - payload.event_id = roomScrollState.focussedEvent; - payload.event_offset = roomScrollState.pixelOffset; - } - } - - this._setState({ + const newState = { roomId: payload.room_id, initialEventId: payload.event_id, initialEventPixelOffset: payload.event_offset, isInitialEventHighlighted: payload.highlighted, roomLoading: false, roomLoadError: null, - }); + }; + + // If an event ID wasn't specified, default to the one saved for this room + // via update_scroll_state. Assume initialEventPixelOffset should be set. + if (!newState.initialEventId) { + const roomScrollState = this._state.scrollStateMap[payload.room_id]; + if (roomScrollState) { + newState.initialEventId = roomScrollState.focussedEvent; + newState.initialEventPixelOffset = roomScrollState.pixelOffset; + } + } + + this._setState(newState); } else if (payload.room_alias) { // Resolve the alias and then do a second dispatch with the room ID acquired this._setState({ From 1ff2be189605c5827dbeedc462446b29b388361b Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 8 Jun 2017 16:00:12 +0100 Subject: [PATCH 0690/1016] Update docs for RVS --- src/stores/RoomViewStore.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/stores/RoomViewStore.js b/src/stores/RoomViewStore.js index a0c9ccc33f..e2a3f574ed 100644 --- a/src/stores/RoomViewStore.js +++ b/src/stores/RoomViewStore.js @@ -23,9 +23,9 @@ import { _t } from '../languageHandler'; const INITIAL_STATE = { // Whether we're joining the currently viewed room joining: false, - // Any error occurred during joining + // Any error that has occurred during joining joinError: null, - // The room ID of the room + // The room ID of the room currently being viewed roomId: null, // The event to scroll to initially @@ -229,38 +229,47 @@ class RoomViewStore extends Store { this._state = Object.assign({}, INITIAL_STATE); } + // The room ID of the room currently being viewed getRoomId() { return this._state.roomId; } + // The event to scroll to initially getInitialEventId() { return this._state.initialEventId; } + // The offset to display the initial event at (see scrollStateMap) getInitialEventPixelOffset() { return this._state.initialEventPixelOffset; } + // Whether to highlight the initial event isInitialEventHighlighted() { return this._state.isInitialEventHighlighted; } + // The room alias of the room (or null if not originally specified in view_room) getRoomAlias() { return this._state.roomAlias; } + // Whether the current room is loading (true whilst resolving an alias) isRoomLoading() { return this._state.roomLoading; } + // Any error that has occurred during loading getRoomLoadError() { return this._state.roomLoadError; } + // Whether we're joining the currently viewed room isJoining() { return this._state.joining; } + // Any error that has occurred during joining getJoinError() { return this._state.joinError; } From 8dc20606c8be5ae78859178b91f2760ea37d0827 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 8 Jun 2017 16:07:29 +0100 Subject: [PATCH 0691/1016] Use comma for list of three things --- src/components/structures/login/Login.js | 2 +- src/i18n/strings/en_EN.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/structures/login/Login.js b/src/components/structures/login/Login.js index 7d283e0e36..6d0e929c93 100644 --- a/src/components/structures/login/Login.js +++ b/src/components/structures/login/Login.js @@ -247,7 +247,7 @@ module.exports = React.createClass({ ; } else { errorText = - { _tJsx("Can't connect to homeserver - please check your connectivity and ensure your homeserver's SSL certificate is trusted and that a browser extension is not blocking requests.", + { _tJsx("Can't connect to homeserver - please check your connectivity, ensure your homeserver's SSL certificate is trusted, and that a browser extension is not blocking requests.", /(.*?)<\/a>/, (sub) => { return { sub }; } )} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 6984d46ed4..c6086faf0a 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -180,7 +180,7 @@ "Bug Report": "Bug Report", "Bulk Options": "Bulk Options", "Call Timeout": "Call Timeout", - "Can't connect to homeserver - please check your connectivity and ensure your homeserver's SSL certificate is trusted and that a browser extension is not blocking requests.": "Can't connect to homeserver - please check your connectivity and ensure your homeserver's SSL certificate is trusted and that a browser extension is not blocking requests.", + "Can't connect to homeserver - please check your connectivity, ensure your homeserver's SSL certificate is trusted, and that a browser extension is not blocking requests.": "Can't connect to homeserver - please check your connectivity, ensure your homeserver's SSL certificate is trusted, and that a browser extension is not blocking requests.", "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or enable unsafe scripts.": "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or enable unsafe scripts.", "Can't load user settings": "Can't load user settings", "Change Password": "Change Password", From 9def0bb5c2947ecfc53ce1abbe8e959cc2ab23d8 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 8 Jun 2017 16:08:19 +0100 Subject: [PATCH 0692/1016] Oops, fix email check --- src/components/structures/login/Login.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/login/Login.js b/src/components/structures/login/Login.js index 6d0e929c93..bbfff3712e 100644 --- a/src/components/structures/login/Login.js +++ b/src/components/structures/login/Login.js @@ -90,7 +90,7 @@ module.exports = React.createClass({ let errorText; // Some error strings only apply for logging in - const usingEmail = username.indexOf("@"); + const usingEmail = username.indexOf("@") > 0; if (error.httpStatus == 400 && usingEmail) { errorText = _t('This Home Server does not support login using email address.'); } else if (error.httpStatus == 401 || error.httpStatus === 403) { From 5e55b6a6435f8d342ab84281d31407f14792b54c Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 8 Jun 2017 16:23:43 +0100 Subject: [PATCH 0693/1016] PR feedback --- src/components/structures/login/Login.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/structures/login/Login.js b/src/components/structures/login/Login.js index bbfff3712e..27c0c200be 100644 --- a/src/components/structures/login/Login.js +++ b/src/components/structures/login/Login.js @@ -93,7 +93,7 @@ module.exports = React.createClass({ const usingEmail = username.indexOf("@") > 0; if (error.httpStatus == 400 && usingEmail) { errorText = _t('This Home Server does not support login using email address.'); - } else if (error.httpStatus == 401 || error.httpStatus === 403) { + } else if (error.httpStatus === 401 || error.httpStatus === 403) { errorText = _t('Incorrect username and/or password.'); } else { // other errors, not specific to doing a password login @@ -102,8 +102,11 @@ module.exports = React.createClass({ this.setState({ errorText: errorText, - // https://matrix.org/jira/browse/SYN-744 - loginIncorrect: error.httpStatus == 401 || error.httpStatus == 403 + // 401 would be the sensible status code for 'incorrect password' + // but the login API gives a 403 https://matrix.org/jira/browse/SYN-744 + // mentions this (although the bug is for UI auth which is not this) + // We treat both as an incorrect password + loginIncorrect: error.httpStatus === 401 || error.httpStatus == 403, }); }).finally(() => { this.setState({ From 175599bedaa4a30e79dd5e7f3d01297ed7303847 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Thu, 8 Jun 2017 14:21:25 +0100 Subject: [PATCH 0694/1016] Improve error logging/reporting in megolm import/export I saw a rageshake where somebody had apparently failed to import a key file. I have no idea why it happened. Also try to make the errors the users see useful. --- .../views/dialogs/ExportE2eKeysDialog.js | 4 +- .../views/dialogs/ImportE2eKeysDialog.js | 4 +- src/i18n/strings/en_EN.json | 5 +- src/utils/MegolmExportEncryption.js | 172 ++++++++++++------ 4 files changed, 129 insertions(+), 56 deletions(-) diff --git a/src/async-components/views/dialogs/ExportE2eKeysDialog.js b/src/async-components/views/dialogs/ExportE2eKeysDialog.js index 045ea63c34..8f113353d9 100644 --- a/src/async-components/views/dialogs/ExportE2eKeysDialog.js +++ b/src/async-components/views/dialogs/ExportE2eKeysDialog.js @@ -81,11 +81,13 @@ export default React.createClass({ FileSaver.saveAs(blob, 'riot-keys.txt'); this.props.onFinished(true); }).catch((e) => { + console.error("Error exporting e2e keys:", e); if (this._unmounted) { return; } + const msg = e.friendlyText || _t('Unknown error'); this.setState({ - errStr: e.message, + errStr: msg, phase: PHASE_EDIT, }); }); diff --git a/src/async-components/views/dialogs/ImportE2eKeysDialog.js b/src/async-components/views/dialogs/ImportE2eKeysDialog.js index 91010d33b9..9eac7f78b2 100644 --- a/src/async-components/views/dialogs/ImportE2eKeysDialog.js +++ b/src/async-components/views/dialogs/ImportE2eKeysDialog.js @@ -89,11 +89,13 @@ export default React.createClass({ // TODO: it would probably be nice to give some feedback about what we've imported here. this.props.onFinished(true); }).catch((e) => { + console.error("Error importing e2e keys:", e); if (this._unmounted) { return; } + const msg = e.friendlyText || _t('Unknown error'); this.setState({ - errStr: e.message, + errStr: msg, phase: PHASE_EDIT, }); }); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 8dbbb98423..9199c3b103 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -860,5 +860,8 @@ "Username not available": "Username not available", "Something went wrong!": "Something went wrong!", "This will be your account name on the homeserver, or you can pick a different server.": "This will be your account name on the homeserver, or you can pick a different server.", - "If you already have a Matrix account you can log in instead.": "If you already have a Matrix account you can log in instead." + "If you already have a Matrix account you can log in instead.": "If you already have a Matrix account you can log in instead.", + "Your browser does not support the required cryptography extensions": "Your browser does not support the required cryptography extensions", + "Not a valid Riot keyfile": "Not a valid Riot keyfile", + "Authentication check failed: incorrect password?": "Authentication check failed: incorrect password?" } diff --git a/src/utils/MegolmExportEncryption.js b/src/utils/MegolmExportEncryption.js index e91dc06b04..11f9d86816 100644 --- a/src/utils/MegolmExportEncryption.js +++ b/src/utils/MegolmExportEncryption.js @@ -27,31 +27,57 @@ if (!TextDecoder) { TextDecoder = TextEncodingUtf8.TextDecoder; } +import { _t } from '../languageHandler'; + + const subtleCrypto = window.crypto.subtle || window.crypto.webkitSubtle; +/** + * Make an Error object which has a friendlyText property which is already + * translated and suitable for showing to the user. + * + * @param {string} msg message for the exception + * @param {string} friendlyText + * @returns {Error} + */ +function friendlyError(msg, friendlyText) { + const e = new Error(msg); + e.friendlyText = friendlyText; + return e; +} + +function cryptoFailMsg() { + return _t('Your browser does not support the required cryptography extensions'); +} + /** * Decrypt a megolm key file * * @param {ArrayBuffer} data file to decrypt * @param {String} password * @return {Promise} promise for decrypted output + * + * */ export async function decryptMegolmKeyFile(data, password) { const body = unpackMegolmKeyFile(data); // check we have a version byte if (body.length < 1) { - throw new Error('Invalid file: too short'); + throw friendlyError('Invalid file: too short', + _t('Not a valid Riot keyfile')); } const version = body[0]; if (version !== 1) { - throw new Error('Unsupported version'); + throw friendlyError('Unsupported version', + _t('Not a valid Riot keyfile')); } const ciphertextLength = body.length-(1+16+16+4+32); if (ciphertextLength < 0) { - throw new Error('Invalid file: too short'); + throw friendlyError('Invalid file: too short', + _t('Not a valid Riot keyfile')); } const salt = body.subarray(1, 1+16); @@ -61,27 +87,38 @@ export async function decryptMegolmKeyFile(data, password) { const hmac = body.subarray(-32); const [aesKey, hmacKey] = await deriveKeys(salt, iterations, password); - const toVerify = body.subarray(0, -32); - const isValid = await subtleCrypto.verify( - {name: 'HMAC'}, - hmacKey, - hmac, - toVerify, - ); + + let isValid; + try { + isValid = await subtleCrypto.verify( + {name: 'HMAC'}, + hmacKey, + hmac, + toVerify, + ); + } catch (e) { + throw friendlyError('subtleCrypto.verify failed: ' + e, cryptoFailMsg()); + } if (!isValid) { - throw new Error('Authentication check failed: incorrect password?'); + throw friendlyError('hmac mismatch', + _t('Authentication check failed: incorrect password?')); } - const plaintext = await subtleCrypto.decrypt( - { - name: "AES-CTR", - counter: iv, - length: 64, - }, - aesKey, - ciphertext, - ); + let plaintext; + try { + plaintext = await subtleCrypto.decrypt( + { + name: "AES-CTR", + counter: iv, + length: 64, + }, + aesKey, + ciphertext, + ); + } catch(e) { + throw friendlyError('subtleCrypto.decrypt failed: ' + e, cryptoFailMsg()); + } return new TextDecoder().decode(new Uint8Array(plaintext)); } @@ -113,16 +150,22 @@ export async function encryptMegolmKeyFile(data, password, options) { iv[9] &= 0x7f; const [aesKey, hmacKey] = await deriveKeys(salt, kdfRounds, password); + const encodedData = new TextEncoder().encode(data); - const ciphertext = await subtleCrypto.encrypt( - { - name: "AES-CTR", - counter: iv, - length: 64, - }, - aesKey, - new TextEncoder().encode(data), - ); + let ciphertext; + try { + ciphertext = await subtleCrypto.encrypt( + { + name: "AES-CTR", + counter: iv, + length: 64, + }, + aesKey, + encodedData, + ); + } catch (e) { + throw friendlyError('subtleCrypto.encrypt failed: ' + e, cryptoFailMsg()); + } const cipherArray = new Uint8Array(ciphertext); const bodyLength = (1+salt.length+iv.length+4+cipherArray.length+32); @@ -139,11 +182,17 @@ export async function encryptMegolmKeyFile(data, password, options) { const toSign = resultBuffer.subarray(0, idx); - const hmac = await subtleCrypto.sign( - {name: 'HMAC'}, - hmacKey, - toSign, - ); + let hmac; + try { + hmac = await subtleCrypto.sign( + {name: 'HMAC'}, + hmacKey, + toSign, + ); + } catch (e) { + throw friendlyError('subtleCrypto.sign failed: ' + e, cryptoFailMsg()); + } + const hmacArray = new Uint8Array(hmac); resultBuffer.set(hmacArray, idx); @@ -160,24 +209,35 @@ export async function encryptMegolmKeyFile(data, password, options) { */ async function deriveKeys(salt, iterations, password) { const start = new Date(); - const key = await subtleCrypto.importKey( - 'raw', - new TextEncoder().encode(password), - {name: 'PBKDF2'}, - false, - ['deriveBits'], - ); - const keybits = await subtleCrypto.deriveBits( - { - name: 'PBKDF2', - salt: salt, - iterations: iterations, - hash: 'SHA-512', - }, - key, - 512, - ); + let key; + try { + key = await subtleCrypto.importKey( + 'raw', + new TextEncoder().encode(password), + {name: 'PBKDF2'}, + false, + ['deriveBits'], + ); + } catch (e) { + throw friendlyError('subtleCrypto.importKey failed: ' + e, cryptoFailMsg()); + } + + let keybits; + try { + keybits = await subtleCrypto.deriveBits( + { + name: 'PBKDF2', + salt: salt, + iterations: iterations, + hash: 'SHA-512', + }, + key, + 512, + ); + } catch (e) { + throw friendlyError('subtleCrypto.deriveBits failed: ' + e, cryptoFailMsg()); + } const now = new Date(); console.log("E2e import/export: deriveKeys took " + (now - start) + "ms"); @@ -191,7 +251,10 @@ async function deriveKeys(salt, iterations, password) { {name: 'AES-CTR'}, false, ['encrypt', 'decrypt'], - ); + ).catch((e) => { + throw friendlyError('subtleCrypto.importKey failed for AES key: ' + e, cryptoFailMsg()); + }); + const hmacProm = subtleCrypto.importKey( 'raw', hmacKey, @@ -201,7 +264,10 @@ async function deriveKeys(salt, iterations, password) { }, false, ['sign', 'verify'], - ); + ).catch((e) => { + throw friendlyError('subtleCrypto.importKey failed for HMAC key: ' + e, cryptoFailMsg()); + }); + return await Promise.all([aesProm, hmacProm]); } From c02dbd1cdcde8b82db68e1ef9e2ff6d285ad3789 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 8 Jun 2017 17:26:08 +0100 Subject: [PATCH 0695/1016] Rename initial state variables --- src/components/structures/RoomView.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 942e3c8e07..79420e776a 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -95,11 +95,11 @@ module.exports = React.createClass({ peekLoading: false, // The event to be scrolled to initially - eventId: null, + initialEventId: null, // The offset in pixels from the event with which to scroll vertically - eventPixelOffset: null, + initialEventPixelOffset: null, // Whether to highlight the event scrolled to - isEventHighlighted: null, + isInitialEventHighlighted: null, forwardingEvent: null, editingRoomSettings: false, From df625b0d318661ba64d2b1155651204f3219f7bf Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Thu, 8 Jun 2017 17:26:40 +0100 Subject: [PATCH 0696/1016] hit MemberInfo with Gemini to fix vector-im/riot-web#4223 --- src/components/views/rooms/MemberInfo.js | 48 +++++++++++++----------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/src/components/views/rooms/MemberInfo.js b/src/components/views/rooms/MemberInfo.js index ff684aeae4..6dc86f9a97 100644 --- a/src/components/views/rooms/MemberInfo.js +++ b/src/components/views/rooms/MemberInfo.js @@ -38,6 +38,8 @@ import Unread from '../../../Unread'; import { findReadReceiptFromUserId } from '../../../utils/Receipt'; import WithMatrixClient from '../../../wrappers/WithMatrixClient'; import AccessibleButton from '../elements/AccessibleButton'; +import GeminiScrollbar from 'react-gemini-scrollbar'; + module.exports = WithMatrixClient(React.createClass({ displayName: 'MemberInfo', @@ -727,34 +729,36 @@ module.exports = WithMatrixClient(React.createClass({ const EmojiText = sdk.getComponent('elements.EmojiText'); return (
    - -
    - -
    - - {memberName} - -
    -
    - { this.props.member.userId } + + +
    +
    -
    - { _t("Level:") } + + {memberName} + +
    +
    + { this.props.member.userId } +
    +
    + { _t("Level:") } +
    +
    + +
    -
    - -
    -
    - { adminTools } + { adminTools } - { startChat } + { startChat } - { this._renderDevices() } + { this._renderDevices() } - { spinner } + { spinner } +
    ); } From 8cfdb4ce091dc947ffe3f9b4a8a98c04b236f208 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 8 Jun 2017 17:27:04 +0100 Subject: [PATCH 0697/1016] Clarify initial event --- src/stores/RoomViewStore.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stores/RoomViewStore.js b/src/stores/RoomViewStore.js index e2a3f574ed..ec1a86b0e4 100644 --- a/src/stores/RoomViewStore.js +++ b/src/stores/RoomViewStore.js @@ -28,7 +28,7 @@ const INITIAL_STATE = { // The room ID of the room currently being viewed roomId: null, - // The event to scroll to initially + // The event to scroll to when the room is first viewed initialEventId: null, // The offset to display the initial event at (see scrollStateMap) initialEventPixelOffset: null, From cd8093d953abf69784aca82bbb1b076773a6cd28 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 8 Jun 2017 17:28:21 +0100 Subject: [PATCH 0698/1016] Explicit undefined initialEventPixelOffset --- src/stores/RoomViewStore.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stores/RoomViewStore.js b/src/stores/RoomViewStore.js index ec1a86b0e4..ec545dcfd5 100644 --- a/src/stores/RoomViewStore.js +++ b/src/stores/RoomViewStore.js @@ -124,7 +124,7 @@ class RoomViewStore extends Store { const newState = { roomId: payload.room_id, initialEventId: payload.event_id, - initialEventPixelOffset: payload.event_offset, + initialEventPixelOffset: undefined, isInitialEventHighlighted: payload.highlighted, roomLoading: false, roomLoadError: null, From c72edab7267dc408fbd2d4900f9deb7fefeb16d4 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 8 Jun 2017 17:28:56 +0100 Subject: [PATCH 0699/1016] Update doc for getInitialEventId --- src/stores/RoomViewStore.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stores/RoomViewStore.js b/src/stores/RoomViewStore.js index ec545dcfd5..15a46894a1 100644 --- a/src/stores/RoomViewStore.js +++ b/src/stores/RoomViewStore.js @@ -234,7 +234,7 @@ class RoomViewStore extends Store { return this._state.roomId; } - // The event to scroll to initially + // The event to scroll to when the room is first viewed getInitialEventId() { return this._state.initialEventId; } From cd6786114b6b1d49d747be3750d7df763a101e04 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 8 Jun 2017 17:40:53 +0100 Subject: [PATCH 0700/1016] When joining, use a roomAlias if we have it And default to the current roomId otherwise --- src/stores/RoomViewStore.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/stores/RoomViewStore.js b/src/stores/RoomViewStore.js index cc8959af7a..2c5dd9e534 100644 --- a/src/stores/RoomViewStore.js +++ b/src/stores/RoomViewStore.js @@ -137,7 +137,9 @@ class RoomViewStore extends Store { this._setState({ joining: true, }); - MatrixClientPeg.get().joinRoom(this._state.roomId, payload.opts).done(() => { + MatrixClientPeg.get().joinRoom( + this._state.roomAlias || this._state.roomId, payload.opts, + ).done(() => { dis.dispatch({ action: 'joined_room', }); From 3218cae6e3bdce6265826df4d02f6f008240e328 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 8 Jun 2017 17:47:48 +0100 Subject: [PATCH 0701/1016] Expect to join by alias if given to RVS --- test/stores/RoomViewStore-test.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/stores/RoomViewStore-test.js b/test/stores/RoomViewStore-test.js index 2f545ffd74..adcae90e9b 100644 --- a/test/stores/RoomViewStore-test.js +++ b/test/stores/RoomViewStore-test.js @@ -28,8 +28,8 @@ describe('RoomViewStore', function() { }); it('can be used to view a room by ID and join', function(done) { - peg.get().joinRoom = (roomId) => { - expect(roomId).toBe("!randomcharacters:aser.ver"); + peg.get().joinRoom = (roomAddress) => { + expect(roomAddress).toBe("!randomcharacters:aser.ver"); done(); }; @@ -40,8 +40,8 @@ describe('RoomViewStore', function() { 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"); + peg.get().joinRoom = (roomAddress) => { + expect(roomAddress).toBe("#somealias2:aser.ver"); done(); }; From 9892566007f2790e2e8295b7e7093977f61672d5 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 8 Jun 2017 17:54:41 +0100 Subject: [PATCH 0702/1016] Highlighted means highlighted --- 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 5f13f53d0b..b803b676f7 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -608,6 +608,7 @@ module.exports = React.createClass({ // @param {string=} roomInfo.event_id ID of the event in this room to show: this will cause a switch to the // context of that particular event. // @param {boolean=} roomInfo.highlighted If true, add event_id to the hash of the URL + // and alter the EventTile to appear highlighted. // @param {Object=} roomInfo.third_party_invite Object containing data about the third party // we received to join the room, if any. // @param {string=} roomInfo.third_party_invite.inviteSignUrl 3pid invite sign URL From 5588e115eb9be9a1cf10fe4c7e33b9a3e44cbc86 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 8 Jun 2017 17:57:37 +0100 Subject: [PATCH 0703/1016] Clarify doc for highlighted --- src/components/structures/MatrixChat.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index b803b676f7..b6e3552a95 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -1119,7 +1119,9 @@ module.exports = React.createClass({ const payload = { action: 'view_room', event_id: eventId, - // If an event ID is set (truthy), mark it as highlighted + // If an event ID is given in the URL hash, notify RoomViewStore to mark + // it as highlighted, which will propagate to RoomView and highlight the + // associated EventTile. highlighted: Boolean(eventId), third_party_invite: thirdPartyInvite, oob_data: oobData, From 7dbb1c3b85bee3aa9ade8956f489d8dcaddb3553 Mon Sep 17 00:00:00 2001 From: Bamstam Date: Thu, 8 Jun 2017 16:04:10 +0000 Subject: [PATCH 0704/1016] Translated using Weblate (German) Currently translated at 100.0% (853 of 853 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/de/ --- src/i18n/strings/de_DE.json | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index 4102d7be56..c5ee272b1f 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -885,31 +885,31 @@ "%(count)s new messages.one": "%(count)s neue Nachricht", "%(count)s new messages.other": "%(count)s neue Nachrichten", "Error: Problem communicating with the given homeserver.": "Fehler: Problem beim kommunizieren mit dem angegebenen Heimserver.", - "Failed to fetch avatar URL": "Fehler beim holen der Avatar-URL", - "The phone number entered looks invalid": "Die Telefonnummer, die eingegeben wurde, sieht ungültig aus", + "Failed to fetch avatar URL": "Abrufen der Avatar-URL fehlgeschlagen", + "The phone number entered looks invalid": "Die eingegebene Telefonnummer scheint ungültig zu sein", "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 betretbar. Du kannst evtl. beitreten 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.": "Es wurde versucht einen spezifischen Punkt in der Chat-Historie zu laden, aber du hast keine Berechtigung diese Nachricht zu sehen.", - "Tried to load a specific point in this room's timeline, but was unable to find it.": "Es wurde versucht einen spezifischen Punkt in der Chat-Historie zu laden, aber er konnte nicht gefunden werden.", + "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.": "Es wurde versucht, einen bestimmten Punkt im Chatverlauf dieses Raumes zu laden. Dir fehlt jedoch die Berechtigung, die betreffende Nachricht zu sehen.", + "Tried to load a specific point in this room's timeline, but was unable to find it.": "Es wurde versucht, einen bestimmten Punkt im Chatverlauf dieses Raumes zu laden, der Punkt konnte jedoch nicht gefunden werden.", "Uploading %(filename)s and %(count)s others.zero": "%(filename)s wird hochgeladen", "Uploading %(filename)s and %(count)s others.one": "%(filename)s und %(count)s weitere Dateien werden hochgeladen", "Uploading %(filename)s and %(count)s others.other": "%(filename)s und %(count)s weitere Dateien werden hochgeladen", - "You must register to use this functionality": "Du musst dich registrieren um diese Funktionalität zu nutzen", - "Resend all or cancel all now. You can also select individual messages to resend or cancel.": "Sende erneut oder breche alles ab. Du kannst auch auch individuelle Nachrichten erneut senden or abbrechen.", + "You must register to use this functionality": "Du musst dich registrieren, um diese Funktionalität nutzen zu können", + "Resend all or cancel all now. You can also select individual messages to resend or cancel.": "Alle erneut senden oder alle verwerfen. Du kannst auch einzelne Nachrichten erneut senden oder verwerfen.", "Create new room": "Neuen Raum erstellen", "Welcome page": "Willkommensseite", "Room directory": "Raum-Verzeichnis", - "Start chat": "Starte Chat", + "Start chat": "Chat starten", "New Password": "Neues Passwort", "Start chatting": "Starte plaudern", "Start Chatting": "Starte Gespräche", - "Click on the button below to start chatting!": "Klicke den Button unten um das Plaudern zu beginnen!", - "Create a new chat or reuse an existing one": "Erstelle einen neuen Chat oder nutze einen existierenden", + "Click on the button below to start chatting!": "Unten auf den Button klicken, um einen Chat zu beginnen!", + "Create a new chat or reuse an existing one": "Neuen Chat erstellen oder einen vorhandenen Chat fortsetzen", "You already have existing direct chats with this user:": "Du hast bereits direkte Chats mit diesem Nutzer:", "Username available": "Nutzername verfügbar", "Username not available": "Nutzername nicht verfügbar", "Something went wrong!": "Etwas ging schief!", - "This will be your account name on the homeserver, or you can pick a different server.": "Dies wird dein Konto-Name auf dem Heimserver, oder du kannst einen anderen Server auswählen.", + "This will be your account name on the homeserver, or you can pick a different server.": "Dies wird dein zukünftiger Benutzername auf dem Heimserver. Alternativ kannst du auch einen anderen Server auswählen.", "If you already have a Matrix account you can log in instead.": "Wenn du bereits ein Matrix-Benutzerkonto hast, kannst du dich stattdessen auch direkt anmelden.", "Home": "Start", - "Username invalid: %(errMessage)s": "Nutzername falsch: %(errMessage)s" + "Username invalid: %(errMessage)s": "Ungültiger Benutzername: %(errMessage)s" } From 80dba43b41b783c8b2809c7747d19925a5f69c43 Mon Sep 17 00:00:00 2001 From: dtygel Date: Thu, 8 Jun 2017 13:47:18 +0000 Subject: [PATCH 0705/1016] Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (853 of 853 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/pt_BR/ --- src/i18n/strings/pt_BR.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/pt_BR.json b/src/i18n/strings/pt_BR.json index 231c6a464d..61cc45c94f 100644 --- a/src/i18n/strings/pt_BR.json +++ b/src/i18n/strings/pt_BR.json @@ -81,7 +81,7 @@ "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 change password. Is your password correct?": "Não foi possível mudar a senha. A sua senha 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", From 0f062a7fe9fb1031bf14d92732c470aba694d9c2 Mon Sep 17 00:00:00 2001 From: Stanislav N Date: Thu, 8 Jun 2017 13:54:08 +0000 Subject: [PATCH 0706/1016] Translated using Weblate (Russian) Currently translated at 100.0% (853 of 853 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/ru/ --- src/i18n/strings/ru.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/i18n/strings/ru.json b/src/i18n/strings/ru.json index 4f1939ffcc..275d27b442 100644 --- a/src/i18n/strings/ru.json +++ b/src/i18n/strings/ru.json @@ -854,10 +854,10 @@ "Drop file here to upload": "Перетащите файл сюда для загрузки", " (unsupported)": " (не поддерживается)", "Ongoing conference call%(supportedText)s. %(joinText)s": "Идет конференц-звонок%(supportedText)s. %(joinText)s", - "for %(amount)ss": "для %(amount)s", - "for %(amount)sm": "для %(amount)s", - "for %(amount)sh": "для %(amount)s", - "for %(amount)sd": "для %(amount)s", + "for %(amount)ss": "уже %(amount)sс", + "for %(amount)sm": "уже %(amount)sм", + "for %(amount)sh": "уже %(amount)sч", + "for %(amount)sd": "уже %(amount)sд", "Online": "В сети", "Idle": "Отошел", "Offline": "Не в сети", From 449b4e87d4fd746bb110a2a82b31550de2a2459b Mon Sep 17 00:00:00 2001 From: Pitchaya Boonsarngsuk Date: Thu, 8 Jun 2017 13:54:32 +0000 Subject: [PATCH 0707/1016] Translated using Weblate (Thai) Currently translated at 56.1% (479 of 853 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.nordgedanken.de/projects/riot-web/matrix-react-sdk/th/ --- src/i18n/strings/th.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/i18n/strings/th.json b/src/i18n/strings/th.json index f97b6b8d50..635dbb256c 100644 --- a/src/i18n/strings/th.json +++ b/src/i18n/strings/th.json @@ -78,7 +78,7 @@ "Remove": "ลบ", "Custom Server Options": "กำหนดเซิร์ฟเวอร์เอง", "Failed to join the room": "การเข้าร่วมห้องล้มเหลว", - "Drop here %(toAction)s": "ปล่อยที่นี่ %(toAction)s", + "Drop here %(toAction)s": "ปล่อยที่นี่%(toAction)s", "Favourite": "รายการโปรด", "Failed to forget room %(errCode)s": "การลืมห้องล้มเหลว %(errCode)s", "%(targetName)s accepted an invitation.": "%(targetName)s ตอบรับคำเชิญแล้ว", @@ -120,7 +120,7 @@ "Bans user with given id": "ผู้ใช้และ id ที่ถูกแบน", "Blacklisted": "ขึ้นบัญชีดำ", "Can't load user settings": "ไม่สามารถโหลดการตั้งค่าผู้ใช้ได้", - "%(senderName)s changed their display name from %(oldDisplayName)s to %(displayName)s.": "%(senderName)s ได้เปลี่ยนชื่อที่แสดงจาก %(oldDisplayName)s ไปเป็น %(displayName)s", + "%(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 เปลี่ยนรูปโปรไฟล์ของเขา", "%(senderDisplayName)s changed the room name to %(roomName)s.": "%(senderDisplayName)s เปลี่ยนชื่อห้องไปเป็น %(roomName)s", "%(senderDisplayName)s removed the room name.": "%(senderDisplayName)s ลบชื่อห้อง", @@ -244,7 +244,7 @@ "New passwords don't match": "รหัสผ่านใหม่ไม่ตรงกัน", "New passwords must match each other.": "รหัสผ่านใหม่ทั้งสองช่องต้องตรงกัน", "none": "ไม่มี", - "not set": "ไม่ได้ตั้ง", + "not set": "ยังไม่ได้ตั้ง", "not specified": "ไม่ได้ระบุ", "(not supported by this browser)": "(เบราว์เซอร์นี้ไม่รองรับ)", "": "<ไม่รองรับ>", From 1d02cde5021fe8e91a3e0329dbdd547849eebe8e Mon Sep 17 00:00:00 2001 From: Dor Ben Dov Date: Thu, 8 Jun 2017 20:03:33 +0000 Subject: [PATCH 0708/1016] Added translation using Weblate (Hebrew) --- src/i18n/strings/he.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/i18n/strings/he.json diff --git a/src/i18n/strings/he.json b/src/i18n/strings/he.json new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/src/i18n/strings/he.json @@ -0,0 +1 @@ +{} \ No newline at end of file From f680bb5d3c29331140313c60eb2441e799c04972 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Thu, 8 Jun 2017 23:00:55 +0100 Subject: [PATCH 0709/1016] broken vars --- src/i18n/strings/el.json | 2 +- src/i18n/strings/th.json | 2 +- src/i18n/strings/zh_Hant.json | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/i18n/strings/el.json b/src/i18n/strings/el.json index d8729202d5..725b5497a5 100644 --- a/src/i18n/strings/el.json +++ b/src/i18n/strings/el.json @@ -1,7 +1,7 @@ { "af": "Αφρικάνικα", "Error": "Σφάλμα", - "Failed to forget room %(errCode)s": "Δεν ήταν δυνατή η διαγραφή του δωματίου", + "Failed to forget room %(errCode)s": "Δεν ήταν δυνατή η διαγραφή του δωματίου %(errCode)s", "Failed to join the room": "Δεν ήταν δυνατή η σύνδεση στο δωμάτιο", "Mute": "Σίγαση", "Notifications": "Ειδοποιήσεις", diff --git a/src/i18n/strings/th.json b/src/i18n/strings/th.json index 635dbb256c..fbc62fdb47 100644 --- a/src/i18n/strings/th.json +++ b/src/i18n/strings/th.json @@ -475,7 +475,7 @@ "Custom server": "เซิร์ฟเวอร์ที่กำหนดเอง", "Home server URL": "URL เซิร์ฟเวอร์บ้าน", "Identity server URL": "URL เซิร์ฟเวอร์ระบุตัวตน", - "%(severalUsers)sleft %(repeats)s times": "%(targetName)sออกจากห้อง %(repeats)s ครั้ง", + "%(severalUsers)sleft %(repeats)s times": "%(severalUsers)sออกจากห้อง %(repeats)s ครั้ง", "%(oneUser)sleft %(repeats)s times": "%(oneUser)sออกจากห้อง %(repeats)s ครั้ง", "%(severalUsers)sleft": "%(severalUsers)sออกจากห้องแล้ว", "%(oneUser)sleft": "%(oneUser)sออกจากห้องแล้ว" diff --git a/src/i18n/strings/zh_Hant.json b/src/i18n/strings/zh_Hant.json index 56cc8f6add..b2e1939565 100644 --- a/src/i18n/strings/zh_Hant.json +++ b/src/i18n/strings/zh_Hant.json @@ -359,7 +359,7 @@ "Enable URL previews for this room (affects only you)": "啟用此房間的網址預覽(僅影響您)", "Drop file here to upload": "把文件放在這裡上傳", "Disable URL previews by default for participants in this room": "默認情況下,此房間的參與者禁用網址預覽", - "URL previews are %(globalDisableUrlPreview)s by default for participants in this room.": "默認情況下,這個房間的參與者的網址預覽是%(globalDisableUrlPreview)。", + "URL previews are %(globalDisableUrlPreview)s by default for participants in this room.": "默認情況下,這個房間的參與者的網址預覽是%(globalDisableUrlPreview)s。", "Removed or unknown message type": "已刪除或未知的信息類型", - "You are about to be taken to a third-party site so you can authenticate your account for use with %(integrationsUrl)s. Do you wish to continue?": "您即將被帶到第三方網站,以便您可以驗證您的帳戶以使用%(integrationsUrl)。你想繼續嗎?" + "You are about to be taken to a third-party site so you can authenticate your account for use with %(integrationsUrl)s. Do you wish to continue?": "您即將被帶到第三方網站,以便您可以驗證您的帳戶以使用%(integrationsUrl)s。你想繼續嗎?" } From 20bdae6079e16d07a498e373d40e31ddc95c118b Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Thu, 8 Jun 2017 18:35:45 +0100 Subject: [PATCH 0710/1016] delint UserSettings.js --- .eslintignore.errorfiles | 1 - src/components/structures/UserSettings.js | 153 ++++++++++++++-------- 2 files changed, 95 insertions(+), 59 deletions(-) diff --git a/.eslintignore.errorfiles b/.eslintignore.errorfiles index f1b63d7367..20c4ca3b4a 100644 --- a/.eslintignore.errorfiles +++ b/.eslintignore.errorfiles @@ -30,7 +30,6 @@ src/components/structures/RoomView.js src/components/structures/ScrollPanel.js src/components/structures/TimelinePanel.js src/components/structures/UploadBar.js -src/components/structures/UserSettings.js src/components/views/avatars/BaseAvatar.js src/components/views/avatars/MemberAvatar.js src/components/views/avatars/RoomAvatar.js diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index 9059378d32..e87bde6d87 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -389,7 +389,10 @@ module.exports = React.createClass({ const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); 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") + ".", + description: _t( + "Your password was successfully changed. You will not receive " + + "push notifications on other devices until you log back in to them", + ) + ".", }); dis.dispatch({action: 'password_changed'}); }, @@ -427,7 +430,10 @@ module.exports = React.createClass({ this._addThreepid.addEmailAddress(emailAddress, true).done(() => { Modal.createDialog(QuestionDialog, { title: _t("Verification Pending"), - description: _t("Please check your email and click on the link it contains. Once this is done, click continue."), + 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, }); @@ -447,7 +453,7 @@ module.exports = React.createClass({ const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); Modal.createDialog(QuestionDialog, { title: _t("Remove Contact Information?"), - description: _t("Remove %(threePid)s?", { threePid : threepid.address }), + description: _t("Remove %(threePid)s?", { threePid: threepid.address }), button: _t('Remove'), onFinished: (submit) => { if (submit) { @@ -489,8 +495,8 @@ module.exports = React.createClass({ this.setState({email_add_pending: false}); if (err.errcode == 'M_THREEPID_AUTH_FAILED') { const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); - let message = _t("Unable to verify email address.") + " " + - _t("Please check your email and click on the link it contains. Once this is done, click continue."); + const message = _t("Unable to verify email address.") + " " + + _t("Please check your email and click on the link it contains. Once this is done, click continue."); Modal.createDialog(QuestionDialog, { title: _t("Verification Pending"), description: message, @@ -608,7 +614,7 @@ module.exports = React.createClass({ } }, - _renderLanguageSetting: function () { + _renderLanguageSetting: function() { const LanguageDropdown = sdk.getComponent('views.elements.LanguageDropdown'); return
    @@ -639,7 +645,7 @@ module.exports = React.createClass({ UserSettingsStore.setUrlPreviewsDisabled(e.target.checked) } + onChange={ this._onPreviewsDisabledChanged } />
    ; }, + _onPreviewsDisabledChanged: function(e) { + UserSettingsStore.setUrlPreviewsDisabled(e.target.checked); + }, + _renderSyncedSetting: function(setting) { + // TODO: this ought to be a separate component so that we don't need + // to rebind the onChange each time we render + + const onChange = (e) => { + UserSettingsStore.setSyncedSetting(setting.id, e.target.checked); + if (setting.fn) setting.fn(e.target.checked); + }; + return
    { - UserSettingsStore.setSyncedSetting(setting.id, e.target.checked); - if (setting.fn) setting.fn(e.target.checked); - } - } + onChange={ onChange } />